use std::{
collections::VecDeque,
num::NonZeroUsize,
};
use getset::CopyGetters;
use num::Float;
use crate::View;
#[derive(Clone, Debug, CopyGetters)]
pub struct HLNormalizer<T, V> {
view: V,
#[getset(get_copy = "pub")]
window_len: NonZeroUsize,
q_vals: VecDeque<T>,
min: T,
max: T,
last: T,
init: bool,
}
impl<T, V> HLNormalizer<T, V>
where
V: View<T>,
T: Float,
{
pub fn new(view: V, window_len: NonZeroUsize) -> Self {
HLNormalizer {
view,
window_len,
q_vals: VecDeque::with_capacity(window_len.get()),
min: T::zero(),
max: T::zero(),
last: T::zero(),
init: true,
}
}
}
fn extent_queue<T: Float>(q: &VecDeque<T>) -> (T, T) {
let mut min = *q.front().unwrap();
let mut max = *q.front().unwrap();
for i in 0..q.len() {
let val = *q.get(i).unwrap();
if val > max {
max = val;
}
if val < min {
min = val;
}
}
(min, max)
}
impl<T, V> View<T> for HLNormalizer<T, V>
where
V: View<T>,
T: Float,
{
fn update(&mut self, val: T) {
debug_assert!(val.is_finite(), "value must be finite");
self.view.update(val);
let Some(view_last) = self.view.last() else {
return;
};
debug_assert!(val.is_finite(), "value must be finite");
if self.init {
self.init = false;
self.min = view_last;
self.max = view_last;
self.last = view_last;
}
if self.q_vals.len() >= self.window_len.get() {
let old = *self.q_vals.front().unwrap();
if old <= self.min || old >= self.max {
let (min, max) = extent_queue(&self.q_vals);
self.min = min;
self.max = max;
}
self.q_vals.pop_front();
}
self.q_vals.push_back(view_last);
if view_last > self.max {
self.max = view_last;
}
if view_last < self.min {
self.min = view_last;
}
self.last = view_last;
}
fn last(&self) -> Option<T> {
if self.last == self.min && self.last == self.max {
Some(T::zero())
} else {
let out = -T::one()
+ (((self.last - self.min) * T::from(2.0).expect("can convert"))
/ (self.max - self.min));
debug_assert!(out.is_finite(), "value must be finite");
Some(out)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
pure_functions::Echo,
test_data::TEST_DATA,
};
#[test]
fn normalizer() {
let mut n = HLNormalizer::new(Echo::new(), NonZeroUsize::new(16).unwrap());
for v in &TEST_DATA {
n.update(*v);
let last = n.last().unwrap();
assert!(last <= 1.0);
assert!(last >= -1.0);
}
}
}