use alloc::vec;
use alloc::vec::Vec;
pub(super) fn distribute(
sz: &[usize],
cnt_old: &[usize],
leaf: bool,
leaf_data: bool,
usable: usize,
) -> Vec<usize> {
let n_cell = sz.len();
let n_old = cnt_old.len();
debug_assert!(n_cell > 0 && n_old > 0);
let usable_space = (usable + if leaf { 4 } else { 0 }) as i64 - 12;
let ld = usize::from(leaf_data);
let cell = |i: usize| -> i64 { sz[i] as i64 };
let cap = n_cell + 2;
let mut sz_new = vec![0i64; cap];
let mut cnt_new = vec![0usize; cap];
let mut k = n_old;
for i in 0..n_old {
let start = if i == 0 { 0 } else { cnt_old[i - 1] + (1 - ld) };
let mut s = 0i64;
for j in start..cnt_old[i] {
s += cell(j) + 2;
}
sz_new[i] = s;
cnt_new[i] = cnt_old[i];
}
let mut i = 0usize;
while i < k {
while sz_new[i] > usable_space {
if i + 1 >= k {
k = i + 2;
debug_assert!(k <= cap);
sz_new[k - 1] = 0;
cnt_new[k - 1] = n_cell;
}
let mut s = 2 + cell(cnt_new[i] - 1);
sz_new[i] -= s;
if ld == 0 {
s = if cnt_new[i] < n_cell {
2 + cell(cnt_new[i])
} else {
0
};
}
sz_new[i + 1] += s;
cnt_new[i] -= 1;
}
while cnt_new[i] < n_cell {
let mut s = 2 + cell(cnt_new[i]);
if sz_new[i] + s > usable_space {
break;
}
sz_new[i] += s;
cnt_new[i] += 1;
if ld == 0 {
s = if cnt_new[i] < n_cell {
2 + cell(cnt_new[i])
} else {
0
};
}
sz_new[i + 1] -= s;
}
if cnt_new[i] >= n_cell {
k = i + 1;
}
i += 1;
}
let mut i = k - 1;
while i > 0 {
let mut sz_right = sz_new[i];
let mut sz_left = sz_new[i - 1];
let mut r = cnt_new[i - 1] as isize - 1;
let mut d = r + 1 - ld as isize;
loop {
if r < 0 {
break;
}
let sz_r = cell(r as usize);
let sz_d = cell(d as usize);
if sz_right != 0
&& sz_right + sz_d + 2 > sz_left - (sz_r + if i == k - 1 { 0 } else { 2 })
{
break;
}
sz_right += sz_d + 2;
sz_left -= sz_r + 2;
cnt_new[i - 1] = r as usize;
r -= 1;
d -= 1;
}
sz_new[i] = sz_right;
sz_new[i - 1] = sz_left;
i -= 1;
}
cnt_new.truncate(k);
cnt_new
}
pub(super) fn sibling_window(p: usize, n_children: usize) -> (usize, usize) {
let i = n_children - 1; if i < 2 {
(0, n_children)
} else if p == 0 {
(0, 3)
} else if p == i {
(i - 2, 3)
} else {
(p - 1, 3)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table_leaf_even_fill() {
let sz: Vec<usize> = vec![9; 730];
let cnt_old = vec![361, 550, 730];
let cnt_new = distribute(&sz, &cnt_old, true, true, 4096);
assert_eq!(cnt_new.last().copied(), Some(730));
let mut prev = 0;
let counts: Vec<usize> = cnt_new
.iter()
.map(|&c| {
let n = c - prev;
prev = c;
n
})
.collect();
let max = *counts.iter().max().unwrap();
let min = *counts.iter().min().unwrap();
assert!(max - min <= 1, "uneven fill: {counts:?}");
}
#[test]
fn index_leaf_partition_exact() {
let sz: Vec<usize> = vec![20; 400];
let cnt_old = vec![133, 266, 400];
let cnt_new = distribute(&sz, &cnt_old, true, false, 4096);
assert_eq!(cnt_new.last().copied(), Some(400));
for w in cnt_new.windows(2) {
assert!(w[1] > w[0]);
}
}
#[test]
fn single_page_splits() {
let sz: Vec<usize> = vec![50; 100];
let cnt_old = vec![100];
let cnt_new = distribute(&sz, &cnt_old, true, true, 4096);
assert!(cnt_new.len() >= 2);
assert_eq!(cnt_new.last().copied(), Some(100));
}
#[test]
fn window_selection() {
assert_eq!(sibling_window(0, 1), (0, 1)); assert_eq!(sibling_window(0, 2), (0, 2)); assert_eq!(sibling_window(1, 2), (0, 2));
assert_eq!(sibling_window(0, 5), (0, 3)); assert_eq!(sibling_window(4, 5), (2, 3)); assert_eq!(sibling_window(2, 5), (1, 3)); }
}