oxygen_quark 0.0.11

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

use std::cmp;
use std::fmt;
use std::ops;

use crate::fraction::{Fraction, ZERO};
//use imaginary::complex::Complex;
//use matrix::complex_matrix2x2::ComplexMatrix2x2;
//use matrix::matrix4x4::Matrix4x4;

pub struct Quaternion {
    /// Real component of the quaternion.
    pub r: Fraction,
    /// i-component of the quaternion.
    pub x: Fraction,
    /// j-component of the quaternion.
    pub y: Fraction,
    /// k-component of the quaternion.
    pub z: Fraction,
}

impl Quaternion {
    /// Generates a new `Quaternion`.
    ///
    pub fn new(r: Fraction, x: Fraction, y: Fraction, z: Fraction) -> Quaternion {
        Quaternion { r, x, y, z }
    }

    /// Returns the conjugate of the `Quaternion`, another `Quaternion`.
    ///
    pub fn conjugate(&self) -> Quaternion {
        Quaternion::new(self.r, -self.x, -self.y, -self.z)
    }

    /// Returns the norm of the `Quaternion`, a `Fraction`.
    pub fn norm(&self) -> Fraction {
        (self.r * self.r + self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
    }

    /// Scales the `Quaternion`. Returns a new `Quaternion`.
    ///
    pub fn scale(&self, scale: Fraction) -> Quaternion {
        Quaternion::new(
            self.r * scale,
            self.x * scale,
            self.y * scale,
            self.z * scale,
        )
    }

    /*
        pub fn as_complex_matrix2x2(&self) -> ComplexMatrix2x2 {
            ComplexMatrix2x2::new()
        }
    */
    /*
        /// ```
        /// # extern crate oxygen_quark as quark;
        /// # use quark::imaginary::quaternion::Quaternion;
        /// # use quark::fraction::Fraction;
        /// # fn main() {
        /// # assert!(false);
        /// # }
        /// ```
        pub fn as_matrix4x4(&self) -> Matrix4x4 {
            Matrix4x4::new()
        }
    */
}

impl PartialEq for Quaternion {
    fn eq(&self, other: &Quaternion) -> bool {
        self.r == other.r && self.x == other.x && self.y == other.y && self.z == other.z
    }
}

impl Eq for Quaternion {}

impl PartialOrd for Quaternion {
    fn partial_cmp(&self, other: &Quaternion) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Quaternion {
    fn cmp(&self, other: &Quaternion) -> cmp::Ordering {
        if (self.r >= other.r && self.x >= other.x && self.y >= other.y && self.z > other.z)
            || (self.r >= other.r && self.x >= other.x && self.y > other.y && self.z >= other.z)
            || (self.r >= other.r && self.x > other.x && self.y >= other.y && self.z >= other.z)
            || (self.r > other.r && self.x >= other.x && self.y >= other.y && self.z >= other.z)
        {
            return cmp::Ordering::Greater;
        } else if (self.r <= other.r && self.x <= other.x && self.y <= other.y && self.z < other.z)
            || (self.r <= other.r && self.x <= other.x && self.y < other.y && self.z <= other.z)
            || (self.r <= other.r && self.x < other.x && self.y <= other.y && self.z <= other.z)
            || (self.r < other.r && self.x <= other.x && self.y <= other.y && self.z <= other.z)
        {
            return cmp::Ordering::Less;
        }

        cmp::Ordering::Equal
    }
}

impl fmt::Debug for Quaternion {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut string = String::from("[");
        if self.r != ZERO {
            string.push_str(&self.r.to_string());
        }

        if self.x != ZERO {
            string.push_str(&self.x.to_string());
            string.push('i');
        }

        if self.y != ZERO {
            string.push_str(&self.y.to_string());
            string.push('j');
        }

        if self.z != ZERO {
            string.push_str(&self.z.to_string());
            string.push('k');
        }

        string.push(']');

        write!(f, "{}", string)
    }
}

impl fmt::Display for Quaternion {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut string = String::from("(");
        if self.r != ZERO {
            string.push_str(&self.r.to_string());
        }

        if self.x != ZERO {
            string.push_str(&self.x.to_string());
            string.push('i');
        }

        if self.y != ZERO {
            string.push_str(&self.y.to_string());
            string.push('j');
        }

        if self.z != ZERO {
            string.push_str(&self.z.to_string());
            string.push('k');
        }

        string.push(')');

        write!(f, "{}", string)
    }
}

impl ops::Add for Quaternion {
    type Output = Quaternion;
    fn add(self, other: Quaternion) -> Quaternion {
        Quaternion::new(
            self.r + other.r,
            self.x + other.x,
            self.y + other.y,
            self.z + other.z,
        )
    }
}

impl ops::AddAssign for Quaternion {
    fn add_assign(&mut self, other: Quaternion) {
        self.r += other.r;
        self.x += other.x;
        self.y += other.y;
        self.z += other.z;
    }
}

impl ops::Sub for Quaternion {
    type Output = Quaternion;
    fn sub(self, other: Quaternion) -> Quaternion {
        Quaternion::new(
            self.r - other.r,
            self.x - other.x,
            self.y - other.y,
            self.z - other.z,
        )
    }
}

impl ops::SubAssign for Quaternion {
    fn sub_assign(&mut self, other: Quaternion) {
        self.r -= other.r;
        self.x -= other.x;
        self.y -= other.y;
        self.z -= other.z;
    }
}

impl ops::Mul for Quaternion {
    type Output = Quaternion;
    fn mul(self, other: Quaternion) -> Quaternion {
        Quaternion::new(
            self.r * other.r - self.x * other.x - self.y * other.y - self.z * other.z,
            self.r * other.x + self.x * other.r + self.y * other.z - self.z * other.y,
            self.r * other.y + self.y * other.r - self.x * other.z + self.z * other.x,
            self.r * other.z + self.z * other.r + self.x * other.y - self.y * other.x,
        )
    }
}

impl ops::MulAssign for Quaternion {
    fn mul_assign(&mut self, other: Quaternion) {
        let temp = *self * other;

        self.r = temp.r;
        self.x = temp.x;
        self.y = temp.y;
        self.z = temp.z;
    }
}

impl ops::Div for Quaternion {
    type Output = Quaternion;
    fn div(self, other: Quaternion) -> Quaternion {
        self * (other.conjugate().scale(other.norm().reciprocal()))
    }
}

impl ops::DivAssign for Quaternion {
    fn div_assign(&mut self, other: Quaternion) {
        let temp = *self / other;

        self.r = temp.r;
        self.x = temp.x;
        self.y = temp.y;
        self.z = temp.z;
    }
}

impl ops::Neg for Quaternion {
    type Output = Quaternion;
    fn neg(self) -> Quaternion {
        Quaternion::new(-self.r, -self.x, -self.y, -self.z)
    }
}

impl Copy for Quaternion {}

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

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

    #[test]
    fn new_test() {
        let quaternion1 = Quaternion {
            r: Fraction::new_denom(7, 1),
            x: Fraction::new_denom(5, 1),
            y: Fraction::new_denom(4, 1),
            z: Fraction::new_denom(2, 1),
        };

        let quaternion2 = Quaternion::new(
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        );

        assert_eq!(quaternion1, quaternion2);
    }

    #[test]
    fn addition_test() {
        let quaternion1 = Quaternion::new(
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        );
        let quaternion2 = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(6, 1),
        );

        let result_quaternion = Quaternion::new(
            Fraction::new_denom(10, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(8, 1),
        );

        assert_eq!(result_quaternion, quaternion1 + quaternion2);
        assert_eq!(result_quaternion, quaternion2 + quaternion1);
    }

    #[test]
    fn subtraction_test() {
        let quaternion1 = Quaternion::new(
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        );
        let quaternion2 = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(6, 1),
        );

        let result_quaternion1 = Quaternion::new(
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(-4, 1),
        );
        let result_quaternion2 = Quaternion::new(
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(-3, 1),
            Fraction::new_denom(-3, 1),
            Fraction::new_denom(4, 1),
        );

        assert_eq!(result_quaternion1, quaternion1 - quaternion2);
        assert_eq!(result_quaternion2, quaternion2 - quaternion1);
    }

    #[test]
    fn multiplication_test() {
        let quaternion1 = Quaternion::new(
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        );
        let quaternion2 = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(6, 1),
        );

        let result_quaternion1 = Quaternion::new(
            Fraction::new_denom(-5, 1),
            Fraction::new_denom(51, 1),
            Fraction::new_denom(-7, 1),
            Fraction::new_denom(45, 1),
        );
        let result_quaternion2 = Quaternion::new(
            Fraction::new_denom(-5, 1),
            Fraction::new_denom(7, 1),
            Fraction::new_denom(45, 1),
            Fraction::new_denom(51, 1),
        );

        assert_eq!(result_quaternion1, quaternion1 * quaternion2);
        assert_eq!(result_quaternion2, quaternion2 * quaternion1);
    }

    #[test]
    fn division_test() {
        let quaternion1 = Quaternion::new(
            Fraction::new_denom(7, 1),
            Fraction::new_denom(5, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(2, 1),
        );
        let quaternion2 = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(2, 1),
            Fraction::new_denom(1, 1),
            Fraction::new_denom(6, 1),
        );

        let result_quaternion1 = Quaternion::new(
            Fraction::new_denom(4_700_000, 707_107),
            Fraction::new_denom(-2_100_000, 707_107),
            Fraction::new_denom(3_100_000, 707_107),
            Fraction::new_denom(-3_300_000, 707_107),
        );
        let result_quaternion2 = Quaternion::new(
            Fraction::new_denom(146_875, 30_298),
            Fraction::new_denom(65_625, 30_298),
            Fraction::new_denom(-96_875, 30_298),
            Fraction::new_denom(103_125, 30_298),
        );

        assert_eq!(result_quaternion1, quaternion1 / quaternion2);
        assert_eq!(result_quaternion2, quaternion2 / quaternion1);
    }

    #[test]
    fn scale_test() {
        let quaternion = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
        );
        let scale = Fraction::new_denom(5, 1);

        let result_quaternion = Quaternion::new(
            Fraction::new_denom(3 * 5, 1),
            Fraction::new_denom(4 * 5, 1),
            Fraction::new_denom(3 * 5, 1),
            Fraction::new_denom(4 * 5, 1),
        );

        assert_eq!(result_quaternion, quaternion.scale(scale));
    }

    #[test]
    fn conjugate_test() {
        let quaternion = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
        );

        let result_quaternion = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(-3, 1),
            Fraction::new_denom(-4, 1),
        );

        assert_eq!(result_quaternion, quaternion.conjugate());
    }

    #[test]
    fn norm_test() {
        let quaternion = Quaternion::new(
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
            Fraction::new_denom(4, 1),
        );

        let a = Fraction::new_denom(3, 1);
        let b = Fraction::new_denom(4, 1);
        let c = Fraction::new_denom(3, 1);
        let d = Fraction::new_denom(4, 1);

        let result = (a * a + b * b + c * c + d * d).sqrt();

        assert_eq!(result, quaternion.norm());
    }

    /*
        #[test]
        fn as_complex_matrix2x2_test() {
            let quaternion = Quaternion::new(Fraction::new_denom(3, 1), Fraction::new_denom(4, 1), Fraction::new_denom(3, 1), Fraction::new_denom(4, 1));

            let values: [Complex; 4] = [
                Complex::new(Fraction::new_denom(0, 1), Fraction::new_denom(0, 1)), Complex::new(Fraction::new_denom(0, 1), Fraction::new_denom(0, 1)),
                Complex::new(Fraction::new_denom(0, 1), Fraction::new_denom(0, 1)), Complex::new(Fraction::new_denom(0, 1), Fraction::new_denom(0, 1)),
            ];

            let result_matrix = ComplexMatrix2x2::new_from_values(&values);

            assert_eq!(result_matrix, quaternion.as_complex_matrix2x2());
        }
    */
    /*
        #[test]
        fn as_matrix4x4_test() {
            let quaternion = Quaternion::new(Fraction::new_denom(3, 1), Fraction::new_denom(4, 1), Fraction::new_denom(3, 1), Fraction::new_denom(4, 1));

            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 result_matrix = Matrix4x4::new_from_values(&values);

            assert_eq!(result_matrix, quaternion.as_matrix4x4());
        }
    */
}