Skip to main content

simple_bitset/
bitset128.rs

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