#![allow(clippy::wildcard_imports)]
use std::cmp::PartialOrd;
use signalo_traits::Filter;
use signalo_traits::{
Config as ConfigTrait, ConfigClone, ConfigRef, FromGuts, Guts, IntoGuts, Reset,
State as StateTrait, StateMut, WithConfig,
};
#[cfg(feature = "derive_reset_mut")]
use signalo_traits::ResetMut;
use classify::{
slopes::{Config as SlopesConfig, Slope, Slopes, State as SlopesState},
Classification,
};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Peak {
Max,
None,
Min,
}
impl Default for Peak {
fn default() -> Self {
Self::None
}
}
impl Classification<Peak, 3> for Peak {
fn classes() -> [Self; 3] {
[Self::Max, Self::None, Self::Min]
}
}
#[derive(Clone, Debug)]
pub struct Config<U> {
pub outputs: [U; 3],
}
#[derive(Clone, Debug)]
pub struct State<T> {
pub slopes: Slopes<T, Slope>,
pub slope: Option<Slope>,
}
#[derive(Clone, Debug)]
pub struct Peaks<T, U> {
config: Config<U>,
state: State<T>,
}
impl<T, U> Peaks<T, U>
where
U: Clone,
{
fn filter_internal(&mut self, slope: Slope) -> (Slope, usize) {
let index = match self.state.slope {
None | Some(Slope::None) => 1,
Some(Slope::Rising) => {
match &slope {
Slope::Rising | Slope::None => 1, Slope::Falling => 0, }
}
Some(Slope::Falling) => {
match &slope {
Slope::Rising => 2, Slope::None | Slope::Falling => 1, }
}
};
(slope, index)
}
}
impl<T, U> ConfigTrait for Peaks<T, U> {
type Config = Config<U>;
}
impl<T, U> StateTrait for Peaks<T, U> {
type State = State<T>;
}
impl<T, U> WithConfig for Peaks<T, U> {
type Output = Self;
fn with_config(config: Self::Config) -> Self::Output {
let state = {
let slopes = Slopes::with_config(SlopesConfig {
outputs: Slope::classes(),
});
let slope = None;
State { slopes, slope }
};
Self { config, state }
}
}
impl<T, U> ConfigRef for Peaks<T, U> {
fn config_ref(&self) -> &Self::Config {
&self.config
}
}
impl<T, U> ConfigClone for Peaks<T, U>
where
Config<U>: Clone,
{
fn config(&self) -> Self::Config {
self.config.clone()
}
}
impl<T, U> StateMut for Peaks<T, U> {
unsafe fn state_mut(&mut self) -> &mut Self::State {
&mut self.state
}
}
impl<T, U> Guts for Peaks<T, U> {
type Guts = (Config<U>, State<T>);
}
impl<T, U> FromGuts for Peaks<T, U> {
fn from_guts(guts: Self::Guts) -> Self {
let (config, state) = guts;
Self { config, state }
}
}
impl<T, U> IntoGuts for Peaks<T, U> {
fn into_guts(self) -> Self::Guts {
(self.config, self.state)
}
}
impl<T, U> Reset for Peaks<T, U> {
fn reset(self) -> Self {
Self::with_config(self.config)
}
}
#[cfg(feature = "derive_reset_mut")]
impl<T, U> ResetMut for Peaks<T, U> where Self: Reset {}
impl<T, U> Filter<T> for Peaks<T, U>
where
T: Clone + PartialOrd<T>,
U: Clone,
{
type Output = U;
fn filter(&mut self, input: T) -> Self::Output {
let slope = self.state.slopes.filter(input);
let (state, index) = self.filter_internal(slope);
self.state.slope = Some(state);
self.config.outputs[index].clone()
}
}
impl<U> Filter<Slope> for Peaks<Slope, U>
where
U: Clone,
{
type Output = U;
fn filter(&mut self, slope: Slope) -> Self::Output {
let (state, index) = self.filter_internal(slope);
unsafe {
let inner_state = self.state.slopes.state_mut();
*inner_state = SlopesState { input: Some(slope) };
}
self.state.slope = Some(state);
self.config.outputs[index].clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use classify::Classification;
#[test]
fn values() {
use self::Peak::*;
let filter = Peaks::with_config(Config {
outputs: Peak::classes(),
});
let input = vec![
0, 1, 7, 2, 5, 8, 16, 3, 19, 6, 14, 9, 9, 17, 17, 4, 12, 20, 20, 7,
];
let output: Vec<_> = input
.iter()
.scan(filter, |filter, &input| Some(filter.filter(input)))
.collect();
assert_eq!(
output,
vec![
None, None, None, Max, Min, None, None, Max, Min, Max, Min, Max, None, None, None,
None, Min, None, None, None,
]
);
}
#[test]
fn slopes() {
use self::Peak::*;
let filter = Peaks::with_config(Config {
outputs: Peak::classes(),
});
let input = {
use self::Slope::*;
vec![
None, Rising, Rising, Falling, Rising, Rising, Rising, Falling, Rising, Falling,
Rising, Falling, None, Rising, None, Falling, Rising, Rising, None, Falling,
]
};
let output: Vec<_> = input
.iter()
.scan(filter, |filter, input| Some(filter.filter(input.clone())))
.collect();
assert_eq!(
output,
vec![
None, None, None, Max, Min, None, None, Max, Min, Max, Min, Max, None, None, None,
None, Min, None, None, None,
]
);
}
}