1use crate::error::SpotResult;
7
8use crate::ubend::Ubend;
9
10#[derive(Debug, Clone)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Peaks {
19 e: f64,
21 e2: f64,
23 #[cfg_attr(feature = "serde", serde(with = "crate::ser::nan_safe_f64"))]
25 min: f64,
26 #[cfg_attr(feature = "serde", serde(with = "crate::ser::nan_safe_f64"))]
28 max: f64,
29 container: Ubend,
31}
32
33impl Peaks {
34 pub fn new(size: usize) -> SpotResult<Self> {
36 Ok(Self {
37 e: 0.0,
38 e2: 0.0,
39 min: f64::NAN,
40 max: f64::NAN,
41 container: Ubend::new(size)?,
42 })
43 }
44
45 pub fn size(&self) -> usize {
47 self.container.size()
48 }
49
50 pub fn push(&mut self, x: f64) {
52 let erased = self.container.push(x);
53 let size = self.size();
54
55 self.e += x;
57 self.e2 += x * x;
58
59 if size == 1 || x < self.min {
61 self.min = x;
62 }
63 if size == 1 || x > self.max {
64 self.max = x;
65 }
66
67 if !erased.is_nan() {
70 self.e -= erased;
71 self.e2 -= erased * erased;
72 if (erased <= self.min) || (erased >= self.max) {
73 self.update_stats();
77 }
78 }
79 }
80
81 pub fn mean(&self) -> f64 {
83 let size = self.size();
84 if size == 0 {
85 f64::NAN
86 } else {
87 self.e / (size as f64)
88 }
89 }
90
91 pub fn variance(&self) -> f64 {
93 let size = self.size();
94 if size == 0 {
95 f64::NAN
96 } else {
97 let size_f = size as f64;
98 let mean = self.e / size_f;
99 (self.e2 / size_f) - (mean * mean)
100 }
101 }
102
103 pub fn min(&self) -> f64 {
105 self.min
106 }
107
108 pub fn max(&self) -> f64 {
110 self.max
111 }
112
113 pub fn sum(&self) -> f64 {
115 self.e
116 }
117
118 pub fn sum_squares(&self) -> f64 {
120 self.e2
121 }
122
123 pub fn container(&self) -> &Ubend {
125 &self.container
126 }
127
128 fn update_stats(&mut self) {
131 self.min = f64::NAN;
133 self.max = f64::NAN;
134 self.e = 0.0;
136 self.e2 = 0.0;
137
138 let max_iteration = self.container.size();
139
140 for i in 0..max_iteration {
141 let value = self.container.raw_data()[i];
143 self.e += value;
144 self.e2 += value * value;
145
146 if self.min.is_nan() || (value < self.min) {
147 self.min = value;
148 }
149 if self.max.is_nan() || (value > self.max) {
150 self.max = value;
151 }
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use crate::error::SpotError;
160 use approx::assert_relative_eq;
161
162 #[test]
163 fn test_peaks_creation() {
164 let peaks = Peaks::new(5).unwrap();
165 assert_eq!(peaks.size(), 0);
166 assert_relative_eq!(peaks.sum(), 0.0);
167 assert_relative_eq!(peaks.sum_squares(), 0.0);
168 assert!(peaks.min().is_nan());
169 assert!(peaks.max().is_nan());
170 assert!(peaks.mean().is_nan());
171 assert!(peaks.variance().is_nan());
172 }
173
174 #[test]
175 fn test_peaks_zero_size() {
176 let result = Peaks::new(0);
177 assert!(result.is_err());
178 assert_eq!(result.unwrap_err(), SpotError::MemoryAllocationFailed);
179 }
180
181 #[test]
182 fn test_peaks_single_element() {
183 let mut peaks = Peaks::new(3).unwrap();
184
185 peaks.push(5.0);
186 assert_eq!(peaks.size(), 1);
187 assert_relative_eq!(peaks.sum(), 5.0);
188 assert_relative_eq!(peaks.sum_squares(), 25.0);
189 assert_relative_eq!(peaks.min(), 5.0);
190 assert_relative_eq!(peaks.max(), 5.0);
191 assert_relative_eq!(peaks.mean(), 5.0);
192 assert_relative_eq!(peaks.variance(), 0.0);
193 }
194
195 #[test]
196 fn test_peaks_multiple_elements() {
197 let mut peaks = Peaks::new(5).unwrap();
198
199 peaks.push(1.0);
200 peaks.push(2.0);
201 peaks.push(3.0);
202
203 assert_eq!(peaks.size(), 3);
204 assert_relative_eq!(peaks.sum(), 6.0);
205 assert_relative_eq!(peaks.sum_squares(), 14.0);
206 assert_relative_eq!(peaks.min(), 1.0);
207 assert_relative_eq!(peaks.max(), 3.0);
208 assert_relative_eq!(peaks.mean(), 2.0);
209
210 assert_relative_eq!(peaks.variance(), 2.0 / 3.0, epsilon = 1e-14);
212 }
213
214 #[test]
215 fn test_peaks_overflow_and_min_max_update() {
216 let mut peaks = Peaks::new(3).unwrap();
217
218 peaks.push(1.0); peaks.push(2.0); peaks.push(3.0); assert_relative_eq!(peaks.min(), 1.0);
224 assert_relative_eq!(peaks.max(), 3.0);
225
226 peaks.push(0.5); assert_eq!(peaks.size(), 3);
230 assert_relative_eq!(peaks.min(), 0.5);
231 assert_relative_eq!(peaks.max(), 3.0);
232 assert_relative_eq!(peaks.sum(), 5.5);
233
234 peaks.push(4.0); assert_relative_eq!(peaks.min(), 0.5);
238 assert_relative_eq!(peaks.max(), 4.0);
239 assert_relative_eq!(peaks.sum(), 7.5);
240 }
241
242 #[test]
243 fn test_peaks_stats_after_min_erasure() {
244 let mut peaks = Peaks::new(3).unwrap();
245
246 peaks.push(2.0);
248 peaks.push(1.0); peaks.push(3.0);
250
251 assert_relative_eq!(peaks.min(), 1.0);
252 assert_relative_eq!(peaks.max(), 3.0);
253
254 peaks.push(2.5); assert_relative_eq!(peaks.min(), 1.0); assert_relative_eq!(peaks.max(), 3.0); peaks.push(2.7); assert_relative_eq!(peaks.min(), 2.5);
265 assert_relative_eq!(peaks.max(), 3.0);
266 }
267
268 #[test]
269 fn test_peaks_stats_after_max_erasure() {
270 let mut peaks = Peaks::new(3).unwrap();
271
272 peaks.push(1.0);
274 peaks.push(3.0); peaks.push(2.0);
276
277 assert_relative_eq!(peaks.min(), 1.0);
278 assert_relative_eq!(peaks.max(), 3.0);
279
280 peaks.push(1.5); peaks.push(1.7); assert_relative_eq!(peaks.min(), 1.5);
286 assert_relative_eq!(peaks.max(), 2.0);
287 }
288}