composable_indexes/aggregation/
mean.rs

1use num_traits::ToPrimitive;
2
3use crate::{
4    Index, ShallowClone,
5    core::{Insert, Remove, Seal, Update},
6};
7
8/// An index that provides the mean (average) of indexed values.
9#[derive(Clone)]
10pub struct Mean<T> {
11    sum: f64,
12    count: usize,
13    _phantom: core::marker::PhantomData<T>,
14}
15
16impl<T> Default for Mean<T> {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl<T> Mean<T> {
23    pub fn new() -> Self {
24        Mean {
25            sum: 0.0,
26            count: 0,
27            _phantom: core::marker::PhantomData,
28        }
29    }
30}
31
32impl<T> Index<T> for Mean<T>
33where
34    T: ToPrimitive + Copy + 'static,
35{
36    #[inline]
37    fn insert(&mut self, _seal: Seal, op: &Insert<T>) {
38        if let Some(val) = op.new.to_f64() {
39            self.sum += val;
40            self.count += 1;
41        }
42    }
43
44    #[inline]
45    fn remove(&mut self, _seal: Seal, op: &Remove<T>) {
46        if let Some(val) = op.existing.to_f64() {
47            self.sum -= val;
48            self.count -= 1;
49        }
50    }
51
52    #[inline]
53    fn update(&mut self, _seal: Seal, op: &Update<T>) {
54        if let (Some(old_val), Some(new_val)) = (op.existing.to_f64(), op.new.to_f64()) {
55            self.sum = self.sum - old_val + new_val;
56        }
57    }
58}
59
60impl<T> Mean<T> {
61    #[inline]
62    pub fn mean(&self) -> Option<f64> {
63        if self.count > 0 {
64            Some(self.sum / self.count as f64)
65        } else {
66            None
67        }
68    }
69
70    #[inline]
71    pub fn count(&self) -> usize {
72        self.count
73    }
74
75    #[inline]
76    pub fn sum(&self) -> f64 {
77        self.sum
78    }
79}
80
81impl<T> ShallowClone for Mean<T> where T: Clone {}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::testutils::prop_assert_reference;
87
88    #[test]
89    fn test_mean() {
90        prop_assert_reference(
91            Mean::<u32>::new,
92            |db| db.query(|ix| ix.mean()),
93            |xs| {
94                if !xs.is_empty() {
95                    let sum: f64 = xs.iter().map(|x| *x as f64).sum();
96                    let count = xs.len() as f64;
97                    Some(sum / count)
98                } else {
99                    None
100                }
101            },
102            None,
103        );
104    }
105}