use super::*;
use captains_log::logfn;
use rstest::rstest;
use std::println;
fn seek_cache_parent<'a, K: Key, V: Value, S: Stats>(
map: &'a mut BTree<K, V, S>, stack: &'a mut S::BufferStack, key: &K,
) -> (&'a mut BTreeInner<K, V>, S::PathBufferRef<'a>, InterNode<K>, u8) {
let (tree, stats) = (&mut map.inner, &mut map.stats);
let inter = tree.root_as_inter().unwrap();
let mut cache = stats.get_cache(stack, Some(inter.clone()));
let _ = inter.find_leaf_with_cache::<K, _, _>(&mut cache, key);
let (parent, idx) = cache.pop_path().unwrap();
(tree, cache, parent, idx)
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_merge_right_height_3_2<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
let mut leaf_2 = builder.new_leaf();
let mut leaf_3 = builder.new_leaf();
let mut leaf_4 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
builder.insert_leaf(
&mut leaf_2,
CounterI32::new((10 + i) * 2),
CounterI32::new((10 + i) * 10),
);
builder.insert_leaf(
&mut leaf_3,
CounterI32::new((20 + i) * 2),
CounterI32::new((20 + i) * 10),
);
builder.insert_leaf(
&mut leaf_4,
CounterI32::new((30 + i) * 2),
CounterI32::new((30 + i) * 10),
);
}
let leaf_1_first = leaf_1.get_keys()[0].clone();
let leaf_2_first = leaf_2.get_keys()[0].clone();
let leaf_3_first = leaf_3.get_keys()[0].clone();
let mut internal_a = builder.new_inter(1);
internal_a.set_left_ptr(leaf_1.get_ptr_mut());
internal_a.insert_no_split(leaf_2_first, leaf_2.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 1);
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_3.get_ptr_mut());
internal_b.insert_no_split(leaf_4.get_keys()[0].clone(), leaf_4.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 1);
let mut root = builder.new_inter(2);
root.set_left_ptr(internal_a.get_ptr_mut());
root.insert_no_split(leaf_3_first, internal_b.get_ptr_mut());
debug_assert_eq!(root.key_count(), 1);
let alive_before = alive_count();
println!("Alive count before handle_inter_underflow: {}", alive_before);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 12);
map.validate();
assert_eq!(map.height(), 3);
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_1_first);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 1);
assert_eq!(popped_node, internal_a);
tree.handle_inter_underflow(&mut cache, internal_a);
}
map.validate();
assert_eq!(map.height(), 2, "Tree height should be 2 after merge");
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
assert!(
map.contains_key(&CounterI32::new((10 + i) * 2)),
"Key {} should exist",
(10 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((20 + i) * 2)),
"Key {} should exist",
(20 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((30 + i) * 2)),
"Key {} should exist",
(30 + i) * 2
);
}
assert_eq!(map.height(), 2);
map.assert_leaf_count(4);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, TestFlag::InterMergeRight as u32);
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_merge_left_height_3_2<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
let mut leaf_2 = builder.new_leaf();
let mut leaf_3 = builder.new_leaf();
let mut leaf_4 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
builder.insert_leaf(
&mut leaf_2,
CounterI32::new((10 + i) * 2),
CounterI32::new((10 + i) * 10),
);
builder.insert_leaf(
&mut leaf_3,
CounterI32::new((20 + i) * 2),
CounterI32::new((20 + i) * 10),
);
builder.insert_leaf(
&mut leaf_4,
CounterI32::new((30 + i) * 2),
CounterI32::new((30 + i) * 10),
);
}
let _leaf_1_first = leaf_1.get_keys()[0].clone();
let leaf_2_first = leaf_2.get_keys()[0].clone();
let leaf_3_first = leaf_3.get_keys()[0].clone();
let mut internal_a = builder.new_inter(1);
internal_a.set_left_ptr(leaf_1.get_ptr_mut());
internal_a.insert_no_split(leaf_2_first, leaf_2.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 1);
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_3.get_ptr_mut());
internal_b.insert_no_split(leaf_4.get_keys()[0].clone(), leaf_4.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 1);
let mut root = builder.new_inter(2);
root.set_left_ptr(internal_a.get_ptr_mut());
root.insert_no_split(leaf_3_first, internal_b.get_ptr_mut());
debug_assert_eq!(root.key_count(), 1);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 12);
map.validate();
assert_eq!(map.height(), 3);
let leaf_3_lookup = &leaf_3.get_keys()[0];
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_3_lookup);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 1);
assert_eq!(popped_node, internal_b);
tree.handle_inter_underflow(&mut cache, internal_b);
}
map.validate();
assert_eq!(map.height(), 2, "Tree height should be 2 after merge");
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
assert!(
map.contains_key(&CounterI32::new((10 + i) * 2)),
"Key {} should exist",
(10 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((20 + i) * 2)),
"Key {} should exist",
(20 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((30 + i) * 2)),
"Key {} should exist",
(30 + i) * 2
);
}
assert_eq!(map.height(), 2);
map.assert_leaf_count(4);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, TestFlag::InterMergeLeft as u32);
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_merge_right_height_3<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
let mut leaf_2 = builder.new_leaf();
let mut leaf_3 = builder.new_leaf();
let mut leaf_4 = builder.new_leaf();
let mut leaf_5 = builder.new_leaf();
let mut leaf_6 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
builder.insert_leaf(
&mut leaf_2,
CounterI32::new((10 + i) * 2),
CounterI32::new((10 + i) * 10),
);
builder.insert_leaf(
&mut leaf_3,
CounterI32::new((20 + i) * 2),
CounterI32::new((20 + i) * 10),
);
builder.insert_leaf(
&mut leaf_4,
CounterI32::new((30 + i) * 2),
CounterI32::new((30 + i) * 10),
);
builder.insert_leaf(
&mut leaf_5,
CounterI32::new((40 + i) * 2),
CounterI32::new((40 + i) * 10),
);
builder.insert_leaf(
&mut leaf_6,
CounterI32::new((50 + i) * 2),
CounterI32::new((50 + i) * 10),
);
}
let _leaf_1_first = leaf_1.get_keys()[0].clone();
let leaf_2_first = leaf_2.get_keys()[0].clone();
let leaf_3_first = leaf_3.get_keys()[0].clone();
let leaf_4_first = leaf_4.get_keys()[0].clone();
let leaf_5_first = leaf_5.get_keys()[0].clone();
let leaf_6_first = leaf_6.get_keys()[0].clone();
let mut internal_a = builder.new_inter(1);
internal_a.set_left_ptr(leaf_1.get_ptr_mut());
internal_a.insert_no_split(leaf_2_first, leaf_2.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 1);
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_3.get_ptr_mut());
internal_b.insert_no_split(leaf_4_first, leaf_4.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 1);
let mut internal_c = builder.new_inter(1);
internal_c.set_left_ptr(leaf_5.get_ptr_mut());
internal_c.insert_no_split(leaf_6_first, leaf_6.get_ptr_mut());
debug_assert_eq!(internal_c.key_count(), 1);
let mut root = builder.new_inter(2);
root.set_left_ptr(internal_a.get_ptr_mut());
root.insert_no_split(leaf_3_first, internal_b.get_ptr_mut());
root.insert_no_split(leaf_5_first, internal_c.get_ptr_mut());
debug_assert_eq!(root.key_count(), 2);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 18);
map.validate();
assert_eq!(map.height(), 3, "Tree height should remain 3");
let leaf_1_first = leaf_1.get_keys()[0].clone();
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_1_first);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 1);
assert_eq!(popped_node, internal_a);
tree.handle_inter_underflow(&mut cache, internal_a);
}
map.validate();
assert_eq!(map.height(), 3, "Tree height should remain 3");
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
assert!(
map.contains_key(&CounterI32::new((10 + i) * 2)),
"Key {} should exist",
(10 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((20 + i) * 2)),
"Key {} should exist",
(20 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((30 + i) * 2)),
"Key {} should exist",
(30 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((40 + i) * 2)),
"Key {} should exist",
(40 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((50 + i) * 2)),
"Key {} should exist",
(50 + i) * 2
);
map.assert_leaf_count(6);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, TestFlag::InterMergeRight as u32);
}
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_merge_left_height_3<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
let mut leaf_2 = builder.new_leaf();
let mut leaf_3 = builder.new_leaf();
let mut leaf_4 = builder.new_leaf();
let mut leaf_5 = builder.new_leaf();
let mut leaf_6 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
builder.insert_leaf(
&mut leaf_2,
CounterI32::new((10 + i) * 2),
CounterI32::new((10 + i) * 10),
);
builder.insert_leaf(
&mut leaf_3,
CounterI32::new((20 + i) * 2),
CounterI32::new((20 + i) * 10),
);
builder.insert_leaf(
&mut leaf_4,
CounterI32::new((30 + i) * 2),
CounterI32::new((30 + i) * 10),
);
builder.insert_leaf(
&mut leaf_5,
CounterI32::new((40 + i) * 2),
CounterI32::new((40 + i) * 10),
);
builder.insert_leaf(
&mut leaf_6,
CounterI32::new((50 + i) * 2),
CounterI32::new((50 + i) * 10),
);
}
let _leaf_1_first = leaf_1.get_keys()[0].clone();
let leaf_2_first = leaf_2.get_keys()[0].clone();
let leaf_3_first = leaf_3.get_keys()[0].clone();
let leaf_4_first = leaf_4.get_keys()[0].clone();
let leaf_5_first = leaf_5.get_keys()[0].clone();
let leaf_6_first = leaf_6.get_keys()[0].clone();
let mut internal_a = builder.new_inter(1);
internal_a.set_left_ptr(leaf_1.get_ptr_mut());
internal_a.insert_no_split(leaf_2_first, leaf_2.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 1);
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_3.get_ptr_mut());
internal_b.insert_no_split(leaf_4_first, leaf_4.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 1);
let mut internal_c = builder.new_inter(1);
internal_c.set_left_ptr(leaf_5.get_ptr_mut());
internal_c.insert_no_split(leaf_6_first, leaf_6.get_ptr_mut());
debug_assert_eq!(internal_c.key_count(), 1);
let mut root = builder.new_inter(2);
root.set_left_ptr(internal_a.get_ptr_mut());
root.insert_no_split(leaf_3_first, internal_b.get_ptr_mut());
root.insert_no_split(leaf_5_first, internal_c.get_ptr_mut());
debug_assert_eq!(root.key_count(), 2);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 18);
map.validate();
assert_eq!(map.height(), 3, "Tree height should remain 3");
let leaf_3_lookup = &leaf_3.get_keys()[0].clone();
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_3_lookup);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 1);
assert_eq!(popped_node, internal_b);
tree.handle_inter_underflow(&mut cache, internal_b);
}
map.validate();
assert_eq!(map.height(), 3, "Tree height should remain 3");
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
assert!(
map.contains_key(&CounterI32::new((10 + i) * 2)),
"Key {} should exist",
(10 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((20 + i) * 2)),
"Key {} should exist",
(20 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((30 + i) * 2)),
"Key {} should exist",
(30 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((40 + i) * 2)),
"Key {} should exist",
(40 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((50 + i) * 2)),
"Key {} should exist",
(50 + i) * 2
);
map.assert_leaf_count(6);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, TestFlag::InterMergeLeft as u32);
}
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_root_becomes_leaf<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
}
let leaf_1_first = leaf_1.get_keys()[0].clone();
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_1.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 0);
let mut internal_a = builder.new_inter(2);
internal_a.set_left_ptr(internal_b.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 0);
let mut root = builder.new_inter(3);
root.set_left_ptr(internal_a.get_ptr_mut());
debug_assert_eq!(root.key_count(), 0);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 3);
map.validate();
assert_eq!(map.height(), 4);
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_1_first);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 0);
assert_eq!(popped_node, internal_b);
tree.handle_inter_underflow(&mut cache, internal_b);
}
map.validate();
assert_eq!(map.height(), 1, "Root should become leaf after cascade merge");
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
}
map.assert_leaf_count(1);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, 0);
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}
#[logfn]
#[rstest]
#[case(TreeInfo::default())]
#[case(Compact::default())]
fn test_inter_underflow_single_leaf_inter_nodes_height_3<S: Stats>(#[case] _s: S, setup_log: ()) {
reset_alive_count();
let mut builder = TreeBuilder::<CounterI32, CounterI32, S>::default();
{
let mut leaf_1 = builder.new_leaf();
let mut leaf_2 = builder.new_leaf();
let mut leaf_3 = builder.new_leaf();
for i in 0..3 {
builder.insert_leaf(&mut leaf_1, CounterI32::new(i * 2), CounterI32::new(i * 10));
builder.insert_leaf(
&mut leaf_2,
CounterI32::new((10 + i) * 2),
CounterI32::new((10 + i) * 10),
);
builder.insert_leaf(
&mut leaf_3,
CounterI32::new((20 + i) * 2),
CounterI32::new((20 + i) * 10),
);
}
let leaf_1_first = leaf_1.get_keys()[0].clone();
let leaf_2_first = leaf_2.get_keys()[0].clone();
let leaf_3_first = leaf_3.get_keys()[0].clone();
let mut internal_a = builder.new_inter(1);
internal_a.set_left_ptr(leaf_1.get_ptr_mut());
debug_assert_eq!(internal_a.key_count(), 0);
let mut internal_b = builder.new_inter(1);
internal_b.set_left_ptr(leaf_2.get_ptr_mut());
debug_assert_eq!(internal_b.key_count(), 0);
let mut internal_c = builder.new_inter(1);
internal_c.set_left_ptr(leaf_3.get_ptr_mut());
debug_assert_eq!(internal_c.key_count(), 0);
let mut root = builder.new_inter(2);
root.set_left_ptr(internal_a.get_ptr_mut());
root.insert_no_split(leaf_2_first, internal_b.get_ptr_mut());
root.insert_no_split(leaf_3_first, internal_c.get_ptr_mut());
debug_assert_eq!(root.key_count(), 2);
let mut map = builder.build(root.into());
assert_eq!(map.len(), 9);
map.validate();
assert_eq!(map.height(), 3, "Tree height should be 3");
let alive_before;
{
let mut stack = S::BufferStack::default();
let (tree, mut cache, popped_node, _) =
seek_cache_parent::<_, _, _>(&mut map, &mut stack, &leaf_1_first);
debug_assert_eq!(popped_node.height(), 1);
debug_assert_eq!(popped_node.key_count(), 0);
assert_eq!(popped_node, internal_a);
alive_before = alive_count();
println!("Alive count before handle_inter_underflow: {}", alive_before);
tree.handle_inter_underflow(&mut cache, internal_a);
}
map.validate();
assert_eq!(map.height(), 3, "Tree height should remain 3");
println!("Alive count after handle_inter_underflow: {}", alive_count());
for i in 0..3 {
assert!(map.contains_key(&CounterI32::new(i * 2)), "Key {} should exist", i * 2);
assert!(
map.contains_key(&CounterI32::new((10 + i) * 2)),
"Key {} should exist",
(10 + i) * 2
);
assert!(
map.contains_key(&CounterI32::new((20 + i) * 2)),
"Key {} should exist",
(20 + i) * 2
);
}
map.assert_leaf_count(3);
#[cfg(feature = "trace_log")]
assert_eq!(map.inner.triggers, 0);
}
assert_eq!(alive_count(), 0, "All CounterI32 should be dropped after cleanup");
}