splinter_rs/
splinter_ops.rs

1use std::{
2    mem,
3    ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Deref, Sub, SubAssign},
4};
5
6use crate::{CowSplinter, Cut, PartitionRead, Splinter, SplinterRef};
7
8impl<B: Deref<Target = [u8]>> PartialEq<SplinterRef<B>> for Splinter {
9    #[inline]
10    fn eq(&self, other: &SplinterRef<B>) -> bool {
11        self.inner() == &other.load_unchecked()
12    }
13}
14
15impl<B: Deref<Target = [u8]>> PartialEq<CowSplinter<B>> for Splinter {
16    fn eq(&self, other: &CowSplinter<B>) -> bool {
17        match other {
18            CowSplinter::Ref(splinter_ref) => self.eq(splinter_ref),
19            CowSplinter::Owned(splinter) => self.eq(splinter),
20        }
21    }
22}
23
24impl Cut for Splinter {
25    type Out = Self;
26
27    fn cut(&mut self, rhs: &Self) -> Self::Out {
28        Self::new(self.inner_mut().cut(rhs.inner()))
29    }
30}
31
32impl<B: Deref<Target = [u8]>> Cut<SplinterRef<B>> for Splinter {
33    type Out = Self;
34
35    fn cut(&mut self, rhs: &SplinterRef<B>) -> Self::Out {
36        Self::new(self.inner_mut().cut(&rhs.load_unchecked()))
37    }
38}
39
40impl<B: Deref<Target = [u8]>> Cut<CowSplinter<B>> for Splinter {
41    type Out = Self;
42
43    fn cut(&mut self, rhs: &CowSplinter<B>) -> Self::Out {
44        match rhs {
45            CowSplinter::Ref(splinter_ref) => self.cut(splinter_ref),
46            CowSplinter::Owned(splinter) => self.cut(splinter),
47        }
48    }
49}
50
51macro_rules! binary_bitop {
52    ($BitOp:tt, $bitop:ident, $bitassign:path) => {
53        impl $BitOp<Splinter> for Splinter {
54            type Output = Splinter;
55            #[inline]
56            fn $bitop(mut self, rhs: Splinter) -> Self::Output {
57                $bitassign(&mut self, rhs);
58                self
59            }
60        }
61        impl $BitOp<&Splinter> for Splinter {
62            type Output = Splinter;
63            #[inline]
64            fn $bitop(mut self, rhs: &Splinter) -> Self::Output {
65                $bitassign(&mut self, rhs);
66                self
67            }
68        }
69        impl<B: Deref<Target = [u8]>> $BitOp<SplinterRef<B>> for Splinter {
70            type Output = Splinter;
71            #[inline]
72            fn $bitop(mut self, rhs: SplinterRef<B>) -> Self::Output {
73                $bitassign(&mut self, rhs);
74                self
75            }
76        }
77        impl<B: Deref<Target = [u8]>> $BitOp<&SplinterRef<B>> for Splinter {
78            type Output = Splinter;
79            #[inline]
80            fn $bitop(mut self, rhs: &SplinterRef<B>) -> Self::Output {
81                $bitassign(&mut self, rhs);
82                self
83            }
84        }
85        impl<B: Deref<Target = [u8]>> $BitOp<SplinterRef<B>> for &Splinter {
86            type Output = Splinter;
87            #[inline]
88            fn $bitop(self, rhs: SplinterRef<B>) -> Self::Output {
89                $BitOp::$bitop(self.clone(), rhs)
90            }
91        }
92        impl<B: Deref<Target = [u8]>> $BitOp<&SplinterRef<B>> for &Splinter {
93            type Output = Splinter;
94            #[inline]
95            fn $bitop(self, rhs: &SplinterRef<B>) -> Self::Output {
96                $BitOp::$bitop(self.clone(), rhs)
97            }
98        }
99        impl<B: Deref<Target = [u8]>> $BitOp<CowSplinter<B>> for Splinter {
100            type Output = Splinter;
101            #[inline]
102            fn $bitop(self, rhs: CowSplinter<B>) -> Self::Output {
103                $BitOp::$bitop(&self, &rhs)
104            }
105        }
106        impl<B: Deref<Target = [u8]>> $BitOp<CowSplinter<B>> for &Splinter {
107            type Output = Splinter;
108            #[inline]
109            fn $bitop(self, rhs: CowSplinter<B>) -> Self::Output {
110                $BitOp::$bitop(self, &rhs)
111            }
112        }
113        impl<B: Deref<Target = [u8]>> $BitOp<&CowSplinter<B>> for Splinter {
114            type Output = Splinter;
115            #[inline]
116            fn $bitop(self, rhs: &CowSplinter<B>) -> Self::Output {
117                $BitOp::$bitop(&self, rhs)
118            }
119        }
120        impl<B: Deref<Target = [u8]>> $BitOp<&CowSplinter<B>> for &Splinter {
121            type Output = Splinter;
122            fn $bitop(self, rhs: &CowSplinter<B>) -> Self::Output {
123                match rhs {
124                    CowSplinter::Ref(inner) => $BitOp::$bitop(self, inner),
125                    CowSplinter::Owned(inner) => $BitOp::$bitop(self, inner),
126                }
127            }
128        }
129    };
130}
131
132macro_rules! unary_bitassign {
133    ($BitOpAssign:tt, $bitassign:ident) => {
134        impl $BitOpAssign<&Splinter> for Splinter {
135            #[inline]
136            fn $bitassign(&mut self, rhs: &Splinter) {
137                $BitOpAssign::$bitassign(self.inner_mut(), rhs.inner())
138            }
139        }
140        impl<B: Deref<Target = [u8]>> $BitOpAssign<SplinterRef<B>> for Splinter {
141            #[inline]
142            fn $bitassign(&mut self, rhs: SplinterRef<B>) {
143                $BitOpAssign::$bitassign(self.inner_mut(), &rhs.load_unchecked())
144            }
145        }
146        impl<B: Deref<Target = [u8]>> $BitOpAssign<&SplinterRef<B>> for Splinter {
147            #[inline]
148            fn $bitassign(&mut self, rhs: &SplinterRef<B>) {
149                $BitOpAssign::$bitassign(self.inner_mut(), &rhs.load_unchecked())
150            }
151        }
152        impl<B: Deref<Target = [u8]>> $BitOpAssign<CowSplinter<B>> for Splinter {
153            fn $bitassign(&mut self, rhs: CowSplinter<B>) {
154                match rhs {
155                    CowSplinter::Ref(splinter_ref) => $BitOpAssign::$bitassign(self, splinter_ref),
156                    CowSplinter::Owned(splinter) => $BitOpAssign::$bitassign(self, splinter),
157                }
158            }
159        }
160        impl<B: Deref<Target = [u8]>> $BitOpAssign<&CowSplinter<B>> for Splinter {
161            fn $bitassign(&mut self, rhs: &CowSplinter<B>) {
162                match rhs {
163                    CowSplinter::Ref(splinter_ref) => $BitOpAssign::$bitassign(self, splinter_ref),
164                    CowSplinter::Owned(splinter) => $BitOpAssign::$bitassign(self, splinter),
165                }
166            }
167        }
168    };
169}
170
171binary_bitop!(BitOr, bitor, BitOrAssign::bitor_assign);
172unary_bitassign!(BitOrAssign, bitor_assign);
173
174binary_bitop!(BitAnd, bitand, BitAndAssign::bitand_assign);
175unary_bitassign!(BitAndAssign, bitand_assign);
176
177binary_bitop!(BitXor, bitxor, BitXorAssign::bitxor_assign);
178unary_bitassign!(BitXorAssign, bitxor_assign);
179
180binary_bitop!(Sub, sub, SubAssign::sub_assign);
181unary_bitassign!(SubAssign, sub_assign);
182
183impl BitOr<&Splinter> for &Splinter {
184    type Output = Splinter;
185    fn bitor(self, rhs: &Splinter) -> Self::Output {
186        // merge into the larger set
187        if rhs.cardinality() > self.cardinality() {
188            let mut result = rhs.clone();
189            result.inner_mut().bitor_assign(self.inner());
190            result
191        } else {
192            let mut result = self.clone();
193            result.inner_mut().bitor_assign(rhs.inner());
194            result
195        }
196    }
197}
198
199impl BitOr<Splinter> for &Splinter {
200    type Output = Splinter;
201    fn bitor(self, rhs: Splinter) -> Self::Output {
202        rhs | self
203    }
204}
205
206impl BitOrAssign<Splinter> for Splinter {
207    fn bitor_assign(&mut self, mut rhs: Splinter) {
208        // merge into the larger set
209        if rhs.cardinality() > self.cardinality() {
210            mem::swap(self, &mut rhs);
211        }
212        self.inner_mut().bitor_assign(rhs.inner())
213    }
214}
215
216impl BitAnd<&Splinter> for &Splinter {
217    type Output = Splinter;
218    fn bitand(self, rhs: &Splinter) -> Self::Output {
219        // intersect into the smaller set
220        if rhs.cardinality() < self.cardinality() {
221            let mut result = rhs.clone();
222            result.inner_mut().bitand_assign(self.inner());
223            result
224        } else {
225            let mut result = self.clone();
226            result.inner_mut().bitand_assign(rhs.inner());
227            result
228        }
229    }
230}
231
232impl BitAnd<Splinter> for &Splinter {
233    type Output = Splinter;
234    fn bitand(self, rhs: Splinter) -> Self::Output {
235        rhs & self
236    }
237}
238
239impl BitAndAssign<Splinter> for Splinter {
240    fn bitand_assign(&mut self, mut rhs: Splinter) {
241        // intersect into the smaller set
242        if rhs.cardinality() < self.cardinality() {
243            mem::swap(self, &mut rhs);
244        }
245        self.inner_mut().bitand_assign(rhs.inner())
246    }
247}
248
249impl BitXor<&Splinter> for &Splinter {
250    type Output = Splinter;
251    fn bitxor(self, rhs: &Splinter) -> Self::Output {
252        let mut result = self.clone();
253        result.inner_mut().bitxor_assign(rhs.inner());
254        result
255    }
256}
257
258impl BitXor<Splinter> for &Splinter {
259    type Output = Splinter;
260    fn bitxor(self, rhs: Splinter) -> Self::Output {
261        rhs ^ self
262    }
263}
264
265impl BitXorAssign<Splinter> for Splinter {
266    fn bitxor_assign(&mut self, rhs: Splinter) {
267        self.inner_mut().bitxor_assign(rhs.inner())
268    }
269}
270
271impl Sub<&Splinter> for &Splinter {
272    type Output = Splinter;
273    fn sub(self, rhs: &Splinter) -> Self::Output {
274        let mut result = self.clone();
275        result.inner_mut().sub_assign(rhs.inner());
276        result
277    }
278}
279
280impl Sub<Splinter> for &Splinter {
281    type Output = Splinter;
282    fn sub(self, rhs: Splinter) -> Self::Output {
283        self - &rhs
284    }
285}
286
287impl SubAssign<Splinter> for Splinter {
288    fn sub_assign(&mut self, rhs: Splinter) {
289        self.inner_mut().sub_assign(rhs.inner())
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use std::ops::{
296        BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Sub, SubAssign,
297    };
298
299    use itertools::Itertools;
300    use proptest::collection::{hash_set, vec};
301    use proptest::proptest;
302
303    use crate::testutil::{mksplinter_cow, mksplinter_ref};
304    use crate::{Optimizable, Splinter, testutil::mksplinter, traits::Cut};
305
306    macro_rules! exercise_bitop {
307        ($a:expr, $b:expr, $seta:expr, $setb:expr, $op_method:ident, $hashset_method:ident) => {
308            let expected: Splinter = $seta.$hashset_method(&$setb).copied().collect();
309
310            assert_eq!((&$a).$op_method(&$b), expected, "&a, &b");
311            assert_eq!((&$a).$op_method($b.clone()), expected, "&a, b");
312            assert_eq!($a.clone().$op_method(&$b), expected, "a, &b");
313            assert_eq!($a.clone().$op_method($b.clone()), expected, "a, b");
314        };
315    }
316
317    macro_rules! exercise_bitop_assign {
318        ($a:expr, $b:expr, $seta:expr, $setb:expr, $op_assign_method:ident, $hashset_method:ident) => {
319            let expected: Splinter = $seta.$hashset_method(&$setb).copied().collect();
320
321            let mut c = $a.clone();
322            c.$op_assign_method($b.clone());
323            assert_eq!(c, expected, "c assign b");
324
325            let mut c = $a.clone();
326            c.$op_assign_method(&$b);
327            assert_eq!(c, expected, "c assign &b");
328        };
329    }
330
331    macro_rules! gen_bitop_test {
332        ($test_name:ident, $make_a:path, $make_b:path) => {
333            proptest! {
334                #[test]
335                fn $test_name(
336                    seta in hash_set(0u32..16384, 0..1024),
337                    setb in hash_set(0u32..16384, 0..1024),
338                ) {
339                    let a = $make_a(seta.clone());
340                    let b = $make_b(setb.clone());
341                    exercise_bitop!(a, b, seta, setb, bitor, union);
342                    exercise_bitop!(a, b, seta, setb, bitand, intersection);
343                    exercise_bitop!(a, b, seta, setb, bitxor, symmetric_difference);
344                    exercise_bitop!(a, b, seta, setb, sub, difference);
345                }
346            }
347        };
348    }
349
350    macro_rules! gen_bitop_assign_test {
351        ($test_name:ident, $make_b:path) => {
352            proptest! {
353                #[test]
354                fn $test_name(
355                    optimize: bool,
356                    seta in hash_set(0u32..16384, 0..1024),
357                    setb in hash_set(0u32..16384, 0..1024),
358                ) {
359                    let mut a = Splinter::from_iter(seta.clone());
360                    let b = $make_b(setb.clone());
361                    if optimize {
362                        a.optimize();
363                    }
364                    exercise_bitop_assign!(a, b, seta, setb, bitor_assign, union);
365                    exercise_bitop_assign!(a, b, seta, setb, bitand_assign, intersection);
366                    exercise_bitop_assign!(a, b, seta, setb, bitxor_assign, symmetric_difference);
367                    exercise_bitop_assign!(a, b, seta, setb, sub_assign, difference);
368                }
369            }
370        };
371    }
372
373    gen_bitop_test!(test_ops_s_s, Splinter::from_iter, Splinter::from_iter);
374    gen_bitop_test!(test_ops_s_sr, Splinter::from_iter, mksplinter_ref);
375    gen_bitop_test!(test_ops_s_sc, Splinter::from_iter, mksplinter_cow);
376
377    gen_bitop_test!(test_ops_sr_sr, mksplinter_ref, mksplinter_ref);
378    gen_bitop_test!(test_ops_sr_sc, mksplinter_ref, mksplinter_cow);
379
380    gen_bitop_test!(test_ops_sc_s, mksplinter_cow, Splinter::from_iter);
381    gen_bitop_test!(test_ops_sc_sr, mksplinter_cow, mksplinter_ref);
382
383    gen_bitop_assign_test!(test_ops_splinter_splinter_assign, Splinter::from_iter);
384    gen_bitop_assign_test!(test_ops_splinter_splinterref_assign, mksplinter_ref);
385    gen_bitop_assign_test!(test_ops_splinter_splintercow_assign, mksplinter_cow);
386
387    proptest! {
388        #[test]
389        fn test_splinter_equality_proptest(values in vec(0u32..16384, 0..1024)) {
390            let mut a = mksplinter(&values);
391            a.optimize();
392            let b = mksplinter(&values);
393            assert!(a == b)
394        }
395
396        #[test]
397        fn test_splinter_equality_ref_proptest(values in vec(0u32..16384, 0..1024)) {
398            let mut a = mksplinter(&values);
399            a.optimize();
400            let b = mksplinter(&values).encode_to_splinter_ref();
401            assert!(a == b)
402        }
403
404        #[test]
405        fn test_splinter_equality_proptest_2(
406            a in vec(0u32..16384, 0..1024),
407            b in vec(0u32..16384, 0..1024),
408        ) {
409            let expected = itertools::equal(a.iter().sorted().dedup(), b.iter().sorted().dedup());
410
411            let mut a = mksplinter(&a);
412            a.optimize();
413            let b = mksplinter(&b);
414
415            assert!((a == b) == expected)
416        }
417
418        #[test]
419        fn test_splinter_equality_ref_proptest_2(
420            a in vec(0u32..16384, 0..1024),
421            b in vec(0u32..16384, 0..1024),
422        ) {
423            let expected = itertools::equal(a.iter().sorted().dedup(), b.iter().sorted().dedup());
424
425            let mut a = mksplinter(&a);
426            a.optimize();
427            let b = mksplinter(&b).encode_to_splinter_ref();
428
429            assert!((a == b) == expected)
430        }
431
432        #[test]
433        fn test_bitor_assign_proptest(
434            optimize: bool,
435            a in hash_set(0u32..16384, 0..1024),
436            b in hash_set(0u32..16384, 0..1024),
437        ) {
438            let mut set: Splinter = a.iter().copied().collect();
439            let other: Splinter = b.iter().copied().collect();
440
441            if optimize {
442                set.optimize();
443            }
444
445            let expected: Splinter = a.union(&b).copied().collect();
446            set |= other;
447            assert!(set == expected)
448        }
449
450        #[test]
451        fn test_cut_proptest(
452            optimize: bool,
453            a in hash_set(0u32..16384, 0..1024),
454            b in hash_set(0u32..16384, 0..1024),
455        ) {
456            let mut source: Splinter = a.iter().copied().collect();
457            let other: Splinter = b.iter().copied().collect();
458
459            if optimize {
460                source.optimize();
461            }
462
463            let expected_intersection: Splinter = a.intersection(&b).copied().collect();
464            let expected_remaining: Splinter = a.difference(&b).copied().collect();
465
466            let actual_intersection = source.cut(&other);
467
468            assert_eq!(actual_intersection,expected_intersection);
469            assert_eq!(source,expected_remaining);
470        }
471
472        #[test]
473        fn test_bitor_ref_proptest(
474            optimize: bool,
475            a in hash_set(0u32..16384, 0..1024),
476            b in hash_set(0u32..16384, 0..1024),
477        ) {
478            let mut set: Splinter = a.iter().copied().collect();
479            let other_ref = Splinter::from_iter(b.clone()).encode_to_splinter_ref();
480
481            if optimize {
482                set.optimize();
483            }
484
485            let expected: Splinter = a.union(&b).copied().collect();
486            set |= other_ref;
487            assert!(set == expected)
488        }
489
490        #[test]
491        fn test_cut_ref_proptest(
492            optimize: bool,
493            a in hash_set(0u32..16384, 0..1024),
494            b in hash_set(0u32..16384, 0..1024),
495        ) {
496            let mut source: Splinter = a.iter().copied().collect();
497            let other_ref = Splinter::from_iter(b.clone()).encode_to_splinter_ref();
498
499            if optimize {
500                source.optimize();
501            }
502
503            let expected_intersection: Splinter = a.intersection(&b).copied().collect();
504            let expected_remaining: Splinter = a.difference(&b).copied().collect();
505
506            let actual_intersection = source.cut(&other_ref);
507
508            assert_eq!(actual_intersection,expected_intersection);
509            assert_eq!(source,expected_remaining);
510        }
511    }
512}