stillwater/
semigroup.rs

1//! Semigroup trait for associative operations
2//!
3//! A Semigroup is a type with an associative binary operation. This trait is fundamental
4//! for error accumulation in validation scenarios, where we want to combine multiple errors
5//! together rather than short-circuiting on the first error.
6//!
7//! # Mathematical Properties
8//!
9//! For a type to be a valid Semigroup, the `combine` operation must be associative:
10//! ```text
11//! a.combine(b).combine(c) == a.combine(b.combine(c))
12//! ```
13//!
14//! # Examples
15//!
16//! ```
17//! use stillwater::Semigroup;
18//!
19//! // Combining vectors
20//! let v1 = vec![1, 2, 3];
21//! let v2 = vec![4, 5, 6];
22//! assert_eq!(v1.combine(v2), vec![1, 2, 3, 4, 5, 6]);
23//!
24//! // Combining strings
25//! let s1 = "Hello, ".to_string();
26//! let s2 = "World!".to_string();
27//! assert_eq!(s1.combine(s2), "Hello, World!");
28//!
29//! // Combining tuples component-wise
30//! let t1 = (vec![1], "a".to_string());
31//! let t2 = (vec![2], "b".to_string());
32//! assert_eq!(t1.combine(t2), (vec![1, 2], "ab".to_string()));
33//! ```
34//!
35//! # Custom Implementations
36//!
37//! You can implement Semigroup for your own types:
38//!
39//! ```
40//! use stillwater::Semigroup;
41//!
42//! #[derive(Debug, PartialEq)]
43//! struct ValidationErrors(Vec<String>);
44//!
45//! impl Semigroup for ValidationErrors {
46//!     fn combine(mut self, other: Self) -> Self {
47//!         self.0.extend(other.0);
48//!         self
49//!     }
50//! }
51//! ```
52
53/// A type that supports an associative binary operation
54///
55/// # Laws
56///
57/// Implementations must satisfy the associativity law:
58/// ```text
59/// a.combine(b).combine(c) == a.combine(b.combine(c))
60/// ```
61///
62/// # Note on Ownership
63///
64/// The `combine` method takes `self` by value, not by reference. If you need to
65/// preserve the original values, you must clone them before combining.
66pub trait Semigroup: Sized {
67    /// Combine this value with another value associatively
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use stillwater::Semigroup;
73    ///
74    /// let v1 = vec![1, 2];
75    /// let v2 = vec![3, 4];
76    /// let result = v1.combine(v2);
77    /// assert_eq!(result, vec![1, 2, 3, 4]);
78    /// ```
79    fn combine(self, other: Self) -> Self;
80}
81
82// Implementation for Vec<T>
83impl<T> Semigroup for Vec<T> {
84    #[inline]
85    fn combine(mut self, other: Self) -> Self {
86        self.extend(other);
87        self
88    }
89}
90
91// Implementation for String
92impl Semigroup for String {
93    #[inline]
94    fn combine(mut self, other: Self) -> Self {
95        self.push_str(&other);
96        self
97    }
98}
99
100// Macro for generating tuple implementations
101macro_rules! impl_semigroup_tuple {
102    ($($idx:tt $T:ident),+) => {
103        impl<$($T: Semigroup),+> Semigroup for ($($T,)+) {
104            #[inline]
105            fn combine(self, other: Self) -> Self {
106                (
107                    $(self.$idx.combine(other.$idx)),+
108                )
109            }
110        }
111    };
112}
113
114// Generate implementations for tuples of size 2 through 12
115impl_semigroup_tuple!(0 T1, 1 T2);
116impl_semigroup_tuple!(0 T1, 1 T2, 2 T3);
117impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4);
118impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5);
119impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6);
120impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7);
121impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8);
122impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9);
123impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10);
124impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11);
125impl_semigroup_tuple!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11, 11 T12);
126
127// Implementation for HashMap<K, V>
128use std::collections::HashMap;
129use std::hash::Hash;
130
131/// Semigroup for HashMap that merges maps, combining values with the same key.
132///
133/// When keys conflict, values are combined using their Semigroup instance.
134///
135/// # Example
136///
137/// ```
138/// use std::collections::HashMap;
139/// use stillwater::Semigroup;
140///
141/// let mut map1 = HashMap::new();
142/// map1.insert("errors", vec!["error1"]);
143/// map1.insert("warnings", vec!["warn1"]);
144///
145/// let mut map2 = HashMap::new();
146/// map2.insert("errors", vec!["error2"]);
147/// map2.insert("info", vec!["info1"]);
148///
149/// let combined = map1.combine(map2);
150/// // Result:
151/// // {
152/// //   "errors": ["error1", "error2"],  // Combined
153/// //   "warnings": ["warn1"],            // From map1
154/// //   "info": ["info1"]                 // From map2
155/// // }
156/// ```
157impl<K, V> Semigroup for HashMap<K, V>
158where
159    K: Eq + Hash + Clone,
160    V: Semigroup + Clone,
161{
162    fn combine(mut self, other: Self) -> Self {
163        for (key, value) in other {
164            self.entry(key.clone())
165                .and_modify(|existing| {
166                    *existing = existing.clone().combine(value.clone());
167                })
168                .or_insert(value);
169        }
170        self
171    }
172}
173
174// Implementation for HashSet<T>
175use std::collections::HashSet;
176
177/// Semigroup for HashSet using union.
178///
179/// # Example
180///
181/// ```
182/// use std::collections::HashSet;
183/// use stillwater::Semigroup;
184///
185/// let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
186/// let set2: HashSet<_> = [3, 4, 5].iter().cloned().collect();
187///
188/// let combined = set1.combine(set2);
189/// assert_eq!(combined.len(), 5); // {1, 2, 3, 4, 5}
190/// ```
191impl<T> Semigroup for HashSet<T>
192where
193    T: Eq + Hash,
194{
195    fn combine(mut self, other: Self) -> Self {
196        self.extend(other);
197        self
198    }
199}
200
201// Implementation for BTreeMap<K, V>
202use std::collections::BTreeMap;
203
204/// Semigroup for BTreeMap that merges maps, combining values with the same key.
205///
206/// Same semantics as HashMap but maintains ordered keys.
207///
208/// # Example
209///
210/// ```
211/// use std::collections::BTreeMap;
212/// use stillwater::Semigroup;
213///
214/// let mut map1 = BTreeMap::new();
215/// map1.insert("a", vec![1, 2]);
216///
217/// let mut map2 = BTreeMap::new();
218/// map2.insert("a", vec![3, 4]);
219/// map2.insert("b", vec![5]);
220///
221/// let combined = map1.combine(map2);
222/// assert_eq!(combined.get("a"), Some(&vec![1, 2, 3, 4]));
223/// assert_eq!(combined.get("b"), Some(&vec![5]));
224/// ```
225impl<K, V> Semigroup for BTreeMap<K, V>
226where
227    K: Ord + Clone,
228    V: Semigroup + Clone,
229{
230    fn combine(mut self, other: Self) -> Self {
231        for (key, value) in other {
232            self.entry(key.clone())
233                .and_modify(|existing| {
234                    *existing = existing.clone().combine(value.clone());
235                })
236                .or_insert(value);
237        }
238        self
239    }
240}
241
242// Implementation for BTreeSet<T>
243use std::collections::BTreeSet;
244
245/// Semigroup for BTreeSet using union.
246///
247/// Same semantics as HashSet but maintains ordered elements.
248///
249/// # Example
250///
251/// ```
252/// use std::collections::BTreeSet;
253/// use stillwater::Semigroup;
254///
255/// let set1: BTreeSet<_> = [1, 2, 3].iter().cloned().collect();
256/// let set2: BTreeSet<_> = [3, 4, 5].iter().cloned().collect();
257///
258/// let combined = set1.combine(set2);
259/// assert_eq!(combined.len(), 5); // {1, 2, 3, 4, 5}
260/// ```
261impl<T> Semigroup for BTreeSet<T>
262where
263    T: Ord,
264{
265    fn combine(mut self, other: Self) -> Self {
266        self.extend(other);
267        self
268    }
269}
270
271// Implementation for Option<T>
272/// Semigroup for Option that lifts the inner Semigroup.
273///
274/// - `Some(a).combine(Some(b))` = `Some(a.combine(b))`
275/// - `Some(a).combine(None)` = `Some(a)`
276/// - `None.combine(Some(b))` = `Some(b)`
277/// - `None.combine(None)` = `None`
278///
279/// # Example
280///
281/// ```
282/// use stillwater::Semigroup;
283///
284/// let opt1 = Some(vec![1, 2]);
285/// let opt2 = Some(vec![3, 4]);
286/// let result = opt1.combine(opt2);
287/// assert_eq!(result, Some(vec![1, 2, 3, 4]));
288///
289/// let none: Option<Vec<i32>> = None;
290/// let some = Some(vec![1, 2]);
291/// assert_eq!(none.combine(some.clone()), some);
292/// ```
293impl<T> Semigroup for Option<T>
294where
295    T: Semigroup,
296{
297    fn combine(self, other: Self) -> Self {
298        match (self, other) {
299            (Some(a), Some(b)) => Some(a.combine(b)),
300            (Some(a), None) => Some(a),
301            (None, Some(b)) => Some(b),
302            (None, None) => None,
303        }
304    }
305}
306
307// Wrapper types for alternative semantics
308
309/// Wrapper type that keeps the first value (left-biased).
310///
311/// # Example
312///
313/// ```
314/// use stillwater::{First, Semigroup};
315///
316/// let first = First(1).combine(First(2));
317/// assert_eq!(first.0, 1); // Keeps first
318/// ```
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
320pub struct First<T>(pub T);
321
322impl<T> Semigroup for First<T> {
323    fn combine(self, _other: Self) -> Self {
324        self // Always keep first
325    }
326}
327
328/// Wrapper type that keeps the last value (right-biased).
329///
330/// # Example
331///
332/// ```
333/// use stillwater::{Last, Semigroup};
334///
335/// let last = Last(1).combine(Last(2));
336/// assert_eq!(last.0, 2); // Keeps last
337/// ```
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
339pub struct Last<T>(pub T);
340
341impl<T> Semigroup for Last<T> {
342    fn combine(self, other: Self) -> Self {
343        other // Always keep last
344    }
345}
346
347/// Wrapper for set intersection (alternative to union).
348///
349/// # Example
350///
351/// ```
352/// use std::collections::HashSet;
353/// use stillwater::{Intersection, Semigroup};
354///
355/// let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
356/// let set2: HashSet<_> = [2, 3, 4].iter().cloned().collect();
357///
358/// let i1 = Intersection(set1);
359/// let i2 = Intersection(set2);
360/// let result = i1.combine(i2);
361///
362/// let expected: HashSet<_> = [2, 3].iter().cloned().collect();
363/// assert_eq!(result.0, expected); // Intersection
364/// ```
365#[derive(Debug, Clone, PartialEq, Eq)]
366pub struct Intersection<S>(pub S);
367
368impl<T> Semigroup for Intersection<HashSet<T>>
369where
370    T: Eq + Hash + Clone,
371{
372    fn combine(self, other: Self) -> Self {
373        Intersection(self.0.intersection(&other.0).cloned().collect())
374    }
375}
376
377impl<T> Semigroup for Intersection<BTreeSet<T>>
378where
379    T: Ord + Clone,
380{
381    fn combine(self, other: Self) -> Self {
382        Intersection(self.0.intersection(&other.0).cloned().collect())
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use super::*;
389
390    // Unit tests
391    #[test]
392    fn test_vec_semigroup() {
393        let v1 = vec![1, 2, 3];
394        let v2 = vec![4, 5, 6];
395        assert_eq!(v1.combine(v2), vec![1, 2, 3, 4, 5, 6]);
396    }
397
398    #[test]
399    fn test_vec_semigroup_empty() {
400        let v1: Vec<i32> = vec![];
401        let v2 = vec![1, 2, 3];
402        assert_eq!(v1.combine(v2), vec![1, 2, 3]);
403    }
404
405    #[test]
406    fn test_string_semigroup() {
407        let s1 = "Hello, ".to_string();
408        let s2 = "World!".to_string();
409        assert_eq!(s1.combine(s2), "Hello, World!");
410    }
411
412    #[test]
413    fn test_string_semigroup_empty() {
414        let s1 = "".to_string();
415        let s2 = "Hello".to_string();
416        assert_eq!(s1.combine(s2), "Hello");
417    }
418
419    #[test]
420    fn test_tuple_2_semigroup() {
421        let t1 = (vec![1], "a".to_string());
422        let t2 = (vec![2], "b".to_string());
423        assert_eq!(t1.combine(t2), (vec![1, 2], "ab".to_string()));
424    }
425
426    #[test]
427    fn test_tuple_3_semigroup() {
428        let t1 = (vec![1], "a".to_string(), vec!["x".to_string()]);
429        let t2 = (vec![2], "b".to_string(), vec!["y".to_string()]);
430        assert_eq!(
431            t1.combine(t2),
432            (
433                vec![1, 2],
434                "ab".to_string(),
435                vec!["x".to_string(), "y".to_string()]
436            )
437        );
438    }
439
440    // Associativity tests
441    #[test]
442    fn test_vec_associativity() {
443        let a = vec![1, 2];
444        let b = vec![3, 4];
445        let c = vec![5, 6];
446
447        let left = a.clone().combine(b.clone()).combine(c.clone());
448        let right = a.combine(b.combine(c));
449
450        assert_eq!(left, right);
451    }
452
453    #[test]
454    fn test_string_associativity() {
455        let a = "hello".to_string();
456        let b = " ".to_string();
457        let c = "world".to_string();
458
459        let left = a.clone().combine(b.clone()).combine(c.clone());
460        let right = a.combine(b.combine(c));
461
462        assert_eq!(left, right);
463    }
464
465    #[test]
466    fn test_tuple_associativity() {
467        let a = (vec![1], "a".to_string());
468        let b = (vec![2], "b".to_string());
469        let c = (vec![3], "c".to_string());
470
471        let left = a.clone().combine(b.clone()).combine(c.clone());
472        let right = a.combine(b.combine(c));
473
474        assert_eq!(left, right);
475    }
476
477    // Property-based tests would go here if we had proptest/quickcheck
478    // For now, we'll test with multiple concrete examples
479
480    #[test]
481    fn test_vec_multiple_combines() {
482        let result = vec![1].combine(vec![2]).combine(vec![3]).combine(vec![4]);
483        assert_eq!(result, vec![1, 2, 3, 4]);
484    }
485
486    #[test]
487    fn test_string_multiple_combines() {
488        let result = "a"
489            .to_string()
490            .combine("b".to_string())
491            .combine("c".to_string())
492            .combine("d".to_string());
493        assert_eq!(result, "abcd");
494    }
495
496    // Test larger tuples
497    #[test]
498    fn test_large_tuple() {
499        let t1 = (vec![1], "a".to_string(), vec![2], "b".to_string(), vec![3]);
500        let t2 = (vec![4], "c".to_string(), vec![5], "d".to_string(), vec![6]);
501        assert_eq!(
502            t1.combine(t2),
503            (
504                vec![1, 4],
505                "ac".to_string(),
506                vec![2, 5],
507                "bd".to_string(),
508                vec![3, 6],
509            )
510        );
511    }
512
513    // Tests for HashMap
514    #[test]
515    fn test_hashmap_combine() {
516        let mut map1 = HashMap::new();
517        map1.insert("a", vec![1, 2]);
518
519        let mut map2 = HashMap::new();
520        map2.insert("a", vec![3, 4]);
521        map2.insert("b", vec![5]);
522
523        let result = map1.combine(map2);
524        assert_eq!(result.get("a"), Some(&vec![1, 2, 3, 4]));
525        assert_eq!(result.get("b"), Some(&vec![5]));
526    }
527
528    #[test]
529    fn test_hashmap_no_overlap() {
530        let mut map1 = HashMap::new();
531        map1.insert("a", vec![1, 2]);
532
533        let mut map2 = HashMap::new();
534        map2.insert("b", vec![3, 4]);
535
536        let result = map1.combine(map2);
537        assert_eq!(result.get("a"), Some(&vec![1, 2]));
538        assert_eq!(result.get("b"), Some(&vec![3, 4]));
539    }
540
541    #[test]
542    fn test_hashmap_associativity() {
543        let mut a = HashMap::new();
544        a.insert("x", vec![1]);
545
546        let mut b = HashMap::new();
547        b.insert("x", vec![2]);
548
549        let mut c = HashMap::new();
550        c.insert("x", vec![3]);
551
552        let left = a.clone().combine(b.clone()).combine(c.clone());
553        let right = a.combine(b.combine(c));
554
555        assert_eq!(left, right);
556    }
557
558    // Tests for HashSet
559    #[test]
560    fn test_hashset_union() {
561        let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
562        let set2: HashSet<_> = [3, 4, 5].iter().cloned().collect();
563
564        let result = set1.combine(set2);
565        assert_eq!(result.len(), 5);
566        assert!(result.contains(&1));
567        assert!(result.contains(&2));
568        assert!(result.contains(&3));
569        assert!(result.contains(&4));
570        assert!(result.contains(&5));
571    }
572
573    #[test]
574    fn test_hashset_associativity() {
575        let a: HashSet<_> = [1, 2].iter().cloned().collect();
576        let b: HashSet<_> = [2, 3].iter().cloned().collect();
577        let c: HashSet<_> = [3, 4].iter().cloned().collect();
578
579        let left = a.clone().combine(b.clone()).combine(c.clone());
580        let right = a.combine(b.combine(c));
581
582        assert_eq!(left, right);
583    }
584
585    // Tests for BTreeMap
586    #[test]
587    fn test_btreemap_combine() {
588        let mut map1 = BTreeMap::new();
589        map1.insert("a", vec![1, 2]);
590
591        let mut map2 = BTreeMap::new();
592        map2.insert("a", vec![3, 4]);
593        map2.insert("b", vec![5]);
594
595        let result = map1.combine(map2);
596        assert_eq!(result.get("a"), Some(&vec![1, 2, 3, 4]));
597        assert_eq!(result.get("b"), Some(&vec![5]));
598    }
599
600    #[test]
601    fn test_btreemap_associativity() {
602        let mut a = BTreeMap::new();
603        a.insert("x", vec![1]);
604
605        let mut b = BTreeMap::new();
606        b.insert("x", vec![2]);
607
608        let mut c = BTreeMap::new();
609        c.insert("x", vec![3]);
610
611        let left = a.clone().combine(b.clone()).combine(c.clone());
612        let right = a.combine(b.combine(c));
613
614        assert_eq!(left, right);
615    }
616
617    // Tests for BTreeSet
618    #[test]
619    fn test_btreeset_union() {
620        let set1: BTreeSet<_> = [1, 2, 3].iter().cloned().collect();
621        let set2: BTreeSet<_> = [3, 4, 5].iter().cloned().collect();
622
623        let result = set1.combine(set2);
624        assert_eq!(result.len(), 5);
625        assert!(result.contains(&1));
626        assert!(result.contains(&5));
627    }
628
629    #[test]
630    fn test_btreeset_associativity() {
631        let a: BTreeSet<_> = [1, 2].iter().cloned().collect();
632        let b: BTreeSet<_> = [2, 3].iter().cloned().collect();
633        let c: BTreeSet<_> = [3, 4].iter().cloned().collect();
634
635        let left = a.clone().combine(b.clone()).combine(c.clone());
636        let right = a.combine(b.combine(c));
637
638        assert_eq!(left, right);
639    }
640
641    // Tests for Option
642    #[test]
643    fn test_option_semigroup() {
644        let opt1 = Some(vec![1, 2]);
645        let opt2 = Some(vec![3, 4]);
646        assert_eq!(opt1.combine(opt2), Some(vec![1, 2, 3, 4]));
647
648        let none: Option<Vec<i32>> = None;
649        let some = Some(vec![1]);
650        assert_eq!(none.clone().combine(some.clone()), some);
651        assert_eq!(some.clone().combine(none), some);
652    }
653
654    #[test]
655    fn test_option_both_none() {
656        let none1: Option<Vec<i32>> = None;
657        let none2: Option<Vec<i32>> = None;
658        assert_eq!(none1.combine(none2), None);
659    }
660
661    #[test]
662    fn test_option_associativity() {
663        let a = Some(vec![1]);
664        let b = Some(vec![2]);
665        let c = Some(vec![3]);
666
667        let left = a.clone().combine(b.clone()).combine(c.clone());
668        let right = a.combine(b.combine(c));
669
670        assert_eq!(left, right);
671    }
672
673    // Tests for First and Last
674    #[test]
675    fn test_first_last() {
676        assert_eq!(First(1).combine(First(2)), First(1));
677        assert_eq!(Last(1).combine(Last(2)), Last(2));
678    }
679
680    #[test]
681    fn test_first_associativity() {
682        let a = First(1);
683        let b = First(2);
684        let c = First(3);
685
686        let left = a.combine(b).combine(c);
687        let right = a.combine(b.combine(c));
688
689        assert_eq!(left, right);
690    }
691
692    #[test]
693    fn test_last_associativity() {
694        let a = Last(1);
695        let b = Last(2);
696        let c = Last(3);
697
698        let left = a.combine(b).combine(c);
699        let right = a.combine(b.combine(c));
700
701        assert_eq!(left, right);
702    }
703
704    // Tests for Intersection
705    #[test]
706    fn test_intersection_hashset() {
707        let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
708        let set2: HashSet<_> = [2, 3, 4].iter().cloned().collect();
709
710        let i1 = Intersection(set1);
711        let i2 = Intersection(set2);
712        let result = i1.combine(i2);
713
714        let expected: HashSet<_> = [2, 3].iter().cloned().collect();
715        assert_eq!(result.0, expected);
716    }
717
718    #[test]
719    fn test_intersection_btreeset() {
720        let set1: BTreeSet<_> = [1, 2, 3].iter().cloned().collect();
721        let set2: BTreeSet<_> = [2, 3, 4].iter().cloned().collect();
722
723        let i1 = Intersection(set1);
724        let i2 = Intersection(set2);
725        let result = i1.combine(i2);
726
727        let expected: BTreeSet<_> = [2, 3].iter().cloned().collect();
728        assert_eq!(result.0, expected);
729    }
730
731    #[test]
732    fn test_intersection_associativity() {
733        let a: HashSet<_> = [1, 2, 3, 4].iter().cloned().collect();
734        let b: HashSet<_> = [2, 3, 4, 5].iter().cloned().collect();
735        let c: HashSet<_> = [3, 4, 5, 6].iter().cloned().collect();
736
737        let left = Intersection(a.clone())
738            .combine(Intersection(b.clone()))
739            .combine(Intersection(c.clone()));
740        let right = Intersection(a).combine(Intersection(b).combine(Intersection(c)));
741
742        assert_eq!(left.0, right.0);
743    }
744
745    // Property-based tests
746    #[cfg(test)]
747    mod proptests {
748        use super::*;
749        use proptest::prelude::*;
750
751        proptest! {
752            #[test]
753            fn prop_hashmap_associative(
754                a: HashMap<String, Vec<i32>>,
755                b: HashMap<String, Vec<i32>>,
756                c: HashMap<String, Vec<i32>>,
757            ) {
758                let left = a.clone().combine(b.clone()).combine(c.clone());
759                let right = a.combine(b.combine(c));
760                prop_assert_eq!(left, right);
761            }
762
763            #[test]
764            fn prop_hashset_associative(
765                a: HashSet<i32>,
766                b: HashSet<i32>,
767                c: HashSet<i32>,
768            ) {
769                let left = a.clone().combine(b.clone()).combine(c.clone());
770                let right = a.combine(b.combine(c));
771                prop_assert_eq!(left, right);
772            }
773
774            #[test]
775            fn prop_btreemap_associative(
776                a: BTreeMap<String, Vec<i32>>,
777                b: BTreeMap<String, Vec<i32>>,
778                c: BTreeMap<String, Vec<i32>>,
779            ) {
780                let left = a.clone().combine(b.clone()).combine(c.clone());
781                let right = a.combine(b.combine(c));
782                prop_assert_eq!(left, right);
783            }
784
785            #[test]
786            fn prop_btreeset_associative(
787                a: BTreeSet<i32>,
788                b: BTreeSet<i32>,
789                c: BTreeSet<i32>,
790            ) {
791                let left = a.clone().combine(b.clone()).combine(c.clone());
792                let right = a.combine(b.combine(c));
793                prop_assert_eq!(left, right);
794            }
795
796            #[test]
797            fn prop_option_associative(
798                a: Option<Vec<i32>>,
799                b: Option<Vec<i32>>,
800                c: Option<Vec<i32>>,
801            ) {
802                let left = a.clone().combine(b.clone()).combine(c.clone());
803                let right = a.combine(b.combine(c));
804                prop_assert_eq!(left, right);
805            }
806
807            #[test]
808            fn prop_first_associative(a: i32, b: i32, c: i32) {
809                let fa = First(a);
810                let fb = First(b);
811                let fc = First(c);
812                let left = fa.combine(fb).combine(fc);
813                let right = fa.combine(fb.combine(fc));
814                prop_assert_eq!(left, right);
815            }
816
817            #[test]
818            fn prop_last_associative(a: i32, b: i32, c: i32) {
819                let la = Last(a);
820                let lb = Last(b);
821                let lc = Last(c);
822                let left = la.combine(lb).combine(lc);
823                let right = la.combine(lb.combine(lc));
824                prop_assert_eq!(left, right);
825            }
826
827            #[test]
828            fn prop_intersection_associative(
829                a: HashSet<i32>,
830                b: HashSet<i32>,
831                c: HashSet<i32>,
832            ) {
833                let left = Intersection(a.clone())
834                    .combine(Intersection(b.clone()))
835                    .combine(Intersection(c.clone()));
836                let right = Intersection(a).combine(
837                    Intersection(b).combine(Intersection(c))
838                );
839                prop_assert_eq!(left.0, right.0);
840            }
841        }
842    }
843}