1#![no_std]
17
18use core::{
19 borrow::Borrow,
20 fmt::{self, Display},
21};
22use num_traits::{float::FloatCore, NumRef};
23
24#[doc(hidden)]
26#[allow(clippy::too_many_arguments)]
27pub fn assert_within_add_impl<N: Display + FloatCore>(
28 file: &'static str,
29 line: u32,
30 val: impl Borrow<N>,
31 val_str: &'static str,
32 target: impl Borrow<N>,
33 target_str: &'static str,
34 eps: impl Borrow<N>,
35 context: fmt::Arguments,
36) {
37 let val = val.borrow();
38 let target = target.borrow();
39 let eps = eps.borrow();
40
41 if eps.is_nan() {
42 panic!("assert_within failed at {file}:{line}:\nepsilon was Nan: {eps}\n{context}");
43 }
44
45 if *eps < N::zero() {
46 panic!(
47 "assert_within failed at {file}:{line}:\nEpsilon cannot be negative when used with assert_within! macro: {eps}\n{context}"
48 )
49 }
50
51 if val.is_nan() {
52 panic!("assert_within failed at {file}:{line}:\n`{val_str}` was Nan: {val}\n{context}");
53 }
54
55 if target.is_nan() {
56 panic!(
57 "assert_within failed at {file}:{line}:\n`{target_str}` was Nan: {target}\n{context}"
58 );
59 }
60
61 if *val < *target - *eps {
62 panic!(
63 "assert_within failed at {file}:{line}:\n`{val_str}` was less than `{target_str}` - {eps})\nleft: {val}\nright: {target}\n{context}"
64 );
65 }
66
67 if *val > *target + *eps {
68 panic!(
69 "assert_within failed at {file}:{line}:\n`{val_str}` was greater than `{target_str}` + {eps})\nleft: {val}\nright: {target}\n{context}"
70 );
71 }
72}
73
74#[doc(hidden)]
76#[allow(clippy::too_many_arguments)]
77pub fn assert_within_mul_impl<N: NumRef + Display + FloatCore>(
78 file: &'static str,
79 line: u32,
80 val: impl Borrow<N>,
81 val_str: &'static str,
82 target: impl Borrow<N>,
83 target_str: &'static str,
84 eps: impl Borrow<N>,
85 context: fmt::Arguments,
86) {
87 let val = val.borrow();
88 let target = target.borrow();
89 let eps = eps.borrow();
90
91 if eps.is_nan() {
92 panic!("assert_within failed at {file}:{line}:\nepsilon was Nan: {eps}\n{context}");
93 }
94
95 if *eps < N::zero() {
96 panic!(
97 "assert_within failed at {file}:{line}:\nEpsilon cannot be negative when used with assert_within! macro: {eps}\n{context}"
98 )
99 }
100
101 if val.is_nan() {
102 panic!("assert_within failed at {file}:{line}:\n`{val_str}` was Nan: {val}\n{context}");
103 }
104
105 if target.is_nan() {
106 panic!(
107 "assert_within failed at {file}:{line}:\n`{target_str}` was Nan: {target}\n{context}"
108 );
109 }
110
111 let one_minus_eps = N::one() - *eps;
112 if *val < one_minus_eps * target {
113 panic!(
114 "assert_within failed at {file}:{line}:\n`{val_str}` was less than (1 ± {eps}) * `{target_str}`\nleft: {val}\nright: {target}\n{context}"
115 );
116 }
117
118 let one_plus_eps = N::one() + *eps;
119 if *val > one_plus_eps * target {
120 panic!(
121 "assert_within failed at {file}:{line}:\n`{val_str}` was greater than (1 ± {eps}) * `{target_str}`\nleft: {val}\nright: {target}\n{context}"
122 );
123 }
124}
125
126#[macro_export]
127macro_rules! assert_within {
128 (+ $epsilon:expr, $val:expr, $target:expr) => {
129 $crate::test_utils::assert_within_add_impl(
130 file!(),
131 line!(),
132 $val,
133 stringify!($val),
134 $target,
135 stringify!($target),
136 $epsilon,
137 format_args!(""),
138 )
139 };
140
141 (+ $epsilon:expr, $val:expr, $target:expr, $($fmt_args:tt)*) => {
142 $crate::test_utils::assert_within_add_impl(
143 file!(),
144 line!(),
145 $val,
146 stringify!($val),
147 $target,
148 stringify!($target),
149 $epsilon,
150 format_args!($($fmt_args)*),
151 )
152 };
153
154 (~ $epsilon:expr, $val:expr, $target:expr) => {
155 $crate::test_utils::assert_within_mul_impl(
156 file!(),
157 line!(),
158 $val,
159 stringify!($val),
160 $target,
161 stringify!($target),
162 $epsilon,
163 format_args!(""),
164 )
165 };
166
167 (~ $epsilon:expr, $val:expr, $target:expr, $($fmt_args:tt)*) => {
168 $crate::test_utils::assert_within_mul_impl(
169 file!(),
170 line!(),
171 $val,
172 stringify!($val),
173 $target,
174 stringify!($target),
175 $epsilon,
176 format_args!($($fmt_args)*),
177 )
178 };
179}