1use karpal_core::Monoid;
2
3pub 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}