mathru/algebra/abstr/
abs_diff_eq.rs

1/// The requisite parameters for testing for approximate equality using a
2/// absolute difference based comparison.
3///
4/// This is not normally used directly, rather via the
5/// `assert_abs_diff_{eq|ne}!` and `abs_diff_{eq|ne}!` macros.
6///
7/// # Example
8///
9/// ```rust
10/// use std::f64;
11/// use mathru::algebra::abstr::AbsDiff;
12///
13/// AbsDiff::default().eq(&1.0, &1.0);
14/// AbsDiff::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
15/// ```
16pub struct AbsDiff<A, B = A>
17where
18    A: AbsDiffEq<B> + ?Sized,
19    B: ?Sized,
20{
21    /// The tolerance to use when testing values that are close together.
22    pub epsilon: A::Epsilon,
23}
24
25impl<A, B> Default for AbsDiff<A, B>
26where
27    A: AbsDiffEq<B> + ?Sized,
28    B: ?Sized,
29{
30    fn default() -> AbsDiff<A, B> {
31        AbsDiff {
32            epsilon: A::default_epsilon(),
33        }
34    }
35}
36
37impl<A, B> AbsDiff<A, B>
38where
39    A: AbsDiffEq<B> + ?Sized,
40    B: ?Sized,
41{
42    /// Replace the epsilon value with the one specified.
43    pub fn epsilon(self, epsilon: A::Epsilon) -> AbsDiff<A, B> {
44        AbsDiff { epsilon }
45    }
46
47    /// Perform the equality comparison
48    pub fn eq(self, lhs: &A, rhs: &B) -> bool {
49        A::abs_diff_eq(lhs, rhs, self.epsilon)
50    }
51
52    /// Perform the inequality comparison
53    pub fn ne(self, lhs: &A, rhs: &B) -> bool {
54        A::abs_diff_ne(lhs, rhs, self.epsilon)
55    }
56}
57
58/// Equality that is defined using the absolute difference of two numbers.
59pub trait AbsDiffEq<Rhs = Self>: PartialEq<Rhs>
60where
61    Rhs: ?Sized,
62{
63    /// Used for specifying relative comparisons.
64    type Epsilon;
65
66    /// The default tolerance to use when testing values that are close together.
67    ///
68    /// This is used when no `epsilon` value is supplied to the abs_diff_eq!, relative_eq!,
69    fn default_epsilon() -> Self::Epsilon;
70
71    /// A test for equality that uses the absolute difference to compute the approximate
72    /// equality of two numbers.
73    fn abs_diff_eq(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool;
74
75    /// The inverse of [`AbsDiffEq::abs_diff_eq`].
76    fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool {
77        !Self::abs_diff_eq(self, other, epsilon)
78    }
79}
80
81macro_rules! impl_abs_diff_eq {
82    ($T:ident) => {
83        impl AbsDiffEq for $T {
84            type Epsilon = $T;
85
86            fn default_epsilon() -> $T {
87                $T::EPSILON
88            }
89
90            fn abs_diff_eq(&self, other: &$T, epsilon: $T) -> bool {
91                (if self > other {
92                    self - other
93                } else {
94                    other - self
95                }) <= epsilon
96            }
97        }
98    };
99}
100
101impl_abs_diff_eq!(f32);
102impl_abs_diff_eq!(f64);
103
104/// Approximate equality using the absolute difference.
105#[macro_export]
106macro_rules! abs_diff_eq {
107    ($lhs:expr, $rhs:expr $(, $opt:ident = $val:expr)*) => {
108        $crate::algebra::abstr::AbsDiff::default()$(.$opt($val))*.eq(&$lhs, &$rhs)
109    };
110    ($lhs:expr, $rhs:expr $(, $opt:ident = $val:expr)*,) => {
111        $crate::algebra::abstr::AbsDiff::default()$(.$opt($val))*.eq(&$lhs, &$rhs)
112    };
113}
114
115/// Approximate inequality using the absolute difference.
116#[macro_export]
117macro_rules! abs_diff_ne {
118    ($lhs:expr, $rhs:expr $(, $opt:ident = $val:expr)*) => {
119        $crate::algebra::abstr::AbsDiff::default()$(.$opt($val))*.ne(&$lhs, &$rhs)
120    };
121    ($lhs:expr, $rhs:expr $(, $opt:ident = $val:expr)*,) => {
122        $crate::algebra::abstr::AbsDiff::default()$(.$opt($val))*.ne(&$lhs, &$rhs)
123    };
124}
125
126#[doc(hidden)]
127#[macro_export]
128macro_rules! __assert_approx {
129    ($eq:ident, $given:expr, $expected:expr) => {{
130        let (given, expected) = (&($given), &($expected));
131
132        if !$eq!(*given, *expected) {
133            panic!(
134                "assert_{}!({}, {})
135                left  = {:?}
136                right = {:?}
137                ",
138                stringify!($eq),
139                stringify!($given),
140                stringify!($expected),
141                given, expected,
142            );
143        }
144    }};
145    ($eq:ident, $given:expr, $expected:expr, $($opt:ident = $val:expr),+) => {{
146        let (given, expected) = (&($given), &($expected));
147
148        if !$eq!(*given, *expected, $($opt = $val),+) {
149            panic!(
150"assert_{}!({}, {}, {})
151    left  = {:?}
152    right = {:?}
153",
154                stringify!($eq),
155                stringify!($given),
156                stringify!($expected),
157                stringify!($($opt = $val),+),
158                given, expected,
159            );
160        }
161    }};
162}
163
164/// An assertion that delegates to abs_diff_eq!, and panics with a helpful error on failure.
165#[macro_export(local_inner_macros)]
166macro_rules! assert_abs_diff_eq {
167    ($given:expr, $expected:expr $(, $opt:ident = $val:expr)*) => {
168        __assert_approx!(abs_diff_eq, $given, $expected $(, $opt = $val)*)
169    };
170    ($given:expr, $expected:expr $(, $opt:ident = $val:expr)*,) => {
171        __assert_approx!(abs_diff_eq, $given, $expected $(, $opt = $val)*)
172    };
173}
174
175/// An assertion that delegates to [`abs_diff_ne!`], and panics with a helpful error on failure.
176#[macro_export(local_inner_macros)]
177macro_rules! assert_abs_diff_ne {
178    ($given:expr, $expected:expr $(, $opt:ident = $val:expr)*) => {
179        __assert_approx!(abs_diff_ne, $given, $expected $(, $opt = $val)*)
180    };
181    ($given:expr, $expected:expr $(, $opt:ident = $val:expr)*,) => {
182        __assert_approx!(abs_diff_ne, $given, $expected $(, $opt = $val)*)
183    };
184}