use num_traits::ToPrimitive;
use std::{
cell::RefCell,
collections::HashMap,
fmt::Display,
marker::PhantomData,
ops::{AddAssign, Deref},
};
use ordered_float::OrderedFloat;
#[derive(Debug, Clone)]
pub struct Value(f64);
impl Deref for Value {
type Target = f64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
macro_rules! impl_partial_eq {
($($ty:ty), *) => {
$(
impl PartialEq<$ty> for Value {
fn eq(&self, other: &$ty) -> bool {
self.0 == *other as f64
}
}
)*
};
() => {
};
}
impl_partial_eq!(usize, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
macro_rules! utilities {
($($ty:ty),*) => {
$(
impl AddAssign<$ty> for Moving<$ty> {
fn add_assign(&mut self, other: $ty) {
let _ = self.add(other);
}
}
impl PartialEq<$ty> for Moving<$ty> {
fn eq(&self, other: &$ty) -> bool {
self.mean() == *other as f64
}
}
impl PartialOrd<$ty> for Moving<$ty> {
fn partial_cmp(&self, other: &$ty) -> Option<std::cmp::Ordering> {
self.mean().partial_cmp(&(*other as f64))
}
}
impl PartialEq<Moving<$ty>> for $ty {
fn eq(&self, other: &Moving<$ty>) -> bool {
*self as f64 == other.mean()
}
}
impl PartialOrd<Moving<$ty>> for $ty {
fn partial_cmp(&self, other: &Moving<$ty>) -> Option<std::cmp::Ordering> {
(*self as f64).partial_cmp(&other.mean())
}
}
)*
};
}
macro_rules! partial_non {
($($ty:ty), *) => {
$(
impl PartialEq<f32> for Moving<$ty> {
fn eq(&self, other: &f32) -> bool {
self.mean() == *other as f64
}
}
impl PartialEq<f64> for Moving<$ty> {
fn eq(&self, other: &f64) -> bool {
self.mean() == *other
}
}
)*
};
}
macro_rules! signed {
($($ty:ty), *) => {
$(
impl Sign for $ty {
fn signed() -> bool {
true
}
}
)*
};
}
macro_rules! unsigned {
($($ty:ty), *) => {
$(
impl Sign for $ty {
fn signed() -> bool {
false
}
}
)*
};
}
utilities!(usize, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
partial_non!(usize, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
signed!(i8, i16, i32, i64, i128, f32, f64);
unsigned!(usize, u8, u16, u32, u64, u128);
pub trait Sign {
fn signed() -> bool;
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum MovingError {
NegativeValueToUnsignedType,
Overflow,
Underflow,
CountOverflow,
ThresholdReached,
}
#[derive(Debug, Default)]
pub struct Moving<T> {
count: RefCell<usize>,
mean: RefCell<f64>,
mode: RefCell<HashMap<OrderedFloat<f64>, usize>>,
threshold: f64,
phantom: std::marker::PhantomData<T>,
}
impl<T> Moving<T>
where
T: Sign + ToPrimitive,
{
pub fn new() -> Self {
Self {
count: RefCell::new(0),
mean: RefCell::new(0.0),
mode: RefCell::new(HashMap::new()),
threshold: f64::MAX,
phantom: PhantomData,
}
}
pub fn new_with_threshold(threshold: f64) -> Self {
Self {
count: RefCell::new(0),
mean: RefCell::new(0.0),
mode: RefCell::new(HashMap::new()),
threshold,
phantom: PhantomData,
}
}
pub fn add_with_result(&self, value: T) -> Result<f64, MovingError> {
let value_f64: f64 = value.to_f64().unwrap();
if !T::signed() && value_f64 < 0.0 {
return Err(MovingError::NegativeValueToUnsignedType);
}
let mut count = self.count.borrow_mut();
let mut mean = self.mean.borrow_mut();
let mut mode = self.mode.borrow_mut();
mode.entry(OrderedFloat(value_f64))
.and_modify(|e| *e += 1)
.or_insert(1);
*count += 1;
*mean += (value_f64 - *mean) / *count as f64;
if *mean >= self.threshold {
return Err(MovingError::ThresholdReached);
}
Ok(*mean)
}
pub fn add(&self, value: T) {
let _ = self.add_with_result(value);
}
pub fn mean(&self) -> f64 {
*self.mean.borrow()
}
pub fn mode(&self) -> f64 {
let mode_map = self.mode.borrow();
if mode_map.is_empty() {
return 0.0;
}
let max_count = match mode_map.values().max() {
Some(&max) if max > 1 => max,
_ => return self.mean(),
};
let modes: Vec<_> = mode_map
.iter()
.filter(|&(_, &count)| count == max_count)
.collect();
if modes.len() != 1 {
let mean = self.mean();
let closest = modes
.iter()
.min_by_key(|&&(value, _)| (value.0 - mean).abs() as i64)
.unwrap();
return closest.0.into_inner();
}
modes[0].0.into_inner()
}
pub fn count(&self) -> usize {
*self.count.borrow()
}
}
impl<T> std::fmt::Display for Moving<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.mean.borrow())
}
}
impl<T> PartialEq for Moving<T> {
fn eq(&self, other: &Self) -> bool {
*self.mean.borrow() == *other.mean.borrow()
}
}
impl<T> PartialOrd for Moving<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.mean.borrow().partial_cmp(&*other.mean.borrow())
}
}
impl std::fmt::Display for MovingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(test)]
mod tests {
use crate::Moving;
#[test]
fn mode() {
let moving = Moving::new();
moving.add(10);
moving.add(20);
moving.add(10);
assert_eq!(moving.mode(), 10.0);
}
#[test]
fn big_mode() {
let moving = Moving::new();
for i in 0..10000 {
moving.add(i);
}
assert_eq!(moving.mode(), moving.mean());
moving.add(9999);
assert_eq!(moving.mode(), 9999.0);
}
#[test]
fn double_mode() {
let moving = Moving::new();
moving.add(10);
moving.add(20);
moving.add(10);
moving.add(20);
moving.add(1);
assert_eq!(moving.mode(), 10.0);
moving.add(3000);
assert_eq!(moving.mode(), 20.0);
}
#[test]
fn partial_order() {
let m1 = Moving::new();
let m2 = Moving::new();
m1.add(10);
m2.add(20);
assert!(m1 < m2);
}
#[test]
fn thresholds() {
let moving_threshold = Moving::new_with_threshold(10.0);
let result = moving_threshold.add_with_result(9);
assert_eq!(result.unwrap(), 9.0);
let result = moving_threshold.add_with_result(15);
assert!(result.is_err(), "{:?}", result);
assert_eq!(result.unwrap_err(), crate::MovingError::ThresholdReached);
}
#[test]
fn never_overflow() {
let moving_average: Moving<usize> = Moving::new();
let result = moving_average.add_with_result(usize::MAX);
assert!(result.is_ok());
assert_eq!(result.unwrap(), usize::MAX as f64);
let result = moving_average.add_with_result(usize::MAX);
assert!(result.is_ok());
assert_eq!(result.unwrap(), usize::MAX as f64);
}
#[test]
fn add_moving_average() {
let moving_average: Moving<usize> = Moving::new();
moving_average.add(10);
assert_eq!(moving_average, 10);
moving_average.add(20);
assert_eq!(moving_average, 15);
}
#[test]
fn float_moving_average() {
let moving_average: Moving<f32> = Moving::new();
moving_average.add(10.0);
moving_average.add(20.0);
assert_eq!(moving_average, 15.0);
}
#[test]
fn assign_add() {
let mut moving_average: Moving<usize> = Moving::new();
moving_average.add(10);
moving_average += 20;
assert_eq!(moving_average, 15);
}
#[test]
fn assign_add_float() {
let mut moving_average: Moving<f32> = Moving::new();
moving_average.add(10.0);
moving_average += 20.0;
assert_eq!(moving_average, 15.0);
}
#[test]
fn assign_add_i64() {
let mut moving_average: Moving<i64> = Moving::new();
moving_average.add(10);
moving_average += 20;
assert_eq!(moving_average, 15);
}
#[test]
fn default_works() {
let moving_average: Moving<usize> = Default::default();
assert_eq!(moving_average, 0);
let moving_average: Moving<f32> = Default::default();
assert_eq!(moving_average, 0.0);
}
#[test]
fn binary_operations() {
let moving_average: Moving<usize> = Moving::new();
moving_average.add(10);
moving_average.add(20);
assert!(moving_average < usize::MAX)
}
#[test]
fn binary_operations_float() {
let moving_average: Moving<f32> = Moving::new();
moving_average.add(10.0);
moving_average.add(20.0);
assert!(moving_average < f32::MAX)
}
#[test]
fn many_operations() {
let moving_average: Moving<_> = Moving::new();
for i in 0..1000 {
moving_average.add(i);
}
assert_eq!(moving_average, 999.0 / 2.0);
}
}