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 crate::fraction::Fraction;
//use imaginary::quaternion::Quaternion;
use super::{Matrix, MatrixError, TransformVector};
use crate::vector::{ VectorError, vector3d::Vector3D };

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

impl Matrix for Matrix4x4 {
    type Data = Fraction;

    /// Generates a new `Matrix4x4` with values `0`.
    ///
    fn new() -> Self {
        Matrix4x4 {
            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),
                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 `Matrix4x4` from the `Fraction` specified.
    ///
    fn from(value: Self::Data) -> Self {
        Matrix4x4 {
            data: [
                value, value, value, value, value, value, value, value, value, value, value, value,
                value, value, value, value,
            ],
        }
    }

    /// Generates a new `Matrix4x4` from a list of `Fraction` values.
    ///
    fn from_slice(values: &[Self::Data]) -> Self {
        let mut new_matrix = Matrix4x4::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.data[9] = values[9];
        new_matrix.data[10] = values[10];
        new_matrix.data[11] = values[11];

        new_matrix.data[12] = values[12];
        new_matrix.data[13] = values[13];
        new_matrix.data[14] = values[14];
        new_matrix.data[15] = values[15];

        new_matrix
    }

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

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

        scaled_matrix
    }

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

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

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

        transposed_matrix
    }

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

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

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

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

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

        Ok(cofactor_matrix)
    }

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

    /// Returns an `Option<Matrix4x4>`.
    /// It returns an `Option<Matrix4x4>` 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()
            .scale(Self::Data::from(1_i16) / determinant);

        Ok(inverse_matrix)
    }
}

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

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

        true
    }
}

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

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

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

        sum_matrix
    }
}

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

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

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

        difference_matrix
    }
}

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

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

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

        product_matrix
    }
}

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

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

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

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

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

        negated_matrix
    }
}

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

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

impl Copy for Matrix4x4 {}

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

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

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

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

    #[test]
    fn new_test() {
        let values: [Fraction; 16] = [
            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),
            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: Matrix4x4 = Matrix4x4 {
            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),
                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 = Matrix4x4::new();
        let matrix3 = Matrix::from(Fraction::new_denom(0, 1));
        let matrix4 = Matrix4x4::from_slice(&values);

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

    #[test]
    fn addition_test() {
        let matrix1: Matrix4x4 = 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: Matrix4x4 = 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; 16] = [
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(3, 1),
        ];

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

        let result_values1: [Fraction; 16] = [
            Fraction::new_denom(92, 1),
            Fraction::new_denom(48, 1),
            Fraction::new_denom(84, 1),
            Fraction::new_denom(79, 1),
            Fraction::new_denom(75, 1),
            Fraction::new_denom(37, 1),
            Fraction::new_denom(67, 1),
            Fraction::new_denom(68, 1),
            Fraction::new_denom(64, 1),
            Fraction::new_denom(28, 1),
            Fraction::new_denom(49, 1),
            Fraction::new_denom(58, 1),
            Fraction::new_denom(90, 1),
            Fraction::new_denom(48, 1),
            Fraction::new_denom(84, 1),
            Fraction::new_denom(85, 1),
        ];

        let result_values2: [Fraction; 16] = [
            Fraction::new_denom(94, 1),
            Fraction::new_denom(32, 1),
            Fraction::new_denom(78, 1),
            Fraction::new_denom(59, 1),
            Fraction::new_denom(72, 1),
            Fraction::new_denom(25, 1),
            Fraction::new_denom(63, 1),
            Fraction::new_denom(54, 1),
            Fraction::new_denom(125, 1),
            Fraction::new_denom(40, 1),
            Fraction::new_denom(89, 1),
            Fraction::new_denom(73, 1),
            Fraction::new_denom(80, 1),
            Fraction::new_denom(28, 1),
            Fraction::new_denom(66, 1),
            Fraction::new_denom(55, 1),
        ];

        let matrix1: Matrix4x4 = Matrix4x4::from_slice(&values1);
        let matrix2 = Matrix4x4::from_slice(&values2);
        let result_matrix1 = Matrix4x4::from_slice(&result_values1);
        let result_matrix2 = Matrix4x4::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; 16] = [
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(6, 1),
        ];

        let result_values: [Fraction; 16] = [
            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(5 * 5, 1),
            Fraction::new_denom(1 * 5, 1),
            Fraction::new_denom(3 * 5, 1),
            Fraction::new_denom(4 * 5, 1),
            Fraction::new_denom(7 * 5, 1),
            Fraction::new_denom(4 * 5, 1),
            Fraction::new_denom(2 * 5, 1),
            Fraction::new_denom(2 * 5, 1),
            Fraction::new_denom(7 * 5, 1),
            Fraction::new_denom(2 * 5, 1),
            Fraction::new_denom(1 * 5, 1),
            Fraction::new_denom(6 * 5, 1),
        ];

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

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

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

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

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

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

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

        let result_values: [Fraction; 16] = [
            Fraction::new_denom(-30, 1),
            Fraction::new_denom(66, 1),
            Fraction::new_denom(26, 1),
            Fraction::new_denom(-26, 1),
            Fraction::new_denom(90, 1),
            Fraction::new_denom(-264, 1),
            Fraction::new_denom(-54, 1),
            Fraction::new_denom(114, 1),
            Fraction::new_denom(-60, 1),
            Fraction::new_denom(210, 1),
            Fraction::new_denom(40, 1),
            Fraction::new_denom(-100, 1),
            Fraction::new_denom(0, 1),
            Fraction::new_denom(6, 1),
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(4, 1),
        ];

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

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

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

        let result_values: [Fraction; 16] = [
            Fraction::new_denom(68, 1),
            Fraction::new_denom(-36, 1),
            Fraction::new_denom(-95, 1),
            Fraction::new_denom(14, 1),
            Fraction::new_denom(92, 1),
            Fraction::new_denom(-110, 1),
            Fraction::new_denom(86, 1),
            Fraction::new_denom(-73, 1),
            Fraction::new_denom(-110, 1),
            Fraction::new_denom(-95, 1),
            Fraction::new_denom(169, 1),
            Fraction::new_denom(8, 1),
            Fraction::new_denom(-55, 1),
            Fraction::new_denom(213, 1),
            Fraction::new_denom(-176, 1),
            Fraction::new_denom(4, 1),
        ];

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

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

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

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

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

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

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

        let result_values: [Fraction; 16] = [
            Fraction::new_denom(252, 555),
            Fraction::new_denom(-63, 555),
            Fraction::new_denom(102, 555),
            Fraction::new_denom(-123, 555),
            Fraction::new_denom(-82, 555),
            Fraction::new_denom(113, 555),
            Fraction::new_denom(-42, 555),
            Fraction::new_denom(18, 555),
            Fraction::new_denom(-109, 555),
            Fraction::new_denom(-19, 555),
            Fraction::new_denom(66, 555),
            Fraction::new_denom(51, 555),
            Fraction::new_denom(-108, 555),
            Fraction::new_denom(27, 555),
            Fraction::new_denom(-123, 555),
            Fraction::new_denom(132, 555),
        ];

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

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