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//! // Ordered navigation, no feature flag needed.
25//! assert_eq!(t.first().map(|(k, _)| *k), Some(2));
26//! assert_eq!(t.ceiling(&3).map(|(k, _)| *k), Some(3));
27//! assert_eq!(t.predecessor(&3).map(|(k, _)| *k), Some(2));
28//! assert_eq!(t.iter().map(|(k, _)| *k).collect::<Vec<_>>(), vec![2, 3]);
29//! ```
30//!
31//! Full writeup, design notes and measured benchmarks:
32//! <https://www.submillisecond.com/cookbook/recipes/subms-treap>
33
34use std::cmp::Ordering;
35use std::fmt;
36use std::mem::ManuallyDrop;
37
38pub(crate) const NIL: u32 = u32::MAX;
39
40// Parked in `right` on a vacated slot. `Drop` and `clear` need to tell a slot
41// whose payload has already been moved out from a live one, and the arena can
42// never reach this index: `u32::MAX - 1` nodes is far past the address space a
43// `Vec<Node>` can hold.
44const FREE: u32 = u32::MAX - 1;
45
46/// The one fallible operation's error.
47///
48/// Every other method on `Treap` is total: lookups return `Option`, removals
49/// of an absent key are a no-op, and there is no capacity to exhaust short of
50/// the allocator failing.
51#[derive(Debug, Clone, PartialEq, Eq)]
52#[non_exhaustive]
53pub enum TreapError {
54    /// `from_sorted` received input that is not strictly ascending. `index` is
55    /// the position of the offending item.
56    UnsortedInput { index: usize },
57    /// `join` was handed two treaps whose key ranges overlap. Joining them
58    /// would break the BST invariant, so the operation is refused and both
59    /// treaps are left untouched.
60    OverlappingRange,
61}
62
63impl fmt::Display for TreapError {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        match self {
66            TreapError::UnsortedInput { index } => {
67                write!(
68                    f,
69                    "from_sorted input not strictly ascending at index {index}"
70                )
71            }
72            TreapError::OverlappingRange => {
73                write!(
74                    f,
75                    "join requires every key on the left below every key on the right"
76                )
77            }
78        }
79    }
80}
81
82impl std::error::Error for TreapError {}
83
84pub struct Treap<K, V> {
85    pub(crate) nodes: Vec<Node<K, V>>,
86    // Singly-linked free list. Head index, or NIL when empty.
87    // Reuses slots vacated by `remove()` so the Vec stops growing under
88    // an insert/remove churn workload.
89    free_head: u32,
90    pub(crate) root: u32,
91    len: usize,
92    rng_state: u64,
93}
94
95pub(crate) struct Node<K, V> {
96    // ManuallyDrop is what makes slot reuse sound: a vacated slot has had its
97    // payload moved out, so assigning over it must not run the old value's
98    // destructor. `Drop for Treap` then drops exactly the live slots. Layout
99    // is identical to a bare `K`/`V`, so the arena stays as dense as it looks.
100    pub(crate) key: ManuallyDrop<K>,
101    pub(crate) value: ManuallyDrop<V>,
102    pub(crate) priority: u64,
103    pub(crate) left: u32,
104    pub(crate) right: u32,
105}
106
107impl<K: Ord, V> Treap<K, V> {
108    pub fn new(seed: u64) -> Self {
109        Self {
110            nodes: Vec::new(),
111            free_head: NIL,
112            root: NIL,
113            len: 0,
114            rng_state: seed | 1,
115        }
116    }
117
118    /// Construct with capacity pre-allocated. Use when an upper bound on
119    /// the working set is known: avoids the doubling-vec growth path
120    /// during the first burst of inserts.
121    pub fn with_capacity(seed: u64, capacity: usize) -> Self {
122        Self {
123            nodes: Vec::with_capacity(capacity),
124            free_head: NIL,
125            root: NIL,
126            len: 0,
127            rng_state: seed | 1,
128        }
129    }
130
131    /// Seed the priority stream from the OS rather than from a constant.
132    ///
133    /// The default constructor takes an explicit seed because a reproducible
134    /// tree shape is what makes a benchmark and a bug report mean anything.
135    /// That same property is a liability when an attacker can both choose the
136    /// keys and observe the latency: the priority sequence is then known, and
137    /// a chosen key order can force the spine the randomized bound rules out.
138    /// Reach for this when the key stream is untrusted, and accept that two
139    /// runs no longer produce the same tree.
140    pub fn from_entropy() -> Self {
141        use std::hash::{BuildHasher, Hasher, RandomState};
142        // std has no RNG, but RandomState is seeded by the OS per instance,
143        // which is exactly the one bit of entropy needed here.
144        Self::new(RandomState::new().build_hasher().finish())
145    }
146
147    /// Build from already-sorted input in `O(n)`, skipping the `n` rotating
148    /// inserts a naive rebuild would pay.
149    ///
150    /// Keys must be strictly ascending; duplicates are rejected rather than
151    /// collapsed, because silently dropping one of two entries is the wrong
152    /// answer for every workload that reaches for this. Pairs with
153    /// [`Treap::collect_in_order`] as a snapshot / restore round trip.
154    ///
155    /// ```
156    /// use subms_treap::Treap;
157    /// let t = Treap::from_sorted(1, [(1u32, "a"), (2, "b"), (3, "c")]).unwrap();
158    /// assert_eq!(t.len(), 3);
159    /// assert_eq!(t.get(&2).copied(), Some("b"));
160    /// ```
161    pub fn from_sorted(
162        seed: u64,
163        items: impl IntoIterator<Item = (K, V)>,
164    ) -> Result<Self, TreapError> {
165        let iter = items.into_iter();
166        let mut t = Self::with_capacity(seed, iter.size_hint().0);
167        // Right spine of the tree built so far, priorities descending from the
168        // root. Every new key exceeds everything already placed, so it can only
169        // enter along that spine - which is the Cartesian-tree construction.
170        let mut spine: Vec<u32> = Vec::new();
171        for (index, (key, value)) in iter.enumerate() {
172            if let Some(&prev) = spine.last()
173                && t.key_at(prev) >= &key
174            {
175                return Err(TreapError::UnsortedInput { index });
176            }
177            let priority = t.next_priority();
178            let idx = t.alloc(key, value, priority);
179            let mut demoted = NIL;
180            while let Some(&top) = spine.last() {
181                if t.nodes[top as usize].priority < priority {
182                    demoted = spine.pop().unwrap();
183                } else {
184                    break;
185                }
186            }
187            t.nodes[idx as usize].left = demoted;
188            match spine.last() {
189                Some(&top) => t.nodes[top as usize].right = idx,
190                None => t.root = idx,
191            }
192            spine.push(idx);
193            t.len += 1;
194        }
195        Ok(t)
196    }
197
198    pub fn len(&self) -> usize {
199        self.len
200    }
201    pub fn is_empty(&self) -> bool {
202        self.len == 0
203    }
204
205    /// Longest root-to-leaf path in edges; `0` for an empty or single-node
206    /// tree. The randomized-priority bound puts this near `3 * ln(n)` in
207    /// expectation, so it is the cheapest way to see whether the priority
208    /// stream is doing its job on real keys.
209    pub fn height(&self) -> usize {
210        let mut best = 0;
211        let mut stack = vec![(self.root, 0usize)];
212        while let Some((idx, depth)) = stack.pop() {
213            if idx == NIL {
214                continue;
215            }
216            best = best.max(depth);
217            let node = &self.nodes[idx as usize];
218            stack.push((node.left, depth + 1));
219            stack.push((node.right, depth + 1));
220        }
221        best
222    }
223
224    /// Drop every entry and reset to empty, keeping the arena's capacity so a
225    /// rebuild does not pay the growth path again.
226    pub fn clear(&mut self) {
227        self.drop_live_payloads();
228        self.nodes.clear();
229        self.free_head = NIL;
230        self.root = NIL;
231        self.len = 0;
232    }
233
234    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
235        let priority = self.next_priority();
236        let (new_root, replaced) = self.ins(self.root, key, value, priority);
237        self.root = new_root;
238        if replaced.is_none() {
239            self.len += 1;
240        }
241        replaced
242    }
243
244    pub fn get(&self, key: &K) -> Option<&V> {
245        let idx = self.find(key);
246        (idx != NIL).then(|| &*self.nodes[idx as usize].value)
247    }
248
249    /// Mutable access to a resting value. The amend path for a price level:
250    /// no re-descent through `insert`, no priority redraw, no rotation.
251    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
252        let idx = self.find(key);
253        (idx != NIL).then(|| &mut *self.nodes[idx as usize].value)
254    }
255
256    pub fn contains_key(&self, key: &K) -> bool {
257        self.find(key) != NIL
258    }
259
260    pub fn remove(&mut self, key: &K) -> Option<V> {
261        let (new_root, removed) = self.rem(self.root, key);
262        self.root = new_root;
263        if removed.is_some() {
264            self.len -= 1;
265        }
266        removed
267    }
268
269    /// Smallest key and its value.
270    pub fn first(&self) -> Option<(&K, &V)> {
271        self.spine_end(false).map(|idx| self.entry_at(idx))
272    }
273
274    /// Largest key and its value.
275    pub fn last(&self) -> Option<(&K, &V)> {
276        self.spine_end(true).map(|idx| self.entry_at(idx))
277    }
278
279    /// Greatest key `<= key`.
280    pub fn floor(&self, key: &K) -> Option<(&K, &V)> {
281        self.search_le(key, false).map(|idx| self.entry_at(idx))
282    }
283
284    /// Least key `>= key`.
285    pub fn ceiling(&self, key: &K) -> Option<(&K, &V)> {
286        self.search_ge(key, false).map(|idx| self.entry_at(idx))
287    }
288
289    /// Greatest key strictly `< key`.
290    pub fn predecessor(&self, key: &K) -> Option<(&K, &V)> {
291        self.search_le(key, true).map(|idx| self.entry_at(idx))
292    }
293
294    /// Least key strictly `> key`.
295    pub fn successor(&self, key: &K) -> Option<(&K, &V)> {
296        self.search_ge(key, true).map(|idx| self.entry_at(idx))
297    }
298
299    /// Remove and return the smallest entry. The top-of-book sweep.
300    pub fn pop_first(&mut self) -> Option<(K, V)> {
301        let (new_root, popped) = self.pop_extreme(self.root, false);
302        self.root = new_root;
303        if popped.is_some() {
304            self.len -= 1;
305        }
306        popped
307    }
308
309    /// Remove and return the largest entry.
310    pub fn pop_last(&mut self) -> Option<(K, V)> {
311        let (new_root, popped) = self.pop_extreme(self.root, true);
312        self.root = new_root;
313        if popped.is_some() {
314            self.len -= 1;
315        }
316        popped
317    }
318
319    /// Cut the treap at `pivot`, keeping everything below it and returning
320    /// everything at or above it.
321    ///
322    /// The cut itself is the treap's distinguishing operation against a
323    /// red-black tree: one descent, expected `O(log n)`, no rebalancing pass.
324    /// The arena then charges for what it buys elsewhere - the upper half's
325    /// `m` nodes are relocated into their own arena, so the whole call is
326    /// expected `O(log n) + O(m)`. Where that relocation matters, the
327    /// `merge-split` feature's `SplittableTreap` is the pointer-backed variant
328    /// that hands the detached subtree over without touching it.
329    ///
330    /// ```
331    /// use subms_treap::Treap;
332    /// let mut book: Treap<u32, u64> = Treap::new(7);
333    /// for px in [9998u32, 9999, 10_000, 10_001] { book.insert(px, 100); }
334    /// let marketable = book.split_off(&10_000);
335    /// assert_eq!(book.len(), 2);
336    /// assert_eq!(marketable.len(), 2);
337    /// assert_eq!(marketable.first().map(|(k, _)| *k), Some(10_000));
338    /// ```
339    pub fn split_off(&mut self, pivot: &K) -> Self {
340        let (lo, hi) = self.split_node(self.root, pivot);
341        self.root = lo;
342        let mut upper = Self::new(self.rng_state ^ 0x9e3779b97f4a7c15);
343        let (new_root, moved) = upper.absorb_subtree(self, hi);
344        upper.root = new_root;
345        upper.len = moved;
346        self.len -= moved;
347        upper
348    }
349
350    /// Splice `other` onto the end of `self`. Every key in `self` must be
351    /// strictly below every key in `other`.
352    ///
353    /// The splice is expected `O(log n)`; as with [`Treap::split_off`], moving
354    /// `other`'s `m` nodes into this arena adds `O(m)`. An overlapping range is
355    /// refused rather than silently corrupting the BST invariant, and both
356    /// treaps are left as they were.
357    pub fn join(&mut self, mut other: Self) -> Result<(), TreapError> {
358        let overlaps = match (self.last(), other.first()) {
359            (Some((l, _)), Some((r, _))) => l >= r,
360            _ => false,
361        };
362        if overlaps {
363            return Err(TreapError::OverlappingRange);
364        }
365        let other_root = other.root;
366        let (moved_root, moved) = self.absorb_subtree(&mut other, other_root);
367        other.root = NIL;
368        other.len = 0;
369        self.root = self.merge_subtrees(self.root, moved_root);
370        self.len += moved;
371        Ok(())
372    }
373
374    /// Ascending in-order iteration. Lazy: the only allocation is the
375    /// traversal stack, sized to the tree's height.
376    pub fn iter(&self) -> Iter<'_, K, V> {
377        let mut it = Iter {
378            treap: self,
379            stack: Vec::new(),
380        };
381        it.push_left(self.root);
382        it
383    }
384
385    /// Descending in-order iteration. A bid ladder is read best price first,
386    /// which is the reverse of the stored order.
387    pub fn iter_rev(&self) -> IterRev<'_, K, V> {
388        let mut it = IterRev {
389            treap: self,
390            stack: Vec::new(),
391        };
392        it.push_right(self.root);
393        it
394    }
395
396    /// In-order traversal; pushes `(key, value)` references into a Vec.
397    pub fn collect_in_order(&self) -> Vec<(&K, &V)> {
398        self.iter().collect()
399    }
400
401    fn find(&self, key: &K) -> u32 {
402        let mut cur = self.root;
403        while cur != NIL {
404            let node = &self.nodes[cur as usize];
405            match key.cmp(&node.key) {
406                Ordering::Less => cur = node.left,
407                Ordering::Greater => cur = node.right,
408                Ordering::Equal => return cur,
409            }
410        }
411        NIL
412    }
413
414    fn spine_end(&self, rightmost: bool) -> Option<u32> {
415        let mut cur = self.root;
416        if cur == NIL {
417            return None;
418        }
419        loop {
420            let node = &self.nodes[cur as usize];
421            let next = if rightmost { node.right } else { node.left };
422            if next == NIL {
423                return Some(cur);
424            }
425            cur = next;
426        }
427    }
428
429    fn search_le(&self, key: &K, strict: bool) -> Option<u32> {
430        let mut cur = self.root;
431        let mut best = NIL;
432        while cur != NIL {
433            let node = &self.nodes[cur as usize];
434            let ok = if strict {
435                *node.key < *key
436            } else {
437                *node.key <= *key
438            };
439            if ok {
440                best = cur;
441                cur = node.right;
442            } else {
443                cur = node.left;
444            }
445        }
446        (best != NIL).then_some(best)
447    }
448
449    fn search_ge(&self, key: &K, strict: bool) -> Option<u32> {
450        let mut cur = self.root;
451        let mut best = NIL;
452        while cur != NIL {
453            let node = &self.nodes[cur as usize];
454            let ok = if strict {
455                *node.key > *key
456            } else {
457                *node.key >= *key
458            };
459            if ok {
460                best = cur;
461                cur = node.left;
462            } else {
463                cur = node.right;
464            }
465        }
466        (best != NIL).then_some(best)
467    }
468
469    fn entry_at(&self, idx: u32) -> (&K, &V) {
470        let node = &self.nodes[idx as usize];
471        (&node.key, &node.value)
472    }
473
474    pub(crate) fn key_at(&self, idx: u32) -> &K {
475        &self.nodes[idx as usize].key
476    }
477
478    fn pop_extreme(&mut self, root: u32, rightmost: bool) -> (u32, Option<(K, V)>) {
479        if root == NIL {
480            return (NIL, None);
481        }
482        let node = &self.nodes[root as usize];
483        let next = if rightmost { node.right } else { node.left };
484        if next == NIL {
485            let other = if rightmost { node.left } else { node.right };
486            let payload = self.take_payload(root);
487            self.free(root);
488            return (other, Some(payload));
489        }
490        let (new_child, popped) = self.pop_extreme(next, rightmost);
491        if rightmost {
492            self.nodes[root as usize].right = new_child;
493        } else {
494            self.nodes[root as usize].left = new_child;
495        }
496        (root, popped)
497    }
498
499    /// Partition the subtree at `idx` into keys below `pivot` and keys at or
500    /// above it. One descent, no rebalancing: the heap invariant survives
501    /// because neither half ever gains an ancestor it did not already have.
502    fn split_node(&mut self, idx: u32, pivot: &K) -> (u32, u32) {
503        if idx == NIL {
504            return (NIL, NIL);
505        }
506        if self.key_at(idx) < pivot {
507            let right = self.nodes[idx as usize].right;
508            let (lo_right, hi) = self.split_node(right, pivot);
509            self.nodes[idx as usize].right = lo_right;
510            (idx, hi)
511        } else {
512            let left = self.nodes[idx as usize].left;
513            let (lo, hi_left) = self.split_node(left, pivot);
514            self.nodes[idx as usize].left = hi_left;
515            (lo, idx)
516        }
517    }
518
519    /// Move a subtree out of `src`'s arena and into this one, priorities and
520    /// shape intact. Returns the new root and the node count.
521    fn absorb_subtree(&mut self, src: &mut Self, idx: u32) -> (u32, usize) {
522        if idx == NIL {
523            return (NIL, 0);
524        }
525        let (left, right, priority) = {
526            let node = &src.nodes[idx as usize];
527            (node.left, node.right, node.priority)
528        };
529        let (new_left, left_n) = self.absorb_subtree(src, left);
530        let (new_right, right_n) = self.absorb_subtree(src, right);
531        let (key, value) = src.take_payload(idx);
532        src.free(idx);
533        let new_idx = self.alloc(key, value, priority);
534        self.nodes[new_idx as usize].left = new_left;
535        self.nodes[new_idx as usize].right = new_right;
536        (new_idx, left_n + right_n + 1)
537    }
538
539    fn next_priority(&mut self) -> u64 {
540        // LCG step: same constants as subms::SubMsLcg.
541        self.rng_state = self
542            .rng_state
543            .wrapping_mul(6364136223846793005)
544            .wrapping_add(1442695040888963407);
545        // SplitMix64 finalizer. The bare LCG state stays correlated with
546        // any sibling LCG-derived stream - including keys generated from
547        // the same family of constants - and a priority correlated with
548        // the key sorts the treap into a spine (O(n) depth). The avalanche
549        // decorrelates the priority from the key so the heap invariant
550        // produces the random shape the O(log n) bound assumes. Same
551        // fix the hyperloglog recipe applies to FNV-1a output.
552        let mut z = self.rng_state;
553        z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
554        z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
555        z ^ (z >> 31)
556    }
557
558    fn alloc(&mut self, key: K, value: V, priority: u64) -> u32 {
559        if self.free_head != NIL {
560            let idx = self.free_head;
561            let slot = &mut self.nodes[idx as usize];
562            // free-list link was stored in `left` while the slot was free
563            self.free_head = slot.left;
564            slot.key = ManuallyDrop::new(key);
565            slot.value = ManuallyDrop::new(value);
566            slot.priority = priority;
567            slot.left = NIL;
568            slot.right = NIL;
569            idx
570        } else {
571            let idx = self.nodes.len() as u32;
572            self.nodes.push(Node {
573                key: ManuallyDrop::new(key),
574                value: ManuallyDrop::new(value),
575                priority,
576                left: NIL,
577                right: NIL,
578            });
579            idx
580        }
581    }
582
583    fn take_payload(&mut self, idx: u32) -> (K, V) {
584        let node = &mut self.nodes[idx as usize];
585        unsafe {
586            (
587                ManuallyDrop::take(&mut node.key),
588                ManuallyDrop::take(&mut node.value),
589            )
590        }
591    }
592
593    fn free(&mut self, idx: u32) {
594        let head = self.free_head;
595        let node = &mut self.nodes[idx as usize];
596        node.left = head;
597        node.right = FREE;
598        self.free_head = idx;
599    }
600
601    fn drop_live_payloads(&mut self) {
602        for node in &mut self.nodes {
603            if node.right != FREE {
604                unsafe {
605                    ManuallyDrop::drop(&mut node.key);
606                    ManuallyDrop::drop(&mut node.value);
607                }
608            }
609        }
610    }
611
612    fn ins(&mut self, root: u32, key: K, value: V, priority: u64) -> (u32, Option<V>) {
613        if root == NIL {
614            return (self.alloc(key, value, priority), None);
615        }
616        let cmp = key.cmp(&self.nodes[root as usize].key);
617        match cmp {
618            Ordering::Equal => {
619                let old = std::mem::replace(
620                    &mut self.nodes[root as usize].value,
621                    ManuallyDrop::new(value),
622                );
623                (root, Some(ManuallyDrop::into_inner(old)))
624            }
625            Ordering::Less => {
626                let left = self.nodes[root as usize].left;
627                let (new_left, replaced) = self.ins(left, key, value, priority);
628                self.nodes[root as usize].left = new_left;
629                let new_left_pri = self.nodes[new_left as usize].priority;
630                let root_pri = self.nodes[root as usize].priority;
631                let r = if new_left_pri > root_pri {
632                    self.rotate_right(root)
633                } else {
634                    root
635                };
636                (r, replaced)
637            }
638            Ordering::Greater => {
639                let right = self.nodes[root as usize].right;
640                let (new_right, replaced) = self.ins(right, key, value, priority);
641                self.nodes[root as usize].right = new_right;
642                let new_right_pri = self.nodes[new_right as usize].priority;
643                let root_pri = self.nodes[root as usize].priority;
644                let r = if new_right_pri > root_pri {
645                    self.rotate_left(root)
646                } else {
647                    root
648                };
649                (r, replaced)
650            }
651        }
652    }
653
654    fn rem(&mut self, root: u32, key: &K) -> (u32, Option<V>) {
655        if root == NIL {
656            return (NIL, None);
657        }
658        let cmp = key.cmp(&self.nodes[root as usize].key);
659        match cmp {
660            Ordering::Less => {
661                let left = self.nodes[root as usize].left;
662                let (new_left, removed) = self.rem(left, key);
663                self.nodes[root as usize].left = new_left;
664                (root, removed)
665            }
666            Ordering::Greater => {
667                let right = self.nodes[root as usize].right;
668                let (new_right, removed) = self.rem(right, key);
669                self.nodes[root as usize].right = new_right;
670                (root, removed)
671            }
672            Ordering::Equal => {
673                let left = self.nodes[root as usize].left;
674                let right = self.nodes[root as usize].right;
675                let (key, value) = self.take_payload(root);
676                drop(key);
677                let merged = self.merge_subtrees(left, right);
678                self.free(root);
679                (merged, Some(value))
680            }
681        }
682    }
683
684    fn merge_subtrees(&mut self, left: u32, right: u32) -> u32 {
685        if left == NIL {
686            return right;
687        }
688        if right == NIL {
689            return left;
690        }
691        let l_pri = self.nodes[left as usize].priority;
692        let r_pri = self.nodes[right as usize].priority;
693        if l_pri > r_pri {
694            let l_right = self.nodes[left as usize].right;
695            let merged = self.merge_subtrees(l_right, right);
696            self.nodes[left as usize].right = merged;
697            left
698        } else {
699            let r_left = self.nodes[right as usize].left;
700            let merged = self.merge_subtrees(left, r_left);
701            self.nodes[right as usize].left = merged;
702            right
703        }
704    }
705
706    fn rotate_right(&mut self, idx: u32) -> u32 {
707        let left = self.nodes[idx as usize].left;
708        debug_assert!(left != NIL, "rotate_right requires left child");
709        let left_right = self.nodes[left as usize].right;
710        self.nodes[idx as usize].left = left_right;
711        self.nodes[left as usize].right = idx;
712        left
713    }
714
715    fn rotate_left(&mut self, idx: u32) -> u32 {
716        let right = self.nodes[idx as usize].right;
717        debug_assert!(right != NIL, "rotate_left requires right child");
718        let right_left = self.nodes[right as usize].left;
719        self.nodes[idx as usize].right = right_left;
720        self.nodes[right as usize].left = idx;
721        right
722    }
723}
724
725impl<K, V> Drop for Treap<K, V> {
726    fn drop(&mut self) {
727        for node in &mut self.nodes {
728            if node.right != FREE {
729                unsafe {
730                    ManuallyDrop::drop(&mut node.key);
731                    ManuallyDrop::drop(&mut node.value);
732                }
733            }
734        }
735    }
736}
737
738impl<K: Ord + fmt::Debug, V: fmt::Debug> fmt::Debug for Treap<K, V> {
739    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
740        f.debug_map().entries(self.iter()).finish()
741    }
742}
743
744impl<'a, K: Ord, V> IntoIterator for &'a Treap<K, V> {
745    type Item = (&'a K, &'a V);
746    type IntoIter = Iter<'a, K, V>;
747
748    fn into_iter(self) -> Self::IntoIter {
749        self.iter()
750    }
751}
752
753/// Ascending in-order iterator. See [`Treap::iter`].
754pub struct Iter<'a, K, V> {
755    treap: &'a Treap<K, V>,
756    stack: Vec<u32>,
757}
758
759impl<K, V> Iter<'_, K, V> {
760    fn push_left(&mut self, mut idx: u32) {
761        while idx != NIL {
762            self.stack.push(idx);
763            idx = self.treap.nodes[idx as usize].left;
764        }
765    }
766}
767
768impl<'a, K, V> Iterator for Iter<'a, K, V> {
769    type Item = (&'a K, &'a V);
770
771    fn next(&mut self) -> Option<Self::Item> {
772        let idx = self.stack.pop()?;
773        let node = &self.treap.nodes[idx as usize];
774        self.push_left(node.right);
775        Some((&node.key, &node.value))
776    }
777}
778
779/// Descending in-order iterator. See [`Treap::iter_rev`].
780pub struct IterRev<'a, K, V> {
781    treap: &'a Treap<K, V>,
782    stack: Vec<u32>,
783}
784
785impl<K, V> IterRev<'_, K, V> {
786    fn push_right(&mut self, mut idx: u32) {
787        while idx != NIL {
788            self.stack.push(idx);
789            idx = self.treap.nodes[idx as usize].right;
790        }
791    }
792}
793
794impl<'a, K, V> Iterator for IterRev<'a, K, V> {
795    type Item = (&'a K, &'a V);
796
797    fn next(&mut self) -> Option<Self::Item> {
798        let idx = self.stack.pop()?;
799        let node = &self.treap.nodes[idx as usize];
800        self.push_right(node.left);
801        Some((&node.key, &node.value))
802    }
803}
804
805// Bounded ordered iteration. Default path, not a feature: range scan is the
806// reason to reach for an ordered index in the first place.
807mod range;
808pub use range::{RangeBound, RangeIter};
809
810#[cfg(feature = "harness")]
811pub mod recipe;
812
813// Opt-in feature catalog. Each submodule is gated by its own Cargo
814// feature flag. See `Cargo.toml` `[features]` and the cookbook page
815// for per-feature semantics + p99 impact.
816#[cfg(any(
817    feature = "persistent",
818    feature = "merge-split",
819    feature = "concurrent-reads",
820))]
821pub mod features;
822
823#[cfg(feature = "concurrent-reads")]
824pub use features::concurrent_reads::TreapSnapshot;
825#[cfg(feature = "merge-split")]
826pub use features::merge_split::SplittableTreap;
827#[cfg(feature = "persistent")]
828pub use features::persistent::PersistentTreap;
829
830// Crate-level unit tests live in colocated files (org convention:
831// `<module>_tests.rs` alongside the module), not the top-level `tests/` dir.
832#[cfg(test)]
833#[path = "lib_tests.rs"]
834mod lib_tests;
835
836#[cfg(test)]
837#[path = "sample_app_tests.rs"]
838mod sample_app_tests;