#[derive(Debug, Clone)]
pub struct HistoryBuffer<T> {
data: Vec<T>,
capacity: usize,
head: usize,
len: usize,
}
impl<T: Default + Clone> HistoryBuffer<T> {
pub fn new(capacity: usize) -> Self {
assert!(capacity > 0, "Capacity must be positive");
Self { data: vec![T::default(); capacity], capacity, head: 0, len: 0 }
}
#[inline]
pub fn push(&mut self, value: T) {
self.data[self.head] = value;
self.head = (self.head + 1) % self.capacity;
self.len = (self.len + 1).min(self.capacity);
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn is_full(&self) -> bool {
self.len == self.capacity
}
pub fn clear(&mut self) {
self.head = 0;
self.len = 0;
}
pub fn last(&self) -> Option<&T> {
if self.len == 0 {
None
} else {
let idx = if self.head == 0 { self.capacity - 1 } else { self.head - 1 };
Some(&self.data[idx])
}
}
pub fn first(&self) -> Option<&T> {
if self.len == 0 {
None
} else {
let start = if self.len < self.capacity { 0 } else { self.head };
Some(&self.data[start])
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
let start = if self.len < self.capacity { 0 } else { self.head };
(0..self.len).map(move |i| &self.data[(start + i) % self.capacity])
}
pub fn to_vec(&self) -> Vec<T>
where
T: Copy,
{
self.iter().copied().collect()
}
}
impl<T: Default + Clone + Copy> HistoryBuffer<T> {
pub fn fill(&mut self, value: T) {
for item in &mut self.data {
*item = value;
}
self.len = self.capacity;
self.head = 0;
}
}
impl HistoryBuffer<f64> {
#[inline]
fn as_slice(&self) -> &[f64] {
&self.data[0..self.len]
}
#[inline]
pub fn avg(&self) -> f64 {
if self.len == 0 {
return 0.0;
}
trueno_viz::monitor::simd::kernels::simd_mean(self.as_slice())
}
#[inline]
pub fn min(&self) -> f64 {
if self.len == 0 {
return f64::INFINITY;
}
trueno_viz::monitor::simd::kernels::simd_min(self.as_slice())
}
#[inline]
pub fn max(&self) -> f64 {
if self.len == 0 {
return f64::NEG_INFINITY;
}
trueno_viz::monitor::simd::kernels::simd_max(self.as_slice())
}
#[inline]
pub fn sum(&self) -> f64 {
if self.len == 0 {
return 0.0;
}
trueno_viz::monitor::simd::kernels::simd_sum(self.as_slice())
}
#[inline]
pub fn stddev(&self) -> f64 {
if self.len < 2 {
return 0.0;
}
let mean = self.avg();
let variance =
self.as_slice().iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (self.len - 1) as f64;
variance.sqrt()
}
#[inline]
pub fn stats(&self) -> (f64, f64, f64) {
if self.len == 0 {
return (f64::INFINITY, f64::NEG_INFINITY, 0.0);
}
let stats = trueno_viz::monitor::simd::kernels::simd_statistics(self.as_slice());
(stats.min, stats.max, stats.mean())
}
pub fn latest(&self, n: usize) -> Vec<f64> {
let n = n.min(self.len);
if n == 0 {
return Vec::new();
}
let start = if self.len < self.capacity {
self.len.saturating_sub(n)
} else {
(self.head + self.capacity - n) % self.capacity
};
(0..n).map(|i| self.data[(start + i) % self.capacity]).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let buf: HistoryBuffer<f64> = HistoryBuffer::new(10);
assert_eq!(buf.capacity(), 10);
assert_eq!(buf.len(), 0);
assert!(buf.is_empty());
}
#[test]
#[should_panic(expected = "Capacity must be positive")]
fn test_new_zero_capacity() {
let _: HistoryBuffer<f64> = HistoryBuffer::new(0);
}
#[test]
fn test_push_and_len() {
let mut buf = HistoryBuffer::new(5);
assert_eq!(buf.len(), 0);
buf.push(1.0);
assert_eq!(buf.len(), 1);
buf.push(2.0);
buf.push(3.0);
assert_eq!(buf.len(), 3);
}
#[test]
fn test_push_wrap() {
let mut buf = HistoryBuffer::new(3);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
assert_eq!(buf.len(), 3);
assert!(buf.is_full());
buf.push(4.0);
assert_eq!(buf.len(), 3);
let values: Vec<f64> = buf.iter().copied().collect();
assert_eq!(values, vec![2.0, 3.0, 4.0]);
}
#[test]
fn test_iter_order() {
let mut buf = HistoryBuffer::new(5);
for i in 1..=5 {
buf.push(i as f64);
}
let values: Vec<f64> = buf.iter().copied().collect();
assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0, 5.0]);
buf.push(6.0);
buf.push(7.0);
let values: Vec<f64> = buf.iter().copied().collect();
assert_eq!(values, vec![3.0, 4.0, 5.0, 6.0, 7.0]);
}
#[test]
fn test_last_and_first() {
let mut buf = HistoryBuffer::new(3);
assert!(buf.last().is_none());
assert!(buf.first().is_none());
buf.push(1.0);
assert_eq!(*buf.last().unwrap(), 1.0);
assert_eq!(*buf.first().unwrap(), 1.0);
buf.push(2.0);
buf.push(3.0);
assert_eq!(*buf.last().unwrap(), 3.0);
assert_eq!(*buf.first().unwrap(), 1.0);
buf.push(4.0); assert_eq!(*buf.last().unwrap(), 4.0);
assert_eq!(*buf.first().unwrap(), 2.0);
}
#[test]
fn test_clear() {
let mut buf = HistoryBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.clear();
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
#[test]
fn test_avg() {
let mut buf = HistoryBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
buf.push(4.0);
buf.push(5.0);
assert!((buf.avg() - 3.0).abs() < 0.001);
}
#[test]
fn test_min_max() {
let mut buf = HistoryBuffer::new(5);
buf.push(3.0);
buf.push(1.0);
buf.push(5.0);
buf.push(2.0);
buf.push(4.0);
assert!((buf.min() - 1.0).abs() < 0.001);
assert!((buf.max() - 5.0).abs() < 0.001);
}
#[test]
fn test_stddev() {
let mut buf = HistoryBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
buf.push(4.0);
buf.push(5.0);
assert!((buf.stddev() - 1.5811).abs() < 0.001);
}
#[test]
fn test_sum() {
let mut buf = HistoryBuffer::new(5);
buf.push(1.0);
buf.push(2.0);
buf.push(3.0);
buf.push(4.0);
buf.push(5.0);
assert!((buf.sum() - 15.0).abs() < 0.001);
}
#[test]
fn test_stats_simd() {
let mut buf = HistoryBuffer::new(5);
buf.push(3.0);
buf.push(1.0);
buf.push(5.0);
buf.push(2.0);
buf.push(4.0);
let (min, max, mean) = buf.stats();
assert!((min - 1.0).abs() < 0.001);
assert!((max - 5.0).abs() < 0.001);
assert!((mean - 3.0).abs() < 0.001);
}
#[test]
fn test_latest() {
let mut buf = HistoryBuffer::new(5);
for i in 1..=5 {
buf.push(i as f64);
}
assert_eq!(buf.latest(3), vec![3.0, 4.0, 5.0]);
assert_eq!(buf.latest(1), vec![5.0]);
assert_eq!(buf.latest(10), vec![1.0, 2.0, 3.0, 4.0, 5.0]);
}
#[test]
fn test_fill() {
let mut buf = HistoryBuffer::new(5);
buf.fill(1.0);
assert_eq!(buf.len(), 5);
assert!(buf.is_full());
assert_eq!(buf.to_vec(), vec![1.0, 1.0, 1.0, 1.0, 1.0]);
}
#[test]
fn test_clone() {
let mut buf = HistoryBuffer::new(3);
buf.push(1.0);
buf.push(2.0);
let cloned = buf.clone();
assert_eq!(cloned.to_vec(), vec![1.0, 2.0]);
}
}