Skip to main content

harmos_storage/signal/
aggregates.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17
18// Third Party Imports
19
20// Local Imports
21use super::{Series, Summary};
22use crate::{Aggregate, aggregate};
23
24/* === Constants === */
25
26/* === Macros === */
27
28/* === Type Aliases === */
29
30/* === Structs === */
31
32/* === Enums === */
33
34/* === Traits === */
35
36// One member of the contract vocabulary, answered from a `Series` (every
37// member) or a `Summary` (the mergeable members; median and percentiles are
38// `None`, since no constant-size state can answer them).
39pub trait Aggregates {
40    fn aggregate(&self, kind: Aggregate) -> Option<f64>;
41}
42
43/* === Implementations === */
44
45impl Aggregates for Series<'_> {
46    fn aggregate(&self, kind: Aggregate) -> Option<f64> {
47        aggregate::of_series(self, kind)
48    }
49}
50
51impl Aggregates for Summary {
52    fn aggregate(&self, kind: Aggregate) -> Option<f64> {
53        aggregate::of_summary(self, kind)
54    }
55}
56
57/* === Public Functions === */
58
59/* === Private Functions === */
60
61/* === Tests === */
62
63#[cfg(test)]
64mod tests {
65    use super::{Aggregate, Aggregates, Series, Summary};
66
67    const EVERY: [Aggregate; 11] = [
68        Aggregate::Count,
69        Aggregate::Sum,
70        Aggregate::Mean,
71        Aggregate::Median,
72        Aggregate::Min,
73        Aggregate::Max,
74        Aggregate::Rms,
75        Aggregate::Std,
76        Aggregate::Percentile(0),
77        Aggregate::Percentile(25),
78        Aggregate::Percentile(100),
79    ];
80
81    #[test]
82    fn series_answers_all() {
83        let series = Series::new(&[4.0, f64::NAN, 1.0, 3.0, 2.0]);
84        let mut answers = Vec::new();
85        for kind in EVERY {
86            answers.push(series.aggregate(kind));
87        }
88
89        assert_eq!(
90            answers,
91            vec![
92                Some(4.0),
93                Some(10.0),
94                Some(2.5),
95                Some(2.5),
96                Some(1.0),
97                Some(4.0),
98                series.rms(),
99                series.std(),
100                Some(1.0),
101                Some(1.75),
102                Some(4.0),
103            ]
104        );
105        assert!((series.std().unwrap() - 1.25_f64.sqrt()).abs() < 1e-12);
106        assert!((series.rms().unwrap() - 7.5_f64.sqrt()).abs() < 1e-12);
107    }
108
109    #[test]
110    fn empty_input() {
111        let series = Series::new(&[]);
112        let summary = Summary::new();
113
114        for kind in EVERY {
115            let expected = matches!(kind, Aggregate::Count | Aggregate::Sum).then_some(0.0);
116            assert_eq!(series.aggregate(kind), expected, "{kind:?} of a series");
117            assert_eq!(summary.aggregate(kind), expected, "{kind:?} of a summary");
118        }
119    }
120
121    #[test]
122    fn summary_refuses_order() {
123        let values = [7.5, -2.0, 3.25, 0.0, 11.0, -6.5];
124        let series = Series::new(&values);
125        let summary = Summary::of(&values[..2]).merge(&Summary::of(&values[2..]));
126
127        for kind in EVERY {
128            if !kind.mergeable() {
129                assert_eq!(summary.aggregate(kind), None, "{kind:?} needs the values");
130                assert!(series.aggregate(kind).is_some());
131                continue;
132            }
133            let (exact, merged) = (series.aggregate(kind).unwrap(), summary.aggregate(kind).unwrap());
134            assert!((exact - merged).abs() < 1e-12, "{kind:?}: {exact} and {merged}");
135        }
136    }
137}