#[derive(Debug, Clone)]
pub struct MetricsBuffer {
data: Vec<f32>,
capacity: usize,
write_idx: usize,
len: usize,
}
impl MetricsBuffer {
pub fn new(capacity: usize) -> Self {
Self { data: vec![0.0; capacity], capacity, write_idx: 0, len: 0 }
}
pub fn push(&mut self, value: f32) {
self.data[self.write_idx] = value;
self.write_idx = (self.write_idx + 1) % self.capacity;
if self.len < self.capacity {
self.len += 1;
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn last_n(&self, n: usize) -> Vec<f32> {
let n = n.min(self.len);
if n == 0 {
return Vec::new();
}
let mut result = Vec::with_capacity(n);
let start_idx = if self.len == self.capacity {
(self.write_idx + self.capacity - n) % self.capacity
} else {
self.len.saturating_sub(n)
};
for i in 0..n {
let idx = (start_idx + i) % self.capacity;
result.push(self.data[idx]);
}
result
}
pub fn values(&self) -> Vec<f32> {
self.last_n(self.len)
}
pub fn last(&self) -> Option<f32> {
if self.len == 0 {
None
} else {
let idx = (self.write_idx + self.capacity - 1) % self.capacity;
Some(self.data[idx])
}
}
pub fn min(&self) -> Option<f32> {
if self.len == 0 {
return None;
}
self.values().into_iter().reduce(f32::min)
}
pub fn max(&self) -> Option<f32> {
if self.len == 0 {
return None;
}
self.values().into_iter().reduce(f32::max)
}
pub fn mean(&self) -> Option<f32> {
if self.len == 0 {
return None;
}
let sum: f32 = self.values().iter().sum();
Some(sum / self.len as f32)
}
pub fn clear(&mut self) {
self.write_idx = 0;
self.len = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_buffer_new() {
let buf = MetricsBuffer::new(10);
assert_eq!(buf.capacity(), 10);
assert_eq!(buf.len(), 0);
assert!(buf.is_empty());
}
#[test]
fn test_metrics_buffer_push() {
let mut buf = MetricsBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
assert_eq!(buf.len(), 3);
assert_eq!(buf.values(), vec![1.0, 2.0, 3.0]);
}
#[test]
fn test_metrics_buffer_wraparound() {
let mut buf = MetricsBuffer::new(3);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
buf.push(4.0); buf.push(5.0);
assert_eq!(buf.len(), 3);
assert_eq!(buf.values(), vec![3.0, 4.0, 5.0]);
}
#[test]
fn test_metrics_buffer_last_n() {
let mut buf = MetricsBuffer::new(10);
for i in 0..10 {
buf.push(i as f32);
}
assert_eq!(buf.last_n(3), vec![7.0, 8.0, 9.0]);
assert_eq!(buf.last_n(1), vec![9.0]);
assert_eq!(buf.last_n(0), Vec::<f32>::new());
}
#[test]
fn test_metrics_buffer_last() {
let mut buf = MetricsBuffer::new(5);
assert_eq!(buf.last(), None);
buf.push(1.0);
assert_eq!(buf.last(), Some(1.0));
buf.push(2.0);
assert_eq!(buf.last(), Some(2.0));
}
#[test]
fn test_metrics_buffer_min_max_mean() {
let mut buf = MetricsBuffer::new(10);
assert_eq!(buf.min(), None);
assert_eq!(buf.max(), None);
assert_eq!(buf.mean(), None);
buf.push(1.0);
buf.push(5.0);
buf.push(3.0);
assert_eq!(buf.min(), Some(1.0));
assert_eq!(buf.max(), Some(5.0));
assert_eq!(buf.mean(), Some(3.0));
}
#[test]
fn test_metrics_buffer_clear() {
let mut buf = MetricsBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.clear();
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
#[test]
fn test_metrics_buffer_last_n_wraparound() {
let mut buf = MetricsBuffer::new(4);
for i in 0..6 {
buf.push(i as f32);
}
assert_eq!(buf.last_n(2), vec![4.0, 5.0]);
assert_eq!(buf.last_n(4), vec![2.0, 3.0, 4.0, 5.0]);
}
#[test]
fn test_metrics_buffer_last_n_more_than_len() {
let mut buf = MetricsBuffer::new(10);
buf.push(1.0);
buf.push(2.0);
assert_eq!(buf.last_n(5), vec![1.0, 2.0]);
}
}