norman 0.0.4

Implementations of different norms for elements of vector spaces
Documentation
/******************************************************************************
 * Copyright 2019 Manuel Simon
 * This file is part of the norman library.
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 *****************************************************************************/

//! Implementations of the [`NormEucl`] and [`DistanceEucl`] trait
//! for several types.
//!
//! Below you find a detailed description of how these traits are implemented
//! on different types.
//!
//! # Types with an absolute value
//!
//! If a types implements `Norm<Abs>` or `Distance<Abs>` then `norm_eucl`
//! and `distance_eucl` calculate the abs-norm or the abs-distance respectively.
//!
//! # Vector types
//!
//! For types that represent **onedimensional** vectors (i.e. arrays, `Vec`
//! and the `Array1` type of `ndarray`) `norm_eucl` and `distance_eucl`
//! calculate the euclidean norm or distance respectively.
//!
//! Higher-dimensional ndarrays do not implement the euclidean traits,
//! since they may represent e.g. matrices and for a matrix,
//! simply calculating the square root of the squared sum of the components
//! is no standard norm in any way.

use std::collections::VecDeque;

use num_traits::Float;

use crate::{Norm, Distance};
use crate::desc::{Abs, PNorm};
use super::{NormEucl, DistanceEucl};

impl<T: Norm<Abs>> NormEucl for T {
    type Output = <Self as Norm<Abs>>::Output;
    fn norm_eucl(&self) -> <Self as NormEucl>::Output {
        self.norm(Abs::new())
    }
}

impl<T: Distance<Abs>> DistanceEucl for T {
    type Output = <Self as Distance<Abs>>::Output;
    fn distance_eucl(&self, other: &Self) -> <Self as DistanceEucl>::Output {
        self.distance(other, Abs::new())
    }
}


impl<T: Norm<Abs, Output = R>, R: Float + From<f32>> NormEucl for Vec<T> {
    type Output = R;
    fn norm_eucl(&self) -> R {
        self.norm(PNorm::eucl())
    }
}

impl<T: Distance<Abs, Output = R>, R: Float + From<f32>> DistanceEucl for Vec<T> {
    type Output = R;
    fn distance_eucl(&self, other: &Self) -> R {
        self.distance(other, PNorm::eucl())
    }
}

impl<T: Norm<Abs, Output = R>, R: Float + From<f32>> NormEucl for VecDeque<T> {
    type Output = R;
    fn norm_eucl(&self) -> R {
        self.norm(PNorm::eucl())
    }
}

impl<T: Distance<Abs, Output = R>, R: Float + From<f32>> DistanceEucl for VecDeque<T> {
    type Output = R;
    fn distance_eucl(&self, other: &Self) -> R {
        self.distance(other, PNorm::eucl())
    }
}


#[cfg(feature = "array")]
#[doc(hidden)]
pub mod array {
    use num_traits::Float;
    use ndarray::Array1;

    use crate::{Norm, Distance};
    use crate::desc::{Abs, PNorm};
    use super::{NormEucl, DistanceEucl};

    /// Implements `NormEucl` and `DistanceEucl` for arrays with n elements
    /// whose base type implements `Norm<Abs>`.
    macro_rules! impl_array_eucl {
        ( $( $n:literal )* ) => ($(
            impl<T: Norm<Abs, Output = R>, R: Float + From<f32>> NormEucl for [T; $n] {
                type Output = R;
                fn norm_eucl(&self) -> R {
                    self.norm(PNorm::eucl())
                }
            }

            impl<T: Distance<Abs, Output = R>, R: Float + From<f32>> DistanceEucl for [T; $n] {
                type Output = R;
                fn distance_eucl(&self, other: &Self) -> R {
                    self.distance(other, PNorm::eucl())
                }
            }
        )*)
    }

    impl_array_eucl!(
        0
        1  2  3  4  5  6  7  8
        9 10 11 12 13 14 15 16
        17 18 19 20 21 22 23 24
        25 26 27 28 29 30 31 32
    );
}

#[cfg(feature = "ndarray")]
#[doc(hidden)]
pub mod ndarray {
    use num_traits::Float;
    use ndarray::Array1;

    use crate::{Norm, Distance};
    use crate::desc::{Abs, PNorm};
    use super::{NormEucl, DistanceEucl};

    impl<T: Norm<Abs, Output = R>, R: Float + From<f32>> NormEucl for Array1<T> {
        type Output = R;
        fn norm_eucl(&self) -> R {
            self.norm(PNorm::eucl())
        }
    }
    impl<T: Distance<Abs, Output = R>, R: Float + From<f32>> DistanceEucl for Array1<T> {
        type Output = R;
        fn distance_eucl(&self, other: &Self) -> R {
            self.distance(other, PNorm::eucl())
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::VecDeque;

    use super::super::{DistanceEucl, NormEucl};
    #[test]
    fn norm_eucl_floating_point() {
        assert_eq!((-2.0f32).norm_eucl(), 2.0);
        assert_eq!((-3.0f64).norm_eucl(), 3.0);
    }

    #[test]
    fn distance_eucl_floating_point() {
        assert_eq!((5.0f32).distance_eucl(&3.0), 2.0);
        assert_eq!((2.0f32).distance_eucl(&-3.0), 5.0);
        assert_eq!((1.0f64).distance_eucl(&4.0), 3.0);
    }

    #[test]
    fn norm_eucl_unsigned_integer() {
        assert_eq!(2u8.norm_eucl(), 2);
        assert_eq!(3u16.norm_eucl(), 3);
        assert_eq!(4u32.norm_eucl(), 4);
        assert_eq!(5u64.norm_eucl(), 5);
        assert_eq!(6usize.norm_eucl(), 6);
    }

    #[test]
    fn norm_eucl_signed_integer() {
        assert_eq!((-2i8).norm_eucl(), 2);
        assert_eq!((-3i16).norm_eucl(), 3);
        assert_eq!((-4i32).norm_eucl(), 4);
        assert_eq!((-5i64).norm_eucl(), 5);
        assert_eq!((-6isize).norm_eucl(), 6);
    }

    #[test]
    fn normalize() {
        let mut a = 0.25f32;
        super::super::normalize_eucl(&mut a);
        assert_eq!(a, 1.0);
        let mut a = -3.0f32;
        super::super::normalize_eucl(&mut a);
        assert_eq!(a, -1.0);
    }

    #[test]
    fn normalized() {
        assert_eq!(super::super::normalized_eucl(0.25f32), 1.0);
        assert_eq!(super::super::normalized_eucl(-3.0f32), -1.0);
    }

    #[cfg(feature = "num-complex")]
    mod num_complex {
        use num_complex::Complex;

        use crate::special::{NormEucl, DistanceEucl};

        #[test]
        fn norm_eucl_complex() {
            assert_eq!(Complex::new(3.0f32, 4.0f32).norm_eucl(), 5.0);
            assert_eq!(
                Complex::new(3.0f32, 4.0f32).distance_eucl(&Complex::new(3.0f32, 1.0f32)),
                3.0
            );
        }
    }

    #[test]
    fn norm_eucl_vec() {
        let a = vec![3.0f32, -4.0, 2.0];
        assert_eq!(a.norm_eucl(), 29.0f32.sqrt());
        let b = vec![2.0f32, 2.0, 2.0];
        assert_eq!(a.distance_eucl(&b), 37.0f32.sqrt());
    }

    #[test]
    fn norm_eucl_vec_deque() {
        let a = VecDeque::from(vec![3.0f32, -4.0, 2.0]);
        assert_eq!(a.norm_eucl(), 29.0f32.sqrt());
        let b = VecDeque::from(vec![2.0f32, 2.0, 2.0]);
        assert_eq!(a.distance_eucl(&b), 37.0f32.sqrt());
    }

    #[cfg(feature = "array")]
    #[test]
    fn norm_eucl_array() {
        let a = [3.0f32, -4.0, 2.0];
        assert_eq!(a.norm_eucl(), 29.0f32.sqrt());
        let b = [2.0f32, 2.0, 2.0];
        assert_eq!(a.distance_eucl(&b), 37.0f32.sqrt());
    }

    #[cfg(feature = "ndarray")]
    mod ndarray {
        use ndarray::Array1;

        use crate::special::{NormEucl, DistanceEucl};
        #[test]
        fn norm_eucl_ndarray() {
            let a = Array1::from(vec![3.0f32, -4.0, 2.0]);
            assert_eq!(a.norm_eucl(), 29.0f32.sqrt());
            let b = Array1::from(vec![2.0f32, 2.0, 2.0]);
            assert_eq!(a.distance_eucl(&b), 37.0f32.sqrt());
        }
    }
}