diff --git a/rational/src/lib.rs b/rational/src/lib.rs index 47bb0b0..7aa036f 100644 --- a/rational/src/lib.rs +++ b/rational/src/lib.rs @@ -56,13 +56,24 @@ pub type Rational64 = Ratio; pub type BigRational = Ratio; impl Ratio { - /// Creates a ratio representing the integer `t`. + /// Creates a new `Ratio`. Fails if `denom` is zero. + #[inline] + pub fn new(numer: T, denom: T) -> Ratio { + if denom.is_zero() { + panic!("denominator == 0"); + } + let mut ret = Ratio::new_raw(numer, denom); + ret.reduce(); + ret + } + + /// Creates a `Ratio` representing the integer `t`. #[inline] pub fn from_integer(t: T) -> Ratio { Ratio::new_raw(t, One::one()) } - /// Creates a ratio without checking for `denom == 0` or reducing. + /// Creates a `Ratio` without checking for `denom == 0` or reducing. #[inline] pub fn new_raw(numer: T, denom: T) -> Ratio { Ratio { @@ -71,18 +82,7 @@ impl Ratio { } } - /// Create a new Ratio. Fails if `denom == 0`. - #[inline] - pub fn new(numer: T, denom: T) -> Ratio { - if denom == Zero::zero() { - panic!("denominator == 0"); - } - let mut ret = Ratio::new_raw(numer, denom); - ret.reduce(); - ret - } - - /// Converts to an integer. + /// Converts to an integer, rounding towards zero. #[inline] pub fn to_integer(&self) -> T { self.trunc().numer @@ -106,7 +106,7 @@ impl Ratio { self.denom == One::one() } - /// Put self into lowest terms, with denom > 0. + /// Puts self into lowest terms, with denom > 0. fn reduce(&mut self) { let g: T = self.numer.gcd(&self.denom); @@ -124,7 +124,10 @@ impl Ratio { } } - /// Returns a `reduce`d copy of self. + /// Returns a reduced copy of self. + /// + /// In general, it is not necessary to use this method, as the only + /// method of procuring a non-reduced fraction is through `new_raw`. pub fn reduced(&self) -> Ratio { let mut ret = self.clone(); ret.reduce(); @@ -132,9 +135,16 @@ impl Ratio { } /// Returns the reciprocal. + /// + /// Fails if the `Ratio` is zero. #[inline] pub fn recip(&self) -> Ratio { - Ratio::new_raw(self.denom.clone(), self.numer.clone()) + match self.numer.cmp(&T::zero()) { + cmp::Ordering::Equal => panic!("numerator == 0"), + cmp::Ordering::Greater => Ratio::new_raw(self.denom.clone(), self.numer.clone()), + cmp::Ordering::Less => Ratio::new_raw(T::zero() - self.denom.clone(), + T::zero() - self.numer.clone()) + } } /// Rounds towards minus infinity. @@ -201,7 +211,9 @@ impl Ratio { Ratio::from_integer(self.numer.clone() / self.denom.clone()) } - /// Returns the fractional part of a number. + /// Returns the fractional part of a number, with division rounded towards zero. + /// + /// Satisfies `self == self.trunc() + self.fract()`. #[inline] pub fn fract(&self) -> Ratio { Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone()) @@ -209,7 +221,7 @@ impl Ratio { } impl Ratio { - /// Raises the ratio to the power of an exponent + /// Raises the `Ratio` to the power of an exponent. #[inline] pub fn pow(&self, expon: i32) -> Ratio { match expon.cmp(&0) { @@ -593,7 +605,7 @@ impl serde::Deserialize for Ratio where D: serde::Deserializer { let (numer, denom) = try!(serde::Deserialize::deserialize(deserializer)); - if denom == Zero::zero() { + if denom.is_zero() { Err(serde::de::Error::invalid_value("denominator is zero")) } else { Ok(Ratio::new_raw(numer, denom)) @@ -664,6 +676,10 @@ mod test { numer: 2, denom: 1, }; + pub const _NEG2: Rational = Ratio { + numer: -2, + denom: 1, + }; pub const _1_2: Rational = Ratio { numer: 1, denom: 2, @@ -1002,6 +1018,16 @@ mod test { assert_eq!(_1_2 * _1_2.recip(), _1); assert_eq!(_3_2 * _3_2.recip(), _1); assert_eq!(_NEG1_2 * _NEG1_2.recip(), _1); + + assert_eq!(_3_2.recip(), _2_3); + assert_eq!(_NEG1_2.recip(), _NEG2); + assert_eq!(_NEG1_2.recip().denom(), &1); + } + + #[test] + #[should_panic = "== 0"] + fn test_recip_fail() { + let _a = Ratio::new(0, 1).recip(); } #[test]