use std::cmp::{Ordering, 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::Classification;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Slope {
Rising,
None,
Falling,
}
impl Default for Slope {
fn default() -> Self {
Self::None
}
}
impl Classification<Slope, 3> for Slope {
fn classes() -> [Self; 3] {
[Self::Rising, Self::None, Self::Falling]
}
}
#[derive(Clone, Debug)]
pub struct Config<U> {
pub outputs: [U; 3],
}
#[derive(Clone, Debug)]
pub struct State<T> {
pub input: Option<T>,
}
#[derive(Clone, Debug)]
pub struct Slopes<T, U> {
config: Config<U>,
state: State<T>,
}
impl<T, U> ConfigTrait for Slopes<T, U> {
type Config = Config<U>;
}
impl<T, U> WithConfig for Slopes<T, U> {
type Output = Self;
fn with_config(config: Self::Config) -> Self::Output {
let state = State { input: None };
Self { config, state }
}
}
impl<T, U> StateTrait for Slopes<T, U> {
type State = State<T>;
}
impl<T, U> ConfigRef for Slopes<T, U> {
fn config_ref(&self) -> &Self::Config {
&self.config
}
}
impl<T, U> ConfigClone for Slopes<T, U>
where
Config<U>: Clone,
{
fn config(&self) -> Self::Config {
self.config.clone()
}
}
impl<T, U> StateMut for Slopes<T, U> {
unsafe fn state_mut(&mut self) -> &mut Self::State {
&mut self.state
}
}
impl<T, U> Guts for Slopes<T, U> {
type Guts = (Config<U>, State<T>);
}
impl<T, U> FromGuts for Slopes<T, U> {
fn from_guts(guts: Self::Guts) -> Self {
let (config, state) = guts;
Self { config, state }
}
}
impl<T, U> IntoGuts for Slopes<T, U> {
fn into_guts(self) -> Self::Guts {
(self.config, self.state)
}
}
impl<T, U> Reset for Slopes<T, U> {
fn reset(self) -> Self {
Self::with_config(self.config)
}
}
#[cfg(feature = "derive_reset_mut")]
impl<T, U> ResetMut for Slopes<T, U> where Self: Reset {}
impl<T, U> Filter<T> for Slopes<T, U>
where
T: Clone + PartialOrd<T>,
U: Clone,
{
type Output = U;
fn filter(&mut self, input: T) -> Self::Output {
let index = match self.state.input {
None => 1, Some(ref state) => {
#[allow(clippy::match_same_arms)]
match state.partial_cmp(&input) {
Some(Ordering::Less) => 0, Some(Ordering::Equal) => 1, Some(Ordering::Greater) => 2, None => 1, }
}
};
self.state.input = Some(input);
self.config.outputs[index].clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use classify::Classification;
#[test]
fn test() {
use self::Slope::*;
let filter = Slopes::with_config(Config {
outputs: Slope::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, Rising, Rising, Falling, Rising, Rising, Rising, Falling, Rising, Falling,
Rising, Falling, None, Rising, None, Falling, Rising, Rising, None, Falling,
]
);
}
}