#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::missing_panics_doc,
clippy::items_after_statements
)]
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
mod iter;
mod ops;
mod serialize;
pub use iter::Iter;
pub use serialize::{DecodeError, EncodeError};
const BITS_PER_WORD: u64 = 64;
const WORDS_PER_CHUNK: usize = 32;
const CHUNK_BITS: u64 = BITS_PER_WORD * WORDS_PER_CHUNK as u64;
#[inline]
const fn window_base(idx: u64) -> u64 {
idx & !(CHUNK_BITS - 1)
}
#[inline]
const fn window_offset(idx: u64) -> usize {
(idx & (CHUNK_BITS - 1)) as usize
}
#[derive(Clone, PartialEq, Eq, Hash)]
enum Chunk {
Run(u32),
Dense(Box<[u64; WORDS_PER_CHUNK]>),
}
impl Chunk {
#[inline]
fn count(&self) -> u64 {
match self {
Chunk::Run(n) => u64::from(*n) * CHUNK_BITS,
Chunk::Dense(w) => w.iter().map(|x| u64::from(x.count_ones())).sum(),
}
}
}
#[derive(Clone, Default, PartialEq, Eq, Hash)]
pub struct SparseMap {
pub(crate) chunks: BTreeMap<u64, Chunk>,
}
impl SparseMap {
#[must_use]
pub const fn new() -> Self {
SparseMap {
chunks: BTreeMap::new(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.chunks.is_empty()
}
pub fn clear(&mut self) {
self.chunks.clear();
}
#[must_use]
pub fn cardinality(&self) -> u64 {
self.chunks.values().map(Chunk::count).sum()
}
#[must_use]
pub fn contains(&self, idx: u64) -> bool {
if let Some((&base, chunk)) = self.chunks.range(..=idx).next_back() {
match chunk {
Chunk::Run(n) => idx < base + u64::from(*n) * CHUNK_BITS,
Chunk::Dense(w) => {
base == window_base(idx) && {
let o = window_offset(idx);
w[o / 64] & (1u64 << (o % 64)) != 0
}
}
}
} else {
false
}
}
pub fn insert(&mut self, idx: u64) -> bool {
let base = window_base(idx);
if let Some((&rbase, Chunk::Run(n))) = self.chunks.range(..=idx).next_back() {
if idx < rbase + u64::from(*n) * CHUNK_BITS {
return false;
}
}
match self.chunks.get_mut(&base) {
Some(Chunk::Run(_)) => false, Some(Chunk::Dense(w)) => {
let o = window_offset(idx);
let mask = 1u64 << (o % 64);
if w[o / 64] & mask != 0 {
return false;
}
w[o / 64] |= mask;
if w.iter().all(|x| *x == u64::MAX) {
self.chunks.insert(base, Chunk::Run(1));
self.coalesce_runs(base);
}
true
}
None => {
let mut w = Box::new([0u64; WORDS_PER_CHUNK]);
let o = window_offset(idx);
w[o / 64] |= 1u64 << (o % 64);
self.chunks.insert(base, Chunk::Dense(w));
true
}
}
}
pub fn remove(&mut self, idx: u64) -> bool {
let base = window_base(idx);
if let Some((&rbase, Chunk::Run(n))) = self.chunks.range(..=idx).next_back() {
let n = u64::from(*n);
if idx < rbase + n * CHUNK_BITS {
self.split_run(rbase, n, base, idx);
return true;
}
}
match self.chunks.get_mut(&base) {
Some(Chunk::Dense(w)) => {
let o = window_offset(idx);
let mask = 1u64 << (o % 64);
if w[o / 64] & mask == 0 {
return false;
}
w[o / 64] &= !mask;
if w.iter().all(|x| *x == 0) {
self.chunks.remove(&base);
}
true
}
_ => false,
}
}
fn split_run(&mut self, rbase: u64, n: u64, wbase: u64, idx: u64) {
self.chunks.remove(&rbase);
let lead = (wbase - rbase) / CHUNK_BITS;
if lead > 0 {
self.chunks.insert(rbase, Chunk::Run(lead as u32));
}
let mut w = Box::new([u64::MAX; WORDS_PER_CHUNK]);
let o = window_offset(idx);
w[o / 64] &= !(1u64 << (o % 64));
self.chunks.insert(wbase, Chunk::Dense(w));
let trail = n - lead - 1;
if trail > 0 {
self.chunks
.insert(wbase + CHUNK_BITS, Chunk::Run(trail as u32));
}
}
fn coalesce_runs(&mut self, base: u64) {
let this_count = match self.chunks.get(&base) {
Some(Chunk::Run(n)) => u64::from(*n),
_ => return,
};
let next_base = base + this_count * CHUNK_BITS;
if let Some(Chunk::Run(nn)) = self.chunks.get(&next_base) {
let merged = this_count + u64::from(*nn);
self.chunks.remove(&next_base);
self.chunks.insert(base, Chunk::Run(merged as u32));
}
if let Some((&pbase, Chunk::Run(pn))) = self.chunks.range(..base).next_back() {
let pn = u64::from(*pn);
if pbase + pn * CHUNK_BITS == base {
let this_count = match self.chunks.get(&base) {
Some(Chunk::Run(n)) => u64::from(*n),
_ => return,
};
self.chunks.remove(&base);
self.chunks
.insert(pbase, Chunk::Run((pn + this_count) as u32));
}
}
}
#[must_use]
pub fn min(&self) -> Option<u64> {
let (&base, chunk) = self.chunks.iter().next()?;
Some(match chunk {
Chunk::Run(_) => base,
Chunk::Dense(w) => base + dense_first_set(w).expect("dense never empty"),
})
}
#[must_use]
pub fn max(&self) -> Option<u64> {
let (&base, chunk) = self.chunks.iter().next_back()?;
Some(match chunk {
Chunk::Run(n) => base + u64::from(*n) * CHUNK_BITS - 1,
Chunk::Dense(w) => base + dense_last_set(w).expect("dense never empty"),
})
}
#[must_use]
pub fn rank(&self, idx: u64) -> u64 {
let mut total = 0;
for (&base, chunk) in &self.chunks {
if base >= idx {
break;
}
match chunk {
Chunk::Run(n) => {
let end = base + u64::from(*n) * CHUNK_BITS;
total += if idx >= end { end - base } else { idx - base };
}
Chunk::Dense(w) => {
if idx >= base + CHUNK_BITS {
total += chunk.count();
} else {
total += dense_rank(w, (idx - base) as usize);
}
}
}
}
total
}
#[must_use]
pub fn select(&self, mut n: u64) -> Option<u64> {
for (&base, chunk) in &self.chunks {
let c = chunk.count();
if n < c {
return Some(match chunk {
Chunk::Run(_) => base + n,
Chunk::Dense(w) => base + dense_select(w, n).expect("n < count"),
});
}
n -= c;
}
None
}
pub fn insert_range(&mut self, start: u64, end: u64) {
let mut i = start;
while i < end {
let base = window_base(i);
if i == base && end - i >= CHUNK_BITS && self.window_is_empty(base) {
self.chunks.insert(base, Chunk::Run(1));
self.coalesce_runs(base);
i += CHUNK_BITS;
} else {
self.insert(i);
i += 1;
}
}
}
fn window_is_empty(&self, base: u64) -> bool {
match self.chunks.range(..=base).next_back() {
Some((&rb, Chunk::Run(n))) => base >= rb + u64::from(*n) * CHUNK_BITS,
Some((&b2, Chunk::Dense(_))) => b2 != base,
None => true,
}
}
pub fn remove_range(&mut self, start: u64, end: u64) {
let mut i = start;
while i < end {
i += 1;
self.remove(i - 1);
}
}
#[must_use]
pub fn span(&self, start: u64, len: u64, value: bool) -> Option<u64> {
if len == 0 {
return Some(start);
}
let mut run_start = start;
let mut i = start;
loop {
if self.contains(i) == value {
if i - run_start + 1 >= len {
return Some(run_start);
}
} else {
run_start = i + 1;
}
if i == u64::MAX {
return None;
}
i += 1;
if !value {
if let Some(mx) = self.max() {
if run_start > mx + 1 && run_start.saturating_sub(start) >= len {
return Some(run_start);
}
if i > mx + len + 1 {
return Some(run_start.max(start));
}
} else {
return Some(start);
}
}
}
}
}
fn dense_first_set(w: &[u64; WORDS_PER_CHUNK]) -> Option<u64> {
for (i, &word) in w.iter().enumerate() {
if word != 0 {
return Some(i as u64 * BITS_PER_WORD + u64::from(word.trailing_zeros()));
}
}
None
}
fn dense_last_set(w: &[u64; WORDS_PER_CHUNK]) -> Option<u64> {
for (i, &word) in w.iter().enumerate().rev() {
if word != 0 {
return Some(
i as u64 * BITS_PER_WORD + (BITS_PER_WORD - 1 - u64::from(word.leading_zeros())),
);
}
}
None
}
fn dense_rank(w: &[u64; WORDS_PER_CHUNK], bit: usize) -> u64 {
let word = bit / 64;
let mut total = 0u64;
for &x in &w[..word] {
total += u64::from(x.count_ones());
}
let rem = bit % 64;
if rem != 0 {
total += u64::from((w[word] & ((1u64 << rem) - 1)).count_ones());
}
total
}
fn dense_select(w: &[u64; WORDS_PER_CHUNK], mut n: u64) -> Option<u64> {
for (i, &word) in w.iter().enumerate() {
let c = u64::from(word.count_ones());
if n < c {
let mut word = word;
for _ in 0..n {
word &= word - 1;
}
return Some(i as u64 * BITS_PER_WORD + u64::from(word.trailing_zeros()));
}
n -= c;
}
None
}
#[cfg(test)]
mod tests;