use crate::error::{SpotError, SpotResult};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ubend {
cursor: usize,
capacity: usize,
#[cfg_attr(feature = "serde", serde(with = "crate::ser::nan_safe_f64"))]
last_erased_data: f64,
filled: bool,
data: Vec<f64>,
}
impl Ubend {
pub fn new(capacity: usize) -> SpotResult<Self> {
if capacity == 0 {
return Err(SpotError::MemoryAllocationFailed);
}
Ok(Self {
cursor: 0,
filled: false,
capacity,
last_erased_data: f64::NAN,
data: vec![0.0; capacity],
})
}
pub fn size(&self) -> usize {
if self.filled {
self.capacity
} else {
self.cursor
}
}
pub fn push(&mut self, x: f64) -> f64 {
if self.filled {
self.last_erased_data = self.data[self.cursor];
}
self.data[self.cursor] = x;
if self.cursor == self.capacity - 1 {
self.cursor = 0;
self.filled = true;
} else {
self.cursor += 1;
}
self.last_erased_data
}
pub fn iter(&self) -> UbendIterator<'_> {
UbendIterator {
ubend: self,
index: 0,
}
}
pub fn get(&self, index: usize) -> Option<f64> {
let size = self.size();
if index >= size {
return None;
}
if !self.filled {
Some(self.data[index])
} else {
let real_index = (self.cursor + index) % self.capacity;
Some(self.data[real_index])
}
}
pub fn raw_data(&self) -> &[f64] {
&self.data
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn is_filled(&self) -> bool {
self.filled
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn last_erased_data(&self) -> f64 {
self.last_erased_data
}
pub fn data(&self) -> Vec<f64> {
self.iter().collect()
}
}
pub struct UbendIterator<'a> {
ubend: &'a Ubend,
index: usize,
}
impl<'a> Iterator for UbendIterator<'a> {
type Item = f64;
fn next(&mut self) -> Option<Self::Item> {
let result = self.ubend.get(self.index);
self.index += 1;
result
}
}
impl<'a> ExactSizeIterator for UbendIterator<'a> {
fn len(&self) -> usize {
self.ubend.size()
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_ubend_creation() {
let ubend = Ubend::new(5).unwrap();
assert_eq!(ubend.capacity(), 5);
assert_eq!(ubend.size(), 0);
assert!(!ubend.is_filled());
assert_eq!(ubend.cursor(), 0);
assert!(ubend.last_erased_data().is_nan());
}
#[test]
fn test_ubend_zero_capacity() {
let result = Ubend::new(0);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), SpotError::MemoryAllocationFailed);
}
#[test]
fn test_ubend_push_before_full() {
let mut ubend = Ubend::new(3).unwrap();
let erased = ubend.push(1.0);
assert!(erased.is_nan());
assert_eq!(ubend.size(), 1);
assert!(!ubend.is_filled());
assert_eq!(ubend.cursor(), 1);
let erased = ubend.push(2.0);
assert!(erased.is_nan());
assert_eq!(ubend.size(), 2);
assert!(!ubend.is_filled());
assert_eq!(ubend.cursor(), 2);
let erased = ubend.push(3.0);
assert!(erased.is_nan());
assert_eq!(ubend.size(), 3);
assert!(ubend.is_filled());
assert_eq!(ubend.cursor(), 0);
}
#[test]
fn test_ubend_push_after_full() {
let mut ubend = Ubend::new(3).unwrap();
ubend.push(1.0);
ubend.push(2.0);
ubend.push(3.0);
let erased = ubend.push(4.0);
assert_relative_eq!(erased, 1.0);
assert_eq!(ubend.size(), 3);
assert!(ubend.is_filled());
assert_eq!(ubend.cursor(), 1);
let erased = ubend.push(5.0);
assert_relative_eq!(erased, 2.0);
assert_eq!(ubend.size(), 3);
assert!(ubend.is_filled());
assert_eq!(ubend.cursor(), 2);
}
#[test]
fn test_ubend_get() {
let mut ubend = Ubend::new(3).unwrap();
assert!(ubend.get(0).is_none());
ubend.push(10.0);
ubend.push(20.0);
assert_relative_eq!(ubend.get(0).unwrap(), 10.0);
assert_relative_eq!(ubend.get(1).unwrap(), 20.0);
assert!(ubend.get(2).is_none());
ubend.push(30.0);
ubend.push(40.0);
assert_relative_eq!(ubend.get(0).unwrap(), 20.0);
assert_relative_eq!(ubend.get(1).unwrap(), 30.0);
assert_relative_eq!(ubend.get(2).unwrap(), 40.0);
}
#[test]
fn test_ubend_iterator() {
let mut ubend = Ubend::new(3).unwrap();
ubend.push(1.0);
ubend.push(2.0);
ubend.push(3.0);
let values: Vec<f64> = ubend.iter().collect();
assert_eq!(values, vec![1.0, 2.0, 3.0]);
ubend.push(4.0);
let values: Vec<f64> = ubend.iter().collect();
assert_eq!(values, vec![2.0, 3.0, 4.0]);
}
#[test]
fn test_ubend_exact_size_iterator() {
let mut ubend = Ubend::new(3).unwrap();
assert_eq!(ubend.iter().len(), 0);
ubend.push(1.0);
assert_eq!(ubend.iter().len(), 1);
ubend.push(2.0);
ubend.push(3.0);
assert_eq!(ubend.iter().len(), 3);
ubend.push(4.0);
assert_eq!(ubend.iter().len(), 3);
}
}