use crate::Price;
#[derive(Clone, Debug)]
pub(crate) struct RingBuffer {
buffer: Vec<Price>,
head: usize,
tail: usize,
len: usize,
}
impl RingBuffer {
#[must_use]
pub(crate) fn new(length: usize) -> Self {
Self {
buffer: vec![0.0; length],
head: 0,
tail: 0,
len: 0,
}
}
pub(crate) fn is_ready(&self) -> bool {
self.len == self.buffer.len()
}
pub(crate) fn push(&mut self, value: Price) -> Option<Price> {
if self.is_ready() {
let old = self.buffer[self.head];
self.buffer[self.head] = value;
self.tail = self.head;
self.head += 1;
if self.head == self.buffer.len() {
self.head = 0;
}
Some(old)
} else {
self.buffer[self.len] = value;
self.tail = self.len;
self.len += 1;
None
}
}
pub(crate) fn replace(&mut self, value: Price) -> Price {
let old = self.buffer[self.tail];
self.buffer[self.tail] = value;
old
}
pub(crate) fn find_value_and_index<F>(&self, should_replace: F) -> (Price, usize)
where
F: Fn(Price, Price) -> bool,
{
let mut index = 0;
let mut found = self.buffer[0];
for i in 1..self.len {
if should_replace(found, self.buffer[i]) {
index = i;
found = self.buffer[i];
}
}
(
found,
(self.tail + self.buffer.len() - index) % self.buffer.len(),
)
}
pub(crate) fn fold<B, F>(&self, init: B, cb: F) -> B
where
F: FnMut(B, &Price) -> B,
{
self.buffer.iter().fold(init, cb)
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::RingBuffer;
#[test]
fn filling_returns_none() {
let mut rb = RingBuffer::new(3);
assert_eq!(rb.push(1.0), None);
assert_eq!(rb.push(2.0), None);
assert_eq!(rb.push(3.0), None);
assert!(rb.is_ready());
}
#[test]
fn full_evicts_oldest() {
let mut rb = RingBuffer::new(3);
rb.push(1.0);
rb.push(2.0);
rb.push(3.0);
assert_eq!(rb.push(4.0), Some(1.0));
assert_eq!(rb.push(5.0), Some(2.0));
assert_eq!(rb.push(6.0), Some(3.0));
}
#[test]
fn replace_swaps_latest() {
let mut rb = RingBuffer::new(3);
rb.push(1.0);
rb.push(2.0);
assert_eq!(rb.replace(9.0), 2.0);
rb.push(3.0);
assert_eq!(rb.push(4.0), Some(1.0));
assert_eq!(rb.push(5.0), Some(9.0)); }
#[test]
fn replace_when_full() {
let mut rb = RingBuffer::new(2);
rb.push(1.0);
rb.push(2.0);
assert_eq!(rb.replace(9.0), 2.0);
assert_eq!(rb.push(3.0), Some(1.0));
assert_eq!(rb.push(4.0), Some(9.0));
}
#[test]
fn capacity_one() {
let mut rb = RingBuffer::new(1);
assert_eq!(rb.push(1.0), None);
assert!(rb.is_ready());
assert_eq!(rb.push(2.0), Some(1.0));
assert_eq!(rb.replace(9.0), 2.0);
assert_eq!(rb.push(3.0), Some(9.0));
}
#[test]
fn wrap_around_correctness() {
let mut rb = RingBuffer::new(2);
rb.push(1.0);
rb.push(2.0);
rb.push(3.0); rb.push(4.0); rb.push(5.0); assert_eq!(rb.push(6.0), Some(4.0));
}
mod find_value_and_index {
use super::RingBuffer;
#[test]
fn find_indexed() {
let mut rb = RingBuffer::new(3);
rb.push(4.0);
rb.push(2.0);
rb.push(5.0);
assert_eq!(rb.find_value_and_index(|a, b| a > b), (2.0, 1));
assert_eq!(rb.find_value_and_index(|a, b| a < b), (5.0, 0));
}
#[test]
fn find_indexed_after_wrap() {
let mut rb = RingBuffer::new(3);
rb.push(1.0);
rb.push(2.0);
rb.push(3.0);
rb.push(4.0);
assert_eq!(rb.find_value_and_index(|a, b| a < b), (4.0, 0));
assert_eq!(rb.find_value_and_index(|a, b| a > b), (2.0, 2));
}
#[test]
fn find_indexed_after_multiple_wraps() {
let mut rb = RingBuffer::new(3);
for v in [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 10.0] {
rb.push(v);
}
assert_eq!(rb.find_value_and_index(|a, b| a < b), (10.0, 0));
assert_eq!(rb.find_value_and_index(|a, b| a > b), (5.0, 2));
}
#[test]
fn find_indexed_all_equal() {
let mut rb = RingBuffer::new(3);
rb.push(7.0);
rb.push(7.0);
rb.push(7.0);
let (val, pos) = rb.find_value_and_index(|a, b| a < b);
assert_eq!(val, 7.0);
assert_eq!(pos, 2);
}
}
}