use std::{
collections::VecDeque,
num::NonZeroUsize,
};
use getset::CopyGetters;
use num::Float;
use crate::View;
#[derive(Clone, Debug, CopyGetters)]
pub struct Min<T, V> {
view: V,
opt_min: Option<T>,
q_vals: VecDeque<T>,
#[getset(get_copy = "pub")]
window_len: NonZeroUsize,
}
impl<T, V> Min<T, V>
where
T: Float,
V: View<T>,
{
pub fn new(view: V, window_len: NonZeroUsize) -> Self {
Self {
view,
opt_min: None,
q_vals: VecDeque::with_capacity(window_len.get()),
window_len,
}
}
}
impl<T, V> View<T> for Min<T, V>
where
T: Float,
V: View<T>,
{
fn update(&mut self, val: T) {
debug_assert!(val.is_finite(), "value must be finite");
self.view.update(val);
let Some(val) = self.view.last() else { return };
debug_assert!(val.is_finite(), "value must be finite");
if self.q_vals.len() >= self.window_len.get() {
let popped = self.q_vals.pop_front().expect("There is a value");
if popped == self.opt_min.expect("Has a minimum value") {
self.opt_min = self
.q_vals
.iter()
.copied()
.min_by(|a, b| a.partial_cmp(b).expect("Can compare elements"));
}
}
self.q_vals.push_back(val);
if let Some(min) = self.opt_min.as_mut() {
if val < *min {
*min = val;
}
} else {
self.opt_min = Some(val);
}
}
#[inline(always)]
fn last(&self) -> Option<T> {
self.opt_min
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::pure_functions::Echo;
#[test]
fn min() {
const WINDOW_LEN: NonZeroUsize = NonZeroUsize::new(3).unwrap();
let mut v = Min::new(Echo::new(), WINDOW_LEN);
assert_eq!(v.last(), None);
v.update(1.0);
assert_eq!(v.last(), Some(1.0));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(2.0);
assert_eq!(v.last(), Some(1.0));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(0.5);
assert_eq!(v.last(), Some(0.5));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(1.1);
assert_eq!(v.last(), Some(0.5));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(1.2);
assert_eq!(v.last(), Some(0.5));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(1.3);
assert_eq!(v.last(), Some(1.1));
assert_eq!(v.window_len(), WINDOW_LEN);
v.update(1.4);
assert_eq!(v.last(), Some(1.2));
assert_eq!(v.window_len(), WINDOW_LEN);
}
}