use crate::common::{FilterError, FloatOps};
#[derive(Debug, Clone, Copy)]
pub struct WindowedStats<const N: usize> {
buffer: [f64; N],
count: usize,
head: usize,
}
impl<const N: usize> WindowedStats<N> {
pub const fn new() -> Self {
Self {
buffer: [0.0; N],
count: 0,
head: 0,
}
}
pub fn clear(&mut self) {
self.count = 0;
self.head = 0;
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub const fn capacity(&self) -> usize {
N
}
pub fn push(&mut self, sample: f64) -> Result<(), FilterError> {
if !sample.is_valid_float() {
return Err(FilterError::NonFiniteInput);
}
self.buffer[self.head] = sample;
self.head = (self.head + 1) % N;
if self.count < N {
self.count += 1;
}
Ok(())
}
pub fn mean(&self) -> Option<f64> {
if self.count == 0 {
return None;
}
let mut sum = 0.0;
for i in 0..self.count {
sum += self.buffer[i];
}
Some(sum / self.count as f64)
}
pub fn variance(&self) -> Option<f64> {
if self.count < 2 {
return Some(0.0);
}
let m = self.mean()?;
let mut sum_sq = 0.0;
for i in 0..self.count {
let diff = self.buffer[i] - m;
sum_sq += diff * diff;
}
Some(sum_sq / (self.count - 1) as f64)
}
pub fn std_dev(&self) -> Option<f64> {
let v = self.variance()?;
Some(v.sqrt_val())
}
pub fn min(&self) -> Option<f64> {
if self.count == 0 {
return None;
}
let mut min_val = self.buffer[0];
for i in 1..self.count {
if self.buffer[i] < min_val {
min_val = self.buffer[i];
}
}
Some(min_val)
}
pub fn max(&self) -> Option<f64> {
if self.count == 0 {
return None;
}
let mut max_val = self.buffer[0];
for i in 1..self.count {
if self.buffer[i] > max_val {
max_val = self.buffer[i];
}
}
Some(max_val)
}
pub fn peak_to_peak(&self) -> Option<f64> {
if self.count == 0 {
return None;
}
Some(self.max()? - self.min()?)
}
}
impl<const N: usize> Default for WindowedStats<N> {
fn default() -> Self {
Self::new()
}
}