feo-math 0.1.0

Math library for 3D transformation.
Documentation
use crate::{F32Fmt, Primitive, SignOps, /*Sum,*/ Three, Two, polynomial::CubicEquation};

use {
    super::SqMatrix,
    crate::{
        Construct, 
        multivariate::{
            two_unknown::Equa2,
            with_var::WithVar
        },
        linear_algebra::{
            matrix4::Matrix4, 
            vector3::Vector3
        },
        One, Zero
    },
    std::{
        fmt, 
        ops::{Add, Div, Rem, Mul, Neg, Sub}
    }
};

/// A 3 x 3 Matrix with row major order
#[derive(PartialEq, Clone, Copy)]
pub struct Matrix3<T>{pub m:[[T; 3]; 3]}

impl<T> Matrix3<T> {
    pub fn new(
            row0: [T; 3], 
            row1: [T; 3], 
            row2: [T; 3]) -> Self {
        Matrix3 {
            m: [
                row0,
                row1,
                row2
            ]
        }
    }

    #[allow(clippy::too_many_arguments)]
    pub fn new_val (
            r0c0: T, r0c1: T, r0c2: T,
            r1c0: T, r1c1: T, r1c2: T,
            r2c0: T, r2c1: T, r2c2: T) -> Self {
        Matrix3 {
            m: [
                [r0c0, r0c1, r0c2],
                [r1c0, r1c1, r1c2],
                [r2c0, r2c1, r2c2]
            ]
        }
    }

    /// return the identity
    pub fn identity() -> Self
    where T: Zero + One {
        Self::ONE
    }

    /// the trace ...
    pub fn trace(&self) -> T
    where T: Add<T, Output = T> + Copy {
        self.m[0][0] + self.m[1][1] + self.m[2][2]
    }

    /// Calculate the determinant
    ///
    /// # Arguments
    ///
    /// * `self` - The Matrix3 the function was called for
    ///
    #[allow(dead_code)]
    pub fn determinant(&self) -> T 
    where T: Add<T, Output = T> + Sub<T, Output = T> + Mul<T, Output = T> + Copy {
        // matrix (a, b, c)
        //        (d, e, f)
        //        (g, h, i)
        // determinant of a 3x3 matrix is |A| = a(ei − fh)
        //                                    − b(di − fg)
        //                                    + c(dh − eg)

        self.m[0][0] * ((self.m[1][1] * self.m[2][2]) - (self.m[1][2] * self.m[2][1]))
            - self.m[0][1] * ((self.m[1][0] * self.m[2][2]) - (self.m[1][2] * self.m[2][0]))
            + self.m[0][2] * ((self.m[1][0] * self.m[2][1]) - (self.m[1][1] * self.m[2][0]))
    }

    /// Return a transposed matrix
    /// transpose
    /// matrix (a, b, c)
    ///        (d, e, f)
    ///        (g, h, i)
    ///
    /// matrix (a, d, g)
    ///        (b, e, h)
    ///        (c, f, i)
    /// ```rust
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let transposed = Matrix3::new(
    ///     [0.0,2.0,2.0],
    ///     [1.0,0.0,2.0],
    ///     [1.0,1.0,0.0]
    /// ).transpose();
    /// let result = Matrix3::new(
    ///     [0.0,1.0,1.0],
    ///     [2.0,0.0,1.0],
    ///     [2.0,2.0,0.0]
    /// );
    /// assert_eq!(transposed, result);
    /// ```
    pub fn transpose(&self) -> Matrix3<T>
    where T: Copy {
        Matrix3 {
            m: [
                [self.m[0][0], self.m[1][0], self.m[2][0]],
                [self.m[0][1], self.m[1][1], self.m[2][1]],
                [self.m[0][2], self.m[1][2], self.m[2][2]],
            ],
        }
    }

    /// Rule:
    /// Mv = λv
    /// where M = a matrix
    ///       λ = an eigenvalue
    ///       v = a eigenvector
    ///
    /// as a 3x3 Matrix can have  
    ///
    /// if v != <0, 0, 0> then:
    ///     ... // self expl todo
    ///     |M - Iλ| = 0
    ///     ... // in fn
    pub fn eigen_value(&self) -> [Option<T>; 3] 
    where T: F32Fmt<F32Fmt = f32> + Add<T, Output = T> + Sub<T, Output = T> + Mul<T, Output = T> + Div<T, Output = T> + Neg<Output = T> + SignOps + F32Fmt + Zero + One + Three + PartialEq + Copy
        + Rem<T, Output = T> + Two + Primitive + fmt::Debug {
        let m = self.m;
        // 0 = (m[0][0] - λ)((m[1][1] - λ)(m[2][2] - λ) - m[1][2]m[2][1])
        //     - m[0][1](m[1][0](m[2][2] - λ) - m[1][2]m[2][0])
        //     + m[0][2](m[1][0]m[2][1] - (m[1][1] - λ)m[2][0])

        // 0 = (m[0][0] - λ)(m[1][1] - λ)(m[2][2] - λ) - (m[0][0] - λ)m[1][2]m[2][1]
        //     - m[0][1]m[1][0](m[2][2] - λ) + m[0][1]m[1][2]m[2][0]
        //     + m[0][2]m[1][0]m[2][1] - m[0][2](m[1][1] - λ)m[2][0]

        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] = 
        //      (m[0][0] - λ)(m[1][1] - λ)(m[2][2] - λ) 
        //      - (m[0][0] - λ)m[1][2]m[2][1]
        //      - m[0][1]m[1][0](m[2][2] - λ)
        //      - m[0][2](m[1][1] - λ)m[2][0]

        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] = 
        //      (m[0][0] - λ)(m[1][1] - λ)(m[2][2] - λ) 
        //      - m[0][0]m[1][2]m[2][1] + m[1][2]m[2][1]λ
        //      - m[0][1]m[1][0]m[2][2] + m[0][1]m[1][0]λ
        //      - m[0][2]m[1][1]m[2][0] + m[0][2]m[2][0]λ

        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] = 
        //      (m[0][0] - λ)(m[1][1] - λ)(m[2][2] - λ) 
        //      + m[1][2]m[2][1]λ
        //      + m[0][1]m[1][0]λ
        //      + m[0][2]m[2][0]λ

        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] = 
        //      (m[0][0]m[1][1] - m[0][0]λ - m[1][1]λ + λ^2)(m[2][2] - λ) 
        //      + m[1][2]m[2][1]λ
        //      + m[0][1]m[1][0]λ
        //      + m[0][2]m[2][0]λ
        
        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] = 
        //      (m[0][0]m[1][1]m[2][2] - m[0][0]m[2][2]λ - m[1][1]m[2][2]λ + m[2][2]λ^2 - m[0][0]m[1][1]λ + m[0][0]λ^2 + m[1][1]λ^2 - λ^3)
        //      + m[1][2]m[2][1]λ
        //      + m[0][1]m[1][0]λ
        //      + m[0][2]m[2][0]λ
        
        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] = 
        //      (m[0][0]m[1][1]m[2][2] - (m[0][0]m[2][2] + m[1][1]m[2][2] + m[0][0]m[1][1])λ + (m[2][2] + m[0][0] + m[1][1])λ^2 - λ^3)
        //      + m[1][2]m[2][1]λ
        //      + m[0][1]m[1][0]λ
        //      + m[0][2]m[2][0]λ
        
        // -m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] - m[0][0]m[1][1]m[2][2] = 
        //      - (m[0][0]m[2][2] + m[1][1]m[2][2] + m[0][0]m[1][1])λ + (m[2][2] + m[0][0] + m[1][1])λ^2 - λ^3
        //      + m[1][2]m[2][1]λ
        //      + m[0][1]m[1][0]λ
        //      + m[0][2]m[2][0]λ

        // 0 =  - λ^3
        //      + (m[2][2] + m[0][0] + m[1][1])λ^2
        //      - (m[0][0]m[2][2] + m[1][1]m[2][2] + m[0][0]m[1][1] - m[1][2]m[2][1] - m[0][1]m[1][0] - m[0][2]m[2][0])λ
        //      - (-m[0][1]m[1][2]m[2][0] - m[0][2]m[1][0]m[2][1] + m[0][0]m[1][2]m[2][1] + m[0][1]m[1][0]m[2][2] + m[0][2]m[1][1]m[2][0] - m[0][0]m[1][1]m[2][2])
        
        // 0 =  - λ^3
        //      + (m[2][2] + m[0][0] + m[1][1])λ^2
        //      + (-m[0][0]m[2][2] - m[1][1]m[2][2] - m[0][0]m[1][1] + m[1][2]m[2][1] + m[0][1]m[1][0] + m[0][2]m[2][0])λ
        //      + (m[0][1]m[1][2]m[2][0] + m[0][2]m[1][0]m[2][1] - m[0][0]m[1][2]m[2][1] - m[0][1]m[1][0]m[2][2] - m[0][2]m[1][1]m[2][0] + m[0][0]m[1][1]m[2][2])
        
        // 0 =  - λ^3
        //      + (m[2][2] + m[0][0] + m[1][1])λ^2
        //      + (-m[0][0]m[2][2] - m[1][1]m[2][2] - m[0][0]m[1][1] + m[1][2]m[2][1] + m[0][1]m[1][0] + m[0][2]m[2][0])λ
        //      + (m[0][1]m[1][2]m[2][0] + m[0][2]m[1][0]m[2][1] - m[0][0]m[1][2]m[2][1] - m[0][1]m[1][0]m[2][2] - m[0][2]m[1][1]m[2][0] + m[0][0]m[1][1]m[2][2])

        let eigenvalues = CubicEquation::new(
            -T::ONE,
            m[2][2] + m[0][0] + m[1][1],
            -m[0][0] * m[2][2] - m[1][1] * m[2][2] - m[0][0] * m[1][1] + m[1][2] * m[2][1] + m[0][1] * m[1][0] + m[0][2] * m[2][0],
            m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] - m[0][2] * m[1][1] * m[2][0] + m[0][0] * m[1][1] * m[2][2],
        ).solve(); // TOFIX
        
        let mut real_eigenvalues = eigenvalues.iter().map(|answer| match *answer {
            crate::imaginary::PossibleImag::Complex(r, _) => Some(r), // very incorrect
            crate::imaginary::PossibleImag::Imag(_) => None,
            crate::imaginary::PossibleImag::Real(r) => Some(r),
        });

        [real_eigenvalues.next().unwrap(), real_eigenvalues.next().unwrap(), real_eigenvalues.next().unwrap()]
    }

    pub fn eigen_vector(&self, eigen_value: T) -> Vector3<T>
    where T:  Add<T, Output = T> + Sub<T, Output = T> + Mul<T, Output = T> + Div<T, Output = T> + fmt::Debug + Zero + One + Copy {
        let m = (*self - (Matrix3::<T>::identity() * eigen_value)).m;
        let equation_1: Equa2<T> = Equa2::new(m[0][0], WithVar{var: 'y', val: m[1][0]}, WithVar{var: 'z', val: m[2][0]});
        let equation_2: Equa2<T> = Equa2::new(m[0][1], WithVar{var: 'y', val: m[1][1]}, WithVar{var: 'z', val: m[2][1]});
        let y_z = equation_1.set_eq(equation_2); // rmv self make Equa2::set_eq()
        Vector3(T::ONE, y_z.0.1, y_z.1.1)
    }

    /// Get the cofactor matrix of a 3x3 matrix.
    #[allow(clippy::many_single_char_names)]
    pub fn cofactor(self) -> Self 
    where T: Mul<T, Output = T> + Neg<Output = T> + Sub<T, Output = T> + Copy {
        // Cofactor of a matrix
        // matrix (a, b, c)
        //        (d, e, f)
        //        (g, h, i)
        //
        // matrix ( |e, f|,  |d, f|,  |d, e|)
        //        ( |h, i|  -|g, i|   |g, h|)
        //
        //        ( |b, c|,  |a, c|,  |a, b|)
        //        (-|h, i|   |g, i|  -|g, h|)
        //
        //        ( |b, c|,  |a, c|,  |a, b|)
        //        ( |e, f|  -|d, f|   |d, e|)
        //
        // matrix (a, b)
        //        (c, d)
        // determinant of 2x2 matrix if |A| = (a * d) - (b * c)
        //
        // so
        //
        // matrix ( ((e * i) - (f * h)), -((d * i) - (f * g)),  ((d * h) - (e * g)))
        //        (-((b * i) - (c * h)),  ((a * i) - (c * g)), -((a * h) - (b * g)))
        //        ( ((b * f) - (c * e)), -((a * f) - (c * d)),  ((a * e) - (b * d)))

        let Matrix3{
            m: [
                [a, b, c],
                [d, e, f],
                [g, h, i]
            ]
        } = self;

        let a0_0 = (e * i) - (f * h);
        let b0_1 = -((d * i) - (f * g));
        let c0_2 = (d * h) - (e * g);
        let d1_0 = -((b * i) - (c * h));
        let e1_1 = (a * i) - (c * g);
        let f1_2 = -((a * h) - (b * g));
        let g2_0 = (b * f) - (c * e);
        let h2_1 = -((a * f) - (c * d));
        let i2_2 = (a * e) - (b * d);
        
        Matrix3 {
            m: [
                [a0_0, b0_1, c0_2], 
                [d1_0, e1_1, f1_2], 
                [g2_0, h2_1, i2_2]
            ],
        }
    }

    /// Get the adjugate matrix of a 3x3 matrix.
    pub fn adjugate(self) -> Self
    where T: Mul<T, Output = T> + Neg<Output = T> + Sub<T, Output = T> + Copy {
        // The adjugate matrix is equal to the transposed cofactor.
        self.cofactor().transpose()
    }

    /// Get the inverse of a 3x3 matrix.
    /// ```rust
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [1, 1, 0],
    ///     [0, 1, 0],
    ///     [1, 0, 1],
    /// );
    /// let expected = Matrix3::new(
    ///     [ 1, -1,  0],
    ///     [ 0,  1,  0],
    ///     [-1,  1,  1],
    /// );
    /// assert_eq!(mat0.inverse(), expected);
    /// ```
    /// # Arguments
    /// * `self` - The matrix the function is being called for
    pub fn inverse(&self) -> Self 
    where T: Add<T, Output = T> + Sub<T, Output = T> + Mul<T, Output = T> + Div<T, Output = T> + Neg<Output = T> + Copy {

        let determinant: T = self.determinant();

        let adjugate_mat = self.adjugate();

        adjugate_mat / determinant
    }
}

impl<T> Construct<T> for Matrix3<T> where T: Construct<T> {}
impl<T> SqMatrix<T, Vector3<T>> for Matrix3<T> where T: Construct<T> {}

impl<T> fmt::Debug for Matrix3<T>
where T: fmt::Debug + Copy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f,"\n[ {:?}\t {:?}\t {:?}\t ]
                  \n[ {:?}\t {:?}\t {:?}\t ]
                  \n[ {:?}\t {:?}\t {:?}\t ]",
                  self.m[0][0], self.m[0][1], self.m[0][2],
                  self.m[1][0], self.m[1][1], self.m[1][2],
                  self.m[2][0], self.m[2][1], self.m[2][2])
    }
}

impl<T> Add for Matrix3<T>
where T: Add<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> + Matrix3<T>
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [4, 5, 6],
    ///     [6, 4, 5],
    ///     [5, 6, 4]
    /// );
    /// let mat1 = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [5, 7, 9],
    ///     [8, 7, 6],
    ///     [8, 7, 6] 
    /// );
    /// assert_eq!(mat0 + mat1, expected);
    /// ```
    fn add(self, rhs: Self) -> Self::Output {
        Matrix3{
            m: [
                [self.m[0][0] + rhs.m[0][0], self.m[0][1] + rhs.m[0][1], self.m[0][2] + rhs.m[0][2]],
                [self.m[1][0] + rhs.m[1][0], self.m[1][1] + rhs.m[1][1], self.m[1][2] + rhs.m[1][2]],
                [self.m[2][0] + rhs.m[2][0], self.m[2][1] + rhs.m[2][1], self.m[2][2] + rhs.m[2][2]],
            ]
        }
    }
}

impl<T> Add<T> for Matrix3<T>
where T: Add<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> + T
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let val = 2;
    /// let expected = Matrix3::new(
    ///     [3, 4, 5],
    ///     [4, 5, 3],
    ///     [5, 3, 4]
    /// );
    /// assert_eq!(mat + val, expected);
    /// ```
    fn add(self, rhs: T) -> Self::Output {
        Matrix3{
            m: [
                [self.m[0][0] + rhs, self.m[0][1] + rhs, self.m[0][2] + rhs],
                [self.m[1][0] + rhs, self.m[1][1] + rhs, self.m[1][2] + rhs],
                [self.m[2][0] + rhs, self.m[2][1] + rhs, self.m[2][2] + rhs],
            ]
        }
    }
}

impl<T> Sub for Matrix3<T>
where T: Sub<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> - Matrix3<T>
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [4, 5, 6],
    ///     [6, 4, 5],
    ///     [5, 6, 4]
    /// );
    /// let mat1 = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [3, 3, 3],
    ///     [4, 1, 4],
    ///     [2, 5, 2] 
    /// );
    /// assert_eq!(mat0 - mat1, expected);
    /// ```
    fn sub(self, rhs: Self) -> Self::Output {
        Matrix3{
            m: [
                [self.m[0][0] - rhs.m[0][0], self.m[0][1] - rhs.m[0][1], self.m[0][2] - rhs.m[0][2]],
                [self.m[1][0] - rhs.m[1][0], self.m[1][1] - rhs.m[1][1], self.m[1][2] - rhs.m[1][2]],
                [self.m[2][0] - rhs.m[2][0], self.m[2][1] - rhs.m[2][1], self.m[2][2] - rhs.m[2][2]],
            ]
        }
    }
}

impl<T> Sub<T> for Matrix3<T>
where T: Sub<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> - T
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let val = 2;
    /// let expected = Matrix3::new(
    ///     [-1,  0,  1],
    ///     [ 0,  1, -1],
    ///     [ 1, -1,  0]
    /// );
    /// assert_eq!(mat - val, expected);
    /// ```
    fn sub(self, rhs: T) -> Self::Output {
        Matrix3{
            m: [
                [self.m[0][0] - rhs, self.m[0][1] - rhs, self.m[0][2] - rhs],
                [self.m[1][0] - rhs, self.m[1][1] - rhs, self.m[1][2] - rhs],
                [self.m[2][0] - rhs, self.m[2][1] - rhs, self.m[2][2] - rhs],
            ]
        }
    }
}

impl<T> Mul for Matrix3<T>
where T: Add<T, Output = T> + Mul<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> * Matrix3<T>
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [4, 5, 6],
    ///     [6, 4, 5],
    ///     [5, 6, 4]
    /// );
    /// let mat1 = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [2, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [26, 29, 29],
    ///     [24, 29, 32],
    ///     [25, 32, 29] 
    /// );
    /// assert_eq!(mat0 * mat1, expected);
    /// ```
    fn mul(self, rhs: Self) -> Self::Output {
        Matrix3 {
            m: [
                [
                    self.m[0][0] * rhs.m[0][0]
                        + self.m[0][1] * rhs.m[1][0]
                        + self.m[0][2] * rhs.m[2][0],
                    self.m[0][0] * rhs.m[0][1]
                        + self.m[0][1] * rhs.m[1][1]
                        + self.m[0][2] * rhs.m[2][1],
                    self.m[0][0] * rhs.m[0][2]
                        + self.m[0][1] * rhs.m[1][2]
                        + self.m[0][2] * rhs.m[2][2],
                ],
                [
                    self.m[1][0] * rhs.m[0][0]
                        + self.m[1][1] * rhs.m[1][0]
                        + self.m[1][2] * rhs.m[2][0],
                    self.m[1][0] * rhs.m[0][1]
                        + self.m[1][1] * rhs.m[1][1]
                        + self.m[1][2] * rhs.m[2][1],
                    self.m[1][0] * rhs.m[0][2]
                        + self.m[1][1] * rhs.m[1][2]
                        + self.m[1][2] * rhs.m[2][2],
                ],
                [
                    self.m[2][0] * rhs.m[0][0]
                        + self.m[2][1] * rhs.m[1][0]
                        + self.m[2][2] * rhs.m[2][0],
                    self.m[2][0] * rhs.m[0][1]
                        + self.m[2][1] * rhs.m[1][1]
                        + self.m[2][2] * rhs.m[2][1],
                    self.m[2][0] * rhs.m[0][2]
                        + self.m[2][1] * rhs.m[1][2]
                        + self.m[2][2] * rhs.m[2][2],
                ],
            ],            
        }
    }
}

impl<T> Mul<Vector3<T>> for Matrix3<T>
where T: Add<T, Output = T> + Mul<T, Output = T> + Copy {
    type Output = Vector3<T>;

    /// Matrix<T> * Vector3<T>
    /// = Vector3<T>
    /// ```text, no_run
    /// [r0c0, r0c1, r0c2]   [x]   [x']
    /// [r1c0, r1c1, r1c2] * [y] = [y']
    /// [r2c0, r2c1, r2c2]   [z]   [z']
    /// ```
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [3, 2, 1],
    ///     [1, 3, 2],
    ///     [2, 1, 3]
    /// );
    /// let vec = Vector3::new(1, 3, 2);
    /// assert_eq!(mat * vec, Vector3::new(11, 14, 11));
    /// ```
    fn mul(self, rhs: Vector3<T>) -> Self::Output {
        Vector3(
            rhs.0 * self.m[0][0] + rhs.1 * self.m[0][1] + rhs.2 * self.m[0][2],
            rhs.0 * self.m[1][0] + rhs.1 * self.m[1][1] + rhs.2 * self.m[1][2],
            rhs.0 * self.m[2][0] + rhs.1 * self.m[2][1] + rhs.2 * self.m[2][2],
        )
    }
}

impl<T> Mul<T> for Matrix3<T>
where T: Mul<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> * T
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let val = 2;
    /// let expected = Matrix3::new(
    ///     [2, 4, 6],
    ///     [4, 6, 2],
    ///     [6, 2, 4] 
    /// );
    /// assert_eq!(mat * val, expected);
    /// ```
    fn mul(self, other: T) -> Self::Output {
        Matrix3{
            m: [
                [self.m[0][0] * other, self.m[0][1] * other, self.m[0][2] * other],
                [self.m[1][0] * other, self.m[1][1] * other, self.m[1][2] * other],
                [self.m[2][0] * other, self.m[2][1] * other, self.m[2][2] * other],
            ]
        }
    }
}

impl<T> Div for Matrix3<T>
where T: Add<T, Output = T> + Sub<T, Output = T> + Mul<T, Output = T> + Div<T, Output = T> + Neg<Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> / Matrix3<T>
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [4, 5, 6],
    ///     [6, 4, 5],
    ///     [5, 6, 4]
    /// );
    /// let mat1 = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [1, 0, 0],
    ///     [0, 0, 1],
    ///     [0, 1, 0] 
    /// );
    /// assert_eq!(mat0 / mat1, expected);
    /// ```
    fn div(self, rhs: Self) -> Self::Output {
        // self * rhs.inverse()

        let determinant = rhs.determinant();

        let adjugate_mat = rhs.adjugate();

        // first multiply to mitigate error with non Floatingpoint types
        self * adjugate_mat / determinant
    }
}

impl<T> Div<T> for Matrix3<T> 
where T: Div<T, Output = T> + Copy {
    type Output = Matrix3<T>;

    /// Matrix3<T> / T
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let val = 2;
    /// let expected = Matrix3::new(
    ///     [0, 1, 1],
    ///     [1, 1, 0],
    ///     [1, 0, 1]
    /// );
    /// assert_eq!(mat / val, expected);
    /// ```
    /// ```should_panic
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// mat / 0;
    /// ```
    fn div(self, rhs: T) -> Self::Output {
        Matrix3 {
            m: [
                [self.m[0][0] / rhs, self.m[0][1] / rhs, self.m[0][2] / rhs],
                [self.m[1][0] / rhs, self.m[1][1] / rhs, self.m[1][2] / rhs],
                [self.m[2][0] / rhs, self.m[2][1] / rhs, self.m[2][2] / rhs],
            ],
        }
    }
}


impl<T> Rem for Matrix3<T>
where T: Rem<T, Output = T> + Copy {
    type Output = Self;

    /// Matrix3<T> % Matrix3<T>
    /// = Matrix3<T>
    /// Finds the remainder after an ELEMENT WISE division
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat0 = Matrix3::new(
    ///     [4, 5, 6],
    ///     [6, 4, 5],
    ///     [5, 6, 4]
    /// );
    /// let mat1 = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [0, 1, 0],
    ///     [0, 1, 0],
    ///     [2, 0, 0] 
    /// );
    /// assert_eq!(mat0 % mat1, expected);
    /// ```
    fn rem(self, rhs: Self) -> Self::Output {
        Matrix3::new(
            [self.m[0][0] % rhs.m[0][0], self.m[0][1] % rhs.m[0][1], self.m[0][2] % rhs.m[0][2]],
            [self.m[1][0] % rhs.m[1][0], self.m[1][1] % rhs.m[1][1], self.m[1][2] % rhs.m[1][2]],
            [self.m[2][0] % rhs.m[2][0], self.m[2][1] % rhs.m[2][1], self.m[2][2] % rhs.m[2][2]],
        )
    }
}

impl<T> Rem<T> for Matrix3<T> 
where T: Rem<T, Output = T> + Copy {
    type Output = Matrix3<T>;

    /// Matrix3<T> % T
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let val = 2;
    /// let expected = Matrix3::new(
    ///     [1, 0, 1],
    ///     [0, 1, 1],
    ///     [1, 1, 0]
    /// );
    /// assert_eq!(mat % val, expected);
    /// ```
    /// ```should_panic
    /// use feo_math::linear_algebra::vector3::Vector3;
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// mat % 0;
    /// ```
    fn rem(self, rhs: T) -> Self::Output {
        Matrix3 {
            m: [
                [self.m[0][0] % rhs, self.m[0][1] % rhs, self.m[0][2] % rhs],
                [self.m[1][0] % rhs, self.m[1][1] % rhs, self.m[1][2] % rhs],
                [self.m[2][0] % rhs, self.m[2][1] % rhs, self.m[2][2] % rhs],
            ],
        }
    }
}

impl<T> Neg for Matrix3<T>
where T: Neg<Output = T> + Copy {
    type Output = Self;
    
    /// -Matrix3<T>
    /// = Matrix3<T>
    /// # Examples
    /// ```rust
    /// use feo_math::linear_algebra::matrix3::Matrix3;
    /// let mat = Matrix3::new(
    ///     [1, 2, 3],
    ///     [2, 3, 1],
    ///     [3, 1, 2]
    /// );
    /// let expected = Matrix3::new(
    ///     [-1, -2, -3],
    ///     [-2, -3, -1],
    ///     [-3, -1, -2]
    /// );
    /// assert_eq!(-mat, expected);
    /// ```
    fn neg(self) -> Self::Output {
        Matrix3{
            m: [
                [-self.m[0][0], -self.m[0][1], -self.m[0][2]],
                [-self.m[1][0], -self.m[1][1], -self.m[1][2]],
                [-self.m[2][0], -self.m[2][1], -self.m[2][2]],
            ],
        }
    }
}

impl<T> From<Matrix3<T>> for Matrix4<T>
where T: One + Zero + Copy {
    fn from(mat3: Matrix3<T>) -> Matrix4<T> {
        Matrix4::new(
            [mat3.m[0][0], mat3.m[0][1], mat3.m[0][2], T::ZERO],
            [mat3.m[1][0], mat3.m[1][1], mat3.m[1][2], T::ZERO],
            [mat3.m[2][0], mat3.m[2][1], mat3.m[2][2], T::ZERO],
            [     T::ZERO,      T::ZERO,      T::ZERO,  T::ONE]
        )
    }
}

impl<T> Zero for Matrix3<T> where T: Zero + Copy {
    const ZERO: Self = Matrix3 {
        m: [
            [T::ZERO, T::ZERO, T::ZERO],
            [T::ZERO, T::ZERO, T::ZERO],
            [T::ZERO, T::ZERO, T::ZERO]
        ]
    };
}

impl<T> One for Matrix3<T> where T: Zero + One {
    /// The identity matrix
    const ONE: Self = Matrix3 {
        m: [
            [T::ONE , T::ZERO, T::ZERO],
            [T::ZERO, T::ONE , T::ZERO],
            [T::ZERO, T::ZERO, T::ONE ]
        ]
    };
}

impl<T> Two for Matrix3<T> where T: Zero + Two {
    /// The identity matrix * 2 
    const TWO: Self = Matrix3 {
        m: [
            [T::TWO , T::ZERO, T::ZERO],
            [T::ZERO, T::TWO , T::ZERO],
            [T::ZERO, T::ZERO, T::TWO ]
        ]
    };
}

impl<T> From<Matrix3<T>> for [[T; 3]; 3]{
    fn from(other: Matrix3<T>) -> [[T; 3]; 3] {
        other.m
    }
}

impl<T> F32Fmt for Matrix3<T> 
where T: F32Fmt + Copy{ 
    type F32Fmt = Matrix3<T::F32Fmt>;
    #[inline]
    fn intoF32Fmt(self) -> Self::F32Fmt {
        let m = &self.m;
        Matrix3{
            m: [
                [m[0][0].intoF32Fmt(), m[0][1].intoF32Fmt(), m[0][2].intoF32Fmt()],
                [m[1][0].intoF32Fmt(), m[1][1].intoF32Fmt(), m[1][2].intoF32Fmt()],
                [m[2][0].intoF32Fmt(), m[2][1].intoF32Fmt(), m[2][2].intoF32Fmt()],
            ]
        }
    }
    #[inline]
    fn fromF32Fmt(f32_fmt: Self::F32Fmt) -> Self { 
        let m = &f32_fmt.m;
        Matrix3{
            m: [
                [T::fromF32Fmt(m[0][0]), T::fromF32Fmt(m[0][1]), T::fromF32Fmt(m[0][2])],
                [T::fromF32Fmt(m[1][0]), T::fromF32Fmt(m[1][1]), T::fromF32Fmt(m[1][2])],
                [T::fromF32Fmt(m[2][0]), T::fromF32Fmt(m[2][1]), T::fromF32Fmt(m[2][2])],
            ]
        }
    }

    fn sqrt(self) -> Self {
        todo!()
    }

    fn cbrt(self) -> Self { 
        todo!() 
    }

    fn f32_const_mul(self, constant: f32) -> Self {
        Matrix3{
            m: [
                [self.m[0][0].f32_const_mul(constant), self.m[0][1].f32_const_mul(constant), self.m[0][2].f32_const_mul(constant)],
                [self.m[1][0].f32_const_mul(constant), self.m[1][1].f32_const_mul(constant), self.m[1][2].f32_const_mul(constant)],
                [self.m[2][0].f32_const_mul(constant), self.m[2][1].f32_const_mul(constant), self.m[2][2].f32_const_mul(constant)],
            ]
        }
    }

    fn sin_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn cos_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn tan_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn asin_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }
    
    fn acos_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn atan_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn atan2_mul(self, _other: Self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn sinh_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn cosh_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }

    fn tanh_mul(self, _mul_by: Self) -> Self where Self: Mul<Self, Output = Self> + Sized {
        todo!()
    }
}

impl<T> SignOps for Matrix3<T> {
    fn ptcopysign(self, _sign: Self) -> Self {
        todo!()
    }

    fn ptsignum(self) -> i8 {
        todo!()
    }

    fn abs(self) -> Self {
        todo!()
    }
}