1#![allow(clippy::approx_constant)]
2use approx::AbsDiffEq;
6
7pub const ACC: f64 = 10e-11;
9
10#[must_use]
13pub fn almost_eq(a: f64, b: f64, acc: f64) -> bool {
14 if a.is_infinite() && b.is_infinite() {
15 return a == b;
16 }
17 a.abs_diff_eq(&b, acc)
18}
19
20#[must_use]
23pub fn convergence(x: &mut f64, x_new: f64) -> bool {
24 let res = approx::relative_eq!(*x, x_new, max_relative = ACC);
25 *x = x_new;
26 res
27}
28
29#[macro_export]
30macro_rules! assert_almost_eq {
31 ($a:expr, $b:expr, $prec:expr $(,)?) => {
32 if !$crate::numeric::almost_eq($a, $b, $prec) {
33 panic!(
34 "assertion failed: `abs(left - right) < {:e}`, (left: `{}`, right: `{}`)",
35 $prec, $a, $b
36 );
37 }
38 };
39}
40
41#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn almost_eq_within_tolerance() {
48 let a = 1.0;
49 let b = 1.0 + 0.5e-11;
50 assert!(almost_eq(a, b, ACC));
52 }
53
54 #[test]
55 fn almost_eq_outside_tolerance() {
56 let a = 1.0;
57 let b = 1.0 + 2e-10;
58 assert!(!almost_eq(a, b, ACC));
60 }
61
62 #[test]
63 fn almost_eq_infinities() {
64 assert!(almost_eq(f64::INFINITY, f64::INFINITY, ACC));
65 assert!(almost_eq(f64::NEG_INFINITY, f64::NEG_INFINITY, ACC));
66 assert!(!almost_eq(f64::INFINITY, f64::NEG_INFINITY, ACC));
67 }
68
69 #[test]
70 fn convergence_updates_and_compares() {
71 let mut x = 100.0;
72 assert!(convergence(&mut x, 100.0));
74 assert_eq!(x, 100.0);
76
77 let x_new = x * (1.0 + 0.5 * ACC);
79 assert!(convergence(&mut x, x_new));
80 assert_eq!(x, x_new);
81
82 let x_new2 = x * (1.0 + 2.0 * ACC);
84 assert!(!convergence(&mut x, x_new2));
85 assert_eq!(x, x_new2);
86 }
87
88 #[test]
89 fn assert_almost_eq_macro_passes() {
90 assert_almost_eq!(3.14159265, 3.14159264, 1e-7);
92 }
93
94 #[test]
95 #[should_panic(expected = "assertion failed")]
96 fn assert_almost_eq_macro_panics() {
97 assert_almost_eq!(1.0, 1.001, 1e-4);
99 }
100}