--- /dev/null
+use std::fmt::Debug;
+use std::rc::Rc;
+
+/// A persistent leftist heap implementing a priority queue
+/// with efficient merge operations and structural sharing.
+/// A leftist heap merges at O(log n), versus O(n) for binary heaps.
+/// It can do this because the leftist property keeps the right spine
+/// short so merge only recurses logarithmically deep.
+///
+/// Type parameters:
+/// - `T`: The element type, must be `Ord` for heap ordering
+#[derive(Clone, Debug)]
+pub enum Heap<T> {
+ Empty,
+ Node {
+ rank: usize,
+ elem: T,
+ left: Rc<Heap<T>>,
+ right: Rc<Heap<T>>,
+ },
+}
+
+impl<T: Ord + Clone> Heap<T> {
+ /// Identity element of the heap monoid
+ pub fn empty() -> Self {
+ Heap::Empty
+ }
+
+ /// Check if the heap is empty (testing for identity)
+ pub fn is_empty(&self) -> bool {
+ matches!(self, Heap::Empty)
+ }
+
+ /// Retrieve the rank (length of right spine)
+ /// This is a measure/annotation on our tree structure
+ fn rank(&self) -> usize {
+ match self {
+ Heap::Empty => 0,
+ Heap::Node { rank, .. } => *rank,
+ }
+ }
+
+ /// Smart constructor maintaining the leftist property
+ /// This ensures our invariant through construction
+ fn make_node(elem: T, left: Heap<T>, right: Heap<T>) -> Self {
+ let left_rank = left.rank();
+ let right_rank = right.rank();
+
+ // Maintain leftist property: left rank >= right rank
+ let (left, right) = if left_rank >= right_rank {
+ (left, right)
+ } else {
+ (right, left)
+ };
+
+ Heap::Node {
+ rank: right_rank + 1,
+ elem,
+ left: Rc::new(left),
+ right: Rc::new(right),
+ }
+ }
+
+ fn unwrap_or_clone(rc: Rc<Self>) -> Self {
+ Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone())
+ }
+
+ // Merge operation: the fundamental monoid operation
+ // This is associative: merge(merge(a, b), c) ≡ merge(a, merge(b, c))
+ // Empty is identity: merge(Empty, h) ≡ merge(h, Empty) ≡ h
+ // Merge(h1, h2):
+ // 1. Compare roots: smaller becomes new root
+ // (this is where the magic happens)
+ // 2. Recursively: merge(new_root.right, other_heap) // ↓ right spine only!
+ // 3. If rank(left) < rank(right), swap children // restore leftist property
+ pub fn merge(self, other: Self) -> Self {
+ match (self, other) {
+ (Heap::Empty, h) => h,
+ (h, Heap::Empty) => h,
+ (h1, h2) => {
+ let is_h1_smaller = match (&h1, &h2) {
+ (Heap::Node { elem: x, .. }, Heap::Node { elem: y, .. }) => x <= y,
+ _ => unreachable!(),
+ };
+
+ if is_h1_smaller {
+ if let Heap::Node {
+ elem, left, right, ..
+ } = h1
+ {
+ let left_heap = Self::unwrap_or_clone(left);
+ let right_heap = Self::unwrap_or_clone(right);
+ Self::make_node(elem, left_heap, right_heap.merge(h2))
+ } else {
+ unreachable!()
+ }
+ } else {
+ if let Heap::Node {
+ elem, left, right, ..
+ } = h2
+ {
+ let left_heap = Self::unwrap_or_clone(left);
+ let right_heap = Self::unwrap_or_clone(right);
+ Self::make_node(elem, left_heap, h1.merge(right_heap))
+ } else {
+ unreachable!()
+ }
+ }
+ }
+ }
+ }
+
+ /// Insert: defined in terms of merge (following monoid composition)
+ /// insert(x, h) = merge(singleton(x), h)
+ pub fn insert(self, elem: T) -> Self {
+ let singleton = Heap::Node {
+ rank: 1,
+ elem,
+ left: Rc::new(Heap::Empty),
+ right: Rc::new(Heap::Empty),
+ };
+ self.merge(singleton)
+ }
+
+ /// Find minimum element (root of heap)
+ /// This is a simple F-algebra: collapsing to optional value
+ pub fn find_min(&self) -> Option<&T> {
+ match self {
+ Heap::Empty => None,
+ Heap::Node { elem, .. } => Some(elem),
+ }
+ }
+
+ /// Delete minimum: removes root and merges children
+ /// Returns new heap (persistent/immutable operation)
+ pub fn delete_min(self) -> Option<(T, Self)> {
+ match self {
+ Heap::Empty => None,
+ Heap::Node {
+ elem, left, right, ..
+ } => {
+ // Extract children from Rc
+ let left_heap = Rc::try_unwrap(left).unwrap_or_else(|rc| (*rc).clone());
+ let right_heap = Rc::try_unwrap(right).unwrap_or_else(|rc| (*rc).clone());
+
+ // Merge children to form new heap
+ let new_heap = left_heap.merge(right_heap);
+ Some((elem, new_heap))
+ }
+ }
+ }
+
+ /// Functor-like map over heap structure
+ /// Note: This breaks heap property unless f preserves ordering!
+ /// Only use with order-preserving functions
+ pub fn map<U, F>(self, f: F) -> Heap<U>
+ where
+ U: Ord + Clone,
+ F: Fn(T) -> U + Copy,
+ {
+ match self {
+ Heap::Empty => Heap::Empty,
+ Heap::Node {
+ elem, left, right, ..
+ } => {
+ let mapped_elem = f(elem);
+ let mapped_left = Rc::try_unwrap(left)
+ .unwrap_or_else(|rc| (*rc).clone())
+ .map(f);
+ let mapped_right = Rc::try_unwrap(right)
+ .unwrap_or_else(|rc| (*rc).clone())
+ .map(f);
+
+ Heap::make_node(mapped_elem, mapped_left, mapped_right)
+ }
+ }
+ }
+
+ /// Catamorphism: fold the heap structure
+ /// This is the F-algebra approach to consuming heap structure
+ pub fn fold<B, F>(self, init: B, f: F) -> B
+ where
+ F: Fn(B, T) -> B + Copy,
+ {
+ match self {
+ Heap::Empty => init,
+ Heap::Node {
+ elem, left, right, ..
+ } => {
+ let result = f(init, elem);
+ let result = Rc::try_unwrap(left)
+ .unwrap_or_else(|rc| (*rc).clone())
+ .fold(result, f);
+ Rc::try_unwrap(right)
+ .unwrap_or_else(|rc| (*rc).clone())
+ .fold(result, f)
+ }
+ }
+ }
+
+ /// Build heap from iterator (using fold and merge)
+ pub fn from_iter<I>(iter: I) -> Self
+ where
+ I: IntoIterator<Item = T>,
+ {
+ iter.into_iter()
+ .fold(Heap::empty(), |heap, elem| heap.insert(elem))
+ }
+
+ /// Convert to sorted vector (heap sort via repeated delete_min)
+ pub fn to_sorted_vec(self) -> Vec<T> {
+ let mut result = Vec::new();
+ let mut heap = self;
+
+ while let Some((min, rest)) = heap.delete_min() {
+ result.push(min);
+ heap = rest;
+ }
+
+ result
+ }
+}
+
+/// Iterator implementation for consuming heap in sorted order
+pub struct HeapIter<T> {
+ heap: Heap<T>,
+}
+
+impl<T: Ord + Clone> Iterator for HeapIter<T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match std::mem::replace(&mut self.heap, Heap::empty()).delete_min() {
+ Some((elem, rest)) => {
+ self.heap = rest;
+ Some(elem)
+ }
+ None => None,
+ }
+ }
+}
+
+impl<T: Ord + Clone> IntoIterator for Heap<T> {
+ type Item = T;
+ type IntoIter = HeapIter<T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ HeapIter { heap: self }
+ }
+}
+
+// Demonstrate monoid laws with property-based testing
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_empty_is_identity() {
+ let heap = Heap::empty().insert(5).insert(3).insert(7);
+ let heap_clone = heap.clone();
+
+ // Left identity: merge(empty, h) = h
+ assert_eq!(
+ Heap::empty().merge(heap.clone()).to_sorted_vec(),
+ heap_clone.clone().to_sorted_vec()
+ );
+
+ // Right identity: merge(h, empty) = h
+ assert_eq!(
+ heap_clone.clone().merge(Heap::empty()).to_sorted_vec(),
+ heap_clone.to_sorted_vec()
+ );
+ }
+
+ #[test]
+ fn test_merge_associativity() {
+ let h1 = Heap::empty().insert(1).insert(4);
+ let h2 = Heap::empty().insert(2).insert(5);
+ let h3 = Heap::empty().insert(3).insert(6);
+
+ // (h1 ⊕ h2) ⊕ h3
+ let left_assoc = h1.clone().merge(h2.clone()).merge(h3.clone());
+
+ // h1 ⊕ (h2 ⊕ h3)
+ let right_assoc = h1.merge(h2.merge(h3));
+
+ assert_eq!(left_assoc.to_sorted_vec(), right_assoc.to_sorted_vec());
+ }
+
+ #[test]
+ fn test_basic_operations() {
+ let heap = Heap::empty()
+ .insert(5)
+ .insert(3)
+ .insert(7)
+ .insert(1)
+ .insert(9);
+
+ assert_eq!(heap.find_min(), Some(&1));
+
+ let sorted = heap.to_sorted_vec();
+ assert_eq!(sorted, vec![1, 3, 5, 7, 9]);
+ }
+
+ #[test]
+ fn test_persistence() {
+ let heap1 = Heap::empty().insert(5).insert(3);
+ let heap2 = heap1.clone().insert(7);
+ let heap3 = heap1.clone().insert(1);
+
+ // Original heap unchanged
+ assert_eq!(heap1.to_sorted_vec(), vec![3, 5]);
+ // New versions have different elements
+ assert_eq!(heap2.to_sorted_vec(), vec![3, 5, 7]);
+ assert_eq!(heap3.to_sorted_vec(), vec![1, 3, 5]);
+ }
+
+ #[test]
+ fn test_fold_catamorphism() {
+ let heap = Heap::empty().insert(1).insert(2).insert(3).insert(4);
+
+ // Sum all elements via catamorphism
+ let sum = heap.fold(0, |acc, x| acc + x);
+ assert_eq!(sum, 10);
+ }
+
+ #[test]
+ fn test_from_iter() {
+ let vec = vec![5, 2, 8, 1, 9, 3];
+ let heap = Heap::from_iter(vec.clone());
+
+ let mut sorted = vec;
+ sorted.sort();
+
+ assert_eq!(heap.to_sorted_vec(), sorted);
+ }
+}
--- /dev/null
+/// A compact binary min-heap (complete binary tree in a Vec).
+/// Functional/persistent API: many operations consume self and return a new heap.
+/// Internals are vector-based for cache efficiency.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct BinaryHeap<T> {
+ // Invariant: `data` is a complete binary tree stored level-order.
+ // Root at index 0. Parent of i is (i-1)/2. Children: 2*i+1, 2*i+2.
+ data: Vec<T>,
+}
+
+impl<T: Ord> BinaryHeap<T> {
+ /// Create an empty heap.
+ pub fn empty() -> Self {
+ Self { data: Vec::new() }
+ }
+
+ /// Create a heap from an existing Vec (consumes vec). Does NOT heapify.
+ /// Use `from_vec_heapify` or `from_slice` to build a heap.
+ pub fn from_vec(vec: Vec<T>) -> Self {
+ Self { data: vec }
+ }
+
+ /// Number of elements.
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ /// Is empty?
+ pub fn is_empty(&self) -> bool {
+ self.data.is_empty()
+ }
+
+ /// Peek min without removing.
+ pub fn peek_min(&self) -> Option<&T> {
+ self.data.get(0)
+ }
+
+ /// Pop the minimum, returning (min, new_heap). Consumes self (functional style).
+ pub fn pop_min(mut self) -> Option<(T, Self)> {
+ match self.data.len() {
+ 0 => None,
+ 1 => {
+ let v = self.data.pop().unwrap();
+ Some((v, Self::empty()))
+ }
+ n => {
+ // swap root with last, pop last (old root), then sift-down new root
+ self.data.swap(0, n - 1);
+ let min = self.data.pop().unwrap();
+ Self::sift_down_inplace(&mut self.data, 0);
+ Some((min, self))
+ }
+ }
+ }
+
+ /// Insert an element, returning a new heap (consumes self).
+ pub fn insert(mut self, item: T) -> Self {
+ let idx = self.data.len(); // immutable borrow ends here[1]
+ self.data.push(item);
+ Self::sift_up_inplace(&mut self.data, idx); // only mutable borrow now[1]
+ self
+ }
+
+ /// Replace the root with `item`, return (old_root, new_heap).
+ /// Useful to do a pop then push more efficiently (heapreplace).
+ pub fn replace_root(mut self, item: T) -> Option<(Vec<T>, Self)> {
+ if self.data.is_empty() {
+ // nothing to replace; equivalent to push
+ self.data.push(item);
+ None
+ } else {
+ let old = std::mem::replace(&mut self.data, vec![item]);
+ Self::sift_down_inplace(&mut self.data, 0);
+ Some((old, self))
+ }
+ }
+
+ /// Build a heap from an iterator (O(n log n) if inserting repeatedly).
+ /// Provided for completeness; prefer from_slice for O(n).
+ pub fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ iter.into_iter().fold(Self::empty(), |h, x| h.insert(x))
+ }
+
+ /// Heapify a Vec in-place (bottom-up) and return a heap. This is the O(n) algorithm.
+ /// This corresponds to the classical heapify/BUILD-MIN-HEAP algorithm.
+ pub fn from_vec_heapify(mut vec: Vec<T>) -> Self {
+ if vec.len() <= 1 {
+ return Self { data: vec };
+ }
+ let last_parent = (vec.len() - 2) / 2; // index of last internal node
+ for idx in (0..=last_parent).rev() {
+ Self::sift_down_inplace(&mut vec, idx);
+ }
+ Self { data: vec }
+ }
+
+ /// Heapify a slice by cloning into an owned Vec and running bottom-up heapify.
+ pub fn from_slice(slice: &[T]) -> Self
+ where
+ T: Clone,
+ {
+ let v: Vec<T> = slice.to_vec();
+ Self::from_vec_heapify(v)
+ }
+
+ // --- internal helpers (pure functions operating on Vec) ---
+
+ /// Sift up element at `pos` (restore heap property by swapping with parent while < parent).
+ /// Invariants: `data` is a valid heap except possibly at `pos` which may be smaller than parent.
+ fn sift_up_inplace(data: &mut [T], mut pos: usize) {
+ while pos > 0 {
+ let parent = (pos - 1) / 2;
+ if data[pos] < data[parent] {
+ data.swap(pos, parent);
+ pos = parent;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /// Sift down element at `pos` (restore heap property by swapping with smaller child).
+ /// Invariants: `data` is a valid heap except possibly at `pos` which may be greater than children.
+ fn sift_down_inplace(data: &mut [T], mut pos: usize) {
+ let len = data.len();
+ loop {
+ let left = 2 * pos + 1;
+ if left >= len {
+ break;
+ }
+ let right = left + 1;
+ // pick smaller child
+ let mut smallest = left;
+ if right < len && data[right] < data[left] {
+ smallest = right;
+ }
+ if data[smallest] < data[pos] {
+ data.swap(pos, smallest);
+ pos = smallest;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /// Expose the internal vector (consumes heap).
+ pub fn into_vec(self) -> Vec<T> {
+ self.data
+ }
+
+ /// Convert heap to a sorted Vec<T> by repeatedly popping min (non-in-place heap sort).
+ /// Consumes the heap.
+ pub fn into_sorted_vec(mut self) -> Vec<T> {
+ let mut out = Vec::with_capacity(self.len());
+ while let Some((min, rest)) = self.pop_min() {
+ out.push(min);
+ self = rest;
+ }
+ out
+ }
+}
+
+// Provide a small convenience: make a heap from a list literal
+impl<T: Ord> From<Vec<T>> for BinaryHeap<T> {
+ fn from(v: Vec<T>) -> Self {
+ BinaryHeap::from_vec_heapify(v)
+ }
+}