Skip to main content

karpal_algebra/
group.rs

1use karpal_core::Monoid;
2
3/// A `Monoid` where every element has an inverse.
4///
5/// Law: `a.combine(a.invert()) == empty()`
6/// Law: `a.invert().combine(a) == empty()`
7pub trait Group: Monoid {
8    fn invert(self) -> Self;
9
10    fn combine_inverse(self, other: Self) -> Self
11    where
12        Self: Sized,
13    {
14        self.combine(other.invert())
15    }
16}
17
18macro_rules! impl_signed_group {
19    ($($t:ty),*) => {
20        $(
21            impl Group for $t {
22                fn invert(self) -> Self {
23                    -self
24                }
25            }
26        )*
27    };
28}
29
30impl_signed_group!(i8, i16, i32, i64, i128, f32, f64);
31
32impl<A: Group, B: Group> Group for (A, B) {
33    fn invert(self) -> Self {
34        (self.0.invert(), self.1.invert())
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn i32_invert() {
44        assert_eq!(5i32.invert(), -5);
45        assert_eq!((-3i32).invert(), 3);
46    }
47
48    #[test]
49    fn i32_combine_inverse() {
50        assert_eq!(10i32.combine_inverse(3), 7);
51    }
52
53    #[test]
54    fn f64_invert() {
55        assert!((1.5f64.invert() - (-1.5)).abs() < 1e-10);
56    }
57}
58
59#[cfg(test)]
60mod law_tests {
61    use super::*;
62    use karpal_core::Semigroup;
63    use proptest::prelude::*;
64
65    proptest! {
66        #[test]
67        fn left_inverse(a in -100i16..100i16) {
68            prop_assert_eq!(a.invert().combine(a), i16::empty());
69        }
70
71        #[test]
72        fn right_inverse(a in -100i16..100i16) {
73            prop_assert_eq!(a.combine(a.invert()), i16::empty());
74        }
75
76        #[test]
77        fn combine_inverse_is_combine_invert(a in -50i16..50i16, b in -50i16..50i16) {
78            prop_assert_eq!(a.combine_inverse(b), a.combine(b.invert()));
79        }
80    }
81}