use std::cmp::Ordering;
use smallvec::{smallvec, SmallVec};
use crate::{
allocator::Loc,
node::{child_bit, child_cover_mask_for_bit, data_bit, extend_repr, Key, MultiBitNode},
table::{DataIdx, EmptyMut, Table, K, NUM_CHILDREN, NUM_DATA},
};
#[inline(always)]
pub(crate) const fn fold_children(cc: u32) -> u32 {
let p = cc & (cc >> 1) & 0x5555_5555;
let p = (p | (p >> 1)) & 0x3333_3333;
let p = (p | (p >> 2)) & 0x0F0F_0F0F;
let p = (p | (p >> 4)) & 0x00FF_00FF;
((p | (p >> 8)) & 0x0000_FFFF) << 15
}
#[inline(always)]
pub(crate) const fn fold_l4(cov: u32) -> u32 {
let s = cov >> 8;
let p = s & (s >> 1) & (0x5555 << 7);
let p = (p | (p >> 1)) & (0x3333 << 7);
let p = (p | (p >> 2)) & (0x0F0F << 7);
(p | (p >> 4)) & (0x00FF << 7)
}
#[inline(always)]
pub(crate) const fn fold_l3(cov: u32) -> u32 {
let s = cov >> 4;
let p = s & (s >> 1) & (0x55 << 3);
let p = (p | (p >> 1)) & (0x33 << 3);
(p | (p >> 2)) & (0x0F << 3)
}
#[inline(always)]
pub(crate) const fn fold_l2(cov: u32) -> u32 {
let s = cov >> 2;
let p = s & (s >> 1) & (0x5 << 1);
(p | (p >> 1)) & (0x3 << 1)
}
#[inline(always)]
pub(crate) const fn fold_l1(cov: u32) -> u32 {
(cov >> 1) & (cov >> 2) & 1
}
#[inline(always)]
pub(crate) const fn push_l0(m: u32) -> u32 {
let s = (m & 0b1) << 1;
s | (s << 1)
}
#[inline(always)]
pub(crate) const fn push_l1(m: u32) -> u32 {
let s = (m & 0b110) << 2;
let e = (s | (s << 1)) & (0x5 << 3);
e | (e << 1)
}
#[inline(always)]
pub(crate) const fn push_l2(m: u32) -> u32 {
let s = (m & (0xF << 3)) << 4;
let e = (s | (s << 2)) & (0x33 << 7);
let e = (e | (e << 1)) & (0x55 << 7);
e | (e << 1)
}
#[inline(always)]
pub(crate) const fn push_l3(m: u32) -> u32 {
let s = (m & (0xFF << 7)) << 8;
let e = (s | (s << 4)) & (0x0F0F << 15);
let e = (e | (e << 2)) & (0x3333 << 15);
let e = (e | (e << 1)) & (0x5555 << 15);
e | (e << 1)
}
#[inline(always)]
pub(crate) const fn push_l4(m: u32) -> u32 {
let p = m >> 15;
let e = (p | (p << 8)) & 0x00FF_00FF;
let e = (e | (e << 4)) & 0x0F0F_0F0F;
let e = (e | (e << 2)) & 0x3333_3333;
let e = (e | (e << 1)) & 0x5555_5555;
e | (e << 1)
}
#[inline(always)]
pub(crate) const fn member_coverage(data_bitmap: u32) -> (u32, u32) {
let mut covered = 0u32;
covered |= push_l0(data_bitmap | covered);
covered |= push_l1(data_bitmap | covered);
covered |= push_l2(data_bitmap | covered);
covered |= push_l3(data_bitmap | covered);
let children_under_member = push_l4(data_bitmap | covered);
(covered, children_under_member)
}
#[inline(always)]
pub(crate) const fn fold_coverage(data_bitmap: u32, child_coverage: u32) -> u32 {
let mut coverage = data_bitmap | fold_children(child_coverage);
coverage |= fold_l4(coverage);
coverage |= fold_l3(coverage);
coverage |= fold_l2(coverage);
coverage |= fold_l1(coverage);
coverage
}
#[inline(always)]
pub(crate) const fn parent_coverage(coverage: u32) -> u32 {
push_l0(coverage) | push_l1(coverage) | push_l2(coverage) | push_l3(coverage)
}
impl<T> Table<T> {
pub(crate) unsafe fn remove_data_bits(&mut self, loc: Loc, depth: u32, bits: u32) -> i64 {
let mut count_delta: i64 = 0;
for bit in 0..NUM_DATA as u32 {
if bits & (1 << bit) != 0 {
unsafe {
DataIdx {
node: loc,
bit,
depth,
}
.resolve_mut(self)
}
.expect("remove_data_bits: data bit not set")
.take();
count_delta -= 1;
}
}
count_delta
}
pub(crate) unsafe fn free_children(&mut self, loc: Loc, child_bits: u32) -> i64 {
let mut count_delta: i64 = 0;
for child_bit in 0..NUM_CHILDREN as u32 {
if child_bits & (1 << child_bit) != 0 {
unsafe {
if let Some(child_loc) = self.child(loc, child_bit) {
count_delta -= self.clear_node_and_children(child_loc) as i64;
self.remove_child_at(loc, child_bit);
}
}
}
}
count_delta
}
fn bit_covered(&self, loc: Loc, bit: u32) -> bool {
let node = self.node(loc);
let data_bitmap = node.data_bitmap();
if data_bitmap & (1 << bit) != 0 {
return true; }
let mask = child_cover_mask_for_bit(bit);
let (_, children_under_member) = member_coverage(data_bitmap);
let mut child_coverage = children_under_member & mask;
for child in node.child_locs() {
let child_bit = child.bit;
if mask & (1 << child_bit) == 0 || children_under_member & (1 << child_bit) != 0 {
continue;
}
if self.bit_covered(child, 0) {
child_coverage |= 1 << child_bit;
}
}
fold_coverage(data_bitmap, child_coverage) & (1 << bit) != 0
}
pub(crate) fn covers_in_aggregate<R: Key>(&self, key: R, prefix_len: u32) -> bool {
let mut loc = Loc::root();
let mut depth = 0;
loop {
let node = self.node(loc);
if node.data_spm_loc(depth, key, prefix_len).is_some() {
return true; }
if prefix_len < depth + K {
let bit = data_bit(key, prefix_len);
return self.bit_covered(loc, bit);
}
let cb = child_bit(depth, key);
let Some(next) = (unsafe { self.child(loc, cb) }) else {
return false; };
loc = next;
depth += K;
}
}
}
impl Table<()> {
pub(crate) unsafe fn aggregate_set(&mut self, loc: Loc, depth: u32) -> (bool, i64) {
let node = *self.node(loc);
let data_bitmap = node.data_bitmap();
let mut count_delta: i64 = 0;
let (covered_by_member, children_under_member) = member_coverage(data_bitmap);
let mut child_coverage = 0u32;
for child in node.child_locs() {
let child_bit = child.bit;
if children_under_member & (1 << child_bit) != 0 {
continue;
}
let (child_covered, child_delta) = unsafe { self.aggregate_set(child, depth + K) };
count_delta += child_delta;
if child_covered {
child_coverage |= 1 << child_bit;
}
}
let coverage = fold_coverage(data_bitmap, child_coverage);
let keep = coverage & !parent_coverage(coverage) & !covered_by_member;
count_delta += unsafe { self.remove_data_bits(loc, depth, data_bitmap & !keep) };
let bits_to_insert = keep & !data_bitmap;
for bit in 0..NUM_DATA as u32 {
if bits_to_insert & (1 << bit) != 0 {
EmptyMut {
table: self,
node: loc,
data_bit: bit,
depth,
}
.insert(());
count_delta += 1;
}
}
let absorbed_children = (children_under_member | push_l4(coverage)) & node.child_bitmap();
count_delta += unsafe { self.free_children(loc, absorbed_children) };
(coverage & 1 != 0, count_delta)
}
pub(crate) unsafe fn aggregate_consistent_set(&mut self, loc: Loc, depth: u32) -> i64 {
let node = *self.node(loc);
let data_bitmap = node.data_bitmap();
let mut count_delta: i64 = 0;
let (covered, children_under_member) = member_coverage(data_bitmap);
for child in node.child_locs() {
if children_under_member & (1 << child.bit) != 0 {
continue;
}
count_delta += unsafe { self.aggregate_consistent_set(child, depth + K) };
}
count_delta += unsafe { self.remove_data_bits(loc, depth, data_bitmap & covered) };
count_delta += unsafe { self.free_children(loc, children_under_member) };
count_delta
}
}
impl<T: Clone + Eq> Table<T> {
pub(crate) unsafe fn covering_values<'a>(
&'a self,
loc: Loc,
depth: u32,
data_bitmap: u32,
inherited: Option<&'a T>,
) -> ([Option<&'a T>; NUM_DATA], u32) {
let mut covering_value: [Option<&T>; NUM_DATA] = [None; NUM_DATA];
let mut redundant = 0u32;
for b in 0..NUM_DATA {
let from_above = if b == 0 {
inherited
} else {
covering_value[(b - 1) / 2]
};
covering_value[b] = if data_bitmap & (1 << b) != 0 {
let present = unsafe {
DataIdx {
node: loc,
bit: b as u32,
depth,
}
.resolve(self)
}
.expect("covering_values: data bit not set");
let val = present.get();
if Some(val) == from_above {
redundant |= 1 << b;
}
Some(val)
} else {
from_above
};
}
(covering_value, redundant)
}
pub(crate) fn child_cover(
node: &MultiBitNode,
covering_value: &[Option<&T>; NUM_DATA],
) -> [Option<T>; NUM_CHILDREN] {
std::array::from_fn(|c| {
if node.has_child_bit(c as u32) {
covering_value[15 + c / 2].cloned()
} else {
None
}
})
}
pub(crate) unsafe fn aggregate_consistent_map(
&mut self,
loc: Loc,
depth: u32,
inherited: Option<T>,
) -> (bool, i64) {
let node = *self.node(loc);
let data_bitmap = node.data_bitmap();
let mut count_delta: i64 = 0;
let (covering_value, bits_to_remove) =
unsafe { self.covering_values(loc, depth, data_bitmap, inherited.as_ref()) };
let mut child_cover = Self::child_cover(&node, &covering_value);
for child_bit in 0..NUM_CHILDREN as u32 {
if let Some(child_loc) = unsafe { self.child(loc, child_bit) } {
let child_inherited = child_cover[child_bit as usize].take();
let (child_empty, delta) =
unsafe { self.aggregate_consistent_map(child_loc, depth + K, child_inherited) };
count_delta += delta;
if child_empty {
unsafe { self.remove_child_at(loc, child_bit) };
}
}
}
count_delta += unsafe { self.remove_data_bits(loc, depth, bits_to_remove) };
let node = self.node(loc);
let empty = node.data_bitmap() == 0 && node.child_bitmap() == 0;
(empty, count_delta)
}
}
const INLINE: usize = 4;
type Values<T> = SmallVec<[T; INLINE]>;
#[derive(Clone, PartialEq, Eq)]
enum CandidateSet<T> {
ContainsHole,
Covered(Values<T>),
}
enum Combined<T> {
New(CandidateSet<T>),
EqualsLeft,
EqualsRight,
EqualsBoth,
}
impl<T: Clone + Ord> CandidateSet<T> {
fn one(value: T) -> Self {
Self::Covered(smallvec![value])
}
fn combine(&self, other: &Self) -> Combined<T> {
match (self, other) {
(Self::ContainsHole, Self::ContainsHole) => Combined::EqualsBoth,
(Self::ContainsHole, Self::Covered(_)) => Combined::EqualsLeft,
(Self::Covered(_), Self::ContainsHole) => Combined::EqualsRight,
(Self::Covered(l), Self::Covered(r)) => {
let mut intersection = Values::new();
let mut union = Values::with_capacity(l.len() + r.len());
let (mut i, mut j) = (0, 0);
while i < l.len() && j < r.len() {
match l[i].cmp(&r[j]) {
Ordering::Less => {
if intersection.is_empty() {
union.push(l[i].clone());
}
i += 1;
}
Ordering::Greater => {
if intersection.is_empty() {
union.push(r[j].clone());
}
j += 1;
}
Ordering::Equal => {
intersection.push(l[i].clone());
i += 1;
j += 1;
}
}
}
if intersection.is_empty() {
union.extend(l[i..].iter().cloned());
union.extend(r[j..].iter().cloned());
Combined::New(Self::Covered(union))
} else {
match (intersection.len() == l.len(), intersection.len() == r.len()) {
(true, true) => Combined::EqualsBoth,
(true, false) => Combined::EqualsLeft,
(false, true) => Combined::EqualsRight,
(false, false) => Combined::New(Self::Covered(intersection)),
}
}
}
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum Aggregation<F> {
Drop,
Fill(F),
}
impl<T: Clone + Ord, F: Fn() -> T + Copy> Aggregation<F> {
fn leaf_set(self, covering: Option<&T>) -> CandidateSet<T> {
match (covering, self) {
(Some(v), _) => CandidateSet::one(v.clone()),
(None, Aggregation::Drop) => CandidateSet::ContainsHole,
(None, Aggregation::Fill(f)) => CandidateSet::one(f()),
}
}
fn leaf_value(self, covering: Option<&T>) -> Option<T> {
match (covering, self) {
(Some(v), _) => Some(v.clone()),
(None, Aggregation::Drop) => None,
(None, Aggregation::Fill(f)) => Some(f()),
}
}
}
struct ChangeSets<T> {
sets: Vec<CandidateSet<T>>,
node_masks: Vec<u32>,
}
impl<T> ChangeSets<T> {
#[inline]
fn new() -> Self {
Self {
sets: Vec::new(),
node_masks: Vec::new(),
}
}
#[inline]
fn is_empty(&self) -> bool {
self.sets.is_empty() && self.node_masks.is_empty()
}
#[inline]
fn node_writer(&mut self) -> NodeWriter<'_, T> {
NodeWriter {
store: self,
mask: 0,
}
}
#[inline]
fn node_reader(&mut self) -> NodeReader<'_, T> {
let mask = self.node_masks.pop().expect("one node mask per node");
NodeReader { store: self, mask }
}
}
struct NodeWriter<'a, T> {
store: &'a mut ChangeSets<T>,
mask: u32,
}
impl<T> NodeWriter<'_, T> {
#[inline]
fn push(&mut self, bit: usize, set: CandidateSet<T>) {
self.mask |= 1 << bit;
self.store.sets.push(set);
}
#[inline]
fn finish(self) {
self.store.node_masks.push(self.mask);
}
}
struct NodeReader<'a, T> {
store: &'a mut ChangeSets<T>,
mask: u32,
}
impl<T> NodeReader<'_, T> {
#[inline]
fn stored(&self, bit: usize) -> bool {
self.mask & (1 << bit) != 0
}
#[inline]
fn pop(&mut self) -> CandidateSet<T> {
self.store.sets.pop().expect("one set per stored bit")
}
}
impl<T: Clone + Ord> Table<T> {
pub(crate) fn aggregate_map<R, F>(&mut self, mode: Aggregation<F>) -> i64
where
R: Key,
F: Fn() -> T + Copy,
{
let mut sets: ChangeSets<T> = ChangeSets::new();
unsafe { self.collect_sets(Loc::root(), 0, None, mode, &mut sets) };
let delta = unsafe { self.rewrite(Loc::root(), 0, R::zero(), None, None, mode, &mut sets) };
debug_assert!(
sets.is_empty(),
"every stored change-set must be consumed exactly once"
);
delta
}
unsafe fn collect_sets<F>(
&self,
loc: Loc,
depth: u32,
covering_inherited: Option<&T>,
mode: Aggregation<F>,
sets: &mut ChangeSets<T>,
) -> CandidateSet<T>
where
F: Fn() -> T + Copy,
{
let node = *self.node(loc);
let (covering_value, _) =
unsafe { self.covering_values(loc, depth, node.data_bitmap(), covering_inherited) };
let mut child_sets: [Option<CandidateSet<T>>; NUM_CHILDREN] = std::array::from_fn(|_| None);
for c in (0..NUM_CHILDREN).rev() {
let covering = covering_value[15 + c / 2];
let set = if let Some(child) = unsafe { self.child(loc, c as u32) } {
unsafe { self.collect_sets(child, depth + K, covering, mode, sets) }
} else {
mode.leaf_set(covering)
};
child_sets[c] = Some(set);
}
let mut node_sets: [Option<CandidateSet<T>>; NUM_DATA] = std::array::from_fn(|_| None);
for j in 0..16 {
let (lo, hi) = (2 * j, 2 * j + 1);
let parent = match child_sets[lo]
.as_ref()
.unwrap()
.combine(child_sets[hi].as_ref().unwrap())
{
Combined::New(parent) => parent,
Combined::EqualsLeft | Combined::EqualsBoth => child_sets[lo].take().unwrap(),
Combined::EqualsRight => child_sets[hi].take().unwrap(),
};
node_sets[15 + j] = Some(parent);
}
let mut transparent = 0u32;
for b in (0..15).rev() {
let (lo, hi) = (2 * b + 1, 2 * b + 2);
let parent = match node_sets[lo]
.as_ref()
.unwrap()
.combine(node_sets[hi].as_ref().unwrap())
{
Combined::New(parent) => parent,
Combined::EqualsLeft => {
transparent |= 1 << lo;
node_sets[lo].take().unwrap()
}
Combined::EqualsRight => {
transparent |= 1 << hi;
node_sets[hi].take().unwrap()
}
Combined::EqualsBoth => {
transparent |= (1 << lo) | (1 << hi);
node_sets[lo].take().unwrap()
}
};
node_sets[b] = Some(parent);
}
let mut writer = sets.node_writer();
for b in (1..NUM_DATA).rev() {
if transparent & (1 << b) == 0 {
writer.push(b, node_sets[b].take().unwrap());
}
}
let root_set = node_sets[0].take().unwrap();
writer.push(0, root_set.clone());
writer.finish();
root_set
}
#[allow(clippy::too_many_arguments)]
unsafe fn rewrite<R, F>(
&mut self,
loc: Loc,
depth: u32,
key: R,
assigned_from_above: Option<&T>,
covering_inherited: Option<&T>,
mode: Aggregation<F>,
sets: &mut ChangeSets<T>,
) -> i64
where
R: Key,
F: Fn() -> T + Copy,
{
let node = *self.node(loc);
let (covering_value, _) =
unsafe { self.covering_values(loc, depth, node.data_bitmap(), covering_inherited) };
let child_covering: [Option<T>; 16] =
std::array::from_fn(|j| covering_value[15 + j].cloned());
let (assigned, mut delta) =
unsafe { self.assign_nodes(&node, loc, depth, assigned_from_above, sets) };
for c in 0..NUM_CHILDREN {
let above = 15 + c / 2;
delta += unsafe {
self.rewrite_child(
loc,
depth,
key,
c,
assigned[above].as_ref(),
child_covering[c / 2].as_ref(),
mode,
sets,
)
};
}
delta
}
unsafe fn assign_nodes(
&mut self,
node: &MultiBitNode,
loc: Loc,
depth: u32,
assigned_from_above: Option<&T>,
sets: &mut ChangeSets<T>,
) -> ([Option<T>; NUM_DATA], i64) {
let mut assigned: [Option<T>; NUM_DATA] = std::array::from_fn(|_| None);
let mut delta = 0;
let mut reader = sets.node_reader();
for b in 0..NUM_DATA {
let parent_assigned = if b == 0 {
assigned_from_above
} else {
assigned[(b - 1) / 2].as_ref()
};
if reader.stored(b) {
let set = reader.pop();
let (value, d) =
unsafe { self.rewrite_bit(node, loc, depth, b, parent_assigned, set) };
assigned[b] = value;
delta += d;
} else {
let present = node.has_data_bit(b as u32);
delta += unsafe { self.rewrite_bit_remove(loc, depth, b, present) };
assigned[b] = parent_assigned.cloned();
}
}
(assigned, delta)
}
unsafe fn rewrite_bit(
&mut self,
node: &MultiBitNode,
loc: Loc,
depth: u32,
b: usize,
parent_assigned: Option<&T>,
set: CandidateSet<T>,
) -> (Option<T>, i64) {
let present = node.has_data_bit(b as u32);
match set {
CandidateSet::ContainsHole => {
debug_assert!(!present, "a hole position never holds an entry");
(None, 0)
}
CandidateSet::Covered(s) if parent_assigned.is_some_and(|v| s.contains(v)) => {
(parent_assigned.cloned(), unsafe {
self.rewrite_bit_remove(loc, depth, b, present)
})
}
CandidateSet::Covered(s) => {
let winner = s.into_iter().next().expect("Covered holds a non-empty set");
self.rewrite_bit_insert(loc, depth, b, present, winner)
}
}
}
#[inline]
unsafe fn rewrite_bit_remove(&mut self, loc: Loc, depth: u32, b: usize, present: bool) -> i64 {
if !present {
return 0;
}
unsafe {
DataIdx {
node: loc,
bit: b as u32,
depth,
}
.resolve_mut(self)
}
.expect("rewrite: data bit not set")
.take();
-1
}
#[inline]
unsafe fn rewrite_bit_insert(
&mut self,
loc: Loc,
depth: u32,
b: usize,
present: bool,
winner: T,
) -> (Option<T>, i64) {
if present {
unsafe {
DataIdx {
node: loc,
bit: b as u32,
depth,
}
.resolve_mut(self)
}
.expect("rewrite: data bit not set")
.replace(winner.clone());
(Some(winner), 0)
} else {
EmptyMut {
table: self,
node: loc,
data_bit: b as u32,
depth,
}
.insert(winner.clone());
(Some(winner), 1)
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn rewrite_child<R, F>(
&mut self,
loc: Loc,
depth: u32,
key: R,
c: usize,
assigned_above: Option<&T>,
covering: Option<&T>,
mode: Aggregation<F>,
sets: &mut ChangeSets<T>,
) -> i64
where
R: Key,
F: Fn() -> T + Copy,
{
if let Some(child_loc) = unsafe { self.child(loc, c as u32) } {
let child_key = extend_repr(key, depth, c as u32);
let delta = unsafe {
self.rewrite(
child_loc,
depth + K,
child_key,
assigned_above,
covering,
mode,
sets,
)
};
let child = self.node(child_loc);
if child.data_bitmap() == 0 && child.child_bitmap() == 0 {
unsafe { self.remove_child_at(loc, c as u32) };
}
delta
} else if let Some(value) = mode.leaf_value(covering) {
if Some(&value) == assigned_above {
return 0;
}
unsafe { self.insert_child_root(loc, c as u32, depth, value) };
1
} else {
0
}
}
}
#[cfg(test)]
mod test {
#![allow(clippy::unusual_byte_groupings)]
use super::*;
fn bits(set: &[u32]) -> u32 {
set.iter().fold(0, |acc, &b| acc | (1 << b))
}
#[test]
fn fold_up_single_pairs() {
assert_eq!(fold_l1(bits(&[1, 2])), bits(&[0]));
assert_eq!(fold_l1(bits(&[1])), 0);
assert_eq!(fold_l1(bits(&[2])), 0);
assert_eq!(fold_l2(bits(&[3, 4])), bits(&[1]));
assert_eq!(fold_l2(bits(&[5, 6])), bits(&[2]));
assert_eq!(fold_l2(bits(&[3])), 0);
assert_eq!(fold_l3(bits(&[7, 8])), bits(&[3]));
assert_eq!(fold_l3(bits(&[13, 14])), bits(&[6]));
assert_eq!(fold_l3(bits(&[7])), 0);
assert_eq!(fold_l4(bits(&[15, 16])), bits(&[7]));
assert_eq!(fold_l4(bits(&[29, 30])), bits(&[14]));
assert_eq!(fold_l4(bits(&[15])), 0);
assert_eq!(fold_children(bits(&[0, 1])), bits(&[15]));
assert_eq!(fold_children(bits(&[2, 3])), bits(&[16]));
assert_eq!(fold_children(bits(&[30, 31])), bits(&[30]));
assert_eq!(fold_children(bits(&[0])), 0);
}
#[test]
fn push_down_single_parents() {
assert_eq!(push_l0(bits(&[0])), bits(&[1, 2]));
assert_eq!(push_l1(bits(&[1])), bits(&[3, 4]));
assert_eq!(push_l1(bits(&[2])), bits(&[5, 6]));
assert_eq!(push_l2(bits(&[3])), bits(&[7, 8]));
assert_eq!(push_l2(bits(&[6])), bits(&[13, 14]));
assert_eq!(push_l3(bits(&[7])), bits(&[15, 16]));
assert_eq!(push_l3(bits(&[14])), bits(&[29, 30]));
assert_eq!(push_l4(bits(&[15])), bits(&[0, 1]));
assert_eq!(push_l4(bits(&[16])), bits(&[2, 3]));
assert_eq!(push_l4(bits(&[30])), bits(&[30, 31]));
}
#[test]
fn push_down_ignores_other_levels() {
assert_eq!(push_l0(bits(&[1, 2, 3])), 0);
assert_eq!(push_l1(bits(&[0, 3, 4])), 0);
assert_eq!(push_l2(bits(&[1, 2, 7])), 0);
assert_eq!(push_l3(bits(&[0, 6, 15])), 0);
assert_eq!(push_l4(bits(&[0, 7, 14])), 0);
}
#[test]
fn fold_up_cascades_two_levels() {
let cov = bits(&[3, 4, 5, 6]);
let cov = cov | fold_l2(cov); assert_eq!(cov & bits(&[1, 2]), bits(&[1, 2]));
let cov = cov | fold_l1(cov); assert_eq!(cov & 1, 1);
}
#[test]
fn fold_and_push_are_inverse_on_full_coverage() {
for j in 0..16u32 {
let pair = bits(&[2 * j, 2 * j + 1]);
let parent = fold_children(pair); assert_eq!(push_l4(parent), pair);
}
}
#[test]
fn keep_drops_member_covered_bit() {
let d = bits(&[1, 7]);
let mut anc = 0u32;
anc |= push_l0(d | anc);
anc |= push_l1(d | anc);
anc |= push_l2(d | anc);
anc |= push_l3(d | anc);
let mut cov = d;
cov |= fold_l4(cov);
cov |= fold_l3(cov);
cov |= fold_l2(cov);
cov |= fold_l1(cov);
let cov_parent = push_l0(cov) | push_l1(cov) | push_l2(cov) | push_l3(cov);
let keep = cov & !cov_parent & !anc;
assert_eq!(keep, bits(&[1])); }
#[test]
fn keep_merges_siblings() {
let d = bits(&[7, 8]);
let mut cov = d;
cov |= fold_l4(cov);
cov |= fold_l3(cov); cov |= fold_l2(cov);
cov |= fold_l1(cov);
let cov_parent = push_l0(cov) | push_l1(cov) | push_l2(cov) | push_l3(cov);
let keep = cov & !cov_parent;
assert_eq!(keep, bits(&[3])); }
}