Skip to main content

spg_storage/
persistent_btree.rs

1// v4.40 — workspace `doc-markdown` flags `B-tree`, `BTreeMap`, `Arc::clone`
2// in prose contexts even when surrounding identifiers are already
3// backticked; the lint is fine in source code but too noisy here.
4// `many-single-char-names` flags the K / V / k / v / i naming the rest of
5// the workspace already uses for map-shaped types.
6#![allow(
7    clippy::doc_markdown,
8    clippy::many_single_char_names,
9    clippy::type_complexity
10)]
11
12//! Persistent (structural-sharing) B-tree map — the v4.40 building block for
13//! migrating `Table::indices` off `alloc::collections::BTreeMap`.
14//!
15//! `PersistentBTreeMap<K, V>` is a path-copy CoW B-tree (`ORDER = 8`,
16//! `MAX_ENTRIES = 7`, `MIN_ENTRIES = 3`). Every mutating operation produces a
17//! new handle that shares interior nodes with the old handle via `Arc`.
18//! `Clone` is `O(1)`; `insert` and `get` are `O(log₈ N)`; a CoW path touches
19//! only the spine to the affected node.
20//!
21//! Same hard rules as `persistent::PersistentVec`:
22//! - `no_std` compatible (`alloc::sync::Arc`, `alloc::vec::Vec`).
23//! - Zero `unsafe`.
24//! - Zero external deps.
25//!
26//! Layout (traditional B-tree, *not* B+ tree — entries live at every level,
27//! including internal nodes; descending hits a value if and only if the key
28//! sits along the spine):
29//!
30//!   enum BNode<K, V> {
31//!       Leaf { entries: Vec<(K, V)> },                    // entries.len() ∈ [1, MAX_ENTRIES]
32//!       Internal {
33//!           entries: Vec<(K, V)>,                          // entries.len() ∈ [1, MAX_ENTRIES]
34//!           children: Vec<Arc<BNode<K, V>>>,              // children.len() == entries.len() + 1
35//!       },
36//!   }
37//!
38//! Invariants (debug-checked in `#[cfg(test)]` only):
39//! - Every internal node satisfies `children.len() == entries.len() + 1`.
40//! - Entries inside any single node are sorted strictly ascending by `K`.
41//! - The root may have fewer than `MIN_ENTRIES`; every other node has ≥
42//!   `MIN_ENTRIES`.
43
44use alloc::sync::Arc;
45use alloc::vec::Vec;
46use core::ops::Bound;
47
48/// B-tree order (max children per internal node). Picked at the small end of
49/// the conventional 8–16 range to keep per-CoW node-clone cost low — the
50/// path-copy hits one node per level, and each cloned node carries up to
51/// `MAX_ENTRIES` of `(K, V)`.
52const ORDER: usize = 8;
53const MAX_ENTRIES: usize = ORDER - 1; // 7
54const MAX_CHILDREN: usize = ORDER; // 8
55
56#[derive(Debug)]
57enum BNode<K, V> {
58    Leaf {
59        entries: Vec<(K, V)>,
60    },
61    Internal {
62        entries: Vec<(K, V)>,
63        children: Vec<Arc<BNode<K, V>>>,
64    },
65}
66
67// Manual `Clone` impl so the bound only applies when `Arc::make_mut`
68// (the v4.40.1 transient path) actually needs it. The non-mutating
69// `get` / `iter` paths stay generic over any `K`, `V`.
70impl<K: Clone, V: Clone> Clone for BNode<K, V> {
71    fn clone(&self) -> Self {
72        match self {
73            Self::Leaf { entries } => Self::Leaf {
74                entries: entries.clone(),
75            },
76            Self::Internal { entries, children } => Self::Internal {
77                entries: entries.clone(),
78                children: children.clone(),
79            },
80        }
81    }
82}
83
84/// A persistent ordered map. `Clone` is `O(1)`; `insert` returns a new handle
85/// that shares unaffected subtrees with the old via `Arc::clone`.
86#[derive(Debug)]
87pub struct PersistentBTreeMap<K, V> {
88    root: Arc<BNode<K, V>>,
89    len: usize,
90}
91
92impl<K, V> Default for PersistentBTreeMap<K, V> {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98impl<K, V> Clone for PersistentBTreeMap<K, V> {
99    /// O(1) — `Arc` bump on the root. The whole reason this type exists in
100    /// v4.40 is to make `Table::indices: Vec<Index>` cheap to clone once
101    /// the inner `BTreeMap` is replaced.
102    fn clone(&self) -> Self {
103        Self {
104            root: self.root.clone(),
105            len: self.len,
106        }
107    }
108}
109
110impl<K: PartialEq, V: PartialEq> PartialEq for PersistentBTreeMap<K, V>
111where
112    K: Ord,
113{
114    fn eq(&self, other: &Self) -> bool {
115        self.len == other.len && self.iter().eq(other.iter())
116    }
117}
118
119impl<K: Eq + Ord, V: Eq> Eq for PersistentBTreeMap<K, V> {}
120
121impl<K, V> PersistentBTreeMap<K, V> {
122    /// Empty map. Builds one empty `Leaf` root; subsequent inserts grow
123    /// the trie outward when overflowing `MAX_ENTRIES`.
124    #[must_use]
125    pub fn new() -> Self {
126        Self {
127            root: Arc::new(BNode::Leaf {
128                entries: Vec::new(),
129            }),
130            len: 0,
131        }
132    }
133
134    #[must_use]
135    pub const fn len(&self) -> usize {
136        self.len
137    }
138
139    #[must_use]
140    pub const fn is_empty(&self) -> bool {
141        self.len == 0
142    }
143}
144
145/// v7.37.43 (INSUBQ B-3) — internal-node search outcome: either the
146/// key matched an entry directly, or the search bisected and the
147/// caller must descend into `children[i]`.
148enum FoundOrDescend {
149    Found(usize),
150    Descend(usize),
151}
152
153/// v7.37.43 (INSUBQ B-3) — linear search a leaf's entries for `key`.
154/// `entries.len() ≤ MAX_ENTRIES = 7`; linear-scan beats binary_search
155/// at this size on modern branch predictors. Returns the entry index
156/// when present.
157#[inline]
158fn linear_find_entry<K: Ord, V>(entries: &[(K, V)], key: &K) -> Option<usize> {
159    for (i, (k, _)) in entries.iter().enumerate() {
160        match k.cmp(key) {
161            core::cmp::Ordering::Equal => return Some(i),
162            core::cmp::Ordering::Greater => return None,
163            core::cmp::Ordering::Less => continue,
164        }
165    }
166    None
167}
168
169/// v7.37.43 (INSUBQ B-3) — linear search an internal node's entries
170/// for `key`. Returns Found(idx) if key matches an entry, otherwise
171/// Descend(idx) with the child slot to follow.
172#[inline]
173fn linear_position_internal<K: Ord, V>(entries: &[(K, V)], key: &K) -> FoundOrDescend {
174    for (i, (k, _)) in entries.iter().enumerate() {
175        match k.cmp(key) {
176            core::cmp::Ordering::Equal => return FoundOrDescend::Found(i),
177            core::cmp::Ordering::Greater => return FoundOrDescend::Descend(i),
178            core::cmp::Ordering::Less => continue,
179        }
180    }
181    FoundOrDescend::Descend(entries.len())
182}
183
184impl<K: Ord, V> PersistentBTreeMap<K, V> {
185    /// `O(log₈ N)`. Per-node search at each level; on hit returns the
186    /// value, on miss descends into the child between adjacent entries.
187    ///
188    /// v7.37.43 (INSUBQ B-3) — every node holds ≤ `MAX_ENTRIES = 7`
189    /// `(K, V)` pairs, so the inner search is over at most 7 elements.
190    /// `binary_search_by` issues a data-dependent branch per probe;
191    /// for n ≤ 7 a straight linear scan with a single ordering compare
192    /// per element wins on modern branch predictors and has tighter
193    /// codegen (no early-exit on found-vs-bisect handling). Probing a
194    /// 100k-entry index on 1k keys (the INSUBQ shape) cuts ~30-60 ns
195    /// per descent × ~5 levels × 1k keys ≈ 150-300 µs off the loop.
196    pub fn get(&self, key: &K) -> Option<&V> {
197        let mut node: &Arc<BNode<K, V>> = &self.root;
198        loop {
199            match &**node {
200                BNode::Leaf { entries } => {
201                    return linear_find_entry(entries, key).map(|i| &entries[i].1);
202                }
203                BNode::Internal { entries, children } => {
204                    match linear_position_internal(entries, key) {
205                        FoundOrDescend::Found(i) => return Some(&entries[i].1),
206                        FoundOrDescend::Descend(i) => {
207                            node = &children[i];
208                        }
209                    }
210                }
211            }
212        }
213    }
214
215    /// v7.39 (round 215) — the entry with the largest key STRICTLY less than
216    /// `key` (the in-order predecessor of `key`), or `None` when every key is
217    /// ≥ `key`. `O(log₈ N)`: descend once toward `key`; at each internal node
218    /// the entries left of the descent slot are all < `key`, and the rightmost
219    /// of them is the best candidate at that level — but the child we descend
220    /// into holds keys strictly between it and `key`, so a deeper hit always
221    /// overrides. The building block for the range-exclusion overlap probe
222    /// (find the existing range whose lower bound sits just below a candidate).
223    pub fn predecessor(&self, key: &K) -> Option<(&K, &V)> {
224        let mut node: &Arc<BNode<K, V>> = &self.root;
225        let mut best: Option<(&K, &V)> = None;
226        loop {
227            match &**node {
228                BNode::Leaf { entries } => {
229                    let i = entries.partition_point(|e| &e.0 < key);
230                    if i > 0 {
231                        let (k, v) = &entries[i - 1];
232                        best = Some((k, v));
233                    }
234                    return best;
235                }
236                BNode::Internal { entries, children } => {
237                    let i = entries.partition_point(|e| &e.0 < key);
238                    if i > 0 {
239                        let (k, v) = &entries[i - 1];
240                        best = Some((k, v));
241                    }
242                    node = &children[i];
243                }
244            }
245        }
246    }
247
248    /// In-order key-then-value iterator. Used by `PartialEq` and any caller
249    /// that needs to walk the whole map (e.g. catalog deserialization).
250    /// v7.39 (round 170) — bulk-build from PRE-SORTED entries, bottom-up.
251    /// The per-row `insert_mut` path pays a path-copy allocation per
252    /// element (~300ns each), which made every `rebuild_indices` pass
253    /// O(n) allocations per index (the dominant cost of a VACUUM on an
254    /// indexed table). This builds leaves in ~ORDER-sized runs and
255    /// levels them up with the run separators as the internal entries —
256    /// zero path copies. Groups are cut evenly so no node is left
257    /// pathologically underfull for the later insert/remove rebalance.
258    ///
259    /// Debug builds assert the input is strictly sorted by `K`.
260    #[must_use]
261    pub fn from_sorted(entries: Vec<(K, V)>) -> Self
262    where
263        K: Ord + Clone,
264        V: Clone,
265    {
266        #[cfg(debug_assertions)]
267        for w in entries.windows(2) {
268            debug_assert!(w[0].0 < w[1].0, "from_sorted requires strictly sorted keys");
269        }
270        let len = entries.len();
271        if len <= MAX_ENTRIES {
272            return Self {
273                root: Arc::new(BNode::Leaf { entries }),
274                len,
275            };
276        }
277        // Level 0 — cut into leaf runs with one separator entry between
278        // consecutive leaves. Choose the number of leaves so every leaf
279        // gets between ceil(MAX_ENTRIES/2) and MAX_ENTRIES entries.
280        let mut nodes: Vec<Arc<BNode<K, V>>> = Vec::new();
281        let mut seps: Vec<(K, V)> = Vec::new();
282        {
283            // n items into g groups of ≤ MAX_ENTRIES with g-1 separators:
284            // g = ceil((n + 1) / (MAX_ENTRIES + 1)).
285            let g = (len + 1).div_ceil(MAX_ENTRIES + 1);
286            let mut it = entries.into_iter();
287            let mut remaining = len;
288            for gi in 0..g {
289                let groups_left = g - gi;
290                // Evenly split what's left (minus the separators still owed).
291                let seps_left = groups_left - 1;
292                let take = (remaining - seps_left).div_ceil(groups_left);
293                let leaf: Vec<(K, V)> = (&mut it).take(take).collect();
294                remaining -= leaf.len();
295                nodes.push(Arc::new(BNode::Leaf { entries: leaf }));
296                if gi + 1 < g {
297                    let sep = it.next().expect("separator exists");
298                    remaining -= 1;
299                    seps.push(sep);
300                }
301            }
302        }
303        // Level up until one root remains: group ≤ MAX_CHILDREN children
304        // with the intra-group separators as the internal entries; the
305        // inter-group separators bubble to the next level.
306        while nodes.len() > 1 {
307            let g = nodes.len().div_ceil(MAX_CHILDREN);
308            let per = nodes.len().div_ceil(g);
309            let mut up_nodes: Vec<Arc<BNode<K, V>>> = Vec::with_capacity(g);
310            let mut up_seps: Vec<(K, V)> = Vec::with_capacity(g - 1);
311            let mut node_it = nodes.into_iter();
312            let mut sep_it = seps.into_iter();
313            let mut children: Vec<Arc<BNode<K, V>>> = Vec::with_capacity(per);
314            let mut inner: Vec<(K, V)> = Vec::with_capacity(per - 1);
315            loop {
316                match node_it.next() {
317                    Some(n) => {
318                        if !children.is_empty() {
319                            // The separator BEFORE this child: intra-group
320                            // if the group isn't full, else it bubbles up.
321                            let sep = sep_it.next().expect("separator per boundary");
322                            if children.len() < per {
323                                inner.push(sep);
324                            } else {
325                                up_nodes.push(Arc::new(BNode::Internal {
326                                    entries: core::mem::take(&mut inner),
327                                    children: core::mem::take(&mut children),
328                                }));
329                                up_seps.push(sep);
330                            }
331                        }
332                        children.push(n);
333                    }
334                    None => {
335                        up_nodes.push(Arc::new(BNode::Internal {
336                            entries: inner,
337                            children,
338                        }));
339                        break;
340                    }
341                }
342            }
343            nodes = up_nodes;
344            seps = up_seps;
345        }
346        Self {
347            root: nodes.pop().expect("one root"),
348            len,
349        }
350    }
351
352    pub fn iter(&self) -> Iter<'_, K, V> {
353        let mut stack: Vec<(&Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
354        stack.push((&self.root, 0));
355        Iter { stack }
356    }
357
358    /// v7.34.4 — descending-order iterator. Mirrors `iter()` but the
359    /// per-node walk visits child-then-entry pairs right-to-left.
360    /// Used by the ORDER BY `<indexed col>` DESC + LIMIT N executor
361    /// path to walk only the first N matches off the rightmost leaf
362    /// instead of materialising every row + partial-sorting.
363    pub fn iter_rev(&self) -> IterRev<'_, K, V> {
364        let mut stack: Vec<(&Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
365        stack.push((&self.root, 1));
366        IterRev { stack }
367    }
368
369    /// v7.38 (perf, index range scan) — in-order iterator over the entries
370    /// whose keys fall in `(lo, hi)` (each end honoured per `core::ops::Bound`).
371    /// Descends to `lo` in `O(log₈ N)` (skipping the subtrees entirely below
372    /// it) by building the same `(node, child_index)` cursor stack `iter()`
373    /// uses, positioned at the first key ≥/> `lo`; then walks forward and stops
374    /// at the first key past `hi`. `O(log N + k)` for `k` hits — the building
375    /// block for `Index::lookup_range` (BETWEEN / `>` / `<` seeks).
376    pub fn range<'a>(&'a self, lo: Bound<&K>, hi: Bound<&K>) -> RangeIter<'a, K, V>
377    where
378        K: Clone,
379    {
380        let mut stack: Vec<(&'a Arc<BNode<K, V>>, usize)> = Vec::with_capacity(8);
381        let mut node = &self.root;
382        loop {
383            match &**node {
384                BNode::Leaf { entries } => {
385                    // Leaf frame: begin emitting at the first in-range entry.
386                    stack.push((node, lower_index(entries, lo)));
387                    break;
388                }
389                BNode::Internal { entries, children } => {
390                    let i = lower_index(entries, lo);
391                    // children[i] may hold keys ≥ lo and < entries[i]; descend
392                    // into it, and set this frame to resume by emitting
393                    // entries[i] once that subtree is exhausted (phase-1 slot i
394                    // → idx = 2*i + 1, matching `Iter::next`'s frame encoding).
395                    stack.push((node, 2 * i + 1));
396                    node = &children[i];
397                }
398            }
399        }
400        let (hi_key, hi_incl) = match hi {
401            Bound::Unbounded => (None, false),
402            Bound::Included(k) => (Some(k.clone()), true),
403            Bound::Excluded(k) => (Some(k.clone()), false),
404        };
405        RangeIter {
406            inner: Iter { stack },
407            hi_key,
408            hi_incl,
409            done: false,
410        }
411    }
412}
413
414/// First entry index whose key is ≥ (Included) / > (Excluded) `lo`; 0 for
415/// Unbounded. Linear — a node holds ≤ `MAX_ENTRIES` = 7 entries.
416fn lower_index<K: Ord, V>(entries: &[(K, V)], lo: Bound<&K>) -> usize {
417    match lo {
418        Bound::Unbounded => 0,
419        Bound::Included(k) => entries.partition_point(|e| &e.0 < k),
420        Bound::Excluded(k) => entries.partition_point(|e| &e.0 <= k),
421    }
422}
423
424impl<K: Ord + Clone, V: Clone> PersistentBTreeMap<K, V> {
425    /// `O(log₈ N)`. Path-copy insert; replaces if `key` exists, otherwise
426    /// inserts and grows by 1. Returns `(new_map, previous_value)`.
427    #[must_use]
428    pub fn insert(&self, key: K, value: V) -> (Self, Option<V>) {
429        let (new_left, split, prev_v) = insert_helper(&self.root, key, value);
430        let new_root = if let Some((right, median)) = split {
431            Arc::new(BNode::Internal {
432                entries: alloc::vec![median],
433                children: alloc::vec![new_left, right],
434            })
435        } else {
436            new_left
437        };
438        let new_len = if prev_v.is_none() {
439            self.len + 1
440        } else {
441            self.len
442        };
443        (
444            Self {
445                root: new_root,
446                len: new_len,
447            },
448            prev_v,
449        )
450    }
451
452    /// `O(log₈ N)` transient insert. v4.40.1 perf path: walks
453    /// `Arc::make_mut` down the spine — when the spine `Arc`s are uniquely
454    /// owned (the common case in `Table::insert` outside a TX wrap), every
455    /// touched node mutates in place at roughly `std::BTreeMap::insert`
456    /// cost. When a cloned handle is outstanding (e.g. a Catalog snapshot
457    /// inside a TX wrap), `Arc::make_mut` path-copies just the affected
458    /// node and the snapshot stays untouched. Either way, callers see the
459    /// same end state as the immutable `insert` followed by reassignment.
460    pub fn insert_mut(&mut self, key: K, value: V) -> Option<V> {
461        let (split, prev_v) = insert_transient_helper(&mut self.root, key, value);
462        if let Some((right, median)) = split {
463            // Root overflow: wrap the old root + new right sibling under a
464            // fresh top-level Internal carrying the median entry. We need
465            // to take ownership of self.root to move it into `children`,
466            // so swap in a placeholder Leaf and then overwrite with the
467            // real new root below.
468            let old_root = core::mem::replace(
469                &mut self.root,
470                Arc::new(BNode::Leaf {
471                    entries: Vec::new(),
472                }),
473            );
474            self.root = Arc::new(BNode::Internal {
475                entries: alloc::vec![median],
476                children: alloc::vec![old_root, right],
477            });
478        }
479        if prev_v.is_none() {
480            self.len += 1;
481        }
482        prev_v
483    }
484
485    /// `O(log₈ N)` transient remove. Returns the value that was stored
486    /// under `key`, or `None` when the key was absent (the map is then
487    /// untouched).
488    ///
489    /// v7.39 (round 465) — the map had no removal at all: `new / get /
490    /// predecessor / from_sorted / iter / iter_rev / range / insert /
491    /// insert_mut`. That is why dropping a single index entry meant
492    /// rebuilding the whole map from the rows, and why one autovacuum tick
493    /// costs 11 ms on a 50k-row table with one secondary index — five
494    /// times the INSERT it exists to protect, all of it under the engine
495    /// write lock. Round 464 measured a filtered rebuild and it lost to
496    /// the existing from-the-rows rebuild, because iterating a
497    /// structurally-shared tree is pointer chasing while the rebuild is a
498    /// linear scan plus one sort. Removal is the operation that was
499    /// missing; with it, reclaiming k rows touches k spines instead of
500    /// rebuilding n entries.
501    ///
502    /// Walks `Arc::make_mut` down the spine like `insert_mut`, so a
503    /// uniquely-owned tree mutates in place and an outstanding snapshot
504    /// (a Catalog clone inside a TX wrap) path-copies only the spine.
505    pub fn remove_mut(&mut self, key: &K) -> Option<V> {
506        let removed = remove_transient_helper(&mut self.root, key)?;
507        // The root is the one node allowed to underflow, but an internal
508        // root emptied of entries has exactly one child and must be
509        // replaced by it or the tree grows a permanently useless level.
510        let collapse = match self.root.as_ref() {
511            BNode::Internal { entries, children } if entries.is_empty() => {
512                debug_assert_eq!(children.len(), 1);
513                children.first().cloned()
514            }
515            _ => None,
516        };
517        if let Some(only) = collapse {
518            self.root = only;
519        }
520        self.len -= 1;
521        Some(removed)
522    }
523
524    /// Immutable removal, for symmetry with [`Self::insert`]. Returns
525    /// `(new_map, previous_value)`; the receiver is untouched.
526    #[must_use]
527    pub fn remove(&self, key: &K) -> (Self, Option<V>) {
528        let mut next = self.clone();
529        let prev = next.remove_mut(key);
530        (next, prev)
531    }
532}
533
534/// Minimum entries in any node but the root. A node that drops below this
535/// borrows from a sibling, or merges with one.
536const MIN_ENTRIES: usize = ORDER / 2 - 1; // 3
537
538impl<K, V> BNode<K, V> {
539    fn entry_count(&self) -> usize {
540        match self {
541            BNode::Leaf { entries } | BNode::Internal { entries, .. } => entries.len(),
542        }
543    }
544}
545
546/// Transient remove worker. Returns the removed value, or `None` when the
547/// key is not in this subtree (in which case nothing was modified).
548fn remove_transient_helper<K: Ord + Clone, V: Clone>(
549    node: &mut Arc<BNode<K, V>>,
550    key: &K,
551) -> Option<V> {
552    // Probe before `make_mut`: a miss must not path-copy the spine.
553    let (found, idx) = match node.as_ref() {
554        BNode::Leaf { entries } | BNode::Internal { entries, .. } => {
555            match entries.binary_search_by(|(ek, _)| ek.cmp(key)) {
556                Ok(i) => (true, i),
557                Err(i) => (false, i),
558            }
559        }
560    };
561    if !found && matches!(node.as_ref(), BNode::Leaf { .. }) {
562        return None;
563    }
564    let inner = Arc::make_mut(node);
565    match inner {
566        BNode::Leaf { entries } => Some(entries.remove(idx).1),
567        BNode::Internal { entries, children } => {
568            if found {
569                // Standard B-tree interior delete: swap in the in-order
570                // predecessor, which always lives in a leaf, then repair
571                // the subtree it came out of.
572                let pred = remove_max(&mut children[idx]);
573                let old = core::mem::replace(&mut entries[idx], pred);
574                fix_child(entries, children, idx);
575                Some(old.1)
576            } else {
577                let removed = remove_transient_helper(&mut children[idx], key)?;
578                fix_child(entries, children, idx);
579                Some(removed)
580            }
581        }
582    }
583}
584
585/// Detach the largest entry of this subtree. The subtree must be non-empty;
586/// callers only reach it from an internal node whose children are populated.
587fn remove_max<K: Ord + Clone, V: Clone>(node: &mut Arc<BNode<K, V>>) -> (K, V) {
588    let inner = Arc::make_mut(node);
589    match inner {
590        BNode::Leaf { entries } => entries.pop().expect("a B-tree leaf is never empty"),
591        BNode::Internal { entries, children } => {
592            let last = children.len() - 1;
593            let kv = remove_max(&mut children[last]);
594            fix_child(entries, children, last);
595            kv
596        }
597    }
598}
599
600/// Restore `children[i]`'s minimum occupancy by borrowing from a sibling, or
601/// merging with one when neither sibling can spare an entry.
602fn fix_child<K: Ord + Clone, V: Clone>(
603    entries: &mut Vec<(K, V)>,
604    children: &mut Vec<Arc<BNode<K, V>>>,
605    i: usize,
606) {
607    if children[i].entry_count() >= MIN_ENTRIES {
608        return;
609    }
610    if i > 0 && children[i - 1].entry_count() > MIN_ENTRIES {
611        rotate_from_left(entries, children, i);
612    } else if i + 1 < children.len() && children[i + 1].entry_count() > MIN_ENTRIES {
613        rotate_from_right(entries, children, i);
614    } else if i > 0 {
615        merge_children(entries, children, i - 1);
616    } else {
617        merge_children(entries, children, i);
618    }
619}
620
621/// Move the left sibling's largest entry up into the separator slot and the
622/// old separator down into `children[i]`'s front.
623fn rotate_from_left<K: Ord + Clone, V: Clone>(
624    entries: &mut [(K, V)],
625    children: &mut [Arc<BNode<K, V>>],
626    i: usize,
627) {
628    let (moved_entry, moved_child) = match Arc::make_mut(&mut children[i - 1]) {
629        BNode::Leaf { entries: le } => (le.pop().expect("sibling has entries to spare"), None),
630        BNode::Internal {
631            entries: le,
632            children: lc,
633        } => (
634            le.pop().expect("sibling has entries to spare"),
635            Some(
636                lc.pop()
637                    .expect("internal node has entries.len()+1 children"),
638            ),
639        ),
640    };
641    let separator = core::mem::replace(&mut entries[i - 1], moved_entry);
642    match Arc::make_mut(&mut children[i]) {
643        BNode::Leaf { entries: ce } => {
644            debug_assert!(moved_child.is_none());
645            ce.insert(0, separator);
646        }
647        BNode::Internal {
648            entries: ce,
649            children: cc,
650        } => {
651            ce.insert(0, separator);
652            cc.insert(
653                0,
654                moved_child.expect("sibling of an internal node is internal"),
655            );
656        }
657    }
658}
659
660/// Mirror of [`rotate_from_left`] using the right sibling.
661fn rotate_from_right<K: Ord + Clone, V: Clone>(
662    entries: &mut [(K, V)],
663    children: &mut [Arc<BNode<K, V>>],
664    i: usize,
665) {
666    let (moved_entry, moved_child) = match Arc::make_mut(&mut children[i + 1]) {
667        BNode::Leaf { entries: re } => (re.remove(0), None),
668        BNode::Internal {
669            entries: re,
670            children: rc,
671        } => (re.remove(0), Some(rc.remove(0))),
672    };
673    let separator = core::mem::replace(&mut entries[i], moved_entry);
674    match Arc::make_mut(&mut children[i]) {
675        BNode::Leaf { entries: ce } => {
676            debug_assert!(moved_child.is_none());
677            ce.push(separator);
678        }
679        BNode::Internal {
680            entries: ce,
681            children: cc,
682        } => {
683            ce.push(separator);
684            cc.push(moved_child.expect("sibling of an internal node is internal"));
685        }
686    }
687}
688
689/// Fold `children[sep + 1]` and the separator entry into `children[sep]`.
690/// Both children are at minimum occupancy, so the result fits.
691fn merge_children<K: Ord + Clone, V: Clone>(
692    entries: &mut Vec<(K, V)>,
693    children: &mut Vec<Arc<BNode<K, V>>>,
694    sep: usize,
695) {
696    let separator = entries.remove(sep);
697    let right = children.remove(sep + 1);
698    let right = Arc::try_unwrap(right).unwrap_or_else(|shared| (*shared).clone());
699    match (Arc::make_mut(&mut children[sep]), right) {
700        (BNode::Leaf { entries: le }, BNode::Leaf { entries: re }) => {
701            le.push(separator);
702            le.extend(re);
703        }
704        (
705            BNode::Internal {
706                entries: le,
707                children: lc,
708            },
709            BNode::Internal {
710                entries: re,
711                children: rc,
712            },
713        ) => {
714            le.push(separator);
715            le.extend(re);
716            lc.extend(rc);
717        }
718        // Siblings are always at the same depth, so the mixed cases are
719        // unreachable; putting the separator back keeps the map a valid
720        // (if larger) tree rather than losing an entry.
721        (BNode::Leaf { entries: le }, BNode::Internal { .. })
722        | (BNode::Internal { entries: le, .. }, BNode::Leaf { .. }) => {
723            debug_assert!(false, "B-tree siblings must be at the same depth");
724            le.push(separator);
725        }
726    }
727}
728
729/// Transient insert worker — walks `Arc::make_mut` down the spine so each
730/// uniquely-owned node mutates in place. Splits still allocate fresh
731/// `Arc<BNode>` for the new right sibling (those are genuinely new nodes,
732/// not CoW copies).
733fn insert_transient_helper<K: Ord + Clone, V: Clone>(
734    node: &mut Arc<BNode<K, V>>,
735    k: K,
736    v: V,
737) -> (Option<(Arc<BNode<K, V>>, (K, V))>, Option<V>) {
738    let inner = Arc::make_mut(node);
739    match inner {
740        BNode::Leaf { entries } => {
741            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
742            let prev_v = match pos {
743                Ok(idx) => Some(core::mem::replace(&mut entries[idx].1, v)),
744                Err(idx) => {
745                    entries.insert(idx, (k, v));
746                    None
747                }
748            };
749            if entries.len() <= MAX_ENTRIES {
750                return (None, prev_v);
751            }
752            // Overflow: split (same arithmetic as the immutable path).
753            let mid = entries.len() / 2;
754            let right_entries = entries.split_off(mid + 1);
755            let median = entries.pop().expect("mid was in-bounds");
756            let right = Arc::new(BNode::Leaf {
757                entries: right_entries,
758            });
759            (Some((right, median)), prev_v)
760        }
761        BNode::Internal { entries, children } => {
762            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
763            match pos {
764                Ok(idx) => {
765                    let prev_v = core::mem::replace(&mut entries[idx].1, v);
766                    (None, Some(prev_v))
767                }
768                Err(idx) => {
769                    let (split, prev_v) = insert_transient_helper(&mut children[idx], k, v);
770                    if let Some((right_sibling, median)) = split {
771                        entries.insert(idx, median);
772                        children.insert(idx + 1, right_sibling);
773                    }
774                    if children.len() <= MAX_CHILDREN {
775                        return (None, prev_v);
776                    }
777                    let mid = entries.len() / 2;
778                    let right_entries = entries.split_off(mid + 1);
779                    let median = entries.pop().expect("mid was in-bounds");
780                    let right_children = children.split_off(mid + 1);
781                    let right = Arc::new(BNode::Internal {
782                        entries: right_entries,
783                        children: right_children,
784                    });
785                    (Some((right, median)), prev_v)
786                }
787            }
788        }
789    }
790}
791
792/// Recursive insert worker. Returns `(new_left_node, optional_split, prev_v)`
793/// where `optional_split = Some((right_sibling, median_entry))` when this
794/// node overflowed; the caller bubbles the split up.
795fn insert_helper<K: Ord + Clone, V: Clone>(
796    node: &Arc<BNode<K, V>>,
797    k: K,
798    v: V,
799) -> (
800    Arc<BNode<K, V>>,
801    Option<(Arc<BNode<K, V>>, (K, V))>,
802    Option<V>,
803) {
804    match &**node {
805        BNode::Leaf { entries } => {
806            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
807            let mut new_entries = entries.clone();
808            let prev_v = match pos {
809                Ok(idx) => Some(core::mem::replace(&mut new_entries[idx].1, v)),
810                Err(idx) => {
811                    new_entries.insert(idx, (k, v));
812                    None
813                }
814            };
815            if new_entries.len() <= MAX_ENTRIES {
816                return (
817                    Arc::new(BNode::Leaf {
818                        entries: new_entries,
819                    }),
820                    None,
821                    prev_v,
822                );
823            }
824            // Overflow: split. With MAX_ENTRIES = 7 the overflowed leaf holds
825            // 8 entries → 4 left + median + 3 right (or vice versa). Each
826            // half retains ≥ MIN_ENTRIES = 3.
827            let mid = new_entries.len() / 2; // 4
828            let right_entries = new_entries.split_off(mid + 1);
829            let median = new_entries.pop().expect("mid was in-bounds");
830            let left = Arc::new(BNode::Leaf {
831                entries: new_entries,
832            });
833            let right = Arc::new(BNode::Leaf {
834                entries: right_entries,
835            });
836            (left, Some((right, median)), prev_v)
837        }
838        BNode::Internal { entries, children } => {
839            let pos = entries.binary_search_by(|(ek, _)| ek.cmp(&k));
840            match pos {
841                Ok(idx) => {
842                    // Key already lives on this internal node — replace V.
843                    let mut new_entries = entries.clone();
844                    let prev_v = core::mem::replace(&mut new_entries[idx].1, v);
845                    (
846                        Arc::new(BNode::Internal {
847                            entries: new_entries,
848                            children: children.clone(),
849                        }),
850                        None,
851                        Some(prev_v),
852                    )
853                }
854                Err(idx) => {
855                    // Descend into children[idx]; bubble up any split.
856                    let (new_child, split, prev_v) = insert_helper(&children[idx], k, v);
857                    let mut new_entries = entries.clone();
858                    let mut new_children = children.clone();
859                    new_children[idx] = new_child;
860                    if let Some((right_sibling, median)) = split {
861                        new_entries.insert(idx, median);
862                        new_children.insert(idx + 1, right_sibling);
863                    }
864                    if new_children.len() <= MAX_CHILDREN {
865                        return (
866                            Arc::new(BNode::Internal {
867                                entries: new_entries,
868                                children: new_children,
869                            }),
870                            None,
871                            prev_v,
872                        );
873                    }
874                    // Internal overflow: 8 entries + 9 children → split into
875                    // 4-entry + 5-child left, median goes up, 3-entry +
876                    // 4-child right. Both halves keep ≥ MIN_ENTRIES = 3 and
877                    // ≥ MIN_CHILDREN = 4.
878                    let mid = new_entries.len() / 2; // 4
879                    let right_entries = new_entries.split_off(mid + 1);
880                    let median = new_entries.pop().expect("mid was in-bounds");
881                    let right_children = new_children.split_off(mid + 1);
882                    let left = Arc::new(BNode::Internal {
883                        entries: new_entries,
884                        children: new_children,
885                    });
886                    let right = Arc::new(BNode::Internal {
887                        entries: right_entries,
888                        children: right_children,
889                    });
890                    (left, Some((right, median)), prev_v)
891                }
892            }
893        }
894    }
895}
896
897/// In-order `(K, V)` iterator. Uses an explicit stack with `(node, child_index)`
898/// frames so we don't need to copy entries into a flat buffer.
899#[derive(Debug)]
900pub struct Iter<'a, K, V> {
901    stack: Vec<(&'a Arc<BNode<K, V>>, usize)>,
902}
903
904impl<'a, K, V> Iterator for Iter<'a, K, V> {
905    type Item = (&'a K, &'a V);
906    fn next(&mut self) -> Option<(&'a K, &'a V)> {
907        loop {
908            let (node, idx) = *self.stack.last()?;
909            match &**node {
910                BNode::Leaf { entries } => {
911                    if idx < entries.len() {
912                        let (k, v) = &entries[idx];
913                        self.stack.last_mut().unwrap().1 = idx + 1;
914                        return Some((k, v));
915                    }
916                    self.stack.pop();
917                }
918                BNode::Internal { entries, children } => {
919                    // Frame layout: child_index `idx` means "we still need
920                    // to descend into children[idx / 2]" (even) or "emit
921                    // entries[idx / 2]" (odd). Encoding two phases per
922                    // entry slot.
923                    let phase = idx & 1;
924                    let slot = idx >> 1;
925                    if phase == 0 {
926                        // Descend into children[slot] if it exists.
927                        if slot < children.len() {
928                            self.stack.last_mut().unwrap().1 = idx + 1;
929                            self.stack.push((&children[slot], 0));
930                            continue;
931                        }
932                        self.stack.pop();
933                    } else {
934                        // Emit entries[slot] if it exists.
935                        if slot < entries.len() {
936                            let (k, v) = &entries[slot];
937                            self.stack.last_mut().unwrap().1 = idx + 1;
938                            return Some((k, v));
939                        }
940                        self.stack.pop();
941                    }
942                }
943            }
944        }
945    }
946}
947
948/// v7.38 — bounded-above wrapper over [`Iter`], produced by
949/// [`PersistentBTreeMap::range`]. `Iter` already starts at `lo` (the seek
950/// stack); this stops emission at the first key past `hi`.
951#[derive(Debug)]
952pub struct RangeIter<'a, K, V> {
953    inner: Iter<'a, K, V>,
954    hi_key: Option<K>,
955    hi_incl: bool,
956    done: bool,
957}
958
959impl<'a, K: Ord, V> Iterator for RangeIter<'a, K, V> {
960    type Item = (&'a K, &'a V);
961    fn next(&mut self) -> Option<(&'a K, &'a V)> {
962        if self.done {
963            return None;
964        }
965        let (k, v) = self.inner.next()?;
966        if let Some(h) = &self.hi_key {
967            let past = if self.hi_incl { k > h } else { k >= h };
968            if past {
969                self.done = true;
970                return None;
971            }
972        }
973        Some((k, v))
974    }
975}
976
977/// v7.34.4 — descending-order `(K, V)` iterator. Mirrors `Iter` but each
978/// node's child-then-entry walk runs right-to-left so the first yielded
979/// pair is the maximum key in the map. Used by the ORDER BY `<indexed
980/// col>` DESC + LIMIT N executor path to walk only the first N matches
981/// off the rightmost leaf instead of materialising every row + partial-
982/// sorting; the existing forward `Iter` stays untouched so unrelated
983/// callers (catalog deserialisation, PartialEq) are unaffected.
984#[derive(Debug)]
985pub struct IterRev<'a, K, V> {
986    // (node, next_pos) where next_pos counts the remaining reverse
987    // step within the node, starting at 1. A pos > step_count means
988    // the node is exhausted (pop). For a Leaf with E entries the
989    // step count is E (emit entries right-to-left). For an Internal
990    // node with E entries / E+1 children the step count is 2E+1: odd
991    // positions descend into a child, even positions emit an entry,
992    // both walking right-to-left.
993    stack: Vec<(&'a Arc<BNode<K, V>>, usize)>,
994}
995
996impl<'a, K, V> Iterator for IterRev<'a, K, V> {
997    type Item = (&'a K, &'a V);
998    fn next(&mut self) -> Option<(&'a K, &'a V)> {
999        loop {
1000            let (node, pos) = *self.stack.last()?;
1001            match &**node {
1002                BNode::Leaf { entries } => {
1003                    if pos <= entries.len() {
1004                        let i = entries.len() - pos;
1005                        self.stack.last_mut().unwrap().1 = pos + 1;
1006                        let (k, v) = &entries[i];
1007                        return Some((k, v));
1008                    }
1009                    self.stack.pop();
1010                }
1011                BNode::Internal { entries, children } => {
1012                    let n_steps = 2 * entries.len() + 1;
1013                    if pos <= n_steps {
1014                        self.stack.last_mut().unwrap().1 = pos + 1;
1015                        if pos % 2 == 1 {
1016                            // Odd: descend into `children[E - (pos-1)/2]`.
1017                            let child_idx = entries.len() - (pos - 1) / 2;
1018                            self.stack.push((&children[child_idx], 1));
1019                            continue;
1020                        }
1021                        // Even: emit `entries[E - pos/2]`.
1022                        let entry_idx = entries.len() - pos / 2;
1023                        let (k, v) = &entries[entry_idx];
1024                        return Some((k, v));
1025                    }
1026                    self.stack.pop();
1027                }
1028            }
1029        }
1030    }
1031}
1032
1033impl<'a, K: Ord, V> IntoIterator for &'a PersistentBTreeMap<K, V> {
1034    type Item = (&'a K, &'a V);
1035    type IntoIter = Iter<'a, K, V>;
1036    fn into_iter(self) -> Self::IntoIter {
1037        self.iter()
1038    }
1039}
1040
1041#[cfg(test)]
1042#[allow(
1043    clippy::cast_possible_truncation,
1044    clippy::cast_possible_wrap,
1045    clippy::cast_sign_loss,
1046    clippy::cast_lossless,
1047    clippy::needless_range_loop,
1048    clippy::items_after_statements,
1049    clippy::manual_range_patterns,
1050    clippy::unreadable_literal,
1051    clippy::similar_names
1052)]
1053mod tests {
1054    use super::*;
1055    use alloc::collections::BTreeMap;
1056    use alloc::vec;
1057
1058    // ---- round 465: structural checks for removal ----
1059
1060    /// Depth of every leaf, or `None` when they disagree. Also asserts each
1061    /// node's local shape: entry count in range, entries strictly ascending,
1062    /// and `children.len() == entries.len() + 1`.
1063    fn check_node<K: Ord + core::fmt::Debug, V>(
1064        node: &BNode<K, V>,
1065        is_root: bool,
1066        depth: usize,
1067    ) -> usize {
1068        let entries = match node {
1069            BNode::Leaf { entries } | BNode::Internal { entries, .. } => entries,
1070        };
1071        assert!(
1072            entries.len() <= MAX_ENTRIES,
1073            "node overfull: {} entries",
1074            entries.len()
1075        );
1076        if !is_root {
1077            assert!(
1078                entries.len() >= MIN_ENTRIES,
1079                "non-root node underfull: {} entries",
1080                entries.len()
1081            );
1082        }
1083        for w in entries.windows(2) {
1084            assert!(w[0].0 < w[1].0, "entries out of order inside a node");
1085        }
1086        match node {
1087            BNode::Leaf { .. } => depth,
1088            BNode::Internal { entries, children } => {
1089                assert_eq!(
1090                    children.len(),
1091                    entries.len() + 1,
1092                    "internal node must have entries.len()+1 children"
1093                );
1094                let mut seen: Option<usize> = None;
1095                for c in children {
1096                    let d = check_node(c, false, depth + 1);
1097                    match seen {
1098                        None => seen = Some(d),
1099                        Some(prev) => assert_eq!(prev, d, "leaves at different depths"),
1100                    }
1101                }
1102                seen.expect("internal node has children")
1103            }
1104        }
1105    }
1106
1107    fn check_map<K: Ord + Clone + core::fmt::Debug, V: Clone>(m: &PersistentBTreeMap<K, V>) {
1108        check_node(m.root.as_ref(), true, 0);
1109        // In-order traversal must be globally sorted, and `len` must match.
1110        let keys: Vec<&K> = m.iter().map(|(k, _)| k).collect();
1111        for w in keys.windows(2) {
1112            assert!(w[0] < w[1], "iteration order is not globally sorted");
1113        }
1114        assert_eq!(keys.len(), m.len(), "len disagrees with iteration");
1115    }
1116
1117    /// Deterministic sequence — no rand dependency, and a failure is
1118    /// reproducible from the seed alone.
1119    fn lcg(state: &mut u64) -> u64 {
1120        *state = state
1121            .wrapping_mul(6_364_136_223_846_793_005)
1122            .wrapping_add(1);
1123        *state >> 33
1124    }
1125
1126    #[test]
1127    fn round465_remove_absent_key_is_a_no_op() {
1128        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1129        for i in 0..50_i64 {
1130            pb.insert_mut(i * 2, i);
1131        }
1132        let before = pb.len();
1133        assert_eq!(pb.remove_mut(&7), None);
1134        assert_eq!(pb.remove_mut(&-1), None);
1135        assert_eq!(pb.remove_mut(&1000), None);
1136        assert_eq!(pb.len(), before);
1137        check_map(&pb);
1138    }
1139
1140    #[test]
1141    fn round465_remove_every_key_empties_the_map() {
1142        for n in [1_i64, 7, 8, 9, 100, 500] {
1143            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1144            for i in 0..n {
1145                pb.insert_mut(i, i * 3);
1146            }
1147            for i in 0..n {
1148                assert_eq!(pb.remove_mut(&i), Some(i * 3), "n={n} i={i}");
1149                assert_eq!(pb.len() as i64, n - i - 1);
1150                check_map(&pb);
1151                // Everything not yet removed must still be reachable.
1152                for j in (i + 1)..n {
1153                    assert_eq!(
1154                        pb.get(&j),
1155                        Some(&(j * 3)),
1156                        "n={n} lost {j} after removing {i}"
1157                    );
1158                }
1159            }
1160            assert!(pb.is_empty());
1161        }
1162    }
1163
1164    #[test]
1165    fn round465_remove_in_reverse_order_empties_the_map() {
1166        // Descending removal drives the merge path from the other side —
1167        // rotate_from_left instead of rotate_from_right.
1168        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1169        for i in 0..300_i64 {
1170            pb.insert_mut(i, i);
1171        }
1172        for i in (0..300_i64).rev() {
1173            assert_eq!(pb.remove_mut(&i), Some(i));
1174            check_map(&pb);
1175        }
1176        assert!(pb.is_empty());
1177    }
1178
1179    #[test]
1180    fn round465_matches_btreemap_under_mixed_traffic() {
1181        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1182        let mut model: BTreeMap<i64, i64> = BTreeMap::new();
1183        let mut seed = 0x5eed_1234_u64;
1184        for step in 0..4000 {
1185            let k = (lcg(&mut seed) % 300) as i64;
1186            if lcg(&mut seed) % 3 == 0 {
1187                assert_eq!(
1188                    pb.remove_mut(&k),
1189                    model.remove(&k),
1190                    "step {step} remove {k}"
1191                );
1192            } else {
1193                let v = (lcg(&mut seed) % 1000) as i64;
1194                assert_eq!(
1195                    pb.insert_mut(k, v),
1196                    model.insert(k, v),
1197                    "step {step} insert {k}"
1198                );
1199            }
1200            assert_eq!(pb.len(), model.len(), "step {step}");
1201            if step % 97 == 0 {
1202                check_map(&pb);
1203                let got: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1204                let want: Vec<(i64, i64)> = model.iter().map(|(k, v)| (*k, *v)).collect();
1205                assert_eq!(got, want, "step {step}");
1206            }
1207        }
1208        check_map(&pb);
1209        let got: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1210        let want: Vec<(i64, i64)> = model.iter().map(|(k, v)| (*k, *v)).collect();
1211        assert_eq!(got, want);
1212    }
1213
1214    #[test]
1215    fn round465_remove_leaves_a_shared_snapshot_untouched() {
1216        // The whole point of the persistent structure: a clone taken before
1217        // the removal must still see the removed key.
1218        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1219        for i in 0..200_i64 {
1220            pb.insert_mut(i, i * 5);
1221        }
1222        let snapshot = pb.clone();
1223        for i in 0..100_i64 {
1224            pb.remove_mut(&(i * 2));
1225        }
1226        check_map(&pb);
1227        check_map(&snapshot);
1228        assert_eq!(snapshot.len(), 200);
1229        for i in 0..200_i64 {
1230            assert_eq!(snapshot.get(&i), Some(&(i * 5)), "snapshot lost {i}");
1231        }
1232        for i in 0..100_i64 {
1233            assert_eq!(pb.get(&(i * 2)), None);
1234            assert_eq!(pb.get(&(i * 2 + 1)), Some(&(i * 10 + 5)));
1235        }
1236    }
1237
1238    #[test]
1239    fn round465_immutable_remove_does_not_touch_the_receiver() {
1240        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1241        for i in 0..60_i64 {
1242            pb.insert_mut(i, i);
1243        }
1244        let (next, prev) = pb.remove(&30);
1245        assert_eq!(prev, Some(30));
1246        assert_eq!(pb.get(&30), Some(&30), "receiver must be untouched");
1247        assert_eq!(next.get(&30), None);
1248        assert_eq!(pb.len(), 60);
1249        assert_eq!(next.len(), 59);
1250        check_map(&pb);
1251        check_map(&next);
1252    }
1253
1254    #[test]
1255    fn round465_range_and_predecessor_still_work_after_removals() {
1256        // Removal rewires spines; the ordered readers must follow.
1257        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1258        for i in 0..400_i64 {
1259            pb.insert_mut(i, i);
1260        }
1261        for i in 0..400_i64 {
1262            if i % 3 == 0 {
1263                pb.remove_mut(&i);
1264            }
1265        }
1266        check_map(&pb);
1267        let in_range: Vec<i64> = pb
1268            .range(Bound::Included(&100), Bound::Excluded(&120))
1269            .map(|(k, _)| *k)
1270            .collect();
1271        let want: Vec<i64> = (100..120).filter(|i| i % 3 != 0).collect();
1272        assert_eq!(in_range, want);
1273        // 99 is a multiple of 3 and was removed, so 100's predecessor is 98.
1274        assert_eq!(pb.predecessor(&100).map(|(k, _)| *k), Some(98));
1275        let rev: Vec<i64> = pb.iter_rev().map(|(k, _)| *k).take(3).collect();
1276        assert_eq!(rev, vec![398, 397, 395]);
1277    }
1278
1279    #[test]
1280    fn empty_map_is_empty() {
1281        let pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1282        assert_eq!(pb.len(), 0);
1283        assert!(pb.is_empty());
1284        assert!(pb.get(&42).is_none());
1285    }
1286
1287    #[test]
1288    fn insert_single_into_empty_works() {
1289        let (pb, prev) = PersistentBTreeMap::<i64, i64>::new().insert(1, 100);
1290        assert_eq!(prev, None);
1291        assert_eq!(pb.len(), 1);
1292        assert_eq!(pb.get(&1), Some(&100));
1293        assert_eq!(pb.get(&2), None);
1294    }
1295
1296    #[test]
1297    fn insert_replace_returns_prev_keeps_len() {
1298        let (pb, p1) = PersistentBTreeMap::<i64, i64>::new().insert(7, 10);
1299        assert_eq!(p1, None);
1300        let (pb, p2) = pb.insert(7, 99);
1301        assert_eq!(p2, Some(10));
1302        assert_eq!(pb.len(), 1);
1303        assert_eq!(pb.get(&7), Some(&99));
1304    }
1305
1306    #[test]
1307    fn insert_crosses_leaf_split_boundary() {
1308        // 8 inserts cause the first leaf to split.
1309        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1310        for i in 0..20_i64 {
1311            pb = pb.insert(i, i * 7).0;
1312        }
1313        for i in 0..20_i64 {
1314            assert_eq!(pb.get(&i), Some(&(i * 7)));
1315        }
1316        assert!(pb.get(&20).is_none());
1317        assert_eq!(pb.len(), 20);
1318    }
1319
1320    #[test]
1321    fn insert_grows_through_multiple_internal_splits() {
1322        // 200 inserts force the trie depth to grow more than once.
1323        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1324        for i in 0..200_i64 {
1325            pb = pb.insert(i, i * 11).0;
1326        }
1327        for i in 0..200_i64 {
1328            assert_eq!(pb.get(&i), Some(&(i * 11)));
1329        }
1330        assert_eq!(pb.len(), 200);
1331    }
1332
1333    #[test]
1334    fn clone_then_insert_preserves_original() {
1335        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1336        for i in 0..100_i64 {
1337            a = a.insert(i, i).0;
1338        }
1339        let b = a.clone();
1340        let (b, _) = b.insert(999, 999);
1341        assert_eq!(a.len(), 100);
1342        assert!(a.get(&999).is_none());
1343        assert_eq!(b.len(), 101);
1344        assert_eq!(b.get(&999), Some(&999));
1345        for i in 0..100_i64 {
1346            assert_eq!(a.get(&i), Some(&i), "A drift at {i}");
1347            assert_eq!(b.get(&i), Some(&i), "B drift at {i}");
1348        }
1349    }
1350
1351    #[test]
1352    fn iter_yields_sorted_order() {
1353        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1354        // Insert in shuffled order; iter must still come out sorted.
1355        for &k in &[7_i64, 3, 11, 1, 9, 5, 14, 2, 8, 12, 4, 6, 10, 13] {
1356            pb = pb.insert(k, k * 2).0;
1357        }
1358        let collected: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1359        let expected: Vec<(i64, i64)> = (1..=14).map(|k| (k, k * 2)).collect();
1360        assert_eq!(collected, expected);
1361    }
1362
1363    #[test]
1364    fn iter_handles_taller_tree() {
1365        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1366        for i in 0..500_i64 {
1367            pb = pb.insert(i, i).0;
1368        }
1369        let collected: Vec<i64> = pb.iter().map(|(k, _)| *k).collect();
1370        let expected: Vec<i64> = (0..500).collect();
1371        assert_eq!(collected, expected);
1372    }
1373
1374    #[test]
1375    fn range_basic_bounds() {
1376        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1377        for i in 0..100_i64 {
1378            pb = pb.insert(i, i * 10).0;
1379        }
1380        let keys = |lo: Bound<&i64>, hi: Bound<&i64>| -> Vec<i64> {
1381            pb.range(lo, hi).map(|(k, _)| *k).collect()
1382        };
1383        assert_eq!(
1384            keys(Bound::Included(&20), Bound::Included(&24)),
1385            vec![20, 21, 22, 23, 24]
1386        );
1387        assert_eq!(
1388            keys(Bound::Excluded(&20), Bound::Excluded(&24)),
1389            vec![21, 22, 23]
1390        );
1391        assert_eq!(keys(Bound::Unbounded, Bound::Excluded(&3)), vec![0, 1, 2]);
1392        assert_eq!(
1393            keys(Bound::Included(&97), Bound::Unbounded),
1394            vec![97, 98, 99]
1395        );
1396        assert!(keys(Bound::Included(&50), Bound::Included(&49)).is_empty());
1397        // Out-of-range bounds clamp to the data.
1398        assert_eq!(
1399            keys(Bound::Included(&-5), Bound::Included(&2)),
1400            vec![0, 1, 2]
1401        );
1402        assert_eq!(
1403            keys(Bound::Included(&200), Bound::Unbounded),
1404            Vec::<i64>::new()
1405        );
1406    }
1407
1408    /// Fuzz `range` against `BTreeMap::range` across random data + random
1409    /// bounds (inclusive / exclusive / unbounded on each end) — the perf
1410    /// index range scan rides on this, and it's a stone (max blast radius),
1411    /// so the range walk must match the std oracle exactly.
1412    #[test]
1413    fn fuzz_range_against_btreemap() {
1414        let mut rng = Splitmix::new(0x5EED_1234_u64);
1415        const KEY_RANGE: i64 = 512;
1416        // A few tree sizes so we exercise leaf-only, shallow, and deep trees.
1417        for &n_inserts in &[0usize, 5, 40, 300, 2000] {
1418            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1419            let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1420            for _ in 0..n_inserts {
1421                let key = (rng.next() as i64).rem_euclid(KEY_RANGE);
1422                let val = rng.next() as i64;
1423                pb = pb.insert(key, val).0;
1424                oracle.insert(key, val);
1425            }
1426            for _ in 0..2000 {
1427                let a = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1428                let b = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1429                let (lo_raw, hi_raw) = if a <= b { (a, b) } else { (b, a) };
1430                let mk = |raw: i64, sel: u64| -> Bound<i64> {
1431                    match sel % 3 {
1432                        0 => Bound::Included(raw),
1433                        1 => Bound::Excluded(raw),
1434                        _ => Bound::Unbounded,
1435                    }
1436                };
1437                let lo = mk(lo_raw, rng.next());
1438                let hi = mk(hi_raw, rng.next());
1439                // `BTreeMap::range` panics on `Excluded(x)..Excluded(x)`; our
1440                // `range` yields empty there. Skip that one case for the oracle.
1441                if lo_raw == hi_raw
1442                    && matches!(lo, Bound::Excluded(_))
1443                    && matches!(hi, Bound::Excluded(_))
1444                {
1445                    continue;
1446                }
1447                let lo_ref = match &lo {
1448                    Bound::Included(k) => Bound::Included(k),
1449                    Bound::Excluded(k) => Bound::Excluded(k),
1450                    Bound::Unbounded => Bound::Unbounded,
1451                };
1452                let hi_ref = match &hi {
1453                    Bound::Included(k) => Bound::Included(k),
1454                    Bound::Excluded(k) => Bound::Excluded(k),
1455                    Bound::Unbounded => Bound::Unbounded,
1456                };
1457                let got: Vec<(i64, i64)> =
1458                    pb.range(lo_ref, hi_ref).map(|(k, v)| (*k, *v)).collect();
1459                let want: Vec<(i64, i64)> = oracle.range((lo, hi)).map(|(k, v)| (*k, *v)).collect();
1460                assert_eq!(
1461                    got, want,
1462                    "range drift n={n_inserts} lo={lo_raw:?} hi={hi_raw:?}"
1463                );
1464            }
1465        }
1466    }
1467
1468    /// Fuzz `predecessor` against a `BTreeMap` oracle (largest key < probe)
1469    /// across leaf-only, shallow, and deep trees.
1470    #[test]
1471    fn fuzz_predecessor_against_btreemap() {
1472        let mut rng = Splitmix::new(0x9E37_79B9_7F4A_7C15_u64);
1473        const KEY_RANGE: i64 = 512;
1474        for &n_inserts in &[0usize, 5, 40, 300, 2000] {
1475            let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1476            let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1477            for _ in 0..n_inserts {
1478                let key = (rng.next() as i64).rem_euclid(KEY_RANGE);
1479                let val = rng.next() as i64;
1480                pb = pb.insert(key, val).0;
1481                oracle.insert(key, val);
1482            }
1483            for _ in 0..2000 {
1484                let probe = (rng.next() as i64).rem_euclid(KEY_RANGE + 40) - 20;
1485                let got = pb.predecessor(&probe).map(|(k, v)| (*k, *v));
1486                let want = oracle.range(..probe).next_back().map(|(k, v)| (*k, *v));
1487                assert_eq!(got, want, "predecessor drift n={n_inserts} probe={probe}");
1488            }
1489        }
1490    }
1491
1492    #[test]
1493    fn iter_rev_yields_descending() {
1494        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1495        for &k in &[7_i64, 3, 11, 1, 9, 5, 14, 2, 8, 12, 4, 6, 10, 13] {
1496            pb = pb.insert(k, k * 2).0;
1497        }
1498        let collected: Vec<(i64, i64)> = pb.iter_rev().map(|(k, v)| (*k, *v)).collect();
1499        let expected: Vec<(i64, i64)> = (1..=14).rev().map(|k| (k, k * 2)).collect();
1500        assert_eq!(collected, expected);
1501    }
1502
1503    #[test]
1504    fn iter_rev_handles_taller_tree() {
1505        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1506        for i in 0..500_i64 {
1507            pb = pb.insert(i, i).0;
1508        }
1509        let collected: Vec<i64> = pb.iter_rev().map(|(k, _)| *k).collect();
1510        let expected: Vec<i64> = (0..500).rev().collect();
1511        assert_eq!(collected, expected);
1512    }
1513
1514    #[test]
1515    fn iter_rev_empty_map_returns_nothing() {
1516        let pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1517        assert_eq!(pb.iter_rev().count(), 0);
1518    }
1519
1520    #[test]
1521    fn iter_rev_lazy_stops_at_take() {
1522        // Critical for the ORDER BY DESC + LIMIT N executor path: only
1523        // the first N entries are touched, not the full N-entry walk.
1524        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1525        for i in 0..10000_i64 {
1526            pb = pb.insert(i, i).0;
1527        }
1528        let top5: Vec<i64> = pb.iter_rev().take(5).map(|(k, _)| *k).collect();
1529        assert_eq!(top5, vec![9999, 9998, 9997, 9996, 9995]);
1530    }
1531
1532    /// SplitMix-style PRNG so the fuzz oracle is reproducible.
1533    struct Splitmix(u64);
1534    impl Splitmix {
1535        fn new(seed: u64) -> Self {
1536            Self(seed)
1537        }
1538        fn next(&mut self) -> u64 {
1539            self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
1540            let mut x = self.0;
1541            x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
1542            x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
1543            x ^ (x >> 31)
1544        }
1545    }
1546
1547    /// 100K-step random `insert` / `get` fuzz against `std::BTreeMap`.
1548    /// Validates split/merge/replace semantics across the full tree depth.
1549    #[test]
1550    fn fuzz_oracle_against_std_btreemap() {
1551        let mut pb: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1552        let mut oracle: BTreeMap<i64, i64> = BTreeMap::new();
1553        let mut rng = Splitmix::new(0xC0FFEE_u64);
1554        const STEPS: usize = 100_000;
1555        // Use a bounded key range so we hit replaces, not just inserts.
1556        const KEY_RANGE: i64 = 4096;
1557        for step in 0..STEPS {
1558            let op = rng.next() % 3; // 0/1: insert, 2: get-check
1559            let key = (rng.next() as i64) % KEY_RANGE;
1560            match op {
1561                0 | 1 => {
1562                    let val = rng.next() as i64;
1563                    let (new_pb, prev_pb) = pb.insert(key, val);
1564                    let prev_oracle = oracle.insert(key, val);
1565                    assert_eq!(prev_pb, prev_oracle, "prev drift @ step {step}, key {key}");
1566                    pb = new_pb;
1567                    assert_eq!(pb.len(), oracle.len(), "len drift @ step {step}");
1568                }
1569                2 => {
1570                    let pb_v = pb.get(&key).copied();
1571                    let oracle_v = oracle.get(&key).copied();
1572                    assert_eq!(pb_v, oracle_v, "get drift @ step {step}, key {key}");
1573                }
1574                _ => unreachable!(),
1575            }
1576        }
1577        // Final sweep: every key in the oracle must match.
1578        for (k, v) in &oracle {
1579            assert_eq!(pb.get(k), Some(v), "final drift at key {k}");
1580        }
1581        // And iter must produce the same sorted sequence.
1582        let pb_collected: Vec<(i64, i64)> = pb.iter().map(|(k, v)| (*k, *v)).collect();
1583        let oracle_collected: Vec<(i64, i64)> = oracle.iter().map(|(k, v)| (*k, *v)).collect();
1584        assert_eq!(pb_collected, oracle_collected);
1585    }
1586
1587    /// Clone-isolation: branch A → B and C, mutate independently, verify
1588    /// each handle reads back its own state without leaking into others.
1589    #[test]
1590    fn fuzz_oracle_clone_isolation() {
1591        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1592        let mut oracle_a: BTreeMap<i64, i64> = BTreeMap::new();
1593        let mut rng = Splitmix::new(0xDECAFBAD_u64);
1594        for _ in 0..1_000 {
1595            let k = (rng.next() as i64) % 1000;
1596            let v = rng.next() as i64;
1597            a = a.insert(k, v).0;
1598            oracle_a.insert(k, v);
1599        }
1600        // Branch.
1601        let mut b = a.clone();
1602        let mut oracle_b = oracle_a.clone();
1603        let mut c = a.clone();
1604        let mut oracle_c = oracle_a.clone();
1605        for _ in 0..500 {
1606            let k = (rng.next() as i64) % 2000;
1607            let v = rng.next() as i64;
1608            b = b.insert(k, v).0;
1609            oracle_b.insert(k, v);
1610        }
1611        for _ in 0..300 {
1612            let k = (rng.next() as i64) % 500;
1613            let v = rng.next() as i64;
1614            c = c.insert(k, v).0;
1615            oracle_c.insert(k, v);
1616        }
1617        for (k, v) in &oracle_a {
1618            assert_eq!(a.get(k), Some(v), "A drift at {k}");
1619        }
1620        for (k, v) in &oracle_b {
1621            assert_eq!(b.get(k), Some(v), "B drift at {k}");
1622        }
1623        for (k, v) in &oracle_c {
1624            assert_eq!(c.get(k), Some(v), "C drift at {k}");
1625        }
1626    }
1627
1628    #[test]
1629    fn partial_eq_compares_by_elements() {
1630        let mut a: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1631        let mut b: PersistentBTreeMap<i64, i64> = PersistentBTreeMap::new();
1632        // Build the same end-state via different insertion orders → tree
1633        // shapes likely differ, but PartialEq compares by iter().
1634        for &k in &[5_i64, 2, 8, 1, 7, 3, 6, 4] {
1635            a = a.insert(k, k * 10).0;
1636        }
1637        for &k in &[1_i64, 2, 3, 4, 5, 6, 7, 8] {
1638            b = b.insert(k, k * 10).0;
1639        }
1640        assert_eq!(a, b);
1641        let (a, _) = a.insert(9, 90);
1642        assert_ne!(a, b);
1643    }
1644}