Skip to main content

bplus_index/
lib.rs

1//! Arena-backed B+Tree for in-memory sorted indexes.
2//!
3//! `BPlusIndex<K, V>` is a sorted multimap where entries are `(K, V)` pairs.
4//! Multiple values per key are supported natively -- the `(K, V)` pair is the
5//! unique identity of each entry. This makes it suitable for secondary indexes
6//! in data-oriented designs where one key maps to many row identifiers.
7//!
8//! # Design
9//!
10//! - **Arena storage**: all nodes live in contiguous `Vec`s, indexed by `u32` ids.
11//!   No `Box`, no pointer chasing between siblings during range scans.
12//! - **Cache-friendly leaves**: each leaf holds up to 64 keys and 64 values in
13//!   inline `ArrayVec`s. Binary search within a leaf touches 1-2 cache lines.
14//! - **Linked leaves**: leaves form a doubly-linked list for O(log n + k) range
15//!   scans without climbing back up the tree.
16//! - **Zero `unsafe`**: built on top of `arrayvec` which encapsulates all the
17//!   `MaybeUninit` machinery.
18//! - **Non-unique key support**: inner node separators store `(K, V)` tuples for
19//!   correct routing even when thousands of entries share the same key.
20//!
21//! # Complexity
22//!
23//! | Operation      | Time           | Notes                                   |
24//! |----------------|----------------|-----------------------------------------|
25//! | `insert`       | O(log n)       | Amortized, with rare leaf/inner splits  |
26//! | `delete`       | O(log n)       | With borrow-or-merge rebalancing        |
27//! | `update`       | O(log n)       | Delete + insert                         |
28//! | `lookup_unique`| O(log n)       | Returns first value for a given key     |
29//! | `lookup_range` | O(log n + k)   | All values for a given key              |
30//! | `range`        | O(log n + k)   | Arbitrary key range with bounds         |
31//! | `iter`         | O(n)           | Full scan via leaf chain                |
32//!
33//! # Example
34//!
35//! ```
36//! use bplus_index::BPlusIndex;
37//!
38//! let mut idx = BPlusIndex::<i32, u64>::new();
39//!
40//! // Non-unique: multiple values for the same key
41//! idx.insert(10, 100);
42//! idx.insert(10, 200);
43//! idx.insert(20, 300);
44//!
45//! // Lookup all values for key 10
46//! let vals: Vec<_> = idx.lookup_range(&10).copied().collect();
47//! assert_eq!(vals, vec![100, 200]);
48//!
49//! // Range scan
50//! let entries: Vec<_> = idx.range(5..15).collect();
51//! assert_eq!(entries.len(), 2);
52//! ```
53
54use arrayvec::ArrayVec;
55use std::cmp::Ordering;
56use std::ops::{Bound, RangeBounds};
57
58// ---------------------------------------------------------------------------
59// Constants
60// ---------------------------------------------------------------------------
61
62/// Maximum number of keys per leaf node.
63const LEAF_CAP: usize = 64;
64
65/// Maximum number of keys (separators) per inner node.
66const INNER_CAP: usize = 64;
67
68/// Maximum number of children per inner node (INNER_CAP + 1).
69const INNER_CHILDREN: usize = INNER_CAP + 1;
70
71/// Minimum occupancy for a leaf before rebalancing is triggered.
72const MIN_LEAF: usize = LEAF_CAP / 4;
73
74/// Minimum occupancy for an inner node before rebalancing is triggered.
75const MIN_INNER: usize = INNER_CAP / 4;
76
77/// Sentinel value indicating "no node" in prev/next links.
78const NONE: u32 = u32::MAX;
79
80/// Maximum tree depth. Supports up to 64^20 entries (effectively unlimited).
81const MAX_DEPTH: usize = 20;
82
83// ---------------------------------------------------------------------------
84// ArrayVec helpers
85// ---------------------------------------------------------------------------
86
87/// Insert `val` at position `idx`, shifting elements to the right.
88fn av_insert<T, const N: usize>(av: &mut ArrayVec<T, N>, idx: usize, val: T) {
89    av.insert(idx, val);
90}
91
92/// Split `av` at position `at`, returning a new `ArrayVec` with `[at..]`.
93/// The original keeps `[..at]`.
94fn av_split_off<T, const N: usize>(av: &mut ArrayVec<T, N>, at: usize) -> ArrayVec<T, N> {
95    let mut right = ArrayVec::new();
96    for item in av.drain(at..) {
97        right.push(item);
98    }
99    right
100}
101
102/// Move all elements from `src` into `dst`.
103fn av_append<T, const N: usize>(dst: &mut ArrayVec<T, N>, src: &mut ArrayVec<T, N>) {
104    for item in src.drain(..) {
105        dst.push(item);
106    }
107}
108
109// ---------------------------------------------------------------------------
110// Leaf node
111// ---------------------------------------------------------------------------
112
113/// A leaf node in the B+Tree.
114///
115/// Stores up to [`LEAF_CAP`] key-value pairs in sorted order. Leaves are
116/// linked via `prev`/`next` indices for efficient range scans.
117struct LeafNode<K, V> {
118    keys: ArrayVec<K, LEAF_CAP>,
119    vals: ArrayVec<V, LEAF_CAP>,
120    prev: u32,
121    next: u32,
122}
123
124impl<K, V> LeafNode<K, V> {
125    fn new() -> Self {
126        Self {
127            keys: ArrayVec::new(),
128            vals: ArrayVec::new(),
129            prev: NONE,
130            next: NONE,
131        }
132    }
133
134    #[inline]
135    fn len(&self) -> usize {
136        self.keys.len()
137    }
138
139    #[inline]
140    fn is_full(&self) -> bool {
141        self.keys.is_full()
142    }
143
144    /// Returns `true` if this leaf has enough entries to lend one to a sibling.
145    fn can_lend(&self) -> bool {
146        self.keys.len() > MIN_LEAF
147    }
148}
149
150impl<K: Ord, V> LeafNode<K, V> {
151    /// Binary search on the composite `(K, V)` ordering.
152    ///
153    /// Returns `Ok(idx)` on exact match, `Err(idx)` for the insertion point.
154    fn search_kv_pos(&self, k: &K, v: &V) -> Result<usize, usize>
155    where
156        V: Ord,
157    {
158        let mut lo = 0usize;
159        let mut hi = self.len();
160        while lo < hi {
161            let mid = lo + (hi - lo) / 2;
162            match self.keys[mid].cmp(k).then_with(|| self.vals[mid].cmp(v)) {
163                Ordering::Less => lo = mid + 1,
164                Ordering::Greater => hi = mid,
165                Ordering::Equal => return Ok(mid),
166            }
167        }
168        Err(lo)
169    }
170
171    /// First position where `key >= k` (lower bound).
172    fn lower_bound_k(&self, k: &K) -> usize {
173        self.keys.partition_point(|sk| sk < k)
174    }
175
176    /// First position where `key > k` (upper bound).
177    fn upper_bound_k(&self, k: &K) -> usize {
178        self.keys.partition_point(|sk| sk <= k)
179    }
180}
181
182// ---------------------------------------------------------------------------
183// Inner node
184// ---------------------------------------------------------------------------
185
186/// An inner (branch) node in the B+Tree.
187///
188/// Stores separators as `(K, V)` pairs rather than just `K`. This is required
189/// for correct child routing when keys are not unique: if many entries share
190/// the same K, the V component of the separator disambiguates which subtree
191/// contains a given `(K, V)` pair.
192struct InnerNode<K, V> {
193    keys: ArrayVec<K, INNER_CAP>,
194    vals: ArrayVec<V, INNER_CAP>,
195    children: ArrayVec<u32, INNER_CHILDREN>,
196}
197
198impl<K, V> InnerNode<K, V> {
199    fn new() -> Self {
200        Self {
201            keys: ArrayVec::new(),
202            vals: ArrayVec::new(),
203            children: ArrayVec::new(),
204        }
205    }
206
207    fn len(&self) -> usize {
208        self.keys.len()
209    }
210
211    fn is_full(&self) -> bool {
212        self.keys.is_full()
213    }
214
215    /// Returns `true` if this inner node can lend a child to a sibling.
216    fn can_lend(&self) -> bool {
217        self.keys.len() > MIN_INNER
218    }
219}
220
221impl<K: Ord, V: Ord> InnerNode<K, V> {
222    /// Find the child index for a given `(K, V)` using upper-bound semantics.
223    ///
224    /// Returns the index of the first separator strictly greater than `(k, v)`.
225    /// The child at that index is the correct subtree for descent.
226    fn find_child_kv(&self, k: &K, v: &V) -> usize {
227        let mut lo = 0usize;
228        let mut hi = self.len();
229        while lo < hi {
230            let mid = lo + (hi - lo) / 2;
231            match self.keys[mid].cmp(k).then_with(|| self.vals[mid].cmp(v)) {
232                Ordering::Less | Ordering::Equal => lo = mid + 1,
233                Ordering::Greater => hi = mid,
234            }
235        }
236        lo
237    }
238
239    /// Find the child index using K-only lower-bound semantics.
240    ///
241    /// Used for `lookup_unique`, `lookup_range`, and `range` queries where we
242    /// want to find the leftmost subtree that might contain a given key.
243    fn find_child_k_lower(&self, k: &K) -> usize {
244        self.keys.partition_point(|sk| sk < k)
245    }
246}
247
248// ---------------------------------------------------------------------------
249// Descent path (stack-allocated)
250// ---------------------------------------------------------------------------
251
252#[derive(Clone, Copy)]
253struct PathEntry {
254    node: u32,
255    child_pos: usize,
256}
257
258/// Records the path from root to leaf during descent, so that insert/delete
259/// can propagate splits and merges back up without re-descending.
260struct Path {
261    entries: [PathEntry; MAX_DEPTH],
262    len: usize,
263}
264
265impl Path {
266    fn new() -> Self {
267        Self {
268            entries: [PathEntry {
269                node: 0,
270                child_pos: 0,
271            }; MAX_DEPTH],
272            len: 0,
273        }
274    }
275
276    fn push(&mut self, node: u32, child_pos: usize) {
277        self.entries[self.len] = PathEntry { node, child_pos };
278        self.len += 1;
279    }
280}
281
282// ---------------------------------------------------------------------------
283// BPlusIndex
284// ---------------------------------------------------------------------------
285
286/// An arena-backed B+Tree sorted multimap.
287///
288/// Entries are `(K, V)` pairs sorted lexicographically. The pair is the unique
289/// identity: inserting the same `(K, V)` twice is a no-op, but inserting
290/// `(K, V1)` and `(K, V2)` stores both.
291///
292/// Nodes are stored in contiguous `Vec`s and referenced by `u32` indices.
293/// Freed nodes are recycled via an internal free list.
294pub struct BPlusIndex<K, V> {
295    leaves: Vec<LeafNode<K, V>>,
296    inners: Vec<InnerNode<K, V>>,
297    free_leaves: Vec<u32>,
298    free_inners: Vec<u32>,
299    root: u32,
300    height: u32,
301    len: usize,
302    first_leaf: u32,
303}
304
305impl<K: Ord + Clone, V: Ord + Clone> BPlusIndex<K, V> {
306    /// Creates a new empty index.
307    pub fn new() -> Self {
308        let mut idx = Self {
309            leaves: Vec::new(),
310            inners: Vec::new(),
311            free_leaves: Vec::new(),
312            free_inners: Vec::new(),
313            root: 0,
314            height: 0,
315            len: 0,
316            first_leaf: 0,
317        };
318        idx.alloc_leaf(); // leaf 0 = empty root
319        idx
320    }
321
322    /// Returns the number of entries in the index.
323    pub fn len(&self) -> usize {
324        self.len
325    }
326
327    /// Returns `true` if the index contains no entries.
328    pub fn is_empty(&self) -> bool {
329        self.len == 0
330    }
331
332    // -- Arena helpers ------------------------------------------------------
333
334    fn alloc_leaf(&mut self) -> u32 {
335        if let Some(id) = self.free_leaves.pop() {
336            self.leaves[id as usize] = LeafNode::new();
337            id
338        } else {
339            let id = self.leaves.len() as u32;
340            self.leaves.push(LeafNode::new());
341            id
342        }
343    }
344
345    fn free_leaf(&mut self, id: u32) {
346        self.free_leaves.push(id);
347    }
348
349    fn alloc_inner(&mut self) -> u32 {
350        if let Some(id) = self.free_inners.pop() {
351            self.inners[id as usize] = InnerNode::new();
352            id
353        } else {
354            let id = self.inners.len() as u32;
355            self.inners.push(InnerNode::new());
356            id
357        }
358    }
359
360    fn free_inner(&mut self, id: u32) {
361        self.free_inners.push(id);
362    }
363
364    #[inline]
365    fn leaf(&self, id: u32) -> &LeafNode<K, V> {
366        &self.leaves[id as usize]
367    }
368
369    #[inline]
370    fn leaf_mut(&mut self, id: u32) -> &mut LeafNode<K, V> {
371        &mut self.leaves[id as usize]
372    }
373
374    #[inline]
375    fn inner(&self, id: u32) -> &InnerNode<K, V> {
376        &self.inners[id as usize]
377    }
378
379    #[inline]
380    fn inner_mut(&mut self, id: u32) -> &mut InnerNode<K, V> {
381        &mut self.inners[id as usize]
382    }
383
384    // -- Descent ------------------------------------------------------------
385
386    /// Descend using full `(K, V)` comparison. Used by insert and delete to
387    /// find the exact leaf and record the path for split/merge propagation.
388    fn descend_kv(&self, k: &K, v: &V) -> (u32, Path) {
389        let mut path = Path::new();
390        if self.height == 0 {
391            return (self.root, path);
392        }
393        let mut node = self.root;
394        for level in (1..=self.height).rev() {
395            let inner = self.inner(node);
396            let child_pos = inner.find_child_kv(k, v);
397            path.push(node, child_pos);
398            node = inner.children[child_pos];
399            if level == 1 {
400                return (node, path);
401            }
402        }
403        unreachable!()
404    }
405
406    /// Descend using K-only lower-bound. Used by lookups and range queries to
407    /// find the leftmost leaf that could contain the key.
408    fn descend_k(&self, k: &K) -> u32 {
409        if self.height == 0 {
410            return self.root;
411        }
412        let mut node = self.root;
413        for level in (1..=self.height).rev() {
414            let inner = self.inner(node);
415            let child_pos = inner.find_child_k_lower(k);
416            node = inner.children[child_pos];
417            if level == 1 {
418                return node;
419            }
420        }
421        unreachable!()
422    }
423
424    // -- Insert -------------------------------------------------------------
425
426    /// Inserts a `(key, val)` pair into the index.
427    ///
428    /// If the exact `(key, val)` pair already exists, this is a no-op.
429    /// Multiple values for the same key are allowed.
430    pub fn insert(&mut self, key: K, val: V) {
431        let (leaf_id, path) = self.descend_kv(&key, &val);
432        let leaf = self.leaf(leaf_id);
433
434        let pos = match leaf.search_kv_pos(&key, &val) {
435            Ok(_) => return, // duplicate (K, V)
436            Err(p) => p,
437        };
438
439        if !self.leaf(leaf_id).is_full() {
440            let leaf = self.leaf_mut(leaf_id);
441            av_insert(&mut leaf.keys, pos, key);
442            av_insert(&mut leaf.vals, pos, val);
443            self.len += 1;
444            return;
445        }
446
447        // Leaf is full: split then propagate upward.
448        let (right_id, sep_k, sep_v) = self.split_leaf(leaf_id, pos, key, val);
449        self.propagate_split(path, sep_k, sep_v, right_id);
450        self.len += 1;
451    }
452
453    /// Split a full leaf at the midpoint, inserting `(key, val)` at `pos`.
454    /// Returns the new right leaf id and the separator to promote.
455    fn split_leaf(&mut self, leaf_id: u32, pos: usize, key: K, val: V) -> (u32, K, V) {
456        let split = LEAF_CAP / 2;
457
458        let mut right_keys = av_split_off(&mut self.leaf_mut(leaf_id).keys, split);
459        let mut right_vals = av_split_off(&mut self.leaf_mut(leaf_id).vals, split);
460
461        if pos <= split {
462            let leaf = self.leaf_mut(leaf_id);
463            av_insert(&mut leaf.keys, pos, key);
464            av_insert(&mut leaf.vals, pos, val);
465        } else {
466            av_insert(&mut right_keys, pos - split, key);
467            av_insert(&mut right_vals, pos - split, val);
468        }
469
470        let sep_k = right_keys[0].clone();
471        let sep_v = right_vals[0].clone();
472
473        let right_id = self.alloc_leaf();
474        let old_next = self.leaf(leaf_id).next;
475
476        let right = self.leaf_mut(right_id);
477        right.keys = right_keys;
478        right.vals = right_vals;
479        right.prev = leaf_id;
480        right.next = old_next;
481
482        self.leaf_mut(leaf_id).next = right_id;
483
484        if old_next != NONE {
485            self.leaf_mut(old_next).prev = right_id;
486        }
487
488        (right_id, sep_k, sep_v)
489    }
490
491    /// Propagate a split upward through inner nodes. If the root overflows,
492    /// a new root is created (increasing tree height by 1).
493    fn propagate_split(&mut self, path: Path, mut sep_k: K, mut sep_v: V, mut right_child: u32) {
494        for i in (0..path.len).rev() {
495            let inner_id = path.entries[i].node;
496            let pos = self.inner(inner_id).find_child_kv(&sep_k, &sep_v);
497
498            if !self.inner(inner_id).is_full() {
499                let inner = self.inner_mut(inner_id);
500                av_insert(&mut inner.keys, pos, sep_k);
501                av_insert(&mut inner.vals, pos, sep_v);
502                av_insert(&mut inner.children, pos + 1, right_child);
503                return;
504            }
505
506            let (new_right, med_k, med_v) =
507                self.split_inner(inner_id, pos, sep_k, sep_v, right_child);
508            sep_k = med_k;
509            sep_v = med_v;
510            right_child = new_right;
511        }
512
513        self.grow_root(sep_k, sep_v, right_child);
514    }
515
516    /// Split a full inner node, returning the new right node and the median
517    /// separator to promote to the parent.
518    fn split_inner(
519        &mut self,
520        inner_id: u32,
521        pos: usize,
522        key: K,
523        val: V,
524        child: u32,
525    ) -> (u32, K, V) {
526        let split = INNER_CAP / 2;
527
528        let mut right_keys = av_split_off(&mut self.inner_mut(inner_id).keys, split + 1);
529        let mut right_vals = av_split_off(&mut self.inner_mut(inner_id).vals, split + 1);
530        let mut right_children = av_split_off(&mut self.inner_mut(inner_id).children, split + 1);
531
532        let med_k = self.inner_mut(inner_id).keys.remove(split);
533        let med_v = self.inner_mut(inner_id).vals.remove(split);
534
535        if pos <= split {
536            let inner = self.inner_mut(inner_id);
537            av_insert(&mut inner.keys, pos, key);
538            av_insert(&mut inner.vals, pos, val);
539            av_insert(&mut inner.children, pos + 1, child);
540        } else {
541            let rp = pos - split - 1;
542            av_insert(&mut right_keys, rp, key);
543            av_insert(&mut right_vals, rp, val);
544            av_insert(&mut right_children, rp + 1, child);
545        }
546
547        let right_id = self.alloc_inner();
548        let right = self.inner_mut(right_id);
549        right.keys = right_keys;
550        right.vals = right_vals;
551        right.children = right_children;
552
553        (right_id, med_k, med_v)
554    }
555
556    /// Create a new root containing one separator and two children.
557    fn grow_root(&mut self, sep_k: K, sep_v: V, right_child: u32) {
558        let old_root = self.root;
559        let new_root = self.alloc_inner();
560        let root = self.inner_mut(new_root);
561        root.keys.push(sep_k);
562        root.vals.push(sep_v);
563        root.children.push(old_root);
564        root.children.push(right_child);
565        self.root = new_root;
566        self.height += 1;
567    }
568
569    // -- Delete -------------------------------------------------------------
570
571    /// Removes the exact `(key, val)` pair from the index.
572    ///
573    /// Returns `true` if the entry was found and removed, `false` otherwise.
574    pub fn delete(&mut self, key: &K, val: &V) -> bool {
575        let (leaf_id, path) = self.descend_kv(key, val);
576
577        let pos = match self.leaf(leaf_id).search_kv_pos(key, val) {
578            Ok(p) => p,
579            Err(_) => return false,
580        };
581
582        self.leaf_mut(leaf_id).keys.remove(pos);
583        self.leaf_mut(leaf_id).vals.remove(pos);
584        self.len -= 1;
585
586        // Root leaf never needs rebalancing.
587        if self.height == 0 {
588            return true;
589        }
590
591        if self.leaf(leaf_id).len() >= MIN_LEAF {
592            return true;
593        }
594
595        self.rebalance_leaf(leaf_id, &path);
596        true
597    }
598
599    /// Rebalance a leaf that has fallen below minimum occupancy.
600    ///
601    /// Tries in order: borrow from right sibling, borrow from left sibling,
602    /// merge with a sibling. Merges may cascade up through inner nodes.
603    fn rebalance_leaf(&mut self, leaf_id: u32, path: &Path) {
604        let parent_entry = path.entries[path.len - 1];
605        let parent_id = parent_entry.node;
606        let child_pos = parent_entry.child_pos;
607
608        // Try borrow from right sibling.
609        if child_pos + 1 < self.inner(parent_id).children.len() {
610            let right_id = self.inner(parent_id).children[child_pos + 1];
611            if self.leaf(right_id).can_lend() {
612                let rk = self.leaf_mut(right_id).keys.remove(0);
613                let rv = self.leaf_mut(right_id).vals.remove(0);
614                self.leaf_mut(leaf_id).keys.push(rk);
615                self.leaf_mut(leaf_id).vals.push(rv);
616
617                let new_sep_k = self.leaf(right_id).keys[0].clone();
618                let new_sep_v = self.leaf(right_id).vals[0].clone();
619                self.inner_mut(parent_id).keys[child_pos] = new_sep_k;
620                self.inner_mut(parent_id).vals[child_pos] = new_sep_v;
621                return;
622            }
623        }
624
625        // Try borrow from left sibling.
626        if child_pos > 0 {
627            let left_id = self.inner(parent_id).children[child_pos - 1];
628            if self.leaf(left_id).can_lend() {
629                let lk = self.leaf_mut(left_id).keys.pop().unwrap();
630                let lv = self.leaf_mut(left_id).vals.pop().unwrap();
631                av_insert(&mut self.leaf_mut(leaf_id).keys, 0, lk);
632                av_insert(&mut self.leaf_mut(leaf_id).vals, 0, lv);
633
634                let new_sep_k = self.leaf(leaf_id).keys[0].clone();
635                let new_sep_v = self.leaf(leaf_id).vals[0].clone();
636                self.inner_mut(parent_id).keys[child_pos - 1] = new_sep_k;
637                self.inner_mut(parent_id).vals[child_pos - 1] = new_sep_v;
638                return;
639            }
640        }
641
642        // Merge with a sibling.
643        if child_pos + 1 < self.inner(parent_id).children.len() {
644            let right_id = self.inner(parent_id).children[child_pos + 1];
645            self.merge_leaves(leaf_id, right_id);
646            self.inner_mut(parent_id).keys.remove(child_pos);
647            self.inner_mut(parent_id).vals.remove(child_pos);
648            self.inner_mut(parent_id).children.remove(child_pos + 1);
649            self.free_leaf(right_id);
650        } else {
651            let left_id = self.inner(parent_id).children[child_pos - 1];
652            self.merge_leaves(left_id, leaf_id);
653            self.inner_mut(parent_id).keys.remove(child_pos - 1);
654            self.inner_mut(parent_id).vals.remove(child_pos - 1);
655            self.inner_mut(parent_id).children.remove(child_pos);
656            self.free_leaf(leaf_id);
657        }
658
659        self.maybe_shrink_or_rebalance_inner(parent_id, path, path.len - 1);
660    }
661
662    /// Merge the right leaf into the left leaf and fix the linked list.
663    fn merge_leaves(&mut self, left_id: u32, right_id: u32) {
664        let right = self.leaf_mut(right_id);
665        let mut rk = std::mem::replace(&mut right.keys, ArrayVec::new());
666        let mut rv = std::mem::replace(&mut right.vals, ArrayVec::new());
667        let right_next = right.next;
668
669        let left = self.leaf_mut(left_id);
670        av_append(&mut left.keys, &mut rk);
671        av_append(&mut left.vals, &mut rv);
672        left.next = right_next;
673
674        if right_next != NONE {
675            self.leaf_mut(right_next).prev = left_id;
676        }
677    }
678
679    /// After a merge, check if the parent needs shrinking (root with one child)
680    /// or rebalancing (inner node below minimum occupancy).
681    fn maybe_shrink_or_rebalance_inner(&mut self, inner_id: u32, path: &Path, path_idx: usize) {
682        // Root with zero separators -> shrink the tree by one level.
683        if inner_id == self.root {
684            if self.inner(inner_id).keys.is_empty() {
685                let only_child = self.inner(inner_id).children[0];
686                self.free_inner(inner_id);
687                self.root = only_child;
688                self.height -= 1;
689                if self.height == 0 {
690                    self.first_leaf = self.root;
691                }
692            }
693            return;
694        }
695
696        if self.inner(inner_id).len() >= MIN_INNER {
697            return;
698        }
699
700        self.rebalance_inner(inner_id, path, path_idx);
701    }
702
703    /// Rebalance an inner node: borrow from a sibling or merge.
704    fn rebalance_inner(&mut self, inner_id: u32, path: &Path, path_idx: usize) {
705        if path_idx == 0 {
706            return;
707        }
708
709        let parent_entry = path.entries[path_idx - 1];
710        let parent_id = parent_entry.node;
711        let child_pos = parent_entry.child_pos;
712
713        // Try borrow from right sibling.
714        if child_pos + 1 < self.inner(parent_id).children.len() {
715            let right_id = self.inner(parent_id).children[child_pos + 1];
716            if self.inner(right_id).can_lend() {
717                let sep_k = self.inner_mut(parent_id).keys.remove(child_pos);
718                let sep_v = self.inner_mut(parent_id).vals.remove(child_pos);
719
720                let rk = self.inner_mut(right_id).keys.remove(0);
721                let rv = self.inner_mut(right_id).vals.remove(0);
722                let rc = self.inner_mut(right_id).children.remove(0);
723
724                av_insert(&mut self.inner_mut(parent_id).keys, child_pos, rk);
725                av_insert(&mut self.inner_mut(parent_id).vals, child_pos, rv);
726
727                self.inner_mut(inner_id).keys.push(sep_k);
728                self.inner_mut(inner_id).vals.push(sep_v);
729                self.inner_mut(inner_id).children.push(rc);
730                return;
731            }
732        }
733
734        // Try borrow from left sibling.
735        if child_pos > 0 {
736            let left_id = self.inner(parent_id).children[child_pos - 1];
737            if self.inner(left_id).can_lend() {
738                let sep_k = self.inner_mut(parent_id).keys.remove(child_pos - 1);
739                let sep_v = self.inner_mut(parent_id).vals.remove(child_pos - 1);
740
741                let lk = self.inner_mut(left_id).keys.pop().unwrap();
742                let lv = self.inner_mut(left_id).vals.pop().unwrap();
743                let lc = self.inner_mut(left_id).children.pop().unwrap();
744
745                av_insert(&mut self.inner_mut(parent_id).keys, child_pos - 1, lk);
746                av_insert(&mut self.inner_mut(parent_id).vals, child_pos - 1, lv);
747
748                av_insert(&mut self.inner_mut(inner_id).keys, 0, sep_k);
749                av_insert(&mut self.inner_mut(inner_id).vals, 0, sep_v);
750                av_insert(&mut self.inner_mut(inner_id).children, 0, lc);
751                return;
752            }
753        }
754
755        // Merge with right sibling.
756        if child_pos + 1 < self.inner(parent_id).children.len() {
757            let right_id = self.inner(parent_id).children[child_pos + 1];
758
759            let sep_k = self.inner_mut(parent_id).keys.remove(child_pos);
760            let sep_v = self.inner_mut(parent_id).vals.remove(child_pos);
761            self.inner_mut(parent_id).children.remove(child_pos + 1);
762
763            self.inner_mut(inner_id).keys.push(sep_k);
764            self.inner_mut(inner_id).vals.push(sep_v);
765
766            let right = self.inner_mut(right_id);
767            let mut rk = std::mem::replace(&mut right.keys, ArrayVec::new());
768            let mut rv = std::mem::replace(&mut right.vals, ArrayVec::new());
769            let mut rc = std::mem::replace(&mut right.children, ArrayVec::new());
770
771            av_append(&mut self.inner_mut(inner_id).keys, &mut rk);
772            av_append(&mut self.inner_mut(inner_id).vals, &mut rv);
773            av_append(&mut self.inner_mut(inner_id).children, &mut rc);
774
775            self.free_inner(right_id);
776        } else {
777            // Merge into left sibling.
778            let left_id = self.inner(parent_id).children[child_pos - 1];
779
780            let sep_k = self.inner_mut(parent_id).keys.remove(child_pos - 1);
781            let sep_v = self.inner_mut(parent_id).vals.remove(child_pos - 1);
782            self.inner_mut(parent_id).children.remove(child_pos);
783
784            self.inner_mut(left_id).keys.push(sep_k);
785            self.inner_mut(left_id).vals.push(sep_v);
786
787            let cur = self.inner_mut(inner_id);
788            let mut ck = std::mem::replace(&mut cur.keys, ArrayVec::new());
789            let mut cv = std::mem::replace(&mut cur.vals, ArrayVec::new());
790            let mut cc = std::mem::replace(&mut cur.children, ArrayVec::new());
791
792            av_append(&mut self.inner_mut(left_id).keys, &mut ck);
793            av_append(&mut self.inner_mut(left_id).vals, &mut cv);
794            av_append(&mut self.inner_mut(left_id).children, &mut cc);
795
796            self.free_inner(inner_id);
797        }
798
799        self.maybe_shrink_or_rebalance_inner(parent_id, path, path_idx - 1);
800    }
801
802    // -- Update -------------------------------------------------------------
803
804    /// Re-indexes an entry by deleting `(old_key, val)` and inserting `(new_key, val)`.
805    ///
806    /// This is a convenience for the common case where the indexed field of a
807    /// row changes but the row identifier (the value) stays the same.
808    pub fn update(&mut self, old_key: &K, new_key: K, val: V) {
809        self.delete(old_key, &val);
810        self.insert(new_key, val);
811    }
812
813    // -- Lookups ------------------------------------------------------------
814
815    /// Returns a reference to the first value associated with `key`, or `None`.
816    ///
817    /// For unique indexes this is the only value. For non-unique indexes this
818    /// returns the smallest `V` for the given `K`.
819    ///
820    /// Time: O(log n).
821    pub fn lookup_unique(&self, key: &K) -> Option<&V> {
822        let leaf_id = self.descend_k(key);
823        let leaf = self.leaf(leaf_id);
824        let pos = leaf.lower_bound_k(key);
825        if pos < leaf.len() && leaf.keys[pos] == *key {
826            return Some(&leaf.vals[pos]);
827        }
828        // Key might be at the start of the next leaf (boundary case).
829        if leaf.next != NONE {
830            let next = self.leaf(leaf.next);
831            if !next.keys.is_empty() && next.keys[0] == *key {
832                return Some(&next.vals[0]);
833            }
834        }
835        None
836    }
837
838    /// Returns an iterator over all values associated with `key`.
839    ///
840    /// Values are yielded in ascending `V` order. The iterator follows the
841    /// leaf chain, so it works correctly even when entries for one key span
842    /// multiple leaves.
843    ///
844    /// Time: O(log n) for the initial descent, then O(k) for k results.
845    pub fn lookup_range<'a>(&'a self, key: &'a K) -> LookupRange<'a, K, V> {
846        let leaf_id = self.descend_k(key);
847        let leaf = self.leaf(leaf_id);
848        let pos = leaf.lower_bound_k(key);
849        LookupRange {
850            tree: self,
851            key,
852            leaf: leaf_id,
853            pos,
854        }
855    }
856
857    /// Returns an iterator over all entries `(&K, &V)` whose keys fall within
858    /// the given range bounds.
859    ///
860    /// Supports all standard range syntax: `a..b`, `a..=b`, `a..`, `..b`, `..`.
861    ///
862    /// Time: O(log n) for the initial descent, then O(k) for k results.
863    pub fn range<R: RangeBounds<K>>(&self, bounds: R) -> RangeIter<'_, K, V> {
864        let (leaf, pos) = match bounds.start_bound() {
865            Bound::Unbounded => (self.first_leaf, 0),
866            Bound::Included(k) => {
867                let lid = self.descend_k(k);
868                let l = self.leaf(lid);
869                (lid, l.lower_bound_k(k))
870            }
871            Bound::Excluded(k) => {
872                let lid = self.descend_k(k);
873                let l = self.leaf(lid);
874                (lid, l.upper_bound_k(k))
875            }
876        };
877
878        let end = match bounds.end_bound() {
879            Bound::Unbounded => RangeEnd::Unbounded,
880            Bound::Included(k) => RangeEnd::Included(k.clone()),
881            Bound::Excluded(k) => RangeEnd::Excluded(k.clone()),
882        };
883
884        RangeIter {
885            tree: self,
886            leaf,
887            pos,
888            end,
889        }
890    }
891
892    /// Returns an iterator over all entries in ascending order.
893    ///
894    /// Time: O(n).
895    pub fn iter(&self) -> RangeIter<'_, K, V> {
896        RangeIter {
897            tree: self,
898            leaf: self.first_leaf,
899            pos: 0,
900            end: RangeEnd::Unbounded,
901        }
902    }
903}
904
905impl<K: Ord + Clone, V: Ord + Clone> Default for BPlusIndex<K, V> {
906    fn default() -> Self {
907        Self::new()
908    }
909}
910
911// ---------------------------------------------------------------------------
912// Iterators
913// ---------------------------------------------------------------------------
914
915/// Iterator returned by [`BPlusIndex::lookup_range`].
916///
917/// Yields `&V` for all entries matching a specific key.
918pub struct LookupRange<'a, K, V> {
919    tree: &'a BPlusIndex<K, V>,
920    key: &'a K,
921    leaf: u32,
922    pos: usize,
923}
924
925impl<'a, K: Ord + Clone, V: Ord + Clone> Iterator for LookupRange<'a, K, V> {
926    type Item = &'a V;
927
928    fn next(&mut self) -> Option<Self::Item> {
929        loop {
930            if self.leaf == NONE {
931                return None;
932            }
933            let leaf = self.tree.leaf(self.leaf);
934            if self.pos < leaf.len() {
935                if leaf.keys[self.pos] == *self.key {
936                    let v = &leaf.vals[self.pos];
937                    self.pos += 1;
938                    return Some(v);
939                }
940                return None;
941            }
942            self.leaf = leaf.next;
943            self.pos = 0;
944        }
945    }
946}
947
948enum RangeEnd<K> {
949    Unbounded,
950    Included(K),
951    Excluded(K),
952}
953
954/// Iterator returned by [`BPlusIndex::range`] and [`BPlusIndex::iter`].
955///
956/// Yields `(&K, &V)` pairs in ascending order. Follows the leaf chain
957/// for sequential memory access.
958pub struct RangeIter<'a, K, V> {
959    tree: &'a BPlusIndex<K, V>,
960    leaf: u32,
961    pos: usize,
962    end: RangeEnd<K>,
963}
964
965impl<'a, K: Ord + Clone, V: Ord + Clone> Iterator for RangeIter<'a, K, V> {
966    type Item = (&'a K, &'a V);
967
968    fn next(&mut self) -> Option<Self::Item> {
969        loop {
970            if self.leaf == NONE {
971                return None;
972            }
973            let leaf = self.tree.leaf(self.leaf);
974            if self.pos < leaf.len() {
975                let k = &leaf.keys[self.pos];
976                match &self.end {
977                    RangeEnd::Excluded(end) if k >= end => return None,
978                    RangeEnd::Included(end) if k > end => return None,
979                    _ => {}
980                }
981                let v = &leaf.vals[self.pos];
982                self.pos += 1;
983                return Some((k, v));
984            }
985            self.leaf = leaf.next;
986            self.pos = 0;
987        }
988    }
989}
990
991#[cfg(test)]
992mod tests {
993    use super::*;
994    use std::collections::HashSet;
995    use std::fmt::Debug;
996
997    // -----------------------------------------------------------------------
998    // Structural invariant validator
999    // -----------------------------------------------------------------------
1000
1001    impl<K: Ord + Clone + Debug, V: Ord + Clone + Debug> BPlusIndex<K, V> {
1002        /// Validate all B+Tree structural invariants. Panics with a descriptive
1003        /// message on any violation.
1004        fn validate(&self) {
1005            self.validate_len();
1006            if self.height == 0 {
1007                self.validate_root_leaf();
1008            } else {
1009                self.validate_inner_recursive(self.root, self.height, None, None);
1010            }
1011            self.validate_leaf_chain();
1012            self.validate_first_leaf();
1013        }
1014
1015        fn validate_len(&self) {
1016            let actual: usize = self.iter().count();
1017            assert_eq!(
1018                actual, self.len,
1019                "len mismatch: stored {} but iter yields {}",
1020                self.len, actual
1021            );
1022        }
1023
1024        fn validate_root_leaf(&self) {
1025            let leaf = self.leaf(self.root);
1026            assert!(
1027                leaf.keys.len() == leaf.vals.len(),
1028                "root leaf: keys.len != vals.len"
1029            );
1030            // Root leaf has no minimum occupancy constraint.
1031            self.validate_leaf_sorted(self.root);
1032        }
1033
1034        fn validate_leaf_sorted(&self, id: u32) {
1035            let leaf = self.leaf(id);
1036            for i in 1..leaf.len() {
1037                let cmp = leaf.keys[i - 1]
1038                    .cmp(&leaf.keys[i])
1039                    .then_with(|| leaf.vals[i - 1].cmp(&leaf.vals[i]));
1040                assert!(
1041                    cmp == Ordering::Less,
1042                    "leaf {}: entries not strictly sorted at position {}: ({:?},{:?}) vs ({:?},{:?})",
1043                    id,
1044                    i,
1045                    leaf.keys[i - 1],
1046                    leaf.vals[i - 1],
1047                    leaf.keys[i],
1048                    leaf.vals[i]
1049                );
1050            }
1051        }
1052
1053        fn validate_inner_recursive(
1054            &self,
1055            node_id: u32,
1056            level: u32,
1057            lower: Option<(&K, &V)>,
1058            upper: Option<(&K, &V)>,
1059        ) {
1060            let inner = self.inner(node_id);
1061
1062            // children.len == keys.len + 1
1063            assert_eq!(
1064                inner.children.len(),
1065                inner.keys.len() + 1,
1066                "inner {}: children.len ({}) != keys.len ({}) + 1",
1067                node_id,
1068                inner.children.len(),
1069                inner.keys.len()
1070            );
1071
1072            // keys/vals sorted
1073            for i in 1..inner.len() {
1074                let cmp = inner.keys[i - 1]
1075                    .cmp(&inner.keys[i])
1076                    .then_with(|| inner.vals[i - 1].cmp(&inner.vals[i]));
1077                assert!(
1078                    cmp == Ordering::Less,
1079                    "inner {}: separators not sorted at position {}",
1080                    node_id,
1081                    i
1082                );
1083            }
1084
1085            // Minimum occupancy (except root)
1086            if node_id != self.root {
1087                assert!(
1088                    inner.len() >= MIN_INNER,
1089                    "inner {}: underflow, len {} < MIN_INNER {}",
1090                    node_id,
1091                    inner.len(),
1092                    MIN_INNER
1093                );
1094            }
1095
1096            // Recurse into children
1097            for ci in 0..inner.children.len() {
1098                let child_lower = if ci == 0 {
1099                    lower
1100                } else {
1101                    Some((&inner.keys[ci - 1], &inner.vals[ci - 1]))
1102                };
1103                let child_upper = if ci == inner.keys.len() {
1104                    upper
1105                } else {
1106                    Some((&inner.keys[ci], &inner.vals[ci]))
1107                };
1108
1109                if level == 1 {
1110                    // Child is a leaf
1111                    let leaf_id = inner.children[ci];
1112                    let leaf = self.leaf(leaf_id);
1113
1114                    // Leaf occupancy (non-root leaves)
1115                    if self.len > 0 {
1116                        // Allow underflow only if tree is very small
1117                        let total_leaves = self.iter().count();
1118                        if total_leaves > LEAF_CAP {
1119                            assert!(
1120                                leaf.len() >= MIN_LEAF,
1121                                "leaf {}: underflow, len {} < MIN_LEAF {}",
1122                                leaf_id,
1123                                leaf.len(),
1124                                MIN_LEAF
1125                            );
1126                        }
1127                    }
1128
1129                    self.validate_leaf_sorted(leaf_id);
1130
1131                    // All entries in leaf must be within [child_lower, child_upper)
1132                    for i in 0..leaf.len() {
1133                        if let Some((lk, lv)) = child_lower {
1134                            let cmp = leaf.keys[i].cmp(lk).then_with(|| leaf.vals[i].cmp(lv));
1135                            assert!(
1136                                cmp != Ordering::Less,
1137                                "leaf {}: entry ({:?},{:?}) below lower bound ({:?},{:?})",
1138                                leaf_id,
1139                                leaf.keys[i],
1140                                leaf.vals[i],
1141                                lk,
1142                                lv
1143                            );
1144                        }
1145                        if let Some((uk, uv)) = child_upper {
1146                            let cmp = leaf.keys[i].cmp(uk).then_with(|| leaf.vals[i].cmp(uv));
1147                            assert!(
1148                                cmp == Ordering::Less,
1149                                "leaf {}: entry ({:?},{:?}) not below upper bound ({:?},{:?})",
1150                                leaf_id,
1151                                leaf.keys[i],
1152                                leaf.vals[i],
1153                                uk,
1154                                uv
1155                            );
1156                        }
1157                    }
1158                } else {
1159                    // Child is an inner node
1160                    self.validate_inner_recursive(
1161                        inner.children[ci],
1162                        level - 1,
1163                        child_lower,
1164                        child_upper,
1165                    );
1166                }
1167            }
1168        }
1169
1170        fn validate_leaf_chain(&self) {
1171            // Walk the forward chain and check prev/next consistency.
1172            let mut visited = HashSet::new();
1173            let mut leaf_id = self.first_leaf;
1174            let mut prev_id = NONE;
1175            let mut total_entries = 0usize;
1176
1177            while leaf_id != NONE {
1178                assert!(
1179                    visited.insert(leaf_id),
1180                    "leaf chain: cycle detected at leaf {}",
1181                    leaf_id
1182                );
1183
1184                let leaf = self.leaf(leaf_id);
1185                assert_eq!(
1186                    leaf.prev, prev_id,
1187                    "leaf {}: prev is {} but expected {}",
1188                    leaf_id, leaf.prev, prev_id
1189                );
1190
1191                // Cross-leaf ordering: last of prev < first of current
1192                if prev_id != NONE && leaf.len() > 0 {
1193                    let prev_leaf = self.leaf(prev_id);
1194                    if prev_leaf.len() > 0 {
1195                        let prev_last_k = &prev_leaf.keys[prev_leaf.len() - 1];
1196                        let prev_last_v = &prev_leaf.vals[prev_leaf.len() - 1];
1197                        let cur_first_k = &leaf.keys[0];
1198                        let cur_first_v = &leaf.vals[0];
1199                        let cmp = prev_last_k
1200                            .cmp(cur_first_k)
1201                            .then_with(|| prev_last_v.cmp(cur_first_v));
1202                        assert!(
1203                            cmp == Ordering::Less,
1204                            "leaf chain: last entry of leaf {} ({:?},{:?}) >= first entry of leaf {} ({:?},{:?})",
1205                            prev_id,
1206                            prev_last_k,
1207                            prev_last_v,
1208                            leaf_id,
1209                            cur_first_k,
1210                            cur_first_v
1211                        );
1212                    }
1213                }
1214
1215                total_entries += leaf.len();
1216                prev_id = leaf_id;
1217                leaf_id = leaf.next;
1218            }
1219
1220            assert_eq!(
1221                total_entries, self.len,
1222                "leaf chain: total entries {} != stored len {}",
1223                total_entries, self.len
1224            );
1225        }
1226
1227        fn validate_first_leaf(&self) {
1228            if self.height == 0 {
1229                assert_eq!(
1230                    self.first_leaf, self.root,
1231                    "height 0 but first_leaf != root"
1232                );
1233            } else {
1234                // first_leaf should be reachable by always going to child 0
1235                let mut node = self.root;
1236                for level in (1..=self.height).rev() {
1237                    let inner = self.inner(node);
1238                    node = inner.children[0];
1239                    if level == 1 {
1240                        assert_eq!(
1241                            self.first_leaf, node,
1242                            "first_leaf {} != leftmost leaf {}",
1243                            self.first_leaf, node
1244                        );
1245                        return;
1246                    }
1247                }
1248            }
1249        }
1250    }
1251
1252    // -----------------------------------------------------------------------
1253    // Helper: validate after every operation
1254    // -----------------------------------------------------------------------
1255
1256    fn insert_and_validate(idx: &mut BPlusIndex<i32, u64>, k: i32, v: u64) {
1257        idx.insert(k, v);
1258        idx.validate();
1259    }
1260
1261    fn delete_and_validate(idx: &mut BPlusIndex<i32, u64>, k: &i32, v: &u64) -> bool {
1262        let result = idx.delete(k, v);
1263        idx.validate();
1264        result
1265    }
1266
1267    // -----------------------------------------------------------------------
1268    // Empty tree
1269    // -----------------------------------------------------------------------
1270
1271    #[test]
1272    fn empty_tree() {
1273        let idx = BPlusIndex::<i32, u64>::new();
1274        assert_eq!(idx.len(), 0);
1275        assert!(idx.is_empty());
1276        assert_eq!(idx.lookup_unique(&0), None);
1277        assert_eq!(idx.lookup_range(&0).count(), 0);
1278        assert_eq!(idx.range(..).count(), 0);
1279        assert_eq!(idx.range(0..10).count(), 0);
1280        assert_eq!(idx.iter().count(), 0);
1281        idx.validate();
1282    }
1283
1284    #[test]
1285    fn empty_tree_delete() {
1286        let mut idx = BPlusIndex::<i32, u64>::new();
1287        assert!(!idx.delete(&1, &1));
1288        idx.validate();
1289    }
1290
1291    // -----------------------------------------------------------------------
1292    // Single element
1293    // -----------------------------------------------------------------------
1294
1295    #[test]
1296    fn single_insert_delete() {
1297        let mut idx = BPlusIndex::<i32, u64>::new();
1298        insert_and_validate(&mut idx, 42, 1);
1299        assert_eq!(idx.len(), 1);
1300        assert_eq!(idx.lookup_unique(&42), Some(&1));
1301        assert!(delete_and_validate(&mut idx, &42, &1));
1302        assert_eq!(idx.len(), 0);
1303        assert_eq!(idx.lookup_unique(&42), None);
1304    }
1305
1306    // -----------------------------------------------------------------------
1307    // Duplicates
1308    // -----------------------------------------------------------------------
1309
1310    #[test]
1311    fn insert_duplicate_kv_is_noop() {
1312        let mut idx = BPlusIndex::<i32, u64>::new();
1313        insert_and_validate(&mut idx, 5, 100);
1314        insert_and_validate(&mut idx, 5, 100);
1315        assert_eq!(idx.len(), 1);
1316    }
1317
1318    // -----------------------------------------------------------------------
1319    // Non-unique keys
1320    // -----------------------------------------------------------------------
1321
1322    #[test]
1323    fn non_unique_keys_small() {
1324        let mut idx = BPlusIndex::<i32, u64>::new();
1325        insert_and_validate(&mut idx, 5, 100);
1326        insert_and_validate(&mut idx, 5, 200);
1327        insert_and_validate(&mut idx, 5, 300);
1328        insert_and_validate(&mut idx, 10, 400);
1329
1330        let vals: Vec<_> = idx.lookup_range(&5).copied().collect();
1331        assert_eq!(vals, vec![100, 200, 300]);
1332        assert_eq!(idx.lookup_unique(&5), Some(&100)); // smallest V
1333    }
1334
1335    #[test]
1336    fn non_unique_keys_spanning_leaves() {
1337        // 200 entries with the same key forces them across multiple leaves
1338        let mut idx = BPlusIndex::<i32, u64>::new();
1339        for v in 0..200u64 {
1340            insert_and_validate(&mut idx, 42, v);
1341        }
1342        assert_eq!(idx.len(), 200);
1343
1344        let vals: Vec<_> = idx.lookup_range(&42).copied().collect();
1345        assert_eq!(vals, (0..200u64).collect::<Vec<_>>());
1346
1347        // Delete every other one
1348        for v in (0..200u64).step_by(2) {
1349            assert!(delete_and_validate(&mut idx, &42, &v));
1350        }
1351        assert_eq!(idx.len(), 100);
1352
1353        let vals: Vec<_> = idx.lookup_range(&42).copied().collect();
1354        let expected: Vec<u64> = (0..200u64).filter(|v| v % 2 != 0).collect();
1355        assert_eq!(vals, expected);
1356    }
1357
1358    // -----------------------------------------------------------------------
1359    // Leaf split boundary (LEAF_CAP = 64)
1360    // -----------------------------------------------------------------------
1361
1362    #[test]
1363    fn insert_exactly_leaf_cap() {
1364        let mut idx = BPlusIndex::<i32, u64>::new();
1365        for i in 0..LEAF_CAP as i32 {
1366            insert_and_validate(&mut idx, i, i as u64);
1367        }
1368        assert_eq!(idx.len(), LEAF_CAP);
1369        assert_eq!(idx.height, 0); // still fits in root leaf
1370    }
1371
1372    #[test]
1373    fn insert_triggers_first_split() {
1374        let mut idx = BPlusIndex::<i32, u64>::new();
1375        for i in 0..=LEAF_CAP as i32 {
1376            insert_and_validate(&mut idx, i, i as u64);
1377        }
1378        assert_eq!(idx.len(), LEAF_CAP + 1);
1379        assert_eq!(idx.height, 1); // split created an inner root
1380    }
1381
1382    #[test]
1383    fn insert_triggers_inner_split() {
1384        // LEAF_CAP=64, INNER_CAP=64
1385        // Fill enough to overflow inner nodes: ~64*64 = 4096+ entries
1386        let mut idx = BPlusIndex::<i32, u64>::new();
1387        let n = LEAF_CAP * INNER_CAP + 100;
1388        for i in 0..n as i32 {
1389            idx.insert(i, i as u64);
1390        }
1391        idx.validate();
1392        assert!(idx.height >= 2, "expected height >= 2, got {}", idx.height);
1393    }
1394
1395    // -----------------------------------------------------------------------
1396    // Delete boundary cases
1397    // -----------------------------------------------------------------------
1398
1399    #[test]
1400    fn delete_nonexistent_key() {
1401        let mut idx = BPlusIndex::<i32, u64>::new();
1402        insert_and_validate(&mut idx, 1, 10);
1403        assert!(!idx.delete(&999, &10));
1404        assert!(!idx.delete(&1, &999));
1405        assert_eq!(idx.len(), 1);
1406        idx.validate();
1407    }
1408
1409    #[test]
1410    fn delete_triggers_borrow_from_right() {
1411        let mut idx = BPlusIndex::<i32, u64>::new();
1412        // Build a tree with 2 leaves
1413        for i in 0..LEAF_CAP as i32 + 10 {
1414            idx.insert(i, i as u64);
1415        }
1416        idx.validate();
1417        // Delete from left leaf until it borrows from right
1418        for i in 0..LEAF_CAP as i32 / 2 {
1419            delete_and_validate(&mut idx, &i, &(i as u64));
1420        }
1421    }
1422
1423    #[test]
1424    fn delete_triggers_borrow_from_left() {
1425        let mut idx = BPlusIndex::<i32, u64>::new();
1426        for i in 0..LEAF_CAP as i32 + 10 {
1427            idx.insert(i, i as u64);
1428        }
1429        idx.validate();
1430        // Delete from right side
1431        for i in (LEAF_CAP as i32..LEAF_CAP as i32 + 10).rev() {
1432            delete_and_validate(&mut idx, &i, &(i as u64));
1433        }
1434    }
1435
1436    #[test]
1437    fn delete_triggers_leaf_merge() {
1438        let mut idx = BPlusIndex::<i32, u64>::new();
1439        // Build a 2-leaf tree
1440        for i in 0..LEAF_CAP as i32 + 2 {
1441            idx.insert(i, i as u64);
1442        }
1443        idx.validate();
1444        assert_eq!(idx.height, 1);
1445        // Delete enough to force underflow + merge
1446        for i in (0..LEAF_CAP as i32 + 2).rev() {
1447            delete_and_validate(&mut idx, &i, &(i as u64));
1448            if idx.len() <= MIN_LEAF {
1449                break;
1450            }
1451        }
1452        // Once only MIN_LEAF entries remain, tree should have shrunk to height 0
1453        assert_eq!(idx.height, 0, "tree should have shrunk back to single leaf");
1454    }
1455
1456    #[test]
1457    fn delete_all_forward() {
1458        let mut idx = BPlusIndex::<i32, u64>::new();
1459        let n = 500;
1460        for i in 0..n {
1461            idx.insert(i, i as u64);
1462        }
1463        for i in 0..n {
1464            assert!(delete_and_validate(&mut idx, &i, &(i as u64)));
1465        }
1466        assert_eq!(idx.len(), 0);
1467    }
1468
1469    #[test]
1470    fn delete_all_reverse() {
1471        let mut idx = BPlusIndex::<i32, u64>::new();
1472        let n = 500;
1473        for i in 0..n {
1474            idx.insert(i, i as u64);
1475        }
1476        for i in (0..n).rev() {
1477            assert!(delete_and_validate(&mut idx, &i, &(i as u64)));
1478        }
1479        assert_eq!(idx.len(), 0);
1480    }
1481
1482    #[test]
1483    fn delete_all_from_middle_out() {
1484        let mut idx = BPlusIndex::<i32, u64>::new();
1485        let n = 500i32;
1486        for i in 0..n {
1487            idx.insert(i, i as u64);
1488        }
1489        // Delete from the center outward
1490        let mid = n / 2;
1491        for d in 0..mid {
1492            delete_and_validate(&mut idx, &(mid + d), &((mid + d) as u64));
1493            if mid - d > 0 {
1494                delete_and_validate(&mut idx, &(mid - d - 1), &((mid - d - 1) as u64));
1495            }
1496        }
1497        assert_eq!(idx.len(), 0);
1498    }
1499
1500    #[test]
1501    fn delete_cascade_inner_rebalance() {
1502        // Build a large enough tree for inner node rebalancing
1503        let mut idx = BPlusIndex::<i32, u64>::new();
1504        let n = 5000;
1505        for i in 0..n {
1506            idx.insert(i, i as u64);
1507        }
1508        idx.validate();
1509        let initial_height = idx.height;
1510        assert!(initial_height >= 2);
1511
1512        // Delete ~90% of entries
1513        for i in 0..(n * 9 / 10) {
1514            assert!(delete_and_validate(&mut idx, &i, &(i as u64)));
1515        }
1516        // Height should have decreased
1517        assert!(idx.height < initial_height);
1518    }
1519
1520    // -----------------------------------------------------------------------
1521    // Lookup
1522    // -----------------------------------------------------------------------
1523
1524    #[test]
1525    fn lookup_unique_all() {
1526        let mut idx = BPlusIndex::<i32, u64>::new();
1527        for i in 0..200 {
1528            idx.insert(i, i as u64 * 10);
1529        }
1530        for i in 0..200 {
1531            assert_eq!(idx.lookup_unique(&i), Some(&(i as u64 * 10)));
1532        }
1533        assert_eq!(idx.lookup_unique(&999), None);
1534        assert_eq!(idx.lookup_unique(&-1), None);
1535    }
1536
1537    #[test]
1538    fn lookup_unique_boundary_between_leaves() {
1539        // Key that falls exactly at a leaf split point
1540        let mut idx = BPlusIndex::<i32, u64>::new();
1541        for i in 0..200 {
1542            idx.insert(i, i as u64);
1543        }
1544        // Verify every key, including those at leaf boundaries
1545        for i in 0..200 {
1546            assert_eq!(
1547                idx.lookup_unique(&i),
1548                Some(&(i as u64)),
1549                "lookup_unique failed for key {}",
1550                i
1551            );
1552        }
1553    }
1554
1555    #[test]
1556    fn lookup_range_empty_result() {
1557        let mut idx = BPlusIndex::<i32, u64>::new();
1558        idx.insert(5, 100);
1559        idx.insert(10, 200);
1560        assert_eq!(idx.lookup_range(&7).count(), 0);
1561    }
1562
1563    #[test]
1564    fn lookup_range_key_not_in_tree() {
1565        let idx = BPlusIndex::<i32, u64>::new();
1566        assert_eq!(idx.lookup_range(&42).count(), 0);
1567    }
1568
1569    // -----------------------------------------------------------------------
1570    // Range queries
1571    // -----------------------------------------------------------------------
1572
1573    #[test]
1574    fn range_exclusive_both_ends() {
1575        let mut idx = BPlusIndex::<i32, u64>::new();
1576        for i in 0..100 {
1577            idx.insert(i, i as u64);
1578        }
1579        let vals: Vec<_> = idx.range(10..20).map(|(k, _)| *k).collect();
1580        assert_eq!(vals, (10..20).collect::<Vec<_>>());
1581    }
1582
1583    #[test]
1584    fn range_inclusive_both_ends() {
1585        let mut idx = BPlusIndex::<i32, u64>::new();
1586        for i in 0..100 {
1587            idx.insert(i, i as u64);
1588        }
1589        let vals: Vec<_> = idx.range(10..=20).map(|(k, _)| *k).collect();
1590        assert_eq!(vals, (10..=20).collect::<Vec<_>>());
1591    }
1592
1593    #[test]
1594    fn range_unbounded_start() {
1595        let mut idx = BPlusIndex::<i32, u64>::new();
1596        for i in 0..50 {
1597            idx.insert(i, i as u64);
1598        }
1599        let vals: Vec<_> = idx.range(..10).map(|(k, _)| *k).collect();
1600        assert_eq!(vals, (0..10).collect::<Vec<_>>());
1601    }
1602
1603    #[test]
1604    fn range_unbounded_end() {
1605        let mut idx = BPlusIndex::<i32, u64>::new();
1606        for i in 0..50 {
1607            idx.insert(i, i as u64);
1608        }
1609        let vals: Vec<_> = idx.range(40..).map(|(k, _)| *k).collect();
1610        assert_eq!(vals, (40..50).collect::<Vec<_>>());
1611    }
1612
1613    #[test]
1614    fn range_fully_unbounded() {
1615        let mut idx = BPlusIndex::<i32, u64>::new();
1616        for i in 0..50 {
1617            idx.insert(i, i as u64);
1618        }
1619        let vals: Vec<_> = idx.range(..).map(|(k, _)| *k).collect();
1620        assert_eq!(vals, (0..50).collect::<Vec<_>>());
1621    }
1622
1623    #[test]
1624    fn range_empty_result() {
1625        let mut idx = BPlusIndex::<i32, u64>::new();
1626        for i in 0..50 {
1627            idx.insert(i, i as u64);
1628        }
1629        assert_eq!(idx.range(100..200).count(), 0);
1630        assert_eq!(idx.range(-10..-5).count(), 0);
1631    }
1632
1633    #[test]
1634    fn range_on_empty_tree() {
1635        let idx = BPlusIndex::<i32, u64>::new();
1636        assert_eq!(idx.range(0..100).count(), 0);
1637        assert_eq!(idx.range(..).count(), 0);
1638    }
1639
1640    #[test]
1641    fn range_spanning_multiple_leaves() {
1642        let mut idx = BPlusIndex::<i32, u64>::new();
1643        for i in 0..500 {
1644            idx.insert(i, i as u64);
1645        }
1646        let vals: Vec<_> = idx.range(100..400).map(|(k, _)| *k).collect();
1647        assert_eq!(vals, (100..400).collect::<Vec<_>>());
1648    }
1649
1650    #[test]
1651    fn range_with_non_unique_keys() {
1652        let mut idx = BPlusIndex::<i32, u64>::new();
1653        for k in 0..10 {
1654            for v in 0..5u64 {
1655                idx.insert(k, v);
1656            }
1657        }
1658        // Range 3..7 should return all V for keys 3, 4, 5, 6
1659        let entries: Vec<_> = idx.range(3..7).collect();
1660        assert_eq!(entries.len(), 4 * 5);
1661        for (k, _) in &entries {
1662            assert!(**k >= 3 && **k < 7);
1663        }
1664    }
1665
1666    // -----------------------------------------------------------------------
1667    // Iteration
1668    // -----------------------------------------------------------------------
1669
1670    #[test]
1671    fn iter_empty() {
1672        let idx = BPlusIndex::<i32, u64>::new();
1673        assert_eq!(idx.iter().count(), 0);
1674    }
1675
1676    #[test]
1677    fn iter_sorted_after_reverse_insert() {
1678        let mut idx = BPlusIndex::<i32, u64>::new();
1679        for i in (0..300).rev() {
1680            idx.insert(i, i as u64);
1681        }
1682        idx.validate();
1683        let keys: Vec<_> = idx.iter().map(|(k, _)| *k).collect();
1684        assert_eq!(keys, (0..300).collect::<Vec<_>>());
1685    }
1686
1687    #[test]
1688    fn iter_sorted_after_random_order_insert() {
1689        let mut idx = BPlusIndex::<i32, u64>::new();
1690        // Insert in a pseudo-random order
1691        let mut keys: Vec<i32> = (0..500).collect();
1692        // Simple deterministic shuffle
1693        for i in (1..keys.len()).rev() {
1694            let j = (i * 2654435761) % (i + 1);
1695            keys.swap(i, j);
1696        }
1697        for k in &keys {
1698            idx.insert(*k, *k as u64);
1699        }
1700        idx.validate();
1701        let result: Vec<_> = idx.iter().map(|(k, _)| *k).collect();
1702        assert_eq!(result, (0..500).collect::<Vec<_>>());
1703    }
1704
1705    // -----------------------------------------------------------------------
1706    // Update
1707    // -----------------------------------------------------------------------
1708
1709    #[test]
1710    fn update_moves_entry() {
1711        let mut idx = BPlusIndex::<i32, u64>::new();
1712        idx.insert(5, 42);
1713        idx.update(&5, 10, 42);
1714        assert_eq!(idx.lookup_unique(&5), None);
1715        assert_eq!(idx.lookup_unique(&10), Some(&42));
1716        idx.validate();
1717    }
1718
1719    #[test]
1720    fn update_nonexistent_key_still_inserts() {
1721        let mut idx = BPlusIndex::<i32, u64>::new();
1722        idx.insert(5, 42);
1723        idx.update(&999, 10, 42); // delete of 999 is a no-op
1724        // 42 is now under both key 5 and key 10 (update doesn't remove from 5)
1725        // Actually: update deletes (999, 42) which doesn't exist, then inserts (10, 42)
1726        assert_eq!(idx.lookup_unique(&5), Some(&42));
1727        assert_eq!(idx.lookup_unique(&10), Some(&42));
1728        assert_eq!(idx.len(), 2);
1729        idx.validate();
1730    }
1731
1732    // -----------------------------------------------------------------------
1733    // Free list / node recycling
1734    // -----------------------------------------------------------------------
1735
1736    #[test]
1737    fn node_recycling_after_delete_insert() {
1738        let mut idx = BPlusIndex::<i32, u64>::new();
1739        // Build up
1740        for i in 0..500 {
1741            idx.insert(i, i as u64);
1742        }
1743        let leaves_after_insert = idx.leaves.len();
1744
1745        // Tear down
1746        for i in 0..500 {
1747            idx.delete(&i, &(i as u64));
1748        }
1749        idx.validate();
1750        let free_after_delete = idx.free_leaves.len();
1751        assert!(free_after_delete > 0, "expected freed leaves");
1752
1753        // Build up again -- should reuse freed nodes
1754        for i in 0..500 {
1755            idx.insert(i, i as u64);
1756        }
1757        idx.validate();
1758        assert_eq!(
1759            idx.leaves.len(),
1760            leaves_after_insert,
1761            "arena grew instead of recycling"
1762        );
1763    }
1764
1765    // -----------------------------------------------------------------------
1766    // Stress / combined operations
1767    // -----------------------------------------------------------------------
1768
1769    #[test]
1770    fn stress_insert_delete_interleaved() {
1771        let mut idx = BPlusIndex::<i32, u64>::new();
1772        for i in 0..1000 {
1773            idx.insert(i, i as u64);
1774        }
1775        idx.validate();
1776
1777        // Delete even
1778        for i in (0..1000).step_by(2) {
1779            assert!(idx.delete(&i, &(i as u64)));
1780        }
1781        idx.validate();
1782        assert_eq!(idx.len(), 500);
1783
1784        // Verify odd remain
1785        for i in (1..1000).step_by(2) {
1786            assert_eq!(idx.lookup_unique(&i), Some(&(i as u64)));
1787        }
1788
1789        // Re-insert even
1790        for i in (0..1000).step_by(2) {
1791            idx.insert(i, i as u64);
1792        }
1793        idx.validate();
1794        assert_eq!(idx.len(), 1000);
1795
1796        let keys: Vec<_> = idx.iter().map(|(k, _)| *k).collect();
1797        assert_eq!(keys, (0..1000).collect::<Vec<_>>());
1798    }
1799
1800    #[test]
1801    fn stress_insert_delete_reinsert_cycles() {
1802        let mut idx = BPlusIndex::<i32, u64>::new();
1803        for cycle in 0..5 {
1804            let base = cycle * 100;
1805            for i in base..base + 100 {
1806                insert_and_validate(&mut idx, i, i as u64);
1807            }
1808            // Delete first half of this cycle
1809            for i in base..base + 50 {
1810                assert!(delete_and_validate(&mut idx, &i, &(i as u64)));
1811            }
1812        }
1813        // Should have 50 entries from each of 5 cycles = 250
1814        assert_eq!(idx.len(), 250);
1815    }
1816
1817    #[test]
1818    fn stress_large_tree() {
1819        let mut idx = BPlusIndex::<i32, u64>::new();
1820        let n = 10_000i32;
1821        for i in 0..n {
1822            idx.insert(i, i as u64);
1823        }
1824        idx.validate();
1825        assert_eq!(idx.len(), n as usize);
1826
1827        // Random-ish deletions
1828        for i in (0..n).step_by(3) {
1829            assert!(idx.delete(&i, &(i as u64)));
1830        }
1831        idx.validate();
1832
1833        // Verify remaining
1834        for i in 0..n {
1835            if i % 3 == 0 {
1836                assert_eq!(idx.lookup_unique(&i), None);
1837            } else {
1838                assert_eq!(idx.lookup_unique(&i), Some(&(i as u64)));
1839            }
1840        }
1841    }
1842
1843    // -----------------------------------------------------------------------
1844    // Composite key (simulating real use case)
1845    // -----------------------------------------------------------------------
1846
1847    #[test]
1848    fn composite_key_range() {
1849        // (game_mode, mmr) -> ticket_id
1850        let mut idx = BPlusIndex::<(u8, i32), u64>::new();
1851        // Mode 1: various MMRs
1852        for i in 0..100 {
1853            idx.insert((1, 1000 + i), i as u64);
1854        }
1855        // Mode 2: various MMRs
1856        for i in 0..50 {
1857            idx.insert((2, 1000 + i), 100 + i as u64);
1858        }
1859        idx.validate();
1860
1861        // Range: mode 1, mmr 1020..1030
1862        let tickets: Vec<_> = idx.range((1, 1020)..(1, 1030)).map(|(_, v)| *v).collect();
1863        assert_eq!(tickets, (20..30u64).collect::<Vec<_>>());
1864
1865        // Range: mode 2 only
1866        let mode2: Vec<_> = idx.range((2, 0)..(3, 0)).map(|(_, v)| *v).collect();
1867        assert_eq!(mode2.len(), 50);
1868    }
1869
1870    // -----------------------------------------------------------------------
1871    // Edge: all same key
1872    // -----------------------------------------------------------------------
1873
1874    #[test]
1875    fn all_same_key() {
1876        let mut idx = BPlusIndex::<i32, u64>::new();
1877        for v in 0..500u64 {
1878            idx.insert(0, v);
1879        }
1880        idx.validate();
1881        assert_eq!(idx.len(), 500);
1882
1883        let vals: Vec<_> = idx.lookup_range(&0).copied().collect();
1884        assert_eq!(vals, (0..500u64).collect::<Vec<_>>());
1885
1886        // Delete all
1887        for v in 0..500u64 {
1888            assert!(delete_and_validate(&mut idx, &0, &v));
1889        }
1890        assert_eq!(idx.len(), 0);
1891    }
1892}