use std::collections::VecDeque;
pub struct SegmentStats {
recent_sizes: VecDeque<i64>,
window_size: usize,
segments_sum: i64,
}
impl SegmentStats {
#[must_use]
pub fn new(window_size: usize) -> Self {
Self {
recent_sizes: VecDeque::with_capacity(window_size),
window_size,
segments_sum: 0,
}
}
pub fn update(&mut self, segment_size: i64) {
if self.recent_sizes.len() >= self.window_size {
if let Some(evicted) = self.recent_sizes.pop_front() {
self.segments_sum -= evicted;
}
}
self.recent_sizes.push_back(segment_size);
self.segments_sum += segment_size;
}
#[must_use]
pub fn trend_ratio(&self, current_segment_size: i64) -> Option<f64> {
if self.segments_sum == 0 {
return None;
}
Some(current_segment_size as f64 / self.segments_sum as f64)
}
#[must_use]
pub fn segments_sum(&self) -> i64 {
self.segments_sum
}
#[must_use]
pub fn count(&self) -> usize {
self.recent_sizes.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_stats() {
let stats = SegmentStats::new(5);
assert_eq!(stats.segments_sum(), 0);
assert_eq!(stats.count(), 0);
assert!(stats.trend_ratio(100).is_none());
}
#[test]
fn test_single_segment() {
let mut stats = SegmentStats::new(5);
stats.update(1000);
assert_eq!(stats.segments_sum(), 1000);
assert!((stats.trend_ratio(1000).unwrap() - 1.0).abs() < 1e-10);
}
#[test]
fn test_window_eviction() {
let mut stats = SegmentStats::new(3);
stats.update(100);
stats.update(200);
stats.update(300);
assert_eq!(stats.segments_sum(), 600);
stats.update(400);
assert_eq!(stats.segments_sum(), 900); assert_eq!(stats.count(), 3);
}
#[test]
fn test_trend_ratio() {
let mut stats = SegmentStats::new(5);
stats.update(100);
stats.update(100);
stats.update(100);
let ratio = stats.trend_ratio(100).unwrap();
assert!((ratio - 1.0 / 3.0).abs() < 1e-10);
}
}