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::convert;
use std::fmt;
use std::ops;

pub static ZERO: Fraction = Fraction {
    numerator: 0,
    denominator: 1,
};

pub struct Fraction {
    /// The numerator
    pub numerator: i64,
    /// The denominator
    pub denominator: i64,
}

impl Fraction {
    /// Generates a new `Fraction`
    ///
    /// It returns a `Fraction` in its fully reduced form,
    /// after calculating the greatest common divisor.
    ///
    pub fn new(numerator: i64) -> Fraction {
        Fraction {
            numerator,
            denominator: 1,
        }
    }

    /// Generates a new `Fraction`
    ///
    /// It returns a `Fraction` in its fully reduced form,
    /// after calculating the greatest common divisor.
    ///
    /// # Panics
    /// Panics if `denominator` is zero (0).
    ///
    pub fn new_denom(mut numerator: i64, mut denominator: i64) -> Fraction {
        if denominator == 0 {
            panic!("Zero is an invalid denominator!");
        }

        if denominator < 0 {
            numerator *= -1;
            denominator *= -1;
        }

        let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);

        Fraction {
            numerator: numerator / greatest_common_divisor,
            denominator: denominator / greatest_common_divisor,
        }
    }

    /// Returns the greatest common divisor, as an `i64`.
    ///
    /// # Examples
    /// ```
    /// # extern crate oxygen_quark as quark;
    /// # use quark::fraction::Fraction;
    /// # fn main() {
    /// let n1 = 50;
    /// let n2 = 20;
    /// # assert_eq!(10, Fraction::greatest_common_divisor(n1, n2));
    /// # assert_eq!(10, Fraction::greatest_common_divisor(n2, n1));
    ///
    /// // Prints out "10"
    /// println!("{}", Fraction::greatest_common_divisor(n1, n2));
    /// # }
    /// ```
    ///
    pub fn greatest_common_divisor(numerator: i64, denominator: i64) -> i64 {
        let mut x = numerator;
        let mut y = denominator;
        while y != 0 {
            let t = y;
            y = x % y;
            x = t;
        }
        x
    }

    /// Returns the reciprocal of the `Fraction` it's called on.
    ///
    /// Just moves the denominator to the numerator position and
    /// vice versa.
    ///
    /// # Panics
    /// Panics when being called on a `Fraction` with numerator 0.
    ///
    pub fn reciprocal(self) -> Fraction {
        Fraction::new_denom(self.denominator, self.numerator)
    }

    /// Returns the square-root of a `Fraction`. Result is a `Fraction`
    ///
    pub fn sqrt(&self) -> Fraction {
        let mut decimal_mode = false;
        let numerator = match self.numerator {
            1 => 1,
            4 => 2,
            9 => 3,
            16 => 4,
            25 => 5,
            36 => 6,
            49 => 7,
            64 => 8,
            81 => 9,
            100 => 10,
            121 => 11,
            144 => 12,
            169 => 13,
            196 => 14,
            225 => 15,
            256 => 16,
            289 => 17,
            324 => 18,
            361 => 19,
            400 => 20,
            441 => 21,
            484 => 22,
            529 => 23,
            576 => 24,
            625 => 25,
            676 => 26,
            729 => 27,
            784 => 28,
            841 => 29,
            900 => 30,
            _ => {
                decimal_mode = true;
                self.numerator
            }
        };

        let denominator = match self.denominator {
            1 => 1,
            4 => 2,
            9 => 3,
            16 => 4,
            25 => 5,
            36 => 6,
            49 => 7,
            64 => 8,
            81 => 9,
            100 => 10,
            121 => 11,
            144 => 12,
            169 => 13,
            196 => 14,
            225 => 15,
            256 => 16,
            289 => 17,
            324 => 18,
            361 => 19,
            400 => 20,
            441 => 21,
            484 => 22,
            529 => 23,
            576 => 24,
            625 => 25,
            676 => 26,
            729 => 27,
            784 => 28,
            841 => 29,
            900 => 30,
            _ => {
                decimal_mode = true;
                self.denominator
            }
        };

        if decimal_mode {
            Fraction::from((numerator as f64 / denominator as f64).sqrt())
        } else {
            Fraction::new_denom(numerator, denominator)
        }
    }

    /// Returns an approximated `Fraction` of the cosine (cos) of the `Fraction`
    ///
    pub fn cosine(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).cos())
    }

    /// Returns an approximated `Fraction` of the arc cosine (cos^-1) of the `Fraction`
    ///
    pub fn arc_cosine(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).acos())
    }

    /// Returns an approximated `Fraction` of the sine (sin) of the `Fraction`
    ///
    pub fn sine(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).sin())
    }

    /// Returns an approximated `Fraction` of the arc sine (sin^-1) of the `Fraction`
    ///
    pub fn arc_sine(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).asin())
    }

    /// Returns an approximated `Fraction` of the tangent (tan) of the `Fraction`
    ///
    pub fn tangent(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).tan())
    }

    /// Returns an approximated `Fraction` of the arc tangent (tan^-1) of the `Fraction`
    ///
    pub fn arc_tangent(self) -> Fraction {
        let numerator = self.numerator as f64;
        let denominator = self.denominator as f64;
        Fraction::from((numerator / denominator).atan())
    }
}

impl PartialEq for Fraction {
    fn eq(&self, other: &Fraction) -> bool {
        self.numerator == other.numerator && self.denominator == other.denominator
    }
}

impl Eq for Fraction {}

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

impl Ord for Fraction {
    fn cmp(&self, other: &Fraction) -> cmp::Ordering {
        if ((self.numerator <= other.numerator) && self.denominator > other.denominator)
            || (self.numerator < other.numerator && self.denominator == other.denominator)
        {
            return cmp::Ordering::Less;
        } else if (self.denominator <= other.denominator) && self.numerator >= other.numerator {
            return cmp::Ordering::Greater;
        }

        cmp::Ordering::Equal
    }
}

impl fmt::Debug for Fraction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.numerator == self.denominator || self.numerator == 0 || self.denominator == 1 {
            if self.numerator < 0 {
                write!(f, "{}", self.numerator)
            } else {
                write!(f, "+{}", self.numerator)
            }
        } else if self.numerator < 0 {
            write!(f, "-[{}/{}]", -self.numerator, self.denominator)
        } else {
            write!(f, "+[{}/{}]", self.numerator, self.denominator)
        }
    }
}

impl fmt::Display for Fraction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.numerator == self.denominator || self.numerator == 0 || self.denominator == 1 {
            if self.numerator < 0 {
                write!(f, "{}", self.numerator)
            } else {
                write!(f, "+{}", self.numerator)
            }
        } else if self.numerator < 0 {
            write!(f, "-({}/{})", -self.numerator, self.denominator)
        } else {
            write!(f, "+({}/{})", self.numerator, self.denominator)
        }
    }
}

impl ops::Add for Fraction {
    type Output = Fraction;
    fn add(self, other: Fraction) -> Fraction {
        if self.denominator == other.denominator {
            Fraction::new_denom(self.numerator + other.numerator, self.denominator)
        } else {
            Fraction::new_denom(
                self.numerator * other.denominator + other.numerator * self.denominator,
                self.denominator * other.denominator,
            )
        }
    }
}

impl ops::AddAssign for Fraction {
    fn add_assign(&mut self, other: Fraction) {
        let numerator = self.numerator * other.denominator + other.numerator * self.denominator;
        let denominator = self.denominator * other.denominator;

        let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);
        self.numerator = numerator / greatest_common_divisor;
        self.denominator = denominator / greatest_common_divisor;
    }
}

impl ops::Sub for Fraction {
    type Output = Fraction;
    fn sub(self, other: Fraction) -> Fraction {
        if self.denominator == other.denominator {
            Fraction::new_denom(self.numerator - other.numerator, self.denominator)
        } else {
            Fraction::new_denom(
                self.numerator * other.denominator - other.numerator * self.denominator,
                self.denominator * other.denominator,
            )
        }
    }
}

impl ops::SubAssign for Fraction {
    fn sub_assign(&mut self, other: Fraction) {
        if self.denominator == other.denominator {
            let numerator = self.numerator - other.numerator;
            let greatest_common_divisor =
                Fraction::greatest_common_divisor(numerator, self.denominator);

            self.numerator = numerator / greatest_common_divisor;
            self.denominator /= greatest_common_divisor;
        } else {
            let numerator = self.numerator * other.denominator - other.numerator * self.denominator;
            let denominator = self.denominator * other.denominator;

            let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);
            self.numerator = numerator / greatest_common_divisor;
            self.denominator = denominator / greatest_common_divisor;
        }
    }
}

impl ops::Mul for Fraction {
    type Output = Fraction;
    fn mul(self, other: Fraction) -> Fraction {
        Fraction::new_denom(
            self.numerator * other.numerator,
            self.denominator * other.denominator,
        )
    }
}

impl ops::MulAssign for Fraction {
    fn mul_assign(&mut self, other: Fraction) {
        let numerator = self.numerator * other.numerator;
        let denominator = self.denominator * other.denominator;

        let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);

        self.numerator = numerator / greatest_common_divisor;
        self.denominator = denominator / greatest_common_divisor;
    }
}

impl ops::Div for Fraction {
    type Output = Fraction;
    fn div(self, other: Fraction) -> Fraction {
        Fraction::new_denom(
            self.numerator * other.denominator,
            self.denominator * other.numerator,
        )
    }
}

impl ops::DivAssign for Fraction {
    fn div_assign(&mut self, other: Fraction) {
        let numerator = self.numerator * other.denominator;
        let denominator = self.denominator * other.numerator;

        let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);

        self.numerator = numerator / greatest_common_divisor;
        self.denominator = denominator / greatest_common_divisor;
    }
}

impl ops::Neg for Fraction {
    type Output = Fraction;
    fn neg(self) -> Fraction {
        Fraction::new_denom(-self.numerator, self.denominator)
    }
}

impl convert::From<i16> for Fraction {
    fn from(item: i16) -> Self {
        Fraction::new(i64::from(item))
    }
}

impl convert::From<i32> for Fraction {
    fn from(item: i32) -> Self {
        Fraction::new(i64::from(item))
    }
}

impl convert::From<i64> for Fraction {
    fn from(item: i64) -> Self {
        Fraction::new(item)
    }
}

impl convert::From<f64> for Fraction {
    fn from(item: f64) -> Self {
        let denominator = 100_000_f64;
        let numerator = (item * denominator).round();

        Fraction::new_denom(numerator as i64, denominator as i64)
    }
}

impl Copy for Fraction {}

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

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

    #[test]
    fn new_test() {
        let fraction1 = Fraction {
            numerator: 23,
            denominator: 21,
        };

        let fraction2 = Fraction::new_denom(23, 21);

        assert_eq!(fraction1, fraction2);
    }

    #[test]
    fn addition_test() {
        let fraction1 = Fraction::new_denom(5, 2);
        let fraction2 = Fraction::new_denom(4, 3);

        let result_fraction = Fraction::new_denom(23, 6);

        assert_eq!(fraction1 + fraction2, result_fraction);
    }

    #[test]
    fn subtraction_test() {
        let fraction1 = Fraction::new_denom(5, 2);
        let fraction2 = Fraction::new_denom(4, 3);

        let result_fraction1 = Fraction::new_denom(7, 6);
        let result_fraction2 = Fraction::new_denom(-7, 6);

        assert_eq!(fraction1 - fraction2, result_fraction1);
        assert_eq!(fraction2 - fraction1, result_fraction2);
    }

    #[test]
    fn multiplication_test() {
        let fraction1 = Fraction::new_denom(5, 2);
        let fraction2 = Fraction::new_denom(4, 3);

        let result_fraction = Fraction::new_denom(10, 3);

        assert_eq!(fraction1 * fraction2, result_fraction);
    }

    #[test]
    fn division_test() {
        let fraction1 = Fraction::new_denom(5, 2);
        let fraction2 = Fraction::new_denom(4, 3);

        let result_fraction = Fraction::new_denom(15, 8);

        assert_eq!(fraction1 / fraction2, result_fraction);
    }

    #[test]
    fn greatest_common_divisor_test() {
        let numerator = 50;
        let denominator = 20;

        let greatest_common_divisor = Fraction::greatest_common_divisor(numerator, denominator);

        assert_eq!(10, greatest_common_divisor);
    }

    #[test]
    fn negation_test() {
        let fraction = -Fraction::new_denom(5, 2);
        let result_fraction = Fraction::new_denom(-5, 2);

        assert_eq!(fraction, result_fraction);
    }

    #[test]
    fn cosine_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

        let result = Fraction::new_denom(41267, 50000);

        assert_eq!(result, (fraction1 / fraction2).cosine());
    }

    #[test]
    fn arc_cosine_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

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

        assert_eq!(result, (fraction1 / fraction2).arc_cosine());
    }

    #[test]
    fn sine_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

        let result = Fraction::new_denom(3529, 6250);

        assert_eq!(result, (fraction1 / fraction2).sine());
    }

    #[test]
    fn arc_sine_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

        let result = Fraction::new_denom(1287, 2000);

        assert_eq!(result, (fraction1 / fraction2).arc_sine());
    }

    #[test]
    fn tangent_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

        let result = Fraction::new_denom(34207, 50000);

        assert_eq!(result, (fraction1 / fraction2).tangent());
    }

    #[test]
    fn arc_tangent_test() {
        let fraction1 = Fraction::new_denom(3, 1);
        let fraction2 = Fraction::new_denom(5, 1);

        let result = Fraction::new_denom(27021, 50000);

        assert_eq!(result, (fraction1 / fraction2).arc_tangent());
    }
}