#![no_std]
use core::mem::MaybeUninit;
use num_traits::{cast, float::FloatCore};
pub struct Window<T: FloatCore, const WINDOW_SIZE: usize> {
window: [T; WINDOW_SIZE],
working_array: [T; WINDOW_SIZE],
oldest: usize, coef: T, }
impl<T: FloatCore, const WINDOW_SIZE: usize> Window<T, WINDOW_SIZE> {
pub fn new(init_val: T, n_sigma: T) -> Self {
assert!(WINDOW_SIZE >= 3, "WINDOW_SIZE must be at least 3");
Self {
window: [init_val; WINDOW_SIZE],
working_array: unsafe { MaybeUninit::uninit().assume_init() },
oldest: 0,
coef: cast::<f32, T>(1.4826).unwrap() * n_sigma, }
}
pub fn update(&mut self, x: T) -> T {
unsafe {*self.window.get_unchecked_mut(self.oldest) = x};
self.oldest = (self.oldest + 1) % WINDOW_SIZE;
self.working_array = self.window;
let w0 = self.get_median();
for w in self.working_array.iter_mut() {
*w = (*w - w0).abs();
}
let s0 = self.get_median();
if (x - w0).abs() <= self.coef * s0 {
x
} else {
#[cfg(feature = "extrapolation")]
{
self.extrapolation()
}
#[cfg(not(feature = "extrapolation"))]
{
w0
}
}
}
fn get_median(&mut self) -> T {
for i in 1..WINDOW_SIZE {
let mut j = i;
while j > 0 {
let j_pre = j - 1;
if unsafe{ self.working_array.get_unchecked(j_pre) > self.working_array.get_unchecked(j) } {
self.working_array.swap(j_pre, j);
j = j_pre;
} else {
break;
}
}
}
self.working_array[WINDOW_SIZE / 2]
}
#[cfg(feature = "extrapolation")]
fn extrapolation(&self) -> T {
let mu_x = cast::<usize, T>(WINDOW_SIZE - 2).unwrap() * cast::<f32, T>(0.5).unwrap();
let mut mu_y = T::zero();
for i in 0..(WINDOW_SIZE - 1) {
mu_y = mu_y + self.window[(self.oldest + i) % WINDOW_SIZE];
}
mu_y = mu_y / cast::<usize, T>(WINDOW_SIZE - 1).unwrap();
let mut numer = T::zero();
let mut denom = T::zero();
for i in 0..(WINDOW_SIZE - 1) {
let dev_x = cast::<usize, T>(i).unwrap() - mu_x;
let dev_y = self.window[(self.oldest + i) % WINDOW_SIZE] - mu_y;
numer = numer + dev_x * dev_y;
denom = denom + dev_x * dev_x;
}
let a = numer / denom; let b = mu_y - a * mu_x;
a * cast::<usize, T>(WINDOW_SIZE - 1).unwrap() + b
}
}