oxygen_quark 0.0.11

Oxygen Quark is a maths library mainly developed for the Oxygen Game Engine.
Documentation
#![allow(
    clippy::erasing_op,
    clippy::identity_op,
    clippy::suspicious_op_assign_impl,
    clippy::suspicious_arithmetic_impl
)]

use std::fmt;

use super::{Matrix, MatrixError, TransformVector};
use crate::fraction::Fraction;
use crate::vector::{vector2d::Vector2D, vector3d::Vector3D, VectorError};

pub struct Matrix3x3 {
    /// The matrix data.
    pub data: [Fraction; 3 * 3],
}

impl Matrix for Matrix3x3 {
    type Data = Fraction;

    /// Generates a new `Matrix3x3` with values `0`.
    ///
    fn new() -> Self {
        Matrix3x3 {
            data: [
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
            ],
        }
    }

    /// Generates a new `Matrix3x3`from the `Fraction` specified.
    ///
    fn from(value: Self::Data) -> Self {
        Matrix3x3 {
            data: [
                value, value, value, value, value, value, value, value, value,
            ],
        }
    }

    /// Generates a new `Matrix3x3` from a list of `Fraction` values.
    ///
    fn from_slice(values: &[Self::Data]) -> Self {
        let mut new_matrix = Matrix3x3::new();

        new_matrix.data[0] = values[0];
        new_matrix.data[1] = values[1];
        new_matrix.data[2] = values[2];

        new_matrix.data[3] = values[3];
        new_matrix.data[4] = values[4];
        new_matrix.data[5] = values[5];

        new_matrix.data[6] = values[6];
        new_matrix.data[7] = values[7];
        new_matrix.data[8] = values[8];

        new_matrix
    }

    /// Scales the given `Matrix4x4` by the given `Fraction`.
    ///
    fn scale(&self, scale: Self::Data) -> Self {
        let mut scaled_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                scaled_matrix.data[3 * i + j] = self.data[3 * i + j] * scale;
            }
        }

        scaled_matrix
    }

    /// Calculates the determinant of the `Matrix3x3` and returns it, a `Fraction`.
    ///
    fn determinant(&self) -> Result<Self::Data, MatrixError> {
        Ok(self.data[3 * 0 + 0]
            * (self.data[3 * 1 + 1] * self.data[3 * 2 + 2]
                - self.data[3 * 1 + 2] * self.data[3 * 2 + 1])
            - self.data[3 * 0 + 1]
                * (self.data[3 * 1 + 0] * self.data[3 * 2 + 2]
                    - self.data[3 * 1 + 2] * self.data[3 * 2 + 0])
            + self.data[3 * 0 + 2]
                * (self.data[3 * 1 + 0] * self.data[3 * 2 + 1]
                    - self.data[3 * 1 + 1] * self.data[3 * 2 + 0]))
    }

    /// Transposes the `Matrix3x3` and returns the result.
    ///
    fn transpose(&self) -> Self {
        let mut transposed_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                transposed_matrix.data[3 * i + j] = self.data[i + 3 * j];
            }
        }

        transposed_matrix
    }

    /// Returns the cofactor of the `Matrix3x3`.
    ///
    fn cofactor(&self) -> Result<Self, MatrixError> {
        let mut cofactor_matrix = Matrix3x3::new();

        cofactor_matrix.data[3 * 0 + 0] = self.data[3 * 1 + 1] * self.data[3 * 2 + 2]
            - self.data[3 * 1 + 2] * self.data[3 * 2 + 1];
        cofactor_matrix.data[3 * 0 + 1] = -(self.data[3 * 1 + 0] * self.data[3 * 2 + 2]
            - self.data[3 * 1 + 2] * self.data[3 * 2 + 0]);
        cofactor_matrix.data[3 * 0 + 2] = self.data[3 * 1 + 0] * self.data[3 * 2 + 1]
            - self.data[3 * 1 + 1] * self.data[3 * 2 + 0];

        cofactor_matrix.data[3 * 1 + 0] = -(self.data[3 * 0 + 1] * self.data[3 * 2 + 2]
            - self.data[3 * 0 + 2] * self.data[3 * 2 + 1]);
        cofactor_matrix.data[3 * 1 + 1] = self.data[3 * 0 + 0] * self.data[3 * 2 + 2]
            - self.data[3 * 0 + 2] * self.data[3 * 2 + 0];
        cofactor_matrix.data[3 * 1 + 2] = -(self.data[3 * 0 + 0] * self.data[3 * 2 + 1]
            - self.data[3 * 0 + 1] * self.data[3 * 2 + 0]);

        cofactor_matrix.data[3 * 2 + 0] = self.data[3 * 0 + 1] * self.data[3 * 1 + 2]
            - self.data[3 * 0 + 2] * self.data[3 * 1 + 1];
        cofactor_matrix.data[3 * 2 + 1] = -(self.data[3 * 0 + 0] * self.data[3 * 1 + 2]
            - self.data[3 * 0 + 2] * self.data[3 * 1 + 0]);
        cofactor_matrix.data[3 * 2 + 2] = self.data[3 * 0 + 0] * self.data[3 * 1 + 1]
            - self.data[3 * 0 + 1] * self.data[3 * 1 + 0];

        Ok(cofactor_matrix)
    }

    /// Returns the adjugate of the `Matrix3x3`.
    /// Equal to calling cofactor() and then transpose().
    ///
    fn adjugate(&self) -> Result<Self, MatrixError> {
        Ok(self.cofactor().unwrap().transpose())
    }

    /// Returns an `Option<Matrix3x3>`.
    /// It returns an `Option<Matrix3x3>` because there won't always be an inverse (if the determinant is `0`), hence requires some extra checking.
    ///
    /// # Panics
    /// Panics when trying to `unwrap()` a `None`-value.
    ///
    fn inverse(&self) -> Result<Self, MatrixError>
    where
        Self: Sized,
    {
        let determinant = self.determinant().unwrap();
        if determinant == Self::Data::from(0_i16) {
            return Err(MatrixError::ZeroDeterminantError);
        }

        let inverse_matrix = self.adjugate().unwrap();

        Ok(inverse_matrix.scale(Self::Data::from(1_i16) / determinant))
    }
}

impl TransformVector<Vector2D> for Matrix3x3 {
    /// Transforms a `Vector2D` with the `Matrix3x3` and returns the result, a `Option<Vector2D>`.
    ///
    fn transform_vector(&self, other: Vector2D) -> Result<Vector2D, VectorError> {
        Ok(Vector2D {
            data: [
                other[0] * self.data[3 * 0 + 0] + other[1] * self.data[3 * 0 + 1] + self.data[3 * 0 + 2],
                other[0] * self.data[3 * 1 + 0] + other[1] * self.data[3 * 1 + 1] + self.data[3 * 1 + 2],
            ],
        })
    }
}

impl TransformVector<Vector3D> for Matrix3x3 {
    /// Transforms a `Vector3D` with the `Matrix3x3` and returns the result, a `Option<Vector3D>`.
    ///
    fn transform_vector(&self, other: Vector3D) -> Result<Vector3D, VectorError> {
        Ok(Vector3D {
            data: [
                other[0] * self.data[3 * 0 + 0] + other[1] * self.data[3 * 0 + 1] + other[2] * self.data[3 * 0 + 2],
                other[0] * self.data[3 * 1 + 0] + other[1] * self.data[3 * 1 + 1] + other[2] * self.data[3 * 1 + 2],
                other[0] * self.data[3 * 2 + 0] + other[1] * self.data[3 * 2 + 1] + other[2] * self.data[3 * 2 + 2],
            ],
        })
    }
}

impl std::cmp::PartialEq for Matrix3x3 {
    fn eq(&self, other: &Matrix3x3) -> bool {
        for i in 0..3 {
            for j in 0..3 {
                if self.data[3 * i + j] != other[3 * i + j] {
                    return false;
                }
            }
        }

        true
    }
}

impl std::cmp::Eq for Matrix3x3 {}

impl std::ops::Add for Matrix3x3 {
    type Output = Matrix3x3;
    fn add(self, other: Matrix3x3) -> Matrix3x3 {
        let mut sum_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                sum_matrix[3 * i + j] = self.data[3 * i + j] + other[3 * i + j];
            }
        }

        sum_matrix
    }
}

impl std::ops::AddAssign for Matrix3x3 {
    fn add_assign(&mut self, other: Matrix3x3) {
        for i in 0..3 {
            for j in 0..3 {
                self.data[3 * i + j] += other[3 * i + j];
            }
        }
    }
}

impl std::ops::Sub for Matrix3x3 {
    type Output = Matrix3x3;
    fn sub(self, other: Matrix3x3) -> Matrix3x3 {
        let mut difference_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                difference_matrix[3 * i + j] = self.data[3 * i + j] - other[3 * i + j];
            }
        }

        difference_matrix
    }
}

impl std::ops::SubAssign for Matrix3x3 {
    fn sub_assign(&mut self, other: Matrix3x3) {
        for i in 0..3 {
            for j in 0..3 {
                self.data[3 * i + j] -= other[3 * i + j];
            }
        }
    }
}

impl std::ops::Mul for Matrix3x3 {
    type Output = Matrix3x3;
    fn mul(self, other: Matrix3x3) -> Matrix3x3 {
        let mut product_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                for m in 0..3 {
                    product_matrix[3 * i + j] += self.data[3 * i + m] * other[3 * m + j];
                }
            }
        }

        product_matrix
    }
}

impl std::ops::MulAssign for Matrix3x3 {
    fn mul_assign(&mut self, other: Matrix3x3) {
        let mut product_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                for m in 0..3 {
                    product_matrix[3 * i + j] += self.data[3 * i + m] * other[3 * m + j];
                }
            }
        }

        self.data[0..9].clone_from_slice(&product_matrix.data[0..9]);
    }
}

impl std::ops::Neg for Matrix3x3 {
    type Output = Matrix3x3;
    fn neg(self) -> Matrix3x3 {
        let mut negated_matrix = Matrix3x3::new();

        for i in 0..3 {
            for j in 0..3 {
                negated_matrix[i + j * 3] = -self.data[i + j * 3];
            }
        }

        negated_matrix
    }
}

impl std::ops::Index<usize> for Matrix3x3 {
    type Output = Fraction;
    fn index(&self, index: usize) -> &Fraction {
        &self.data[index]
    }
}

impl std::ops::IndexMut<usize> for Matrix3x3 {
    fn index_mut(&mut self, index: usize) -> &mut Fraction {
        &mut self.data[index]
    }
}

impl Copy for Matrix3x3 {}

impl Clone for Matrix3x3 {
    fn clone(&self) -> Matrix3x3 {
        *self
    }
}

impl fmt::Debug for Matrix3x3 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "\n[{}, {}, {}]\n[{}, {}, {}]\n[{}, {}, {}]",
            self.data[3 * 0 + 0],
            self.data[3 * 0 + 1],
            self.data[3 * 0 + 2],
            self.data[3 * 1 + 0],
            self.data[3 * 1 + 1],
            self.data[3 * 1 + 2],
            self.data[3 * 2 + 0],
            self.data[3 * 2 + 1],
            self.data[3 * 2 + 2]
        )
    }
}

impl fmt::Display for Matrix3x3 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "\n({}, {}, {})\n({}, {}, {})\n({}, {}, {})",
            self.data[3 * 0 + 0],
            self.data[3 * 0 + 1],
            self.data[3 * 0 + 2],
            self.data[3 * 1 + 0],
            self.data[3 * 1 + 1],
            self.data[3 * 1 + 2],
            self.data[3 * 2 + 0],
            self.data[3 * 2 + 1],
            self.data[3 * 2 + 2]
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_test() {
        let values: [Fraction; 9] = [
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(0, 1),
        ];

        let matrix1: Matrix3x3 = Matrix3x3 {
            data: [
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
                Fraction::new_denom(0, 1),
            ],
        };

        let matrix2 = Matrix3x3::new();
        let matrix3 = Matrix::from(Fraction::new_denom(0, 1));
        let matrix4 = Matrix3x3::from_slice(&values);

        assert_eq!(matrix1, matrix2);
        assert_eq!(matrix2, matrix3);
        assert_eq!(matrix3, matrix4);
    }

    #[test]
    fn addition_test() {
        let matrix1: Matrix3x3 = Matrix::from(Fraction::new_denom(5, 1));
        let matrix2 = Matrix::from(Fraction::new_denom(6, 1));

        let result_matrix = Matrix::from(Fraction::new_denom(11, 1));

        assert_eq!(matrix1 + matrix2, result_matrix);
        assert_eq!(matrix2 + matrix1, result_matrix);
    }

    #[test]
    fn subtraction_test() {
        let matrix1: Matrix3x3 = Matrix::from(Fraction::new_denom(5, 1));
        let matrix2 = Matrix::from(Fraction::new_denom(6, 1));

        let result_matrix1 = Matrix::from(Fraction::new_denom(-1, 1));
        let result_matrix2 = Matrix::from(Fraction::new_denom(1, 1));

        assert_eq!(matrix1 - matrix2, result_matrix1);
        assert_eq!(matrix2 - matrix1, result_matrix2);
    }

    #[test]
    fn multiplication_test() {
        let values1: [Fraction; 9] = [
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
        ];

        let values2: [Fraction; 9] = [
            Fraction::new_denom(5, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
        ];

        let result_values1: [Fraction; 9] = [
            Fraction::new_denom(62, 1),
            Fraction::new_denom(38, 1),
            Fraction::new_denom(64, 1),
            Fraction::new_denom(51, 1),
            Fraction::new_denom(29, 1),
            Fraction::new_denom(51, 1),
            Fraction::new_denom(52, 1),
            Fraction::new_denom(24, 1),
            Fraction::new_denom(41, 1),
        ];

        let result_values2: [Fraction; 9] = [
            Fraction::new_denom(58, 1),
            Fraction::new_denom(20, 1),
            Fraction::new_denom(42, 1),
            Fraction::new_denom(60, 1),
            Fraction::new_denom(21, 1),
            Fraction::new_denom(51, 1),
            Fraction::new_denom(89, 1),
            Fraction::new_denom(28, 1),
            Fraction::new_denom(53, 1),
        ];

        let matrix1: Matrix3x3 = Matrix3x3::from_slice(&values1);
        let matrix2 = Matrix3x3::from_slice(&values2);
        let result_matrix1 = Matrix3x3::from_slice(&result_values1);
        let result_matrix2 = Matrix3x3::from_slice(&result_values2);

        assert_eq!(matrix1 * matrix2, result_matrix1);
        assert_eq!(matrix2 * matrix1, result_matrix2);
    }

    #[test]
    fn scale_test() {
        let scale = Fraction::new_denom(5, 1);
        let values: [Fraction; 9] = [
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        ];

        let result_values: [Fraction; 9] = [
            Fraction::new_denom(4 * 5, 1),
            Fraction::new_denom(2 * 5, 1),
            Fraction::new_denom(6 * 5, 1),
            Fraction::new_denom(5 * 5, 1),
            Fraction::new_denom(1 * 5, 1),
            Fraction::new_denom(3 * 5, 1),
            Fraction::new_denom(7 * 5, 1),
            Fraction::new_denom(4 * 5, 1),
            Fraction::new_denom(2 * 5, 1),
        ];

        let matrix = Matrix3x3::from_slice(&values);
        let result_matrix = Matrix3x3::from_slice(&result_values);

        assert_eq!(matrix.scale(scale), result_matrix);
    }

    #[test]
    fn transpose_test() {
        let values1: [Fraction; 9] = [
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(8, 1),
            Fraction::new_denom(9, 1),
            Fraction::new_denom(4, 1),
        ];

        let result_values: [Fraction; 9] = [
            Fraction::new_denom(2, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(8, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(9, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(4, 1),
        ];

        let matrix = Matrix3x3::from_slice(&values1);
        let result_matrix = Matrix3x3::from_slice(&result_values);

        assert_eq!(matrix.transpose(), result_matrix);
    }

    #[test]
    fn cofactor_test() {
        let values: [Fraction; 9] = [
            Fraction::new_denom(5, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(4, 1),
        ];

        let result_values: [Fraction; 9] = [
            Fraction::new_denom(-2, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(-29, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(24, 1),
            Fraction::new_denom(-4, 1),
        ];

        let matrix = Matrix3x3::from_slice(&values);
        let result_matrix = Matrix3x3::from_slice(&result_values);

        assert_eq!(matrix.cofactor().unwrap(), result_matrix);
    }

    #[test]
    fn adjugate_test() {
        let values: [Fraction; 9] = [
            Fraction::new_denom(5, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(4, 1),
        ];

        let result_values: [Fraction; 9] = [
            Fraction::new_denom(-2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(-29, 1),
            Fraction::new_denom(24, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(-4, 1),
        ];

        let matrix = Matrix3x3::from_slice(&values);
        let result_matrix = Matrix3x3::from_slice(&result_values);

        assert_eq!(matrix.adjugate().unwrap(), result_matrix);
    }

    #[test]
    fn determinant_test() {
        let values: [Fraction; 9] = [
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(5, 1),
        ];

        let matrix = Matrix3x3::from_slice(&values);

        let determinant = Fraction::new_denom(9, 1);

        assert_eq!(matrix.determinant().unwrap(), determinant);
    }

    #[test]
    fn inverse_test() {
        let values: [Fraction; 9] = [
            Fraction::new_denom(5, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(6, 1),
        ];

        let result_values: [Fraction; 9] = [
            Fraction::new_denom(3, 11),
            Fraction::new_denom(-3, 44),
            Fraction::new_denom(-1, 44),
            Fraction::new_denom(-4, 33),
            Fraction::new_denom(13, 66),
            Fraction::new_denom(-1, 22),
            Fraction::new_denom(-4, 33),
            Fraction::new_denom(-7, 132),
            Fraction::new_denom(9, 44),
        ];

        let matrix = Matrix3x3::from_slice(&values);
        let result_matrix = Matrix3x3::from_slice(&result_values);

        assert_eq!(matrix.inverse().unwrap(), result_matrix);
    }
}