Skip to main content

simple_bitset/
bitset128.rs

1use core::fmt;
2use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index};
3#[cfg(feature = "serde")]
4use {
5    sequential_storage::map::PostcardValue,
6    serde::{Deserialize, Serialize},
7};
8
9/// A memory-efficient 128-bit set for embedded environments.
10#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct BitSet128(u64, u64);
13
14impl BitSet128 {
15    /// Create a new empty bitset.
16    pub const fn new() -> Self {
17        Self(0, 0)
18    }
19}
20
21#[cfg(feature = "serde")]
22impl PostcardValue<'_> for BitSet128 {}
23
24impl Default for BitSet128 {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl BitSet128 {
31    /// Resets all bits to 0.
32    #[inline]
33    pub fn reset_all(&mut self) {
34        self.0 = 0;
35        self.1 = 0;
36    }
37
38    /// Resets the bit at `index` to 0. Does nothing if the index is out of bounds.
39    #[inline]
40    pub fn reset(&mut self, index: u8) {
41        if index < 64 {
42            self.0 &= !(1u64 << index);
43        } else if index < 128 {
44            self.1 &= !(1u64 << (index - 64));
45        }
46    }
47
48    /// Sets all bits to 1.
49    #[inline]
50    pub fn set_all(&mut self) {
51        self.0 = u64::MAX;
52        self.1 = u64::MAX;
53    }
54
55    /// Sets the bit at `index` to 1. Does nothing if the index is out of bounds.
56    #[inline]
57    pub fn set(&mut self, index: u8) {
58        if index < 64 {
59            self.0 |= 1u64 << index;
60        } else if index < 128 {
61            self.1 |= 1u64 << (index - 64);
62        }
63    }
64
65    /// Flips the bit at `index`. Does nothing if the index is out of bounds.
66    #[inline]
67    pub fn flip(&mut self, index: u8) {
68        if index < 64 {
69            self.0 ^= 1u64 << index;
70        } else if index < 128 {
71            self.1 ^= 1u64 << (index - 64);
72        }
73    }
74
75    /// Flips all bits in the bitset (0s become 1s, and 1s become 0s).
76    #[inline]
77    pub fn flip_all(&mut self) {
78        self.0 = !self.0;
79        self.1 = !self.1;
80    }
81
82    /// In-place Difference / Mask-Clear. Clears any bits that are set in `other`.
83    /// This represents the mathematical operation: `self = self AND NOT other`.
84    #[inline]
85    pub fn and_not(&mut self, other: Self) {
86        self.0 &= !other.0;
87        self.1 &= !other.1;
88    }
89
90    /// Tests if the bit at `index` is 1.
91    /// Returns false if the bit is 0 or index is out of bounds.
92    #[inline]
93    pub fn test(&self, index: u8) -> bool {
94        if index < 64 {
95            (self.0 & (1u64 << index)) != 0
96        } else if index < 128 {
97            (self.1 & (1u64 << (index - 64))) != 0
98        } else {
99            false
100        }
101    }
102
103    /// Returns the number of set bits (population count).
104    #[inline]
105    pub const fn count_ones(&self) -> u32 {
106        self.0.count_ones() + self.1.count_ones()
107    }
108
109    /// Returns the number of leading zeros in the bitset,
110    /// counting from the most significant bit (index 127).
111    #[inline]
112    pub const fn leading_zeros(&self) -> u32 {
113        // If the higher 64 bits are completely empty, then all those 64 bits
114        // are zeros. We add 64 to whatever leading zeros are found in the lower 64 bits.
115        if self.1 == 0 {
116            64 + self.0.leading_zeros()
117        } else {
118            // If the higher 64 bits contain data, the leading zeros of the
119            // entire 128-bit structure are determined solely by the higher block.
120            self.1.leading_zeros()
121        }
122    }
123
124    /// Returns true if no bits are set.
125    #[inline]
126    pub const fn is_empty(&self) -> bool {
127        self.0 == 0 && self.1 == 0
128    }
129    /// Returns the highest index set, or None if the bitset is empty.
130    /// Useful for finding the "top" of a priority queue or resource map.
131    #[inline]
132    pub const fn last_set(&self) -> Option<u8> {
133        let leading = self.leading_zeros();
134        if leading == 128 {
135            None
136        } else {
137            // Index is 127 minus the number of leading zeros.
138            // Example: 0 leading zeros means bit 127 is set.
139            // Cast is safe as (127 - leading) is guaranteed to be between 0 and 127.
140            #[allow(clippy::cast_possible_truncation)]
141            Some((127 - leading) as u8)
142        }
143    }
144
145    /// Returns true if this bitset contains all the bits set in `other`.
146    #[inline]
147    pub const fn is_superset(&self, other: &Self) -> bool {
148        // A bitset is a superset if clearing any bits not present in self
149        // results in exactly the other bitset configuration for both halves.
150        (self.0 & other.0) == other.0 && (self.1 & other.1) == other.1
151    }
152
153    /// Returns true if this bitset is a subset of `other`.
154    #[inline]
155    pub const fn is_subset(&self, other: &BitSet128) -> bool {
156        other.is_superset(self)
157    }
158
159    /// Returns true if this bitset shares at least one common set bit with `other`.
160    /// Returns false if there is no overlap or if either bitset is empty.
161    #[inline]
162    pub const fn intersects(&self, other: &Self) -> bool {
163        // Run a bitwise AND between the bitsets.
164        // If the result is non-zero, an intersection exists.
165        (self.0 & other.0) != 0 || (self.1 & other.1) != 0
166    }
167
168    /// Returns an iterator over the indices of the set bits.
169    #[inline]
170    pub fn iter(&self) -> BitSet128Iter {
171        self.into_iter()
172    }
173}
174
175// **** Bit operations ****
176
177impl BitOr for BitSet128 {
178    type Output = Self;
179    fn bitor(self, rhs: Self) -> Self::Output {
180        Self(self.0 | rhs.0, self.1 | rhs.1)
181    }
182}
183
184impl BitAnd for BitSet128 {
185    type Output = Self;
186    fn bitand(self, rhs: Self) -> Self::Output {
187        Self(self.0 & rhs.0, self.1 & rhs.1)
188    }
189}
190
191impl BitXor for BitSet128 {
192    type Output = Self;
193    fn bitxor(self, rhs: Self) -> Self::Output {
194        Self(self.0 ^ rhs.0, self.1 ^ rhs.1)
195    }
196}
197
198impl BitOrAssign for BitSet128 {
199    fn bitor_assign(&mut self, rhs: Self) {
200        self.0 |= rhs.0;
201        self.1 |= rhs.1;
202    }
203}
204
205impl BitAndAssign for BitSet128 {
206    fn bitand_assign(&mut self, rhs: Self) {
207        self.0 &= rhs.0;
208        self.1 &= rhs.1;
209    }
210}
211
212impl BitXorAssign for BitSet128 {
213    fn bitxor_assign(&mut self, rhs: Self) {
214        self.0 ^= rhs.0;
215        self.1 ^= rhs.1;
216    }
217}
218
219impl Index<u8> for BitSet128 {
220    type Output = bool;
221
222    fn index(&self, index: u8) -> &Self::Output {
223        // We use static booleans because we must return a reference
224        if self.test(index) { &true } else { &false }
225    }
226}
227
228impl Index<usize> for BitSet128 {
229    type Output = bool;
230
231    #[allow(clippy::cast_possible_truncation)]
232    fn index(&self, index: usize) -> &Self::Output {
233        // We use static booleans because we must return a reference
234        if self.test(index as u8) { &true } else { &false }
235    }
236}
237
238/// `BitSet128` from `u32`.
239impl From<u32> for BitSet128 {
240    #[inline]
241    fn from(a: u32) -> Self {
242        Self(u64::from(a), 0)
243    }
244}
245
246/// `BitSet128` from `(u32, u32)`.
247impl From<(u32, u32)> for BitSet128 {
248    #[inline]
249    fn from((a, b): (u32, u32)) -> Self {
250        Self(u64::from(a) << 32 | u64::from(b), 0)
251    }
252}
253
254/// `BitSet128` from `(u32, u32, u32, u32)`.
255impl From<(u32, u32, u32, u32)> for BitSet128 {
256    #[inline]
257    fn from((a, b, c, d): (u32, u32, u32, u32)) -> Self {
258        Self(u64::from(a) << 32 | u64::from(b), u64::from(c) << 32 | u64::from(d))
259    }
260}
261
262// **** Iter ****
263
264#[derive(Debug, Default, Eq, PartialEq)]
265pub struct BitSet128Iter(u64, u64);
266
267/// Consuming iterator.
268impl Iterator for BitSet128Iter {
269    type Item = u8;
270
271    fn next(&mut self) -> Option<Self::Item> {
272        if self.0 == 0 {
273            if self.1 == 0 {
274                None
275            } else {
276                // Find the index of the least significant bit set to 1
277                #[allow(clippy::cast_possible_truncation)]
278                let index = self.1.trailing_zeros() as u8;
279                // Clear the least significant bit to prep for next iteration
280                self.1 &= self.1 - 1;
281                Some(index + 64)
282            }
283        } else {
284            // Find the index of the least significant bit set to 1
285            #[allow(clippy::cast_possible_truncation)]
286            let index = self.0.trailing_zeros() as u8;
287            // Clear the least significant bit to prep for next iteration
288            self.0 &= self.0 - 1;
289            Some(index)
290        }
291    }
292
293    #[inline]
294    fn size_hint(&self) -> (usize, Option<usize>) {
295        // We know exactly how many bits are left to yield at any moment!
296        let len = (self.0.count_ones() + self.1.count_ones()) as usize;
297        (len, Some(len))
298    }
299}
300
301// Implementing ExactSizeIterator unlocks additional optimizations automatically
302impl ExactSizeIterator for BitSet128Iter {}
303impl core::iter::FusedIterator for BitSet128Iter {}
304
305/// Non-consuming iterator for bitset reference.
306impl IntoIterator for &BitSet128 {
307    type Item = u8;
308    type IntoIter = BitSet128Iter;
309
310    fn into_iter(self) -> Self::IntoIter {
311        // Since `BitSet128` is `Copy` and just a `(u64, u64)`,
312        // we just pass the underlying value to the iterator.
313        BitSet128Iter(self.0, self.1)
314    }
315}
316
317/// Non-consuming iterator for bitset.
318impl IntoIterator for BitSet128 {
319    type Item = u8;
320    type IntoIter = BitSet128Iter;
321
322    fn into_iter(self) -> Self::IntoIter {
323        // Since `BitSet128` is `Copy` and just a `(u64, u64)`,
324        // we just pass the underlying value to the iterator.
325        BitSet128Iter(self.0, self.1)
326    }
327}
328
329/// From iterator for bitset.
330impl FromIterator<u8> for BitSet128 {
331    fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
332        let mut bitset = Self::new();
333        for index in iter {
334            bitset.set(index);
335        }
336        bitset
337    }
338}
339
340impl Extend<u8> for BitSet128 {
341    fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
342        for index in iter {
343            self.set(index);
344        }
345    }
346}
347
348// **** fmt ****
349
350impl fmt::Binary for BitSet128 {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        // Handle the "0b" prefix if requested via {:#b}
353        if f.alternate() {
354            f.write_str("0b")?;
355        }
356        // Print from high bits to low bits (left-to-right)
357        // High bits (127 down to 64)
358        for i in (0..64).rev() {
359            let val = (self.1 >> i) & 1;
360            write!(f, "{val}")?;
361        }
362        // Low bits (63 down to 0)
363        for i in (0..64).rev() {
364            let val = (self.0 >> i) & 1;
365            write!(f, "{val}")?;
366        }
367
368        Ok(())
369    }
370}
371
372impl fmt::UpperHex for BitSet128 {
373    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374        if f.alternate() {
375            f.write_str("0x")?;
376        }
377        write!(f, "{:016X}{:016X}", self.1, self.0)
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384
385    fn is_normal<T: Sized + Send + Sync + Unpin>() {}
386    fn is_full<T: Sized + Send + Sync + Unpin + Copy + Clone + Default + PartialEq>() {}
387    #[cfg(feature = "serde")]
388    fn is_config<T: Serialize + for<'a> Deserialize<'a>>() {}
389
390    #[test]
391    fn normal_types() {
392        is_full::<BitSet128>();
393        #[cfg(feature = "serde")]
394        is_config::<BitSet128>();
395        is_normal::<BitSet128Iter>();
396    }
397    #[test]
398    fn new() {
399        let mut bits = BitSet128::new();
400        bits.set(42);
401        assert!(bits[42u8]);
402        assert!(bits.test(42));
403    }
404    #[allow(unused)]
405    #[test]
406    fn const_new() {
407        const FLAGS: BitSet128 = BitSet128::new();
408        const EMPTY_CHECK: bool = FLAGS.is_empty(); // Evaluated at compile time
409    }
410    #[test]
411    fn assign() {
412        let mut bits = BitSet128::new();
413        bits.set(42);
414        assert!(bits[42u8]);
415        assert!(bits.test(42));
416        let mask = bits;
417        assert!(mask.test(42));
418    }
419    #[test]
420    fn from() {
421        let _bits = BitSet128::from((0xab_u32, 0x12_u32));
422    }
423    #[test]
424    fn flip_all() {
425        let mut bitset = BitSet128::new();
426
427        // Alternating pattern test
428        bitset.set(0);
429        bitset.set(64);
430
431        bitset.flip_all();
432
433        // Bit 0 and 64 should now be 0, others should be 1
434        assert!(!bitset.test(0));
435        assert!(!bitset.test(64));
436        assert!(bitset.test(1));
437        assert!(bitset.test(65));
438
439        // Full cycle test (Empty -> Full -> Empty)
440        let mut empty_set = BitSet128::new();
441        empty_set.flip_all(); // Should become full
442
443        let mut full_set = BitSet128::new();
444        full_set.set_all();
445
446        assert_eq!(empty_set, full_set);
447
448        empty_set.flip_all(); // Should return to completely empty
449        assert!(empty_set.is_empty());
450    }
451    #[test]
452    fn leading_zeros() {
453        let mut bitset = BitSet128::new();
454
455        // Completely empty set should return 128 zeros
456        assert_eq!(bitset.leading_zeros(), 128);
457
458        // Setting the absolute most significant bit (index 127) leaves 0 leading zeros
459        bitset.set(127);
460        assert_eq!(bitset.leading_zeros(), 0);
461
462        // Setting index 126 leaves exactly 1 leading zero
463        bitset.reset_all();
464        bitset.set(126);
465        assert_eq!(bitset.leading_zeros(), 1);
466
467        // Test boundary exactly at the edge of the higher u64 (index 64)
468        bitset.reset_all();
469        bitset.set(64);
470        assert_eq!(bitset.leading_zeros(), 63);
471
472        // Test boundary exactly at the edge of the lower u64 (index 63)
473        bitset.reset_all();
474        bitset.set(63);
475        assert_eq!(bitset.leading_zeros(), 64);
476
477        // Setting the absolute lowest bit (index 0) leaves 127 leading zeros
478        bitset.reset_all();
479        bitset.set(0);
480        assert_eq!(bitset.leading_zeros(), 127);
481    }
482    #[test]
483    fn last_set() {
484        let mut bitset = BitSet128::new();
485
486        // Empty set should return None
487        assert_eq!(bitset.last_set(), None);
488
489        // Single lowest bit set
490        bitset.set(0);
491        assert_eq!(bitset.last_set(), Some(0));
492
493        // Multiple bits set, should return the highest one
494        bitset.set(10);
495        bitset.set(45);
496        assert_eq!(bitset.last_set(), Some(45));
497
498        // Boundary verification at the top of the lower u64
499        bitset.reset_all();
500        bitset.set(63);
501        assert_eq!(bitset.last_set(), Some(63));
502
503        // Boundary verification at the bottom of the higher u64
504        bitset.set(64);
505        assert_eq!(bitset.last_set(), Some(64));
506
507        // Multiple bits stretching into the high byte
508        bitset.set(100);
509        bitset.set(127);
510        assert_eq!(bitset.last_set(), Some(127));
511    }
512    #[test]
513    fn is_superset() {
514        let mut set_a = BitSet128::new();
515        let mut set_b = BitSet128::new();
516
517        // An empty set is always a superset of another empty set
518        assert!(set_a.is_superset(&set_b));
519
520        // Setup indices spanning across the 64-bit boundary
521        set_a.set(10);
522        set_a.set(80);
523
524        set_b.set(10);
525
526        // set_a has [10, 80], set_b has [10] -> Should be true
527        assert!(set_a.is_superset(&set_b));
528        // set_b is missing 80 -> Should be false
529        assert!(!set_b.is_superset(&set_a));
530
531        // Test exact match
532        set_b.set(80);
533        assert!(set_a.is_superset(&set_b));
534
535        // Test failure where lower matches but higher fails (Catches the original bug)
536        let mut set_c = BitSet128::new();
537        let mut set_d = BitSet128::new();
538        set_c.set(15); // Higher element (.1) is 0
539
540        set_d.set(15);
541        set_d.set(95); // Higher element (.1) is non-zero
542
543        // Lower halves match, but set_c is missing bit 95.
544        // Your old function would mistakenly return true here.
545        assert!(!set_c.is_superset(&set_d));
546    }
547    #[test]
548    fn intersects() {
549        let mut set_a = BitSet128::new();
550        let mut set_b = BitSet128::new();
551
552        // Empty sets should never intersect
553        assert!(!set_a.intersects(&set_b));
554
555        // Add an item to set_a only
556        set_a.set(15);
557        assert!(!set_a.intersects(&set_b));
558
559        // Match the item in set_b (Intersection in the lower u64 block)
560        set_b.set(15);
561        assert!(set_a.intersects(&set_b));
562        assert!(set_b.intersects(&set_a));
563
564        // Test disjoint sets across the 64-bit split boundary
565        set_a.reset_all();
566        set_b.reset_all();
567        set_a.set(10); // Lower u64 block
568        set_b.set(80); // Higher u64 block
569        assert!(!set_a.intersects(&set_b));
570
571        // Test intersection purely inside the higher u64 block (index >= 64)
572        set_a.set(80);
573        assert!(set_a.intersects(&set_b));
574    }
575    #[test]
576    fn inplace_logical_ops() {
577        let mut set_a = BitSet128::new();
578        let mut set_b = BitSet128::new();
579
580        // Setup overlapping patterns crossing 64-bit boundaries
581        set_a.set(10);
582        set_a.set(70);
583
584        set_b.set(10);
585        set_b.set(80);
586
587        // Test BitAndAssign (&=)
588        let mut result = set_a;
589        result &= set_b;
590        assert!(result.test(10));
591        assert!(!result.test(70));
592        assert!(!result.test(80));
593
594        // Test BitOrAssign (|=)
595        let mut result = set_a;
596        result |= set_b;
597        assert!(result.test(10));
598        assert!(result.test(70));
599        assert!(result.test(80));
600
601        // Test BitXorAssign (^=)
602        let mut result = set_a;
603        result ^= set_b;
604        assert!(!result.test(10)); // Both were 1, turns to 0
605        assert!(result.test(70));
606        assert!(result.test(80));
607
608        // Test and_not
609        let mut result = set_a;
610        result.and_not(set_b);
611        assert!(!result.test(10)); // Cleared by mask
612        assert!(result.test(70)); // Preserved
613        assert!(!result.test(80)); // Never present in set_a
614    }
615    #[test]
616    fn exercise() {
617        let mut system_flags = BitSet128::new();
618        let error_mask = BitSet128::new(); // imagine this has error bits set
619
620        // Combine with OR-assign
621        system_flags |= error_mask;
622
623        // Toggle bits with XOR-assign
624        system_flags ^= error_mask;
625
626        // Mask out bits with AND-assign
627        //system_flags &= BitSet128(0x0000_FFFF_FFFF_FFFF);
628
629        let mut set_a = BitSet128::new();
630        set_a.set(10);
631        set_a.set(20);
632
633        let mut set_b = BitSet128::new();
634        set_b.set(20);
635        set_b.set(30);
636
637        // Intersection (AND): only bit 20 remains
638        let common = set_a & set_b;
639        assert!(!common.test(10));
640        assert!(common.test(20));
641        assert!(!common.test(30));
642
643        // Union (OR): bits 10, 20, and 30 are set
644        let all = set_a | set_b;
645        assert!(all.test(10));
646        assert!(all.test(20));
647        assert!(all.test(30));
648
649        // Difference (XOR): bits 10 and 30 are set (20 is cancelled out)
650        let diff = set_a ^ set_b;
651        assert!(diff.test(10));
652        assert!(!diff.test(20));
653        assert!(diff.test(30));
654    }
655
656    #[test]
657    fn test_iterator_empty() {
658        let bitset = BitSet128::new();
659        let mut iter = bitset.iter();
660
661        assert_eq!(iter.size_hint(), (0, Some(0)));
662        assert_eq!(iter.next(), None);
663    }
664
665    #[test]
666    fn test_iterator_single_bits() {
667        // Test lower bound (index 0)
668        let mut bitset = BitSet128::new();
669        bitset.set(0);
670        let mut iter = bitset.iter();
671        assert_eq!(iter.next(), Some(0));
672        assert_eq!(iter.next(), None);
673
674        // Test boundary transition index (63)
675        bitset.reset_all();
676        bitset.set(63);
677        let mut iter = bitset.iter();
678        assert_eq!(iter.next(), Some(63));
679        assert_eq!(iter.next(), None);
680
681        // Test boundary transition index (64)
682        bitset.reset_all();
683        bitset.set(64);
684        let mut iter = bitset.iter();
685        assert_eq!(iter.next(), Some(64));
686        assert_eq!(iter.next(), None);
687
688        // Test upper bound (index 127)
689        bitset.reset_all();
690        bitset.set(127);
691        let mut iter = bitset.iter();
692        assert_eq!(iter.next(), Some(127));
693        assert_eq!(iter.next(), None);
694    }
695
696    #[test]
697    fn test_iterator_multiple_scattered_bits() {
698        let mut bitset = BitSet128::new();
699        // A stack-allocated expected sequence
700        let expected_indices = [5, 12, 63, 64, 99, 127];
701
702        for &idx in &expected_indices {
703            bitset.set(idx);
704        }
705
706        // Check size_hint tracks perfectly before iteration begins
707        let mut iter = bitset.iter();
708        assert_eq!(iter.size_hint(), (expected_indices.len(), Some(expected_indices.len())));
709
710        // Verify elements using a zero-allocation loop matching indices
711        for &expected in &expected_indices {
712            assert_eq!(iter.next(), Some(expected));
713        }
714        assert_eq!(iter.next(), None);
715    }
716
717    #[test]
718    fn test_iterator_size_hint_drainage() {
719        let mut bitset = BitSet128::new();
720        bitset.set(10);
721        bitset.set(90);
722
723        let mut iter = bitset.iter();
724        assert_eq!(iter.size_hint(), (2, Some(2)));
725        assert_eq!(iter.len(), 2); // Enabled by ExactSizeIterator
726
727        assert_eq!(iter.next(), Some(10));
728        assert_eq!(iter.size_hint(), (1, Some(1)));
729        assert_eq!(iter.len(), 1);
730
731        assert_eq!(iter.next(), Some(90));
732        assert_eq!(iter.size_hint(), (0, Some(0)));
733        assert_eq!(iter.len(), 0);
734
735        assert_eq!(iter.next(), None);
736    }
737
738    #[test]
739    fn test_iterator_all_bits_set() {
740        let mut bitset = BitSet128::new();
741        bitset.set_all();
742
743        let mut count = 0;
744        #[allow(clippy::cast_possible_truncation)]
745        for (expected_idx, actual_idx) in bitset.iter().enumerate() {
746            assert_eq!(expected_idx as u8, actual_idx);
747            count += 1;
748        }
749        assert_eq!(count, 128);
750    }
751
752    #[test]
753    fn test_consuming_into_iter() {
754        let mut bitset = BitSet128::new();
755        bitset.set(42);
756
757        let mut count = 0;
758        for idx in bitset {
759            assert_eq!(idx, 42);
760            count += 1;
761        }
762        assert_eq!(count, 1);
763    }
764
765    #[test]
766    fn from_iterator() {
767        // Collect from a concrete array slice
768        let indices = [5, 63, 64, 120];
769        let bitset: BitSet128 = indices.into_iter().collect();
770
771        assert!(bitset.test(5));
772        assert!(bitset.test(63));
773        assert!(bitset.test(64));
774        assert!(bitset.test(120));
775        assert_eq!(bitset.count_ones(), 4);
776
777        // Verify out-of-bounds values are ignored safely without panics
778        let invalid_indices = [20, 200, 255];
779        let safe_bitset: BitSet128 = invalid_indices.into_iter().collect();
780
781        assert!(safe_bitset.test(20));
782        assert_eq!(safe_bitset.count_ones(), 1); // Only index 20 should be set
783    }
784    #[test]
785    fn empty_and_full() {
786        let empty = BitSet128::new();
787        assert_eq!(empty.iter().count(), 0);
788
789        let mut full = BitSet128::new();
790        full.set_all();
791        assert_eq!(full.iter().count(), 128);
792    }
793}