diff --git a/src/num.rs b/src/num.rs index 36ba60c..3769276 100644 --- a/src/num.rs +++ b/src/num.rs @@ -257,6 +257,7 @@ mod tests { #[test] fn test_gcd() { + assert_eq!(u8::gcd(2, 3), 1); assert_eq!(u8::gcd(2, 2), 2); assert_eq!(u8::gcd(2, 8), 2); assert_eq!(u16::gcd(36, 48), 12); @@ -264,6 +265,8 @@ mod tests { #[test] fn test_lcm() { + assert_eq!(u32::lcm(2, 8), 8); + assert_eq!(u16::lcm(2, 3), 6); assert_eq!(usize::lcm(15, 30), 30); assert_eq!(u128::lcm(1, 5), 5); } diff --git a/src/rational.rs b/src/rational.rs index 2acfef0..55c23a6 100644 --- a/src/rational.rs +++ b/src/rational.rs @@ -32,10 +32,10 @@ macro_rules! frac { frac!($w) + frac!($n / $d) }; ($n:literal / $d:literal) => { - frac($n, $d) + $crate::rational::Frac::new($n, $d) }; ($w:literal) => { - frac($w, 1) + $crate::rational::Frac::new($w, 1) }; } @@ -45,35 +45,44 @@ enum FracOp { Other, } -/// Create a new rational number from signed or unsigned integers -#[allow(dead_code)] -fn frac, U: Unsigned>(n: T, d: T) -> Frac { - let mut sign = Sign::Positive; - - if n.is_neg() { - sign = !sign; - } - - if d.is_neg() { - sign = !sign; - } - - let numer = n.to_unsigned(); - let denom = d.to_unsigned(); - - Frac::new(numer, denom, sign) -} - impl Frac { - /// Create a new rational number from unsigned integers and a sign + /// Create a new rational number from signed or unsigned arguments /// /// Generally, you will probably prefer to use the [frac!](../macro.frac.html) macro - /// instead, as that accepts both signed and unsigned arguments - pub fn new(n: T, d: T, s: Sign) -> Frac { - Self::new_raw(n, d, s).reduce() + /// instead, as that is easier for mixed fractions and whole numbers + pub fn new>(n: N, d: N) -> Frac { + Self::new_unreduced(n, d).reduce() } - fn new_raw(n: T, d: T, s: Sign) -> Frac { + /// Create a new rational number from signed or unsigned arguments + /// where the resulting fraction is not reduced + pub fn new_unreduced>(n: N, d: N) -> Frac { + if d.is_zero() { + panic!("Fraction can not have a zero denominator"); + } + + let mut sign = Sign::Positive; + + if n.is_neg() { + sign = !sign; + } + + if d.is_neg() { + sign = !sign; + } + + let numer = n.to_unsigned(); + let denom = d.to_unsigned(); + + Frac { + numer, + denom, + sign, + } + } + + /// Create a new rational from the raw parts + fn raw(n: T, d: T, s: Sign) -> Frac { if d.is_zero() { panic!("Fraction can not have a zero denominator"); } @@ -82,7 +91,7 @@ impl Frac { numer: n, denom: d, sign: s, - } + }.reduce() } /// Determine the output sign given the two input signs @@ -108,51 +117,63 @@ impl Frac { } } -impl + Sub + Mul + Div> PartialOrd - for Frac +impl PartialOrd for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl + Sub + Mul + Div> Ord - for Frac +impl Ord for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn cmp(&self, other: &Self) -> Ordering { - if self.sign == other.sign { - if self.denom == other.denom { - return self.numer.cmp(&other.numer); + if self.sign != other.sign { + return if self.sign == Sign::Positive { + Ordering::Greater } else { - // @TODO fix logic for comparing fractions of different denominators - let gcd = T::gcd(self.denom, other.denom); - let x = gcd / self.denom; - let y = gcd / self.denom; - let mut a = self.clone(); - let mut b = other.clone(); - - a.numer = a.numer * x; - a.denom = a.denom * x; - - b.numer = b.numer * y; - b.denom = b.denom * y; - - assert_eq!(a.denom, b.denom, "Denominators should be equal here. {:#?}{:#?}{:?}{:?}", a, b, x, y); - - a.numer.cmp(&b.numer) - } - } else { - if self.sign == Sign::Positive { - return Ordering::Greater; - } else { - return Ordering::Less; - } + Ordering::Less + }; } + + if self.denom == other.denom { + return self.numer.cmp(&other.numer); + } + + let mut a = self.reduce(); + let mut b = other.reduce(); + + if a.denom == b.denom { + assert!(false, "{:#?}\n{:#?}", a, b); + return a.numer.cmp(&b.numer); + } + + let lcm = T::lcm(self.denom, other.denom); + let x = lcm / self.denom; + let y = lcm / other.denom; + + a.numer *= x; + a.denom *= x; + + b.numer *= y; + b.denom *= y; + + debug_assert_eq!( + a.denom, b.denom, + "Denominators should be equal here. \n{:#?}\n{:#?}\n{:?}\n{:?}", + a, b, x, y + ); + + a.numer.cmp(&b.numer) } } -impl + Sub + Mul + Div> Mul - for Frac +impl Mul for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { type Output = Self; @@ -161,20 +182,22 @@ impl + Sub + Mul + Div + Sub + Mul + Div> MulAssign - for Frac +impl MulAssign for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn mul_assign(&mut self, rhs: Self) { *self = self.clone() * rhs } } -impl + Sub + Mul + Div> Div - for Frac +impl Div for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { type Output = Self; @@ -183,20 +206,22 @@ impl + Sub + Mul + Div + Sub + Mul + Div> DivAssign - for Frac +impl DivAssign for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn div_assign(&mut self, rhs: Self) { *self = self.clone() / rhs } } -impl + Sub + Mul + Div> Add - for Frac +impl Add for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { type Output = Self; @@ -222,27 +247,29 @@ impl + Sub + Mul + Div + Sub + Mul + Div> AddAssign - for Frac +impl AddAssign for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn add_assign(&mut self, rhs: Self) { *self = self.clone() + rhs } } -impl + Sub + Mul + Div> Sub - for Frac +impl Sub for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { type Output = Self; @@ -257,19 +284,20 @@ impl + Sub + Mul + Div + Sub + Mul + Div> SubAssign - for Frac +impl SubAssign for Frac +where + T: Unsigned + Add + Sub + Mul + Div, { fn sub_assign(&mut self, rhs: Self) { *self = self.clone() - rhs @@ -317,7 +345,10 @@ mod tests { #[test] fn cmp_test() { - // assert_eq!(Ordering::Equal, Frac::new_raw(1u8, 2u8, Sign::Positive).cmp(&Frac::new_raw(4u8, 8u8, Sign::Positive))); + assert!(-frac!(5 / 3) < frac!(1 / 10_000)); + assert!(frac!(1 / 10_000) > -frac!(10)); + assert!(frac!(1 / 3) < frac!(1 / 2)); + assert_eq!(frac!(1 / 2), frac!(3 / 6)); } #[test]