1use crate::error::{RillError, checked_finite_add, checked_increment, ensure_finite};
6use crate::traits::OnlineStatistic;
7
8#[derive(Debug, Clone, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Sum {
12 sum: f64,
13 count: u64,
14}
15
16impl Sum {
17 pub const fn new() -> Self {
19 Self { sum: 0.0, count: 0 }
20 }
21
22 pub const fn value(&self) -> f64 {
24 self.sum
25 }
26
27 pub const fn count(&self) -> u64 {
29 self.count
30 }
31}
32
33impl OnlineStatistic for Sum {
34 fn update(&mut self, value: f64) -> Result<(), RillError> {
35 ensure_finite("value", value)?;
36 let next_sum = checked_finite_add(self.sum, value, "sum")?;
37 let next_count = checked_increment(self.count, "sum sample")?;
38 self.sum = next_sum;
39 self.count = next_count;
40 Ok(())
41 }
42
43 fn samples_seen(&self) -> u64 {
44 self.count
45 }
46
47 fn reset(&mut self) {
48 self.sum = 0.0;
49 self.count = 0;
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn sum_accumulates() {
59 let mut s = Sum::new();
60 s.update(1.5).unwrap();
61 s.update(2.5).unwrap();
62 assert_eq!(s.value(), 4.0);
63 }
64
65 #[test]
66 fn sum_rejects_overflow_without_mutating_state() {
67 let mut s = Sum::new();
68 s.update(f64::MAX).unwrap();
69 assert!(s.update(f64::MAX).is_err());
70 assert_eq!(s.value(), f64::MAX);
71 assert_eq!(s.count(), 1);
72 }
73}