pub const K_NO_KEY: usize = usize::MAX;
pub trait CompactSetKey: Copy + Ord {
const NO_KEY: Self;
fn span(min: Self, max: Self) -> Option<usize>;
}
macro_rules! impl_compact_set_key {
($($ty:ty => $no_key:expr),* $(,)?) => {
$(
impl CompactSetKey for $ty {
const NO_KEY: Self = $no_key;
#[inline(always)]
fn span(min: Self, max: Self) -> Option<usize> {
let width = (max as i128) - (min as i128) + 1;
usize::try_from(width).ok()
}
}
)*
};
}
impl_compact_set_key! {
usize => usize::MAX,
u32 => u32::MAX,
u64 => u64::MAX,
i32 => -1,
i64 => -1,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompactSet<K: CompactSetKey = usize> {
keys: Vec<K>,
min_key: K,
max_key: K,
}
impl<K: CompactSetKey> Default for CompactSet<K> {
fn default() -> Self {
Self::new()
}
}
impl<K: CompactSetKey> CompactSet<K> {
#[inline]
pub fn new() -> Self {
Self {
keys: Vec::new(),
min_key: K::NO_KEY,
max_key: K::NO_KEY,
}
}
#[inline]
pub fn insert(&mut self, key: K) {
if let Err(index) = self.keys.binary_search(&key) {
self.keys.insert(index, key);
}
if self.min_key == K::NO_KEY || key < self.min_key {
self.min_key = key;
}
if self.max_key == K::NO_KEY || key > self.max_key {
self.max_key = key;
}
}
#[inline]
pub fn erase(&mut self, key: K) {
let Ok(index) = self.keys.binary_search(&key) else {
return;
};
self.keys.remove(index);
if self.keys.is_empty() {
self.min_key = K::NO_KEY;
self.max_key = K::NO_KEY;
} else if key == self.min_key {
self.min_key = self.keys[0];
} else if key == self.max_key {
self.max_key = self.keys[self.keys.len() - 1];
}
}
#[inline]
pub fn clear(&mut self) {
self.keys.clear();
self.min_key = K::NO_KEY;
self.max_key = K::NO_KEY;
}
#[inline]
pub fn is_member(&self, key: K) -> bool {
if self.min_key == K::NO_KEY || key < self.min_key || key > self.max_key {
false
} else if K::span(self.min_key, self.max_key) == Some(self.keys.len()) {
true
} else {
self.keys.binary_search(&key).is_ok()
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.keys.len()
}
#[inline]
pub fn lower_bound(&self) -> K {
self.min_key
}
#[inline]
pub fn upper_bound(&self) -> K {
self.max_key
}
#[inline]
pub fn iter(&self) -> std::slice::Iter<'_, K> {
self.keys.iter()
}
#[inline]
pub fn as_slice(&self) -> &[K] {
&self.keys
}
}
impl<'a, K: CompactSetKey> IntoIterator for &'a CompactSet<K> {
type Item = &'a K;
type IntoIter = std::slice::Iter<'a, K>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<K: CompactSetKey> FromIterator<K> for CompactSet<K> {
fn from_iter<I: IntoIterator<Item = K>>(iter: I) -> Self {
let mut set = Self::new();
for key in iter {
set.insert(key);
}
set
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn membership_and_bounds_track_the_contents() {
let mut set = CompactSet::new();
assert!(set.is_empty());
assert_eq!(set.lower_bound(), K_NO_KEY);
assert_eq!(set.upper_bound(), K_NO_KEY);
assert!(!set.is_member(0));
for key in [5, 7, 6] {
set.insert(key);
}
assert_eq!(set.len(), 3);
assert_eq!(set.lower_bound(), 5);
assert_eq!(set.upper_bound(), 7);
for key in 5..=7 {
assert!(set.is_member(key));
}
assert!(!set.is_member(4));
assert!(!set.is_member(8));
}
#[test]
fn inserting_a_key_twice_changes_nothing() {
let mut set = CompactSet::new();
set.insert(3);
set.insert(3);
assert_eq!(set.len(), 1);
assert_eq!(set.as_slice(), &[3]);
}
#[test]
fn iteration_is_sorted_regardless_of_insertion_order() {
let set: CompactSet = [9, 1, 5, 3, 7].into_iter().collect();
assert_eq!(set.iter().copied().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9]);
let reversed: CompactSet = [7, 3, 5, 1, 9].into_iter().collect();
assert_eq!(set, reversed, "order of insertion must not be observable");
}
#[test]
fn the_dense_fast_path_agrees_with_a_real_lookup() {
let mut set: CompactSet = (10..20).collect();
assert_eq!(set.upper_bound() - set.lower_bound() + 1, set.len());
for key in 0..30 {
assert_eq!(set.is_member(key), (10..20).contains(&key), "dense, {key}");
}
set.erase(15);
for key in 0..30 {
let expected = (10..20).contains(&key) && key != 15;
assert_eq!(set.is_member(key), expected, "sparse, {key}");
}
}
#[test]
fn erasing_an_endpoint_tightens_the_bound() {
let mut set: CompactSet = (1..=3).collect();
set.erase(2);
assert_eq!(set.lower_bound(), 1);
assert_eq!(set.upper_bound(), 3);
assert!(!set.is_member(2));
set.erase(1);
assert_eq!(set.lower_bound(), 3, "3 is the only key left");
assert_eq!(set.upper_bound(), 3);
assert!(set.is_member(3));
assert!(!set.is_member(1));
}
#[test]
fn signed_keys_work_and_exclude_the_sentinel() {
let mut set: CompactSet<i32> = CompactSet::new();
assert_eq!(set.lower_bound(), <i32 as CompactSetKey>::NO_KEY);
for key in [-5, 0, 3] {
set.insert(key);
}
assert_eq!(set.as_slice(), &[-5, 0, 3]);
assert!(set.is_member(-5));
assert!(set.is_member(0));
assert!(!set.is_member(-4));
assert_eq!(set.lower_bound(), -5);
assert_eq!(set.upper_bound(), 3);
}
#[test]
fn erasing_the_last_key_resets_the_bounds() {
let mut set = CompactSet::new();
set.insert(4);
set.erase(4);
assert!(set.is_empty());
assert_eq!(set.lower_bound(), K_NO_KEY);
assert_eq!(set.upper_bound(), K_NO_KEY);
assert!(!set.is_member(4));
}
#[test]
fn erasing_an_absent_key_leaves_the_set_alone() {
let mut set: CompactSet = [2, 4].into_iter().collect();
set.erase(3);
set.erase(100);
assert_eq!(set.as_slice(), &[2, 4]);
assert_eq!(set.lower_bound(), 2);
assert_eq!(set.upper_bound(), 4);
}
#[test]
fn erasing_zero_does_not_underflow_the_upper_bound() {
let mut set = CompactSet::new();
set.insert(0);
set.erase(0);
assert_eq!(set.upper_bound(), K_NO_KEY);
}
#[test]
fn clearing_resets_everything() {
let mut set: CompactSet = (0..5).collect();
set.clear();
assert!(set.is_empty());
assert_eq!(set.lower_bound(), K_NO_KEY);
assert!(!set.is_member(0));
set.insert(9);
assert_eq!(set.lower_bound(), 9);
}
#[test]
fn matches_a_reference_set_under_random_operations() {
use std::collections::BTreeSet;
let mut state = 0x5DEE_CE66_D1E5_1234u64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let mut set = CompactSet::new();
let mut reference = BTreeSet::new();
for _ in 0..2000 {
let key = (next() % 64) as usize;
if next() % 3 == 0 {
set.erase(key);
reference.remove(&key);
} else {
set.insert(key);
reference.insert(key);
}
assert_eq!(set.len(), reference.len());
assert_eq!(
set.iter().copied().collect::<Vec<_>>(),
reference.iter().copied().collect::<Vec<_>>()
);
for probe in 0..70usize {
assert_eq!(
set.is_member(probe),
reference.contains(&probe),
"probe {probe}"
);
}
if let Some(&min) = reference.iter().next() {
assert!(set.lower_bound() <= min, "lower bound must stay valid");
assert!(
set.upper_bound() >= *reference.iter().next_back().unwrap(),
"upper bound must stay valid"
);
}
}
}
}