use alloc::vec::Vec;
use core::ops::RangeBounds;
use crate::iter::Iter;
use crate::node::{Insert, Internal, Node};
const MIN_ORDER: usize = 3;
const DEFAULT_ORDER: usize = 64;
pub struct BPlusTree<K, V> {
root: Node<K, V>,
order: usize,
len: usize,
}
impl<K, V> BPlusTree<K, V> {
#[must_use]
pub fn new() -> Self {
Self::with_order(DEFAULT_ORDER)
}
#[must_use]
pub(crate) fn with_order(order: usize) -> Self {
BPlusTree {
root: Node::empty_leaf(),
order: if order < MIN_ORDER { MIN_ORDER } else { order },
len: 0,
}
}
#[must_use]
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn height(&self) -> usize {
let mut height = 1;
let mut node = &self.root;
while let Node::Internal(internal) = node {
height += 1;
node = &internal.children[0];
}
height
}
pub fn clear(&mut self) {
self.root = Node::empty_leaf();
self.len = 0;
}
#[must_use]
pub fn iter(&self) -> Iter<'_, K, V> {
Iter::full(&self.root)
}
}
impl<K: Ord, V> BPlusTree<K, V> {
#[must_use]
#[inline]
pub fn get(&self, key: &K) -> Option<&V> {
self.root.get(key)
}
#[must_use]
#[inline]
pub fn contains_key(&self, key: &K) -> bool {
self.get(key).is_some()
}
#[must_use]
pub fn range<R: RangeBounds<K>>(&self, range: R) -> Iter<'_, K, V> {
Iter::range(&self.root, range.start_bound(), range.end_bound())
}
}
impl<'a, K, V> IntoIterator for &'a BPlusTree<K, V> {
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<K: Ord + Clone, V> BPlusTree<K, V> {
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
match self.root.insert(key, value, self.order) {
Insert::Replaced(old) => Some(old),
Insert::Inserted => {
self.len = self.len.saturating_add(1);
None
}
Insert::Split { sep, right } => {
self.grow_root(sep, right);
self.len = self.len.saturating_add(1);
None
}
}
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let removed = self.root.remove(key, self.min_keys());
if removed.is_some() {
self.len -= 1;
self.shrink_root();
}
removed
}
#[inline]
fn min_keys(&self) -> usize {
self.order.div_ceil(2) - 1
}
fn shrink_root(&mut self) {
let only_child = match &mut self.root {
Node::Internal(internal) if internal.children.len() == 1 => internal.children.pop(),
_ => None,
};
if let Some(child) = only_child {
self.root = child;
}
}
fn grow_root(&mut self, sep: K, right: Node<K, V>) {
let old_root = core::mem::replace(&mut self.root, Node::empty_leaf());
let mut keys = Vec::with_capacity(self.order);
keys.push(sep);
let mut children = Vec::with_capacity(self.order + 1);
children.push(old_root);
children.push(right);
self.root = Node::Internal(Internal { keys, children });
}
}
impl<K, V> Default for BPlusTree<K, V> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, reason = "test assertions")]
mod tests {
use alloc::vec::Vec;
use proptest::prelude::*;
use super::*;
fn check<K: Ord + Clone + core::fmt::Debug, V>(
node: &Node<K, V>,
order: usize,
min_keys: usize,
is_root: bool,
) -> (K, K, usize) {
match node {
Node::Leaf(leaf) => {
assert!(!leaf.keys.is_empty(), "non-root leaf is empty");
assert!(
leaf.keys.len() < order,
"leaf over capacity: {} >= {order}",
leaf.keys.len()
);
assert!(
is_root || leaf.keys.len() >= min_keys,
"non-root leaf under capacity: {} < {min_keys}",
leaf.keys.len()
);
assert_eq!(
leaf.keys.len(),
leaf.vals.len(),
"keys/vals length mismatch"
);
for w in leaf.keys.windows(2) {
assert!(w[0] < w[1], "leaf keys not strictly ascending");
}
(
leaf.keys[0].clone(),
leaf.keys[leaf.keys.len() - 1].clone(),
1,
)
}
Node::Internal(internal) => {
assert!(!internal.keys.is_empty(), "internal node has no separators");
assert!(
internal.keys.len() < order,
"internal node over capacity: {} >= {order}",
internal.keys.len()
);
assert!(
is_root || internal.keys.len() >= min_keys,
"non-root internal node under capacity: {} < {min_keys}",
internal.keys.len()
);
assert_eq!(
internal.children.len(),
internal.keys.len() + 1,
"child count must be separator count + 1"
);
for w in internal.keys.windows(2) {
assert!(w[0] < w[1], "separators not strictly ascending");
}
let mut child_height = None;
let mut subtree_min = None;
let mut last_max: Option<K> = None;
for (i, child) in internal.children.iter().enumerate() {
let (cmin, cmax, h) = check(child, order, min_keys, false);
match child_height {
None => child_height = Some(h),
Some(prev) => assert_eq!(prev, h, "subtrees differ in height (unbalanced)"),
}
if subtree_min.is_none() {
subtree_min = Some(cmin.clone());
}
if i > 0 {
let sep = &internal.keys[i - 1];
assert!(
last_max.as_ref().is_some_and(|m| m < sep),
"left subtree max not below separator"
);
assert!(sep <= &cmin, "separator above right subtree's min key");
}
last_max = Some(cmax);
}
let height = child_height.map_or(1, |h| h + 1);
match (subtree_min, last_max) {
(Some(min), Some(max)) => (min, max, height),
_ => panic!("internal node with no children"),
}
}
}
}
fn check_tree<K: Ord + Clone + core::fmt::Debug, V>(tree: &BPlusTree<K, V>) {
let _bounds = check(&tree.root, tree.order, tree.min_keys(), true);
}
fn collect_keys<K: Clone, V>(node: &Node<K, V>, out: &mut Vec<K>) {
match node {
Node::Leaf(leaf) => out.extend(leaf.keys.iter().cloned()),
Node::Internal(internal) => {
for child in &internal.children {
collect_keys(child, out);
}
}
}
}
#[test]
fn test_get_empty_returns_none() {
let tree: BPlusTree<u32, u32> = BPlusTree::new();
assert_eq!(tree.get(&0), None);
assert!(tree.is_empty());
assert_eq!(tree.height(), 1);
}
#[test]
fn test_insert_duplicate_key_replaces_value() {
let mut tree = BPlusTree::new();
assert_eq!(tree.insert(1_u32, "a"), None);
assert_eq!(tree.insert(1, "b"), Some("a"));
assert_eq!(tree.get(&1), Some(&"b"));
assert_eq!(tree.len(), 1);
}
#[test]
fn test_insert_many_splits_and_stays_balanced() {
let mut tree = BPlusTree::with_order(4);
for k in 0..256_u32 {
assert_eq!(tree.insert(k, k * 10), None);
}
check_tree(&tree);
assert!(
tree.height() > 1,
"tree should have split into multiple levels"
);
for k in 0..256_u32 {
assert_eq!(tree.get(&k), Some(&(k * 10)));
}
assert_eq!(tree.get(&256), None);
}
#[test]
fn test_insert_reverse_order_keeps_keys_sorted() {
let mut tree = BPlusTree::with_order(3);
for k in (0..100_u32).rev() {
assert_eq!(tree.insert(k, k), None);
}
let mut keys = Vec::new();
collect_keys(&tree.root, &mut keys);
assert_eq!(keys.len(), 100);
assert!(keys.windows(2).all(|w| w[0] < w[1]), "leaf order broken");
}
proptest! {
#[test]
fn prop_matches_reference_map(
order in 3_usize..8,
ops in prop::collection::vec((0_u32..200, 0_u32..1_000_000), 0..400),
) {
use std::collections::BTreeMap;
let mut tree = BPlusTree::with_order(order);
let mut reference = BTreeMap::new();
for (k, v) in ops {
let got = tree.insert(k, v);
let want = reference.insert(k, v);
prop_assert_eq!(got, want);
}
prop_assert_eq!(tree.len(), reference.len());
if !tree.is_empty() {
check_tree(&tree);
}
for (k, v) in &reference {
prop_assert_eq!(tree.get(k), Some(v));
}
for k in 0_u32..200 {
if !reference.contains_key(&k) {
prop_assert_eq!(tree.get(&k), None);
}
}
let mut keys = Vec::new();
collect_keys(&tree.root, &mut keys);
let expected: Vec<u32> = reference.keys().copied().collect();
prop_assert_eq!(keys, expected);
}
}
#[test]
fn test_remove_absent_key_returns_none() {
let mut tree = BPlusTree::new();
assert_eq!(tree.insert(1_u32, "a"), None);
assert_eq!(tree.remove(&2), None);
assert_eq!(tree.len(), 1);
}
#[test]
fn test_remove_present_key_returns_value() {
let mut tree = BPlusTree::new();
assert_eq!(tree.insert(1_u32, "a"), None);
assert_eq!(tree.insert(2, "b"), None);
assert_eq!(tree.remove(&1), Some("a"));
assert_eq!(tree.get(&1), None);
assert_eq!(tree.get(&2), Some(&"b"));
assert_eq!(tree.len(), 1);
}
#[test]
fn test_remove_all_empties_tree_and_collapses_root() {
let mut tree = BPlusTree::with_order(4);
for k in 0..200_u32 {
let _ = tree.insert(k, k);
}
assert!(tree.height() > 1);
for k in (0..200_u32).step_by(2) {
assert_eq!(tree.remove(&k), Some(k));
}
for k in (1..200_u32).step_by(2) {
assert_eq!(tree.remove(&k), Some(k));
}
assert!(tree.is_empty());
assert_eq!(tree.height(), 1, "root should collapse back to a leaf");
}
#[test]
fn test_remove_keeps_tree_balanced() {
let mut tree = BPlusTree::with_order(3);
for k in 0..500_u32 {
let _ = tree.insert(k, k);
}
for k in (0..500_u32).filter(|k| k % 3 == 0) {
assert_eq!(tree.remove(&k), Some(k));
}
check_tree(&tree);
for k in 0..500_u32 {
assert_eq!(tree.get(&k), if k % 3 == 0 { None } else { Some(&k) });
}
}
#[test]
fn test_iter_empty_yields_nothing() {
let tree: BPlusTree<u32, u32> = BPlusTree::new();
assert_eq!(tree.iter().count(), 0);
assert_eq!(tree.iter().next_back(), None);
assert_eq!(tree.range(..).count(), 0);
}
#[test]
fn test_iter_forward_and_reverse() {
let mut tree = BPlusTree::with_order(4);
for k in 0..50_u32 {
let _ = tree.insert(k, k * 10);
}
let fwd: Vec<_> = tree.iter().map(|(&k, &v)| (k, v)).collect();
let expected: Vec<_> = (0..50_u32).map(|k| (k, k * 10)).collect();
assert_eq!(fwd, expected);
let rev: Vec<_> = tree.iter().rev().map(|(&k, _)| k).collect();
let expected_rev: Vec<_> = (0..50_u32).rev().collect();
assert_eq!(rev, expected_rev);
}
#[test]
fn test_iter_from_both_ends_meets_in_middle() {
let mut tree = BPlusTree::with_order(3);
for k in 0..9_u32 {
let _ = tree.insert(k, k);
}
let mut it = tree.iter();
let mut seq = Vec::new();
let mut take_front = true;
loop {
let item = if take_front {
it.next()
} else {
it.next_back()
};
match item {
Some((&k, _)) => seq.push(k),
None => break,
}
take_front = !take_front;
}
assert_eq!(seq, vec![0, 8, 1, 7, 2, 6, 3, 5, 4]);
}
#[test]
fn test_range_bounds() {
let mut tree = BPlusTree::with_order(4);
for k in 0..20_u32 {
let _ = tree.insert(k, k);
}
let collect = |it: Iter<'_, u32, u32>| it.map(|(&k, _)| k).collect::<Vec<_>>();
assert_eq!(collect(tree.range(5..10)), vec![5, 6, 7, 8, 9]);
assert_eq!(collect(tree.range(5..=10)), vec![5, 6, 7, 8, 9, 10]);
assert_eq!(collect(tree.range(..3)), vec![0, 1, 2]);
assert_eq!(collect(tree.range(17..)), vec![17, 18, 19]);
assert_eq!(collect(tree.range(100..200)), Vec::<u32>::new());
let mut sparse = BPlusTree::with_order(3);
for k in [0_u32, 10, 20, 30, 40] {
let _ = sparse.insert(k, k);
}
assert_eq!(collect(sparse.range(5..35)), vec![10, 20, 30]);
}
proptest! {
#[test]
fn prop_iter_and_range_match_reference(
order in 3_usize..8,
keys in prop::collection::vec(0_u32..200, 0..300),
lo in 0_u32..200,
hi in 0_u32..200,
) {
use std::collections::BTreeMap;
let mut tree = BPlusTree::with_order(order);
let mut reference = BTreeMap::new();
for k in keys {
let _ = tree.insert(k, k.wrapping_mul(7));
let _ = reference.insert(k, k.wrapping_mul(7));
}
let tree_fwd: Vec<_> = tree.iter().map(|(&k, &v)| (k, v)).collect();
let ref_fwd: Vec<_> = reference.iter().map(|(&k, &v)| (k, v)).collect();
prop_assert_eq!(&tree_fwd, &ref_fwd);
let tree_rev: Vec<_> = tree.iter().rev().map(|(&k, &v)| (k, v)).collect();
let ref_rev: Vec<_> = reference.iter().rev().map(|(&k, &v)| (k, v)).collect();
prop_assert_eq!(tree_rev, ref_rev);
let (lo, hi) = (lo.min(hi), lo.max(hi));
let tree_range: Vec<_> = tree.range(lo..hi).map(|(&k, _)| k).collect();
let ref_range: Vec<_> = reference.range(lo..hi).map(|(&k, _)| k).collect();
prop_assert_eq!(tree_range, ref_range);
let tree_incl: Vec<_> = tree.range(lo..=hi).rev().map(|(&k, _)| k).collect();
let ref_incl: Vec<_> = reference.range(lo..=hi).rev().map(|(&k, _)| k).collect();
prop_assert_eq!(tree_incl, ref_incl);
}
#[test]
fn prop_insert_remove_matches_reference(
order in 3_usize..8,
ops in prop::collection::vec((any::<bool>(), 0_u32..150), 0..600),
) {
use std::collections::BTreeMap;
let mut tree = BPlusTree::with_order(order);
let mut reference = BTreeMap::new();
for (is_insert, k) in ops {
if is_insert {
prop_assert_eq!(tree.insert(k, k), reference.insert(k, k));
} else {
prop_assert_eq!(tree.remove(&k), reference.remove(&k));
}
prop_assert_eq!(tree.len(), reference.len());
if !tree.is_empty() {
check_tree(&tree);
}
}
let mut keys = Vec::new();
collect_keys(&tree.root, &mut keys);
let expected: Vec<u32> = reference.keys().copied().collect();
prop_assert_eq!(keys, expected);
}
}
}