1use crate::ring::Ring;
2
3pub 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}