use std::collections::BTreeMap;
use super::*;
use crate::seg_leaf::{Shape, head_of};
pub(crate) fn check(t: &Tree) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut out = Vec::new();
let mut leaves = Vec::new();
if t.root == NIL {
assert_eq!(
(t.len, t.first, t.live_leaves(), t.live_inners()),
(0, NIL, 0, 0),
"an empty tree holds nothing"
);
return out;
}
let n = walk(t, t.root, t.height, None, None, &mut out, &mut leaves);
assert_eq!(n, t.len, "len");
assert_eq!(out.len(), t.len);
for w in out.windows(2) {
assert!(w[0].0 < w[1].0, "strictly increasing: {:?} then {:?}", w[0].0, w[1].0);
}
assert_eq!(t.first, leaves[0], "first leaf");
for w in leaves.windows(2) {
assert_eq!(t.leaf(w[0]).next, w[1]);
assert_eq!(t.leaf(w[1]).prev, w[0]);
}
assert_eq!(t.leaf(*leaves.last().unwrap()).next, NIL);
assert_eq!(t.leaf(leaves[0]).prev, NIL);
if t.height > 0 {
assert!(leaves.iter().all(|&l| !t.leaf(l).is_empty()), "no empty leaf under an inner node");
}
let seps: usize =
live_inners(t).map(|i| t.inners[i].seps.iter().map(|s| s.len()).sum::<usize>()).sum();
assert_eq!(seps, t.sep_bytes, "separator bytes");
let slabs: usize = leaves
.iter()
.flat_map(|&l| (0..t.leaf(l).len()).map(move |i| (l, i)))
.filter_map(|(l, i)| t.leaf(l).slab_of(i))
.map(|id| t.ov.get(id).len())
.sum();
assert_eq!(slabs, t.ov.bytes, "overflow bytes are exactly the live slabs");
assert_eq!(t.live_leaves(), leaves.len(), "no leaked leaf");
out
}
fn live_inners(t: &Tree) -> impl Iterator<Item = usize> + '_ {
(0..t.inners.len()).filter(|&i| !t.free_inners.contains(&(i as u32)))
}
fn walk(
t: &Tree,
node: u32,
h: usize,
lo: Option<&[u8]>,
hi: Option<&[u8]>,
out: &mut Vec<(Vec<u8>, Vec<u8>)>,
leaves: &mut Vec<u32>,
) -> usize {
if h == 0 {
let l = t.leaf(node);
leaves.push(node);
let mut key = Vec::new();
for i in 0..l.len() {
let payload = t.entry(Pos { leaf: node, slot: i }, &mut key).to_vec();
assert_eq!(head_of(&key), l.head(i), "head matches key");
if let Some(lo) = lo {
assert!(key.as_slice() >= lo, "entry at or above its separator");
}
if let Some(hi) = hi {
assert!(key.as_slice() < hi, "entry below the next separator");
}
out.push((key.clone(), payload));
}
return l.len();
}
let inner = &t.inners[node as usize];
assert_eq!(inner.kids.len(), inner.seps.len() + 1, "kids and separators");
assert!(inner.kids.len() <= FANOUT, "fanout");
assert!(!inner.kids.is_empty());
let mut total = 0;
for (j, &kid) in inner.kids.iter().enumerate() {
if j < inner.seps.len() {
assert_eq!(inner.heads[j], crate::seg_leaf::head16_of(&inner.seps[j]));
}
let klo = if j == 0 { lo } else { Some(&*inner.seps[j - 1]) };
let khi = if j < inner.seps.len() { Some(&*inner.seps[j]) } else { hi };
let n = walk(t, kid, h - 1, klo, khi, out, leaves);
assert_eq!(inner.counts[j] as usize, n, "count of child {j}");
total += n;
}
total
}
const CHECK_EVERY: usize = 7;
pub(crate) struct Rng(pub(crate) u64);
impl Rng {
pub(crate) fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
pub(crate) fn below(&mut self, n: u64) -> u64 {
self.next() % n
}
}
pub(crate) fn key_for(r: &mut Rng, shape: u64) -> Vec<u8> {
match shape {
0 => [r.below(400).to_be_bytes().as_slice(), &[0, 0, 0, 1]].concat(),
1 => {
let n = r.below(20) as usize;
let mut k = vec![b'k'; n];
if n > 0 {
k[n - 1] = r.below(4) as u8;
}
k
}
_ => vec![r.below(3) as u8; 300 + r.below(3) as usize * 200],
}
}
fn run_model(seed: u64, payloads: bool, ops: usize) {
let mut r = Rng(seed);
let mut t = Tree::new(Shape { payloads, vlens: false });
let mut m: BTreeMap<Vec<u8>, Vec<u8>> = BTreeMap::new();
for step in 0..ops {
let shape = r.below(10).min(2).max(r.below(2));
let key = key_for(&mut r, shape);
let payload: Vec<u8> =
if payloads { vec![step as u8; r.below(40) as usize] } else { Vec::new() };
match r.below(10) {
0..=5 => assert_eq!(t.insert(&key, &payload), m.insert(key, payload).is_none()),
6..=8 => assert_eq!(t.remove(&key), m.remove(&key).is_some()),
_ => {
let mut cut = Vec::new();
t.cut_below(&Probe::new(&key), |e| cut.push((e.key.to_vec(), e.payload.to_vec())));
let keep = m.split_off(&key);
let gone: Vec<(Vec<u8>, Vec<u8>)> =
std::mem::replace(&mut m, keep).into_iter().collect();
assert_eq!(cut, gone, "cut below {key:?}");
}
}
if step % CHECK_EVERY == 0 || step == ops - 1 {
let got = check(&t);
let want: Vec<(Vec<u8>, Vec<u8>)> =
m.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
assert_eq!(got, want, "step {step}");
ranks_agree(&t, &m, &mut r);
}
}
t.repack();
let got = check(&t);
assert_eq!(got.len(), m.len());
}
pub(crate) fn ranks_agree(t: &Tree, m: &BTreeMap<Vec<u8>, Vec<u8>>, r: &mut Rng) {
for _ in 0..20 {
let shape = r.below(3);
let k = key_for(r, shape);
let below = m.range(..k.clone()).count();
assert_eq!(t.rank(&Probe::new(&k)), below, "rank {k:?}");
let upto = m.keys().filter(|x| x.as_slice() < k.as_slice() || x.starts_with(&k)).count();
assert_eq!(t.rank(&Probe::past(&k)), upto, "rank past {k:?}");
let lb = t.lower_bound(&Probe::new(&k)).map(|p| {
let mut key = Vec::new();
t.entry(p, &mut key);
key
});
assert_eq!(lb, m.range(k.clone()..).next().map(|(x, _)| x.clone()), "lower bound {k:?}");
}
}
#[test]
fn random_operations_match_a_map() {
for seed in 1..=12u64 {
run_model(seed.wrapping_mul(0x9E37_79B9_7F4A_7C15), seed % 2 == 0, 6000);
}
}
#[test]
fn ascending_and_descending_runs_pack_leaves_full() {
for desc in [false, true] {
let mut t = Tree::new(Shape { payloads: false, vlens: false });
for i in 0..20_000u64 {
let v = if desc { u64::MAX - i } else { i };
t.insert(&[v.to_be_bytes().as_slice(), &[1, 2, 3, 4]].concat(), &[]);
}
check(&t);
let fill = t.len as f64 * 15.0 / (t.live_leaves() * Leaf::capacity()) as f64;
assert!(fill > 0.95, "desc={desc}: fill {fill:.3}");
}
}
#[test]
fn random_inserts_fill_about_ln2_and_repack_fills_them() {
let mut r = Rng(7);
let mut t = Tree::new(Shape { payloads: false, vlens: false });
for _ in 0..50_000 {
t.insert(&[r.next().to_be_bytes().as_slice(), &[1, 2, 3, 4]].concat(), &[]);
}
let fill = |t: &Tree| t.len as f64 * 15.0 / (t.live_leaves() * Leaf::capacity()) as f64;
let before = fill(&t);
assert!((0.6..0.8).contains(&before), "random fill {before:.3}");
t.repack();
check(&t);
assert!(fill(&t) > 0.95, "repacked fill {:.3}", fill(&t));
}
#[test]
fn stepping_both_ways_visits_every_entry() {
let mut t = Tree::new(Shape { payloads: false, vlens: false });
for i in 0..5000u32 {
t.insert(&(i * 7 % 5000).to_be_bytes(), &[]);
}
let mut n = 0;
let mut pos = t.first_pos();
while let Some(p) = pos {
n += 1;
pos = t.next_pos(p);
}
assert_eq!(n, 5000);
let mut n = 0;
let mut pos = t.last_pos();
while let Some(p) = pos {
n += 1;
pos = t.prev_pos(p);
}
assert_eq!(n, 5000);
let p = t.before(&Probe::new(&100u32.to_be_bytes())).expect("99 is there");
let mut key = Vec::new();
t.entry(p, &mut key);
assert_eq!(key, 99u32.to_be_bytes());
}
#[test]
fn thinning_a_deep_tree_merges_leaves_and_keeps_it_whole() {
let mut r = Rng(11);
let mut t = Tree::new(Shape { payloads: true, vlens: false });
let mut m = BTreeMap::new();
for i in 0..40_000u32 {
let k = (i.wrapping_mul(2_654_435_761)).to_be_bytes().to_vec();
t.insert(&k, &[i as u8; 3]);
m.insert(k, vec![i as u8; 3]);
}
let leaves = t.live_leaves();
let keys: Vec<Vec<u8>> = m.keys().cloned().collect();
for (n, k) in keys.iter().enumerate() {
if r.below(10) < 9 {
assert!(t.remove(k));
m.remove(k);
}
if n % 4999 == 0 {
let got = check(&t);
assert_eq!(got, m.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<_>>());
}
}
check(&t);
assert!(t.live_leaves() * 3 < leaves, "thinned leaves merged: {} of {leaves}", t.live_leaves());
}
fn nth_leaf(t: &Tree, n: usize) -> u32 {
(0..n).fold(t.first, |id, _| t.leaf(id).next)
}
fn key_at(t: &Tree, id: u32, i: usize) -> Vec<u8> {
let mut k = Vec::new();
t.leaf(id).key_into(i, &t.ov, &mut k);
k
}
fn even_tree() -> Tree {
let mut t = Tree::new(Shape { payloads: false, vlens: false });
for i in 0..3000u64 {
t.insert(&(2 * i).to_be_bytes(), &[]);
}
assert_eq!(t.height, 1, "one inner level");
t
}
fn raise_first_key(t: &mut Tree, n: usize) -> Vec<u8> {
let l = nth_leaf(t, n);
let first = key_at(t, l, 0);
assert!(t.remove(&first));
let above = u64::from_be_bytes(first.as_slice().try_into().unwrap()) + 1;
assert!(t.insert(&above.to_be_bytes(), &[]));
first
}
#[test]
fn a_key_below_a_full_leaf_goes_to_its_left_neighbour_when_that_has_room() {
let mut t = even_tree();
let p = nth_leaf(&t, 3);
let p_len = t.leaf(p).len();
assert!(t.remove(&key_at(&t, p, p_len - 1)));
let first = raise_first_key(&mut t, 4);
let leaves = t.live_leaves();
assert!(t.insert(&first, &[]));
assert_eq!((t.leaf(p).len(), key_at(&t, p, p_len - 1)), (p_len, first));
assert_eq!(t.live_leaves(), leaves, "no leaf was split");
assert_eq!(check(&t).len(), 3000);
}
#[test]
fn a_key_below_a_full_leaf_opens_a_leaf_when_its_neighbour_is_full() {
let mut t = even_tree();
let first = raise_first_key(&mut t, 7);
let leaves = t.live_leaves();
assert!(t.insert(&first, &[]));
assert_eq!(t.live_leaves(), leaves + 1);
let new = nth_leaf(&t, 7);
assert_eq!((t.leaf(new).len(), key_at(&t, new, 0)), (1, first));
assert_eq!(t.leaf(new).prev, nth_leaf(&t, 6));
assert_eq!(check(&t).len(), 3001);
}
#[test]
fn inner_nodes_freed_by_removals_are_taken_again_by_later_splits() {
let mut t = Tree::new(Shape { payloads: false, vlens: false });
let key = |i: u64| i.to_be_bytes();
for i in 0..40_000u64 {
t.insert(&key(i), &[]);
}
assert!(t.height >= 2);
for i in 0..30_000u64 {
assert!(t.remove(&key(i)));
}
let freed = t.free_inners.len();
assert!(freed > 0, "emptied inner nodes are freed");
for i in 40_000..80_000u64 {
t.insert(&key(i), &[]);
}
assert!(t.free_inners.len() < freed, "{} of {freed} still free", t.free_inners.len());
assert_eq!(check(&t).len(), 50_000);
}
#[test]
fn removing_everything_leaves_no_leaf() {
let mut t = Tree::new(Shape { payloads: true, vlens: false });
for i in 0..3000u32 {
t.insert(&i.to_be_bytes(), b"x");
}
for i in 0..3000u32 {
assert!(t.remove(&i.to_be_bytes()));
}
check(&t);
assert_eq!((t.len, t.height, t.live_leaves()), (0, 0, 0), "the last leaf went too");
assert!(t.first_pos().is_none() && t.last_pos().is_none());
}