oxygen_quark 0.0.11

Oxygen Quark is a maths library mainly developed for the Oxygen Game Engine.
Documentation
use std::cmp;
use std::fmt;
use std::ops;

use crate::fraction::{Fraction, ZERO};
use crate::matrix::matrix2x2::Matrix2x2;

pub struct Complex {
    /// The real component of the complex number.
    pub real: Fraction,
    /// The imaginary component of the complex number.
    pub imaginary: Fraction,
}

impl Complex {
    /// Generates a new `Complex`.
    ///
    pub fn new(real: Fraction, imaginary: Fraction) -> Complex {
        Complex { real, imaginary }
    }

    /// Returns the conjugate of the `Complex`.
    ///
    pub fn conjugate(self) -> Complex {
        Complex::new(self.real, -self.imaginary)
    }

    /// Returns the norm of the `Complex`.
    ///
    pub fn norm(self) -> Fraction {
        (self.real * self.real + self.imaginary * self.imaginary).sqrt()
    }

    /// Returns the angle of the `Complex` vector.
    ///
    pub fn angle(self) -> Fraction {
        Fraction::arc_tangent(self.imaginary / self.real)
    }

    /// Returns the `Complex` as a `Matrix2x2`.
    ///
    pub fn as_matrix2x2(self) -> Matrix2x2 {
        use crate::matrix::Matrix;
        let values: [Fraction; 4] = [self.real, -self.imaginary, self.imaginary, self.real];

        Matrix2x2::from_slice(&values)
    }
}

impl PartialEq for Complex {
    fn eq(&self, other: &Complex) -> bool {
        self.real == other.real && self.imaginary == other.imaginary
    }
}

impl Eq for Complex {}

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

impl Ord for Complex {
    fn cmp(&self, other: &Complex) -> cmp::Ordering {
        if self.norm() > other.norm() {
            return cmp::Ordering::Greater;
        } else if self.norm() < other.norm() {
            return cmp::Ordering::Less;
        }

        cmp::Ordering::Equal
    }
}

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

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

        string.push(']');

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

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

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

        string.push(')');

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

impl ops::Add for Complex {
    type Output = Complex;
    fn add(self, other: Complex) -> Complex {
        Complex::new(self.real + other.real, self.imaginary + other.imaginary)
    }
}

impl ops::AddAssign for Complex {
    fn add_assign(&mut self, other: Complex) {
        self.real += other.real;
        self.imaginary += other.imaginary;
    }
}

impl ops::Sub for Complex {
    type Output = Complex;
    fn sub(self, other: Complex) -> Complex {
        Complex::new(self.real - other.real, self.imaginary - other.imaginary)
    }
}

impl ops::SubAssign for Complex {
    fn sub_assign(&mut self, other: Complex) {
        self.real -= other.real;
        self.imaginary -= other.imaginary;
    }
}

impl ops::Mul for Complex {
    type Output = Complex;
    fn mul(self, other: Complex) -> Complex {
        Complex::new(
            self.real * other.real - self.imaginary * other.imaginary,
            self.real * other.imaginary + self.imaginary * other.real,
        )
    }
}

impl ops::Div for Complex {
    type Output = Complex;
    fn div(self, other: Complex) -> Complex {
        Complex::new(
            (self.real * other.real + self.imaginary * other.imaginary)
                / (other.real * other.real + other.imaginary * other.imaginary),
            -(self.real * other.imaginary - self.imaginary * other.real)
                / (other.real * other.real + other.imaginary * other.imaginary),
        )
    }
}

impl ops::Neg for Complex {
    type Output = Complex;
    fn neg(self) -> Complex {
        Complex::new(-self.real, -self.imaginary)
    }
}

impl Copy for Complex {}

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

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

    #[test]
    fn new_test() {
        let complex1 = Complex {
            real: Fraction::new_denom(7, 1),
            imaginary: Fraction::new_denom(5, 1),
        };

        let complex2 = Complex::new(Fraction::new_denom(7, 1), Fraction::new_denom(5, 1));

        assert_eq!(complex1, complex2);
    }

    #[test]
    fn addition_test() {
        let complex1 = Complex::new(Fraction::new_denom(7, 1), Fraction::new_denom(5, 1));
        let complex2 = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(2, 1));

        let result_complex = Complex::new(Fraction::new_denom(10, 1), Fraction::new_denom(7, 1));

        assert_eq!(result_complex, complex1 + complex2);
        assert_eq!(result_complex, complex2 + complex1);
    }

    #[test]
    fn subtraction_test() {
        let complex1 = Complex::new(Fraction::new_denom(7, 1), Fraction::new_denom(5, 1));
        let complex2 = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(2, 1));

        let result_complex1 = Complex::new(Fraction::new_denom(4, 1), Fraction::new_denom(3, 1));
        let result_complex2 = Complex::new(Fraction::new_denom(-4, 1), Fraction::new_denom(-3, 1));

        assert_eq!(result_complex1, complex1 - complex2);
        assert_eq!(result_complex2, complex2 - complex1);
    }

    #[test]
    fn multiplication_test() {
        let complex1 = Complex::new(Fraction::new_denom(7, 1), Fraction::new_denom(5, 1));
        let complex2 = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(2, 1));

        let result_complex1 = Complex::new(Fraction::new_denom(11, 1), Fraction::new_denom(29, 1));

        assert_eq!(result_complex1, complex1 * complex2);
    }

    #[test]
    fn division_test() {
        let complex1 = Complex::new(Fraction::new_denom(7, 1), Fraction::new_denom(5, 1));
        let complex2 = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(2, 1));

        let result_complex1 = Complex::new(Fraction::new_denom(31, 13), Fraction::new_denom(1, 13));
        let result_complex2 =
            Complex::new(Fraction::new_denom(31, 74), Fraction::new_denom(-1, 74));

        assert_eq!(result_complex1, complex1 / complex2);
        assert_eq!(result_complex2, complex2 / complex1);
    }

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

        let result_complex = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(-4, 1));

        assert_eq!(result_complex, complex.conjugate());
    }

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

        let result = Fraction::new_denom(5, 1);

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

    #[test]
    fn angle_test() {
        let complex = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(4, 1));

        let result = Fraction::new_denom(9273, 10000);

        assert_eq!(result, complex.angle());
    }

    #[test]
    fn as_matrix2x2_test() {
        use crate::matrix::Matrix;
        let complex = Complex::new(Fraction::new_denom(3, 1), Fraction::new_denom(4, 1));

        let values: [Fraction; 4] = [
            Fraction::new_denom(3, 1),
            Fraction::new_denom(-4, 1),
            Fraction::new_denom(4, 1),
            Fraction::new_denom(3, 1),
        ];

        let result_matrix = Matrix2x2::from_slice(&values);

        assert_eq!(result_matrix, complex.as_matrix2x2());
    }
}