Skip to main content

commonware_utils/bitmap/roaring/
ops.rs

1//! Set operations for roaring bitmaps.
2//!
3//! Provides union, intersection, and difference operations with optional early termination
4//! when a result limit is reached, along with the boolean predicates `is_subset` and
5//! `intersects` which short-circuit on first conclusive observation.
6
7#[cfg(test)]
8use super::container::Container;
9use super::Bitmap;
10#[cfg(not(feature = "std"))]
11use alloc::collections::BTreeMap;
12use core::cmp::Ordering;
13#[cfg(feature = "std")]
14use std::collections::BTreeMap;
15
16impl Bitmap {
17    /// Computes the union of two bitmaps, returning at most `limit` values.
18    ///
19    /// Pass `u64::MAX` for unlimited results.
20    pub fn union(&self, other: &Self, limit: u64) -> Self {
21        let mut result = BTreeMap::new();
22        let mut remaining = limit;
23
24        let mut a_iter = self.containers.iter().peekable();
25        let mut b_iter = other.containers.iter().peekable();
26
27        while remaining > 0 {
28            let a_key = a_iter.peek().map(|(&key, _)| key);
29            let b_key = b_iter.peek().map(|(&key, _)| key);
30            let Some(key) = next_key(a_key, b_key) else {
31                break;
32            };
33
34            let (container, count) = match (a_key == Some(key), b_key == Some(key)) {
35                (true, true) => {
36                    let (_, a_container) = a_iter.next().unwrap();
37                    let (_, b_container) = b_iter.next().unwrap();
38                    a_container.union(b_container, remaining)
39                }
40                (true, false) => {
41                    let (_, container) = a_iter.next().unwrap();
42                    container.limit(remaining)
43                }
44                (false, true) => {
45                    let (_, container) = b_iter.next().unwrap();
46                    container.limit(remaining)
47                }
48                (false, false) => unreachable!("next_key returned a key from neither iterator"),
49            };
50
51            if count > 0 {
52                result.insert(key, container);
53                remaining -= count;
54            }
55        }
56
57        Self { containers: result }
58    }
59
60    /// Computes the intersection of two bitmaps, returning at most `limit` values.
61    ///
62    /// Pass `u64::MAX` for unlimited results.
63    pub fn intersection(&self, other: &Self, limit: u64) -> Self {
64        let mut result = BTreeMap::new();
65        let mut remaining = limit;
66
67        let (smaller, larger) = if self.containers.len() <= other.containers.len() {
68            (&self.containers, &other.containers)
69        } else {
70            (&other.containers, &self.containers)
71        };
72
73        for (&key, container) in smaller.iter() {
74            if remaining == 0 {
75                break;
76            }
77
78            if let Some(other_container) = larger.get(&key) {
79                let (container, count) = container.intersection(other_container, remaining);
80                if count > 0 {
81                    result.insert(key, container);
82                    remaining -= count;
83                }
84            }
85        }
86
87        Self { containers: result }
88    }
89
90    /// Computes the difference `self - other`, returning at most `limit` values.
91    ///
92    /// Pass `u64::MAX` for unlimited results.
93    pub fn difference(&self, other: &Self, limit: u64) -> Self {
94        let mut result = BTreeMap::new();
95        let mut remaining = limit;
96
97        for (&key, container) in self.containers.iter() {
98            if remaining == 0 {
99                break;
100            }
101
102            let (container, count) = other.containers.get(&key).map_or_else(
103                || container.limit(remaining),
104                |other_container| container.difference(other_container, remaining),
105            );
106
107            if count > 0 {
108                result.insert(key, container);
109                remaining -= count;
110            }
111        }
112
113        Self { containers: result }
114    }
115
116    /// Returns `true` if every value in this bitmap is present in `other`.
117    pub fn is_subset(&self, other: &Self) -> bool {
118        if self.len() > other.len() {
119            return false;
120        }
121
122        self.containers.iter().all(|(key, container)| {
123            other
124                .containers
125                .get(key)
126                .is_some_and(|other_container| container.is_subset(other_container))
127        })
128    }
129
130    /// Returns `true` if the bitmaps share at least one value.
131    pub fn intersects(&self, other: &Self) -> bool {
132        let (smaller, larger) = if self.containers.len() <= other.containers.len() {
133            (&self.containers, &other.containers)
134        } else {
135            (&other.containers, &self.containers)
136        };
137
138        smaller.iter().any(|(key, container)| {
139            larger
140                .get(key)
141                .is_some_and(|other_container| container.intersects(other_container))
142        })
143    }
144}
145
146fn next_key(a: Option<u64>, b: Option<u64>) -> Option<u64> {
147    match a.cmp(&b) {
148        Ordering::Equal => a,
149        Ordering::Less => a.or(b),
150        Ordering::Greater => b.or(a),
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::bitmap::BitMap as Reference;
158
159    fn reference_len(a: &Bitmap, b: &Bitmap) -> u64 {
160        a.iter().chain(b.iter()).max().map_or(0, |value| value + 1)
161    }
162
163    fn build_reference(bitmap: &Bitmap, len: u64) -> Reference {
164        let mut reference = Reference::zeroes(len);
165        for value in bitmap.iter() {
166            reference.set(value, true);
167        }
168        reference
169    }
170
171    fn expected_values(reference: &Reference, limit: u64) -> Vec<u64> {
172        if limit == 0 {
173            return Vec::new();
174        }
175
176        let mut values = Vec::new();
177        for value in 0..reference.len() {
178            if reference.get(value) {
179                values.push(value);
180                if values.len() as u64 == limit {
181                    break;
182                }
183            }
184        }
185        values
186    }
187
188    fn assert_matches_reference(result: &Bitmap, reference: &Reference, limit: u64, op: &str) {
189        let actual: Vec<_> = result.iter().collect();
190        let expected = expected_values(reference, limit);
191        assert_eq!(actual, expected, "{op} mismatch");
192        assert_eq!(result.len(), expected.len() as u64, "{op} length mismatch");
193    }
194
195    fn assert_union_matches_reference(a: &Bitmap, b: &Bitmap, limit: u64) -> Bitmap {
196        let len = reference_len(a, b);
197        let mut reference = build_reference(a, len);
198        reference.or(&build_reference(b, len));
199
200        let result = a.union(b, limit);
201        assert_matches_reference(&result, &reference, limit, "union");
202        result
203    }
204
205    fn assert_intersection_matches_reference(a: &Bitmap, b: &Bitmap, limit: u64) -> Bitmap {
206        let len = reference_len(a, b);
207        let mut reference = build_reference(a, len);
208        reference.and(&build_reference(b, len));
209
210        let result = a.intersection(b, limit);
211        assert_matches_reference(&result, &reference, limit, "intersection");
212        result
213    }
214
215    fn assert_difference_matches_reference(a: &Bitmap, b: &Bitmap, limit: u64) -> Bitmap {
216        let len = reference_len(a, b);
217        let mut reference = build_reference(a, len);
218        for value in b.iter() {
219            reference.set(value, false);
220        }
221
222        let result = a.difference(b, limit);
223        assert_matches_reference(&result, &reference, limit, "difference");
224        result
225    }
226
227    fn assert_is_subset_matches_reference(a: &Bitmap, b: &Bitmap) -> bool {
228        let len = reference_len(a, b);
229        let a_reference = build_reference(a, len);
230        let b_reference = build_reference(b, len);
231        let expected = (0..len).all(|value| !a_reference.get(value) || b_reference.get(value));
232        let result = a.is_subset(b);
233        assert_eq!(result, expected, "is_subset mismatch");
234        result
235    }
236
237    fn assert_intersects_matches_reference(a: &Bitmap, b: &Bitmap) -> bool {
238        let len = reference_len(a, b);
239        let a_reference = build_reference(a, len);
240        let b_reference = build_reference(b, len);
241        let expected = (0..len).any(|value| a_reference.get(value) && b_reference.get(value));
242        let result = a.intersects(b);
243        assert_eq!(result, expected, "intersects mismatch");
244        result
245    }
246
247    #[test]
248    fn test_union_basic() {
249        let a = Bitmap::from_iter([1, 2, 3]);
250        let b = Bitmap::from_iter([2, 3, 4, 5]);
251
252        assert_union_matches_reference(&a, &b, u64::MAX);
253    }
254
255    #[test]
256    fn test_union_with_limit() {
257        let a = Bitmap::from_iter([1, 2, 3]);
258        let b = Bitmap::from_iter([4, 5, 6]);
259
260        assert_union_matches_reference(&a, &b, 4);
261    }
262
263    #[test]
264    fn test_union_empty() {
265        let a = Bitmap::new();
266        let b = Bitmap::from_iter([1, 2, 3]);
267
268        assert_union_matches_reference(&a, &b, u64::MAX);
269    }
270
271    #[test]
272    fn test_union_a_key_less_than_b() {
273        // a's container at key 0, b's at key 1. Exercises the `a_key < b_key` branch
274        // in `union` for a's container, then the trailing `(None, Some)` branch picks
275        // up b's container.
276        let a = Bitmap::from_iter([1, 2, 3]);
277        let b = Bitmap::from_iter([65_536, 65_537, 65_538]);
278        assert_union_matches_reference(&a, &b, u64::MAX);
279    }
280
281    #[test]
282    fn test_union_b_key_less_than_a() {
283        // a's container at key 1, b's at key 0. Exercises the `b_key < a_key` branch.
284        let a = Bitmap::from_iter([65_536, 65_537, 65_538]);
285        let b = Bitmap::from_iter([1, 2, 3]);
286        assert_union_matches_reference(&a, &b, u64::MAX);
287    }
288
289    #[test]
290    fn test_union_alternating_keys() {
291        // Containers interleave: a at keys 0, 2; b at keys 1, 3. The outer merge loop
292        // hits both `a_key < b_key` and `b_key < a_key` branches multiple times.
293        let mut a = Bitmap::new();
294        a.insert(10); // key 0
295        a.insert(2 * 65_536 + 10); // key 2
296
297        let mut b = Bitmap::new();
298        b.insert(65_536 + 20); // key 1
299        b.insert(3 * 65_536 + 20); // key 3
300
301        let result = assert_union_matches_reference(&a, &b, u64::MAX);
302        assert_eq!(result.container_count(), 4);
303    }
304
305    #[test]
306    fn test_union_disjoint_keys_with_limit() {
307        // Disjoint containers + limit smaller than a's container. Exercises the
308        // `a_key < b_key` branch through `Container::limit` truncation.
309        let a = Bitmap::from_iter([1, 2, 3]);
310        let b = Bitmap::from_iter([65_536, 65_537, 65_538]);
311        assert_union_matches_reference(&a, &b, 2);
312    }
313
314    #[test]
315    fn test_container_union_array_array_promotes_to_bitmap_when_oversized() {
316        use commonware_codec::{Decode, Encode};
317
318        let mut a = Bitmap::new();
319        let mut b = Bitmap::new();
320        // Two interleaved every-fourth-value sequences in shelf 0; each side stays in
321        // Array variant. The union forms 4000 two-element runs (e.g. 0,1, 4,5, 8,9, ...),
322        // which exceeds RUN_TO_BITMAP_THRESHOLD and thus normalizes to Bitmap (not Run).
323        for i in 0..4000u64 {
324            a.insert(i * 4);
325            b.insert(i * 4 + 1);
326        }
327        assert!(matches!(
328            a.containers().get(&0).unwrap(),
329            Container::Array(_)
330        ));
331        assert!(matches!(
332            b.containers().get(&0).unwrap(),
333            Container::Array(_)
334        ));
335
336        let result = assert_union_matches_reference(&a, &b, u64::MAX);
337
338        // Result must promote to Bitmap.
339        assert!(
340            matches!(result.containers().get(&0).unwrap(), Container::Bitmap(_)),
341            "oversized array union must promote to Bitmap variant"
342        );
343
344        // Roundtrip exercises the codec, which would reject an oversized Array.
345        let bytes = result.encode();
346        let decoded = Bitmap::decode_cfg(bytes, &(..=10usize).into()).unwrap();
347        assert_eq!(result, decoded);
348    }
349
350    #[test]
351    fn test_container_union_bitmap_bitmap_fast_path() {
352        // Both containers in `Bitmap` variant at the same key. Exercises the
353        // bitmap-bitmap fast path in `Container::union` (`Bitmap::or_new`).
354        // Inserting alternating values past the Array threshold yields a Bitmap with
355        // many isolated runs, well above the Bitmap->Run threshold so it stays Bitmap.
356        let mut a = Bitmap::new();
357        let mut b = Bitmap::new();
358        for i in 0..4097u64 {
359            a.insert(i * 2); // even values 0, 2, ..., 8192
360            b.insert(i * 2 + 1); // odd values 1, 3, ..., 8193
361        }
362        assert!(matches!(
363            a.containers().get(&0).unwrap(),
364            Container::Bitmap(_)
365        ));
366        assert!(matches!(
367            b.containers().get(&0).unwrap(),
368            Container::Bitmap(_)
369        ));
370
371        assert_union_matches_reference(&a, &b, u64::MAX);
372    }
373
374    #[test]
375    fn test_container_union_bitmap_bitmap_limit_truncates() {
376        // Same setup as above but with a small limit, forcing the bitmap-bitmap fast
377        // path through `Container::limit` after the OR is computed.
378        let mut a = Bitmap::new();
379        let mut b = Bitmap::new();
380        for i in 0..4097u64 {
381            a.insert(i * 2);
382            b.insert(i * 2 + 1);
383        }
384
385        assert_union_matches_reference(&a, &b, 100);
386    }
387
388    #[test]
389    fn test_container_union_mixed_variants_general_path() {
390        // a's container is `Array`, b's is `Bitmap`, both at key 0. Neither fast path
391        // applies, so `Container::union` falls through to the general iterator-merge
392        // case.
393        let mut a = Bitmap::new();
394        a.insert(1);
395        a.insert(50);
396        a.insert(100);
397
398        let mut b = Bitmap::new();
399        for i in 0..4097u64 {
400            b.insert(i * 2 + 200); // alternating values starting at 200
401        }
402        assert!(matches!(
403            a.containers().get(&0).unwrap(),
404            Container::Array(_)
405        ));
406        assert!(matches!(
407            b.containers().get(&0).unwrap(),
408            Container::Bitmap(_)
409        ));
410
411        assert_union_matches_reference(&a, &b, u64::MAX);
412    }
413
414    #[test]
415    fn test_intersection_basic() {
416        let a = Bitmap::from_iter([1, 2, 3, 4]);
417        let b = Bitmap::from_iter([2, 3, 4, 5]);
418
419        assert_intersection_matches_reference(&a, &b, u64::MAX);
420    }
421
422    #[test]
423    fn test_intersection_with_limit() {
424        let a = Bitmap::from_iter([1, 2, 3, 4, 5]);
425        let b = Bitmap::from_iter([1, 2, 3, 4, 5]);
426
427        assert_intersection_matches_reference(&a, &b, 3);
428    }
429
430    #[test]
431    fn test_intersection_disjoint() {
432        let a = Bitmap::from_iter([1, 2, 3]);
433        let b = Bitmap::from_iter([4, 5, 6]);
434
435        assert_intersection_matches_reference(&a, &b, u64::MAX);
436    }
437
438    #[test]
439    fn test_intersection_containers_bitmap_bitmap_fast_path() {
440        // Both containers in `Bitmap` variant at the same key. Exercises the
441        // bitmap-bitmap fast path in `Container::intersection` (`Bitmap::and_new`).
442        let mut a = Bitmap::new();
443        let mut b = Bitmap::new();
444        // a: even values 0, 2, ..., 8192 (4097 alternating)
445        // b: every fourth value 0, 4, 8, ..., 16384 (4097 values)
446        // Common values: 0, 4, 8, ..., 8192 (the multiples of 4 in [0, 8193]).
447        for i in 0..4097u64 {
448            a.insert(i * 2);
449            b.insert(i * 4);
450        }
451        assert!(matches!(
452            a.containers().get(&0).unwrap(),
453            Container::Bitmap(_)
454        ));
455        assert!(matches!(
456            b.containers().get(&0).unwrap(),
457            Container::Bitmap(_)
458        ));
459
460        assert_intersection_matches_reference(&a, &b, u64::MAX);
461    }
462
463    #[test]
464    fn test_intersection_containers_bitmap_bitmap_limit_truncates() {
465        // Same setup as above but a small limit forces the bitmap-bitmap fast path
466        // through `Container::limit` after `Bitmap::and_new` is computed.
467        let mut a = Bitmap::new();
468        let mut b = Bitmap::new();
469        for i in 0..4097u64 {
470            a.insert(i * 2);
471            b.insert(i * 4);
472        }
473
474        assert_intersection_matches_reference(&a, &b, 50);
475    }
476
477    #[test]
478    fn test_intersection_containers_mixed_variants_general_path() {
479        // a's container is `Array`, b's is `Bitmap`. Neither fast path applies, so
480        // `Container::intersection` falls through to the general "iterate smaller"
481        // case. With a.len() (3) < b.len() (4097), the loop iterates a's values.
482        let mut a = Bitmap::new();
483        a.insert(1);
484        a.insert(50);
485        a.insert(200); // value 200 is in b's set (200 = 0*2 + 200; b has even+200 series)
486
487        let mut b = Bitmap::new();
488        for i in 0..4097u64 {
489            b.insert(i * 2 + 200);
490        }
491        assert!(matches!(
492            a.containers().get(&0).unwrap(),
493            Container::Array(_)
494        ));
495        assert!(matches!(
496            b.containers().get(&0).unwrap(),
497            Container::Bitmap(_)
498        ));
499
500        assert_intersection_matches_reference(&a, &b, u64::MAX);
501    }
502
503    #[test]
504    fn test_difference_basic() {
505        let a = Bitmap::from_iter([1, 2, 3, 4]);
506        let b = Bitmap::from_iter([2, 3]);
507
508        assert_difference_matches_reference(&a, &b, u64::MAX);
509    }
510
511    #[test]
512    fn test_difference_with_limit() {
513        let a = Bitmap::from_iter([1, 2, 3, 4, 5]);
514        let b = Bitmap::from_iter([2, 4]);
515
516        assert_difference_matches_reference(&a, &b, 2);
517    }
518
519    #[test]
520    fn test_difference_all_removed() {
521        let a = Bitmap::from_iter([1, 2, 3]);
522        let b = Bitmap::from_iter([1, 2, 3, 4, 5]);
523
524        assert_difference_matches_reference(&a, &b, u64::MAX);
525    }
526
527    #[test]
528    fn test_container_difference_bitmap_bitmap_fast_path() {
529        // Both containers in `Bitmap` variant at the same key. Exercises the
530        // bitmap-bitmap fast path in `Container::difference` (`Bitmap::and_not_new`).
531        let mut a = Bitmap::new();
532        let mut b = Bitmap::new();
533        // a: even values 0, 2, ..., 8192 (4097 values)
534        // b: multiples of 4 from 0, 4, ..., 16384 (4097 values)
535        // a − b: even values not divisible by 4 in [0, 8192] = {2, 6, 10, ..., 8190}
536        for i in 0..4097u64 {
537            a.insert(i * 2);
538            b.insert(i * 4);
539        }
540        assert!(matches!(
541            a.containers().get(&0).unwrap(),
542            Container::Bitmap(_)
543        ));
544        assert!(matches!(
545            b.containers().get(&0).unwrap(),
546            Container::Bitmap(_)
547        ));
548
549        assert_difference_matches_reference(&a, &b, u64::MAX);
550    }
551
552    #[test]
553    fn test_container_difference_bitmap_bitmap_limit_truncates() {
554        // Same setup but a small limit forces the fast path through
555        // `Container::limit` after `Bitmap::and_not_new` is computed.
556        let mut a = Bitmap::new();
557        let mut b = Bitmap::new();
558        for i in 0..4097u64 {
559            a.insert(i * 2);
560            b.insert(i * 4);
561        }
562
563        assert_difference_matches_reference(&a, &b, 30);
564    }
565
566    #[test]
567    fn test_container_difference_mixed_variants_general_path() {
568        // a's container is `Array`, b's is `Bitmap`. Neither fast path applies, so
569        // `Container::difference` falls through to the general "iterate a, skip values in b"
570        // case.
571        let mut a = Bitmap::new();
572        a.insert(1);
573        a.insert(50);
574        a.insert(200);
575
576        let mut b = Bitmap::new();
577        for i in 0..4097u64 {
578            b.insert(i * 2 + 200); // 200, 202, ..., 8392
579        }
580        assert!(matches!(
581            a.containers().get(&0).unwrap(),
582            Container::Array(_)
583        ));
584        assert!(matches!(
585            b.containers().get(&0).unwrap(),
586            Container::Bitmap(_)
587        ));
588
589        assert_difference_matches_reference(&a, &b, u64::MAX);
590    }
591
592    #[test]
593    fn test_union_multiple_containers() {
594        let mut a = Bitmap::new();
595        let mut b = Bitmap::new();
596
597        // Values in different containers
598        a.insert(100);
599        a.insert(65536 + 100); // Second container
600
601        b.insert(200);
602        b.insert(65536 + 200); // Second container
603
604        assert_union_matches_reference(&a, &b, u64::MAX);
605    }
606
607    #[test]
608    fn test_intersection_multiple_containers() {
609        let mut a = Bitmap::new();
610        let mut b = Bitmap::new();
611
612        a.insert(100);
613        a.insert(200);
614        a.insert(65536 + 100);
615
616        b.insert(200);
617        b.insert(300);
618        b.insert(65536 + 100);
619
620        assert_intersection_matches_reference(&a, &b, u64::MAX);
621    }
622
623    #[test]
624    fn test_operations_with_zero_limit() {
625        let a = Bitmap::from_iter([1, 2, 3]);
626        let b = Bitmap::from_iter([2, 3, 4]);
627
628        assert_union_matches_reference(&a, &b, 0);
629        assert_intersection_matches_reference(&a, &b, 0);
630        assert_difference_matches_reference(&a, &b, 0);
631    }
632
633    // ---- is_subset ----
634
635    #[test]
636    fn test_is_subset_proper() {
637        let a = Bitmap::from_iter([1, 2, 3]);
638        let b = Bitmap::from_iter([1, 2, 3, 4, 5]);
639        assert!(assert_is_subset_matches_reference(&a, &b));
640        assert!(!assert_is_subset_matches_reference(&b, &a));
641    }
642
643    #[test]
644    fn test_is_subset_equal() {
645        let a = Bitmap::from_iter([1, 2, 3]);
646        let b = Bitmap::from_iter([1, 2, 3]);
647        assert!(assert_is_subset_matches_reference(&a, &b));
648        assert!(assert_is_subset_matches_reference(&b, &a));
649    }
650
651    #[test]
652    fn test_is_subset_empty() {
653        let empty = Bitmap::new();
654        let nonempty = Bitmap::from_iter([1, 2, 3]);
655        // Empty is subset of any set, including itself.
656        assert!(assert_is_subset_matches_reference(&empty, &nonempty));
657        assert!(assert_is_subset_matches_reference(&empty, &empty));
658        // Non-empty is not a subset of empty.
659        assert!(!assert_is_subset_matches_reference(&nonempty, &empty));
660    }
661
662    #[test]
663    fn test_is_subset_missing_value_same_container() {
664        let a = Bitmap::from_iter([1, 2, 3]);
665        let b = Bitmap::from_iter([1, 3]); // missing 2
666        assert!(!assert_is_subset_matches_reference(&a, &b));
667    }
668
669    #[test]
670    fn test_is_subset_missing_container() {
671        // a has values in container 1; b has only container 0.
672        let a = Bitmap::from_iter([1, 65536 + 100]);
673        let b = Bitmap::from_iter([1, 2, 3]);
674        assert!(!assert_is_subset_matches_reference(&a, &b));
675    }
676
677    #[test]
678    fn test_is_subset_multi_container() {
679        let mut a = Bitmap::new();
680        let mut b = Bitmap::new();
681        a.insert(100);
682        a.insert(65536 + 50);
683        a.insert(131_072 + 7);
684
685        b.insert(50);
686        b.insert(100);
687        b.insert(65536 + 50);
688        b.insert(65536 + 100);
689        b.insert(131_072 + 7);
690        b.insert(131_072 + 8);
691
692        assert!(assert_is_subset_matches_reference(&a, &b));
693        assert!(!assert_is_subset_matches_reference(&b, &a));
694    }
695
696    #[test]
697    fn test_is_subset_cardinality_short_circuit() {
698        // |a| > |b| means a can't be subset; should return false without checking values.
699        let a = Bitmap::from_iter([1, 2, 3, 4]);
700        let b = Bitmap::from_iter([1, 2]);
701        assert!(!assert_is_subset_matches_reference(&a, &b));
702    }
703
704    // ---- intersects ----
705
706    #[test]
707    fn test_intersects_overlap() {
708        let a = Bitmap::from_iter([1, 2, 3]);
709        let b = Bitmap::from_iter([3, 4, 5]);
710        assert!(assert_intersects_matches_reference(&a, &b));
711        assert!(assert_intersects_matches_reference(&b, &a));
712    }
713
714    #[test]
715    fn test_intersects_disjoint() {
716        let a = Bitmap::from_iter([1, 2, 3]);
717        let b = Bitmap::from_iter([4, 5, 6]);
718        assert!(!assert_intersects_matches_reference(&a, &b));
719        assert!(!assert_intersects_matches_reference(&b, &a));
720    }
721
722    #[test]
723    fn test_intersects_one_empty() {
724        let a = Bitmap::new();
725        let b = Bitmap::from_iter([1, 2, 3]);
726        assert!(!assert_intersects_matches_reference(&a, &b));
727        assert!(!assert_intersects_matches_reference(&b, &a));
728    }
729
730    #[test]
731    fn test_intersects_both_empty() {
732        let a = Bitmap::new();
733        let b = Bitmap::new();
734        assert!(!assert_intersects_matches_reference(&a, &b));
735    }
736
737    #[test]
738    fn test_intersects_self() {
739        let a = Bitmap::from_iter([1, 2, 3]);
740        assert!(assert_intersects_matches_reference(&a, &a));
741    }
742
743    #[test]
744    fn test_intersects_multi_container_only_in_second() {
745        // Containers 0 are disjoint, but container 1 has an overlap.
746        let mut a = Bitmap::new();
747        let mut b = Bitmap::new();
748        a.insert(100);
749        a.insert(65536 + 50);
750        b.insert(200);
751        b.insert(65536 + 50); // overlap here
752
753        assert!(assert_intersects_matches_reference(&a, &b));
754        assert!(assert_intersects_matches_reference(&b, &a));
755    }
756
757    #[test]
758    fn test_intersects_multi_container_no_overlap() {
759        // Same container keys but no shared values.
760        let mut a = Bitmap::new();
761        let mut b = Bitmap::new();
762        a.insert(100);
763        a.insert(65536 + 50);
764        b.insert(200);
765        b.insert(65536 + 99);
766
767        assert!(!assert_intersects_matches_reference(&a, &b));
768        assert!(!assert_intersects_matches_reference(&b, &a));
769    }
770
771    #[test]
772    fn test_intersects_disjoint_keys() {
773        // No container keys in common.
774        let mut a = Bitmap::new();
775        let mut b = Bitmap::new();
776        a.insert(100);
777        b.insert(65536 + 50);
778        assert!(!assert_intersects_matches_reference(&a, &b));
779    }
780
781    // ---- relationships between ops (sanity checks) ----
782
783    #[test]
784    fn test_intersects_iff_intersection_nonempty() {
785        // intersects(a, b) <=> !intersection(a, b).is_empty()
786        let cases = [
787            (vec![1, 2, 3], vec![3, 4, 5]),
788            (vec![1, 2, 3], vec![4, 5, 6]),
789            (vec![], vec![1, 2, 3]),
790            (vec![1, 65536 + 50], vec![100, 65536 + 50]),
791            (vec![1, 65536 + 50], vec![100, 65536 + 99]),
792        ];
793        for (a_vals, b_vals) in cases {
794            let a = Bitmap::from_iter(a_vals.iter().copied());
795            let b = Bitmap::from_iter(b_vals.iter().copied());
796            let inter = assert_intersection_matches_reference(&a, &b, u64::MAX);
797            assert_eq!(
798                assert_intersects_matches_reference(&a, &b),
799                !inter.is_empty(),
800                "mismatch for a={a_vals:?} b={b_vals:?}"
801            );
802        }
803    }
804
805    #[test]
806    fn test_is_subset_iff_difference_empty() {
807        // is_subset(a, b) <=> difference(a, b).is_empty()
808        let cases = [
809            (vec![1, 2], vec![1, 2, 3]),
810            (vec![1, 2, 3], vec![1, 2]),
811            (vec![], vec![1, 2, 3]),
812            (vec![1, 2, 3], vec![]),
813            (vec![1, 65536 + 50], vec![1, 65536 + 50, 131_072]),
814        ];
815        for (a_vals, b_vals) in cases {
816            let a = Bitmap::from_iter(a_vals.iter().copied());
817            let b = Bitmap::from_iter(b_vals.iter().copied());
818            let diff = assert_difference_matches_reference(&a, &b, u64::MAX);
819            assert_eq!(
820                assert_is_subset_matches_reference(&a, &b),
821                diff.is_empty(),
822                "mismatch for a={a_vals:?} b={b_vals:?}"
823            );
824        }
825    }
826}