use crate::error::{BloomCraftError, Result};
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug)]
pub struct BitVec {
blocks: Box<[AtomicU64]>,
len: usize,
}
#[inline]
fn word_mask(lo: usize, hi: usize) -> u64 {
debug_assert!(lo < hi && hi <= 64, "word_mask: invalid range [{lo},{hi})");
let count = hi - lo;
if count == 64 {
u64::MAX
} else {
((1u64 << count) - 1) << lo
}
}
impl BitVec {
pub fn new(num_bits: usize) -> Result<Self> {
if num_bits == 0 {
return Err(BloomCraftError::invalid_parameters(
"BitVec size must be greater than 0",
));
}
let num_blocks = num_bits.div_ceil(64);
let blocks = (0..num_blocks)
.map(|_| AtomicU64::new(0))
.collect::<Vec<_>>()
.into_boxed_slice();
Ok(Self {
blocks,
len: num_bits,
})
}
#[must_use]
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn set(&self, index: usize) {
assert!(
index < self.len,
"BitVec index out of bounds: index={} len={}",
index,
self.len
);
let block_idx = index / 64;
let bit_offset = index % 64;
let mask = 1u64 << bit_offset;
self.blocks[block_idx].fetch_or(mask, Ordering::Release);
}
#[must_use]
#[inline]
pub fn get(&self, index: usize) -> bool {
assert!(
index < self.len,
"BitVec index out of bounds: index={} len={}",
index,
self.len
);
let block_idx = index / 64;
let bit_offset = index % 64;
let mask = 1u64 << bit_offset;
(self.blocks[block_idx].load(Ordering::Acquire) & mask) != 0
}
pub fn set_range(&self, start: usize, end: usize, value: bool) {
if start > end {
panic!(
"set_range: invalid range [{}..{}) - start must be <= end",
start, end
);
}
if end > self.len {
panic!(
"set_range: end index {} out of bounds (length {})",
end, self.len
);
}
if start == end {
return;
}
let start_word = start / 64;
let end_word = (end - 1) / 64;
for word_idx in start_word..=end_word {
let bit_lo = if word_idx == start_word { start % 64 } else { 0 };
let bit_hi = if word_idx == end_word { (end - 1) % 64 + 1 } else { 64 };
let mask = word_mask(bit_lo, bit_hi);
if value {
self.blocks[word_idx].fetch_or(mask, Ordering::Release);
} else {
self.blocks[word_idx].fetch_and(!mask, Ordering::Release);
}
}
}
#[inline]
pub fn clear_bit(&self, index: usize) {
assert!(
index < self.len,
"BitVec index out of bounds: index={} len={}",
index,
self.len
);
let block_idx = index / 64;
let bit_offset = index % 64;
let mask = !(1u64 << bit_offset);
self.blocks[block_idx].fetch_and(mask, Ordering::Release);
}
#[must_use]
pub fn get_range(&self, start: usize, end: usize) -> Vec<bool> {
if start > end {
panic!(
"get_range: invalid range [{}..{}) - start must be <= end",
start, end
);
}
if end > self.len {
panic!(
"get_range: end index {} out of bounds (length {})",
end, self.len
);
}
(start..end).map(|i| self.get(i)).collect()
}
pub fn clear(&mut self) {
for block in &*self.blocks {
block.store(0, Ordering::Relaxed);
}
}
#[must_use]
pub fn count_ones(&self) -> usize {
self.blocks
.iter()
.map(|block| block.load(Ordering::Relaxed).count_ones() as usize)
.sum()
}
#[must_use]
pub fn memory_usage(&self) -> usize {
self.blocks.len() * std::mem::size_of::<AtomicU64>() + std::mem::size_of::<Self>()
}
pub fn union(&self, other: &Self) -> Result<Self> {
if self.len != other.len {
return Err(BloomCraftError::incompatible_filters(format!(
"BitVec size mismatch: {} vs {}",
self.len, other.len
)));
}
let result = Self::new(self.len)?;
for (i, (a, b)) in self.blocks.iter().zip(&*other.blocks).enumerate() {
let val = a.load(Ordering::Relaxed) | b.load(Ordering::Relaxed);
result.blocks[i].store(val, Ordering::Relaxed);
}
Ok(result)
}
pub fn intersect(&self, other: &Self) -> Result<Self> {
if self.len != other.len {
return Err(BloomCraftError::incompatible_filters(format!(
"BitVec size mismatch: {} vs {}",
self.len, other.len
)));
}
let result = Self::new(self.len)?;
for (i, (a, b)) in self.blocks.iter().zip(&*other.blocks).enumerate() {
let val = a.load(Ordering::Relaxed) & b.load(Ordering::Relaxed);
result.blocks[i].store(val, Ordering::Relaxed);
}
Ok(result)
}
pub fn from_raw(raw: Vec<u64>, len: usize) -> crate::error::Result<Self> {
if raw.is_empty() {
return Err(BloomCraftError::invalid_parameters(
"raw bit vector cannot be empty".to_string(),
));
}
if len == 0 {
return Err(BloomCraftError::invalid_parameters(
"BitVec length must be greater than 0".to_string(),
));
}
let required_blocks = len.div_ceil(64);
if raw.len() < required_blocks {
return Err(BloomCraftError::invalid_parameters(format!(
"Insufficient blocks: need {} for {} bits, got {}",
required_blocks, len, raw.len()
)));
}
let blocks: Box<[AtomicU64]> = raw
.into_iter()
.map(AtomicU64::new)
.collect::<Vec<_>>()
.into_boxed_slice();
if len % 64 != 0 {
let valid_bits = len % 64;
let mask = (1u64 << valid_bits) - 1;
blocks[required_blocks - 1].fetch_and(mask, Ordering::Relaxed);
}
Ok(Self { blocks, len })
}
#[must_use]
pub fn to_raw(&self) -> Vec<u64> {
self.blocks
.iter()
.map(|block| block.load(Ordering::Relaxed))
.collect()
}
#[must_use]
#[inline]
#[allow(dead_code)]
pub(crate) fn num_blocks(&self) -> usize {
self.blocks.len()
}
#[inline]
#[allow(dead_code)]
pub(crate) fn block_count(&self) -> usize {
self.blocks.len()
}
#[inline]
#[allow(dead_code)]
pub(crate) fn clear_block_atomic(&self, index: usize) {
assert!(
index < self.blocks.len(),
"BitVec::clear_block_atomic: word index {} out of bounds (num_blocks={})",
index,
self.blocks.len()
);
self.blocks[index].store(0, Ordering::Release);
}
#[inline]
#[allow(dead_code)]
pub(crate) fn prefetch(&self, index: usize) {
if index >= self.len() {
return;
}
let block_idx = index / 64;
if block_idx >= self.blocks.len() {
return;
}
#[cfg(target_arch = "x86_64")]
{
unsafe {
use std::arch::x86_64::{_mm_prefetch, _MM_HINT_T0};
let ptr = &self.blocks[block_idx] as *const _ as *const i8;
_mm_prefetch(ptr, _MM_HINT_T0);
}
}
#[cfg(not(target_arch = "x86_64"))]
{
let _ = self.blocks[block_idx].load(Ordering::Relaxed);
}
}
pub fn union_inplace(&self, other: &Self) -> Result<()> {
if self.len() != other.len() {
return Err(BloomCraftError::IncompatibleFilters {
reason: format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
),
});
}
for (i, block) in self.blocks.iter().enumerate() {
let other_val = other.blocks[i].load(Ordering::Relaxed);
block.fetch_or(other_val, Ordering::Release);
}
Ok(())
}
pub fn intersect_inplace(&self, other: &Self) -> Result<()> {
if self.len() != other.len() {
return Err(BloomCraftError::IncompatibleFilters {
reason: format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
),
});
}
for (i, block) in self.blocks.iter().enumerate() {
let other_val = other.blocks[i].load(Ordering::Relaxed);
block.fetch_and(other_val, Ordering::Release);
}
Ok(())
}
pub fn xor(&self, other: &Self) -> Result<Self> {
if self.len() != other.len() {
return Err(BloomCraftError::IncompatibleFilters {
reason: format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
),
});
}
let result = self.clone();
for (i, block) in result.blocks.iter().enumerate() {
let other_val = other.blocks[i].load(Ordering::Relaxed);
let current = block.load(Ordering::Relaxed);
block.store(current ^ other_val, Ordering::Relaxed);
}
Ok(result)
}
#[must_use]
pub fn fill_rate(&self) -> f64 {
self.count_ones() as f64 / self.len() as f64
}
pub fn is_subset_of(&self, other: &Self) -> Result<bool> {
if self.len() != other.len() {
return Err(BloomCraftError::incompatible_filters(format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
)));
}
for (a, b) in self.blocks.iter().zip(other.blocks.iter()) {
let av = a.load(Ordering::Relaxed);
let bv = b.load(Ordering::Relaxed);
if av & !bv != 0 {
return Ok(false);
}
}
Ok(true)
}
pub fn is_disjoint(&self, other: &Self) -> Result<bool> {
if self.len() != other.len() {
return Err(BloomCraftError::incompatible_filters(format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
)));
}
for (a, b) in self.blocks.iter().zip(other.blocks.iter()) {
if a.load(Ordering::Relaxed) & b.load(Ordering::Relaxed) != 0 {
return Ok(false);
}
}
Ok(true)
}
pub fn jaccard_similarity(&self, other: &Self) -> Result<f64> {
if self.len() != other.len() {
return Err(BloomCraftError::incompatible_filters(format!(
"BitVec size mismatch: {} vs {}",
self.len(),
other.len()
)));
}
let mut intersection: u64 = 0;
let mut union: u64 = 0;
for (a, b) in self.blocks.iter().zip(other.blocks.iter()) {
let av = a.load(Ordering::Relaxed);
let bv = b.load(Ordering::Relaxed);
intersection += (av & bv).count_ones() as u64;
union += (av | bv).count_ones() as u64;
}
if union == 0 {
return Ok(1.0);
}
Ok(intersection as f64 / union as f64)
}
}
impl Clone for BitVec {
fn clone(&self) -> Self {
let blocks = self
.blocks
.iter()
.map(|b| AtomicU64::new(b.load(Ordering::Relaxed)))
.collect();
Self {
blocks,
len: self.len,
}
}
}
impl PartialEq for BitVec {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
self.blocks
.iter()
.zip(other.blocks.iter())
.all(|(a, b)| a.load(Ordering::Relaxed) == b.load(Ordering::Relaxed))
}
}
impl Eq for BitVec {}
#[cfg(feature = "serde")]
impl Serialize for BitVec {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::SerializeStruct;
let blocks_data: Vec<u64> = self
.blocks
.iter()
.map(|b| b.load(Ordering::Relaxed))
.collect();
let mut state = serializer.serialize_struct("BitVec", 2)?;
state.serialize_field("blocks", &blocks_data)?;
state.serialize_field("len", &self.len)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for BitVec {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
use std::fmt;
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
Blocks,
Len,
}
struct BitVecVisitor;
impl<'de> Visitor<'de> for BitVecVisitor {
type Value = BitVec;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct BitVec with fields 'blocks' and 'len'")
}
fn visit_map<V>(self, mut map: V) -> std::result::Result<BitVec, V::Error>
where
V: MapAccess<'de>,
{
let mut blocks_data: Option<Vec<u64>> = None;
let mut len: Option<usize> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Blocks => {
if blocks_data.is_some() {
return Err(de::Error::duplicate_field("blocks"));
}
blocks_data = Some(map.next_value()?);
}
Field::Len => {
if len.is_some() {
return Err(de::Error::duplicate_field("len"));
}
len = Some(map.next_value()?);
}
}
}
let blocks_data = blocks_data
.ok_or_else(|| de::Error::missing_field("blocks"))?;
let len = len.ok_or_else(|| de::Error::missing_field("len"))?;
let blocks = blocks_data
.into_iter()
.map(AtomicU64::new)
.collect::<Vec<_>>()
.into_boxed_slice();
if len % 64 != 0 {
let valid_bits = len % 64;
let mask = (1u64 << valid_bits) - 1;
blocks[blocks.len() - 1].fetch_and(mask, Ordering::Relaxed);
}
Ok(BitVec { blocks, len })
}
}
const FIELDS: &[&str] = &["blocks", "len"];
deserializer.deserialize_struct("BitVec", FIELDS, BitVecVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let bv = BitVec::new(100).unwrap();
assert_eq!(bv.len(), 100);
assert_eq!(bv.num_blocks(), 2); assert!(!bv.is_empty());
}
#[test]
fn test_new_zero_bits_error() {
assert!(BitVec::new(0).is_err());
}
#[test]
fn test_set_get() {
let bv = BitVec::new(128).unwrap();
assert!(!bv.get(0));
bv.set(0);
bv.set(63);
bv.set(64);
bv.set(127);
assert!(bv.get(0));
assert!(bv.get(63));
assert!(bv.get(64));
assert!(bv.get(127));
assert!(!bv.get(32));
}
#[test]
fn test_set_idempotent() {
let bv = BitVec::new(64).unwrap();
bv.set(10);
bv.set(10);
bv.set(10);
assert_eq!(bv.count_ones(), 1);
}
#[test]
fn test_clear() {
let mut bv = BitVec::new(64).unwrap();
bv.set(10);
bv.set(20);
bv.set(30);
assert_eq!(bv.count_ones(), 3);
bv.clear();
assert_eq!(bv.count_ones(), 0);
assert!(!bv.get(10));
assert!(!bv.get(20));
assert!(!bv.get(30));
}
#[test]
fn test_clear_bit() {
let bv = BitVec::new(64).unwrap();
bv.set(10);
assert!(bv.get(10));
bv.clear_bit(10);
assert!(!bv.get(10));
}
#[test]
fn test_count_ones() {
let bv = BitVec::new(100).unwrap();
assert_eq!(bv.count_ones(), 0);
bv.set(0);
bv.set(50);
bv.set(99);
assert_eq!(bv.count_ones(), 3);
}
#[test]
fn test_set_range_basic() {
let bv = BitVec::new(100).unwrap();
bv.set_range(10, 20, true);
for i in 10..20 {
assert!(bv.get(i), "Bit {} should be set", i);
}
assert!(!bv.get(9), "Bit 9 should not be set");
assert!(!bv.get(20), "Bit 20 should not be set");
}
#[test]
fn test_set_range_clear() {
let bv = BitVec::new(100).unwrap();
for i in 0..100 {
bv.set(i);
}
bv.set_range(40, 60, false);
for i in 40..60 {
assert!(!bv.get(i), "Bit {} should be cleared", i);
}
assert!(bv.get(39), "Bit 39 should still be set");
assert!(bv.get(60), "Bit 60 should still be set");
}
#[test]
fn test_set_range_multiple_words() {
let bv = BitVec::new(200).unwrap();
bv.set_range(50, 150, true);
assert_eq!(bv.count_ones(), 100);
assert!(!bv.get(49));
assert!(bv.get(50));
assert!(bv.get(149));
assert!(!bv.get(150));
}
#[test]
fn test_set_range_word_boundary_crossing() {
let bv = BitVec::new(200).unwrap();
bv.set_range(60, 70, true);
assert!(!bv.get(59));
assert!(bv.get(60));
assert!(bv.get(63));
assert!(bv.get(64));
assert!(bv.get(69));
assert!(!bv.get(70));
assert_eq!(bv.count_ones(), 10);
}
#[test]
fn test_set_range_exactly_one_full_word() {
let bv = BitVec::new(128).unwrap();
bv.set_range(64, 128, true);
for i in 0..64 { assert!(!bv.get(i)); }
for i in 64..128 { assert!(bv.get(i)); }
assert_eq!(bv.count_ones(), 64);
}
#[test]
fn test_set_range_empty() {
let bv = BitVec::new(100).unwrap();
bv.set_range(50, 50, true);
assert_eq!(bv.count_ones(), 0);
}
#[test]
#[should_panic(expected = "out of bounds")]
fn test_set_range_out_of_bounds() {
let bv = BitVec::new(100).unwrap();
bv.set_range(50, 150, true);
}
#[test]
#[should_panic(expected = "start must be <= end")]
fn test_set_range_invalid_range() {
let bv = BitVec::new(100).unwrap();
bv.set_range(60, 50, true);
}
#[test]
fn test_set_range_boundaries() {
let bv = BitVec::new(100).unwrap();
bv.set_range(0, 10, true);
assert!(bv.get(0));
assert!(bv.get(9));
assert!(!bv.get(10));
bv.set_range(90, 100, true);
assert!(bv.get(90));
assert!(bv.get(99));
}
#[test]
fn test_get_range_basic() {
let bv = BitVec::new(100).unwrap();
bv.set(10);
bv.set(11);
bv.set(13);
let range = bv.get_range(10, 15);
assert_eq!(range, vec![true, true, false, true, false]);
}
#[test]
fn test_get_range_empty() {
let bv = BitVec::new(100).unwrap();
let range = bv.get_range(50, 50);
assert_eq!(range, Vec::<bool>::new());
}
#[test]
#[should_panic(expected = "out of bounds")]
fn test_get_range_out_of_bounds() {
let bv = BitVec::new(100).unwrap();
let _ = bv.get_range(50, 150);
}
#[test]
#[should_panic(expected = "start must be <= end")]
fn test_get_range_invalid_range() {
let bv = BitVec::new(100).unwrap();
let _ = bv.get_range(60, 50);
}
#[test]
fn test_clear_block_atomic_zeros_word() {
let bv = BitVec::new(128).unwrap();
bv.set_range(0, 64, true);
assert_eq!(bv.count_ones(), 64);
bv.clear_block_atomic(0);
assert_eq!(bv.count_ones(), 0);
}
#[test]
#[should_panic(expected = "out of bounds")]
fn test_clear_block_atomic_oob_panics() {
let bv = BitVec::new(64).unwrap();
bv.clear_block_atomic(1);
}
#[test]
fn test_union() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(64).unwrap();
bv1.set(10); bv1.set(20);
bv2.set(20); bv2.set(30);
let union = bv1.union(&bv2).unwrap();
assert!(union.get(10));
assert!(union.get(20));
assert!(union.get(30));
assert!(!union.get(40));
assert_eq!(union.count_ones(), 3);
}
#[test]
fn test_union_size_mismatch() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(128).unwrap();
assert!(bv1.union(&bv2).is_err());
}
#[test]
fn test_intersect() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(64).unwrap();
bv1.set(10); bv1.set(20); bv1.set(30);
bv2.set(20); bv2.set(30); bv2.set(40);
let intersection = bv1.intersect(&bv2).unwrap();
assert!(!intersection.get(10));
assert!(intersection.get(20));
assert!(intersection.get(30));
assert!(!intersection.get(40));
assert_eq!(intersection.count_ones(), 2);
}
#[test]
fn test_intersect_size_mismatch() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(128).unwrap();
assert!(bv1.intersect(&bv2).is_err());
}
#[test]
fn test_union_inplace() {
let bv1 = BitVec::new(1000).unwrap();
let bv2 = BitVec::new(1000).unwrap();
bv1.set(10); bv2.set(20);
bv1.union_inplace(&bv2).unwrap();
assert!(bv1.get(10) && bv1.get(20));
}
#[test]
fn test_union_inplace_size_mismatch() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(128).unwrap();
assert!(bv1.union_inplace(&bv2).is_err());
}
#[test]
fn test_intersect_inplace() {
let bv1 = BitVec::new(1000).unwrap();
let bv2 = BitVec::new(1000).unwrap();
bv1.set(10); bv1.set(20);
bv2.set(10); bv2.set(30);
bv1.intersect_inplace(&bv2).unwrap();
assert!(bv1.get(10));
assert!(!bv1.get(20));
assert!(!bv1.get(30));
}
#[test]
fn test_xor() {
let bv1 = BitVec::new(1000).unwrap();
let bv2 = BitVec::new(1000).unwrap();
bv1.set(10); bv1.set(20);
bv2.set(10); bv2.set(30);
let result = bv1.xor(&bv2).unwrap();
assert!(!result.get(10)); assert!(result.get(20)); assert!(result.get(30)); }
#[test]
fn test_xor_size_mismatch() {
let bv1 = BitVec::new(64).unwrap();
let bv2 = BitVec::new(128).unwrap();
assert!(bv1.xor(&bv2).is_err());
}
#[test]
fn test_clone() {
let bv1 = BitVec::new(64).unwrap();
bv1.set(10);
bv1.set(20);
let bv2 = bv1.clone();
assert!(bv2.get(10));
assert!(bv2.get(20));
bv1.set(30);
assert!(bv1.get(30));
assert!(!bv2.get(30));
}
#[test]
fn test_memory_usage() {
let bv = BitVec::new(1000).unwrap();
assert!(bv.memory_usage() >= 128); }
#[test]
#[should_panic(expected = "out of bounds")]
fn test_set_out_of_bounds() {
let bv = BitVec::new(64).unwrap();
bv.set(64);
}
#[test]
#[should_panic(expected = "out of bounds")]
fn test_get_out_of_bounds() {
let bv = BitVec::new(64).unwrap();
let _ = bv.get(64);
}
#[test]
#[should_panic(expected = "out of bounds")]
fn test_clear_bit_out_of_bounds() {
let bv = BitVec::new(64).unwrap();
bv.clear_bit(64);
}
#[test]
fn test_roundtrip_via_raw() {
let original = BitVec::new(128).unwrap();
original.set(5);
original.set(100);
let raw = original.to_raw();
assert_eq!(raw.len(), 2);
let restored = BitVec::from_raw(raw, original.len()).unwrap();
assert!(restored.get(5));
assert!(restored.get(100));
assert!(!restored.get(6));
}
#[test]
fn test_from_raw_empty_errors() {
assert!(BitVec::from_raw(vec![], 64).is_err());
}
#[test]
fn test_from_raw_zero_len_errors() {
assert!(BitVec::from_raw(vec![0u64], 0).is_err());
}
#[test]
fn test_from_raw_insufficient_blocks_errors() {
assert!(BitVec::from_raw(vec![0u64], 128).is_err());
}
#[test]
fn from_raw_masks_stray_bits_in_last_partial_word() {
let raw = vec![0u64, u64::MAX];
let bv = BitVec::from_raw(raw, 65).unwrap();
assert_eq!(bv.count_ones(), 1, "stray bits must be masked on import");
assert!(bv.get(64), "the one valid bit must survive");
}
#[test]
fn from_raw_does_not_mask_when_len_is_multiple_of_64() {
let raw = vec![0u64, u64::MAX];
let bv = BitVec::from_raw(raw, 128).unwrap();
assert_eq!(bv.count_ones(), 64);
}
#[test]
fn from_raw_stray_bits_do_not_corrupt_fill_rate() {
let raw = vec![u64::MAX];
let bv = BitVec::from_raw(raw, 10).unwrap();
assert_eq!(bv.count_ones(), 10);
assert!((bv.fill_rate() - 1.0).abs() < 1e-10);
}
#[test]
fn from_raw_to_raw_roundtrip_is_stable_with_partial_last_word() {
let original = BitVec::new(70).unwrap();
original.set(0);
original.set(69);
let raw = original.to_raw();
let restored = BitVec::from_raw(raw, 70).unwrap();
assert_eq!(original, restored);
assert_eq!(original.count_ones(), restored.count_ones());
}
#[test]
fn test_concurrent_set_no_lost_writes() {
use std::sync::Arc;
use std::thread;
let bv = Arc::new(BitVec::new(10_000).unwrap());
let handles: Vec<_> = (0..8u64)
.map(|t| {
let bv = Arc::clone(&bv);
thread::spawn(move || {
for i in 0..100 {
bv.set((t * 100 + i) as usize);
}
})
})
.collect();
for h in handles { h.join().unwrap(); }
assert_eq!(bv.count_ones(), 800);
}
#[test]
fn test_concurrent_union_inplace_all_bits_visible() {
use std::sync::Arc;
use std::thread;
let target = Arc::new(BitVec::new(1000).unwrap());
let handles: Vec<_> = (0..4usize)
.map(|t| {
let target = Arc::clone(&target);
thread::spawn(move || {
let src = BitVec::new(1000).unwrap();
src.set(t * 100);
target.union_inplace(&src).unwrap();
})
})
.collect();
for h in handles { h.join().unwrap(); }
assert!(target.get(0));
assert!(target.get(100));
assert!(target.get(200));
assert!(target.get(300));
}
#[test]
fn test_fill_rate_empty() {
let bv = BitVec::new(100).unwrap();
assert_eq!(bv.fill_rate(), 0.0);
}
#[test]
fn test_fill_rate_half() {
let bv = BitVec::new(64).unwrap();
for i in 0..32 { bv.set(i); }
assert!((bv.fill_rate() - 0.5).abs() < 1e-10);
}
#[test]
fn test_fill_rate_full() {
let bv = BitVec::new(64).unwrap();
for i in 0..64 { bv.set(i); }
assert!((bv.fill_rate() - 1.0).abs() < 1e-10);
}
#[test]
fn test_fill_rate_consistent_with_count_ones() {
let bv = BitVec::new(200).unwrap();
bv.set_range(50, 100, true);
let expected = 50.0 / 200.0;
assert!((bv.fill_rate() - expected).abs() < 1e-10);
}
#[test]
fn test_subset_true() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10);
b.set(10); b.set(20);
assert!(a.is_subset_of(&b).unwrap());
}
#[test]
fn test_subset_false_extra_bit() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); a.set(30);
b.set(10); b.set(20);
assert!(!a.is_subset_of(&b).unwrap());
}
#[test]
fn test_empty_is_subset_of_any() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
b.set(10);
assert!(a.is_subset_of(&b).unwrap());
}
#[test]
fn test_every_bitvec_is_subset_of_itself() {
let a = BitVec::new(64).unwrap();
a.set(5); a.set(10); a.set(63);
assert!(a.is_subset_of(&a).unwrap());
}
#[test]
fn test_subset_size_mismatch_errors() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(128).unwrap();
assert!(a.is_subset_of(&b).is_err());
}
#[test]
fn test_subset_after_union() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
b.set(10); b.set(20);
a.union_inplace(&b).unwrap();
assert!(b.is_subset_of(&a).unwrap());
}
#[test]
fn test_disjoint_true() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); b.set(20);
assert!(a.is_disjoint(&b).unwrap());
}
#[test]
fn test_disjoint_false_shared_bit() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); a.set(20);
b.set(20); b.set(30);
assert!(!a.is_disjoint(&b).unwrap());
}
#[test]
fn test_empty_is_disjoint_with_anything() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
b.set(10); b.set(20);
assert!(a.is_disjoint(&b).unwrap());
}
#[test]
fn test_disjoint_after_intersect_clears_both() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); b.set(20);
let result = a.intersect(&b).unwrap();
assert!(result.is_disjoint(&a).unwrap());
assert!(result.is_disjoint(&b).unwrap());
}
#[test]
fn test_disjoint_size_mismatch_errors() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(128).unwrap();
assert!(a.is_disjoint(&b).is_err());
}
#[test]
fn test_jaccard_both_empty_is_one() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
assert_eq!(a.jaccard_similarity(&b).unwrap(), 1.0);
}
#[test]
fn test_jaccard_identical_is_one() {
let a = BitVec::new(64).unwrap();
a.set(10); a.set(20); a.set(30);
let b = a.clone();
assert!((a.jaccard_similarity(&b).unwrap() - 1.0).abs() < 1e-10);
}
#[test]
fn test_jaccard_disjoint_is_zero() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); b.set(20);
assert_eq!(a.jaccard_similarity(&b).unwrap(), 0.0);
}
#[test]
fn test_jaccard_partial_overlap() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); a.set(20);
b.set(10); b.set(30);
let sim = a.jaccard_similarity(&b).unwrap();
assert!((sim - 1.0 / 3.0).abs() < 1e-10);
}
#[test]
fn test_jaccard_commutative() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10); a.set(20);
b.set(20); b.set(30);
let j_ab = a.jaccard_similarity(&b).unwrap();
let j_ba = b.jaccard_similarity(&a).unwrap();
assert!((j_ab - j_ba).abs() < 1e-15);
}
#[test]
fn test_jaccard_size_mismatch_errors() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(128).unwrap();
assert!(a.jaccard_similarity(&b).is_err());
}
#[test]
fn test_jaccard_subset_implies_gt_zero() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(10);
b.set(10); b.set(20); b.set(30);
let sim = a.jaccard_similarity(&b).unwrap();
assert!((sim - 1.0 / 3.0).abs() < 1e-10);
}
#[test]
fn test_partial_eq_identical_bits() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(5); b.set(5);
assert_eq!(a, b);
}
#[test]
fn test_partial_eq_different_bits() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(5); b.set(6);
assert_ne!(a, b);
}
#[test]
fn test_partial_eq_different_lengths() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(128).unwrap();
assert_ne!(a, b);
}
#[test]
fn test_eq_reflexive() {
let a = BitVec::new(64).unwrap();
a.set(5); a.set(63);
assert_eq!(a, a);
}
#[test]
fn test_eq_symmetric() {
let a = BitVec::new(64).unwrap();
let b = BitVec::new(64).unwrap();
a.set(5); b.set(5);
assert_eq!(a, b);
assert_eq!(b, a);
}
#[test]
fn test_clone_is_eq() {
let a = BitVec::new(128).unwrap();
a.set_range(10, 50, true);
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn test_union_result_ge_operands() {
let a = BitVec::new(128).unwrap();
let b = BitVec::new(128).unwrap();
a.set(10); a.set(20);
b.set(20); b.set(30);
let u = a.union(&b).unwrap();
assert!(a.is_subset_of(&u).unwrap());
assert!(b.is_subset_of(&u).unwrap());
}
#[test]
fn test_intersection_result_le_operands() {
let a = BitVec::new(128).unwrap();
let b = BitVec::new(128).unwrap();
a.set(10); a.set(20);
b.set(20); b.set(30);
let r = a.intersect(&b).unwrap();
assert!(r.is_subset_of(&a).unwrap());
assert!(r.is_subset_of(&b).unwrap());
}
}