Skip to main content

subms_treap/
lib.rs

1//! Treap - probabilistic balanced BST.
2//!
3//! Each node carries a random priority. The tree is a BST on keys and a
4//! max-heap on priorities. Insert + delete rebalance via tree rotations.
5//! With uniform priorities the expected height is `O(log n)`.
6//!
7//! Nodes are stored in a contiguous `Vec<Node>` and referenced by `u32`
8//! indices (NULL = `u32::MAX`). This is the production-style memory
9//! layout: avoids one heap allocation per insert (the `Box::new(Node)`
10//! pattern), keeps nodes cache-dense, and lets the tree resize via
11//! `Vec::push` amortised O(1) instead of fragmenting the global heap.
12//!
13//! ```
14//! use subms_treap::Treap;
15//! let mut t: Treap<u32, &'static str> = Treap::new(42);
16//! t.insert(3, "three");
17//! t.insert(1, "one");
18//! t.insert(2, "two");
19//! assert_eq!(t.get(&2).copied(), Some("two"));
20//! assert_eq!(t.len(), 3);
21//! assert_eq!(t.remove(&1), Some("one"));
22//! assert_eq!(t.len(), 2);
23//! ```
24
25use std::cmp::Ordering;
26
27pub(crate) const NIL: u32 = u32::MAX;
28
29pub struct Treap<K, V> {
30    pub(crate) nodes: Vec<Node<K, V>>,
31    // Singly-linked free list. Head index, or NIL when empty.
32    // Reuses slots vacated by `remove()` so the Vec stops growing under
33    // an insert/remove churn workload.
34    free_head: u32,
35    pub(crate) root: u32,
36    len: usize,
37    rng_state: u64,
38}
39
40pub(crate) struct Node<K, V> {
41    pub(crate) key: K,
42    pub(crate) value: V,
43    pub(crate) priority: u64,
44    pub(crate) left: u32,
45    pub(crate) right: u32,
46}
47
48impl<K: Ord, V> Treap<K, V> {
49    pub fn new(seed: u64) -> Self {
50        Self {
51            nodes: Vec::new(),
52            free_head: NIL,
53            root: NIL,
54            len: 0,
55            rng_state: seed | 1,
56        }
57    }
58
59    /// Construct with capacity pre-allocated. Use when an upper bound on
60    /// the working set is known: avoids the doubling-vec growth path
61    /// during the first burst of inserts.
62    pub fn with_capacity(seed: u64, capacity: usize) -> Self {
63        Self {
64            nodes: Vec::with_capacity(capacity),
65            free_head: NIL,
66            root: NIL,
67            len: 0,
68            rng_state: seed | 1,
69        }
70    }
71
72    pub fn len(&self) -> usize {
73        self.len
74    }
75    pub fn is_empty(&self) -> bool {
76        self.len == 0
77    }
78
79    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
80        let priority = self.next_priority();
81        let (new_root, replaced) = self.ins(self.root, key, value, priority);
82        self.root = new_root;
83        if replaced.is_none() {
84            self.len += 1;
85        }
86        replaced
87    }
88
89    pub fn get(&self, key: &K) -> Option<&V> {
90        let mut cur = self.root;
91        while cur != NIL {
92            let node = &self.nodes[cur as usize];
93            match key.cmp(&node.key) {
94                Ordering::Less => cur = node.left,
95                Ordering::Greater => cur = node.right,
96                Ordering::Equal => return Some(&node.value),
97            }
98        }
99        None
100    }
101
102    pub fn remove(&mut self, key: &K) -> Option<V> {
103        let (new_root, removed) = self.rem(self.root, key);
104        self.root = new_root;
105        if removed.is_some() {
106            self.len -= 1;
107        }
108        removed
109    }
110
111    /// In-order traversal; pushes `(key, value)` references into a Vec.
112    pub fn collect_in_order(&self) -> Vec<(&K, &V)> {
113        let mut out = Vec::with_capacity(self.len);
114        self.in_order(self.root, &mut out);
115        out
116    }
117
118    fn next_priority(&mut self) -> u64 {
119        // LCG step: same constants as subms::SubMsLcg.
120        self.rng_state = self
121            .rng_state
122            .wrapping_mul(6364136223846793005)
123            .wrapping_add(1442695040888963407);
124        // SplitMix64 finalizer. The bare LCG state stays correlated with
125        // any sibling LCG-derived stream - including keys generated from
126        // the same family of constants - and a priority correlated with
127        // the key sorts the treap into a spine (O(n) depth). The avalanche
128        // decorrelates the priority from the key so the heap invariant
129        // produces the random shape the O(log n) bound assumes. Same
130        // fix the hyperloglog recipe applies to FNV-1a output.
131        let mut z = self.rng_state;
132        z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
133        z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
134        z ^ (z >> 31)
135    }
136
137    fn alloc(&mut self, key: K, value: V, priority: u64) -> u32 {
138        if self.free_head != NIL {
139            let idx = self.free_head;
140            let slot = &mut self.nodes[idx as usize];
141            // free-list link was stored in `left` while the slot was free
142            self.free_head = slot.left;
143            slot.key = key;
144            slot.value = value;
145            slot.priority = priority;
146            slot.left = NIL;
147            slot.right = NIL;
148            idx
149        } else {
150            let idx = self.nodes.len() as u32;
151            self.nodes.push(Node {
152                key,
153                value,
154                priority,
155                left: NIL,
156                right: NIL,
157            });
158            idx
159        }
160    }
161
162    fn free(&mut self, idx: u32) {
163        self.nodes[idx as usize].left = self.free_head;
164        self.free_head = idx;
165    }
166
167    fn ins(&mut self, root: u32, key: K, value: V, priority: u64) -> (u32, Option<V>) {
168        if root == NIL {
169            return (self.alloc(key, value, priority), None);
170        }
171        let cmp = key.cmp(&self.nodes[root as usize].key);
172        match cmp {
173            Ordering::Equal => {
174                let old = std::mem::replace(&mut self.nodes[root as usize].value, value);
175                (root, Some(old))
176            }
177            Ordering::Less => {
178                let left = self.nodes[root as usize].left;
179                let (new_left, replaced) = self.ins(left, key, value, priority);
180                self.nodes[root as usize].left = new_left;
181                let new_left_pri = self.nodes[new_left as usize].priority;
182                let root_pri = self.nodes[root as usize].priority;
183                let r = if new_left_pri > root_pri {
184                    self.rotate_right(root)
185                } else {
186                    root
187                };
188                (r, replaced)
189            }
190            Ordering::Greater => {
191                let right = self.nodes[root as usize].right;
192                let (new_right, replaced) = self.ins(right, key, value, priority);
193                self.nodes[root as usize].right = new_right;
194                let new_right_pri = self.nodes[new_right as usize].priority;
195                let root_pri = self.nodes[root as usize].priority;
196                let r = if new_right_pri > root_pri {
197                    self.rotate_left(root)
198                } else {
199                    root
200                };
201                (r, replaced)
202            }
203        }
204    }
205
206    fn rem(&mut self, root: u32, key: &K) -> (u32, Option<V>) {
207        if root == NIL {
208            return (NIL, None);
209        }
210        let cmp = key.cmp(&self.nodes[root as usize].key);
211        match cmp {
212            Ordering::Less => {
213                let left = self.nodes[root as usize].left;
214                let (new_left, removed) = self.rem(left, key);
215                self.nodes[root as usize].left = new_left;
216                (root, removed)
217            }
218            Ordering::Greater => {
219                let right = self.nodes[root as usize].right;
220                let (new_right, removed) = self.rem(right, key);
221                self.nodes[root as usize].right = new_right;
222                (root, removed)
223            }
224            Ordering::Equal => {
225                let left = self.nodes[root as usize].left;
226                let right = self.nodes[root as usize].right;
227                let value = unsafe {
228                    // value moved out; the slot is about to go on the
229                    // free list - safe because `free` doesn't read it.
230                    std::ptr::read(&self.nodes[root as usize].value)
231                };
232                let merged = self.merge_subtrees(left, right);
233                self.free(root);
234                (merged, Some(value))
235            }
236        }
237    }
238
239    fn merge_subtrees(&mut self, left: u32, right: u32) -> u32 {
240        if left == NIL {
241            return right;
242        }
243        if right == NIL {
244            return left;
245        }
246        let l_pri = self.nodes[left as usize].priority;
247        let r_pri = self.nodes[right as usize].priority;
248        if l_pri > r_pri {
249            let l_right = self.nodes[left as usize].right;
250            let merged = self.merge_subtrees(l_right, right);
251            self.nodes[left as usize].right = merged;
252            left
253        } else {
254            let r_left = self.nodes[right as usize].left;
255            let merged = self.merge_subtrees(left, r_left);
256            self.nodes[right as usize].left = merged;
257            right
258        }
259    }
260
261    fn rotate_right(&mut self, idx: u32) -> u32 {
262        let left = self.nodes[idx as usize].left;
263        debug_assert!(left != NIL, "rotate_right requires left child");
264        let left_right = self.nodes[left as usize].right;
265        self.nodes[idx as usize].left = left_right;
266        self.nodes[left as usize].right = idx;
267        left
268    }
269
270    fn rotate_left(&mut self, idx: u32) -> u32 {
271        let right = self.nodes[idx as usize].right;
272        debug_assert!(right != NIL, "rotate_left requires right child");
273        let right_left = self.nodes[right as usize].left;
274        self.nodes[idx as usize].right = right_left;
275        self.nodes[right as usize].left = idx;
276        right
277    }
278
279    fn in_order<'a>(&'a self, idx: u32, out: &mut Vec<(&'a K, &'a V)>) {
280        if idx == NIL {
281            return;
282        }
283        let node = &self.nodes[idx as usize];
284        self.in_order(node.left, out);
285        out.push((&node.key, &node.value));
286        self.in_order(node.right, out);
287    }
288}
289
290#[cfg(feature = "harness")]
291pub mod recipe;
292
293// Opt-in feature catalog. Each submodule is gated by its own Cargo
294// feature flag. See `Cargo.toml` `[features]` and the cookbook page
295// for per-feature semantics + p99 impact.
296#[cfg(any(
297    feature = "range-query",
298    feature = "persistent",
299    feature = "merge-split",
300    feature = "concurrent-reads",
301))]
302pub mod features;
303
304#[cfg(feature = "concurrent-reads")]
305pub use features::concurrent_reads::TreapSnapshot;
306#[cfg(feature = "merge-split")]
307pub use features::merge_split::SplittableTreap;
308#[cfg(feature = "persistent")]
309pub use features::persistent::PersistentTreap;
310#[cfg(feature = "range-query")]
311pub use features::range_query::{RangeBound, RangeIter};