// 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 //! //! ## Compatibility //! //! The `num-rational` crate is tested for rustc 1.60 and greater.
#![doc(html_root_url = "https://docs.rs/num-rational/0.4")] #![no_std] // Ratio ops often use other "suspicious" ops #![allow(clippy::suspicious_arithmetic_impl)] #![allow(clippy::suspicious_op_assign_impl)]
use core::cmp; use core::fmt; use core::fmt::{Binary, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; use core::hash::{Hash, Hasher}; use core::ops::{Add, Div, Mul, Neg, Rem, ShlAssign, Sub}; use core::str::FromStr; #[cfg(feature = "std")] use std::error::Error;
#[cfg(feature = "num-bigint")] use num_bigint::{BigInt, BigUint, Sign, ToBigInt};
use num_integer::Integer; use num_traits::float::FloatCore; use num_traits::{
Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, ConstOne, ConstZero, FromPrimitive,
Inv, Num, NumCast, One, Pow, Signed, ToPrimitive, Unsigned, Zero,
};
mod pow;
/// Represents the ratio between two numbers. #[derive(Copy, Clone, Debug)] #[allow(missing_docs)] pubstruct Ratio<T> { /// Numerator.
numer: T, /// Denominator.
denom: T,
}
/// Alias for a `Ratio` of machine-sized integers. #[deprecated(
since = "0.4.0",
note = "it's better to use a specific size, like `Rational32` or `Rational64`"
)] pubtype Rational = Ratio<isize>; /// Alias for a `Ratio` of 32-bit-sized integers. pubtype Rational32 = Ratio<i32>; /// Alias for a `Ratio` of 64-bit-sized integers. pubtype Rational64 = Ratio<i64>;
#[cfg(feature = "num-bigint")] /// Alias for arbitrary precision rationals. pubtype BigRational = Ratio<BigInt>;
/// These method are `const`. impl<T> Ratio<T> { /// Creates a `Ratio` without checking for `denom == 0` or reducing. /// /// **There are several methods that will panic if used on a `Ratio` with /// `denom == 0`.** #[inline] pubconstfn new_raw(numer: T, denom: T) -> Ratio<T> {
Ratio { numer, denom }
}
/// Deconstructs a `Ratio` into its numerator and denominator. #[inline] pubfn into_raw(self) -> (T, T) {
(self.numer, self.denom)
}
/// Gets an immutable reference to the numerator. #[inline] pubconstfn numer(&self) -> &T {
&self.numer
}
/// Gets an immutable reference to the denominator. #[inline] pubconstfn denom(&self) -> &T {
&self.denom
}
}
impl<T: Clone + Integer> Ratio<T> { /// Creates a new `Ratio`. /// /// **Panics if `denom` is zero.** #[inline] pubfn new(numer: T, denom: T) -> Ratio<T> { letmut ret = Ratio::new_raw(numer, denom);
ret.reduce();
ret
}
/// Creates a `Ratio` representing the integer `t`. #[inline] pubfn from_integer(t: T) -> Ratio<T> {
Ratio::new_raw(t, One::one())
}
/// Converts to an integer, rounding towards zero. #[inline] pubfn to_integer(&self) -> T { self.trunc().numer
}
/// Returns true if the rational number is an integer (denominator is 1). #[inline] pubfn is_integer(&self) -> bool { self.denom.is_one()
}
/// Puts self into lowest terms, with `denom` > 0. /// /// **Panics if `denom` is zero.** fn reduce(&mutself) { ifself.denom.is_zero() {
panic!("denominator == 0");
} ifself.numer.is_zero() { self.denom.set_one(); return;
} ifself.numer == self.denom { self.set_one(); return;
} let g: T = self.numer.gcd(&self.denom);
/// 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`. /// /// **Panics if `denom` is zero.** pubfn reduced(&self) -> Ratio<T> { letmut ret = self.clone();
ret.reduce();
ret
}
/// Returns the reciprocal. /// /// **Panics if the `Ratio` is zero.** #[inline] pubfn recip(&self) -> Ratio<T> { self.clone().into_recip()
}
/// Rounds towards minus infinity. #[inline] pubfn 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] pubfn 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] pubfn 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 letmut 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 / two
} else {
fractional.numer >= (fractional.denom / two) + one
};
if half_or_larger { let one: Ratio<T> = One::one(); if *self >= Zero::zero() { self.trunc() + one
} else { self.trunc() - one
}
} else { self.trunc()
}
}
/// Returns the fractional part of a number, with division rounded towards zero. /// /// Satisfies `self == self.trunc() + self.fract()`. #[inline] pubfn fract(&self) -> Ratio<T> {
Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone())
}
/// Raises the `Ratio` to the power of an exponent. #[inline] pubfn pow(&self, expon: i32) -> Ratio<T> where for<'a> &'a T: Pow<u32, Output = T>,
{
Pow::pow(self, expon)
}
}
#[cfg(feature = "num-bigint")] impl Ratio<BigInt> { /// Converts a float into a rational number. pubfn from_float<T: FloatCore>(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 { letmut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
numer <<= exponent as usize;
Some(Ratio::from_integer(BigInt::from_biguint(
bigint_sign,
numer,
)))
}
}
}
impl<T: Clone + Integer> Default for Ratio<T> { /// Returns zero fn default() -> Self {
Ratio::zero()
}
}
// 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 ifself.denom == other.denom { let ord = self.numer.cmp(&other.numer); returnifself.denom < T::zero() {
ord.reverse()
} else {
ord
};
}
// With equal numerators, the denominators can be inversely compared ifself.numer == other.numer { ifself.numer.is_zero() { return cmp::Ordering::Equal;
} let ord = self.denom.cmp(&other.denom); returnifself.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()
}
}
}
}
}
}
// NB: We can't just `#[derive(Hash)]`, because it needs to agree // with `Eq` even for non-reduced ratios. impl<T: Clone + Integer + Hash> Hash for Ratio<T> { fn hash<H: Hasher>(&self, state: &mut H) {
recurse(&self.numer, &self.denom, state);
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> { letmut split = s.splitn(2, '/');
let n = split.next().ok_or(ParseRatioError {
kind: RatioErrorKind::ParseError,
})?; let num = FromStr::from_str(n).map_err(|_| ParseRatioError {
kind: RatioErrorKind::ParseError,
})?;
let d = split.next().unwrap_or("1"); let den = FromStr::from_str(d).map_err(|_| ParseRatioError {
kind: RatioErrorKind::ParseError,
})?;
impl<T: Integer + Signed + Bounded + NumCast + Clone> Ratio<T> { pubfn approximate_float<F: FloatCore + 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)
}
}
impl<T: Integer + Unsigned + Bounded + NumCast + Clone> Ratio<T> { pubfn approximate_float_unsigned<F: FloatCore + 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_unsigned(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: FloatCore + 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
Some(if negative { 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: FloatCore + NumCast,
{ // Continued fractions algorithm // https://web.archive.org/web/20200629111319/http://mathforum.org:80/dr.math/faq/faq.fractions.html#decfrac
if val < F::zero() || val.is_nan() { return None;
}
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();
}
/// Converts a ratio of `T` to an f64. /// /// In addition to stated trait bounds, `T` must be able to hold numbers 56 bits larger than /// the largest of `numer` and `denom`. This is automatically true if `T` is `BigInt`. fn ratio_to_f64<T: Bits + Clone + Integer + Signed + ShlAssign<usize> + ToPrimitive>(
numer: T,
denom: T,
) -> f64 { use core::f64::{INFINITY, MANTISSA_DIGITS, MAX_EXP, MIN_EXP, RADIX};
assert_eq!(
RADIX, 2, "only floating point implementations with radix 2 are supported"
);
// Inclusive upper and lower bounds to the range of exactly-representable ints in an f64. const MAX_EXACT_INT: i64 = 1i64 << MANTISSA_DIGITS; const MIN_EXACT_INT: i64 = -MAX_EXACT_INT;
let flo_sign = numer.signum().to_f64().unwrap() / denom.signum().to_f64().unwrap(); if !flo_sign.is_normal() { return flo_sign;
}
// Fast track: both sides can losslessly be converted to f64s. In this case, letting the // FPU do the job is faster and easier. In any other case, converting to f64s may lead // to an inexact result: https://stackoverflow.com/questions/56641441/. iflet (Some(n), Some(d)) = (numer.to_i64(), denom.to_i64()) { let exact = MIN_EXACT_INT..=MAX_EXACT_INT; if exact.contains(&n) && exact.contains(&d) { return n.to_f64().unwrap() / d.to_f64().unwrap();
}
}
// Otherwise, the goal is to obtain a quotient with at least 55 bits. 53 of these bits will // be used as the mantissa of the resulting float, and the remaining two are for rounding. // There's an error of up to 1 on the number of resulting bits, so we may get either 55 or // 56 bits. letmut numer = numer.abs(); letmut denom = denom.abs(); let (is_diff_positive, absolute_diff) = match numer.bits().checked_sub(denom.bits()) {
Some(diff) => (true, diff),
None => (false, denom.bits() - numer.bits()),
};
// Filter out overflows and underflows. After this step, the signed difference fits in an // isize. if is_diff_positive && absolute_diff > MAX_EXP as u64 { return INFINITY * flo_sign;
} if !is_diff_positive && absolute_diff > -MIN_EXP as u64 + MANTISSA_DIGITS as u64 + 1 { return0.0 * flo_sign;
} let diff = if is_diff_positive {
absolute_diff.to_isize().unwrap()
} else {
-absolute_diff.to_isize().unwrap()
};
// Shift is chosen so that the quotient will have 55 or 56 bits. The exception is if the // quotient is going to be subnormal, in which case it may have fewer bits. let shift: isize = diff.max(MIN_EXP as isize) - MANTISSA_DIGITS as isize - 2; if shift >= 0 {
denom <<= shift as usize
} else {
numer <<= -shift as usize
};
let (quotient, remainder) = numer.div_rem(&denom);
// This is guaranteed to fit since we've set up quotient to be at most 56 bits. letmut quotient = quotient.to_u64().unwrap(); let n_rounding_bits = { let quotient_bits = 64 - quotient.leading_zeros() as isize; let subnormal_bits = MIN_EXP as isize - shift;
quotient_bits.max(subnormal_bits) - MANTISSA_DIGITS as isize
} as usize;
debug_assert!(n_rounding_bits == 2 || n_rounding_bits == 3); let rounding_bit_mask = (1u64 << n_rounding_bits) - 1;
// Round to 53 bits with round-to-even. For rounding, we need to take into account both // our rounding bits and the division's remainder. let ls_bit = quotient & (1u64 << n_rounding_bits) != 0; let ms_rounding_bit = quotient & (1u64 << (n_rounding_bits - 1)) != 0; let ls_rounding_bits = quotient & (rounding_bit_mask >> 1) != 0; if ms_rounding_bit && (ls_bit || ls_rounding_bits || !remainder.is_zero()) {
quotient += 1u64 << n_rounding_bits;
}
quotient &= !rounding_bit_mask;
// The quotient is guaranteed to be exactly representable as it's now 53 bits + 2 or 3 // trailing zeros, so there is no risk of a rounding error here. let q_float = quotient as f64 * flo_sign;
ldexp(q_float, shift as i32)
}
/// Multiply `x` by 2 to the power of `exp`. Returns an accurate result even if `2^exp` is not /// representable. fn ldexp(x: f64, exp: i32) -> f64 { use core::f64::{INFINITY, MANTISSA_DIGITS, MAX_EXP, RADIX};
assert_eq!(
RADIX, 2, "only floating point implementations with radix 2 are supported"
);
if x.is_zero() || x.is_infinite() || x.is_nan() { return x;
}
// Filter out obvious over / underflows to make sure the resulting exponent fits in an isize. if exp > 3 * MAX_EXP { return INFINITY * x.signum();
} elseif exp < -3 * MAX_EXP { return0.0 * x.signum();
}
// curr_exp is the x's *biased* exponent, and is in the [-54, MAX_UNSIGNED_EXPONENT] range. let (bits, curr_exp) = if !x.is_normal() { // If x is subnormal, we make it normal by multiplying by 2^53. This causes no loss of // precision or rounding. let normal_x = x * 2f64.powi(MIN_SUBNORMAL_POWER); let bits = normal_x.to_bits(); // This cast is safe because the exponent is at most 0x7fe, which fits in an i32.
(
bits,
((bits & EXPONENT_MASK) >> 52) as i32 - MIN_SUBNORMAL_POWER,
)
} else { let bits = x.to_bits(); let curr_exp = (bits & EXPONENT_MASK) >> 52; // This cast is safe because the exponent is at most 0x7fe, which fits in an i32.
(bits, curr_exp as i32)
};
// The addition can't overflow because exponent is between 0 and 0x7fe, and exp is between // -2*MAX_EXP and 2*MAX_EXP. let new_exp = curr_exp + exp;
if new_exp > MAX_UNSIGNED_EXPONENT {
INFINITY * x.signum()
} elseif new_exp > 0 { // Normal case: exponent is not too large nor subnormal. let new_bits = (bits & !EXPONENT_MASK) | ((new_exp as u64) << 52);
f64::from_bits(new_bits)
} elseif new_exp >= -(MANTISSA_DIGITS as i32) { // Result is subnormal but may not be zero. // In this case, we increase the exponent by 54 to make it normal, then multiply the end // result by 2^-53. This results in a single multiplication with no prior rounding error, // so there is no risk of double rounding. let new_exp = new_exp + MIN_SUBNORMAL_POWER;
debug_assert!(new_exp >= 0); let new_bits = (bits & !EXPONENT_MASK) | ((new_exp as u64) << 52);
f64::from_bits(new_bits) * 2f64.powi(-MIN_SUBNORMAL_POWER)
} else { // Result is zero. return0.0 * x.signum();
}
}
#[cfg(test)] #[cfg(feature = "std")] fn hash<T: Hash>(x: &T) -> u64 { use std::collections::hash_map::RandomState; use std::hash::BuildHasher; letmut hasher = <RandomState as BuildHasher>::Hasher::new();
x.hash(&mut hasher);
hasher.finish()
}
#[cfg(test)] mod test { usesuper::ldexp; #[cfg(feature = "num-bigint")] usesuper::{BigInt, BigRational}; usesuper::{Ratio, Rational64};
use core::f64; use core::i32; use core::i64; use core::str::FromStr; use num_integer::Integer; use num_traits::ToPrimitive; use num_traits::{FromPrimitive, One, Pow, Signed, Zero};
let _0_2: Rational64 = Ratio::new_raw(0, 2);
assert_eq!(_0, _0_2);
}
#[test] fn test_cmp_overflow() { use core::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 = [
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),
];
#[test] fn test_div_overflow() { fn test_div_typed_overflow<T>() where
T: Integer + Bounded + Clone + Debug + NumAssign + CheckedMul,
{ let two = T::one() + T::one(); let _3 = T::one() + T::one() + T::one();
// 1/big / 3/2 = 1/(max/4*3), where big is max/2 // big ~ max/2, and big is divisible by 2 let big = T::max_value() / two.clone() / two.clone() * two.clone();
assert_eq!(None, big.clone().checked_mul(&_3.clone())); let _1_big: Ratio<T> = Ratio::new(T::one(), big.clone()); let _3_two: Ratio<T> = Ratio::new(_3.clone(), two.clone()); let expected = Ratio::new(T::one(), big / two.clone() * _3.clone());
assert_eq!(expected.clone(), _1_big.clone() / _3_two.clone());
assert_eq!(
Some(expected.clone()),
_1_big.clone().checked_div(&_3_two.clone())
);
assert_eq!(expected, { letmut tmp = _1_big;
tmp /= _3_two;
tmp
});
// 3/big / 3 = 1/big where big is max/2 // big ~ max/2, and big is not divisible by 3 let big = T::max_value() / two / _3.clone() * _3.clone() + T::one();
assert_eq!(None, big.clone().checked_mul(&_3.clone())); let _3_big = Ratio::new(_3.clone(), big.clone()); let expected = Ratio::new(T::one(), big);
assert_eq!(expected, _3_big.clone() / _3.clone());
assert_eq!(expected, { letmut tmp = _3_big;
tmp /= _3;
tmp
});
}
test_div_typed_overflow::<u8>();
test_div_typed_overflow::<u16>();
test_div_typed_overflow::<u32>();
test_div_typed_overflow::<u64>();
test_div_typed_overflow::<usize>();
test_div_typed_overflow::<u128>();
// a == b -> hash(a) == hash(b) let a = Rational64::new_raw(4, 2); let b = Rational64::new_raw(6, 3);
assert_eq!(a, b);
assert_eq!(crate::hash(&a), crate::hash(&b));
let a = Rational64::new_raw(123456789, 1000); let b = Rational64::new_raw(123456789 * 5, 5000);
assert_eq!(a, b);
assert_eq!(crate::hash(&a), crate::hash(&b));
}
#[test] fn ratio_iter_sum() { // generic function to assure the iter method can be called // for any Iterator with Item = Ratio<impl Integer> or Ratio<&impl Integer> fn iter_sums<T: Integer + Clone>(slice: &[Ratio<T>]) -> [Ratio<T>; 3] { letmut manual_sum = Ratio::new(T::zero(), T::one()); for ratio in slice {
manual_sum = manual_sum + ratio;
}
[manual_sum, slice.iter().sum(), slice.iter().cloned().sum()]
} // collect into array so test works on no_std letmut nums = [Ratio::new(0, 1); 1000]; for (i, r) in (0..1000).map(|n| Ratio::new(n, 500)).enumerate() {
nums[i] = r;
} let sums = iter_sums(&nums[..]);
assert_eq!(sums[0], sums[1]);
assert_eq!(sums[0], sums[2]);
}
#[test] fn ratio_iter_product() { // generic function to assure the iter method can be called // for any Iterator with Item = Ratio<impl Integer> or Ratio<&impl Integer> fn iter_products<T: Integer + Clone>(slice: &[Ratio<T>]) -> [Ratio<T>; 3] { letmut manual_prod = Ratio::new(T::one(), T::one()); for ratio in slice {
manual_prod = manual_prod * ratio;
}
[
manual_prod,
slice.iter().product(),
slice.iter().cloned().product(),
]
}
// collect into array so test works on no_std letmut nums = [Ratio::new(0, 1); 1000]; for (i, r) in (0..1000).map(|n| Ratio::new(n, 500)).enumerate() {
nums[i] = r;
} let products = iter_products(&nums[..]);
assert_eq!(products[0], products[1]);
assert_eq!(products[0], products[2]);
}
#[test] fn test_num_zero() { let zero = Rational64::zero();
assert!(zero.is_zero());
letmut r = Rational64::new(123, 456);
assert!(!r.is_zero());
assert_eq!(r + zero, r);
r.set_zero();
assert!(r.is_zero());
}
#[test] fn test_num_one() { let one = Rational64::one();
assert!(one.is_one());
letmut r = Rational64::new(123, 456);
assert!(!r.is_one());
assert_eq!(r * one, r);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.