Auto merge of #213 - ExpHP:ratio-pr3, r=cuviper

rational: recip bugfix and documentation tweaks

Cherry picked from #210 (minus the `new_raw` stuff), with small additions [in a third commit](32dee9a0c8).
This commit is contained in:
Homu 2016-07-25 14:00:23 +09:00
commit d9f08cb148
1 changed files with 46 additions and 20 deletions

View File

@ -56,13 +56,24 @@ pub type Rational64 = Ratio<i64>;
pub type BigRational = Ratio<BigInt>; pub type BigRational = Ratio<BigInt>;
impl<T: Clone + Integer> Ratio<T> { impl<T: Clone + Integer> Ratio<T> {
/// 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<T> {
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] #[inline]
pub fn from_integer(t: T) -> Ratio<T> { pub fn from_integer(t: T) -> Ratio<T> {
Ratio::new_raw(t, One::one()) 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] #[inline]
pub fn new_raw(numer: T, denom: T) -> Ratio<T> { pub fn new_raw(numer: T, denom: T) -> Ratio<T> {
Ratio { Ratio {
@ -71,18 +82,7 @@ impl<T: Clone + Integer> Ratio<T> {
} }
} }
/// Create a new Ratio. Fails if `denom == 0`. /// Converts to an integer, rounding towards zero.
#[inline]
pub fn new(numer: T, denom: T) -> Ratio<T> {
if denom == Zero::zero() {
panic!("denominator == 0");
}
let mut ret = Ratio::new_raw(numer, denom);
ret.reduce();
ret
}
/// Converts to an integer.
#[inline] #[inline]
pub fn to_integer(&self) -> T { pub fn to_integer(&self) -> T {
self.trunc().numer self.trunc().numer
@ -106,7 +106,7 @@ impl<T: Clone + Integer> Ratio<T> {
self.denom == One::one() self.denom == One::one()
} }
/// Put self into lowest terms, with denom > 0. /// Puts self into lowest terms, with denom > 0.
fn reduce(&mut self) { fn reduce(&mut self) {
let g: T = self.numer.gcd(&self.denom); let g: T = self.numer.gcd(&self.denom);
@ -124,7 +124,10 @@ impl<T: Clone + Integer> Ratio<T> {
} }
} }
/// 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<T> { pub fn reduced(&self) -> Ratio<T> {
let mut ret = self.clone(); let mut ret = self.clone();
ret.reduce(); ret.reduce();
@ -132,9 +135,16 @@ impl<T: Clone + Integer> Ratio<T> {
} }
/// Returns the reciprocal. /// Returns the reciprocal.
///
/// Fails if the `Ratio` is zero.
#[inline] #[inline]
pub fn recip(&self) -> Ratio<T> { pub fn recip(&self) -> Ratio<T> {
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. /// Rounds towards minus infinity.
@ -201,7 +211,9 @@ impl<T: Clone + Integer> Ratio<T> {
Ratio::from_integer(self.numer.clone() / self.denom.clone()) 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] #[inline]
pub fn fract(&self) -> Ratio<T> { pub fn fract(&self) -> Ratio<T> {
Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone()) Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone())
@ -209,7 +221,7 @@ impl<T: Clone + Integer> Ratio<T> {
} }
impl<T: Clone + Integer + PrimInt> Ratio<T> { impl<T: Clone + Integer + PrimInt> Ratio<T> {
/// Raises the ratio to the power of an exponent /// Raises the `Ratio` to the power of an exponent.
#[inline] #[inline]
pub fn pow(&self, expon: i32) -> Ratio<T> { pub fn pow(&self, expon: i32) -> Ratio<T> {
match expon.cmp(&0) { match expon.cmp(&0) {
@ -593,7 +605,7 @@ impl<T> serde::Deserialize for Ratio<T>
where D: serde::Deserializer where D: serde::Deserializer
{ {
let (numer, denom) = try!(serde::Deserialize::deserialize(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")) Err(serde::de::Error::invalid_value("denominator is zero"))
} else { } else {
Ok(Ratio::new_raw(numer, denom)) Ok(Ratio::new_raw(numer, denom))
@ -664,6 +676,10 @@ mod test {
numer: 2, numer: 2,
denom: 1, denom: 1,
}; };
pub const _NEG2: Rational = Ratio {
numer: -2,
denom: 1,
};
pub const _1_2: Rational = Ratio { pub const _1_2: Rational = Ratio {
numer: 1, numer: 1,
denom: 2, denom: 2,
@ -1002,6 +1018,16 @@ mod test {
assert_eq!(_1_2 * _1_2.recip(), _1); assert_eq!(_1_2 * _1_2.recip(), _1);
assert_eq!(_3_2 * _3_2.recip(), _1); assert_eq!(_3_2 * _3_2.recip(), _1);
assert_eq!(_NEG1_2 * _NEG1_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] #[test]