Skip to main content

assertables/assert_in/
assert_in_epsilon.rs

1//! Assert a number is within epsilon of another.
2//!
3//! Pseudocode:<br>
4//! | a - b | ≤ ε * min(a, b)
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! let a: i8 = 10;
12//! let b: i8 = 20;
13//! let epsilon: i8 = 1;
14//! assert_in_epsilon!(a, b, epsilon);
15//! ```
16//!
17//! ## Comparisons
18//!
19//! This crate provides macro groups that test approximations and nearness:
20//!
21//! * [`assert_approx_eq`](macro@crate::assert_approx_eq) and
22//!   [`assert_approx_ne`](macro@crate::assert_approx_ne) test the approximate
23//!   equality within 1e-6. The macro name and the approximate value are chosen
24//!   to be similar to the longtime popular rust crate `assert_approx_eq`.
25//!
26//! * [`assert_in_delta`](macro@crate::assert_in_delta) tests the absolute error
27//!   (i.e. delta). This is the magnitude of the difference between the exact
28//!   value and the approximation.
29//!
30//! * [`assert_in_epsilon`](macro@crate::assert_in_epsilon) tests the relative
31//!   error (i.e. epsilon). This is the absolute error divided by the magnitude
32//!   of the exact value. This can be used to compare approximations of numbers
33//!   of wildly differing size.
34//!
35//! Examples:
36//!
37//! * Approximating the number 100 and 103 has an absolute error (delta) of 3
38//!   and a relative error (epsilon) of 0.03.
39//!
40//! * Approximating the number 1,000,000 and 1,000,003 has an absolute error
41//!   (delta) of 3, and a relative error (epsilon) of 0.000003.
42//!
43//! * For many kinds of applications, the relative error is more important than
44//!   the absolute error.
45//!
46//! ## Thanks
47//!
48//! * Thanks to [Ashley Williams](https://github.com/ashleygwilliams) for
49//!   creating and maintaining the `assert_approx_eq` crate.
50//!
51//! * Thanks to [Ryan Davis](https://github.com/zenspider) and Ruby minitest for
52//!   creating and maintaining `assert_in_delta` and `assert_in_epsilon` code.
53//!
54//! # Module macros
55//!
56//! * [`assert_in_epsilon`](macro@crate::assert_in_epsilon)
57//! * [`assert_in_epsilon_as_result`](macro@crate::assert_in_epsilon_as_result)
58//! * [`debug_assert_in_epsilon`](macro@crate::debug_assert_in_epsilon)
59
60/// Assert a number is within epsilon of another.
61///
62/// Pseudocode:<br>
63/// | a - b | ≤ ε * min(a, b)
64///
65/// * If true, return Result `Ok((lhs, rhs))`.
66///
67/// * When false, return [`Err`] with a message and the values of the
68///   expressions with their debug representations.
69///
70/// This macro is useful for runtime checks, such as checking parameters,
71/// or sanitizing inputs, or handling different results in different ways.
72///
73/// # Module macros
74///
75/// * [`assert_in_epsilon`](macro@crate::assert_in_epsilon)
76/// * [`assert_in_epsilon_as_result`](macro@crate::assert_in_epsilon_as_result)
77/// * [`debug_assert_in_epsilon`](macro@crate::debug_assert_in_epsilon)
78///
79#[macro_export]
80macro_rules! assert_in_epsilon_as_result {
81    ($a:expr, $b:expr, $epsilon:expr $(,)?) => {
82        match (&$a, &$b, &$epsilon) {
83            (a, b, epsilon) => {
84                let abs_diff = if (a >= b) { a - b } else { b - a };
85                let min = if (a < b) { a } else { b };
86                let rhs = *epsilon * min;
87                if abs_diff <= rhs {
88                    Ok((abs_diff, rhs))
89                } else {
90                    Err(format!(
91                        concat!(
92                            "assertion failed: `assert_in_epsilon!(a, b, ε)`\n",
93                            "https://docs.rs/assertables/9.8.6/assertables/macro.assert_in_epsilon.html\n",
94                            "                   a label: `{}`,\n",
95                            "                   a debug: `{:?}`,\n",
96                            "                   b label: `{}`,\n",
97                            "                   b debug: `{:?}`,\n",
98                            "                   ε label: `{}`,\n",
99                            "                   ε debug: `{:?}`,\n",
100                            "                 | a - b |: `{:?}`,\n",
101                            "             ε * min(a, b): `{:?}`,\n",
102                            " | a - b | ≤ ε * min(a, b): {}",
103                        ),
104                        stringify!($a),
105                        a,
106                        stringify!($b),
107                        b,
108                        stringify!($epsilon),
109                        epsilon,
110                        abs_diff,
111                        rhs,
112                        false
113                    ))
114                }
115            }
116        }
117    };
118}
119
120#[cfg(test)]
121mod test_assert_in_epsilon_as_result {
122    use std::sync::Once;
123
124    #[test]
125    fn success() {
126        let a: i8 = 10;
127        let b: i8 = 20;
128        let epsilon: i8 = 1;
129        for _ in 0..1 {
130            let actual = assert_in_epsilon_as_result!(a, b, epsilon);
131            assert_eq!(actual.unwrap(), (10, 10));
132        }
133    }
134
135    #[test]
136    fn success_once() {
137        static A: Once = Once::new();
138        fn a() -> i8 {
139            if A.is_completed() {
140                panic!("A.is_completed()")
141            } else {
142                A.call_once(|| {})
143            }
144            10
145        }
146
147        static B: Once = Once::new();
148        fn b() -> i8 {
149            if B.is_completed() {
150                panic!("B.is_completed()")
151            } else {
152                B.call_once(|| {})
153            }
154            11
155        }
156
157        static EPSILON: Once = Once::new();
158        fn epsilon() -> i8 {
159            if EPSILON.is_completed() {
160                panic!("EPSILON.is_completed()")
161            } else {
162                EPSILON.call_once(|| {})
163            }
164            1
165        }
166
167        assert_eq!(A.is_completed(), false);
168        assert_eq!(B.is_completed(), false);
169        assert_eq!(EPSILON.is_completed(), false);
170        let result = assert_in_epsilon_as_result!(a(), b(), epsilon());
171        assert!(result.is_ok());
172        assert_eq!(A.is_completed(), true);
173        assert_eq!(B.is_completed(), true);
174        assert_eq!(EPSILON.is_completed(), true);
175    }
176
177    #[test]
178    fn failure() {
179        let a: i8 = 10;
180        let b: i8 = 30;
181        let epsilon: i8 = 1;
182        let actual = assert_in_epsilon_as_result!(a, b, epsilon);
183        let message = concat!(
184            "assertion failed: `assert_in_epsilon!(a, b, ε)`\n",
185            "https://docs.rs/assertables/9.8.6/assertables/macro.assert_in_epsilon.html\n",
186            "                   a label: `a`,\n",
187            "                   a debug: `10`,\n",
188            "                   b label: `b`,\n",
189            "                   b debug: `30`,\n",
190            "                   ε label: `epsilon`,\n",
191            "                   ε debug: `1`,\n",
192            "                 | a - b |: `20`,\n",
193            "             ε * min(a, b): `10`,\n",
194            " | a - b | ≤ ε * min(a, b): false"
195        );
196        assert_eq!(actual.unwrap_err(), message);
197    }
198}
199
200/// Assert a number is within epsilon of another.
201///
202/// Pseudocode:<br>
203/// | a - b | ≤ ε * min(a, b)
204///
205/// * If true, return `(lhs, rhs)`.
206///
207/// * Otherwise, call [`panic!`] with a message and the values of the
208///   expressions with their debug representations.
209///
210/// # Examples
211///
212/// ```rust
213/// use assertables::*;
214/// # use std::panic;
215///
216/// # fn main() {
217/// let a: i8 = 10;
218/// let b: i8 = 20;
219/// let epsilon: i8 = 1;
220/// assert_in_epsilon!(a, b, epsilon);
221///
222/// # let result = panic::catch_unwind(|| {
223/// // This will panic
224/// let a: i8 = 10;
225/// let b: i8 = 30;
226/// let epsilon: i8 = 1;
227/// assert_in_epsilon!(a, b, epsilon);
228/// # });
229/// // assertion failed: `assert_in_epsilon!(a, b, epsilon)`
230/// // https://docs.rs/assertables/…/assertables/macro.assert_in_epsilon.html
231/// //                    a label: `a`,
232/// //                    a debug: `10`,
233/// //                    b label: `b`,
234/// //                    b debug: `30`,
235/// //                    ε label: `epsilon`,
236/// //                    ε debug: `1`,
237/// //                  | a - b |: `20`,
238/// //              ε * min(a, b): `10`,\n",
239/// //  | a - b | ≤ ε * min(a, b): false"
240/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
241/// # let message = concat!(
242/// #     "assertion failed: `assert_in_epsilon!(a, b, ε)`\n",
243/// #     "https://docs.rs/assertables/9.8.6/assertables/macro.assert_in_epsilon.html\n",
244/// #     "                   a label: `a`,\n",
245/// #     "                   a debug: `10`,\n",
246/// #     "                   b label: `b`,\n",
247/// #     "                   b debug: `30`,\n",
248/// #     "                   ε label: `epsilon`,\n",
249/// #     "                   ε debug: `1`,\n",
250/// #     "                 | a - b |: `20`,\n",
251/// #     "             ε * min(a, b): `10`,\n",
252/// #     " | a - b | ≤ ε * min(a, b): false"
253/// # );
254/// # assert_eq!(actual, message);
255/// # }
256/// ```
257///
258/// The macros `assert_in_delta` and `assert_in_epsilon` can test
259/// approximations:
260///
261/// * For an approximation, the absolute error (i.e. delta) is the magnitude of
262///   the difference between the exact value and the approximation. For this,
263///  use the macro
264///
265/// * For an approximation, the relative error (i.e. epsilon) is the absolute
266///   error divided by the magnitude of the exact value. This can be used to
267///   compare approximations of numbers of wildly differing size.
268///
269/// * For example, approximating the number 1,000 with an absolute error of 3
270///   is, in most applications, much worse than approximating the number
271///   1,000,000 with an absolute error of 3; in the first case the relative
272///   error is 0.003 and in the second it is only 0.000003.
273///
274/// * Thanks to Ruby minitest for the example and documentation.
275///
276/// # Module macros
277///
278/// * [`assert_in_epsilon`](macro@crate::assert_in_epsilon)
279/// * [`assert_in_epsilon_as_result`](macro@crate::assert_in_epsilon_as_result)
280/// * [`debug_assert_in_epsilon`](macro@crate::debug_assert_in_epsilon)
281///
282#[macro_export]
283macro_rules! assert_in_epsilon {
284    ($a:expr, $b:expr, $epsilon:expr $(,)?) => {
285        match $crate::assert_in_epsilon_as_result!($a, $b, $epsilon) {
286            Ok(x) => x,
287            Err(err) => panic!("{}", err),
288        }
289    };
290    ($a:expr, $b:expr, $epsilon:expr, $($message:tt)+) => {
291        match $crate::assert_in_epsilon_as_result!($a, $b, $epsilon) {
292            Ok(x) => x,
293            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
294        }
295    };
296}
297
298#[cfg(test)]
299mod test_assert_in_epsilon {
300    use std::panic;
301
302    #[test]
303    fn success() {
304        let a: i8 = 10;
305        let b: i8 = 20;
306        let epsilon: i8 = 1;
307        for _ in 0..1 {
308            let actual = assert_in_epsilon!(a, b, epsilon);
309            assert_eq!(actual, (10, 10));
310        }
311    }
312
313    #[test]
314    fn failure() {
315        let a: i8 = 10;
316        let b: i8 = 30;
317        let epsilon: i8 = 1;
318        let result = panic::catch_unwind(|| {
319            let _actual = assert_in_epsilon!(a, b, epsilon);
320        });
321        let message = concat!(
322            "assertion failed: `assert_in_epsilon!(a, b, ε)`\n",
323            "https://docs.rs/assertables/9.8.6/assertables/macro.assert_in_epsilon.html\n",
324            "                   a label: `a`,\n",
325            "                   a debug: `10`,\n",
326            "                   b label: `b`,\n",
327            "                   b debug: `30`,\n",
328            "                   ε label: `epsilon`,\n",
329            "                   ε debug: `1`,\n",
330            "                 | a - b |: `20`,\n",
331            "             ε * min(a, b): `10`,\n",
332            " | a - b | ≤ ε * min(a, b): false"
333        );
334        assert_eq!(
335            result
336                .unwrap_err()
337                .downcast::<String>()
338                .unwrap()
339                .to_string(),
340            message
341        );
342    }
343}
344
345/// Assert a number is within epsilon of another.
346///
347/// Pseudocode:<br>
348/// | a - b | ≤ ε * min(a, b)
349///
350/// This macro provides the same statements as [`assert_in_epsilon`](macro.assert_in_epsilon.html),
351/// except this macro's statements are only enabled in non-optimized
352/// builds by default. An optimized build will not execute this macro's
353/// statements unless `-C debug-assertions` is passed to the compiler.
354///
355/// This macro is useful for checks that are too expensive to be present
356/// in a release build but may be helpful during development.
357///
358/// The result of expanding this macro is always type checked.
359///
360/// An unchecked assertion allows a program in an inconsistent state to
361/// keep running, which might have unexpected consequences but does not
362/// introduce unsafety as long as this only happens in safe code. The
363/// performance cost of assertions, however, is not measurable in general.
364/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
365/// after thorough profiling, and more importantly, only in safe code!
366///
367/// This macro is intended to work in a similar way to
368/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
369///
370/// # Module macros
371///
372/// * [`assert_in_epsilon`](macro@crate::assert_in_epsilon)
373/// * [`assert_in_epsilon`](macro@crate::assert_in_epsilon)
374/// * [`debug_assert_in_epsilon`](macro@crate::debug_assert_in_epsilon)
375///
376#[macro_export]
377macro_rules! debug_assert_in_epsilon {
378    ($($arg:tt)*) => {
379        if cfg!(debug_assertions) {
380            $crate::assert_in_epsilon!($($arg)*);
381        }
382    };
383}
384
385#[cfg(test)]
386mod test_debug_assert_in_epsilon {
387    use std::panic;
388
389    #[test]
390    fn success() {
391        let a: i8 = 10;
392        let b: i8 = 20;
393        let epsilon: i8 = 1;
394        for _ in 0..1 {
395            let _actual = debug_assert_in_epsilon!(a, b, epsilon);
396            // assert_eq!(actual, (10, 10));
397        }
398    }
399
400    #[test]
401    fn failure() {
402        let a: i8 = 10;
403        let b: i8 = 30;
404        let epsilon: i8 = 1;
405        let result = panic::catch_unwind(|| {
406            let _actual = debug_assert_in_epsilon!(a, b, epsilon);
407        });
408        let message = concat!(
409            "assertion failed: `assert_in_epsilon!(a, b, ε)`\n",
410            "https://docs.rs/assertables/9.8.6/assertables/macro.assert_in_epsilon.html\n",
411            "                   a label: `a`,\n",
412            "                   a debug: `10`,\n",
413            "                   b label: `b`,\n",
414            "                   b debug: `30`,\n",
415            "                   ε label: `epsilon`,\n",
416            "                   ε debug: `1`,\n",
417            "                 | a - b |: `20`,\n",
418            "             ε * min(a, b): `10`,\n",
419            " | a - b | ≤ ε * min(a, b): false"
420        );
421        assert_eq!(
422            result
423                .unwrap_err()
424                .downcast::<String>()
425                .unwrap()
426                .to_string(),
427            message
428        );
429    }
430}