algebra_core/
lib.rs

1#![deny(missing_docs)]
2//! # algebra-core — Core algebraic abstractions
3//!
4//! **Part of the [postbox workspace](../index.html)**
5//!
6//! This crate provides fundamental algebraic structures as Rust
7//! traits:
8//!
9//! - [`Semigroup`]: associative binary operation
10//! - [`Monoid`]: semigroup with identity element
11//! - [`CommutativeMonoid`]: monoid with commutative operation
12//! - [`Group`]: monoid with inverse elements
13//! - [`AbelianGroup`]: commutative group
14//! - [`SemigroupHom`]: structure-preserving map between semigroups
15//! - [`MonoidHom`]: structure-preserving map between monoids
16//! - [`JoinSemilattice`]: associative, commutative, idempotent operation (least upper bound)
17//! - [`BoundedJoinSemilattice`]: join-semilattice with bottom element
18//! - [`MeetSemilattice`]: associative, commutative, idempotent operation (greatest lower bound)
19//! - [`BoundedMeetSemilattice`]: meet-semilattice with top element
20//! - [`Lattice`]: both join and meet operations
21//! - [`BoundedLattice`]: lattice with both bottom and top elements
22//!
23//! ### Recursion schemes
24//!
25//! - [`TypeApp`]: higher-kinded type encoding
26//! - [`Functor`]: functor on one type parameter
27//! - [`Fix`]: least fixed point (μF) for recursive data structures
28//! - [`fold`]: fold / catamorphism for `Fix` (given an F-algebra)
29//!
30//! ## Quick start
31//!
32//! ```rust
33//! use algebra_core::{Semigroup, Monoid, CommutativeMonoid, Sum};
34//!
35//! // Sum<T> is provided by the library for addition monoids
36//! let a = Sum(5);
37//! let b = Sum(3);
38//! let c = a.combine(&b);
39//! assert_eq!(c, Sum(8));
40//! assert_eq!(Sum::<i32>::empty(), Sum(0));
41//! ```
42//!
43//! ## Standard library implementations
44//!
45//! This crate provides implementations for common standard library types:
46//!
47//! ### Sets (union as join, intersection as meet)
48//!
49//! - **[`HashSet<T>`](std::collections::HashSet)**: `JoinSemilattice`,
50//!   `BoundedJoinSemilattice`, `MeetSemilattice`, `Lattice`,
51//!   `Semigroup`, `Monoid`, `CommutativeMonoid`
52//!   - join = union, meet = intersection, bottom = ∅ (no top for arbitrary T)
53//! - **[`BTreeSet<T>`](std::collections::BTreeSet)**: `JoinSemilattice`,
54//!   `BoundedJoinSemilattice`, `MeetSemilattice`, `Lattice`,
55//!   `Semigroup`, `Monoid`, `CommutativeMonoid`
56//!
57//! ### Booleans (OR/AND lattice)
58//!
59//! - **[`bool`]**: `JoinSemilattice`, `BoundedJoinSemilattice`,
60//!   `MeetSemilattice`, `BoundedMeetSemilattice`, `BoundedLattice`
61//!   - join = OR, meet = AND, bottom = false, top = true
62//!
63//! ### Strings (concatenation)
64//!
65//! - **[`String`](String)**: `Semigroup`, `Monoid`
66//!
67//! ### Optional values (lifted monoid/lattice)
68//!
69//! - **[`Option<M>`](Option)** (where `M: Semigroup + Clone`): `Semigroup`
70//! - **[`Option<M>`](Option)** (where `M: Monoid + Clone`): `Monoid`, `Semigroup`
71//! - **[`Option<M>`](Option)** (where `M: CommutativeMonoid + Clone`): `CommutativeMonoid`
72//! - **[`Option<L>`](Option)** (where `L: JoinSemilattice + Clone`):
73//!   `JoinSemilattice`, `BoundedJoinSemilattice`
74//!   - `None` is bottom, `Some(a) ⊔ Some(b) = Some(a ⊔ b)`
75//! - **[`Option<L>`](Option)** (where `L: MeetSemilattice + Clone`):
76//!   `MeetSemilattice`
77//!   - `None ⊓ x = None` (bottom ⊓ anything = bottom)
78//! - **[`Option<L>`](Option)** (where `L: BoundedMeetSemilattice + Clone`):
79//!   `BoundedMeetSemilattice`
80//!   - top = `Some(L::top())`
81//!
82//! ### Tuples (product algebras)
83//!
84//! - **`()`**: All traits (trivial implementations)
85//! - **`(A,)`, `(A, B)`, `(A, B, C)`, `(A, B, C, D)`**:
86//!   - `Semigroup`, `Monoid`, `CommutativeMonoid` (componentwise)
87//!   - `JoinSemilattice`, `BoundedJoinSemilattice` (componentwise)
88//!   - `MeetSemilattice`, `BoundedMeetSemilattice` (componentwise)
89//!
90//! All tuple implementations require component types to implement the
91//! corresponding trait, and operations are applied componentwise.
92
93// Make the current crate visible as `algebra_core` for consistency
94extern crate self as algebra_core;
95
96pub mod fix;
97pub use fix::{fold, Fix, Functor, TypeApp};
98
99/// A **semigroup**: a type with an associative binary operation.
100///
101/// Laws (not enforced by type system):
102///
103/// - **Associative**:
104///   `a.combine(b).combine(c) == a.combine(b.combine(c))`
105///
106/// # Example
107///
108/// ```rust
109/// use algebra_core::Semigroup;
110///
111/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
112/// struct Max(i32);
113///
114/// impl Semigroup for Max {
115///     fn combine(&self, other: &Self) -> Self {
116///         Max(self.0.max(other.0))
117///     }
118/// }
119///
120/// let x = Max(3);
121/// let y = Max(5);
122/// let z = Max(2);
123/// assert_eq!(x.combine(&y).combine(&z), x.combine(&y.combine(&z)));
124/// ```
125pub trait Semigroup: Sized {
126    /// Combine two elements associatively.
127    fn combine(&self, other: &Self) -> Self;
128
129    /// In-place combine.
130    fn combine_assign(&mut self, other: &Self) {
131        *self = self.combine(other);
132    }
133}
134
135/// A **monoid**: a semigroup with an identity element.
136///
137/// Laws (not enforced by type system):
138///
139/// - **Associative**:
140///   `a.combine(b).combine(c) == a.combine(b.combine(c))`
141/// - **Left identity**: `empty().combine(a) == a`
142/// - **Right identity**: `a.combine(empty()) == a`
143///
144/// # Example
145///
146/// ```rust
147/// use algebra_core::{Semigroup, Monoid};
148///
149/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
150/// struct Product(i32);
151///
152/// impl Semigroup for Product {
153///     fn combine(&self, other: &Self) -> Self {
154///         Product(self.0 * other.0)
155///     }
156/// }
157///
158/// impl Monoid for Product {
159///     fn empty() -> Self {
160///         Product(1)
161///     }
162/// }
163///
164/// let x = Product(3);
165/// let y = Product(5);
166/// assert_eq!(x.combine(&y), Product(15));
167/// assert_eq!(Product::empty().combine(&x), x);
168/// assert_eq!(x.combine(&Product::empty()), x);
169/// ```
170pub trait Monoid: Semigroup {
171    /// The identity element.
172    fn empty() -> Self;
173
174    /// Fold an iterator using combine, starting from empty.
175    fn concat<I>(iter: I) -> Self
176    where
177        I: IntoIterator<Item = Self>,
178    {
179        iter.into_iter()
180            .fold(Self::empty(), |acc, x| acc.combine(&x))
181    }
182}
183
184/// A **commutative monoid**: a monoid where combine is commutative.
185///
186/// Laws (not enforced by type system):
187///
188/// - **Associative**:
189///   `a.combine(b).combine(c) == a.combine(b.combine(c))`
190/// - **Commutative**: `a.combine(b) == b.combine(a)`
191/// - **Identity**: `a.combine(empty()) == a == empty().combine(a)`
192///
193/// # Example
194///
195/// ```rust
196/// use algebra_core::{Semigroup, Monoid, CommutativeMonoid};
197///
198/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
199/// struct Sum(i32);
200///
201/// impl Semigroup for Sum {
202///     fn combine(&self, other: &Self) -> Self {
203///         Sum(self.0 + other.0)
204///     }
205/// }
206///
207/// impl Monoid for Sum {
208///     fn empty() -> Self {
209///         Sum(0)
210///     }
211/// }
212///
213/// impl CommutativeMonoid for Sum {}
214///
215/// let x = Sum(3);
216/// let y = Sum(5);
217/// assert_eq!(x.combine(&y), y.combine(&x)); // commutative
218/// ```
219pub trait CommutativeMonoid: Monoid {
220    // Marker trait - laws are documented above
221}
222
223/// A **group**: a monoid where every element has an inverse.
224///
225/// Laws (not enforced by type system):
226///
227/// - **Associative**: `a.combine(b).combine(c) == a.combine(b.combine(c))`
228/// - **Identity**: `a.combine(empty()) == a == empty().combine(a)`
229/// - **Inverse**: `a.combine(a.inverse()) == empty() == a.inverse().combine(a)`
230///
231/// # Example
232///
233/// ```rust
234/// use algebra_core::{Semigroup, Monoid, Group};
235///
236/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
237/// struct AddInt(i32);
238///
239/// impl Semigroup for AddInt {
240///     fn combine(&self, other: &Self) -> Self {
241///         AddInt(self.0 + other.0)
242///     }
243/// }
244///
245/// impl Monoid for AddInt {
246///     fn empty() -> Self {
247///         AddInt(0)
248///     }
249/// }
250///
251/// impl Group for AddInt {
252///     fn inverse(&self) -> Self {
253///         AddInt(-self.0)
254///     }
255/// }
256///
257/// let x = AddInt(5);
258/// let inv = x.inverse();
259/// assert_eq!(x.combine(&inv), AddInt::empty());
260/// ```
261pub trait Group: Monoid {
262    /// Return the inverse of this element.
263    fn inverse(&self) -> Self;
264}
265
266/// An **abelian group** (commutative group): a group where combine is
267/// commutative.
268///
269/// Laws (not enforced by type system):
270///
271/// - **Associative**: `a.combine(b).combine(c) == a.combine(b.combine(c))`
272/// - **Commutative**: `a.combine(b) == b.combine(a)`
273/// - **Identity**: `a.combine(empty()) == a == empty().combine(a)`
274/// - **Inverse**: `a.combine(a.inverse()) == empty() == a.inverse().combine(a)`
275///
276/// Named after mathematician Niels Henrik Abel.
277pub trait AbelianGroup: Group + CommutativeMonoid {
278    // Marker trait - combines Group and CommutativeMonoid
279}
280
281/// A **semigroup homomorphism**: a structure-preserving map between
282/// semigroups.
283///
284/// A homomorphism `f: S → T` preserves the semigroup operation:
285///
286/// Laws (not enforced by type system):
287///
288/// - **Preserve combine**: `f(x.combine(y)) == f(x).combine(f(y))`
289///
290/// # Example
291///
292/// ```rust
293/// use algebra_core::{Semigroup, Monoid, SemigroupHom};
294///
295/// // Wrapper for usize with addition
296/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
297/// struct Sum(usize);
298///
299/// impl Semigroup for Sum {
300///     fn combine(&self, other: &Self) -> Self {
301///         Sum(self.0 + other.0)
302///     }
303/// }
304///
305/// impl Monoid for Sum {
306///     fn empty() -> Self { Sum(0) }
307/// }
308///
309/// // String length is a homomorphism from (String, concat) to (Sum, +)
310/// struct Length;
311///
312/// impl SemigroupHom for Length {
313///     type Source = String;
314///     type Target = Sum;
315///
316///     fn apply(&self, s: &String) -> Sum {
317///         Sum(s.len())
318///     }
319/// }
320///
321/// // Verify: len(s1 + s2) = len(s1) + len(s2)
322/// let len = Length;
323/// let s1 = String::from("hello");
324/// let s2 = String::from("world");
325/// assert_eq!(
326///     len.apply(&s1.clone().combine(&s2)),
327///     len.apply(&s1).combine(&len.apply(&s2))
328/// );
329/// ```
330pub trait SemigroupHom {
331    /// The source semigroup
332    type Source: Semigroup;
333
334    /// The target semigroup
335    type Target: Semigroup;
336
337    /// Apply the homomorphism
338    fn apply(&self, x: &Self::Source) -> Self::Target;
339}
340
341/// Helper trait for explicitly specifying source and target types.
342///
343/// This is a blanket-implemented alias that allows writing
344/// `T: SemigroupHomFromTo<S, T>` instead of
345/// `T: SemigroupHom<Source = S, Target = T>`.
346pub trait SemigroupHomFromTo<S: Semigroup, T: Semigroup>:
347    SemigroupHom<Source = S, Target = T>
348{
349}
350
351impl<H, S, T> SemigroupHomFromTo<S, T> for H
352where
353    H: SemigroupHom<Source = S, Target = T>,
354    S: Semigroup,
355    T: Semigroup,
356{
357}
358
359/// A **monoid homomorphism**: a structure-preserving map between
360/// monoids.
361///
362/// A homomorphism `f: M → N` preserves both the monoid operation and
363/// identity:
364///
365/// Laws (not enforced by type system):
366///
367/// - **Preserve combine**: `f(x.combine(y)) == f(x).combine(f(y))`
368/// - **Preserve identity**: `f(M::empty()) == N::empty()`
369///
370/// # Example
371///
372/// ```rust
373/// use algebra_core::{Monoid, Semigroup, MonoidHom, SemigroupHom};
374/// use std::collections::HashSet;
375///
376/// // Wrapper for usize with addition
377/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
378/// struct Sum(usize);
379///
380/// impl Semigroup for Sum {
381///     fn combine(&self, other: &Self) -> Self {
382///         Sum(self.0 + other.0)
383///     }
384/// }
385///
386/// impl Monoid for Sum {
387///     fn empty() -> Self { Sum(0) }
388/// }
389///
390/// // Set cardinality: a monoid homomorphism from (HashSet, ∪) to
391/// // (Sum, +)
392/// // Note: Exact homomorphism property holds for disjoint unions
393/// struct Cardinality;
394///
395/// impl SemigroupHom for Cardinality {
396///     type Source = HashSet<i32>;
397///     type Target = Sum;
398///
399///     fn apply(&self, s: &HashSet<i32>) -> Sum {
400///         Sum(s.len())
401///     }
402/// }
403///
404/// impl MonoidHom for Cardinality {}
405///
406/// // Verify identity preservation: |∅| = 0
407/// let card = Cardinality;
408/// assert_eq!(card.apply(&HashSet::empty()), Sum::empty());
409/// ```
410pub trait MonoidHom: SemigroupHom {
411    // Source and Target are already constrained to be Semigroups. We
412    // further require them to be Monoids (but Rust doesn't let us
413    // re-constrain associated types, so this is documented in the
414    // laws)
415}
416
417/// Helper trait for explicitly specifying source and target monoids.
418///
419/// This is a blanket-implemented alias that allows writing
420/// `T: MonoidHomFromTo<M, N>` instead of
421/// `T: MonoidHom<Source = M, Target = N>`.
422pub trait MonoidHomFromTo<M: Monoid, N: Monoid>: MonoidHom<Source = M, Target = N> {}
423
424impl<H, M, N> MonoidHomFromTo<M, N> for H
425where
426    H: MonoidHom<Source = M, Target = N>,
427    M: Monoid,
428    N: Monoid,
429{
430}
431
432/// A **join-semilattice**: a type with an associative, commutative,
433/// idempotent binary operation.
434///
435/// Laws (not enforced by type system):
436///
437/// - **Associative**: `a.join(b).join(c) == a.join(b.join(c))`
438/// - **Commutative**: `a.join(b) == b.join(a)`
439/// - **Idempotent**: `a.join(a) == a`
440///
441/// The `join` operation computes the least upper bound (supremum) in the
442/// induced partial order: `x ≤ y` iff `x.join(y) == y`.
443///
444/// # Example
445///
446/// ```rust
447/// use algebra_core::JoinSemilattice;
448/// use std::collections::HashSet;
449///
450/// let a: HashSet<_> = [1, 2].into_iter().collect();
451/// let b: HashSet<_> = [2, 3].into_iter().collect();
452///
453/// // join = union
454/// let c = a.join(&b);
455/// assert_eq!(c, [1, 2, 3].into_iter().collect());
456///
457/// // Idempotent
458/// assert_eq!(a.join(&a), a);
459/// ```
460pub trait JoinSemilattice: Sized {
461    /// The join (least upper bound).
462    fn join(&self, other: &Self) -> Self;
463
464    /// In-place variant.
465    fn join_assign(&mut self, other: &Self) {
466        *self = self.join(other);
467    }
468
469    /// Derived partial order: x ≤ y iff x ∨ y = y.
470    fn leq(&self, other: &Self) -> bool
471    where
472        Self: PartialEq,
473    {
474        self.join(other) == *other
475    }
476
477    /// Join a finite iterator of values. Returns `None` for empty iterators.
478    fn join_all<I>(it: I) -> Option<Self>
479    where
480        I: IntoIterator<Item = Self>,
481    {
482        it.into_iter().reduce(|acc, x| acc.join(&x))
483    }
484}
485
486/// A **bounded join-semilattice**: a join-semilattice with a bottom element.
487///
488/// Laws (not enforced by type system):
489///
490/// - **Associative**: `a.join(b).join(c) == a.join(b.join(c))`
491/// - **Commutative**: `a.join(b) == b.join(a)`
492/// - **Idempotent**: `a.join(a) == a`
493/// - **Identity**: `bottom().join(a) == a == a.join(bottom())`
494///
495/// The bottom element (⊥) is the least element in the partial order.
496///
497/// # Example
498///
499/// ```rust
500/// use algebra_core::{BoundedJoinSemilattice, JoinSemilattice};
501/// use std::collections::HashSet;
502///
503/// let a: HashSet<_> = [1, 2].into_iter().collect();
504///
505/// // bottom = empty set
506/// let bottom = HashSet::<i32>::bottom();
507/// assert!(bottom.is_empty());
508///
509/// // Identity law
510/// assert_eq!(bottom.join(&a), a);
511/// assert_eq!(a.join(&bottom), a);
512/// ```
513pub trait BoundedJoinSemilattice: JoinSemilattice {
514    /// The bottom element of the lattice (⊥).
515    ///
516    /// This is the least element w.r.t. the induced partial order:
517    /// for all `x`, `bottom().join(x) == x`.
518    fn bottom() -> Self;
519
520    /// Join a finite iterator of values, starting from ⊥.
521    ///
522    /// Never returns `None`: an empty iterator produces `bottom()`.
523    fn join_all_from_bottom<I>(it: I) -> Self
524    where
525        I: IntoIterator<Item = Self>,
526    {
527        it.into_iter().fold(Self::bottom(), |acc, x| acc.join(&x))
528    }
529}
530
531/// A **meet-semilattice**: a type with an associative, commutative,
532/// idempotent binary operation that computes greatest lower bounds.
533///
534/// Laws (not enforced by type system):
535///
536/// - **Associative**: `a.meet(b).meet(c) == a.meet(b.meet(c))`
537/// - **Commutative**: `a.meet(b) == b.meet(a)`
538/// - **Idempotent**: `a.meet(a) == a`
539///
540/// The `meet` operation computes the greatest lower bound (infimum) in the
541/// induced partial order: `x ≤ y` iff `x.meet(y) == x`.
542///
543/// # Example
544///
545/// ```rust
546/// use algebra_core::MeetSemilattice;
547/// use std::collections::HashSet;
548///
549/// let a: HashSet<_> = [1, 2, 3].into_iter().collect();
550/// let b: HashSet<_> = [2, 3, 4].into_iter().collect();
551///
552/// // meet = intersection
553/// let c = a.meet(&b);
554/// assert_eq!(c, [2, 3].into_iter().collect());
555///
556/// // Idempotent
557/// assert_eq!(a.meet(&a), a);
558/// ```
559pub trait MeetSemilattice: Sized {
560    /// The meet (greatest lower bound).
561    fn meet(&self, other: &Self) -> Self;
562
563    /// In-place variant.
564    fn meet_assign(&mut self, other: &Self) {
565        *self = self.meet(other);
566    }
567
568    /// Derived partial order: x ≤ y iff x ∧ y = x.
569    fn leq_meet(&self, other: &Self) -> bool
570    where
571        Self: PartialEq,
572    {
573        self.meet(other) == *self
574    }
575
576    /// Meet a finite iterator of values. Returns `None` for empty iterators.
577    fn meet_all<I>(it: I) -> Option<Self>
578    where
579        I: IntoIterator<Item = Self>,
580    {
581        it.into_iter().reduce(|acc, x| acc.meet(&x))
582    }
583}
584
585/// A **bounded meet-semilattice**: a meet-semilattice with a top element.
586///
587/// Laws (not enforced by type system):
588///
589/// - **Associative**: `a.meet(b).meet(c) == a.meet(b.meet(c))`
590/// - **Commutative**: `a.meet(b) == b.meet(a)`
591/// - **Idempotent**: `a.meet(a) == a`
592/// - **Identity**: `top().meet(a) == a == a.meet(top())`
593///
594/// The top element (⊤) is the greatest element in the partial order.
595///
596/// # Example
597///
598/// ```rust
599/// use algebra_core::{BoundedMeetSemilattice, MeetSemilattice};
600///
601/// // For bool: top = true, meet = AND
602/// let a = true;
603/// let top = bool::top();
604/// assert_eq!(top, true);
605/// assert_eq!(top.meet(&a), a);
606/// assert_eq!(a.meet(&top), a);
607/// ```
608pub trait BoundedMeetSemilattice: MeetSemilattice {
609    /// The top element of the lattice (⊤).
610    ///
611    /// This is the greatest element w.r.t. the induced partial order:
612    /// for all `x`, `top().meet(x) == x`.
613    fn top() -> Self;
614
615    /// Meet a finite iterator of values, starting from ⊤.
616    ///
617    /// Never returns `None`: an empty iterator produces `top()`.
618    fn meet_all_from_top<I>(it: I) -> Self
619    where
620        I: IntoIterator<Item = Self>,
621    {
622        it.into_iter().fold(Self::top(), |acc, x| acc.meet(&x))
623    }
624}
625
626/// A **lattice**: a type with both join (least upper bound) and meet
627/// (greatest lower bound) operations.
628///
629/// Laws (not enforced by type system):
630///
631/// - All join-semilattice laws for `join`
632/// - All meet-semilattice laws for `meet`
633/// - **Absorption**: `a.join(a.meet(b)) == a` and `a.meet(a.join(b)) == a`
634///
635/// # Example
636///
637/// ```rust
638/// use algebra_core::{JoinSemilattice, MeetSemilattice, Lattice};
639/// use std::collections::HashSet;
640///
641/// let a: HashSet<_> = [1, 2].into_iter().collect();
642/// let b: HashSet<_> = [2, 3].into_iter().collect();
643///
644/// // join = union, meet = intersection
645/// assert_eq!(a.join(&b), [1, 2, 3].into_iter().collect());
646/// assert_eq!(a.meet(&b), [2].into_iter().collect());
647/// ```
648pub trait Lattice: JoinSemilattice + MeetSemilattice {}
649
650impl<T: JoinSemilattice + MeetSemilattice> Lattice for T {}
651
652/// A **bounded lattice**: a lattice with both bottom and top elements.
653///
654/// Laws (not enforced by type system):
655///
656/// - All bounded join-semilattice laws
657/// - All bounded meet-semilattice laws
658/// - **Absorption**: `a.join(a.meet(b)) == a` and `a.meet(a.join(b)) == a`
659/// - **Complement bounds**: `bottom().meet(a) == bottom()` and `top().join(a) == top()`
660///
661/// # Example
662///
663/// ```rust
664/// use algebra_core::{BoundedJoinSemilattice, BoundedMeetSemilattice, BoundedLattice};
665///
666/// // bool is a bounded lattice
667/// assert_eq!(bool::bottom(), false);
668/// assert_eq!(bool::top(), true);
669/// ```
670pub trait BoundedLattice: BoundedJoinSemilattice + BoundedMeetSemilattice {}
671
672impl<T: BoundedJoinSemilattice + BoundedMeetSemilattice> BoundedLattice for T {}
673
674// Implementations for standard library types
675
676use std::collections::{BTreeSet, HashSet};
677use std::hash::Hash;
678
679// HashSet: join = union
680
681impl<T: Eq + Hash + Clone> JoinSemilattice for HashSet<T> {
682    fn join(&self, other: &Self) -> Self {
683        self.union(other).cloned().collect()
684    }
685}
686
687impl<T: Eq + Hash + Clone> BoundedJoinSemilattice for HashSet<T> {
688    fn bottom() -> Self {
689        HashSet::new()
690    }
691}
692
693impl<T: Eq + Hash + Clone> Semigroup for HashSet<T> {
694    fn combine(&self, other: &Self) -> Self {
695        self.join(other)
696    }
697}
698
699impl<T: Eq + Hash + Clone> Monoid for HashSet<T> {
700    fn empty() -> Self {
701        Self::bottom()
702    }
703}
704
705impl<T: Eq + Hash + Clone> CommutativeMonoid for HashSet<T> {}
706
707impl<T: Eq + Hash + Clone> MeetSemilattice for HashSet<T> {
708    fn meet(&self, other: &Self) -> Self {
709        self.intersection(other).cloned().collect()
710    }
711}
712
713// Note: HashSet does NOT implement BoundedMeetSemilattice because
714// the universal set (top) cannot be represented for arbitrary T.
715
716// BTreeSet: join = union
717
718impl<T: Ord + Clone> JoinSemilattice for BTreeSet<T> {
719    fn join(&self, other: &Self) -> Self {
720        self.union(other).cloned().collect()
721    }
722}
723
724impl<T: Ord + Clone> BoundedJoinSemilattice for BTreeSet<T> {
725    fn bottom() -> Self {
726        BTreeSet::new()
727    }
728}
729
730impl<T: Ord + Clone> Semigroup for BTreeSet<T> {
731    fn combine(&self, other: &Self) -> Self {
732        self.join(other)
733    }
734}
735
736impl<T: Ord + Clone> Monoid for BTreeSet<T> {
737    fn empty() -> Self {
738        Self::bottom()
739    }
740}
741
742impl<T: Ord + Clone> CommutativeMonoid for BTreeSet<T> {}
743
744impl<T: Ord + Clone> MeetSemilattice for BTreeSet<T> {
745    fn meet(&self, other: &Self) -> Self {
746        self.intersection(other).cloned().collect()
747    }
748}
749
750// Note: BTreeSet does NOT implement BoundedMeetSemilattice because
751// the universal set (top) cannot be represented for arbitrary T.
752
753// bool: join = OR, meet = AND
754
755impl JoinSemilattice for bool {
756    fn join(&self, other: &Self) -> Self {
757        *self || *other
758    }
759}
760
761impl BoundedJoinSemilattice for bool {
762    fn bottom() -> Self {
763        false
764    }
765}
766
767impl MeetSemilattice for bool {
768    fn meet(&self, other: &Self) -> Self {
769        *self && *other
770    }
771}
772
773impl BoundedMeetSemilattice for bool {
774    fn top() -> Self {
775        true
776    }
777}
778
779// String: combine = concatenation
780
781impl Semigroup for String {
782    fn combine(&self, other: &Self) -> Self {
783        let mut result = self.clone();
784        result.push_str(other);
785        result
786    }
787}
788
789impl Monoid for String {
790    fn empty() -> Self {
791        String::new()
792    }
793}
794
795// ============================================================
796// Numeric wrappers: Sum and Product
797// ============================================================
798
799/// Wrapper type for the addition monoid.
800///
801/// `Sum<T>` forms a monoid under addition (`+`), making it useful for
802/// accumulating numeric values, counting, or gradient accumulation in
803/// automatic differentiation.
804///
805/// # Examples
806///
807/// ```
808/// use algebra_core::{Monoid, Semigroup, Sum};
809///
810/// let a = Sum(5);
811/// let b = Sum(3);
812/// let c = a.combine(&b);
813/// assert_eq!(c, Sum(8));
814///
815/// assert_eq!(Sum::<i32>::empty(), Sum(0));
816/// ```
817///
818/// # Use in Autodiff
819///
820/// Gradient accumulation in reverse-mode automatic differentiation
821/// is fundamentally `Sum<f64>`:
822///
823/// ```
824/// use algebra_core::{Semigroup, Sum};
825///
826/// let grad1 = Sum(0.5);
827/// let grad2 = Sum(0.3);
828/// let total_grad = grad1.combine(&grad2);
829/// assert_eq!(total_grad.0, 0.8);
830/// ```
831#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
832pub struct Sum<T>(pub T);
833
834impl<T: std::ops::Add<Output = T> + Clone> Semigroup for Sum<T> {
835    fn combine(&self, other: &Self) -> Self {
836        Sum(self.0.clone() + other.0.clone())
837    }
838}
839
840impl<T: std::ops::Add<Output = T> + Clone + num_traits::Zero> Monoid for Sum<T> {
841    fn empty() -> Self {
842        Sum(T::zero())
843    }
844}
845
846impl<T: std::ops::Add<Output = T> + Clone + num_traits::Zero> CommutativeMonoid for Sum<T> {}
847
848/// Wrapper type for the multiplication monoid.
849///
850/// `Product<T>` forms a monoid under multiplication (`*`), useful for
851/// computing products, scaling factors, or combining probabilities.
852///
853/// # Examples
854///
855/// ```
856/// use algebra_core::{Monoid, Semigroup, Product};
857///
858/// let a = Product(5);
859/// let b = Product(3);
860/// let c = a.combine(&b);
861/// assert_eq!(c, Product(15));
862///
863/// assert_eq!(Product::<i32>::empty(), Product(1));
864/// ```
865///
866/// # Combining Probabilities
867///
868/// ```
869/// use algebra_core::{Semigroup, Product};
870///
871/// let prob1 = Product(0.5);
872/// let prob2 = Product(0.5);
873/// let joint_prob = prob1.combine(&prob2);
874/// assert_eq!(joint_prob.0, 0.25);
875/// ```
876#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
877pub struct Product<T>(pub T);
878
879impl<T: std::ops::Mul<Output = T> + Clone> Semigroup for Product<T> {
880    fn combine(&self, other: &Self) -> Self {
881        Product(self.0.clone() * other.0.clone())
882    }
883}
884
885impl<T: std::ops::Mul<Output = T> + Clone + num_traits::One> Monoid for Product<T> {
886    fn empty() -> Self {
887        Product(T::one())
888    }
889}
890
891impl<T: std::ops::Mul<Output = T> + Clone + num_traits::One> CommutativeMonoid for Product<T> {}
892
893// Option: lifted lattice
894
895impl<L: JoinSemilattice + Clone> JoinSemilattice for Option<L> {
896    fn join(&self, other: &Self) -> Self {
897        match (self, other) {
898            (None, x) | (x, None) => x.clone(),
899            (Some(a), Some(b)) => Some(a.join(b)),
900        }
901    }
902}
903
904impl<L: JoinSemilattice + Clone> BoundedJoinSemilattice for Option<L> {
905    fn bottom() -> Self {
906        None
907    }
908}
909
910impl<M: Semigroup + Clone> Semigroup for Option<M> {
911    fn combine(&self, other: &Self) -> Self {
912        match (self, other) {
913            (None, x) | (x, None) => x.clone(),
914            (Some(a), Some(b)) => Some(a.combine(b)),
915        }
916    }
917}
918
919impl<M: Monoid + Clone> Monoid for Option<M> {
920    fn empty() -> Self {
921        None
922    }
923}
924
925impl<M: CommutativeMonoid + Clone> CommutativeMonoid for Option<M> {}
926
927impl<L: MeetSemilattice + Clone> MeetSemilattice for Option<L> {
928    fn meet(&self, other: &Self) -> Self {
929        match (self, other) {
930            (Some(a), Some(b)) => Some(a.meet(b)),
931            // None is bottom, and bottom ⊓ x = bottom
932            _ => None,
933        }
934    }
935}
936
937impl<L: BoundedMeetSemilattice + Clone> BoundedMeetSemilattice for Option<L> {
938    fn top() -> Self {
939        Some(L::top())
940    }
941}
942
943// Unit type
944
945impl JoinSemilattice for () {
946    fn join(&self, _other: &Self) -> Self {}
947}
948
949impl BoundedJoinSemilattice for () {
950    fn bottom() -> Self {}
951}
952
953impl Semigroup for () {
954    fn combine(&self, _other: &Self) -> Self {}
955}
956
957impl Monoid for () {
958    fn empty() -> Self {}
959}
960
961impl CommutativeMonoid for () {}
962
963impl Group for () {
964    fn inverse(&self) -> Self {}
965}
966
967impl AbelianGroup for () {}
968
969impl MeetSemilattice for () {
970    fn meet(&self, _other: &Self) -> Self {}
971}
972
973impl BoundedMeetSemilattice for () {
974    fn top() -> Self {}
975}
976
977// Tuples: product lattices
978
979macro_rules! impl_product_lattice {
980    ( $( $T:ident : $idx:tt ),+ ) => {
981        impl<$( $T ),+> JoinSemilattice for ( $( $T, )+ )
982        where
983            $( $T: JoinSemilattice ),+
984        {
985            fn join(&self, other: &Self) -> Self {
986                (
987                    $( self.$idx.join(&other.$idx), )+
988                )
989            }
990        }
991
992        impl<$( $T ),+> BoundedJoinSemilattice for ( $( $T, )+ )
993        where
994            $( $T: BoundedJoinSemilattice ),+
995        {
996            fn bottom() -> Self {
997                (
998                    $( $T::bottom(), )+
999                )
1000            }
1001        }
1002
1003        impl<$( $T ),+> MeetSemilattice for ( $( $T, )+ )
1004        where
1005            $( $T: MeetSemilattice ),+
1006        {
1007            fn meet(&self, other: &Self) -> Self {
1008                (
1009                    $( self.$idx.meet(&other.$idx), )+
1010                )
1011            }
1012        }
1013
1014        impl<$( $T ),+> BoundedMeetSemilattice for ( $( $T, )+ )
1015        where
1016            $( $T: BoundedMeetSemilattice ),+
1017        {
1018            fn top() -> Self {
1019                (
1020                    $( $T::top(), )+
1021                )
1022            }
1023        }
1024    }
1025}
1026
1027impl_product_lattice!(A:0);
1028impl_product_lattice!(A:0, B:1);
1029impl_product_lattice!(A:0, B:1, C:2);
1030impl_product_lattice!(A:0, B:1, C:2, D:3);
1031
1032// Tuples: product monoids
1033
1034macro_rules! impl_product_monoid {
1035    ( $( $T:ident : $idx:tt ),+ ) => {
1036        impl<$( $T ),+> Semigroup for ( $( $T, )+ )
1037        where
1038            $( $T: Semigroup ),+
1039        {
1040            fn combine(&self, other: &Self) -> Self {
1041                (
1042                    $( self.$idx.combine(&other.$idx), )+
1043                )
1044            }
1045        }
1046
1047        impl<$( $T ),+> Monoid for ( $( $T, )+ )
1048        where
1049            $( $T: Monoid ),+
1050        {
1051            fn empty() -> Self {
1052                (
1053                    $( $T::empty(), )+
1054                )
1055            }
1056        }
1057
1058        impl<$( $T ),+> CommutativeMonoid for ( $( $T, )+ )
1059        where
1060            $( $T: CommutativeMonoid ),+
1061        {
1062        }
1063    }
1064}
1065
1066impl_product_monoid!(A:0);
1067impl_product_monoid!(A:0, B:1);
1068impl_product_monoid!(A:0, B:1, C:2);
1069impl_product_monoid!(A:0, B:1, C:2, D:3);
1070
1071// Re-export derive macros when derive feature is enabled
1072#[cfg(feature = "derive")]
1073pub use algebra_core_derive::{
1074    AbelianGroup, BoundedJoinSemilattice, BoundedMeetSemilattice, CommutativeMonoid, Group,
1075    JoinSemilattice, MeetSemilattice, Monoid, Semigroup,
1076};
1077
1078#[cfg(test)]
1079mod tests {
1080    use super::*;
1081
1082    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1083    struct Sum(i32);
1084
1085    impl Semigroup for Sum {
1086        fn combine(&self, other: &Self) -> Self {
1087            Sum(self.0 + other.0)
1088        }
1089    }
1090
1091    impl Monoid for Sum {
1092        fn empty() -> Self {
1093            Sum(0)
1094        }
1095    }
1096
1097    impl CommutativeMonoid for Sum {}
1098
1099    #[test]
1100    fn semigroup_combine_works() {
1101        let x = Sum(3);
1102        let y = Sum(5);
1103        assert_eq!(x.combine(&y), Sum(8));
1104    }
1105
1106    #[test]
1107    fn semigroup_is_associative() {
1108        let x = Sum(1);
1109        let y = Sum(2);
1110        let z = Sum(3);
1111        assert_eq!(x.combine(&y).combine(&z), x.combine(&y.combine(&z)));
1112    }
1113
1114    #[test]
1115    fn monoid_has_identity() {
1116        let x = Sum(5);
1117        assert_eq!(Sum::empty().combine(&x), x);
1118        assert_eq!(x.combine(&Sum::empty()), x);
1119    }
1120
1121    #[test]
1122    fn monoid_concat_works() {
1123        let values = vec![Sum(1), Sum(2), Sum(3)];
1124        assert_eq!(Sum::concat(values), Sum(6));
1125    }
1126
1127    #[test]
1128    fn monoid_concat_empty_is_identity() {
1129        let empty: Vec<Sum> = vec![];
1130        assert_eq!(Sum::concat(empty), Sum::empty());
1131    }
1132
1133    #[test]
1134    fn commutative_monoid_is_commutative() {
1135        let x = Sum(3);
1136        let y = Sum(5);
1137        assert_eq!(x.combine(&y), y.combine(&x));
1138    }
1139
1140    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1141    struct AddInt(i32);
1142
1143    impl Semigroup for AddInt {
1144        fn combine(&self, other: &Self) -> Self {
1145            AddInt(self.0 + other.0)
1146        }
1147    }
1148
1149    impl Monoid for AddInt {
1150        fn empty() -> Self {
1151            AddInt(0)
1152        }
1153    }
1154
1155    impl CommutativeMonoid for AddInt {}
1156
1157    impl Group for AddInt {
1158        fn inverse(&self) -> Self {
1159            AddInt(-self.0)
1160        }
1161    }
1162
1163    impl AbelianGroup for AddInt {}
1164
1165    #[test]
1166    fn group_has_inverse() {
1167        let x = AddInt(5);
1168        assert_eq!(x.combine(&x.inverse()), AddInt::empty());
1169        assert_eq!(x.inverse().combine(&x), AddInt::empty());
1170    }
1171
1172    #[test]
1173    fn abelian_group_is_commutative() {
1174        let x = AddInt(3);
1175        let y = AddInt(-7);
1176        assert_eq!(x.combine(&y), y.combine(&x));
1177    }
1178
1179    // ============================================================
1180    // Standard library implementations
1181    // ============================================================
1182
1183    #[test]
1184    fn hashset_semigroup_is_union() {
1185        use std::collections::HashSet;
1186        let a: HashSet<_> = [1, 2, 3].into_iter().collect();
1187        let b: HashSet<_> = [3, 4, 5].into_iter().collect();
1188        let result = a.combine(&b);
1189        let expected: HashSet<_> = [1, 2, 3, 4, 5].into_iter().collect();
1190        assert_eq!(result, expected);
1191    }
1192
1193    #[test]
1194    fn hashset_monoid_empty_is_empty_set() {
1195        use std::collections::HashSet;
1196        let empty = HashSet::<i32>::empty();
1197        assert!(empty.is_empty());
1198    }
1199
1200    #[test]
1201    fn hashset_join_semilattice_is_union() {
1202        use std::collections::HashSet;
1203        let a: HashSet<_> = [1, 2].into_iter().collect();
1204        let b: HashSet<_> = [2, 3].into_iter().collect();
1205        assert_eq!(a.join(&b), [1, 2, 3].into_iter().collect());
1206    }
1207
1208    #[test]
1209    fn hashset_bottom_is_empty() {
1210        use std::collections::HashSet;
1211        let bottom = HashSet::<i32>::bottom();
1212        assert!(bottom.is_empty());
1213    }
1214
1215    #[test]
1216    fn btreeset_semigroup_is_union() {
1217        use std::collections::BTreeSet;
1218        let a: BTreeSet<_> = [1, 2, 3].into_iter().collect();
1219        let b: BTreeSet<_> = [3, 4, 5].into_iter().collect();
1220        let result = a.combine(&b);
1221        let expected: BTreeSet<_> = [1, 2, 3, 4, 5].into_iter().collect();
1222        assert_eq!(result, expected);
1223    }
1224
1225    #[test]
1226    fn btreeset_bottom_is_empty() {
1227        use std::collections::BTreeSet;
1228        let bottom = BTreeSet::<i32>::bottom();
1229        assert!(bottom.is_empty());
1230    }
1231
1232    #[test]
1233    fn string_semigroup_is_concatenation() {
1234        let a = String::from("Hello");
1235        let b = String::from(" World");
1236        assert_eq!(a.combine(&b), "Hello World");
1237    }
1238
1239    #[test]
1240    fn string_monoid_empty_is_empty_string() {
1241        assert_eq!(String::empty(), "");
1242    }
1243
1244    #[test]
1245    fn option_semigroup_combines_inner_values() {
1246        let a = Some(Sum(3));
1247        let b = Some(Sum(5));
1248        assert_eq!(a.combine(&b), Some(Sum(8)));
1249    }
1250
1251    #[test]
1252    fn option_semigroup_none_propagates() {
1253        let a: Option<Sum> = None;
1254        let b = Some(Sum(5));
1255        assert_eq!(a.combine(&b), Some(Sum(5)));
1256        assert_eq!(b.combine(&a), Some(Sum(5)));
1257    }
1258
1259    #[test]
1260    fn option_monoid_empty_is_none() {
1261        assert_eq!(Option::<Sum>::empty(), None);
1262    }
1263
1264    #[test]
1265    fn option_join_semilattice_joins_inner() {
1266        use std::collections::HashSet;
1267        let a: Option<HashSet<_>> = Some([1, 2].into_iter().collect());
1268        let b: Option<HashSet<_>> = Some([2, 3].into_iter().collect());
1269        let expected: Option<HashSet<_>> = Some([1, 2, 3].into_iter().collect());
1270        assert_eq!(a.join(&b), expected);
1271    }
1272
1273    #[test]
1274    fn option_bottom_is_none() {
1275        use std::collections::HashSet;
1276        let bottom = Option::<HashSet<i32>>::bottom();
1277        assert_eq!(bottom, None);
1278    }
1279
1280    // ============================================================
1281    // Sum<T> and Product<T> tests
1282    // ============================================================
1283
1284    #[test]
1285    fn sum_wrapper_implements_semigroup() {
1286        use crate::Sum;
1287        let a = Sum(5);
1288        let b = Sum(3);
1289        assert_eq!(a.combine(&b), Sum(8));
1290    }
1291
1292    #[test]
1293    fn sum_wrapper_monoid_empty_is_zero() {
1294        use crate::Sum;
1295        assert_eq!(Sum::<i32>::empty(), Sum(0));
1296    }
1297
1298    #[test]
1299    fn product_wrapper_implements_semigroup() {
1300        use crate::Product;
1301        let a = Product(5);
1302        let b = Product(3);
1303        assert_eq!(a.combine(&b), Product(15));
1304    }
1305
1306    #[test]
1307    fn product_wrapper_monoid_empty_is_one() {
1308        use crate::Product;
1309        assert_eq!(Product::<i32>::empty(), Product(1));
1310    }
1311
1312    // ============================================================
1313    // MeetSemilattice tests
1314    // ============================================================
1315
1316    #[test]
1317    fn hashset_meet_is_intersection() {
1318        use std::collections::HashSet;
1319        let a: HashSet<_> = [1, 2, 3].into_iter().collect();
1320        let b: HashSet<_> = [2, 3, 4].into_iter().collect();
1321        assert_eq!(a.meet(&b), [2, 3].into_iter().collect());
1322    }
1323
1324    #[test]
1325    fn hashset_meet_is_commutative() {
1326        use std::collections::HashSet;
1327        let a: HashSet<_> = [1, 2].into_iter().collect();
1328        let b: HashSet<_> = [2, 3].into_iter().collect();
1329        assert_eq!(a.meet(&b), b.meet(&a));
1330    }
1331
1332    #[test]
1333    fn hashset_is_lattice() {
1334        use std::collections::HashSet;
1335        let a: HashSet<_> = [1, 2].into_iter().collect();
1336        let b: HashSet<_> = [2, 3].into_iter().collect();
1337        // join = union, meet = intersection
1338        assert_eq!(a.join(&b), [1, 2, 3].into_iter().collect());
1339        assert_eq!(a.meet(&b), [2].into_iter().collect());
1340    }
1341
1342    #[test]
1343    fn bool_is_bounded_lattice() {
1344        // join = OR, meet = AND
1345        assert!(true.join(&false));
1346        assert!(!true.meet(&false));
1347        assert!(!bool::bottom());
1348        assert!(bool::top());
1349    }
1350
1351    #[test]
1352    fn bool_absorption_laws() {
1353        // a ∨ (a ∧ b) = a
1354        // a ∧ (a ∨ b) = a
1355        for a in [true, false] {
1356            for b in [true, false] {
1357                assert_eq!(a.join(&a.meet(&b)), a);
1358                assert_eq!(a.meet(&a.join(&b)), a);
1359            }
1360        }
1361    }
1362
1363    #[test]
1364    fn option_meet_with_none_is_none() {
1365        use std::collections::HashSet;
1366        let a: Option<HashSet<_>> = Some([1, 2].into_iter().collect());
1367        let b: Option<HashSet<i32>> = None;
1368        // None is bottom, and bottom ∧ x = bottom
1369        assert_eq!(a.meet(&b), None);
1370        assert_eq!(b.meet(&a), None);
1371    }
1372
1373    #[test]
1374    fn option_meet_with_some_meets_inner() {
1375        use std::collections::HashSet;
1376        let a: Option<HashSet<_>> = Some([1, 2, 3].into_iter().collect());
1377        let b: Option<HashSet<_>> = Some([2, 3, 4].into_iter().collect());
1378        let expected: Option<HashSet<_>> = Some([2, 3].into_iter().collect());
1379        assert_eq!(a.meet(&b), expected);
1380    }
1381
1382    #[test]
1383    fn option_top_is_some_top() {
1384        assert_eq!(Option::<bool>::top(), Some(true));
1385    }
1386
1387    #[test]
1388    fn tuple_meet_is_componentwise() {
1389        let a = (true, false);
1390        let b = (false, true);
1391        assert_eq!(a.meet(&b), (false, false));
1392    }
1393
1394    #[test]
1395    fn tuple_top_is_componentwise() {
1396        assert_eq!(<(bool, bool)>::top(), (true, true));
1397    }
1398
1399    // Tests for derive macros on tuple structs
1400    #[cfg(feature = "derive")]
1401    mod derive_tuple_tests {
1402        use super::*;
1403
1404        #[derive(Clone, Debug, PartialEq, Eq, Semigroup, Monoid)]
1405        struct TupleMonoid(Sum, HashSet<String>);
1406
1407        #[test]
1408        fn tuple_monoid_empty_works() {
1409            let empty = TupleMonoid::empty();
1410            assert_eq!(empty.0, Sum(0));
1411            assert_eq!(empty.1, HashSet::<String>::new());
1412        }
1413
1414        #[test]
1415        fn tuple_semigroup_combine_works() {
1416            let x = TupleMonoid(Sum(3), ["a".to_string()].into_iter().collect());
1417            let y = TupleMonoid(Sum(5), ["b".to_string()].into_iter().collect());
1418            let result = x.combine(&y);
1419            assert_eq!(result.0, Sum(8));
1420            assert_eq!(
1421                result.1,
1422                ["a".to_string(), "b".to_string()].into_iter().collect()
1423            );
1424        }
1425
1426        #[derive(Clone, Debug, PartialEq, Eq, JoinSemilattice, BoundedJoinSemilattice)]
1427        struct TupleLattice(HashSet<i32>, HashSet<String>);
1428
1429        #[test]
1430        fn tuple_lattice_bottom_works() {
1431            let bottom = TupleLattice::bottom();
1432            assert!(bottom.0.is_empty());
1433            assert!(bottom.1.is_empty());
1434        }
1435
1436        #[test]
1437        fn tuple_lattice_join_works() {
1438            let x = TupleLattice(
1439                [1, 2].into_iter().collect(),
1440                ["a".to_string()].into_iter().collect(),
1441            );
1442            let y = TupleLattice(
1443                [2, 3].into_iter().collect(),
1444                ["b".to_string()].into_iter().collect(),
1445            );
1446            let result = x.join(&y);
1447            assert_eq!(result.0, [1, 2, 3].into_iter().collect());
1448            assert_eq!(
1449                result.1,
1450                ["a".to_string(), "b".to_string()].into_iter().collect()
1451            );
1452        }
1453
1454        #[derive(Clone, Copy, Debug, PartialEq, Eq, Semigroup, Monoid, Group)]
1455        struct TupleGroup(AddInt, AddInt);
1456
1457        #[test]
1458        fn tuple_group_inverse_works() {
1459            let x = TupleGroup(AddInt(3), AddInt(5));
1460            let inv = x.inverse();
1461            assert_eq!(inv.0, AddInt(-3));
1462            assert_eq!(inv.1, AddInt(-5));
1463            assert_eq!(x.combine(&inv), TupleGroup::empty());
1464        }
1465    }
1466}