From d5e6913197ad872039cf021d7130085651787d9b Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 7 Sep 2018 17:50:02 -0700 Subject: [PATCH] [types] Fix up tests for Frac type --- types/src/number/frac.rs | 96 +++++++++++++++++++++++++++---------- types/src/number/integer.rs | 17 ++++--- types/src/number/mod.rs | 1 + 3 files changed, 83 insertions(+), 31 deletions(-) diff --git a/types/src/number/frac.rs b/types/src/number/frac.rs index 0a49a1e..8e56140 100644 --- a/types/src/number/frac.rs +++ b/types/src/number/frac.rs @@ -5,7 +5,7 @@ use std::any::Any; use std::fmt; use std::ops::{Add, Mul}; -use number::arith::{GCD, LCM}; +use number::arith::GCD; use number::{Int, Number}; use object::{Obj, Object}; @@ -15,7 +15,7 @@ pub struct Frac { p: Int, q: Int } impl Frac { pub fn new(p: Int, q: Int) -> Result { - if q == Int(0) { + if q.is_zero() { // TODO: Return a more specific error about dividing by zero. Err(()) } else { @@ -23,29 +23,26 @@ impl Frac { } } + pub fn from_ints(p: i64, q: i64) -> Result { + Frac::new(Int(p), Int(q)) + } + fn reduced(self) -> Frac { let gcd = self.p.gcd(self.q); Frac { p: self.p / gcd, q: self.q / gcd } } fn _add(self, rhs: Frac) -> Frac { - let lcm = self.q.lcm(rhs.q); - let p = self.p * lcm + rhs.p * lcm; - let q = self.q * lcm; - Frac::new(p, q).unwrap() - } -} - -impl Number for Frac { - fn as_int(&self) -> Option { - if self.q == Int(1) { - Some(self.p) - } else { - None - } + let p = self.p * rhs.q + rhs.p * self.q; + let q = self.q * rhs.q; + Frac{p,q}.reduced() } - fn as_frac(&self) -> Option { Frac::new(self.p, self.q).ok() } + fn _mul(self, rhs: Frac) -> Frac { + let p = self.p * rhs.p; + let q = self.q * rhs.q; + Frac{p,q}.reduced() + } } impl Add for Frac { @@ -75,6 +72,41 @@ impl fmt::Display for Frac { } } +impl Mul for Frac { + type Output = Frac; + fn mul(self, rhs: Self) -> Self::Output { + self._mul(rhs) + } +} + +impl<'a> Mul for &'a Frac { + type Output = Frac; + fn mul(self, rhs: Frac) -> Self::Output { + self._mul(rhs) + } +} + +impl<'a, 'b> Mul<&'a Frac> for &'b Frac { + type Output = Frac; + fn mul(self, rhs: &Frac) -> Self::Output { + self._mul(*rhs) + } +} + +impl Number for Frac { + fn as_int(&self) -> Option { + if self.q == Int(1) { + Some(self.p) + } else { + None + } + } + + fn as_frac(&self) -> Option { Frac::new(self.p, self.q).ok() } + + fn is_zero(&self) -> bool { self.p.is_zero() } +} + impl Object for Frac { fn as_any(&self) -> &Any { self } fn as_num(&self) -> Option<&Number> { Some(self) } @@ -100,29 +132,43 @@ impl<'a> PartialEq for Frac { #[cfg(test)] mod tests { + use number::Number; use super::*; + #[test] + fn fracs_with_zero_q_are_invalid() { + assert!(Frac::from_ints(3, 0).is_err()) + } + #[test] fn equal_fracs_are_equal() { - assert_eq!(Frac(Int(3), Int(2)), Frac(Int(3), Int(2))); - assert_ne!(Frac(Int(12), Int(4)), Frac(Int(9), Int(7))); + assert_eq!(Frac::from_ints(3, 2), Frac::from_ints(3, 2)); + assert_ne!(Frac::from_ints(12, 4), Frac::from_ints(9, 7)); } #[test] fn fracs_should_reduce_to_ints_where_possible() { - let rational_as_integer = Frac(Int(3), Int(1)).as_int(); - assert!(rational_as_integer.is_some()); - // Oh my god this line is so dumb. + let fr = Frac::from_ints(3, 1).unwrap(); + assert_eq!(fr.as_int(), Some(Int(3))); } #[test] fn fracs_should_not_reduce_to_ints_where_impossible() { - let rational_as_integer = Frac(Int(3), Int(2)).as_int(); - assert!(rational_as_integer.is_none()); + let fr = Frac::from_ints(3, 2).unwrap(); + assert_eq!(fr.as_int(), None); } #[test] fn fracs_are_exact() { - assert!(Frac(Int(4), Int(2)).is_exact()); + let fr = Frac::from_ints(4, 2).unwrap(); + assert!(fr.is_exact()); + } + + #[test] + fn fracs_can_add() { + let a = Frac::from_ints(5, 6).unwrap(); + let b = Frac::from_ints(2, 3).unwrap(); + let r = Frac::from_ints(3, 2).unwrap(); + assert_eq!(a + b, r); } } diff --git a/types/src/number/integer.rs b/types/src/number/integer.rs index eb146b2..21cb6c4 100644 --- a/types/src/number/integer.rs +++ b/types/src/number/integer.rs @@ -12,6 +12,10 @@ use object::{Obj, Object}; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Int(pub i64); +impl Int { + pub fn zero() -> Int { Int(0) } +} + impl Add for Int { type Output = Int; fn add(self, rhs: Self) -> Self::Output { @@ -63,25 +67,25 @@ impl<'a, 'b> Div<&'a Int> for &'b Int { impl GCD for Int { fn gcd(self, other: Int) -> Int { let (mut a, mut b) = if self > other { - (self.0, other.0) + (self, other) } else { - (other.0, self.0) + (other, self) }; - while b != 0 { + while !b.is_zero() { let r = a % b; a = b; b = r; } - Int(a) + a } } impl LCM for Int { fn lcm(self, other: Int) -> Int { if self.0 == 0 && other.0 == 0 { - Int(0) + Int::zero() } else { - Int(self.0 * other.0 / self.gcd(other).0) + self * other / self.gcd(other) } } } @@ -94,6 +98,7 @@ impl Object for Int { impl Number for Int { fn as_int(&self) -> Option { Some(*self) } fn as_frac(&self) -> Option { Frac::new(*self, Int(1)).ok() } + fn is_zero(&self) -> bool { self.0 == 0 } } impl Mul for Int { diff --git a/types/src/number/mod.rs b/types/src/number/mod.rs index f34cd8a..8424942 100644 --- a/types/src/number/mod.rs +++ b/types/src/number/mod.rs @@ -29,6 +29,7 @@ pub trait Number: fn as_frac(&self) -> Option { None } /// Return `true` if this Number is an exact representation of its value. fn is_exact(&self) -> bool { true } + fn is_zero(&self) -> bool; } // TODO: Implement PartialEq myself cause there are some weird nuances to comparing numbers.