s2gpp 1.0.2

Algorithm for Highly Efficient Detection of Correlation Anomalies in Multivariate Time Series
Documentation
use ndarray::ArrayView1;
use num_traits::Float;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::ops::Mul;

#[repr(transparent)]
#[derive(Debug, Clone)]
pub(crate) struct FloatApprox<A>(pub A);

impl<A> FloatApprox<A>
where
    A: Mul + Float,
{
    pub fn approximate(&self, tolerance: usize) -> isize {
        self.0
            .mul(A::from(10 * tolerance).unwrap())
            .to_isize()
            .unwrap()
    }

    pub fn from_array_view_clone(array: ArrayView1<A>) -> Vec<Self> {
        array.into_iter().map(|x| FloatApprox(*x)).collect()
    }

    #[allow(dead_code)]
    pub fn from_array_view(array: ArrayView1<A>) -> Vec<FloatApprox<&A>> {
        array.into_iter().map(FloatApprox).collect()
    }

    pub fn to_base(&self) -> A {
        self.0
    }
}

impl<A> Mul for FloatApprox<A>
where
    A: Mul + Float,
{
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        Self(self.0.mul(rhs.0))
    }
}

impl<A: PartialEq<A>> PartialEq<Self> for FloatApprox<A> {
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl Eq for FloatApprox<f32> {}
impl Eq for FloatApprox<f64> {}

impl<A> Hash for FloatApprox<A>
where
    A: Mul + Float,
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.approximate(8).hash(state);
    }
}

impl PartialOrd<Self> for FloatApprox<f32> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl Ord for FloatApprox<f32> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.approximate(8).cmp(&other.approximate(8))
    }
}

#[cfg(test)]
mod tests {
    use crate::utils::float_approx::FloatApprox;
    use ndarray::aview1;
    use std::collections::HashSet;

    #[test]
    fn approx_float() {
        let a = FloatApprox(0.2);
        let b = FloatApprox(2.0);
        assert_eq!(a * b, FloatApprox(0.4))
    }

    #[test]
    fn approx_float_hash_key() {
        let a = FloatApprox(0.1);
        let mut set = HashSet::new();
        set.insert(a);
        assert_eq!(set.into_iter().last().unwrap(), FloatApprox(0.1))
    }

    #[test]
    fn from_array_view() {
        let arr = aview1(&[1., 2., 3.]);
        let expect = vec![FloatApprox(&1.), FloatApprox(&2.), FloatApprox(&3.)];
        assert_eq!(FloatApprox::from_array_view(arr), expect)
    }

    #[test]
    fn from_array_view_clone() {
        let arr = aview1(&[1., 2., 3.]);
        let expect = vec![FloatApprox(1.), FloatApprox(2.), FloatApprox(3.)];
        assert_eq!(FloatApprox::from_array_view_clone(arr), expect)
    }
}