From 9f8eb500ef69970a77b467431044e2a01283a920 Mon Sep 17 00:00:00 2001 From: qjerome Date: Tue, 15 Jul 2025 09:40:42 +0200 Subject: [PATCH 1/3] fix: bug in bitset unset function --- poppy/src/bitset/array.rs | 142 +++++++++++++++++++++++++++++++++++++- poppy/src/bitset/vec.rs | 111 ++++++++++++++++++++++++++++- 2 files changed, 251 insertions(+), 2 deletions(-) diff --git a/poppy/src/bitset/array.rs b/poppy/src/bitset/array.rs index c5313d4..28fd31f 100644 --- a/poppy/src/bitset/array.rs +++ b/poppy/src/bitset/array.rs @@ -1,8 +1,16 @@ +use core::f32; + /// Array based bitset implementation. /// N gives the size in Bytes of the bucket #[derive(Debug, Clone)] pub struct BitSet([u8; N]); +impl Default for BitSet { + fn default() -> Self { + Self([0u8; N]) + } +} + impl BitSet { /// Creates a new bitset pub const fn new() -> Self { @@ -15,7 +23,11 @@ impl BitSet { // equivalent to index % 8 let mask = (value as u8) << (index & 7); let old = self.0[iblock] & mask == mask; - self.0[iblock] |= mask; + if value { + self.0[iblock] |= mask; + } else { + self.0[iblock] &= mask; + } old } @@ -131,3 +143,131 @@ impl BitSet { N * 8 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new_bitset() { + let bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.as_slice(), &[0u8; 2]); + } + + #[test] + fn test_default_bitset() { + let bitset: BitSet<2> = BitSet::default(); + assert_eq!(bitset.as_slice(), &[0u8; 2]); + } + + #[test] + fn test_set_nth_bit() { + let mut bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.set_nth_bit(0), false); // Bit was 0, now set to 1 + assert_eq!(bitset.get_nth_bit(0), true); + } + + #[test] + fn test_unset_nth_bit() { + let mut bitset: BitSet<2> = BitSet::new(); + bitset.set_nth_bit(0); // Set bit to 1 first + assert_eq!(bitset.unset_nth_bit(0), true); // Bit was 1, now set to 0 + assert_eq!(bitset.get_nth_bit(0), false); + } + + #[test] + fn test_get_nth_bit() { + let mut bitset: BitSet<2> = BitSet::new(); + bitset.set_nth_bit(0); + assert_eq!(bitset.get_nth_bit(0), true); + assert_eq!(bitset.get_nth_bit(1), false); + } + + #[test] + #[should_panic(expected = "index out of bounds")] + fn test_set_nth_bit_out_of_bounds() { + let mut bitset: BitSet<2> = BitSet::new(); + bitset.set_nth_bit(16); // Assuming N=2, so 16 bits is out of bounds + } + + #[test] + fn test_clear() { + let mut bitset: BitSet<2> = BitSet::new(); + bitset.set_nth_bit(0); + bitset.set_nth_bit(1); + bitset.clear(); + assert_eq!(bitset.as_slice(), &[0u8; 2]); + } + + #[test] + fn test_count_ones() { + let mut bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.count_ones(), 0); + bitset.set_nth_bit(0); + bitset.set_nth_bit(1); + assert_eq!(bitset.count_ones(), 2); + } + + #[test] + fn test_count_zeros() { + let mut bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.count_zeros(), 16); // 2 bytes = 16 bits, all zeros initially + bitset.set_nth_bit(0); + assert_eq!(bitset.count_zeros(), 15); + } + + #[test] + fn test_union() { + let mut bitset1: BitSet<2> = BitSet::new(); + let mut bitset2: BitSet<2> = BitSet::new(); + bitset1.set_nth_bit(0); + bitset2.set_nth_bit(1); + bitset1.union(&bitset2); + assert_eq!(bitset1.get_nth_bit(0), true); + assert_eq!(bitset1.get_nth_bit(1), true); + } + + #[test] + fn test_intersection() { + let mut bitset1: BitSet<2> = BitSet::new(); + let mut bitset2: BitSet<2> = BitSet::new(); + bitset1.set_nth_bit(0); + bitset1.set_nth_bit(1); + bitset2.set_nth_bit(1); + bitset1.intersection(&bitset2); + assert_eq!(bitset1.get_nth_bit(0), false); + assert_eq!(bitset1.get_nth_bit(1), true); + } + + #[test] + fn test_count_ones_in_common() { + let mut bitset1: BitSet<2> = BitSet::new(); + let mut bitset2: BitSet<2> = BitSet::new(); + bitset1.set_nth_bit(0); + bitset1.set_nth_bit(1); + bitset2.set_nth_bit(1); + assert_eq!(bitset1.count_ones_in_common(&bitset2), 1); + } + + #[test] + fn test_bit_len() { + let bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.bit_len(), 16); // 2 bytes = 16 bits + } + + #[test] + fn test_byte_len() { + let bitset: BitSet<2> = BitSet::new(); + assert_eq!(bitset.byte_len(), 2); + } + + #[test] + fn test_byte_size() { + assert_eq!(BitSet::<2>::byte_size(), 2); + } + + #[test] + fn test_bit_size() { + assert_eq!(BitSet::<2>::bit_size(), 16); + } +} diff --git a/poppy/src/bitset/vec.rs b/poppy/src/bitset/vec.rs index b3e7615..27fa721 100644 --- a/poppy/src/bitset/vec.rs +++ b/poppy/src/bitset/vec.rs @@ -55,7 +55,11 @@ impl VecBitSet { // equivalent to index % 8 let mask = (value as u8) << (index & 7); let old = self.0[iblock] & mask == mask; - self.0[iblock] |= mask; + if value { + self.0[iblock] |= mask; + } else { + self.0[iblock] &= mask; + } old } @@ -149,3 +153,108 @@ impl VecBitSet { self.0.iter().map(|b| b.count_zeros() as usize).sum() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_with_bit_capacity() { + let capacity = 10; + let bitset = VecBitSet::with_bit_capacity(capacity); + assert_eq!(bitset.bit_len(), 16); // because of byte alignment, with capacity 10 it's 16 bits (2 bytes) + assert_eq!(bitset.byte_len(), 2); + } + + #[test] + fn test_is_empty() { + let bitset = VecBitSet::with_bit_capacity(0); + assert!(bitset.is_empty()); + } + + #[test] + fn test_set_nth_bit() { + let mut bitset = VecBitSet::with_bit_capacity(8); + assert!(!bitset.get_nth_bit(0)); + bitset.set_nth_bit(0); + assert!(bitset.get_nth_bit(0)); + } + + #[test] + fn test_unset_nth_bit() { + let mut bitset = VecBitSet::with_bit_capacity(8); + bitset.set_nth_bit(0); + assert!(bitset.get_nth_bit(0)); + bitset.unset_nth_bit(0); + assert!(!bitset.get_nth_bit(0)); + } + + #[test] + fn test_clear() { + let mut bitset = VecBitSet::with_bit_capacity(8); + bitset.set_nth_bit(0); + bitset.set_nth_bit(1); + bitset.clear(); + assert_eq!(bitset.count_ones(), 0); + } + + #[test] + fn test_union() { + let mut bitset1 = VecBitSet::with_bit_capacity(8); + let mut bitset2 = VecBitSet::with_bit_capacity(8); + bitset1.set_nth_bit(0); + bitset2.set_nth_bit(1); + bitset1.union(&bitset2); + assert!(bitset1.get_nth_bit(0)); + assert!(bitset1.get_nth_bit(1)); + } + + #[test] + fn test_intersection() { + let mut bitset1 = VecBitSet::with_bit_capacity(8); + let mut bitset2 = VecBitSet::with_bit_capacity(8); + bitset1.set_nth_bit(0); + bitset1.set_nth_bit(1); + bitset2.set_nth_bit(1); + bitset2.set_nth_bit(2); + bitset1.intersection(&bitset2); + assert!(!bitset1.get_nth_bit(0)); + assert!(bitset1.get_nth_bit(1)); + assert!(!bitset1.get_nth_bit(2)); + } + + #[test] + fn test_count_ones_in_common() { + let mut bitset1 = VecBitSet::with_bit_capacity(8); + let mut bitset2 = VecBitSet::with_bit_capacity(8); + bitset1.set_nth_bit(0); + bitset1.set_nth_bit(1); + bitset2.set_nth_bit(1); + bitset2.set_nth_bit(2); + assert_eq!(bitset1.count_ones_in_common(&bitset2), 1); + } + + #[test] + fn test_count_ones() { + let mut bitset = VecBitSet::with_bit_capacity(8); + bitset.set_nth_bit(0); + bitset.set_nth_bit(1); + assert_eq!(bitset.count_ones(), 2); + } + + #[test] + fn test_count_zeros() { + let mut bitset = VecBitSet::with_bit_capacity(8); + bitset.set_nth_bit(0); + bitset.set_nth_bit(1); + assert_eq!(bitset.count_zeros(), 6); + } + + #[test] + #[should_panic] + fn test_out_of_bounds_access() { + let bitset = VecBitSet::with_bit_capacity(8); + // should panic but handled in the test if it does. + bitset.get_nth_bit(8); + } +} From 547a4d62de6313f0f6d48cde73b1723d195f16c6 Mon Sep 17 00:00:00 2001 From: qjerome Date: Tue, 15 Jul 2025 11:50:19 +0200 Subject: [PATCH 2/3] =?UTF-8?q?fix:=C2=A0clippy=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poppy/src/bitset/array.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/poppy/src/bitset/array.rs b/poppy/src/bitset/array.rs index 28fd31f..76d72e4 100644 --- a/poppy/src/bitset/array.rs +++ b/poppy/src/bitset/array.rs @@ -1,4 +1,3 @@ -use core::f32; /// Array based bitset implementation. /// N gives the size in Bytes of the bucket @@ -163,24 +162,24 @@ mod tests { #[test] fn test_set_nth_bit() { let mut bitset: BitSet<2> = BitSet::new(); - assert_eq!(bitset.set_nth_bit(0), false); // Bit was 0, now set to 1 - assert_eq!(bitset.get_nth_bit(0), true); + assert!(!bitset.set_nth_bit(0)); // Bit was 0, now set to 1 + assert!(bitset.get_nth_bit(0)); } #[test] fn test_unset_nth_bit() { let mut bitset: BitSet<2> = BitSet::new(); bitset.set_nth_bit(0); // Set bit to 1 first - assert_eq!(bitset.unset_nth_bit(0), true); // Bit was 1, now set to 0 - assert_eq!(bitset.get_nth_bit(0), false); + assert!(bitset.unset_nth_bit(0)); // Bit was 1, now set to 0 + assert!(!bitset.get_nth_bit(0)); } #[test] fn test_get_nth_bit() { let mut bitset: BitSet<2> = BitSet::new(); bitset.set_nth_bit(0); - assert_eq!(bitset.get_nth_bit(0), true); - assert_eq!(bitset.get_nth_bit(1), false); + assert!(bitset.get_nth_bit(0)); + assert!(!bitset.get_nth_bit(1)); } #[test] @@ -223,8 +222,8 @@ mod tests { bitset1.set_nth_bit(0); bitset2.set_nth_bit(1); bitset1.union(&bitset2); - assert_eq!(bitset1.get_nth_bit(0), true); - assert_eq!(bitset1.get_nth_bit(1), true); + assert!(bitset1.get_nth_bit(0)); + assert!(bitset1.get_nth_bit(1)); } #[test] @@ -235,8 +234,8 @@ mod tests { bitset1.set_nth_bit(1); bitset2.set_nth_bit(1); bitset1.intersection(&bitset2); - assert_eq!(bitset1.get_nth_bit(0), false); - assert_eq!(bitset1.get_nth_bit(1), true); + assert!(!bitset1.get_nth_bit(0)); + assert!(bitset1.get_nth_bit(1)); } #[test] From 7dcd1bd014d35d4b19fb127a226de3e27615da4c Mon Sep 17 00:00:00 2001 From: qjerome Date: Tue, 15 Jul 2025 11:51:17 +0200 Subject: [PATCH 3/3] =?UTF-8?q?add:=C2=A0clippy=20check=20on=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e699b96..1c9d3b2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,6 +16,8 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Clippy + run: cargo clippy -- -D warnings - name: Build run: cargo build --release --verbose - name: Run tests