use crate::ops::Skew;
use crate::ops::{Norm, NormSquared};
use math::{BaseNum, InnerSpace, Matrix3, Vector3};
use num_traits::{Float, Zero};
use std::ops::Neg;

impl<T: BaseNum + Float> Norm for Vector3<T> {
    type Output = T;
    fn norm(self) -> T {
        self.magnitude()
    }
}

impl<T: BaseNum + Float> NormSquared for Vector3<T> {
    type Output = T;
    fn norm_squared(self) -> T {
        self.magnitude2()
    }
}

impl<T: Zero + Copy + Neg<Output = T>> Skew for [T; 3] {
    type Output = [[T; 3]; 3];

    /// A skew symmetric representation of the vector that represents the cross product operator.
    fn skew(self) -> Self::Output {
        [
            [T::zero(), self[2], -self[1]],
            [-self[2], T::zero(), self[0]],
            [self[1], -self[0], T::zero()],
        ]
    }
}

impl<T: Zero + Copy + Neg<Output = T>> Skew for Vector3<T> {
    type Output = Matrix3<T>;

    /// A skew symmetric representation of the vector that represents the cross product operator.
    fn skew(self) -> Self::Output {
        let arr: [T; 3] = self.into();
        Matrix3::from(arr.skew())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use math::{Matrix3, Vector3};

    #[test]
    fn skew() {
        let a = [1.0, 2.0, 3.0];
        let b = [4.0, 5.0, 6.0];
        assert_eq!(
            Matrix3::from(a.skew()) * Vector3::from(b),
            Vector3::from(a).cross(Vector3::from(b))
        );
    }
}
