oxihuman_core/
time_series_buffer.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
8pub struct TimeSeriesSample {
9 pub timestamp: f64,
10 pub value: f64,
11}
12
13#[derive(Debug, Clone)]
14pub struct TimeSeriesBuffer {
15 buf: Vec<TimeSeriesSample>,
16 capacity: usize,
17 head: usize,
18 len: usize,
19}
20
21impl TimeSeriesBuffer {
22 pub fn new(capacity: usize) -> Self {
23 TimeSeriesBuffer {
24 buf: Vec::with_capacity(capacity),
25 capacity,
26 head: 0,
27 len: 0,
28 }
29 }
30
31 pub fn push(&mut self, timestamp: f64, value: f64) {
32 let sample = TimeSeriesSample { timestamp, value };
33 if self.len < self.capacity {
34 self.buf.push(sample);
35 self.len += 1;
36 } else {
37 self.buf[self.head] = sample;
38 self.head = (self.head + 1) % self.capacity;
39 }
40 }
41
42 pub fn len(&self) -> usize {
43 self.len
44 }
45
46 pub fn is_empty(&self) -> bool {
47 self.len == 0
48 }
49
50 pub fn iter(&self) -> impl Iterator<Item = &TimeSeriesSample> {
51 let (a, b) = if self.len < self.capacity {
52 (&self.buf[..self.len], &self.buf[0..0])
53 } else {
54 let (lo, hi) = self.buf.split_at(self.head);
55 (hi, lo)
56 };
57 b.iter().chain(a.iter())
58 }
59
60 pub fn mean(&self) -> Option<f64> {
61 if self.is_empty() {
62 return None;
63 }
64 let sum: f64 = self.iter().map(|s| s.value).sum();
65 Some(sum / self.len as f64)
66 }
67
68 pub fn latest(&self) -> Option<&TimeSeriesSample> {
69 if self.is_empty() {
70 return None;
71 }
72 if self.len < self.capacity {
73 self.buf.last()
74 } else {
75 let prev = self.head.saturating_sub(1);
76 let idx = if self.head == 0 {
77 self.capacity - 1
78 } else {
79 prev
80 };
81 self.buf.get(idx)
82 }
83 }
84
85 pub fn capacity(&self) -> usize {
86 self.capacity
87 }
88}
89
90pub fn buffer_variance(buf: &TimeSeriesBuffer) -> Option<f64> {
91 let mean = buf.mean()?;
92 let var = buf.iter().map(|s| (s.value - mean).powi(2)).sum::<f64>() / buf.len() as f64;
93 Some(var)
94}
95
96pub fn buffer_min_max(buf: &TimeSeriesBuffer) -> Option<(f64, f64)> {
97 let mut it = buf.iter();
98 let first = it.next()?;
99 let (min, max) = it.fold((first.value, first.value), |(mn, mx), s| {
100 (mn.min(s.value), mx.max(s.value))
101 });
102 Some((min, max))
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_push_and_len() {
111 let mut buf = TimeSeriesBuffer::new(5);
112 buf.push(1.0, 10.0);
113 buf.push(2.0, 20.0);
114 assert_eq!(buf.len(), 2);
115 }
116
117 #[test]
118 fn test_ring_overwrite() {
119 let mut buf = TimeSeriesBuffer::new(3);
120 for i in 0..5 {
121 buf.push(i as f64, i as f64 * 10.0);
122 }
123 assert_eq!(buf.len(), 3 ,);
124 }
125
126 #[test]
127 fn test_mean_simple() {
128 let mut buf = TimeSeriesBuffer::new(4);
129 buf.push(1.0, 2.0);
130 buf.push(2.0, 4.0);
131 buf.push(3.0, 6.0);
132 let m = buf.mean().expect("should succeed");
133 assert!((m - 4.0).abs() < 1e-10 ,);
134 }
135
136 #[test]
137 fn test_empty_mean_none() {
138 let buf = TimeSeriesBuffer::new(5);
139 assert!(buf.mean().is_none() ,);
140 }
141
142 #[test]
143 fn test_variance() {
144 let mut buf = TimeSeriesBuffer::new(4);
145 buf.push(0.0, 2.0);
146 buf.push(1.0, 4.0);
147 buf.push(2.0, 6.0);
148 let var = buffer_variance(&buf).expect("should succeed");
149 assert!(var > 0.0 ,);
150 }
151
152 #[test]
153 fn test_min_max() {
154 let mut buf = TimeSeriesBuffer::new(10);
155 buf.push(0.0, 5.0);
156 buf.push(1.0, 1.0);
157 buf.push(2.0, 9.0);
158 let (mn, mx) = buffer_min_max(&buf).expect("should succeed");
159 assert_eq!(mn, 1.0);
160 assert_eq!(mx, 9.0);
161 }
162
163 #[test]
164 fn test_is_empty() {
165 let buf = TimeSeriesBuffer::new(5);
166 assert!(buf.is_empty() ,);
167 }
168
169 #[test]
170 fn test_capacity() {
171 let buf = TimeSeriesBuffer::new(8);
172 assert_eq!(buf.capacity(), 8);
173 }
174
175 #[test]
176 fn test_latest_after_overwrite() {
177 let mut buf = TimeSeriesBuffer::new(3);
178 buf.push(1.0, 10.0);
179 buf.push(2.0, 20.0);
180 buf.push(3.0, 30.0);
181 buf.push(4.0, 40.0);
182 let latest = buf.latest().expect("should succeed");
183 assert!((latest.value - 40.0).abs() < 1e-10, );
184 }
185}