average/macros.rs
1/// Assert that two numbers are almost equal to each other.
2///
3/// On panic, this macro will print the values of the expressions with their
4/// debug representations.
5#[macro_export]
6macro_rules! assert_almost_eq {
7 ($a:expr, $b:expr, $prec:expr) => {
8 let diff = ($a - $b).abs();
9 if diff > $prec {
10 panic!(
11 "assertion failed: `abs(left - right) = {:.1e} < {:e}`, \
12 (left: `{}`, right: `{}`)",
13 diff, $prec, $a, $b
14 );
15 }
16 };
17}
18
19/// Concatenate several iterative estimators into one.
20///
21/// `$name` is the name of the new struct. `$statistic` is the name of a
22/// statistic and must exist as a method of the corresponding type `$estimator`.
23/// `$estimator` must have an `add` method for adding new observations to the
24/// sample (taking an `f64` as an argument). It must also implement `Default`.
25///
26/// If the short syntax is used, the fields will be named `$statistic`. Use the
27/// long syntax and `$field` to give them explicit names. The long syntax also
28/// supports calculating several statistics from one estimator.
29///
30/// For moments, only an estimator for the highest moment should be used and
31/// reused for the lower moments (see the example below).
32///
33/// The following methods will be implemented: `new`, `add`, `$statistic`.
34///
35/// The following traits will be implemented: `Default`, `FromIterator<f64>`.
36///
37///
38/// # Examples
39///
40/// ```
41/// use average::{Min, Max, Estimate, concatenate};
42///
43/// concatenate!(MinMax, [Min, min], [Max, max]);
44///
45/// let s: MinMax = (1..6).map(f64::from).collect();
46///
47/// assert_eq!(s.min(), 1.0);
48/// assert_eq!(s.max(), 5.0);
49/// ```
50///
51/// The generated code looks roughly like this:
52///
53/// ```
54/// # use average::{Min, Max, Estimate};
55/// #
56/// struct MinMax {
57/// min: Min,
58/// max: Max,
59/// }
60///
61/// impl MinMax {
62/// pub fn new() -> MinMax {
63/// MinMax { min: Min::default(), max: Max::default() }
64/// }
65///
66/// pub fn add(&mut self, x: f64) {
67/// self.min.add(x);
68/// self.max.add(x);
69/// }
70///
71/// pub fn min(&self) -> f64 {
72/// self.min.min()
73/// }
74///
75/// pub fn max(&self) -> f64 {
76/// self.max.max()
77/// }
78/// }
79/// ```
80///
81/// If you want to calculate the mean, variance and the median in one pass, you
82/// can do the following:
83///
84/// ```ignore
85/// use average::{Variance, Quantile, Estimate, concatenate};
86///
87/// concatenate!(Estimator,
88/// [Variance, variance, mean, sample_variance],
89/// [Quantile, quantile, quantile]);
90/// ```
91#[macro_export]
92macro_rules! concatenate {
93 ( $visibility:vis $name:ident, $([$estimator:ident, $statistic:ident]),+ ) => {
94 concatenate!($visibility $name, $([$estimator, $statistic, $statistic]),* );
95 };
96 ( $visibility:vis $name:ident, $( [$estimator:ident, $field:ident, $($statistic:ident),+] ),+ ) => {
97 $visibility struct $name {
98 $(
99 $field: $estimator,
100 )*
101 }
102
103 impl $name {
104 #[inline]
105 pub fn new() -> $name {
106 $name {
107 $(
108 $field: ::core::default::Default::default(),
109 )*
110 }
111 }
112
113 #[inline]
114 pub fn add(&mut self, x: f64) {
115 $(
116 self.$field.add(x);
117 )*
118 }
119
120 $( $(
121 #[inline]
122 pub fn $statistic(&self) -> f64 {
123 self.$field.$statistic()
124 }
125 )* )*
126 }
127
128 impl Default for $name {
129 fn default() -> $name {
130 $name::new()
131 }
132 }
133
134 $crate::impl_from_iterator!($name);
135
136 // This should be conditionally activated if all fields implement `Merge`.
137 // Could probably be implemented with specialization.
138 /*
139 impl $crate::Merge for $name {
140 #[inline]
141 fn merge(&mut self, other: &Self) {
142 use $crate::Merge;
143 $(
144 self.$field.merge(&other.$field);
145 )*
146 }
147 }
148 */
149 };
150}
151
152/// Implement `FromIterator<f64>` for an iterative estimator.
153#[macro_export]
154macro_rules! impl_from_iterator {
155 ( $name:ident ) => {
156 impl ::core::iter::FromIterator<f64> for $name {
157 fn from_iter<T>(iter: T) -> $name
158 where
159 T: IntoIterator<Item = f64>,
160 {
161 let mut e = $name::new();
162 for i in iter {
163 e.add(i);
164 }
165 e
166 }
167 }
168
169 impl<'a> ::core::iter::FromIterator<&'a f64> for $name {
170 fn from_iter<T>(iter: T) -> $name
171 where
172 T: IntoIterator<Item = &'a f64>,
173 {
174 let mut e = $name::new();
175 for &i in iter {
176 e.add(i);
177 }
178 e
179 }
180 }
181 };
182}
183
184/// Implement `FromParallelIterator<f64>` for an iterative estimator.
185///
186/// This will do nothing unless the `rayon` feature is enabled.
187#[macro_export]
188macro_rules! impl_from_par_iterator {
189 ( $name:ident ) => {
190 #[cfg(feature = "rayon")]
191 impl ::rayon::iter::FromParallelIterator<f64> for $name {
192 fn from_par_iter<I>(par_iter: I) -> $name
193 where
194 I: ::rayon::iter::IntoParallelIterator<Item = f64>,
195 Self: $crate::Merge,
196 {
197 use ::rayon::iter::ParallelIterator;
198 use $crate::Merge;
199
200 let par_iter = par_iter.into_par_iter();
201 par_iter
202 .fold(
203 || $name::new(),
204 |mut e, i| {
205 e.add(i);
206 e
207 },
208 )
209 .reduce(
210 || $name::new(),
211 |mut a, b| {
212 a.merge(&b);
213 a
214 },
215 )
216 }
217 }
218
219 #[cfg(feature = "rayon")]
220 impl<'a> ::rayon::iter::FromParallelIterator<&'a f64> for $name {
221 fn from_par_iter<I>(par_iter: I) -> $name
222 where
223 I: ::rayon::iter::IntoParallelIterator<Item = &'a f64>,
224 Self: $crate::Merge,
225 {
226 use ::rayon::iter::ParallelIterator;
227 use $crate::Merge;
228
229 let par_iter = par_iter.into_par_iter();
230 par_iter
231 .fold(
232 || $name::new(),
233 |mut e, i| {
234 e.add(*i);
235 e
236 },
237 )
238 .reduce(
239 || $name::new(),
240 |mut a, b| {
241 a.merge(&b);
242 a
243 },
244 )
245 }
246 }
247 };
248}
249
250/// Implement `Extend<f64>` for an iterative estimator.
251#[macro_export]
252macro_rules! impl_extend {
253 ( $name:ident ) => {
254 impl ::core::iter::Extend<f64> for $name {
255 fn extend<T>(&mut self, iter: T)
256 where
257 T: IntoIterator<Item = f64>,
258 {
259 for i in iter {
260 self.add(i);
261 }
262 }
263 }
264
265 impl<'a> ::core::iter::Extend<&'a f64> for $name {
266 fn extend<T>(&mut self, iter: T)
267 where
268 T: IntoIterator<Item = &'a f64>,
269 {
270 for &i in iter {
271 self.add(i);
272 }
273 }
274 }
275 };
276}