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}