#[cfg(test)]
#[path = "../../../tests/unit/algorithms/math/remedian_test.rs"]
mod remedian_test;
use std::cmp::Ordering;
pub type RemedianUsize = Remedian<usize, fn(&usize, &usize) -> Ordering>;
pub struct Remedian<T, F>
where
T: Clone,
F: Fn(&T, &T) -> Ordering,
{
base: usize,
buffers: Vec<Vec<T>>,
order_fn: F,
}
impl<T, F> Remedian<T, F>
where
T: Clone,
F: Fn(&T, &T) -> Ordering,
{
pub fn new(base: usize, order_fn: F) -> Self {
assert!(base > 0);
Self { base, buffers: vec![], order_fn }
}
pub fn add_observation(&mut self, value: T) {
let _ = (0..).try_fold(value, |value, idx| {
if self.buffers.len() <= idx {
self.buffers.push(Vec::with_capacity(self.base))
}
let buffer = self.buffers.get_mut(idx).unwrap();
buffer.push(value);
if buffer.len() < self.base {
return Err(());
}
buffer.sort_by(&self.order_fn);
let value = buffer.get(self.base / 2).unwrap().clone();
buffer.clear();
if idx == 1 {
buffer.push(value);
debug_assert!(self.buffers[0].is_empty());
Err(())
} else {
Ok(value)
}
});
}
pub fn approx_median(&self) -> Option<T> {
let has_not_enough_observations = self.buffers.len() == 1 && self.buffers[0].len() < self.base;
if self.buffers.is_empty() || has_not_enough_observations {
None
} else {
self.buffers.last().and_then(|buffer| buffer.last()).cloned()
}
}
}