use rudb_common::{Error, Result};
use crate::bits::BitVector;
use crate::link::Link;
use crate::rid::{NO_PARENT, PART_ROWS, Rid};
pub const SPARSE_RATIO: u64 = 1000;
pub const STOP_AFTER: u64 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Form {
Full,
Sparse,
Dense,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Rids {
rows: u64,
body: Body,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Body {
Full,
Sparse(Vec<Rid>),
Dense { words: Vec<u64>, members: u64 },
}
impl Rids {
#[must_use]
pub fn full(rows: u64) -> Self {
if rows == 0 { Self::none(0) } else { Self { rows, body: Body::Full } }
}
#[must_use]
pub fn none(rows: u64) -> Self {
Self { rows, body: Body::Sparse(Vec::new()) }
}
pub fn from_sorted(rows: u64, members: Vec<Rid>) -> Result<Self> {
let mut previous = None;
for &member in &members {
if member >= rows || previous.is_some_and(|previous| member <= previous) {
return Err(Error::internal(format!(
"row {member} is out of order or past the end of a table of {rows} rows"
)));
}
previous = Some(member);
}
Ok(Self::settle_sparse(rows, members))
}
pub fn from_words(rows: u64, words: Vec<u64>) -> Result<Self> {
if count(words.len()) != rows.div_ceil(64) {
return Err(Error::internal(format!(
"{} words is not a bitmap over {rows} rows",
words.len()
)));
}
let tail = rows % 64;
if tail != 0 && words.last().is_some_and(|last| last >> tail != 0) {
return Err(Error::internal("a bitmap has rows set past the end of its table"));
}
Ok(Self::settle_dense(rows, words))
}
#[must_use]
pub fn rows(&self) -> u64 {
self.rows
}
#[must_use]
pub fn len(&self) -> u64 {
match &self.body {
Body::Full => self.rows,
Body::Sparse(members) => count(members.len()),
Body::Dense { members, .. } => *members,
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn is_full(&self) -> bool {
matches!(self.body, Body::Full)
}
#[must_use]
pub fn form(&self) -> Form {
match self.body {
Body::Full => Form::Full,
Body::Sparse(_) => Form::Sparse,
Body::Dense { .. } => Form::Dense,
}
}
#[must_use]
pub fn bytes(&self) -> usize {
match &self.body {
Body::Full => 0,
Body::Sparse(members) => members.len() * size_of::<Rid>(),
Body::Dense { words, .. } => words.len() * size_of::<u64>(),
}
}
#[must_use]
pub fn contains(&self, rid: Rid) -> bool {
if rid >= self.rows {
return false;
}
match &self.body {
Body::Full => true,
Body::Sparse(members) => members.binary_search(&rid).is_ok(),
Body::Dense { words, .. } => bit(words, rid),
}
}
#[must_use]
pub fn any_between(&self, low: Rid, high: Rid) -> bool {
let high = high.min(self.rows.saturating_sub(1));
if low > high || self.rows == 0 {
return false;
}
match &self.body {
Body::Full => true,
Body::Sparse(members) => {
let from = members.partition_point(|&member| member < low);
members.get(from).is_some_and(|&member| member <= high)
}
Body::Dense { words, .. } => {
let (first, last) = (index(low / 64), index(high / 64));
(first..=last).any(|at| {
let mut word = words[at];
if at == first {
word &= u64::MAX << (low % 64);
}
if at == last {
word &= u64::MAX >> (63 - high % 64);
}
word != 0
})
}
}
}
pub fn iter(&self) -> impl Iterator<Item = Rid> + '_ {
let (full, sparse, dense) = match &self.body {
Body::Full => (Some(0..self.rows), None, None),
Body::Sparse(members) => (None, Some(members.iter().copied()), None),
Body::Dense { words, .. } => (None, None, Some(ones(words))),
};
full.into_iter()
.flatten()
.chain(sparse.into_iter().flatten())
.chain(dense.into_iter().flatten())
}
pub fn intersect(&self, other: &Self) -> Result<Self> {
self.same_table(other)?;
Ok(match (&self.body, &other.body) {
(Body::Full, _) => other.clone(),
(_, Body::Full) => self.clone(),
(Body::Dense { words: left, .. }, Body::Dense { words: right, .. }) => {
let words = left.iter().zip(right).map(|(left, right)| left & right).collect();
Self::settle_dense(self.rows, words)
}
(Body::Sparse(members), _) => Self::settle_sparse(
self.rows,
members.iter().copied().filter(|&member| other.contains(member)).collect(),
),
(_, Body::Sparse(members)) => Self::settle_sparse(
self.rows,
members.iter().copied().filter(|&member| self.contains(member)).collect(),
),
})
}
pub fn union(&self, other: &Self) -> Result<Self> {
self.same_table(other)?;
if self.is_full() || other.is_full() {
return Ok(Self::full(self.rows));
}
let mut words = self.words();
for member in other.iter() {
words[index(member / 64)] |= 1 << (member % 64);
}
Ok(Self::settle_dense(self.rows, words))
}
pub fn forward(&self, link: &Link) -> Result<Pushed> {
self.push(link, false)
}
pub fn forward_or_stop(&self, link: &Link) -> Result<Pushed> {
self.push(link, true)
}
pub fn reach(&self, link: &Link) -> Result<(u64, u64)> {
if self.rows != link.parents() {
return Err(Error::internal(format!(
"a set over {} rows reached through a link whose parent has {}",
self.rows,
link.parents()
)));
}
let parts = link.children().div_ceil(count(PART_ROWS));
let reached = (0..parts)
.filter(|&part| match link.part_bounds(index(part)) {
Some(Some((low, high))) => self.any_between(low, high),
_ => false,
})
.count();
Ok((count(reached), parts))
}
fn push(&self, link: &Link, stopping: bool) -> Result<Pushed> {
if self.rows != link.parents() {
return Err(Error::internal(format!(
"a set over {} rows pushed through a link whose parent has {}",
self.rows,
link.parents()
)));
}
let children = link.children();
let parts = children.div_ceil(count(PART_ROWS));
if self.is_full() && link.linked() == children {
return Ok(Pushed { rids: Self::full(children), parts, skipped: 0, stopped: false });
}
if let (Body::Sparse(members), Some(_)) = (&self.body, link.runs()) {
return Self::push_members(members, link, children, parts);
}
if let Some(runs) = link.runs() {
return Ok(self.push_runs(runs, children, parts, stopping));
}
let mut words = vec![0_u64; index(children.div_ceil(64))];
let mut parents = vec![NO_PARENT; PART_ROWS];
let mut skipped = 0_u64;
let mark = children.div_ceil(STOP_AFTER);
let mut asked = !stopping;
let mut kept = 0_u64;
for part in 0..parts {
let first = part * count(PART_ROWS);
if !asked && first >= mark {
asked = true;
if kept == first {
return Ok(Pushed {
rids: Self::full(children),
parts,
skipped,
stopped: true,
});
}
}
let reach = match link.part_bounds(index(part)) {
Some(Some((low, high))) => self.any_between(low, high),
_ => false,
};
if !reach {
skipped += 1;
continue;
}
let run = index((children - first).min(count(PART_ROWS)));
link.forward_run(first, &mut parents[..run])?;
for (at, &parent) in parents[..run].iter().enumerate() {
if parent != NO_PARENT && self.contains(parent) {
let child = first + count(at);
words[index(child / 64)] |= 1 << (child % 64);
kept += 1;
}
}
}
Ok(Pushed { rids: Self::settle_dense(children, words), parts, skipped, stopped: false })
}
fn push_runs(&self, runs: &BitVector, children: u64, parts: u64, stopping: bool) -> Pushed {
let mut words = vec![0_u64; index(children.div_ceil(64))];
let bits = runs.words();
let len = runs.len();
let mark = children.div_ceil(STOP_AFTER);
let mut asked = !stopping;
let (mut parent, mut at, mut kept) = (0_u64, 0_usize, 0_u64);
for held in self.iter() {
if held > parent {
let Some(next) = past_zeros(bits, at, held - parent) else { break };
at = next;
parent = held;
}
if at > len {
break;
}
let run = count(ones_from(bits, at, len));
let child = count(at) - parent;
if run > 0 {
if !asked && child >= mark {
asked = true;
if kept == child {
return Pushed {
rids: Self::full(children),
parts,
skipped: 0,
stopped: true,
};
}
}
set_range(&mut words, child, child + run);
kept += run;
}
at += index(run) + 1;
parent += 1;
}
let per_part = PART_ROWS / 64;
let skipped =
count(words.chunks(per_part).filter(|part| part.iter().all(|&word| word == 0)).count());
Pushed { rids: Self::settle_dense(children, words), parts, skipped, stopped: false }
}
fn push_members(members: &[Rid], link: &Link, children: u64, parts: u64) -> Result<Pushed> {
let mut kept = Vec::new();
for &member in members {
let children = link.backward(member).ok_or_else(|| {
Error::internal(format!("parent {member} has no run in a monotone link"))
})?;
kept.extend(children);
}
let part = count(PART_ROWS);
let reached = count(kept.chunk_by(|a, b| a / part == b / part).count());
Ok(Pushed {
rids: Self::settle_sparse(children, kept),
parts,
skipped: parts.saturating_sub(reached),
stopped: false,
})
}
#[must_use]
pub fn offsets_in(&self, first: Rid, len: usize) -> Vec<u32> {
let end = first.saturating_add(count(len)).min(self.rows);
let offset = |rid: Rid| u32::try_from(rid - first).unwrap_or(u32::MAX);
match &self.body {
Body::Full => (first..end).map(offset).collect(),
Body::Sparse(members) => {
let from = members.partition_point(|&member| member < first);
members[from..]
.iter()
.take_while(|&&member| member < end)
.map(|&m| offset(m))
.collect()
}
Body::Dense { words, .. } => {
let mut out = Vec::new();
let mut at = first;
while at < end {
let word = words.get(index(at / 64)).copied().unwrap_or(0) >> (at % 64);
let span = (64 - at % 64).min(end - at);
let mut bits = if span == 64 { word } else { word & ((1 << span) - 1) };
while bits != 0 {
out.push(offset(at + u64::from(bits.trailing_zeros())));
bits &= bits - 1;
}
at += span;
}
out
}
}
}
pub fn backward(&self, link: &Link) -> Result<Self> {
if self.rows != link.children() {
return Err(Error::internal(format!(
"a set over {} rows pushed back through a link whose child has {}",
self.rows,
link.children()
)));
}
let mut words = vec![0_u64; index(link.parents().div_ceil(64))];
let mut parents = vec![NO_PARENT; PART_ROWS];
let children = link.children();
for part in 0..children.div_ceil(count(PART_ROWS)) {
let first = part * count(PART_ROWS);
let last = (first + count(PART_ROWS)).min(children) - 1;
if !self.any_between(first, last) {
continue;
}
let run = index(last - first + 1);
link.forward_run(first, &mut parents[..run])?;
for (at, &parent) in parents[..run].iter().enumerate() {
if parent != NO_PARENT && self.contains(first + count(at)) {
words[index(parent / 64)] |= 1 << (parent % 64);
}
}
}
Ok(Self::settle_dense(link.parents(), words))
}
fn same_table(&self, other: &Self) -> Result<()> {
if self.rows == other.rows {
Ok(())
} else {
Err(Error::internal(format!(
"a set over {} rows combined with one over {}",
self.rows, other.rows
)))
}
}
fn words(&self) -> Vec<u64> {
let mut words = vec![0_u64; index(self.rows.div_ceil(64))];
match &self.body {
Body::Dense { words: held, .. } => words.copy_from_slice(held),
_ => {
for member in self.iter() {
words[index(member / 64)] |= 1 << (member % 64);
}
}
}
words
}
fn settle_dense(rows: u64, words: Vec<u64>) -> Self {
let members = words.iter().map(|word| u64::from(word.count_ones())).sum::<u64>();
match shape(rows, members) {
Form::Full => Self::full(rows),
Form::Sparse => Self { rows, body: Body::Sparse(ones(&words).collect()) },
Form::Dense => Self { rows, body: Body::Dense { words, members } },
}
}
fn settle_sparse(rows: u64, members: Vec<Rid>) -> Self {
match shape(rows, count(members.len())) {
Form::Full => Self::full(rows),
Form::Sparse => Self { rows, body: Body::Sparse(members) },
Form::Dense => {
let mut words = vec![0_u64; index(rows.div_ceil(64))];
for member in &members {
words[index(member / 64)] |= 1 << (member % 64);
}
Self { rows, body: Body::Dense { words, members: count(members.len()) } }
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pushed {
pub rids: Rids,
pub parts: u64,
pub skipped: u64,
pub stopped: bool,
}
fn shape(rows: u64, members: u64) -> Form {
if rows > 0 && members == rows {
Form::Full
} else if members == 0 || members.saturating_mul(SPARSE_RATIO) < rows {
Form::Sparse
} else {
Form::Dense
}
}
fn bit(words: &[u64], at: u64) -> bool {
words.get(index(at / 64)).is_some_and(|word| word >> (at % 64) & 1 == 1)
}
fn past_zeros(bits: &[u64], mut at: usize, n: u64) -> Option<usize> {
let mut left = n - 1;
loop {
let shift = at % 64;
let zeros = !*bits.get(at / 64)? >> shift;
let found = u64::from(zeros.count_ones());
if found > left {
#[expect(clippy::cast_possible_truncation, reason = "under the zeros in one word")]
let within = crate::bits::nth_set(zeros, left as u32) as usize;
return Some(at + within + 1);
}
left -= found;
at += 64 - shift;
}
}
fn ones_from(bits: &[u64], at: usize, len: usize) -> usize {
let mut end = at;
while end < len {
let shift = end % 64;
let word = bits.get(end / 64).copied().unwrap_or(0) >> shift;
let found = (!word).trailing_zeros() as usize;
if found < 64 - shift {
return (end + found).min(len) - at;
}
end += 64 - shift;
}
len - at
}
fn set_range(words: &mut [u64], from: u64, to: u64) {
let (mut at, to) = (index(from), index(to));
while at < to {
let shift = at % 64;
let take = (64 - shift).min(to - at);
let mask = if take == 64 { u64::MAX } else { ((1_u64 << take) - 1) << shift };
words[at / 64] |= mask;
at += take;
}
}
fn ones(words: &[u64]) -> impl Iterator<Item = Rid> + '_ {
words.iter().enumerate().flat_map(|(at, &word)| {
let base = count(at) * 64;
let mut rest = word;
std::iter::from_fn(move || {
if rest == 0 {
return None;
}
let low = u64::from(rest.trailing_zeros());
rest &= rest - 1;
Some(base + low)
})
})
}
fn count(rows: usize) -> u64 {
u64::try_from(rows).unwrap_or(u64::MAX)
}
fn index(rows: u64) -> usize {
usize::try_from(rows).unwrap_or(usize::MAX)
}
#[cfg(test)]
mod tests {
use super::{Form, Rids, SPARSE_RATIO, STOP_AFTER};
use crate::link::Link;
use crate::rid::{NO_PARENT, PART_ROWS, Rid};
fn members(rids: &Rids) -> Vec<Rid> {
(0..rids.rows()).filter(|&rid| rids.contains(rid)).collect()
}
#[test]
fn the_offsets_of_a_bitmap_are_its_members_in_the_range() {
let rows = 5000;
let members: Vec<Rid> = (0..rows).filter(|rid| rid % 3 == 0 || rid % 64 == 63).collect();
let set = Rids::from_sorted(rows, members.clone()).expect("sorted");
assert_eq!(set.form(), Form::Dense);
for (first, len) in [(0, 64), (1, 63), (63, 2), (100, 1000), (4990, 64), (0, 5000), (7, 0)]
{
let slow = members
.iter()
.filter(|&&rid| rid >= first && rid < first + len)
.map(|&rid| u32::try_from(rid - first).expect("small"))
.collect::<Vec<_>>();
let len = usize::try_from(len).expect("small");
assert_eq!(set.offsets_in(first, len), slow, "from {first} for {len}");
}
}
#[test]
fn the_form_follows_the_count_and_not_the_constructor() {
let rows = 10 * SPARSE_RATIO;
assert_eq!(Rids::from_sorted(rows, vec![1, 2, 3]).expect("sorted").form(), Form::Sparse);
let many: Vec<Rid> = (0..rows).step_by(2).collect();
assert_eq!(Rids::from_sorted(rows, many).expect("sorted").form(), Form::Dense);
let every: Vec<Rid> = (0..rows).collect();
assert_eq!(Rids::from_sorted(rows, every).expect("sorted").form(), Form::Full);
let mut words = vec![0_u64; usize::try_from(rows.div_ceil(64)).expect("small")];
words[0] = 1;
assert_eq!(Rids::from_words(rows, words).expect("bitmap").form(), Form::Sparse);
}
#[test]
fn the_same_members_are_the_same_set_whichever_way_they_came_in() {
let rows = 5000;
let members: Vec<Rid> = (0..rows).filter(|rid| rid % 3 == 0).collect();
let mut words = vec![0_u64; usize::try_from(rows.div_ceil(64)).expect("small")];
for member in &members {
words[usize::try_from(member / 64).expect("small")] |= 1 << (member % 64);
}
let listed = Rids::from_sorted(rows, members).expect("sorted");
let mapped = Rids::from_words(rows, words).expect("bitmap");
assert_eq!(listed, mapped);
}
#[test]
fn a_member_out_of_order_or_past_the_end_is_refused() {
assert!(Rids::from_sorted(10, vec![3, 2]).is_err());
assert!(Rids::from_sorted(10, vec![3, 3]).is_err());
assert!(Rids::from_sorted(10, vec![10]).is_err());
assert!(Rids::from_words(10, vec![1 << 10]).is_err(), "a bit past the end");
assert!(Rids::from_words(10, vec![0, 0]).is_err(), "a word too many");
}
#[test]
fn a_full_set_holds_nothing_and_answers_everything() {
let full = Rids::full(1_000_000);
assert_eq!(full.bytes(), 0);
assert_eq!(full.len(), 1_000_000);
assert!(full.contains(999_999));
assert!(!full.contains(1_000_000));
}
#[test]
fn reach_counts_the_parts_a_push_would_decode() {
let parents = 100;
let parents_of: Vec<Rid> = (0..count(PART_ROWS) * 4)
.map(|child| child * parents / (count(PART_ROWS) * 4))
.collect();
let link = Link::build(&parents_of, parents).expect("every parent exists");
let one = Rids::from_sorted(parents, vec![30]).expect("a parent that exists");
assert_eq!(one.reach(&link).expect("the same parent"), (1, 4));
let two = Rids::from_sorted(parents, vec![10, 90]).expect("parents that exist");
assert_eq!(two.reach(&link).expect("the same parent"), (2, 4));
assert_eq!(Rids::full(parents).reach(&link).expect("the same parent"), (4, 4));
assert!(Rids::full(parents + 1).reach(&link).is_err(), "a set over another table");
}
#[test]
fn any_between_looks_only_inside_the_range_in_every_form() {
let rows = 4096;
for members in [vec![700], (0..rows).filter(|rid| rid % 2 == 0 && *rid != 700).collect()] {
let rids = Rids::from_sorted(rows, members.clone()).expect("sorted");
for (low, high) in [(0, 63), (64, 699), (699, 701), (700, 700), (1000, 5000)] {
let expected = members.iter().any(|&member| (low..=high).contains(&member));
assert_eq!(
rids.any_between(low, high),
expected,
"{low}..={high} over {:?}",
rids.form()
);
}
}
assert!(Rids::full(10).any_between(3, 3));
assert!(!Rids::full(10).any_between(10, 20), "past the end is outside the table");
}
#[test]
fn intersect_and_union_agree_with_the_slow_answer_across_forms() {
let rows = 20_000;
let sets = [
Rids::none(rows),
Rids::from_sorted(rows, vec![5, 700, 19_999]).expect("sorted"),
Rids::from_sorted(rows, (0..rows).filter(|rid| rid % 3 == 0).collect())
.expect("sorted"),
Rids::from_sorted(rows, (0..rows).filter(|rid| rid % 5 == 0).collect())
.expect("sorted"),
Rids::full(rows),
];
for left in &sets {
for right in &sets {
let both = left.intersect(right).expect("same table");
let either = left.union(right).expect("same table");
let (left_members, right_members) = (members(left), members(right));
let expected_both: Vec<Rid> = left_members
.iter()
.copied()
.filter(|rid| right_members.contains(rid))
.collect();
let mut expected_either = left_members.clone();
expected_either.extend(right_members.iter().copied());
expected_either.sort_unstable();
expected_either.dedup();
assert_eq!(members(&both), expected_both);
assert_eq!(members(&either), expected_either);
assert_eq!(both.iter().collect::<Vec<_>>(), expected_both, "iteration is in order");
}
}
assert!(Rids::full(3).intersect(&Rids::full(4)).is_err(), "two different tables");
}
fn link(children: u64, parents: u64, parent_of: impl Fn(u64) -> Rid) -> Link {
let of: Vec<Rid> = (0..children).map(parent_of).collect();
Link::build(&of, parents).expect("a link")
}
#[test]
fn a_forward_push_finds_exactly_the_children_that_point_into_the_set() {
let parents = 3000;
let children = 10 * count(PART_ROWS) + 17;
let clustered = link(children, parents, |child| child * parents / children);
let scattered = link(children, parents, |child| {
if child / count(PART_ROWS) == 4 { NO_PARENT } else { (child * 7919) % parents }
});
for link in [&clustered, &scattered] {
for set in [
Rids::none(parents),
Rids::from_sorted(parents, vec![0, 1500, 2999]).expect("sorted"),
Rids::from_sorted(parents, (0..parents).filter(|p| p % 4 == 1).collect())
.expect("sorted"),
Rids::full(parents),
] {
let pushed = set.forward(link).expect("the same table");
let expected: Vec<Rid> = (0..children)
.filter(|&child| link.forward(child).is_some_and(|parent| set.contains(parent)))
.collect();
assert_eq!(
members(&pushed.rids),
expected,
"{:?} through {:?}",
set.form(),
link.form()
);
}
}
}
#[test]
fn a_push_a_parent_at_a_time_keeps_exactly_the_children_of_the_parents_held() {
let parents: u64 = 2000;
let sizes: Vec<u64> =
(0..parents).map(|p| if p % 100 == 42 { 300 } else { p % 7 }).collect();
let of: Vec<Rid> =
(0..parents).flat_map(|p| std::iter::repeat_n(p, index(sizes[index(p)]))).collect();
let children = count(of.len());
let link = Link::build(&of, parents).expect("a link");
assert_eq!(link.form(), crate::link::Form::Monotone);
for set in [
Rids::none(parents),
Rids::from_sorted(parents, vec![0, 42, 1999]).expect("sorted"),
Rids::from_sorted(parents, (0..parents).filter(|p| p % 3 != 0).collect())
.expect("sorted"),
Rids::from_sorted(parents, (0..parents).filter(|p| p % 5 == 2).collect())
.expect("sorted"),
Rids::from_sorted(parents, (0..parents).filter(|p| p % 97 == 42).collect())
.expect("sorted"),
Rids::from_sorted(parents, (1990..parents).collect()).expect("sorted"),
] {
let pushed = set.forward(&link).expect("the same table");
let expected: Vec<Rid> =
(0..children).filter(|&child| set.contains(of[index(child)])).collect();
assert_eq!(members(&pushed.rids), expected, "{:?}", set.form());
let parts = children.div_ceil(count(PART_ROWS));
let untouched = (0..parts)
.filter(|part| !expected.iter().any(|child| child / count(PART_ROWS) == *part))
.count();
assert_eq!(pushed.skipped, count(untouched), "{:?}", set.form());
}
}
#[test]
fn a_sparse_push_a_member_at_a_time_keeps_exactly_the_children_of_its_members() {
let parents: u64 = 20 * SPARSE_RATIO;
let sizes: Vec<u64> =
(0..parents).map(|p| if p % 1000 == 42 { 3000 } else { p % 5 }).collect();
let of: Vec<Rid> =
(0..parents).flat_map(|p| std::iter::repeat_n(p, index(sizes[index(p)]))).collect();
let children = count(of.len());
let link = Link::build(&of, parents).expect("a link");
assert_eq!(link.form(), crate::link::Form::Monotone);
let set =
Rids::from_sorted(parents, vec![0, 5, 42, 7042, 7043, parents - 1]).expect("sorted");
assert_eq!(set.form(), Form::Sparse);
let pushed = set.forward_or_stop(&link).expect("the same table");
let expected: Vec<Rid> =
(0..children).filter(|&child| set.contains(of[index(child)])).collect();
assert_eq!(members(&pushed.rids), expected);
let parts = children.div_ceil(count(PART_ROWS));
let untouched = (0..parts)
.filter(|part| !expected.iter().any(|child| child / count(PART_ROWS) == *part))
.count();
assert_eq!(
(pushed.parts, pushed.skipped, pushed.stopped),
(parts, count(untouched), false)
);
}
#[test]
fn the_offsets_in_a_range_are_its_members_counted_from_its_start() {
let rows = 10 * SPARSE_RATIO;
let sparse = Rids::from_sorted(rows, vec![3, 100, 101, 9999]).expect("sorted");
let dense = Rids::from_sorted(rows, (0..rows).step_by(3).collect()).expect("sorted");
for set in [sparse, dense, Rids::full(rows), Rids::none(rows)] {
for (first, len) in [(0, 200), (100, 2), (9990, 50), (rows, 10)] {
let expected: Vec<u32> =
(0..len).filter(|&at| set.contains(first + u64::from(at))).collect();
assert_eq!(
set.offsets_in(first, index(u64::from(len))),
expected,
"{:?}",
set.form()
);
}
}
}
fn index(rows: u64) -> usize {
usize::try_from(rows).expect("small")
}
fn count(rows: usize) -> u64 {
u64::try_from(rows).expect("small")
}
#[test]
fn a_push_that_removes_nothing_by_the_third_stops_and_one_that_removes_something_finishes() {
let parents = 3000;
let children = 4 * STOP_AFTER * count(PART_ROWS);
let clustered = link(children, parents, |child| child * parents / children);
let every = Rids::full(parents);
let all_but_last: Vec<Rid> = (0..parents - 1).collect();
let most = Rids::from_sorted(parents, all_but_last).expect("sorted");
let stopped = most.forward_or_stop(&clustered).expect("the same table");
assert!(stopped.stopped, "nothing was removed in the first third");
assert!(stopped.rids.is_full(), "a stopped push keeps every row");
assert_eq!(stopped.parts, 4 * STOP_AFTER);
assert!(!every.forward_or_stop(&clustered).expect("the same table").stopped);
let all_but_first: Vec<Rid> = (1..parents).collect();
let early = Rids::from_sorted(parents, all_but_first).expect("sorted");
let finished = early.forward_or_stop(&clustered).expect("the same table");
assert!(!finished.stopped, "the first parent's children were removed before the third");
assert_eq!(finished, early.forward(&clustered).expect("the same table"));
assert_eq!(
finished.rids.len(),
children - count((0..children).filter(|child| child * parents / children == 0).count())
);
let orphans =
link(children, parents, |child| if child == 5 { NO_PARENT } else { child % parents });
assert!(!every.forward_or_stop(&orphans).expect("the same table").stopped);
}
#[test]
fn a_clustered_child_skips_every_part_that_points_outside_the_set() {
let parents = 1000;
let children = 100 * count(PART_ROWS);
let link = link(children, parents, |child| child * parents / children);
let set = Rids::from_sorted(parents, (100..200).collect()).expect("sorted");
let pushed = set.forward(&link).expect("the same table");
assert_eq!(pushed.parts, 100);
assert!(pushed.skipped >= 88, "only {} of 100 parts were skipped", pushed.skipped);
assert_eq!(pushed.rids.len(), children / 10);
}
#[test]
fn nothing_in_the_set_skips_every_part_and_everything_skips_the_pass() {
let link = link(5000, 100, |child| child % 100);
let pushed = Rids::none(100).forward(&link).expect("the same table");
assert_eq!((pushed.skipped, pushed.rids.len()), (pushed.parts, 0));
let pushed = Rids::full(100).forward(&link).expect("the same table");
assert!(pushed.rids.is_full(), "every child matched, so every child is in");
assert_eq!(pushed.skipped, 0);
}
#[test]
fn a_backward_push_finds_exactly_the_parents_the_set_points_at() {
let parents = 500;
let children = 7 * count(PART_ROWS) + 3;
let clustered = link(children, parents, |child| child * parents / children);
let scattered = link(children, parents, |child| {
if child % 11 == 0 { NO_PARENT } else { (child * 31) % parents }
});
for link in [&clustered, &scattered] {
let set = Rids::from_sorted(children, (0..children).filter(|c| c % 97 == 3).collect())
.expect("sorted");
let pushed = set.backward(link).expect("the same table");
let mut expected: Vec<Rid> =
set.iter().filter_map(|child| link.forward(child)).collect();
expected.sort_unstable();
expected.dedup();
assert_eq!(members(&pushed), expected, "through {:?}", link.form());
}
}
#[test]
fn a_set_over_the_wrong_table_is_refused_rather_than_pushed() {
let link = link(100, 10, |child| child % 10);
assert!(Rids::full(11).forward(&link).is_err());
assert!(Rids::full(10).backward(&link).is_err());
}
}