ic_stable_structures/min_heap.rs
1use crate::base_vec::{BaseVec, InitError};
2use crate::storable::Storable;
3use crate::{GrowFailed, Memory};
4use std::fmt;
5
6#[cfg(test)]
7mod tests;
8
9const MAGIC: [u8; 3] = *b"SMH"; // Short for "stable min heap".
10
11/// An implementation of the [binary min heap](https://en.wikipedia.org/wiki/Binary_heap).
12// NB. Contrary to [std::collections::BinaryHeap], this heap is a min-heap (smallest items come first).
13// Motivation: max heaps are helpful for sorting, but most daily programming tasks require min
14// heaps.
15pub struct MinHeap<T: Storable + PartialOrd, M: Memory>(BaseVec<T, M>);
16
17// Note: Heap Invariant
18// ~~~~~~~~~~~~~~~~~~~~
19//
20// HeapInvariant(heap, i, j) :=
21// ∀ k: i ≤ k ≤ j: LET p = (k - 1)/2 IN (p ≤ i) => heap[p] ≤ heap[k]
22
23impl<T, M> MinHeap<T, M>
24where
25 T: Storable + PartialOrd,
26 M: Memory,
27{
28 /// Creates a new empty heap in the specified memory,
29 /// overwriting any data structures the memory might have
30 /// contained.
31 ///
32 /// Complexity: O(1)
33 pub fn new(memory: M) -> Result<Self, GrowFailed> {
34 BaseVec::<T, M>::new(memory, MAGIC).map(Self)
35 }
36
37 /// Initializes a heap in the specified memory.
38 ///
39 /// Complexity: O(1)
40 ///
41 /// PRECONDITION: the memory is either empty or contains a valid
42 /// stable heap.
43 pub fn init(memory: M) -> Result<Self, InitError> {
44 BaseVec::<T, M>::init(memory, MAGIC).map(Self)
45 }
46
47 /// Returns the number of items in the heap.
48 ///
49 /// Complexity: O(1)
50 pub fn len(&self) -> u64 {
51 self.0.len()
52 }
53
54 /// Returns true if the heap is empty.
55 ///
56 /// Complexity: O(1)
57 pub fn is_empty(&self) -> bool {
58 self.0.is_empty()
59 }
60
61 /// Pushes an item onto the heap.
62 ///
63 /// Complexity: O(log(self.len()))
64 pub fn push(&mut self, item: &T) -> Result<(), GrowFailed> {
65 self.0.push(item)?;
66 self.bubble_up(self.0.len() - 1, item);
67 debug_assert_eq!(Ok(()), self.check_invariant());
68 Ok(())
69 }
70
71 /// Removes the smallest item from the heap and returns it.
72 /// Returns `None` if the heap is empty.
73 ///
74 /// Complexity: O(log(self.len()))
75 pub fn pop(&mut self) -> Option<T> {
76 let n = self.len();
77 match n {
78 0 => None,
79 1 => self.0.pop(),
80 _more => {
81 let smallest = self.0.get(0).unwrap();
82 let last = self.0.pop().unwrap();
83 self.0.set(0, &last);
84 self.bubble_down(0, n - 1, &last);
85 debug_assert_eq!(Ok(()), self.check_invariant());
86 Some(smallest)
87 }
88 }
89 }
90
91 /// Returns the smallest item in the heap.
92 /// Returns `None` if the heap is empty.
93 ///
94 /// Complexity: O(1)
95 pub fn peek(&self) -> Option<T> {
96 self.0.get(0)
97 }
98
99 /// Returns an iterator visiting all values in the underlying vector, in arbitrary order.
100 pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
101 self.0.iter()
102 }
103
104 /// Returns the underlying memory instance.
105 pub fn into_memory(self) -> M {
106 self.0.into_memory()
107 }
108
109 #[allow(dead_code)]
110 /// Checks the HeapInvariant(self, 0, self.len() - 1)
111 fn check_invariant(&self) -> Result<(), String> {
112 let n = self.len();
113 for i in 1..n {
114 let p = (i - 1) / 2;
115 let item = self.0.get(i).unwrap();
116 let parent = self.0.get(p).unwrap();
117 if is_less(&item, &parent) {
118 return Err(format!(
119 "Binary heap invariant violated in indices {i} and {p}"
120 ));
121 }
122 }
123 Ok(())
124 }
125
126 /// PRECONDITION: self.0.get(i) == item
127 fn bubble_up(&mut self, mut i: u64, item: &T) {
128 // We set the flag if self.0.get(i) does not contain the item anymore.
129 let mut swapped = false;
130 // LOOP INVARIANT: HeapInvariant(self, i, self.len() - 1)
131 while i > 0 {
132 let p = (i - 1) / 2;
133 let parent = self.0.get(p).unwrap();
134 if is_less(item, &parent) {
135 self.0.set(i, &parent);
136 swapped = true;
137 } else {
138 break;
139 }
140 i = p;
141 }
142 if swapped {
143 self.0.set(i, item);
144 }
145 }
146
147 /// PRECONDITION: self.0.get(i) == item
148 fn bubble_down(&mut self, mut i: u64, n: u64, item: &T) {
149 // We set the flag if self.0.get(i) does not contain the item anymore.
150 let mut swapped = false;
151 // LOOP INVARIANT: HeapInvariant(self, 0, i)
152 loop {
153 let l = i * 2 + 1;
154 let r = l + 1;
155
156 if n <= l {
157 break;
158 }
159
160 if n <= r {
161 // Only the left child is within the array bounds.
162
163 let left = self.0.get(l).unwrap();
164 if is_less(&left, item) {
165 self.0.set(i, &left);
166 swapped = true;
167 i = l;
168 continue;
169 }
170 } else {
171 // Both children are within the array bounds.
172
173 let left = self.0.get(l).unwrap();
174 let right = self.0.get(r).unwrap();
175
176 let (min_index, min_elem) = if is_less(&left, &right) {
177 (l, &left)
178 } else {
179 (r, &right)
180 };
181
182 if is_less(min_elem, item) {
183 self.0.set(i, min_elem);
184 swapped = true;
185 i = min_index;
186 continue;
187 }
188 }
189 break;
190 }
191 if swapped {
192 self.0.set(i, item);
193 }
194 }
195}
196
197fn is_less<T: PartialOrd>(x: &T, y: &T) -> bool {
198 x.partial_cmp(y) == Some(std::cmp::Ordering::Less)
199}
200
201impl<T, M> fmt::Debug for MinHeap<T, M>
202where
203 T: Storable + PartialOrd + fmt::Debug,
204 M: Memory,
205{
206 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
207 self.0.fmt(fmt)
208 }
209}