#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Malformed {
Short,
Width,
Length,
Order,
}
const W16: u32 = 2;
const W32: u32 = 4;
const W64: u32 = 8;
const HEADER: usize = 8;
const RUN_MAX: usize = 512;
const RUN_MIN: usize = RUN_MAX / 4;
const STEP: usize = 32;
#[derive(Debug, Clone)]
pub struct Intset {
runs: Vec<Run>,
total: usize,
maxima: Vec<i64>,
fen: Vec<u32>,
}
impl Intset {
#[must_use]
pub fn new() -> Intset {
Intset {
runs: vec![Run::new()],
total: 0,
maxima: vec![i64::MAX],
fen: vec![0, 0],
}
}
#[must_use]
pub fn with_capacity(n: usize) -> Intset {
let mut s = Intset::new();
s.runs[0].reserve_members(n.min(RUN_MAX));
if n > RUN_MAX {
s.runs.reserve(n / (RUN_MAX / 2));
s.maxima.reserve(n / (RUN_MAX / 2));
}
s
}
pub fn from_bytes(bytes: &[u8]) -> Result<Intset, Malformed> {
let run = Run::from_bytes(bytes)?;
let total = run.len();
let mut s = Intset {
runs: vec![run],
total,
maxima: Vec::new(),
fen: Vec::new(),
};
s.maxima.push(top_of(&s.runs[0]));
s.rebuild_ranks();
Ok(s)
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> Option<&[u8]> {
match self.runs.as_slice() {
[run] if run.base == 0 => Some(run.as_bytes()),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.total
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.total == 0
}
#[inline]
#[must_use]
pub fn runs(&self) -> usize {
self.runs.len()
}
#[must_use]
pub fn width(&self) -> usize {
self.runs
.iter()
.map(Run::width)
.max()
.unwrap_or(W16 as usize)
}
#[inline]
#[must_use]
pub fn byte_len(&self) -> usize {
self.runs.iter().map(Run::byte_len).sum()
}
#[must_use]
pub fn memory_bytes(&self) -> usize {
let runs: usize = self.runs.iter().map(Run::memory_bytes).sum();
runs + self.runs.capacity() * size_of::<Run>()
+ self.maxima.capacity() * size_of::<i64>()
+ self.fen.capacity() * size_of::<u32>()
}
#[inline]
#[must_use]
pub fn at(&self, index: usize) -> i64 {
assert!(index < self.total, "index {index} is past the set");
let (run, offset) = self.select(index);
self.runs[run].at(offset)
}
#[inline]
#[must_use]
pub fn get(&self, index: usize) -> Option<i64> {
(index < self.total).then(|| self.at(index))
}
#[inline]
#[must_use]
pub fn min(&self) -> Option<i64> {
self.runs.first().and_then(Run::min)
}
#[inline]
#[must_use]
pub fn max(&self) -> Option<i64> {
self.runs.last().and_then(Run::max)
}
#[inline]
#[must_use]
pub fn contains(&self, v: i64) -> bool {
self.runs[self.run_for(v)].contains(v)
}
pub fn iter(&self) -> impl Iterator<Item = i64> + '_ {
self.runs.iter().flat_map(Run::iter)
}
#[inline]
#[must_use]
pub fn walk(&self) -> Walk<'_> {
Walk::new(self)
}
pub fn add(&mut self, v: i64) -> bool {
let i = self.run_for(v);
let rebase = self.runs.len() > 1;
if !self.runs[i].add(v, rebase) {
return false;
}
self.total += 1;
if self.runs[i].len() > RUN_MAX {
self.split(i);
} else {
self.maxima[i] = top_of(&self.runs[i]);
self.bump(i, 1);
}
true
}
pub fn remove(&mut self, v: i64) -> bool {
let i = self.run_for(v);
if !self.runs[i].remove(v) {
return false;
}
self.total -= 1;
self.maxima[i] = top_of(&self.runs[i]);
if self.runs.len() > 1 && self.runs[i].len() < RUN_MIN {
self.shrink(i);
} else {
self.bump(i, -1);
}
true
}
#[inline]
fn run_for(&self, v: i64) -> usize {
let i = self.maxima.partition_point(|&m| m < v);
i.min(self.runs.len() - 1)
}
fn split(&mut self, i: usize) {
let n = self.runs[i].len();
let half = n / 2;
let src = &self.runs[i];
let (base, w) = frame(src.at(half), src.at(n - 1));
let mut hi = Run::with_base(base, w, n - half);
for k in half..n {
hi.push_back(src.at(k));
}
self.runs[i].truncate(half);
self.runs[i].rebase();
self.runs[i].tighten();
self.runs.insert(i + 1, hi);
self.maxima[i] = top_of(&self.runs[i]);
let top = top_of(&self.runs[i + 1]);
self.maxima.insert(i + 1, top);
self.rebuild_ranks();
}
fn shrink(&mut self, i: usize) {
if self.runs[i].is_empty() {
self.runs.remove(i);
self.maxima.remove(i);
self.rebuild_ranks();
return;
}
let fits = |a: usize, b: usize| self.runs[a].len() + self.runs[b].len() <= RUN_MAX;
let (lo, hi) = if i > 0 && fits(i - 1, i) {
(i - 1, i)
} else if i + 1 < self.runs.len() && fits(i, i + 1) {
(i, i + 1)
} else {
self.bump(i, -1);
return;
};
let src = self.runs.remove(hi);
self.maxima.remove(hi);
self.runs[lo].append(&src);
if self.runs.len() == 1 {
self.runs[0].unframe();
}
self.maxima[lo] = top_of(&self.runs[lo]);
self.rebuild_ranks();
}
fn select(&self, k: usize) -> (usize, usize) {
let n = self.runs.len();
let mut pos = 0usize;
let mut rem = k;
let mut step = 1usize << (usize::BITS - 1 - n.leading_zeros());
while step > 0 {
let next = pos + step;
if next <= n {
let covered = self.fen[next] as usize;
if covered <= rem {
pos = next;
rem -= covered;
}
}
step >>= 1;
}
(pos, rem)
}
fn bump(&mut self, i: usize, delta: i32) {
let n = self.runs.len();
let mut at = i + 1;
while at <= n {
if delta > 0 {
self.fen[at] += 1;
} else {
self.fen[at] -= 1;
}
at += at & at.wrapping_neg();
}
}
fn rebuild_ranks(&mut self) {
let n = self.runs.len();
self.fen.clear();
self.fen.resize(n + 1, 0);
for i in 1..=n {
let len = u32::try_from(self.runs[i - 1].len()).expect("a run is under RUN_MAX");
self.fen[i] += len;
let parent = i + (i & i.wrapping_neg());
if parent <= n {
let carry = self.fen[i];
self.fen[parent] += carry;
}
}
}
}
impl Default for Intset {
fn default() -> Intset {
Intset::new()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Walk<'a> {
set: &'a Intset,
run: usize,
off: usize,
}
impl<'a> Walk<'a> {
fn new(set: &'a Intset) -> Walk<'a> {
let mut w = Walk {
set,
run: 0,
off: 0,
};
w.settle();
w
}
#[inline]
#[must_use]
pub fn peek(&self) -> Option<i64> {
(self.run < self.set.runs.len()).then(|| self.set.runs[self.run].at(self.off))
}
#[inline]
pub fn bump(&mut self) {
self.off += 1;
self.settle();
}
pub fn seek(&mut self, v: i64) {
match self.peek() {
Some(cur) if cur < v => {}
_ => return,
}
if self.set.maxima[self.run] < v {
let after = &self.set.maxima[self.run + 1..];
let hop = after.partition_point(|&m| m < v);
self.run += 1 + hop;
if self.run >= self.set.runs.len() {
self.run = self.set.runs.len();
self.off = 0;
return;
}
self.off = 0;
}
self.off = self.set.runs[self.run].lower_bound(v, self.off);
self.settle();
}
#[inline]
fn settle(&mut self) {
while self.run < self.set.runs.len() && self.off >= self.set.runs[self.run].len() {
self.run += 1;
self.off = 0;
}
}
}
impl PartialEq for Intset {
fn eq(&self, other: &Intset) -> bool {
self.total == other.total && self.iter().eq(other.iter())
}
}
impl Eq for Intset {}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Run {
bytes: Vec<u8>,
base: i64,
}
impl Run {
fn new() -> Run {
Run::with_base(0, W16 as usize, 0)
}
fn with_base(base: i64, w: usize, n: usize) -> Run {
let mut bytes = Vec::with_capacity(HEADER + n * w);
bytes.extend_from_slice(&(w as u32).to_le_bytes());
bytes.extend_from_slice(&0u32.to_le_bytes());
Run { bytes, base }
}
fn from_bytes(bytes: &[u8]) -> Result<Run, Malformed> {
if bytes.len() < HEADER {
return Err(Malformed::Short);
}
let width = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes"));
if width != W16 && width != W32 && width != W64 {
return Err(Malformed::Width);
}
let count = u32::from_le_bytes(bytes[4..8].try_into().expect("four bytes")) as usize;
let want = count
.checked_mul(width as usize)
.and_then(|n| n.checked_add(HEADER))
.ok_or(Malformed::Length)?;
if bytes.len() != want {
return Err(Malformed::Length);
}
let s = Run {
bytes: bytes.to_vec(),
base: 0,
};
for i in 1..count {
if s.at(i - 1) >= s.at(i) {
return Err(Malformed::Order);
}
}
Ok(s)
}
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[inline]
fn len(&self) -> usize {
u32::from_le_bytes(self.bytes[4..8].try_into().expect("four bytes")) as usize
}
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
fn width(&self) -> usize {
u32::from_le_bytes(self.bytes[0..4].try_into().expect("four bytes")) as usize
}
#[inline]
fn byte_len(&self) -> usize {
self.bytes.len()
}
#[inline]
fn memory_bytes(&self) -> usize {
self.bytes.capacity()
}
#[inline]
fn at(&self, index: usize) -> i64 {
self.base + self.raw(index)
}
#[inline]
fn raw(&self, index: usize) -> i64 {
self.raw_w(index, self.width())
}
#[inline]
fn raw_w(&self, index: usize, w: usize) -> i64 {
let at = HEADER + index * w;
let raw = &self.bytes[at..at + w];
match w {
2 => i64::from(i16::from_le_bytes(raw.try_into().expect("two bytes"))),
4 => i64::from(i32::from_le_bytes(raw.try_into().expect("four bytes"))),
_ => i64::from_le_bytes(raw.try_into().expect("eight bytes")),
}
}
#[inline]
fn framed(&self, v: i64) -> bool {
v.checked_sub(self.base)
.is_some_and(|off| width_of(off) as usize <= self.width())
}
#[inline]
fn min(&self) -> Option<i64> {
(!self.is_empty()).then(|| self.at(0))
}
#[inline]
fn max(&self) -> Option<i64> {
self.len().checked_sub(1).map(|last| self.at(last))
}
#[inline]
fn contains(&self, v: i64) -> bool {
self.framed(v) && self.search(v).is_ok()
}
fn iter(&self) -> impl Iterator<Item = i64> + '_ {
(0..self.len()).map(|i| self.at(i))
}
fn reserve_members(&mut self, n: usize) {
self.bytes.reserve_exact(n * self.width());
}
fn add(&mut self, v: i64, rebase: bool) -> bool {
if !self.framed(v) {
self.refit_and_add(v, rebase);
return true;
}
match self.search(v) {
Ok(_) => false,
Err(at) => {
self.insert_at(at, v);
true
}
}
}
fn push_back(&mut self, v: i64) {
let w = self.width();
let at = self.bytes.len();
self.grow_by(w);
write_at(&mut self.bytes, at, w, v - self.base);
self.set_len(self.len() + 1);
}
fn append(&mut self, other: &Run) {
self.reserve_members(other.len());
for v in other.iter() {
self.add(v, true);
}
}
fn truncate(&mut self, n: usize) {
self.bytes.truncate(HEADER + n * self.width());
self.set_len(n);
}
fn tighten(&mut self) {
self.bytes.shrink_to_fit();
}
fn remove(&mut self, v: i64) -> bool {
if !self.framed(v) {
return false;
}
let Ok(at) = self.search(v) else {
return false;
};
let w = self.width();
let from = HEADER + at * w;
self.bytes.drain(from..from + w);
self.set_len(self.len() - 1);
true
}
fn lower_bound(&self, v: i64, from: usize) -> usize {
let w = self.width();
let off = self.offset_of(v);
let (mut lo, mut hi) = (from, self.len());
while lo < hi {
let mid = lo.midpoint(hi);
if self.raw_w(mid, w) < off {
lo = mid + 1;
} else {
hi = mid;
}
}
lo
}
#[inline]
fn offset_of(&self, v: i64) -> i64 {
v.checked_sub(self.base)
.unwrap_or(if v < self.base { i64::MIN } else { i64::MAX })
}
fn search(&self, v: i64) -> Result<usize, usize> {
let n = self.len();
if n == 0 {
return Err(0);
}
let w = self.width();
let off = self.offset_of(v);
if off > self.raw_w(n - 1, w) {
return Err(n);
}
if off < self.raw_w(0, w) {
return Err(0);
}
let (mut lo, mut hi) = (0usize, n - 1);
while lo <= hi {
let mid = lo.midpoint(hi);
let cur = self.raw_w(mid, w);
if off > cur {
lo = mid + 1;
} else if off < cur {
hi = mid - 1;
} else {
return Ok(mid);
}
}
Err(lo)
}
fn refit_and_add(&mut self, v: i64, rebase: bool) {
let n = self.len();
let mut held = [0i64; RUN_MAX + 2];
for (i, slot) in held.iter_mut().enumerate().take(n) {
*slot = self.at(i);
}
let ahead = usize::from(n > 0 && v < held[0]);
if ahead == 1 {
held.copy_within(0..n, 1);
}
held[if ahead == 1 { 0 } else { n }] = v;
self.repack(&held[..n + 1], rebase);
}
fn rebase(&mut self) {
let n = self.len();
let mut held = [0i64; RUN_MAX + 2];
for (i, slot) in held.iter_mut().enumerate().take(n) {
*slot = self.at(i);
}
self.repack(&held[..n], true);
}
fn unframe(&mut self) {
if self.base == 0 {
return;
}
let n = self.len();
let mut held = [0i64; RUN_MAX + 2];
for (i, slot) in held.iter_mut().enumerate().take(n) {
*slot = self.at(i);
}
self.repack(&held[..n], false);
}
fn repack(&mut self, members: &[i64], rebase: bool) {
let (base, w) = match members {
[] => (0, W16 as usize),
[only] => (if rebase { *only } else { 0 }, {
let off = if rebase { 0 } else { *only };
width_of(off) as usize
}),
[lo, .., hi] if rebase => frame(*lo, *hi),
[lo, .., hi] => (0, width_of(*lo).max(width_of(*hi)) as usize),
};
self.base = base;
self.bytes.resize(HEADER + members.len() * w, 0);
self.bytes[0..4].copy_from_slice(&(w as u32).to_le_bytes());
for (i, &v) in members.iter().enumerate() {
write_at(&mut self.bytes, HEADER + i * w, w, v - base);
}
self.set_len(members.len());
}
fn insert_at(&mut self, at: usize, v: i64) {
let w = self.width();
let from = HEADER + at * w;
let old = self.bytes.len();
self.grow_by(w);
self.bytes.copy_within(from..old, from + w);
write_at(&mut self.bytes, from, w, v - self.base);
self.set_len(self.len() + 1);
}
#[inline]
fn grow_by(&mut self, w: usize) {
let want = self.bytes.len() + w;
if want > self.bytes.capacity() {
yo_alloc::for_the_data(|| self.bytes.reserve_exact(STEP * w));
}
self.bytes.resize(want, 0);
}
#[inline]
fn set_len(&mut self, n: usize) {
let n = u32::try_from(n).expect("a run never reaches four billion members");
self.bytes[4..8].copy_from_slice(&n.to_le_bytes());
}
}
#[inline]
fn top_of(r: &Run) -> i64 {
r.max().unwrap_or(i64::MAX)
}
#[inline]
fn frame(lo: i64, hi: i64) -> (i64, usize) {
let Some(span) = hi.checked_sub(lo) else {
return (0, W64 as usize);
};
let base = lo + span / 2;
let w = width_of(lo - base).max(width_of(hi - base));
(base, w as usize)
}
#[inline]
const fn width_of(v: i64) -> u32 {
if v < i32::MIN as i64 || v > i32::MAX as i64 {
W64
} else if v < i16::MIN as i64 || v > i16::MAX as i64 {
W32
} else {
W16
}
}
#[inline]
fn write_at(bytes: &mut [u8], at: usize, w: usize, v: i64) {
match w {
2 => bytes[at..at + 2].copy_from_slice(&(v as i16).to_le_bytes()),
4 => bytes[at..at + 4].copy_from_slice(&(v as i32).to_le_bytes()),
_ => bytes[at..at + 8].copy_from_slice(&v.to_le_bytes()),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn of(vals: &[i64]) -> Intset {
let mut s = Intset::new();
for &v in vals {
assert!(s.add(v), "{v} was supposed to be new");
}
s
}
fn members(s: &Intset) -> Vec<i64> {
s.iter().collect()
}
#[test]
fn a_set_of_large_integers_still_stores_them_in_two_bytes() {
let n: i64 = if cfg!(miri) { 3_000 } else { 10_000 };
let mut s = Intset::new();
for i in 0..n {
s.add(1_000_000_000 + i * 3);
}
assert_eq!(s.width(), W16 as usize, "every run is two bytes a member");
assert!(
s.byte_len() < n as usize * 2 + s.runs() * 16,
"{} bytes for {n} members over {} runs",
s.byte_len(),
s.runs()
);
for i in 0..n {
assert!(s.contains(1_000_000_000 + i * 3), "member {i}");
assert!(!s.contains(1_000_000_000 + i * 3 + 1), "gap after {i}");
}
assert_eq!(s.len(), n as usize);
}
#[test]
fn a_member_under_the_frame_moves_it_instead_of_widening_it() {
let mut s = Intset::new();
for i in 0..2_000i64 {
s.add(500_000 + i * 100);
}
let before = s.width();
for i in 0..50i64 {
assert!(s.add(500_000 - 1 - i), "{i} is new and under everything");
}
assert_eq!(s.width(), before, "still two bytes a member");
assert_eq!(s.min(), Some(500_000 - 50));
assert_eq!(s.len(), 2_050);
for i in 0..50i64 {
assert!(s.contains(500_000 - 1 - i));
}
}
#[test]
fn the_frame_holds_negative_members_too() {
let n: i64 = if cfg!(miri) { 1_500 } else { 3_000 };
let mut s = Intset::new();
for i in 0..n {
s.add(-2_000_000_000 + i * 7);
}
assert_eq!(s.width(), W16 as usize);
assert_eq!(s.min(), Some(-2_000_000_000));
assert_eq!(s.max(), Some(-2_000_000_000 + (n - 1) * 7));
for i in 0..n {
assert!(s.contains(-2_000_000_000 + i * 7), "member {i}");
}
assert_eq!(members(&s).len(), n as usize);
}
#[test]
fn a_span_too_wide_to_subtract_gets_no_frame() {
assert_eq!(frame(i64::MIN, i64::MAX), (0, W64 as usize));
let mut s = Intset::new();
for i in 0..600i64 {
s.add(i);
}
s.add(i64::MIN);
s.add(i64::MAX);
assert_eq!(s.width(), W64 as usize, "the widest run holds both ends");
assert!(s.contains(i64::MIN) && s.contains(i64::MAX) && s.contains(300));
assert_eq!(s.len(), 602);
}
#[test]
fn a_one_run_set_is_still_a_redis_intset() {
let s = of(&[1_000_000_000, 1_000_000_001, 2_000_000_000]);
let bytes = s.as_bytes().expect("one run");
assert_eq!(
Intset::from_bytes(bytes).expect("a real server could read this"),
s
);
assert_eq!(s.width(), W32 as usize, "no base, so the values decide");
}
#[test]
fn a_set_drained_back_to_one_run_is_a_redis_intset_again() {
let n: i64 = if cfg!(miri) { 1_500 } else { 4_000 };
let mut s = Intset::new();
for i in 0..n {
s.add(1_000_000_000 + i * 3);
}
assert!(s.runs.len() > 1, "several runs to start with");
assert!(s.as_bytes().is_none(), "framed, so not a Redis intset");
for i in 100..n {
s.remove(1_000_000_000 + i * 3);
}
sound(&s);
assert_eq!(s.runs.len(), 1, "the merges took it back to one run");
let bytes = s.as_bytes().expect("one run, so the frame is gone");
assert_eq!(
Intset::from_bytes(bytes).expect("a real server could read this"),
s
);
assert_eq!(s.len(), 100);
}
fn sound(s: &Intset) {
assert!(!s.runs.is_empty(), "there is always a run to land in");
assert_eq!(s.maxima.len(), s.runs.len(), "one maximum per run");
let mut seen = 0usize;
let mut last: Option<i64> = None;
for (i, r) in s.runs.iter().enumerate() {
assert!(
!r.is_empty() || s.runs.len() == 1,
"run {i} is empty and is not the only one"
);
assert!(r.len() <= RUN_MAX, "run {i} holds {} members", r.len());
assert_eq!(s.maxima[i], top_of(r), "the maximum of run {i} is stale");
for v in r.iter() {
if let Some(prev) = last {
assert!(prev < v, "{prev} then {v} is not ascending");
}
last = Some(v);
}
seen += r.len();
}
assert_eq!(seen, s.len(), "the runs and the count disagree");
let mut at = 0usize;
for (i, r) in s.runs.iter().enumerate() {
for k in 0..r.len() {
assert_eq!(s.select(at), (i, k), "position {at}");
at += 1;
}
}
}
#[test]
fn an_empty_set_is_eight_bytes_and_holds_nothing() {
let s = Intset::new();
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert_eq!(s.width(), 2);
assert_eq!(s.byte_len(), 8);
assert_eq!(s.min(), None);
assert_eq!(s.max(), None);
assert!(!s.contains(0));
assert_eq!(s.as_bytes(), Some(&[2, 0, 0, 0, 0, 0, 0, 0][..]));
}
#[test]
fn members_come_back_sorted_however_they_went_in() {
let s = of(&[5, -3, 100, 0, -70, 42]);
assert_eq!(members(&s), [-70, -3, 0, 5, 42, 100]);
assert_eq!(s.min(), Some(-70));
assert_eq!(s.max(), Some(100));
assert_eq!(s.len(), 6);
sound(&s);
}
#[test]
fn adding_the_same_member_twice_says_so_and_changes_nothing() {
let mut s = of(&[1, 2, 3]);
assert!(!s.add(2));
assert_eq!(members(&s), [1, 2, 3]);
assert_eq!(s.byte_len(), 8 + 3 * 2);
}
#[test]
fn a_small_set_of_integers_costs_two_bytes_each() {
let s = of(&(0..512).collect::<Vec<i64>>());
assert_eq!(s.runs(), 1, "512 is still one run");
assert_eq!(s.width(), 2);
assert_eq!(s.byte_len(), 8 + 512 * 2);
assert_eq!((s.byte_len() - 8) / s.len(), 2);
}
#[test]
fn the_width_follows_the_widest_member_and_never_comes_back_down() {
let mut s = of(&[1, 2, 3]);
assert_eq!(s.width(), 2);
s.add(100_000);
assert_eq!(s.width(), 4, "past an i16");
assert_eq!(members(&s), [1, 2, 3, 100_000]);
s.add(-5_000_000_000);
assert_eq!(s.width(), 8, "past an i32");
assert_eq!(members(&s), [-5_000_000_000, 1, 2, 3, 100_000]);
assert!(s.remove(-5_000_000_000));
assert!(s.remove(100_000));
assert_eq!(s.width(), 8, "removing does not narrow it back");
assert_eq!(members(&s), [1, 2, 3]);
}
#[test]
fn widening_puts_a_negative_at_the_front_and_a_positive_at_the_back() {
let mut up = of(&[-2, -1, 0, 1, 2]);
up.add(70_000);
assert_eq!(members(&up), [-2, -1, 0, 1, 2, 70_000]);
let mut down = of(&[-2, -1, 0, 1, 2]);
down.add(-70_000);
assert_eq!(members(&down), [-70_000, -2, -1, 0, 1, 2]);
}
#[test]
fn widening_an_empty_set_still_works() {
let mut s = Intset::new();
assert!(s.add(i64::MIN));
assert_eq!(s.width(), 8);
assert_eq!(members(&s), [i64::MIN]);
}
#[test]
fn the_extremes_of_every_width_land_in_the_width_they_belong_to() {
assert_eq!(width_of(0), 2);
assert_eq!(width_of(i64::from(i16::MAX)), 2);
assert_eq!(width_of(i64::from(i16::MIN)), 2);
assert_eq!(width_of(i64::from(i16::MAX) + 1), 4);
assert_eq!(width_of(i64::from(i16::MIN) - 1), 4);
assert_eq!(width_of(i64::from(i32::MAX)), 4);
assert_eq!(width_of(i64::from(i32::MIN)), 4);
assert_eq!(width_of(i64::from(i32::MAX) + 1), 8);
assert_eq!(width_of(i64::from(i32::MIN) - 1), 8);
assert_eq!(width_of(i64::MAX), 8);
assert_eq!(width_of(i64::MIN), 8);
let s = of(&[i64::MIN, i64::MAX, 0]);
assert_eq!(members(&s), [i64::MIN, 0, i64::MAX]);
assert!(s.contains(i64::MIN));
assert!(s.contains(i64::MAX));
}
#[test]
fn a_member_too_wide_for_the_set_is_not_in_it() {
let s = of(&[1, 2, 3]);
assert!(!s.contains(100_000));
assert!(!s.contains(i64::MAX));
}
#[test]
fn removing_takes_out_the_right_one_and_only_that_one() {
let mut s = of(&[10, 20, 30, 40, 50]);
assert!(s.remove(30));
assert_eq!(members(&s), [10, 20, 40, 50]);
assert!(!s.remove(30), "gone already");
assert!(s.remove(10), "the first");
assert_eq!(members(&s), [20, 40, 50]);
assert!(s.remove(50), "the last");
assert_eq!(members(&s), [20, 40]);
assert_eq!(s.byte_len(), 8 + 2 * 2, "and the blob shrank each time");
}
#[test]
fn a_set_can_be_emptied_and_used_again() {
let mut s = of(&[1, 2, 3]);
for v in [1, 2, 3] {
assert!(s.remove(v));
}
assert!(s.is_empty());
assert_eq!(s.byte_len(), 8);
assert!(s.add(9));
assert_eq!(members(&s), [9]);
sound(&s);
}
#[test]
fn every_member_of_a_big_set_is_found_and_no_stranger_is() {
let mut s = Intset::new();
for i in 0..1000i64 {
assert!(s.add((i * 7919) % 1000 * 2));
}
assert_eq!(s.len(), 1000);
for i in 0..1000i64 {
assert!(s.contains(i * 2), "{} is a member", i * 2);
assert!(!s.contains(i * 2 + 1), "{} is not", i * 2 + 1);
}
assert_eq!(members(&s), (0..1000i64).map(|i| i * 2).collect::<Vec<_>>());
sound(&s);
}
#[test]
fn a_blob_survives_a_round_trip_through_bytes() {
for vals in [
&[][..],
&[0],
&[1, 2, 3],
&[-70_000, 5, 70_000],
&[i64::MIN, 0, i64::MAX],
] {
let s = of(vals);
let back = Intset::from_bytes(s.as_bytes().expect("one run")).expect("we wrote it");
assert_eq!(back, s);
assert_eq!(members(&back), members(&s));
}
}
#[test]
fn a_blob_that_is_wrong_is_refused_rather_than_believed() {
assert_eq!(Intset::from_bytes(&[]), Err(Malformed::Short));
assert_eq!(
Intset::from_bytes(&[2, 0, 0, 0, 0, 0, 0]),
Err(Malformed::Short)
);
let good = |vals: &[i64]| of(vals).as_bytes().expect("one run").to_vec();
let mut bad = good(&[1, 2, 3]);
bad[0] = 3;
assert_eq!(Intset::from_bytes(&bad), Err(Malformed::Width));
let mut short = good(&[1, 2, 3]);
short.pop();
assert_eq!(Intset::from_bytes(&short), Err(Malformed::Length));
let mut over = good(&[1, 2, 3]);
over[4] = 9;
assert_eq!(Intset::from_bytes(&over), Err(Malformed::Length));
let mut jumbled = good(&[1, 2, 3]);
jumbled[8..10].copy_from_slice(&9i16.to_le_bytes());
assert_eq!(Intset::from_bytes(&jumbled), Err(Malformed::Order));
let mut twice = good(&[1, 2, 3]);
twice[10..12].copy_from_slice(&1i16.to_le_bytes());
assert_eq!(Intset::from_bytes(&twice), Err(Malformed::Order));
}
#[test]
fn the_header_is_little_endian_on_every_machine() {
let s = of(&[1, 258]);
assert_eq!(
s.as_bytes(),
Some(
&[
2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 2, 1, ][..]
)
);
}
#[test]
fn an_ascending_fill_never_moves_anything() {
let mut r = Run::new();
for i in 0..100i64 {
assert_eq!(r.search(i), Err(i as usize), "{i} appends");
r.add(i, false);
}
assert_eq!(r.len(), 100);
}
#[test]
fn a_set_splits_at_the_ceiling_and_the_client_cannot_tell() {
let mut s = Intset::new();
for i in 0..RUN_MAX as i64 {
s.add(i);
}
assert_eq!(s.runs(), 1, "at the ceiling it is still one array");
assert!(s.as_bytes().is_some());
s.add(RUN_MAX as i64);
assert_eq!(s.runs(), 2, "one past it splits");
assert_eq!(s.as_bytes(), None, "and there is no single blob any more");
assert_eq!(s.len(), RUN_MAX + 1);
assert_eq!(
members(&s),
(0..=RUN_MAX as i64).collect::<Vec<_>>(),
"and every member is still there in order"
);
sound(&s);
}
#[test]
fn a_scattered_fill_past_the_ceiling_stays_sorted_and_whole() {
let (n, least) = if cfg!(miri) {
(4_000i64, 6)
} else {
(20_000, 30)
};
let mut s = Intset::new();
for i in 0..n {
assert!(s.add((i * 7919) % n), "{i}");
}
assert_eq!(s.len(), n as usize);
assert!(s.runs() > least, "it really did split, {} runs", s.runs());
sound(&s);
for i in 0..n {
assert!(s.contains(i), "{i} is a member");
assert_eq!(s.at(i as usize), i, "position {i}");
}
assert!(!s.contains(n));
assert!(!s.contains(-1));
}
#[test]
fn draining_a_split_set_folds_the_runs_back_together() {
let (n, least) = if cfg!(miri) {
(1_600i64, 2)
} else {
(5_000, 5)
};
let mut s = Intset::new();
for i in 0..n {
s.add(i);
}
let split = s.runs();
assert!(split > least, "{split} runs to start with");
for i in (0..n).map(|i| (i * 7919) % n) {
assert!(s.remove(i), "{i}");
}
assert!(s.is_empty());
assert_eq!(s.runs(), 1, "back to one run, not {} empty ones", s.runs());
sound(&s);
assert!(s.add(1));
assert_eq!(members(&s), [1]);
}
#[test]
fn adds_and_removes_in_any_order_leave_the_runs_sound() {
use std::collections::BTreeSet;
let mut s = Intset::new();
let mut want = BTreeSet::new();
let (steps, space) = if cfg!(miri) {
(4_500, 1_500)
} else {
(12_000, 4_000)
};
let mut x = 12_345i64;
for step in 0..steps {
x = x.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let v = (x >> 33) % space;
if step % 3 == 2 {
assert_eq!(s.remove(v), want.remove(&v), "removing {v} at {step}");
} else {
assert_eq!(s.add(v), want.insert(v), "adding {v} at {step}");
}
assert_eq!(s.len(), want.len(), "at {step}");
}
sound(&s);
assert_eq!(members(&s), want.iter().copied().collect::<Vec<_>>());
}
#[test]
fn a_run_only_widens_the_members_it_holds() {
let (n, least) = if cfg!(miri) {
(2_000i64, 2)
} else {
(5_000, 5)
};
let mut s = Intset::new();
for i in 0..n {
s.add(i);
}
s.add(i64::MAX);
assert_eq!(s.width(), 8, "the widest run is eight");
let narrow = s.runs.iter().filter(|r| r.width() == 2).count();
assert!(narrow > least, "only {narrow} runs stayed narrow");
assert_eq!(s.max(), Some(i64::MAX));
sound(&s);
}
#[test]
#[cfg_attr(
miri,
ignore = "a cost spread over a population is a claim about the population"
)]
fn a_run_never_holds_much_more_than_it_uses() {
let mut s = Intset::new();
for i in 0..100_000i64 {
s.add(i);
}
let per = s.memory_bytes() as f64 / s.len() as f64;
assert!(per < 4.6, "{per:.2} bytes a member");
}
#[test]
fn the_member_at_a_position_is_the_same_one_a_walk_would_reach() {
let n: usize = if cfg!(miri) { 1_500 } else { 3_000 };
let mut s = Intset::new();
for i in 0..n as i64 {
s.add(i * 3);
}
let walked: Vec<i64> = s.iter().collect();
assert_eq!(walked.len(), n);
for (i, &v) in walked.iter().enumerate() {
assert_eq!(s.at(i), v, "position {i}");
}
assert_eq!(s.get(n), None, "past the end");
}
}