use std::collections::VecDeque;
use indicato_rs_proc::{Apply, Evaluate};
use crate::{
error::{FinError, FinErrorType},
traits::{Apply, Current, Evaluate, Executable, ExecutionContext, IoState},
};
#[derive(Apply, Evaluate)]
pub struct MaximumPeriod {
period: usize,
values: VecDeque<f64>,
}
impl MaximumPeriod {
pub fn new(period: usize) -> Result<Self, FinError> {
match period {
0 => Err(FinError::new(
FinErrorType::InvalidInput,
"Period must be greater than 0",
)),
_ => Ok(Self {
period,
values: VecDeque::with_capacity(period),
}),
}
}
}
impl IoState for MaximumPeriod {
type Input = f64;
type Output = f64;
}
impl Executable for MaximumPeriod {
fn execute(
&mut self,
input: Self::Input,
execution_context: &ExecutionContext,
) -> Self::Output {
match execution_context {
ExecutionContext::Apply => {
self.values.push_back(input);
if self.values.len() > self.period {
self.values.pop_front();
}
self.values.iter().fold(f64::MIN, |acc, &x| acc.max(x))
}
ExecutionContext::Evaluate => self
.values
.iter()
.skip(1)
.fold(f64::MIN, |acc, &x| acc.max(x))
.max(input),
}
}
}
impl Current for MaximumPeriod {
fn current(&self) -> Self::Output {
self.values.iter().fold(f64::MIN, |acc, &x| acc.max(x))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_maximum_period_apply() {
let mut max = MaximumPeriod::new(3).unwrap();
assert_eq!(max.apply(1.0), 1.0);
assert_eq!(max.apply(2.0), 2.0);
assert_eq!(max.apply(3.0), 3.0);
assert_eq!(max.apply(2.0), 3.0);
assert_eq!(max.apply(1.0), 3.0);
assert_eq!(max.apply(0.0), 2.0);
}
#[test]
fn test_maximum_period_evaluate() {
let mut max = MaximumPeriod::new(3).unwrap();
assert_eq!(max.apply(1.0), 1.0);
assert_eq!(max.apply(2.0), 2.0);
assert_eq!(max.apply(3.0), 3.0);
assert_eq!(max.evaluate(5.0), 5.0);
assert_eq!(max.apply(2.0), 3.0);
assert_eq!(max.apply(1.0), 3.0);
assert_eq!(max.apply(0.0), 2.0);
assert_eq!(max.evaluate(0.5), 1.0);
}
}