12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382 |
- // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
- // file at the top-level directory of this distribution and at
- // http://rust-lang.org/COPYRIGHT.
- //
- // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
- // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
- // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
- // option. This file may not be copied, modified, or distributed
- // except according to those terms.
- //! Rational numbers
- #![doc(html_logo_url = "https://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
- html_favicon_url = "https://rust-num.github.io/num/favicon.ico",
- html_root_url = "https://rust-num.github.io/num/",
- html_playground_url = "http://play.integer32.com/")]
- #[cfg(feature = "rustc-serialize")]
- extern crate rustc_serialize;
- #[cfg(feature = "serde")]
- extern crate serde;
- #[cfg(feature = "num-bigint")]
- extern crate num_bigint as bigint;
- extern crate num_traits as traits;
- extern crate num_integer as integer;
- use std::cmp;
- use std::error::Error;
- use std::fmt;
- #[cfg(test)]
- use std::hash;
- use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
- use std::str::FromStr;
- #[cfg(feature = "num-bigint")]
- use bigint::{BigInt, BigUint, Sign};
- use integer::Integer;
- use traits::{FromPrimitive, Float, PrimInt, Num, Signed, Zero, One, Bounded, NumCast};
- /// Represents the ratio between 2 numbers.
- #[derive(Copy, Clone, Hash, Debug)]
- #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
- #[allow(missing_docs)]
- pub struct Ratio<T> {
- numer: T,
- denom: T,
- }
- /// Alias for a `Ratio` of machine-sized integers.
- pub type Rational = Ratio<isize>;
- pub type Rational32 = Ratio<i32>;
- pub type Rational64 = Ratio<i64>;
- #[cfg(feature = "num-bigint")]
- /// Alias for arbitrary precision rationals.
- pub type BigRational = Ratio<BigInt>;
- impl<T: Clone + Integer> Ratio<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]
- pub fn from_integer(t: T) -> Ratio<T> {
- Ratio::new_raw(t, One::one())
- }
- /// Creates a `Ratio` without checking for `denom == 0` or reducing.
- #[inline]
- pub fn new_raw(numer: T, denom: T) -> Ratio<T> {
- Ratio {
- numer: numer,
- denom: denom,
- }
- }
- /// Converts to an integer, rounding towards zero.
- #[inline]
- pub fn to_integer(&self) -> T {
- self.trunc().numer
- }
- /// Gets an immutable reference to the numerator.
- #[inline]
- pub fn numer<'a>(&'a self) -> &'a T {
- &self.numer
- }
- /// Gets an immutable reference to the denominator.
- #[inline]
- pub fn denom<'a>(&'a self) -> &'a T {
- &self.denom
- }
- /// Returns true if the rational number is an integer (denominator is 1).
- #[inline]
- pub fn is_integer(&self) -> bool {
- self.denom == One::one()
- }
- /// Puts self into lowest terms, with denom > 0.
- fn reduce(&mut self) {
- let g: T = self.numer.gcd(&self.denom);
- // FIXME(#5992): assignment operator overloads
- // self.numer /= g;
- self.numer = self.numer.clone() / g.clone();
- // FIXME(#5992): assignment operator overloads
- // self.denom /= g;
- self.denom = self.denom.clone() / g;
- // keep denom positive!
- if self.denom < T::zero() {
- self.numer = T::zero() - self.numer.clone();
- self.denom = T::zero() - self.denom.clone();
- }
- }
- /// 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> {
- let mut ret = self.clone();
- ret.reduce();
- ret
- }
- /// Returns the reciprocal.
- ///
- /// Fails if the `Ratio` is zero.
- #[inline]
- pub fn recip(&self) -> Ratio<T> {
- 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.
- #[inline]
- pub fn floor(&self) -> Ratio<T> {
- if *self < Zero::zero() {
- let one: T = One::one();
- Ratio::from_integer((self.numer.clone() - self.denom.clone() + one) /
- self.denom.clone())
- } else {
- Ratio::from_integer(self.numer.clone() / self.denom.clone())
- }
- }
- /// Rounds towards plus infinity.
- #[inline]
- pub fn ceil(&self) -> Ratio<T> {
- if *self < Zero::zero() {
- Ratio::from_integer(self.numer.clone() / self.denom.clone())
- } else {
- let one: T = One::one();
- Ratio::from_integer((self.numer.clone() + self.denom.clone() - one) /
- self.denom.clone())
- }
- }
- /// Rounds to the nearest integer. Rounds half-way cases away from zero.
- #[inline]
- pub fn round(&self) -> Ratio<T> {
- let zero: Ratio<T> = Zero::zero();
- let one: T = One::one();
- let two: T = one.clone() + one.clone();
- // Find unsigned fractional part of rational number
- let mut fractional = self.fract();
- if fractional < zero {
- fractional = zero - fractional
- };
- // The algorithm compares the unsigned fractional part with 1/2, that
- // is, a/b >= 1/2, or a >= b/2. For odd denominators, we use
- // a >= (b/2)+1. This avoids overflow issues.
- let half_or_larger = if fractional.denom().is_even() {
- *fractional.numer() >= fractional.denom().clone() / two.clone()
- } else {
- *fractional.numer() >= (fractional.denom().clone() / two.clone()) + one.clone()
- };
- if half_or_larger {
- let one: Ratio<T> = One::one();
- if *self >= Zero::zero() {
- self.trunc() + one
- } else {
- self.trunc() - one
- }
- } else {
- self.trunc()
- }
- }
- /// Rounds towards zero.
- #[inline]
- pub fn trunc(&self) -> Ratio<T> {
- Ratio::from_integer(self.numer.clone() / self.denom.clone())
- }
- /// Returns the fractional part of a number, with division rounded towards zero.
- ///
- /// Satisfies `self == self.trunc() + self.fract()`.
- #[inline]
- pub fn fract(&self) -> Ratio<T> {
- Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone())
- }
- }
- impl<T: Clone + Integer + PrimInt> Ratio<T> {
- /// Raises the `Ratio` to the power of an exponent.
- #[inline]
- pub fn pow(&self, expon: i32) -> Ratio<T> {
- match expon.cmp(&0) {
- cmp::Ordering::Equal => One::one(),
- cmp::Ordering::Less => self.recip().pow(-expon),
- cmp::Ordering::Greater => {
- Ratio::new_raw(self.numer.pow(expon as u32), self.denom.pow(expon as u32))
- }
- }
- }
- }
- #[cfg(feature = "num-bigint")]
- impl Ratio<BigInt> {
- /// Converts a float into a rational number.
- pub fn from_float<T: Float>(f: T) -> Option<BigRational> {
- if !f.is_finite() {
- return None;
- }
- let (mantissa, exponent, sign) = f.integer_decode();
- let bigint_sign = if sign == 1 {
- Sign::Plus
- } else {
- Sign::Minus
- };
- if exponent < 0 {
- let one: BigInt = One::one();
- let denom: BigInt = one << ((-exponent) as usize);
- let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
- Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom))
- } else {
- let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
- numer = numer << (exponent as usize);
- Some(Ratio::from_integer(BigInt::from_biguint(bigint_sign, numer)))
- }
- }
- }
- // From integer
- impl<T> From<T> for Ratio<T> where T: Clone + Integer {
- fn from(x: T) -> Ratio<T> {
- Ratio::from_integer(x)
- }
- }
- // From pair (through the `new` constructor)
- impl<T> From<(T, T)> for Ratio<T> where T: Clone + Integer {
- fn from(pair: (T, T)) -> Ratio<T> {
- Ratio::new(pair.0, pair.1)
- }
- }
- // Comparisons
- // Mathematically, comparing a/b and c/d is the same as comparing a*d and b*c, but it's very easy
- // for those multiplications to overflow fixed-size integers, so we need to take care.
- impl<T: Clone + Integer> Ord for Ratio<T> {
- #[inline]
- fn cmp(&self, other: &Self) -> cmp::Ordering {
- // With equal denominators, the numerators can be directly compared
- if self.denom == other.denom {
- let ord = self.numer.cmp(&other.numer);
- return if self.denom < T::zero() {
- ord.reverse()
- } else {
- ord
- };
- }
- // With equal numerators, the denominators can be inversely compared
- if self.numer == other.numer {
- let ord = self.denom.cmp(&other.denom);
- return if self.numer < T::zero() {
- ord
- } else {
- ord.reverse()
- };
- }
- // Unfortunately, we don't have CheckedMul to try. That could sometimes avoid all the
- // division below, or even always avoid it for BigInt and BigUint.
- // FIXME- future breaking change to add Checked* to Integer?
- // Compare as floored integers and remainders
- let (self_int, self_rem) = self.numer.div_mod_floor(&self.denom);
- let (other_int, other_rem) = other.numer.div_mod_floor(&other.denom);
- match self_int.cmp(&other_int) {
- cmp::Ordering::Greater => cmp::Ordering::Greater,
- cmp::Ordering::Less => cmp::Ordering::Less,
- cmp::Ordering::Equal => {
- match (self_rem.is_zero(), other_rem.is_zero()) {
- (true, true) => cmp::Ordering::Equal,
- (true, false) => cmp::Ordering::Less,
- (false, true) => cmp::Ordering::Greater,
- (false, false) => {
- // Compare the reciprocals of the remaining fractions in reverse
- let self_recip = Ratio::new_raw(self.denom.clone(), self_rem);
- let other_recip = Ratio::new_raw(other.denom.clone(), other_rem);
- self_recip.cmp(&other_recip).reverse()
- }
- }
- }
- }
- }
- }
- impl<T: Clone + Integer> PartialOrd for Ratio<T> {
- #[inline]
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- Some(self.cmp(other))
- }
- }
- impl<T: Clone + Integer> PartialEq for Ratio<T> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.cmp(other) == cmp::Ordering::Equal
- }
- }
- impl<T: Clone + Integer> Eq for Ratio<T> {}
- macro_rules! forward_val_val_binop {
- (impl $imp:ident, $method:ident) => {
- impl<T: Clone + Integer> $imp<Ratio<T>> for Ratio<T> {
- type Output = Ratio<T>;
- #[inline]
- fn $method(self, other: Ratio<T>) -> Ratio<T> {
- (&self).$method(&other)
- }
- }
- }
- }
- macro_rules! forward_ref_val_binop {
- (impl $imp:ident, $method:ident) => {
- impl<'a, T> $imp<Ratio<T>> for &'a Ratio<T> where
- T: Clone + Integer
- {
- type Output = Ratio<T>;
- #[inline]
- fn $method(self, other: Ratio<T>) -> Ratio<T> {
- self.$method(&other)
- }
- }
- }
- }
- macro_rules! forward_val_ref_binop {
- (impl $imp:ident, $method:ident) => {
- impl<'a, T> $imp<&'a Ratio<T>> for Ratio<T> where
- T: Clone + Integer
- {
- type Output = Ratio<T>;
- #[inline]
- fn $method(self, other: &Ratio<T>) -> Ratio<T> {
- (&self).$method(other)
- }
- }
- }
- }
- macro_rules! forward_all_binop {
- (impl $imp:ident, $method:ident) => {
- forward_val_val_binop!(impl $imp, $method);
- forward_ref_val_binop!(impl $imp, $method);
- forward_val_ref_binop!(impl $imp, $method);
- };
- }
- // Arithmetic
- forward_all_binop!(impl Mul, mul);
- // a/b * c/d = (a*c)/(b*d)
- impl<'a, 'b, T> Mul<&'b Ratio<T>> for &'a Ratio<T>
- where T: Clone + Integer
- {
- type Output = Ratio<T>;
- #[inline]
- fn mul(self, rhs: &Ratio<T>) -> Ratio<T> {
- Ratio::new(self.numer.clone() * rhs.numer.clone(),
- self.denom.clone() * rhs.denom.clone())
- }
- }
- forward_all_binop!(impl Div, div);
- // (a/b) / (c/d) = (a*d)/(b*c)
- impl<'a, 'b, T> Div<&'b Ratio<T>> for &'a Ratio<T>
- where T: Clone + Integer
- {
- type Output = Ratio<T>;
- #[inline]
- fn div(self, rhs: &Ratio<T>) -> Ratio<T> {
- Ratio::new(self.numer.clone() * rhs.denom.clone(),
- self.denom.clone() * rhs.numer.clone())
- }
- }
- // Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern
- macro_rules! arith_impl {
- (impl $imp:ident, $method:ident) => {
- forward_all_binop!(impl $imp, $method);
- impl<'a, 'b, T: Clone + Integer>
- $imp<&'b Ratio<T>> for &'a Ratio<T> {
- type Output = Ratio<T>;
- #[inline]
- fn $method(self, rhs: &Ratio<T>) -> Ratio<T> {
- Ratio::new((self.numer.clone() * rhs.denom.clone()).$method(self.denom.clone() * rhs.numer.clone()),
- self.denom.clone() * rhs.denom.clone())
- }
- }
- }
- }
- // a/b + c/d = (a*d + b*c)/(b*d)
- arith_impl!(impl Add, add);
- // a/b - c/d = (a*d - b*c)/(b*d)
- arith_impl!(impl Sub, sub);
- // a/b % c/d = (a*d % b*c)/(b*d)
- arith_impl!(impl Rem, rem);
- impl<T> Neg for Ratio<T>
- where T: Clone + Integer + Neg<Output = T>
- {
- type Output = Ratio<T>;
- #[inline]
- fn neg(self) -> Ratio<T> {
- Ratio::new_raw(-self.numer, self.denom)
- }
- }
- impl<'a, T> Neg for &'a Ratio<T>
- where T: Clone + Integer + Neg<Output = T>
- {
- type Output = Ratio<T>;
- #[inline]
- fn neg(self) -> Ratio<T> {
- -self.clone()
- }
- }
- // Constants
- impl<T: Clone + Integer> Zero for Ratio<T> {
- #[inline]
- fn zero() -> Ratio<T> {
- Ratio::new_raw(Zero::zero(), One::one())
- }
- #[inline]
- fn is_zero(&self) -> bool {
- self.numer.is_zero()
- }
- }
- impl<T: Clone + Integer> One for Ratio<T> {
- #[inline]
- fn one() -> Ratio<T> {
- Ratio::new_raw(One::one(), One::one())
- }
- }
- impl<T: Clone + Integer> Num for Ratio<T> {
- type FromStrRadixErr = ParseRatioError;
- /// Parses `numer/denom` where the numbers are in base `radix`.
- fn from_str_radix(s: &str, radix: u32) -> Result<Ratio<T>, ParseRatioError> {
- let split: Vec<&str> = s.splitn(2, '/').collect();
- if split.len() < 2 {
- Err(ParseRatioError { kind: RatioErrorKind::ParseError })
- } else {
- let a_result: Result<T, _> = T::from_str_radix(split[0], radix).map_err(|_| {
- ParseRatioError { kind: RatioErrorKind::ParseError }
- });
- a_result.and_then(|a| {
- let b_result: Result<T, _> = T::from_str_radix(split[1], radix).map_err(|_| {
- ParseRatioError { kind: RatioErrorKind::ParseError }
- });
- b_result.and_then(|b| {
- if b.is_zero() {
- Err(ParseRatioError { kind: RatioErrorKind::ZeroDenominator })
- } else {
- Ok(Ratio::new(a.clone(), b.clone()))
- }
- })
- })
- }
- }
- }
- impl<T: Clone + Integer + Signed> Signed for Ratio<T> {
- #[inline]
- fn abs(&self) -> Ratio<T> {
- if self.is_negative() {
- -self.clone()
- } else {
- self.clone()
- }
- }
- #[inline]
- fn abs_sub(&self, other: &Ratio<T>) -> Ratio<T> {
- if *self <= *other {
- Zero::zero()
- } else {
- self - other
- }
- }
- #[inline]
- fn signum(&self) -> Ratio<T> {
- if self.is_positive() {
- Self::one()
- } else if self.is_zero() {
- Self::zero()
- } else {
- -Self::one()
- }
- }
- #[inline]
- fn is_positive(&self) -> bool {
- (self.numer.is_positive() && self.denom.is_positive()) ||
- (self.numer.is_negative() && self.denom.is_negative())
- }
- #[inline]
- fn is_negative(&self) -> bool {
- (self.numer.is_negative() && self.denom.is_positive()) ||
- (self.numer.is_positive() && self.denom.is_negative())
- }
- }
- // String conversions
- impl<T> fmt::Display for Ratio<T>
- where T: fmt::Display + Eq + One
- {
- /// Renders as `numer/denom`. If denom=1, renders as numer.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.denom == One::one() {
- write!(f, "{}", self.numer)
- } else {
- write!(f, "{}/{}", self.numer, self.denom)
- }
- }
- }
- impl<T: FromStr + Clone + Integer> FromStr for Ratio<T> {
- type Err = ParseRatioError;
- /// Parses `numer/denom` or just `numer`.
- fn from_str(s: &str) -> Result<Ratio<T>, ParseRatioError> {
- let mut split = s.splitn(2, '/');
- let n = try!(split.next().ok_or(ParseRatioError { kind: RatioErrorKind::ParseError }));
- let num = try!(FromStr::from_str(n)
- .map_err(|_| ParseRatioError { kind: RatioErrorKind::ParseError }));
- let d = split.next().unwrap_or("1");
- let den = try!(FromStr::from_str(d)
- .map_err(|_| ParseRatioError { kind: RatioErrorKind::ParseError }));
- if Zero::is_zero(&den) {
- Err(ParseRatioError { kind: RatioErrorKind::ZeroDenominator })
- } else {
- Ok(Ratio::new(num, den))
- }
- }
- }
- impl<T> Into<(T, T)> for Ratio<T> {
- fn into(self) -> (T, T) {
- (self.numer, self.denom)
- }
- }
- #[cfg(feature = "serde")]
- impl<T> serde::Serialize for Ratio<T>
- where T: serde::Serialize + Clone + Integer + PartialOrd
- {
- fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
- where S: serde::Serializer
- {
- (self.numer(), self.denom()).serialize(serializer)
- }
- }
- #[cfg(feature = "serde")]
- impl<T> serde::Deserialize for Ratio<T>
- where T: serde::Deserialize + Clone + Integer + PartialOrd
- {
- fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
- where D: serde::Deserializer
- {
- let (numer, denom): (T,T) = try!(serde::Deserialize::deserialize(deserializer));
- if denom.is_zero() {
- Err(serde::de::Error::invalid_value("denominator is zero"))
- } else {
- Ok(Ratio::new_raw(numer, denom))
- }
- }
- }
- // FIXME: Bubble up specific errors
- #[derive(Copy, Clone, Debug, PartialEq)]
- pub struct ParseRatioError {
- kind: RatioErrorKind,
- }
- #[derive(Copy, Clone, Debug, PartialEq)]
- enum RatioErrorKind {
- ParseError,
- ZeroDenominator,
- }
- impl fmt::Display for ParseRatioError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.description().fmt(f)
- }
- }
- impl Error for ParseRatioError {
- fn description(&self) -> &str {
- self.kind.description()
- }
- }
- impl RatioErrorKind {
- fn description(&self) -> &'static str {
- match *self {
- RatioErrorKind::ParseError => "failed to parse integer",
- RatioErrorKind::ZeroDenominator => "zero value denominator",
- }
- }
- }
- impl FromPrimitive for Ratio<BigInt> {
- fn from_i64(n: i64) -> Option<Self> {
- Some(Ratio::from_integer(n.into()))
- }
- fn from_u64(n: u64) -> Option<Self> {
- Some(Ratio::from_integer(n.into()))
- }
- fn from_f32(n: f32) -> Option<Self> {
- Ratio::from_float(n)
- }
- fn from_f64(n: f64) -> Option<Self> {
- Ratio::from_float(n)
- }
- }
- macro_rules! from_primitive_integer {
- ($typ:ty, $approx:ident) => {
- impl FromPrimitive for Ratio<$typ> {
- fn from_i64(n: i64) -> Option<Self> {
- <$typ as FromPrimitive>::from_i64(n).map(Ratio::from_integer)
- }
- fn from_u64(n: u64) -> Option<Self> {
- <$typ as FromPrimitive>::from_u64(n).map(Ratio::from_integer)
- }
- fn from_f32(n: f32) -> Option<Self> {
- $approx(n, 10e-20, 30)
- }
- fn from_f64(n: f64) -> Option<Self> {
- $approx(n, 10e-20, 30)
- }
- }
- }
- }
- from_primitive_integer!(i8, approximate_float);
- from_primitive_integer!(i16, approximate_float);
- from_primitive_integer!(i32, approximate_float);
- from_primitive_integer!(i64, approximate_float);
- from_primitive_integer!(isize, approximate_float);
- from_primitive_integer!(u8, approximate_float_unsigned);
- from_primitive_integer!(u16, approximate_float_unsigned);
- from_primitive_integer!(u32, approximate_float_unsigned);
- from_primitive_integer!(u64, approximate_float_unsigned);
- from_primitive_integer!(usize, approximate_float_unsigned);
- impl<T: Integer + Signed + Bounded + NumCast + Clone> Ratio<T> {
- pub fn approximate_float<F: Float + NumCast>(f: F) -> Option<Ratio<T>> {
- // 1/10e-20 < 1/2**32 which seems like a good default, and 30 seems
- // to work well. Might want to choose something based on the types in the future, e.g.
- // T::max().recip() and T::bits() or something similar.
- let epsilon = <F as NumCast>::from(10e-20).expect("Can't convert 10e-20");
- approximate_float(f, epsilon, 30)
- }
- }
- fn approximate_float<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>>
- where T: Integer + Signed + Bounded + NumCast + Clone,
- F: Float + NumCast
- {
- let negative = val.is_sign_negative();
- let abs_val = val.abs();
- let r = approximate_float_unsigned(abs_val, max_error, max_iterations);
- // Make negative again if needed
- if negative {
- r.map(|r| r.neg())
- } else {
- r
- }
- }
- // No Unsigned constraint because this also works on positive integers and is called
- // like that, see above
- fn approximate_float_unsigned<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>>
- where T: Integer + Bounded + NumCast + Clone,
- F: Float + NumCast
- {
- // Continued fractions algorithm
- // http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac
- if val < F::zero() {
- return None;
- }
- let mut q = val;
- let mut n0 = T::zero();
- let mut d0 = T::one();
- let mut n1 = T::one();
- let mut d1 = T::zero();
- let t_max = T::max_value();
- let t_max_f = match <F as NumCast>::from(t_max.clone()) {
- None => return None,
- Some(t_max_f) => t_max_f,
- };
- // 1/epsilon > T::MAX
- let epsilon = t_max_f.recip();
- // Overflow
- if q > t_max_f {
- return None;
- }
- for _ in 0..max_iterations {
- let a = match <T as NumCast>::from(q) {
- None => break,
- Some(a) => a,
- };
- let a_f = match <F as NumCast>::from(a.clone()) {
- None => break,
- Some(a_f) => a_f,
- };
- let f = q - a_f;
- // Prevent overflow
- if !a.is_zero() &&
- (n1 > t_max.clone() / a.clone() ||
- d1 > t_max.clone() / a.clone() ||
- a.clone() * n1.clone() > t_max.clone() - n0.clone() ||
- a.clone() * d1.clone() > t_max.clone() - d0.clone()) {
- break;
- }
- let n = a.clone() * n1.clone() + n0.clone();
- let d = a.clone() * d1.clone() + d0.clone();
- n0 = n1;
- d0 = d1;
- n1 = n.clone();
- d1 = d.clone();
- // Simplify fraction. Doing so here instead of at the end
- // allows us to get closer to the target value without overflows
- let g = Integer::gcd(&n1, &d1);
- if !g.is_zero() {
- n1 = n1 / g.clone();
- d1 = d1 / g.clone();
- }
- // Close enough?
- let (n_f, d_f) = match (<F as NumCast>::from(n), <F as NumCast>::from(d)) {
- (Some(n_f), Some(d_f)) => (n_f, d_f),
- _ => break,
- };
- if (n_f / d_f - val).abs() < max_error {
- break;
- }
- // Prevent division by ~0
- if f < epsilon {
- break;
- }
- q = f.recip();
- }
- // Overflow
- if d1.is_zero() {
- return None;
- }
- Some(Ratio::new(n1, d1))
- }
- #[cfg(test)]
- fn hash<T: hash::Hash>(x: &T) -> u64 {
- use std::hash::Hasher;
- let mut hasher = hash::SipHasher::new();
- x.hash(&mut hasher);
- hasher.finish()
- }
- #[cfg(test)]
- mod test {
- use super::{Ratio, Rational};
- #[cfg(feature = "num-bigint")]
- use super::BigRational;
- use std::str::FromStr;
- use std::i32;
- use std::f64;
- use traits::{Zero, One, Signed, FromPrimitive, Float};
- pub const _0: Rational = Ratio {
- numer: 0,
- denom: 1,
- };
- pub const _1: Rational = Ratio {
- numer: 1,
- denom: 1,
- };
- pub const _2: Rational = Ratio {
- numer: 2,
- denom: 1,
- };
- pub const _NEG2: Rational = Ratio {
- numer: -2,
- denom: 1,
- };
- pub const _1_2: Rational = Ratio {
- numer: 1,
- denom: 2,
- };
- pub const _3_2: Rational = Ratio {
- numer: 3,
- denom: 2,
- };
- pub const _NEG1_2: Rational = Ratio {
- numer: -1,
- denom: 2,
- };
- pub const _1_NEG2: Rational = Ratio {
- numer: 1,
- denom: -2,
- };
- pub const _NEG1_NEG2: Rational = Ratio {
- numer: -1,
- denom: -2,
- };
- pub const _1_3: Rational = Ratio {
- numer: 1,
- denom: 3,
- };
- pub const _NEG1_3: Rational = Ratio {
- numer: -1,
- denom: 3,
- };
- pub const _2_3: Rational = Ratio {
- numer: 2,
- denom: 3,
- };
- pub const _NEG2_3: Rational = Ratio {
- numer: -2,
- denom: 3,
- };
- #[cfg(feature = "num-bigint")]
- pub fn to_big(n: Rational) -> BigRational {
- Ratio::new(FromPrimitive::from_isize(n.numer).unwrap(),
- FromPrimitive::from_isize(n.denom).unwrap())
- }
- #[cfg(not(feature = "num-bigint"))]
- pub fn to_big(n: Rational) -> Rational {
- Ratio::new(FromPrimitive::from_isize(n.numer).unwrap(),
- FromPrimitive::from_isize(n.denom).unwrap())
- }
- #[test]
- fn test_test_constants() {
- // check our constants are what Ratio::new etc. would make.
- assert_eq!(_0, Zero::zero());
- assert_eq!(_1, One::one());
- assert_eq!(_2, Ratio::from_integer(2));
- assert_eq!(_1_2, Ratio::new(1, 2));
- assert_eq!(_3_2, Ratio::new(3, 2));
- assert_eq!(_NEG1_2, Ratio::new(-1, 2));
- assert_eq!(_2, From::from(2));
- }
- #[test]
- fn test_new_reduce() {
- let one22 = Ratio::new(2, 2);
- assert_eq!(one22, One::one());
- }
- #[test]
- #[should_panic]
- fn test_new_zero() {
- let _a = Ratio::new(1, 0);
- }
- #[test]
- fn test_approximate_float() {
- assert_eq!(Ratio::from_f32(0.5f32), Some(Ratio::new(1i64, 2)));
- assert_eq!(Ratio::from_f64(0.5f64), Some(Ratio::new(1i32, 2)));
- assert_eq!(Ratio::from_f32(5f32), Some(Ratio::new(5i64, 1)));
- assert_eq!(Ratio::from_f64(5f64), Some(Ratio::new(5i32, 1)));
- assert_eq!(Ratio::from_f32(29.97f32), Some(Ratio::new(2997i64, 100)));
- assert_eq!(Ratio::from_f32(-29.97f32), Some(Ratio::new(-2997i64, 100)));
- assert_eq!(Ratio::<i8>::from_f32(63.5f32), Some(Ratio::new(127i8, 2)));
- assert_eq!(Ratio::<i8>::from_f32(126.5f32), Some(Ratio::new(126i8, 1)));
- assert_eq!(Ratio::<i8>::from_f32(127.0f32), Some(Ratio::new(127i8, 1)));
- assert_eq!(Ratio::<i8>::from_f32(127.5f32), None);
- assert_eq!(Ratio::<i8>::from_f32(-63.5f32), Some(Ratio::new(-127i8, 2)));
- assert_eq!(Ratio::<i8>::from_f32(-126.5f32), Some(Ratio::new(-126i8, 1)));
- assert_eq!(Ratio::<i8>::from_f32(-127.0f32), Some(Ratio::new(-127i8, 1)));
- assert_eq!(Ratio::<i8>::from_f32(-127.5f32), None);
- assert_eq!(Ratio::<u8>::from_f32(-127f32), None);
- assert_eq!(Ratio::<u8>::from_f32(127f32), Some(Ratio::new(127u8, 1)));
- assert_eq!(Ratio::<u8>::from_f32(127.5f32), Some(Ratio::new(255u8, 2)));
- assert_eq!(Ratio::<u8>::from_f32(256f32), None);
- assert_eq!(Ratio::<i64>::from_f64(-10e200), None);
- assert_eq!(Ratio::<i64>::from_f64(10e200), None);
- assert_eq!(Ratio::<i64>::from_f64(f64::INFINITY), None);
- assert_eq!(Ratio::<i64>::from_f64(f64::NEG_INFINITY), None);
- assert_eq!(Ratio::<i64>::from_f64(f64::NAN), None);
- assert_eq!(Ratio::<i64>::from_f64(f64::EPSILON), Some(Ratio::new(1, 4503599627370496)));
- assert_eq!(Ratio::<i64>::from_f64(0.0), Some(Ratio::new(0, 1)));
- assert_eq!(Ratio::<i64>::from_f64(-0.0), Some(Ratio::new(0, 1)));
- }
- #[test]
- fn test_cmp() {
- assert!(_0 == _0 && _1 == _1);
- assert!(_0 != _1 && _1 != _0);
- assert!(_0 < _1 && !(_1 < _0));
- assert!(_1 > _0 && !(_0 > _1));
- assert!(_0 <= _0 && _1 <= _1);
- assert!(_0 <= _1 && !(_1 <= _0));
- assert!(_0 >= _0 && _1 >= _1);
- assert!(_1 >= _0 && !(_0 >= _1));
- }
- #[test]
- fn test_cmp_overflow() {
- use std::cmp::Ordering;
- // issue #7 example:
- let big = Ratio::new(128u8, 1);
- let small = big.recip();
- assert!(big > small);
- // try a few that are closer together
- // (some matching numer, some matching denom, some neither)
- let ratios = vec![
- Ratio::new(125_i8, 127_i8),
- Ratio::new(63_i8, 64_i8),
- Ratio::new(124_i8, 125_i8),
- Ratio::new(125_i8, 126_i8),
- Ratio::new(126_i8, 127_i8),
- Ratio::new(127_i8, 126_i8),
- ];
- fn check_cmp(a: Ratio<i8>, b: Ratio<i8>, ord: Ordering) {
- println!("comparing {} and {}", a, b);
- assert_eq!(a.cmp(&b), ord);
- assert_eq!(b.cmp(&a), ord.reverse());
- }
- for (i, &a) in ratios.iter().enumerate() {
- check_cmp(a, a, Ordering::Equal);
- check_cmp(-a, a, Ordering::Less);
- for &b in &ratios[i + 1..] {
- check_cmp(a, b, Ordering::Less);
- check_cmp(-a, -b, Ordering::Greater);
- check_cmp(a.recip(), b.recip(), Ordering::Greater);
- check_cmp(-a.recip(), -b.recip(), Ordering::Less);
- }
- }
- }
- #[test]
- fn test_to_integer() {
- assert_eq!(_0.to_integer(), 0);
- assert_eq!(_1.to_integer(), 1);
- assert_eq!(_2.to_integer(), 2);
- assert_eq!(_1_2.to_integer(), 0);
- assert_eq!(_3_2.to_integer(), 1);
- assert_eq!(_NEG1_2.to_integer(), 0);
- }
- #[test]
- fn test_numer() {
- assert_eq!(_0.numer(), &0);
- assert_eq!(_1.numer(), &1);
- assert_eq!(_2.numer(), &2);
- assert_eq!(_1_2.numer(), &1);
- assert_eq!(_3_2.numer(), &3);
- assert_eq!(_NEG1_2.numer(), &(-1));
- }
- #[test]
- fn test_denom() {
- assert_eq!(_0.denom(), &1);
- assert_eq!(_1.denom(), &1);
- assert_eq!(_2.denom(), &1);
- assert_eq!(_1_2.denom(), &2);
- assert_eq!(_3_2.denom(), &2);
- assert_eq!(_NEG1_2.denom(), &2);
- }
- #[test]
- fn test_is_integer() {
- assert!(_0.is_integer());
- assert!(_1.is_integer());
- assert!(_2.is_integer());
- assert!(!_1_2.is_integer());
- assert!(!_3_2.is_integer());
- assert!(!_NEG1_2.is_integer());
- }
- #[test]
- fn test_show() {
- assert_eq!(format!("{}", _2), "2".to_string());
- assert_eq!(format!("{}", _1_2), "1/2".to_string());
- assert_eq!(format!("{}", _0), "0".to_string());
- assert_eq!(format!("{}", Ratio::from_integer(-2)), "-2".to_string());
- }
- mod arith {
- use super::{_0, _1, _2, _1_2, _3_2, _NEG1_2, to_big};
- use super::super::{Ratio, Rational};
- #[test]
- fn test_add() {
- fn test(a: Rational, b: Rational, c: Rational) {
- assert_eq!(a + b, c);
- assert_eq!(to_big(a) + to_big(b), to_big(c));
- }
- test(_1, _1_2, _3_2);
- test(_1, _1, _2);
- test(_1_2, _3_2, _2);
- test(_1_2, _NEG1_2, _0);
- }
- #[test]
- fn test_sub() {
- fn test(a: Rational, b: Rational, c: Rational) {
- assert_eq!(a - b, c);
- assert_eq!(to_big(a) - to_big(b), to_big(c))
- }
- test(_1, _1_2, _1_2);
- test(_3_2, _1_2, _1);
- test(_1, _NEG1_2, _3_2);
- }
- #[test]
- fn test_mul() {
- fn test(a: Rational, b: Rational, c: Rational) {
- assert_eq!(a * b, c);
- assert_eq!(to_big(a) * to_big(b), to_big(c))
- }
- test(_1, _1_2, _1_2);
- test(_1_2, _3_2, Ratio::new(3, 4));
- test(_1_2, _NEG1_2, Ratio::new(-1, 4));
- }
- #[test]
- fn test_div() {
- fn test(a: Rational, b: Rational, c: Rational) {
- assert_eq!(a / b, c);
- assert_eq!(to_big(a) / to_big(b), to_big(c))
- }
- test(_1, _1_2, _2);
- test(_3_2, _1_2, _1 + _2);
- test(_1, _NEG1_2, _NEG1_2 + _NEG1_2 + _NEG1_2 + _NEG1_2);
- }
- #[test]
- fn test_rem() {
- fn test(a: Rational, b: Rational, c: Rational) {
- assert_eq!(a % b, c);
- assert_eq!(to_big(a) % to_big(b), to_big(c))
- }
- test(_3_2, _1, _1_2);
- test(_2, _NEG1_2, _0);
- test(_1_2, _2, _1_2);
- }
- #[test]
- fn test_neg() {
- fn test(a: Rational, b: Rational) {
- assert_eq!(-a, b);
- assert_eq!(-to_big(a), to_big(b))
- }
- test(_0, _0);
- test(_1_2, _NEG1_2);
- test(-_1, _1);
- }
- #[test]
- fn test_zero() {
- assert_eq!(_0 + _0, _0);
- assert_eq!(_0 * _0, _0);
- assert_eq!(_0 * _1, _0);
- assert_eq!(_0 / _NEG1_2, _0);
- assert_eq!(_0 - _0, _0);
- }
- #[test]
- #[should_panic]
- fn test_div_0() {
- let _a = _1 / _0;
- }
- }
- #[test]
- fn test_round() {
- assert_eq!(_1_3.ceil(), _1);
- assert_eq!(_1_3.floor(), _0);
- assert_eq!(_1_3.round(), _0);
- assert_eq!(_1_3.trunc(), _0);
- assert_eq!(_NEG1_3.ceil(), _0);
- assert_eq!(_NEG1_3.floor(), -_1);
- assert_eq!(_NEG1_3.round(), _0);
- assert_eq!(_NEG1_3.trunc(), _0);
- assert_eq!(_2_3.ceil(), _1);
- assert_eq!(_2_3.floor(), _0);
- assert_eq!(_2_3.round(), _1);
- assert_eq!(_2_3.trunc(), _0);
- assert_eq!(_NEG2_3.ceil(), _0);
- assert_eq!(_NEG2_3.floor(), -_1);
- assert_eq!(_NEG2_3.round(), -_1);
- assert_eq!(_NEG2_3.trunc(), _0);
- assert_eq!(_1_2.ceil(), _1);
- assert_eq!(_1_2.floor(), _0);
- assert_eq!(_1_2.round(), _1);
- assert_eq!(_1_2.trunc(), _0);
- assert_eq!(_NEG1_2.ceil(), _0);
- assert_eq!(_NEG1_2.floor(), -_1);
- assert_eq!(_NEG1_2.round(), -_1);
- assert_eq!(_NEG1_2.trunc(), _0);
- assert_eq!(_1.ceil(), _1);
- assert_eq!(_1.floor(), _1);
- assert_eq!(_1.round(), _1);
- assert_eq!(_1.trunc(), _1);
- // Overflow checks
- let _neg1 = Ratio::from_integer(-1);
- let _large_rat1 = Ratio::new(i32::MAX, i32::MAX - 1);
- let _large_rat2 = Ratio::new(i32::MAX - 1, i32::MAX);
- let _large_rat3 = Ratio::new(i32::MIN + 2, i32::MIN + 1);
- let _large_rat4 = Ratio::new(i32::MIN + 1, i32::MIN + 2);
- let _large_rat5 = Ratio::new(i32::MIN + 2, i32::MAX);
- let _large_rat6 = Ratio::new(i32::MAX, i32::MIN + 2);
- let _large_rat7 = Ratio::new(1, i32::MIN + 1);
- let _large_rat8 = Ratio::new(1, i32::MAX);
- assert_eq!(_large_rat1.round(), One::one());
- assert_eq!(_large_rat2.round(), One::one());
- assert_eq!(_large_rat3.round(), One::one());
- assert_eq!(_large_rat4.round(), One::one());
- assert_eq!(_large_rat5.round(), _neg1);
- assert_eq!(_large_rat6.round(), _neg1);
- assert_eq!(_large_rat7.round(), Zero::zero());
- assert_eq!(_large_rat8.round(), Zero::zero());
- }
- #[test]
- fn test_fract() {
- assert_eq!(_1.fract(), _0);
- assert_eq!(_NEG1_2.fract(), _NEG1_2);
- assert_eq!(_1_2.fract(), _1_2);
- assert_eq!(_3_2.fract(), _1_2);
- }
- #[test]
- fn test_recip() {
- assert_eq!(_1 * _1.recip(), _1);
- assert_eq!(_2 * _2.recip(), _1);
- 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(expected = "== 0")]
- fn test_recip_fail() {
- let _a = Ratio::new(0, 1).recip();
- }
- #[test]
- fn test_pow() {
- assert_eq!(_1_2.pow(2), Ratio::new(1, 4));
- assert_eq!(_1_2.pow(-2), Ratio::new(4, 1));
- assert_eq!(_1.pow(1), _1);
- assert_eq!(_NEG1_2.pow(2), _1_2.pow(2));
- assert_eq!(_NEG1_2.pow(3), -_1_2.pow(3));
- assert_eq!(_3_2.pow(0), _1);
- assert_eq!(_3_2.pow(-1), _3_2.recip());
- assert_eq!(_3_2.pow(3), Ratio::new(27, 8));
- }
- #[test]
- fn test_to_from_str() {
- fn test(r: Rational, s: String) {
- assert_eq!(FromStr::from_str(&s), Ok(r));
- assert_eq!(r.to_string(), s);
- }
- test(_1, "1".to_string());
- test(_0, "0".to_string());
- test(_1_2, "1/2".to_string());
- test(_3_2, "3/2".to_string());
- test(_2, "2".to_string());
- test(_NEG1_2, "-1/2".to_string());
- }
- #[test]
- fn test_from_str_fail() {
- fn test(s: &str) {
- let rational: Result<Rational, _> = FromStr::from_str(s);
- assert!(rational.is_err());
- }
- let xs = ["0 /1", "abc", "", "1/", "--1/2", "3/2/1", "1/0"];
- for &s in xs.iter() {
- test(s);
- }
- }
- #[cfg(feature = "num-bigint")]
- #[test]
- fn test_from_float() {
- fn test<T: Float>(given: T, (numer, denom): (&str, &str)) {
- let ratio: BigRational = Ratio::from_float(given).unwrap();
- assert_eq!(ratio,
- Ratio::new(FromStr::from_str(numer).unwrap(),
- FromStr::from_str(denom).unwrap()));
- }
- // f32
- test(3.14159265359f32, ("13176795", "4194304"));
- test(2f32.powf(100.), ("1267650600228229401496703205376", "1"));
- test(-2f32.powf(100.), ("-1267650600228229401496703205376", "1"));
- test(1.0 / 2f32.powf(100.),
- ("1", "1267650600228229401496703205376"));
- test(684729.48391f32, ("1369459", "2"));
- test(-8573.5918555f32, ("-4389679", "512"));
- // f64
- test(3.14159265359f64, ("3537118876014453", "1125899906842624"));
- test(2f64.powf(100.), ("1267650600228229401496703205376", "1"));
- test(-2f64.powf(100.), ("-1267650600228229401496703205376", "1"));
- test(684729.48391f64, ("367611342500051", "536870912"));
- test(-8573.5918555f64, ("-4713381968463931", "549755813888"));
- test(1.0 / 2f64.powf(100.),
- ("1", "1267650600228229401496703205376"));
- }
- #[cfg(feature = "num-bigint")]
- #[test]
- fn test_from_float_fail() {
- use std::{f32, f64};
- assert_eq!(Ratio::from_float(f32::NAN), None);
- assert_eq!(Ratio::from_float(f32::INFINITY), None);
- assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None);
- assert_eq!(Ratio::from_float(f64::NAN), None);
- assert_eq!(Ratio::from_float(f64::INFINITY), None);
- assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None);
- }
- #[test]
- fn test_signed() {
- assert_eq!(_NEG1_2.abs(), _1_2);
- assert_eq!(_3_2.abs_sub(&_1_2), _1);
- assert_eq!(_1_2.abs_sub(&_3_2), Zero::zero());
- assert_eq!(_1_2.signum(), One::one());
- assert_eq!(_NEG1_2.signum(), -<Ratio<isize>>::one());
- assert_eq!(_0.signum(), Zero::zero());
- assert!(_NEG1_2.is_negative());
- assert!(_1_NEG2.is_negative());
- assert!(!_NEG1_2.is_positive());
- assert!(!_1_NEG2.is_positive());
- assert!(_1_2.is_positive());
- assert!(_NEG1_NEG2.is_positive());
- assert!(!_1_2.is_negative());
- assert!(!_NEG1_NEG2.is_negative());
- assert!(!_0.is_positive());
- assert!(!_0.is_negative());
- }
- #[test]
- fn test_hash() {
- assert!(::hash(&_0) != ::hash(&_1));
- assert!(::hash(&_0) != ::hash(&_3_2));
- }
- #[test]
- fn test_into_pair() {
- assert_eq! ((0, 1), _0.into());
- assert_eq! ((-2, 1), _NEG2.into());
- assert_eq! ((1, -2), _1_NEG2.into());
- }
- #[test]
- fn test_from_pair() {
- assert_eq! (_0, Ratio::from ((0, 1)));
- assert_eq! (_1, Ratio::from ((1, 1)));
- assert_eq! (_NEG2, Ratio::from ((-2, 1)));
- assert_eq! (_1_NEG2, Ratio::from ((1, -2)));
- }
- }
|