use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct RingBuffer<T> {
data: VecDeque<T>,
capacity: usize,
}
impl<T> RingBuffer<T> {
pub fn new(capacity: usize) -> Self {
Self {
data: VecDeque::with_capacity(capacity),
capacity,
}
}
pub fn push(&mut self, value: T) {
if self.data.len() >= self.capacity {
self.data.pop_front();
}
self.data.push_back(value);
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn back(&self) -> Option<&T> {
self.data.back()
}
pub fn front(&self) -> Option<&T> {
self.data.front()
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter()
}
pub fn get(&self, index: usize) -> Option<&T> {
self.data.get(index)
}
pub fn last_n(&self, n: usize) -> impl Iterator<Item = &T> {
let skip = self.data.len().saturating_sub(n);
self.data.iter().skip(skip)
}
}
impl<T: Clone> RingBuffer<T> {
pub fn to_vec(&self) -> Vec<T> {
self.data.iter().cloned().collect()
}
}
impl<T: Copy + Default> RingBuffer<T> {
pub fn as_slices(&self) -> (&[T], &[T]) {
self.data.as_slices()
}
}
impl<T> Default for RingBuffer<T> {
fn default() -> Self {
Self::new(64)
}
}
impl RingBuffer<f64> {
pub fn mean(&self) -> f64 {
if self.is_empty() {
return 0.0;
}
let sum: f64 = self.data.iter().sum();
sum / self.data.len() as f64
}
pub fn min(&self) -> f64 {
self.data.iter().copied().fold(f64::INFINITY, f64::min)
}
pub fn max(&self) -> f64 {
self.data.iter().copied().fold(f64::NEG_INFINITY, f64::max)
}
pub fn percentile(&self, p: f64) -> f64 {
if self.is_empty() {
return 0.0;
}
let mut sorted: Vec<f64> = self.data.iter().copied().collect();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let idx = ((sorted.len() - 1) as f64 * p.clamp(0.0, 1.0)) as usize;
sorted[idx]
}
pub fn std_dev(&self) -> f64 {
let data: Vec<f64> = self.data.iter().copied().collect();
batuta_common::math::std_dev(&data)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ring_buffer_push() {
let mut rb = RingBuffer::new(3);
rb.push(1);
rb.push(2);
rb.push(3);
assert_eq!(rb.len(), 3);
rb.push(4);
assert_eq!(rb.len(), 3);
assert_eq!(rb.front(), Some(&2));
assert_eq!(rb.back(), Some(&4));
}
#[test]
fn test_ring_buffer_iter() {
let mut rb = RingBuffer::new(3);
rb.push(1);
rb.push(2);
rb.push(3);
let values: Vec<_> = rb.iter().copied().collect();
assert_eq!(values, vec![1, 2, 3]);
}
#[test]
fn test_ring_buffer_last_n() {
let mut rb = RingBuffer::new(5);
for i in 1..=5 {
rb.push(i);
}
let last3: Vec<_> = rb.last_n(3).copied().collect();
assert_eq!(last3, vec![3, 4, 5]);
}
#[test]
fn test_ring_buffer_statistics() {
let mut rb: RingBuffer<f64> = RingBuffer::new(5);
rb.push(1.0);
rb.push(2.0);
rb.push(3.0);
rb.push(4.0);
rb.push(5.0);
assert_eq!(rb.mean(), 3.0);
assert_eq!(rb.min(), 1.0);
assert_eq!(rb.max(), 5.0);
assert_eq!(rb.percentile(0.5), 3.0);
}
#[test]
fn test_ring_buffer_empty() {
let rb: RingBuffer<f64> = RingBuffer::new(5);
assert!(rb.is_empty());
assert_eq!(rb.mean(), 0.0);
assert_eq!(rb.percentile(0.5), 0.0);
}
}