use std::{cmp, fmt};
mod err;
pub use err::Error;
const NUM_WORD_BITS: usize = usize::BITS as usize;
pub struct CountedBitmap {
bits: Vec<usize>,
length: usize,
remain: usize
}
impl fmt::Debug for CountedBitmap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let blocks: Vec<String> = self
.iter_ones_block()
.map(|(first, last)| {
if first == last {
format!("{first}")
} else {
format!("{first}-{last}")
}
})
.collect();
let set_blocks = blocks.join(",");
write!(
f,
"BMap {{ length: {}, remain: {}, [{}] }}",
self.length, self.remain, set_blocks
)
}
}
impl CountedBitmap {
#[must_use]
pub fn new(bitcount: usize) -> Self {
let align = NUM_WORD_BITS; let num = bitcount;
let len = num.div_ceil(align);
let bits = vec![0; len];
Self {
bits,
length: bitcount,
remain: bitcount
}
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.length
}
pub fn set_len(&mut self, bitcount: usize) {
if bitcount > self.length {
let new_len = bitcount.div_ceil(NUM_WORD_BITS);
if new_len > self.bits.len() {
self.bits.resize(new_len, 0);
}
let added = bitcount - self.length;
self.remain += added;
self.length = bitcount;
} else if bitcount < self.length {
let new_len = bitcount.div_ceil(NUM_WORD_BITS);
for idx in bitcount..self.length {
if !self.is_set_unchecked(idx) {
self.remain -= 1;
}
}
self.bits.truncate(new_len);
if bitcount > 0 {
let last_word_idx = new_len - 1;
let bits_in_last_word = bitcount % NUM_WORD_BITS;
if bits_in_last_word != 0 {
let mask = (1 << bits_in_last_word) - 1;
self.bits[last_word_idx] &= mask;
}
}
self.length = bitcount;
}
}
pub fn set_len_clear(&mut self, bitcount: usize) {
match bitcount.cmp(&self.length) {
cmp::Ordering::Less => {
let new_len = bitcount.div_ceil(NUM_WORD_BITS);
self.bits.truncate(new_len);
self.bits.as_mut_slice().fill(0);
}
cmp::Ordering::Equal => {
self.bits.as_mut_slice().fill(0);
}
cmp::Ordering::Greater => {
self.bits.as_mut_slice().fill(0);
let new_len = bitcount.div_ceil(NUM_WORD_BITS);
self.bits.resize(new_len, 0);
}
}
self.length = bitcount;
self.remain = self.length;
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.remain == self.length
}
#[inline]
#[must_use]
pub const fn remain(&self) -> usize {
self.remain
}
#[must_use]
#[expect(clippy::cast_precision_loss)]
pub fn progress(&self) -> f64 {
if self.is_empty() {
1.0
} else {
let done = (self.length - self.remain) as f64;
done / self.len() as f64
}
}
#[inline]
#[must_use]
pub const fn is_finished(&self) -> bool {
self.remain == 0
}
#[inline]
pub fn is_set(&self, idx: usize) -> Result<bool, Error> {
let (iword, bitval) = self.get_bidx(idx)?;
let v = unsafe { self.bits.get_unchecked(iword) };
Ok(v & bitval != 0)
}
#[inline]
pub fn set(&mut self, idx: usize) -> Result<(), Error> {
let (iword, bitval) = self.get_bidx(idx)?;
let v = unsafe { self.bits.get_unchecked_mut(iword) };
if *v & bitval == 0 {
*v |= bitval;
self.remain -= 1;
}
Ok(())
}
#[inline]
pub unsafe fn set_unchecked(&mut self, idx: usize) {
let (iword, bitval) = Self::get_bidx_unchecked(idx);
let v = unsafe { self.bits.get_unchecked_mut(iword) };
if *v & bitval == 0 {
*v |= bitval;
self.remain -= 1;
}
}
#[inline]
pub fn unset(&mut self, idx: usize) -> Result<(), Error> {
let (iword, bitval) = self.get_bidx(idx)?;
let v = unsafe { self.bits.get_unchecked_mut(iword) };
if *v & bitval != 0 {
*v &= !bitval;
self.remain += 1;
}
Ok(())
}
pub fn clear(&mut self) {
self.bits.as_mut_slice().fill(0);
self.remain = self.length;
}
pub fn cond_set<F>(&mut self, idx: usize, mut f: F) -> Result<bool, Error>
where
F: FnMut() -> bool
{
let (iword, bitval) = self.get_bidx(idx)?;
let v = unsafe { self.bits.get_unchecked_mut(iword) };
if *v & bitval == 0 && f() {
*v |= bitval;
self.remain -= 1;
Ok(true)
} else {
Ok(false)
}
}
}
impl CountedBitmap {
#[must_use]
pub const fn iter(&self) -> BitIter<'_> {
BitIter { bmap: self, idx: 0 }
}
#[must_use]
pub const fn iter_zeroes(&self) -> BitValIter<'_> {
BitValIter {
bmap: self,
idx: 0,
set: false
}
}
#[must_use]
pub const fn iter_ones(&self) -> BitValIter<'_> {
BitValIter {
bmap: self,
idx: 0,
set: true
}
}
#[must_use]
pub const fn iter_zeroes_block(&self) -> BitBlocksIter<'_> {
BitBlocksIter {
bmap: self,
idx: 0,
set: false
}
}
#[must_use]
pub const fn iter_ones_block(&self) -> BitBlocksIter<'_> {
BitBlocksIter {
bmap: self,
idx: 0,
set: true
}
}
}
impl<'a> IntoIterator for &'a CountedBitmap {
type Item = bool;
type IntoIter = BitIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct BitIter<'a> {
bmap: &'a CountedBitmap,
idx: usize
}
impl Iterator for BitIter<'_> {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.bmap.len() {
let val = self.bmap.is_set_unchecked(self.idx);
self.idx += 1;
Some(val)
} else {
None
}
}
}
impl ExactSizeIterator for BitIter<'_> {
fn len(&self) -> usize {
self.bmap.len()
}
}
pub struct BitValIter<'a> {
bmap: &'a CountedBitmap,
idx: usize,
set: bool
}
impl Iterator for BitValIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
while self.idx < self.bmap.length {
let val = self.bmap.is_set_unchecked(self.idx);
if self.set == val {
let idx = self.idx;
self.idx += 1;
return Some(idx);
}
self.idx += 1;
}
None
}
}
impl ExactSizeIterator for BitValIter<'_> {
fn len(&self) -> usize {
if self.set {
self.bmap.length - self.bmap.remain
} else {
self.bmap.remain
}
}
}
pub struct BitBlocksIter<'a> {
bmap: &'a CountedBitmap,
idx: usize,
set: bool
}
impl Iterator for BitBlocksIter<'_> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
while self.idx < self.bmap.length {
let val = self.bmap.is_set_unchecked(self.idx);
if self.set != val {
self.idx += 1;
continue;
}
let low = self.idx;
self.idx += 1;
let high = loop {
if self.idx < self.bmap.length {
let val = self.bmap.is_set_unchecked(self.idx);
if self.set == val {
self.idx += 1;
continue;
}
let idx = self.idx - 1;
self.idx += 1;
break idx;
}
break self.idx - 1;
};
return Some((low, high));
}
None
}
}
impl CountedBitmap {
#[allow(dead_code)]
fn word_count(&self) -> usize {
self.bits.len()
}
#[inline]
const fn get_bidx(&self, idx: usize) -> Result<(usize, usize), Error> {
if idx >= self.length {
return Err(Error::OutOfBounds);
}
let t = Self::get_bidx_unchecked(idx);
Ok(t)
}
#[inline]
const fn get_bidx_unchecked(idx: usize) -> (usize, usize) {
let iword = idx / NUM_WORD_BITS;
let ibit = idx % NUM_WORD_BITS;
let bitval = 1 << ibit;
(iword, bitval)
}
#[inline]
fn is_set_unchecked(&self, idx: usize) -> bool {
let (iword, bitval) = Self::get_bidx_unchecked(idx);
let v = unsafe { self.bits.get_unchecked(iword) };
v & bitval != 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size() {
let bmap = CountedBitmap::new(0);
assert_eq!(bmap.len(), 0);
assert_eq!(bmap.word_count(), 0);
let bmap = CountedBitmap::new(1);
assert_eq!(bmap.len(), 1);
assert_eq!(bmap.word_count(), 1);
let idx = NUM_WORD_BITS - 1;
let bmap = CountedBitmap::new(idx);
assert_eq!(bmap.len(), idx);
assert_eq!(bmap.word_count(), 1);
let bmap = CountedBitmap::new(NUM_WORD_BITS);
assert_eq!(bmap.len(), NUM_WORD_BITS);
assert_eq!(bmap.word_count(), 1);
let idx = NUM_WORD_BITS + 1;
let bmap = CountedBitmap::new(idx);
assert_eq!(bmap.len(), idx);
assert_eq!(bmap.word_count(), 2);
}
#[test]
fn finished() {
let bmap = CountedBitmap::new(0);
assert!(bmap.is_finished());
let mut bmap = CountedBitmap::new(1);
assert!(!bmap.is_finished());
bmap.set(0).unwrap();
assert!(bmap.is_finished());
}
#[test]
fn dbg_output0() {
let bmap = CountedBitmap::new(3);
let s = format!("{bmap:?}");
assert_eq!(s, "BMap { length: 3, remain: 3, [] }");
}
#[test]
fn dbg_output1() {
let mut bmap = CountedBitmap::new(3);
bmap.set(1).unwrap();
let s = format!("{bmap:?}");
assert_eq!(s, "BMap { length: 3, remain: 2, [1] }");
}
#[test]
fn dbg_output2() {
let mut bmap = CountedBitmap::new(5);
bmap.set(1).unwrap();
bmap.set(2).unwrap();
let s = format!("{bmap:?}");
assert_eq!(s, "BMap { length: 5, remain: 3, [1-2] }");
}
#[test]
fn dbg_output3() {
let mut bmap = CountedBitmap::new(7);
bmap.set(1).unwrap();
bmap.set(2).unwrap();
bmap.set(4).unwrap();
bmap.set(5).unwrap();
let s = format!("{bmap:?}");
assert_eq!(s, "BMap { length: 7, remain: 3, [1-2,4-5] }");
}
}