1use std::collections::VecDeque;
14
15#[derive(Debug, Clone)]
17pub struct RingBuffer<T> {
18 data: VecDeque<T>,
19 capacity: usize,
20}
21
22impl<T> RingBuffer<T> {
23 pub fn new(capacity: usize) -> Self {
25 Self {
26 data: VecDeque::with_capacity(capacity),
27 capacity,
28 }
29 }
30
31 pub fn push(&mut self, value: T) {
33 if self.data.len() >= self.capacity {
34 self.data.pop_front();
35 }
36 self.data.push_back(value);
37 }
38
39 pub fn len(&self) -> usize {
41 self.data.len()
42 }
43
44 pub fn is_empty(&self) -> bool {
46 self.data.is_empty()
47 }
48
49 pub fn capacity(&self) -> usize {
51 self.capacity
52 }
53
54 pub fn back(&self) -> Option<&T> {
56 self.data.back()
57 }
58
59 pub fn front(&self) -> Option<&T> {
61 self.data.front()
62 }
63
64 pub fn clear(&mut self) {
66 self.data.clear();
67 }
68
69 pub fn iter(&self) -> impl Iterator<Item = &T> {
71 self.data.iter()
72 }
73
74 pub fn get(&self, index: usize) -> Option<&T> {
76 self.data.get(index)
77 }
78
79 pub fn last_n(&self, n: usize) -> impl Iterator<Item = &T> {
81 let skip = self.data.len().saturating_sub(n);
82 self.data.iter().skip(skip)
83 }
84}
85
86impl<T: Clone> RingBuffer<T> {
87 pub fn to_vec(&self) -> Vec<T> {
89 self.data.iter().cloned().collect()
90 }
91}
92
93impl<T: Copy + Default> RingBuffer<T> {
94 pub fn as_slices(&self) -> (&[T], &[T]) {
97 self.data.as_slices()
98 }
99}
100
101impl<T> Default for RingBuffer<T> {
102 fn default() -> Self {
103 Self::new(64)
104 }
105}
106
107impl RingBuffer<f64> {
109 pub fn mean(&self) -> f64 {
111 if self.is_empty() {
112 return 0.0;
113 }
114 let sum: f64 = self.data.iter().sum();
115 sum / self.data.len() as f64
116 }
117
118 pub fn min(&self) -> f64 {
120 self.data.iter().copied().fold(f64::INFINITY, f64::min)
121 }
122
123 pub fn max(&self) -> f64 {
125 self.data.iter().copied().fold(f64::NEG_INFINITY, f64::max)
126 }
127
128 pub fn percentile(&self, p: f64) -> f64 {
130 if self.is_empty() {
131 return 0.0;
132 }
133 let mut sorted: Vec<f64> = self.data.iter().copied().collect();
134 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
135
136 let idx = ((sorted.len() - 1) as f64 * p.clamp(0.0, 1.0)) as usize;
137 sorted[idx]
138 }
139
140 pub fn std_dev(&self) -> f64 {
142 let data: Vec<f64> = self.data.iter().copied().collect();
143 batuta_common::math::std_dev(&data)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_ring_buffer_push() {
153 let mut rb = RingBuffer::new(3);
154 rb.push(1);
155 rb.push(2);
156 rb.push(3);
157 assert_eq!(rb.len(), 3);
158
159 rb.push(4);
160 assert_eq!(rb.len(), 3);
161 assert_eq!(rb.front(), Some(&2));
162 assert_eq!(rb.back(), Some(&4));
163 }
164
165 #[test]
166 fn test_ring_buffer_iter() {
167 let mut rb = RingBuffer::new(3);
168 rb.push(1);
169 rb.push(2);
170 rb.push(3);
171
172 let values: Vec<_> = rb.iter().copied().collect();
173 assert_eq!(values, vec![1, 2, 3]);
174 }
175
176 #[test]
177 fn test_ring_buffer_last_n() {
178 let mut rb = RingBuffer::new(5);
179 for i in 1..=5 {
180 rb.push(i);
181 }
182
183 let last3: Vec<_> = rb.last_n(3).copied().collect();
184 assert_eq!(last3, vec![3, 4, 5]);
185 }
186
187 #[test]
188 fn test_ring_buffer_statistics() {
189 let mut rb: RingBuffer<f64> = RingBuffer::new(5);
190 rb.push(1.0);
191 rb.push(2.0);
192 rb.push(3.0);
193 rb.push(4.0);
194 rb.push(5.0);
195
196 assert_eq!(rb.mean(), 3.0);
197 assert_eq!(rb.min(), 1.0);
198 assert_eq!(rb.max(), 5.0);
199 assert_eq!(rb.percentile(0.5), 3.0);
200 }
201
202 #[test]
203 fn test_ring_buffer_empty() {
204 let rb: RingBuffer<f64> = RingBuffer::new(5);
205 assert!(rb.is_empty());
206 assert_eq!(rb.mean(), 0.0);
207 assert_eq!(rb.percentile(0.5), 0.0);
208 }
209}