Skip to main content

karpal_algebra/
field.rs

1use crate::ring::Ring;
2
3/// A `Ring` with multiplicative inverses for all non-zero elements.
4pub trait Field: Ring {
5    fn reciprocal(self) -> Self;
6
7    fn div(self, other: Self) -> Self
8    where
9        Self: Sized,
10    {
11        self.mul(other.reciprocal())
12    }
13}
14
15impl Field for f32 {
16    fn reciprocal(self) -> Self {
17        1.0 / self
18    }
19}
20
21impl Field for f64 {
22    fn reciprocal(self) -> Self {
23        1.0 / self
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    macro_rules! assert_approx_eq {
31        ($a:expr, $b:expr) => {
32            assert_approx_eq!($a, $b, 1e-10)
33        };
34        ($a:expr, $b:expr, $eps:expr) => {
35            let (a, b) = ($a as f64, $b as f64);
36            assert!(
37                (a - b).abs() < $eps,
38                "assertion failed: |{} - {}| = {} >= {}",
39                a,
40                b,
41                (a - b).abs(),
42                $eps
43            );
44        };
45    }
46
47    #[test]
48    fn f64_reciprocal() {
49        assert_approx_eq!(2.0f64.reciprocal(), 0.5);
50        assert_approx_eq!(4.0f64.reciprocal(), 0.25);
51    }
52
53    #[test]
54    fn f64_div() {
55        assert_approx_eq!(10.0f64.div(4.0), 2.5);
56    }
57
58    #[test]
59    fn f32_reciprocal() {
60        assert!((2.0f32.reciprocal() - 0.5).abs() < 1e-6);
61    }
62}
63
64#[cfg(test)]
65mod law_tests {
66    use super::*;
67    use crate::semiring::Semiring;
68    use proptest::prelude::*;
69
70    proptest! {
71        #[test]
72        fn f64_multiplicative_inverse(a in proptest::num::f64::NORMAL.prop_filter("non-zero", |x| x.abs() > 1e-10)) {
73            let result = a.mul(a.reciprocal());
74            prop_assert!((result - 1.0).abs() < 1e-6, "a={}, a*a^-1={}", a, result);
75        }
76
77        #[test]
78        fn f64_div_is_mul_reciprocal(
79            a in proptest::num::f64::NORMAL.prop_filter("bounded", |x| x.abs() < 1e6),
80            b in proptest::num::f64::NORMAL.prop_filter("non-zero bounded", |x| x.abs() > 1e-10 && x.abs() < 1e6)
81        ) {
82            let left = a.div(b);
83            let right = a.mul(b.reciprocal());
84            prop_assert!((left - right).abs() < 1e-6, "a/b={}, a*b^-1={}", left, right);
85        }
86    }
87}