//! # Rational Numbers (fractions) //! //! Traits to implement: //! * Add //! * AddAssign //! * Div //! * DivAssign //! * Mul //! * MulAssign //! * Neg //! * Sub //! * SubAssign use crate::num::*; use std::ops::{Mul, Neg}; #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] pub struct Frac { numer: T, denom: T, sign: Sign, } impl Frac { /// Create a new rational number pub fn new(n: T, d: T) -> Self { if d.is_zero() { panic!("Fraction can not have a zero denominator"); } Frac { numer: n, denom: d, sign: Sign::Positive, } .reduce() } pub fn new_neg(n: T, d: T) -> Self { let mut frac = Frac::new(n, d); frac.sign = Sign::Negative; frac } fn reduce(mut self) -> Self { let gcd = T::gcd(self.numer, self.denom); self.numer /= gcd; self.denom /= gcd; self } } macro_rules! impl_mul { ($( $Type: ty ),* ) => { $( impl Mul for Frac<$Type> { type Output = Self; fn mul(self, rhs: Self) -> Self { let numer = self.numer * rhs.numer; let denom = self.denom * rhs.denom; // Figure out the sign if self.sign != rhs.sign { Self::new_neg(numer, denom) } else { Self::new(numer, denom) } } } )* }; } impl_mul!(u8, u16, u32, u64, usize, u128); impl Neg for Frac { type Output = Self; fn neg(self) -> Self::Output { let mut out = self.clone(); out.sign = !self.sign; out } } #[cfg(test)] mod tests { use super::*; #[test] fn mul_test() { let frac1 = Frac::new(1u8, 3u8); let frac2 = Frac::new(2u8, 3u8); let expected = Frac::new(2u8, 9u8); assert_eq!(frac1 * frac2, expected); } }