1use super::types::DataPoint;
9
10#[derive(Debug, Clone)]
27pub struct RingBuffer<T> {
28 data: Vec<T>,
30 capacity: usize,
32 write_pos: usize,
34 len: usize,
36 total_written: u64,
38}
39
40impl<T: Clone + Default> RingBuffer<T> {
41 pub fn new(capacity: usize) -> Self {
47 assert!(capacity > 0, "RingBuffer capacity must be > 0");
48 Self {
49 data: vec![T::default(); capacity],
50 capacity,
51 write_pos: 0,
52 len: 0,
53 total_written: 0,
54 }
55 }
56
57 pub fn from_vec(mut data: Vec<T>, capacity: usize) -> Self {
61 assert!(capacity > 0, "RingBuffer capacity must be > 0");
62
63 let total_written = data.len() as u64;
64
65 if data.len() > capacity {
66 let excess = data.len() - capacity;
68 data.drain(..excess);
69 }
70
71 let len = data.len();
72 let write_pos = len % capacity;
73
74 data.resize_with(capacity, T::default);
76
77 Self {
78 data,
79 capacity,
80 write_pos,
81 len,
82 total_written,
83 }
84 }
85
86 #[inline]
90 pub fn push(&mut self, item: T) {
91 self.data[self.write_pos] = item;
92 self.write_pos = (self.write_pos + 1) % self.capacity;
93 self.len = (self.len + 1).min(self.capacity);
94 self.total_written = self.total_written.wrapping_add(1);
95 }
96
97 pub fn extend<I: IntoIterator<Item = T>>(&mut self, items: I) {
101 for item in items {
102 self.push(item);
103 }
104 }
105
106 #[inline]
110 pub fn get(&self, index: usize) -> Option<&T> {
111 if index >= self.len {
112 return None;
113 }
114
115 let start = if self.len < self.capacity {
117 0
118 } else {
119 self.write_pos
120 };
121 let actual_idx = (start + index) % self.capacity;
122 Some(&self.data[actual_idx])
123 }
124
125 #[inline]
127 pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
128 if index >= self.len {
129 return None;
130 }
131
132 let start = if self.len < self.capacity {
133 0
134 } else {
135 self.write_pos
136 };
137 let actual_idx = (start + index) % self.capacity;
138 Some(&mut self.data[actual_idx])
139 }
140
141 #[inline]
143 pub fn last(&self) -> Option<&T> {
144 if self.len == 0 {
145 None
146 } else {
147 self.get(self.len - 1)
148 }
149 }
150
151 #[inline]
153 pub fn first(&self) -> Option<&T> {
154 if self.len == 0 { None } else { self.get(0) }
155 }
156
157 #[inline]
159 pub fn len(&self) -> usize {
160 self.len
161 }
162
163 #[inline]
165 pub fn is_empty(&self) -> bool {
166 self.len == 0
167 }
168
169 #[inline]
171 pub fn capacity(&self) -> usize {
172 self.capacity
173 }
174
175 #[inline]
177 pub fn has_wrapped(&self) -> bool {
178 self.total_written > self.capacity as u64
179 }
180
181 #[inline]
183 pub fn total_written(&self) -> u64 {
184 self.total_written
185 }
186
187 #[inline]
189 pub fn is_full(&self) -> bool {
190 self.len == self.capacity
191 }
192
193 pub fn clear(&mut self) {
195 self.len = 0;
196 self.write_pos = 0;
197 }
199
200 pub fn iter(&self) -> RingBufferIter<'_, T> {
202 RingBufferIter {
203 buffer: self,
204 index: 0,
205 }
206 }
207
208 pub fn as_slices(&self) -> (&[T], Option<&[T]>) {
214 if self.len == 0 {
215 return (&[], None);
216 }
217
218 if self.len < self.capacity || self.write_pos == 0 {
219 (&self.data[..self.len], None)
221 } else {
222 let first = &self.data[self.write_pos..];
224 let second = &self.data[..self.write_pos];
225 (first, Some(second))
226 }
227 }
228
229 pub fn to_vec(&self) -> Vec<T> {
234 let (first, second) = self.as_slices();
235 if let Some(second) = second {
236 let mut result = Vec::with_capacity(self.len);
237 result.extend_from_slice(first);
238 result.extend_from_slice(second);
239 result
240 } else {
241 first.to_vec()
242 }
243 }
244}
245
246impl<T> Default for RingBuffer<T>
247where
248 T: Clone + Default,
249{
250 fn default() -> Self {
251 Self::new(1024)
252 }
253}
254
255pub struct RingBufferIter<'a, T> {
257 buffer: &'a RingBuffer<T>,
258 index: usize,
259}
260
261impl<'a, T: Clone + Default> Iterator for RingBufferIter<'a, T> {
262 type Item = &'a T;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 if self.index >= self.buffer.len {
266 None
267 } else {
268 let item = self.buffer.get(self.index);
269 self.index += 1;
270 item
271 }
272 }
273
274 fn size_hint(&self) -> (usize, Option<usize>) {
275 let remaining = self.buffer.len - self.index;
276 (remaining, Some(remaining))
277 }
278}
279
280impl<T: Clone + Default> ExactSizeIterator for RingBufferIter<'_, T> {}
281
282#[derive(Debug, Clone, Copy, PartialEq, Default)]
287pub struct TimeSeriesPoint {
288 pub time: f64,
290 pub value: f64,
292 pub min: Option<f64>,
294 pub max: Option<f64>,
296 pub count: Option<u32>,
298}
299
300impl TimeSeriesPoint {
301 pub fn new(time: f64, value: f64) -> Self {
303 Self {
304 time,
305 value,
306 min: None,
307 max: None,
308 count: None,
309 }
310 }
311
312 pub fn with_range(time: f64, value: f64, min: f64, max: f64) -> Self {
314 Self {
315 time,
316 value,
317 min: Some(min),
318 max: Some(max),
319 count: None,
320 }
321 }
322
323 pub fn aggregated(time: f64, value: f64, min: f64, max: f64, count: u32) -> Self {
325 Self {
326 time,
327 value,
328 min: Some(min),
329 max: Some(max),
330 count: Some(count),
331 }
332 }
333
334 pub fn to_data_point(&self) -> DataPoint {
336 DataPoint::new(self.time, self.value)
337 }
338}
339
340impl From<TimeSeriesPoint> for DataPoint {
341 fn from(point: TimeSeriesPoint) -> Self {
342 DataPoint::new(point.time, point.value)
343 }
344}
345
346impl From<(f64, f64)> for TimeSeriesPoint {
347 fn from((time, value): (f64, f64)) -> Self {
348 Self::new(time, value)
349 }
350}
351
352#[derive(Debug, Clone)]
360pub enum SeriesData {
361 Vec(Vec<DataPoint>),
363 Ring(RingBuffer<DataPoint>),
365 TimeSeries(RingBuffer<TimeSeriesPoint>),
367 Downsampled {
369 source: Box<SeriesData>,
371 factor: usize,
373 cache: Vec<DataPoint>,
375 source_version: u64,
377 },
378}
379
380impl Default for SeriesData {
381 fn default() -> Self {
382 Self::Vec(Vec::new())
383 }
384}
385
386impl SeriesData {
387 pub fn from_vec(data: Vec<DataPoint>) -> Self {
389 Self::Vec(data)
390 }
391
392 pub fn ring(capacity: usize) -> Self {
394 Self::Ring(RingBuffer::new(capacity))
395 }
396
397 pub fn time_series(capacity: usize) -> Self {
399 Self::TimeSeries(RingBuffer::new(capacity))
400 }
401
402 pub fn len(&self) -> usize {
404 match self {
405 Self::Vec(v) => v.len(),
406 Self::Ring(r) => r.len(),
407 Self::TimeSeries(r) => r.len(),
408 Self::Downsampled { cache, .. } => cache.len(),
409 }
410 }
411
412 pub fn is_empty(&self) -> bool {
414 self.len() == 0
415 }
416
417 pub fn get(&self, index: usize) -> Option<DataPoint> {
419 match self {
420 Self::Vec(v) => v.get(index).copied(),
421 Self::Ring(r) => r.get(index).copied(),
422 Self::TimeSeries(r) => r.get(index).map(|p| p.to_data_point()),
423 Self::Downsampled { cache, .. } => cache.get(index).copied(),
424 }
425 }
426
427 pub fn first(&self) -> Option<DataPoint> {
429 self.get(0)
430 }
431
432 pub fn last(&self) -> Option<DataPoint> {
434 if self.is_empty() {
435 None
436 } else {
437 self.get(self.len() - 1)
438 }
439 }
440
441 pub fn push(&mut self, point: DataPoint) {
443 match self {
444 Self::Vec(v) => v.push(point),
445 Self::Ring(r) => r.push(point),
446 Self::TimeSeries(r) => r.push(TimeSeriesPoint::new(point.x, point.y)),
447 Self::Downsampled { .. } => {
448 }
450 }
451 }
452
453 pub fn push_time_point(&mut self, point: TimeSeriesPoint) {
455 if let Self::TimeSeries(r) = self {
456 r.push(point);
457 }
458 }
459
460 pub fn clear(&mut self) {
462 match self {
463 Self::Vec(v) => v.clear(),
464 Self::Ring(r) => r.clear(),
465 Self::TimeSeries(r) => r.clear(),
466 Self::Downsampled { cache, .. } => cache.clear(),
467 }
468 }
469
470 pub fn iter(&self) -> SeriesDataIter<'_> {
472 SeriesDataIter {
473 data: self,
474 index: 0,
475 len: self.len(),
476 }
477 }
478
479 pub fn x_range(&self) -> Option<(f64, f64)> {
481 if self.is_empty() {
482 return None;
483 }
484
485 let mut min = f64::INFINITY;
486 let mut max = f64::NEG_INFINITY;
487
488 for point in self.iter() {
489 min = min.min(point.x);
490 max = max.max(point.x);
491 }
492
493 Some((min, max))
494 }
495
496 pub fn y_range(&self) -> Option<(f64, f64)> {
498 if self.is_empty() {
499 return None;
500 }
501
502 let mut min = f64::INFINITY;
503 let mut max = f64::NEG_INFINITY;
504
505 for point in self.iter() {
506 min = min.min(point.y);
507 max = max.max(point.y);
508 }
509
510 Some((min, max))
511 }
512
513 pub fn bounds(&self) -> Option<(DataPoint, DataPoint)> {
515 if self.is_empty() {
516 return None;
517 }
518
519 let mut min = DataPoint::new(f64::INFINITY, f64::INFINITY);
520 let mut max = DataPoint::new(f64::NEG_INFINITY, f64::NEG_INFINITY);
521
522 for point in self.iter() {
523 min.x = min.x.min(point.x);
524 min.y = min.y.min(point.y);
525 max.x = max.x.max(point.x);
526 max.y = max.y.max(point.y);
527 }
528
529 Some((min, max))
530 }
531
532 pub fn is_ring(&self) -> bool {
534 matches!(self, Self::Ring(_) | Self::TimeSeries(_))
535 }
536
537 pub fn has_wrapped(&self) -> bool {
539 match self {
540 Self::Ring(r) => r.has_wrapped(),
541 Self::TimeSeries(r) => r.has_wrapped(),
542 _ => false,
543 }
544 }
545
546 pub fn capacity(&self) -> usize {
548 match self {
549 Self::Vec(v) => v.capacity(),
550 Self::Ring(r) => r.capacity(),
551 Self::TimeSeries(r) => r.capacity(),
552 Self::Downsampled { source, .. } => source.capacity(),
553 }
554 }
555
556 pub fn to_vec(&self) -> Vec<DataPoint> {
558 self.iter().collect()
559 }
560}
561
562pub struct SeriesDataIter<'a> {
564 data: &'a SeriesData,
565 index: usize,
566 len: usize,
567}
568
569impl Iterator for SeriesDataIter<'_> {
570 type Item = DataPoint;
571
572 fn next(&mut self) -> Option<Self::Item> {
573 if self.index >= self.len {
574 None
575 } else {
576 let item = self.data.get(self.index);
577 self.index += 1;
578 item
579 }
580 }
581
582 fn size_hint(&self) -> (usize, Option<usize>) {
583 let remaining = self.len - self.index;
584 (remaining, Some(remaining))
585 }
586}
587
588impl ExactSizeIterator for SeriesDataIter<'_> {}
589
590pub fn lttb_downsample(data: &[DataPoint], target_count: usize) -> Vec<usize> {
595 let n = data.len();
596
597 if n <= target_count {
598 return (0..n).collect();
599 }
600
601 if target_count < 3 {
602 if target_count == 0 {
603 return vec![];
604 } else if target_count == 1 {
605 return vec![0];
606 } else {
607 return vec![0, n - 1];
608 }
609 }
610
611 let mut result = Vec::with_capacity(target_count);
612
613 result.push(0);
615
616 let bucket_size = (n - 2) as f64 / (target_count - 2) as f64;
618
619 let mut a = 0usize; for i in 0..(target_count - 2) {
622 let bucket_start = ((i as f64 * bucket_size) as usize + 1).min(n - 1);
624 let bucket_end = (((i + 1) as f64 * bucket_size) as usize + 1).min(n - 1);
625
626 let next_bucket_start = bucket_end;
628 let next_bucket_end = (((i + 2) as f64 * bucket_size) as usize + 1).min(n);
629
630 let mut avg_x = 0.0;
631 let mut avg_y = 0.0;
632 let count = next_bucket_end - next_bucket_start;
633
634 if count > 0 {
635 for point in &data[next_bucket_start..next_bucket_end] {
636 avg_x += point.x;
637 avg_y += point.y;
638 }
639 avg_x /= count as f64;
640 avg_y /= count as f64;
641 }
642
643 let mut max_area = 0.0;
645 let mut max_area_idx = bucket_start;
646
647 let point_a = &data[a];
648
649 for (offset, point) in data[bucket_start..bucket_end].iter().enumerate() {
650 let area = ((point_a.x - avg_x) * (point.y - point_a.y)
652 - (point_a.x - point.x) * (avg_y - point_a.y))
653 .abs();
654
655 if area > max_area {
656 max_area = area;
657 max_area_idx = bucket_start + offset;
658 }
659 }
660
661 result.push(max_area_idx);
662 a = max_area_idx;
663 }
664
665 result.push(n - 1);
667
668 result
669}
670
671pub fn downsample_data(data: &[DataPoint], target_count: usize) -> Vec<DataPoint> {
673 let indices = lttb_downsample(data, target_count);
674 indices.into_iter().map(|i| data[i]).collect()
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680
681 #[test]
682 fn test_ring_buffer_basic() {
683 let mut buffer = RingBuffer::<i32>::new(5);
684
685 buffer.push(1);
687 buffer.push(2);
688 buffer.push(3);
689
690 assert_eq!(buffer.len(), 3);
691 assert!(!buffer.has_wrapped());
692 assert_eq!(buffer.get(0), Some(&1));
693 assert_eq!(buffer.get(2), Some(&3));
694 }
695
696 #[test]
697 fn test_ring_buffer_wrap() {
698 let mut buffer = RingBuffer::<i32>::new(3);
699
700 for i in 0..5 {
702 buffer.push(i);
703 }
704
705 assert_eq!(buffer.len(), 3);
706 assert!(buffer.has_wrapped());
707 assert_eq!(buffer.get(0), Some(&2)); assert_eq!(buffer.get(1), Some(&3));
709 assert_eq!(buffer.get(2), Some(&4)); }
711
712 #[test]
713 fn test_ring_buffer_iter() {
714 let mut buffer = RingBuffer::<i32>::new(4);
715
716 for i in 0..6 {
717 buffer.push(i);
718 }
719
720 let items: Vec<_> = buffer.iter().copied().collect();
721 assert_eq!(items, vec![2, 3, 4, 5]);
722 }
723
724 #[test]
725 fn test_ring_buffer_slices() {
726 let mut buffer = RingBuffer::<i32>::new(4);
727
728 buffer.push(1);
730 buffer.push(2);
731 let (first, second) = buffer.as_slices();
732 assert_eq!(first, &[1, 2]);
733 assert!(second.is_none());
734
735 buffer.push(3);
737 buffer.push(4);
738 buffer.push(5);
739 buffer.push(6);
740 let (first, second) = buffer.as_slices();
741 assert_eq!(first.len() + second.map_or(0, |s| s.len()), 4);
743 }
744
745 #[test]
746 fn test_series_data_vec() {
747 let mut data =
748 SeriesData::from_vec(vec![DataPoint::new(0.0, 1.0), DataPoint::new(1.0, 2.0)]);
749
750 assert_eq!(data.len(), 2);
751 data.push(DataPoint::new(2.0, 3.0));
752 assert_eq!(data.len(), 3);
753 assert_eq!(data.get(2), Some(DataPoint::new(2.0, 3.0)));
754 }
755
756 #[test]
757 fn test_series_data_ring() {
758 let mut data = SeriesData::ring(3);
759
760 for i in 0..5 {
761 data.push(DataPoint::new(i as f64, i as f64 * 2.0));
762 }
763
764 assert_eq!(data.len(), 3);
765 assert!(data.has_wrapped());
766 assert_eq!(data.first(), Some(DataPoint::new(2.0, 4.0)));
767 }
768
769 #[test]
770 fn test_lttb_downsample() {
771 let data: Vec<DataPoint> = (0..100)
773 .map(|i| DataPoint::new(i as f64, (i as f64 * 0.1).sin()))
774 .collect();
775
776 let downsampled = downsample_data(&data, 20);
777
778 assert_eq!(downsampled.len(), 20);
779 assert_eq!(downsampled[0], data[0]);
781 assert_eq!(downsampled[19], data[99]);
782 }
783}