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