1 | // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! Rational numbers |
12 | //! |
13 | //! ## Compatibility |
14 | //! |
15 | //! The `num-rational` crate is tested for rustc 1.31 and greater. |
16 | |
17 | #![doc (html_root_url = "https://docs.rs/num-rational/0.4" )] |
18 | #![no_std ] |
19 | // Ratio ops often use other "suspicious" ops |
20 | #![allow (clippy::suspicious_arithmetic_impl)] |
21 | #![allow (clippy::suspicious_op_assign_impl)] |
22 | |
23 | #[cfg (feature = "std" )] |
24 | #[macro_use ] |
25 | extern crate std; |
26 | |
27 | use core::cmp; |
28 | use core::fmt; |
29 | use core::fmt::{Binary, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; |
30 | use core::hash::{Hash, Hasher}; |
31 | use core::ops::{Add, Div, Mul, Neg, Rem, ShlAssign, Sub}; |
32 | use core::str::FromStr; |
33 | #[cfg (feature = "std" )] |
34 | use std::error::Error; |
35 | |
36 | #[cfg (feature = "num-bigint" )] |
37 | use num_bigint::{BigInt, BigUint, Sign, ToBigInt}; |
38 | |
39 | use num_integer::Integer; |
40 | use num_traits::float::FloatCore; |
41 | use num_traits::ToPrimitive; |
42 | use num_traits::{ |
43 | Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Inv, Num, NumCast, One, |
44 | Pow, Signed, Zero, |
45 | }; |
46 | |
47 | mod pow; |
48 | |
49 | /// Represents the ratio between two numbers. |
50 | #[derive (Copy, Clone, Debug)] |
51 | #[allow (missing_docs)] |
52 | pub struct Ratio<T> { |
53 | /// Numerator. |
54 | numer: T, |
55 | /// Denominator. |
56 | denom: T, |
57 | } |
58 | |
59 | /// Alias for a `Ratio` of machine-sized integers. |
60 | #[deprecated ( |
61 | since = "0.4.0" , |
62 | note = "it's better to use a specific size, like `Rational32` or `Rational64`" |
63 | )] |
64 | pub type Rational = Ratio<isize>; |
65 | /// Alias for a `Ratio` of 32-bit-sized integers. |
66 | pub type Rational32 = Ratio<i32>; |
67 | /// Alias for a `Ratio` of 64-bit-sized integers. |
68 | pub type Rational64 = Ratio<i64>; |
69 | |
70 | #[cfg (feature = "num-bigint" )] |
71 | /// Alias for arbitrary precision rationals. |
72 | pub type BigRational = Ratio<BigInt>; |
73 | |
74 | /// These method are `const` for Rust 1.31 and later. |
75 | impl<T> Ratio<T> { |
76 | /// Creates a `Ratio` without checking for `denom == 0` or reducing. |
77 | /// |
78 | /// **There are several methods that will panic if used on a `Ratio` with |
79 | /// `denom == 0`.** |
80 | #[inline ] |
81 | pub const fn new_raw(numer: T, denom: T) -> Ratio<T> { |
82 | Ratio { numer, denom } |
83 | } |
84 | |
85 | /// Gets an immutable reference to the numerator. |
86 | #[inline ] |
87 | pub const fn numer(&self) -> &T { |
88 | &self.numer |
89 | } |
90 | |
91 | /// Gets an immutable reference to the denominator. |
92 | #[inline ] |
93 | pub const fn denom(&self) -> &T { |
94 | &self.denom |
95 | } |
96 | } |
97 | |
98 | impl<T: Clone + Integer> Ratio<T> { |
99 | /// Creates a new `Ratio`. |
100 | /// |
101 | /// **Panics if `denom` is zero.** |
102 | #[inline ] |
103 | pub fn new(numer: T, denom: T) -> Ratio<T> { |
104 | let mut ret = Ratio::new_raw(numer, denom); |
105 | ret.reduce(); |
106 | ret |
107 | } |
108 | |
109 | /// Creates a `Ratio` representing the integer `t`. |
110 | #[inline ] |
111 | pub fn from_integer(t: T) -> Ratio<T> { |
112 | Ratio::new_raw(t, One::one()) |
113 | } |
114 | |
115 | /// Converts to an integer, rounding towards zero. |
116 | #[inline ] |
117 | pub fn to_integer(&self) -> T { |
118 | self.trunc().numer |
119 | } |
120 | |
121 | /// Returns true if the rational number is an integer (denominator is 1). |
122 | #[inline ] |
123 | pub fn is_integer(&self) -> bool { |
124 | self.denom.is_one() |
125 | } |
126 | |
127 | /// Puts self into lowest terms, with `denom` > 0. |
128 | /// |
129 | /// **Panics if `denom` is zero.** |
130 | fn reduce(&mut self) { |
131 | if self.denom.is_zero() { |
132 | panic!("denominator == 0" ); |
133 | } |
134 | if self.numer.is_zero() { |
135 | self.denom.set_one(); |
136 | return; |
137 | } |
138 | if self.numer == self.denom { |
139 | self.set_one(); |
140 | return; |
141 | } |
142 | let g: T = self.numer.gcd(&self.denom); |
143 | |
144 | // FIXME(#5992): assignment operator overloads |
145 | // T: Clone + Integer != T: Clone + NumAssign |
146 | |
147 | #[inline ] |
148 | fn replace_with<T: Zero>(x: &mut T, f: impl FnOnce(T) -> T) { |
149 | let y = core::mem::replace(x, T::zero()); |
150 | *x = f(y); |
151 | } |
152 | |
153 | // self.numer /= g; |
154 | replace_with(&mut self.numer, |x| x / g.clone()); |
155 | |
156 | // self.denom /= g; |
157 | replace_with(&mut self.denom, |x| x / g); |
158 | |
159 | // keep denom positive! |
160 | if self.denom < T::zero() { |
161 | replace_with(&mut self.numer, |x| T::zero() - x); |
162 | replace_with(&mut self.denom, |x| T::zero() - x); |
163 | } |
164 | } |
165 | |
166 | /// Returns a reduced copy of self. |
167 | /// |
168 | /// In general, it is not necessary to use this method, as the only |
169 | /// method of procuring a non-reduced fraction is through `new_raw`. |
170 | /// |
171 | /// **Panics if `denom` is zero.** |
172 | pub fn reduced(&self) -> Ratio<T> { |
173 | let mut ret = self.clone(); |
174 | ret.reduce(); |
175 | ret |
176 | } |
177 | |
178 | /// Returns the reciprocal. |
179 | /// |
180 | /// **Panics if the `Ratio` is zero.** |
181 | #[inline ] |
182 | pub fn recip(&self) -> Ratio<T> { |
183 | self.clone().into_recip() |
184 | } |
185 | |
186 | #[inline ] |
187 | fn into_recip(self) -> Ratio<T> { |
188 | match self.numer.cmp(&T::zero()) { |
189 | cmp::Ordering::Equal => panic!("division by zero" ), |
190 | cmp::Ordering::Greater => Ratio::new_raw(self.denom, self.numer), |
191 | cmp::Ordering::Less => Ratio::new_raw(T::zero() - self.denom, T::zero() - self.numer), |
192 | } |
193 | } |
194 | |
195 | /// Rounds towards minus infinity. |
196 | #[inline ] |
197 | pub fn floor(&self) -> Ratio<T> { |
198 | if *self < Zero::zero() { |
199 | let one: T = One::one(); |
200 | Ratio::from_integer( |
201 | (self.numer.clone() - self.denom.clone() + one) / self.denom.clone(), |
202 | ) |
203 | } else { |
204 | Ratio::from_integer(self.numer.clone() / self.denom.clone()) |
205 | } |
206 | } |
207 | |
208 | /// Rounds towards plus infinity. |
209 | #[inline ] |
210 | pub fn ceil(&self) -> Ratio<T> { |
211 | if *self < Zero::zero() { |
212 | Ratio::from_integer(self.numer.clone() / self.denom.clone()) |
213 | } else { |
214 | let one: T = One::one(); |
215 | Ratio::from_integer( |
216 | (self.numer.clone() + self.denom.clone() - one) / self.denom.clone(), |
217 | ) |
218 | } |
219 | } |
220 | |
221 | /// Rounds to the nearest integer. Rounds half-way cases away from zero. |
222 | #[inline ] |
223 | pub fn round(&self) -> Ratio<T> { |
224 | let zero: Ratio<T> = Zero::zero(); |
225 | let one: T = One::one(); |
226 | let two: T = one.clone() + one.clone(); |
227 | |
228 | // Find unsigned fractional part of rational number |
229 | let mut fractional = self.fract(); |
230 | if fractional < zero { |
231 | fractional = zero - fractional |
232 | }; |
233 | |
234 | // The algorithm compares the unsigned fractional part with 1/2, that |
235 | // is, a/b >= 1/2, or a >= b/2. For odd denominators, we use |
236 | // a >= (b/2)+1. This avoids overflow issues. |
237 | let half_or_larger = if fractional.denom.is_even() { |
238 | fractional.numer >= fractional.denom / two |
239 | } else { |
240 | fractional.numer >= (fractional.denom / two) + one |
241 | }; |
242 | |
243 | if half_or_larger { |
244 | let one: Ratio<T> = One::one(); |
245 | if *self >= Zero::zero() { |
246 | self.trunc() + one |
247 | } else { |
248 | self.trunc() - one |
249 | } |
250 | } else { |
251 | self.trunc() |
252 | } |
253 | } |
254 | |
255 | /// Rounds towards zero. |
256 | #[inline ] |
257 | pub fn trunc(&self) -> Ratio<T> { |
258 | Ratio::from_integer(self.numer.clone() / self.denom.clone()) |
259 | } |
260 | |
261 | /// Returns the fractional part of a number, with division rounded towards zero. |
262 | /// |
263 | /// Satisfies `self == self.trunc() + self.fract()`. |
264 | #[inline ] |
265 | pub fn fract(&self) -> Ratio<T> { |
266 | Ratio::new_raw(self.numer.clone() % self.denom.clone(), self.denom.clone()) |
267 | } |
268 | |
269 | /// Raises the `Ratio` to the power of an exponent. |
270 | #[inline ] |
271 | pub fn pow(&self, expon: i32) -> Ratio<T> |
272 | where |
273 | for<'a> &'a T: Pow<u32, Output = T>, |
274 | { |
275 | Pow::pow(self, expon) |
276 | } |
277 | } |
278 | |
279 | #[cfg (feature = "num-bigint" )] |
280 | impl Ratio<BigInt> { |
281 | /// Converts a float into a rational number. |
282 | pub fn from_float<T: FloatCore>(f: T) -> Option<BigRational> { |
283 | if !f.is_finite() { |
284 | return None; |
285 | } |
286 | let (mantissa, exponent, sign) = f.integer_decode(); |
287 | let bigint_sign = if sign == 1 { Sign::Plus } else { Sign::Minus }; |
288 | if exponent < 0 { |
289 | let one: BigInt = One::one(); |
290 | let denom: BigInt = one << ((-exponent) as usize); |
291 | let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap(); |
292 | Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom)) |
293 | } else { |
294 | let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap(); |
295 | numer <<= exponent as usize; |
296 | Some(Ratio::from_integer(BigInt::from_biguint( |
297 | bigint_sign, |
298 | numer, |
299 | ))) |
300 | } |
301 | } |
302 | } |
303 | |
304 | impl<T: Clone + Integer> Default for Ratio<T> { |
305 | /// Returns zero |
306 | fn default() -> Self { |
307 | Ratio::zero() |
308 | } |
309 | } |
310 | |
311 | // From integer |
312 | impl<T> From<T> for Ratio<T> |
313 | where |
314 | T: Clone + Integer, |
315 | { |
316 | fn from(x: T) -> Ratio<T> { |
317 | Ratio::from_integer(x) |
318 | } |
319 | } |
320 | |
321 | // From pair (through the `new` constructor) |
322 | impl<T> From<(T, T)> for Ratio<T> |
323 | where |
324 | T: Clone + Integer, |
325 | { |
326 | fn from(pair: (T, T)) -> Ratio<T> { |
327 | Ratio::new(numer:pair.0, denom:pair.1) |
328 | } |
329 | } |
330 | |
331 | // Comparisons |
332 | |
333 | // Mathematically, comparing a/b and c/d is the same as comparing a*d and b*c, but it's very easy |
334 | // for those multiplications to overflow fixed-size integers, so we need to take care. |
335 | |
336 | impl<T: Clone + Integer> Ord for Ratio<T> { |
337 | #[inline ] |
338 | fn cmp(&self, other: &Self) -> cmp::Ordering { |
339 | // With equal denominators, the numerators can be directly compared |
340 | if self.denom == other.denom { |
341 | let ord = self.numer.cmp(&other.numer); |
342 | return if self.denom < T::zero() { |
343 | ord.reverse() |
344 | } else { |
345 | ord |
346 | }; |
347 | } |
348 | |
349 | // With equal numerators, the denominators can be inversely compared |
350 | if self.numer == other.numer { |
351 | if self.numer.is_zero() { |
352 | return cmp::Ordering::Equal; |
353 | } |
354 | let ord = self.denom.cmp(&other.denom); |
355 | return if self.numer < T::zero() { |
356 | ord |
357 | } else { |
358 | ord.reverse() |
359 | }; |
360 | } |
361 | |
362 | // Unfortunately, we don't have CheckedMul to try. That could sometimes avoid all the |
363 | // division below, or even always avoid it for BigInt and BigUint. |
364 | // FIXME- future breaking change to add Checked* to Integer? |
365 | |
366 | // Compare as floored integers and remainders |
367 | let (self_int, self_rem) = self.numer.div_mod_floor(&self.denom); |
368 | let (other_int, other_rem) = other.numer.div_mod_floor(&other.denom); |
369 | match self_int.cmp(&other_int) { |
370 | cmp::Ordering::Greater => cmp::Ordering::Greater, |
371 | cmp::Ordering::Less => cmp::Ordering::Less, |
372 | cmp::Ordering::Equal => { |
373 | match (self_rem.is_zero(), other_rem.is_zero()) { |
374 | (true, true) => cmp::Ordering::Equal, |
375 | (true, false) => cmp::Ordering::Less, |
376 | (false, true) => cmp::Ordering::Greater, |
377 | (false, false) => { |
378 | // Compare the reciprocals of the remaining fractions in reverse |
379 | let self_recip = Ratio::new_raw(self.denom.clone(), self_rem); |
380 | let other_recip = Ratio::new_raw(other.denom.clone(), other_rem); |
381 | self_recip.cmp(&other_recip).reverse() |
382 | } |
383 | } |
384 | } |
385 | } |
386 | } |
387 | } |
388 | |
389 | impl<T: Clone + Integer> PartialOrd for Ratio<T> { |
390 | #[inline ] |
391 | fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
392 | Some(self.cmp(other)) |
393 | } |
394 | } |
395 | |
396 | impl<T: Clone + Integer> PartialEq for Ratio<T> { |
397 | #[inline ] |
398 | fn eq(&self, other: &Self) -> bool { |
399 | self.cmp(other) == cmp::Ordering::Equal |
400 | } |
401 | } |
402 | |
403 | impl<T: Clone + Integer> Eq for Ratio<T> {} |
404 | |
405 | // NB: We can't just `#[derive(Hash)]`, because it needs to agree |
406 | // with `Eq` even for non-reduced ratios. |
407 | impl<T: Clone + Integer + Hash> Hash for Ratio<T> { |
408 | fn hash<H: Hasher>(&self, state: &mut H) { |
409 | recurse(&self.numer, &self.denom, state); |
410 | |
411 | fn recurse<T: Integer + Hash, H: Hasher>(numer: &T, denom: &T, state: &mut H) { |
412 | if !denom.is_zero() { |
413 | let (int: T, rem: T) = numer.div_mod_floor(denom); |
414 | int.hash(state); |
415 | recurse(numer:denom, &rem, state); |
416 | } else { |
417 | denom.hash(state); |
418 | } |
419 | } |
420 | } |
421 | } |
422 | |
423 | mod iter_sum_product { |
424 | use crate::Ratio; |
425 | use core::iter::{Product, Sum}; |
426 | use num_integer::Integer; |
427 | use num_traits::{One, Zero}; |
428 | |
429 | impl<T: Integer + Clone> Sum for Ratio<T> { |
430 | fn sum<I>(iter: I) -> Self |
431 | where |
432 | I: Iterator<Item = Ratio<T>>, |
433 | { |
434 | iter.fold(Self::zero(), |sum, num| sum + num) |
435 | } |
436 | } |
437 | |
438 | impl<'a, T: Integer + Clone> Sum<&'a Ratio<T>> for Ratio<T> { |
439 | fn sum<I>(iter: I) -> Self |
440 | where |
441 | I: Iterator<Item = &'a Ratio<T>>, |
442 | { |
443 | iter.fold(Self::zero(), |sum, num| sum + num) |
444 | } |
445 | } |
446 | |
447 | impl<T: Integer + Clone> Product for Ratio<T> { |
448 | fn product<I>(iter: I) -> Self |
449 | where |
450 | I: Iterator<Item = Ratio<T>>, |
451 | { |
452 | iter.fold(Self::one(), |prod, num| prod * num) |
453 | } |
454 | } |
455 | |
456 | impl<'a, T: Integer + Clone> Product<&'a Ratio<T>> for Ratio<T> { |
457 | fn product<I>(iter: I) -> Self |
458 | where |
459 | I: Iterator<Item = &'a Ratio<T>>, |
460 | { |
461 | iter.fold(Self::one(), |prod, num| prod * num) |
462 | } |
463 | } |
464 | } |
465 | |
466 | mod opassign { |
467 | use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; |
468 | |
469 | use crate::Ratio; |
470 | use num_integer::Integer; |
471 | use num_traits::NumAssign; |
472 | |
473 | impl<T: Clone + Integer + NumAssign> AddAssign for Ratio<T> { |
474 | fn add_assign(&mut self, other: Ratio<T>) { |
475 | if self.denom == other.denom { |
476 | self.numer += other.numer |
477 | } else { |
478 | let lcm = self.denom.lcm(&other.denom); |
479 | let lhs_numer = self.numer.clone() * (lcm.clone() / self.denom.clone()); |
480 | let rhs_numer = other.numer * (lcm.clone() / other.denom); |
481 | self.numer = lhs_numer + rhs_numer; |
482 | self.denom = lcm; |
483 | } |
484 | self.reduce(); |
485 | } |
486 | } |
487 | |
488 | // (a/b) / (c/d) = (a/gcd_ac)*(d/gcd_bd) / ((c/gcd_ac)*(b/gcd_bd)) |
489 | impl<T: Clone + Integer + NumAssign> DivAssign for Ratio<T> { |
490 | fn div_assign(&mut self, other: Ratio<T>) { |
491 | let gcd_ac = self.numer.gcd(&other.numer); |
492 | let gcd_bd = self.denom.gcd(&other.denom); |
493 | self.numer /= gcd_ac.clone(); |
494 | self.numer *= other.denom / gcd_bd.clone(); |
495 | self.denom /= gcd_bd; |
496 | self.denom *= other.numer / gcd_ac; |
497 | self.reduce(); // TODO: remove this line. see #8. |
498 | } |
499 | } |
500 | |
501 | // a/b * c/d = (a/gcd_ad)*(c/gcd_bc) / ((d/gcd_ad)*(b/gcd_bc)) |
502 | impl<T: Clone + Integer + NumAssign> MulAssign for Ratio<T> { |
503 | fn mul_assign(&mut self, other: Ratio<T>) { |
504 | let gcd_ad = self.numer.gcd(&other.denom); |
505 | let gcd_bc = self.denom.gcd(&other.numer); |
506 | self.numer /= gcd_ad.clone(); |
507 | self.numer *= other.numer / gcd_bc.clone(); |
508 | self.denom /= gcd_bc; |
509 | self.denom *= other.denom / gcd_ad; |
510 | self.reduce(); // TODO: remove this line. see #8. |
511 | } |
512 | } |
513 | |
514 | impl<T: Clone + Integer + NumAssign> RemAssign for Ratio<T> { |
515 | fn rem_assign(&mut self, other: Ratio<T>) { |
516 | if self.denom == other.denom { |
517 | self.numer %= other.numer |
518 | } else { |
519 | let lcm = self.denom.lcm(&other.denom); |
520 | let lhs_numer = self.numer.clone() * (lcm.clone() / self.denom.clone()); |
521 | let rhs_numer = other.numer * (lcm.clone() / other.denom); |
522 | self.numer = lhs_numer % rhs_numer; |
523 | self.denom = lcm; |
524 | } |
525 | self.reduce(); |
526 | } |
527 | } |
528 | |
529 | impl<T: Clone + Integer + NumAssign> SubAssign for Ratio<T> { |
530 | fn sub_assign(&mut self, other: Ratio<T>) { |
531 | if self.denom == other.denom { |
532 | self.numer -= other.numer |
533 | } else { |
534 | let lcm = self.denom.lcm(&other.denom); |
535 | let lhs_numer = self.numer.clone() * (lcm.clone() / self.denom.clone()); |
536 | let rhs_numer = other.numer * (lcm.clone() / other.denom); |
537 | self.numer = lhs_numer - rhs_numer; |
538 | self.denom = lcm; |
539 | } |
540 | self.reduce(); |
541 | } |
542 | } |
543 | |
544 | // a/b + c/1 = (a*1 + b*c) / (b*1) = (a + b*c) / b |
545 | impl<T: Clone + Integer + NumAssign> AddAssign<T> for Ratio<T> { |
546 | fn add_assign(&mut self, other: T) { |
547 | self.numer += self.denom.clone() * other; |
548 | self.reduce(); |
549 | } |
550 | } |
551 | |
552 | impl<T: Clone + Integer + NumAssign> DivAssign<T> for Ratio<T> { |
553 | fn div_assign(&mut self, other: T) { |
554 | let gcd = self.numer.gcd(&other); |
555 | self.numer /= gcd.clone(); |
556 | self.denom *= other / gcd; |
557 | self.reduce(); // TODO: remove this line. see #8. |
558 | } |
559 | } |
560 | |
561 | impl<T: Clone + Integer + NumAssign> MulAssign<T> for Ratio<T> { |
562 | fn mul_assign(&mut self, other: T) { |
563 | let gcd = self.denom.gcd(&other); |
564 | self.denom /= gcd.clone(); |
565 | self.numer *= other / gcd; |
566 | self.reduce(); // TODO: remove this line. see #8. |
567 | } |
568 | } |
569 | |
570 | // a/b % c/1 = (a*1 % b*c) / (b*1) = (a % b*c) / b |
571 | impl<T: Clone + Integer + NumAssign> RemAssign<T> for Ratio<T> { |
572 | fn rem_assign(&mut self, other: T) { |
573 | self.numer %= self.denom.clone() * other; |
574 | self.reduce(); |
575 | } |
576 | } |
577 | |
578 | // a/b - c/1 = (a*1 - b*c) / (b*1) = (a - b*c) / b |
579 | impl<T: Clone + Integer + NumAssign> SubAssign<T> for Ratio<T> { |
580 | fn sub_assign(&mut self, other: T) { |
581 | self.numer -= self.denom.clone() * other; |
582 | self.reduce(); |
583 | } |
584 | } |
585 | |
586 | macro_rules! forward_op_assign { |
587 | (impl $imp:ident, $method:ident) => { |
588 | impl<'a, T: Clone + Integer + NumAssign> $imp<&'a Ratio<T>> for Ratio<T> { |
589 | #[inline] |
590 | fn $method(&mut self, other: &Ratio<T>) { |
591 | self.$method(other.clone()) |
592 | } |
593 | } |
594 | impl<'a, T: Clone + Integer + NumAssign> $imp<&'a T> for Ratio<T> { |
595 | #[inline] |
596 | fn $method(&mut self, other: &T) { |
597 | self.$method(other.clone()) |
598 | } |
599 | } |
600 | }; |
601 | } |
602 | |
603 | forward_op_assign!(impl AddAssign, add_assign); |
604 | forward_op_assign!(impl DivAssign, div_assign); |
605 | forward_op_assign!(impl MulAssign, mul_assign); |
606 | forward_op_assign!(impl RemAssign, rem_assign); |
607 | forward_op_assign!(impl SubAssign, sub_assign); |
608 | } |
609 | |
610 | macro_rules! forward_ref_ref_binop { |
611 | (impl $imp:ident, $method:ident) => { |
612 | impl<'a, 'b, T: Clone + Integer> $imp<&'b Ratio<T>> for &'a Ratio<T> { |
613 | type Output = Ratio<T>; |
614 | |
615 | #[inline] |
616 | fn $method(self, other: &'b Ratio<T>) -> Ratio<T> { |
617 | self.clone().$method(other.clone()) |
618 | } |
619 | } |
620 | impl<'a, 'b, T: Clone + Integer> $imp<&'b T> for &'a Ratio<T> { |
621 | type Output = Ratio<T>; |
622 | |
623 | #[inline] |
624 | fn $method(self, other: &'b T) -> Ratio<T> { |
625 | self.clone().$method(other.clone()) |
626 | } |
627 | } |
628 | }; |
629 | } |
630 | |
631 | macro_rules! forward_ref_val_binop { |
632 | (impl $imp:ident, $method:ident) => { |
633 | impl<'a, T> $imp<Ratio<T>> for &'a Ratio<T> |
634 | where |
635 | T: Clone + Integer, |
636 | { |
637 | type Output = Ratio<T>; |
638 | |
639 | #[inline] |
640 | fn $method(self, other: Ratio<T>) -> Ratio<T> { |
641 | self.clone().$method(other) |
642 | } |
643 | } |
644 | impl<'a, T> $imp<T> for &'a Ratio<T> |
645 | where |
646 | T: Clone + Integer, |
647 | { |
648 | type Output = Ratio<T>; |
649 | |
650 | #[inline] |
651 | fn $method(self, other: T) -> Ratio<T> { |
652 | self.clone().$method(other) |
653 | } |
654 | } |
655 | }; |
656 | } |
657 | |
658 | macro_rules! forward_val_ref_binop { |
659 | (impl $imp:ident, $method:ident) => { |
660 | impl<'a, T> $imp<&'a Ratio<T>> for Ratio<T> |
661 | where |
662 | T: Clone + Integer, |
663 | { |
664 | type Output = Ratio<T>; |
665 | |
666 | #[inline] |
667 | fn $method(self, other: &Ratio<T>) -> Ratio<T> { |
668 | self.$method(other.clone()) |
669 | } |
670 | } |
671 | impl<'a, T> $imp<&'a T> for Ratio<T> |
672 | where |
673 | T: Clone + Integer, |
674 | { |
675 | type Output = Ratio<T>; |
676 | |
677 | #[inline] |
678 | fn $method(self, other: &T) -> Ratio<T> { |
679 | self.$method(other.clone()) |
680 | } |
681 | } |
682 | }; |
683 | } |
684 | |
685 | macro_rules! forward_all_binop { |
686 | (impl $imp:ident, $method:ident) => { |
687 | forward_ref_ref_binop!(impl $imp, $method); |
688 | forward_ref_val_binop!(impl $imp, $method); |
689 | forward_val_ref_binop!(impl $imp, $method); |
690 | }; |
691 | } |
692 | |
693 | // Arithmetic |
694 | forward_all_binop!(impl Mul, mul); |
695 | // a/b * c/d = (a/gcd_ad)*(c/gcd_bc) / ((d/gcd_ad)*(b/gcd_bc)) |
696 | impl<T> Mul<Ratio<T>> for Ratio<T> |
697 | where |
698 | T: Clone + Integer, |
699 | { |
700 | type Output = Ratio<T>; |
701 | #[inline ] |
702 | fn mul(self, rhs: Ratio<T>) -> Ratio<T> { |
703 | let gcd_ad: T = self.numer.gcd(&rhs.denom); |
704 | let gcd_bc: T = self.denom.gcd(&rhs.numer); |
705 | Ratio::new( |
706 | self.numer / gcd_ad.clone() * (rhs.numer / gcd_bc.clone()), |
707 | self.denom / gcd_bc * (rhs.denom / gcd_ad), |
708 | ) |
709 | } |
710 | } |
711 | // a/b * c/1 = (a*c) / (b*1) = (a*c) / b |
712 | impl<T> Mul<T> for Ratio<T> |
713 | where |
714 | T: Clone + Integer, |
715 | { |
716 | type Output = Ratio<T>; |
717 | #[inline ] |
718 | fn mul(self, rhs: T) -> Ratio<T> { |
719 | let gcd: T = self.denom.gcd(&rhs); |
720 | Ratio::new(self.numer * (rhs / gcd.clone()), self.denom / gcd) |
721 | } |
722 | } |
723 | |
724 | forward_all_binop!(impl Div, div); |
725 | // (a/b) / (c/d) = (a/gcd_ac)*(d/gcd_bd) / ((c/gcd_ac)*(b/gcd_bd)) |
726 | impl<T> Div<Ratio<T>> for Ratio<T> |
727 | where |
728 | T: Clone + Integer, |
729 | { |
730 | type Output = Ratio<T>; |
731 | |
732 | #[inline ] |
733 | fn div(self, rhs: Ratio<T>) -> Ratio<T> { |
734 | let gcd_ac: T = self.numer.gcd(&rhs.numer); |
735 | let gcd_bd: T = self.denom.gcd(&rhs.denom); |
736 | Ratio::new( |
737 | self.numer / gcd_ac.clone() * (rhs.denom / gcd_bd.clone()), |
738 | self.denom / gcd_bd * (rhs.numer / gcd_ac), |
739 | ) |
740 | } |
741 | } |
742 | // (a/b) / (c/1) = (a*1) / (b*c) = a / (b*c) |
743 | impl<T> Div<T> for Ratio<T> |
744 | where |
745 | T: Clone + Integer, |
746 | { |
747 | type Output = Ratio<T>; |
748 | |
749 | #[inline ] |
750 | fn div(self, rhs: T) -> Ratio<T> { |
751 | let gcd: T = self.numer.gcd(&rhs); |
752 | Ratio::new(self.numer / gcd.clone(), self.denom * (rhs / gcd)) |
753 | } |
754 | } |
755 | |
756 | macro_rules! arith_impl { |
757 | (impl $imp:ident, $method:ident) => { |
758 | forward_all_binop!(impl $imp, $method); |
759 | // Abstracts a/b `op` c/d = (a*lcm/b `op` c*lcm/d)/lcm where lcm = lcm(b,d) |
760 | impl<T: Clone + Integer> $imp<Ratio<T>> for Ratio<T> { |
761 | type Output = Ratio<T>; |
762 | #[inline] |
763 | fn $method(self, rhs: Ratio<T>) -> Ratio<T> { |
764 | if self.denom == rhs.denom { |
765 | return Ratio::new(self.numer.$method(rhs.numer), rhs.denom); |
766 | } |
767 | let lcm = self.denom.lcm(&rhs.denom); |
768 | let lhs_numer = self.numer * (lcm.clone() / self.denom); |
769 | let rhs_numer = rhs.numer * (lcm.clone() / rhs.denom); |
770 | Ratio::new(lhs_numer.$method(rhs_numer), lcm) |
771 | } |
772 | } |
773 | // Abstracts the a/b `op` c/1 = (a*1 `op` b*c) / (b*1) = (a `op` b*c) / b pattern |
774 | impl<T: Clone + Integer> $imp<T> for Ratio<T> { |
775 | type Output = Ratio<T>; |
776 | #[inline] |
777 | fn $method(self, rhs: T) -> Ratio<T> { |
778 | Ratio::new(self.numer.$method(self.denom.clone() * rhs), self.denom) |
779 | } |
780 | } |
781 | }; |
782 | } |
783 | |
784 | arith_impl!(impl Add, add); |
785 | arith_impl!(impl Sub, sub); |
786 | arith_impl!(impl Rem, rem); |
787 | |
788 | // a/b * c/d = (a*c)/(b*d) |
789 | impl<T> CheckedMul for Ratio<T> |
790 | where |
791 | T: Clone + Integer + CheckedMul, |
792 | { |
793 | #[inline ] |
794 | fn checked_mul(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> { |
795 | let gcd_ad: T = self.numer.gcd(&rhs.denom); |
796 | let gcd_bc: T = self.denom.gcd(&rhs.numer); |
797 | Some(Ratio::new( |
798 | (self.numer.clone() / gcd_ad.clone()) |
799 | .checked_mul(&(rhs.numer.clone() / gcd_bc.clone()))?, |
800 | (self.denom.clone() / gcd_bc).checked_mul(&(rhs.denom.clone() / gcd_ad))?, |
801 | )) |
802 | } |
803 | } |
804 | |
805 | // (a/b) / (c/d) = (a*d)/(b*c) |
806 | impl<T> CheckedDiv for Ratio<T> |
807 | where |
808 | T: Clone + Integer + CheckedMul, |
809 | { |
810 | #[inline ] |
811 | fn checked_div(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> { |
812 | if rhs.is_zero() { |
813 | return None; |
814 | } |
815 | let (numer, denom) = if self.denom == rhs.denom { |
816 | (self.numer.clone(), rhs.numer.clone()) |
817 | } else if self.numer == rhs.numer { |
818 | (rhs.denom.clone(), self.denom.clone()) |
819 | } else { |
820 | let gcd_ac = self.numer.gcd(&rhs.numer); |
821 | let gcd_bd = self.denom.gcd(&rhs.denom); |
822 | ( |
823 | (self.numer.clone() / gcd_ac.clone()) |
824 | .checked_mul(&(rhs.denom.clone() / gcd_bd.clone()))?, |
825 | (self.denom.clone() / gcd_bd).checked_mul(&(rhs.numer.clone() / gcd_ac))?, |
826 | ) |
827 | }; |
828 | // Manual `reduce()`, avoiding sharp edges |
829 | if denom.is_zero() { |
830 | None |
831 | } else if numer.is_zero() { |
832 | Some(Self::zero()) |
833 | } else if numer == denom { |
834 | Some(Self::one()) |
835 | } else { |
836 | let g = numer.gcd(&denom); |
837 | let numer = numer / g.clone(); |
838 | let denom = denom / g; |
839 | let raw = if denom < T::zero() { |
840 | // We need to keep denom positive, but 2's-complement MIN may |
841 | // overflow negation -- instead we can check multiplying -1. |
842 | let n1 = T::zero() - T::one(); |
843 | Ratio::new_raw(numer.checked_mul(&n1)?, denom.checked_mul(&n1)?) |
844 | } else { |
845 | Ratio::new_raw(numer, denom) |
846 | }; |
847 | Some(raw) |
848 | } |
849 | } |
850 | } |
851 | |
852 | // As arith_impl! but for Checked{Add,Sub} traits |
853 | macro_rules! checked_arith_impl { |
854 | (impl $imp:ident, $method:ident) => { |
855 | impl<T: Clone + Integer + CheckedMul + $imp> $imp for Ratio<T> { |
856 | #[inline] |
857 | fn $method(&self, rhs: &Ratio<T>) -> Option<Ratio<T>> { |
858 | let gcd = self.denom.clone().gcd(&rhs.denom); |
859 | let lcm = (self.denom.clone() / gcd.clone()).checked_mul(&rhs.denom)?; |
860 | let lhs_numer = (lcm.clone() / self.denom.clone()).checked_mul(&self.numer)?; |
861 | let rhs_numer = (lcm.clone() / rhs.denom.clone()).checked_mul(&rhs.numer)?; |
862 | Some(Ratio::new(lhs_numer.$method(&rhs_numer)?, lcm)) |
863 | } |
864 | } |
865 | }; |
866 | } |
867 | |
868 | // a/b + c/d = (lcm/b*a + lcm/d*c)/lcm, where lcm = lcm(b,d) |
869 | checked_arith_impl!(impl CheckedAdd, checked_add); |
870 | |
871 | // a/b - c/d = (lcm/b*a - lcm/d*c)/lcm, where lcm = lcm(b,d) |
872 | checked_arith_impl!(impl CheckedSub, checked_sub); |
873 | |
874 | impl<T> Neg for Ratio<T> |
875 | where |
876 | T: Clone + Integer + Neg<Output = T>, |
877 | { |
878 | type Output = Ratio<T>; |
879 | |
880 | #[inline ] |
881 | fn neg(self) -> Ratio<T> { |
882 | Ratio::new_raw(-self.numer, self.denom) |
883 | } |
884 | } |
885 | |
886 | impl<'a, T> Neg for &'a Ratio<T> |
887 | where |
888 | T: Clone + Integer + Neg<Output = T>, |
889 | { |
890 | type Output = Ratio<T>; |
891 | |
892 | #[inline ] |
893 | fn neg(self) -> Ratio<T> { |
894 | -self.clone() |
895 | } |
896 | } |
897 | |
898 | impl<T> Inv for Ratio<T> |
899 | where |
900 | T: Clone + Integer, |
901 | { |
902 | type Output = Ratio<T>; |
903 | |
904 | #[inline ] |
905 | fn inv(self) -> Ratio<T> { |
906 | self.recip() |
907 | } |
908 | } |
909 | |
910 | impl<'a, T> Inv for &'a Ratio<T> |
911 | where |
912 | T: Clone + Integer, |
913 | { |
914 | type Output = Ratio<T>; |
915 | |
916 | #[inline ] |
917 | fn inv(self) -> Ratio<T> { |
918 | self.recip() |
919 | } |
920 | } |
921 | |
922 | // Constants |
923 | impl<T: Clone + Integer> Zero for Ratio<T> { |
924 | #[inline ] |
925 | fn zero() -> Ratio<T> { |
926 | Ratio::new_raw(numer:Zero::zero(), denom:One::one()) |
927 | } |
928 | |
929 | #[inline ] |
930 | fn is_zero(&self) -> bool { |
931 | self.numer.is_zero() |
932 | } |
933 | |
934 | #[inline ] |
935 | fn set_zero(&mut self) { |
936 | self.numer.set_zero(); |
937 | self.denom.set_one(); |
938 | } |
939 | } |
940 | |
941 | impl<T: Clone + Integer> One for Ratio<T> { |
942 | #[inline ] |
943 | fn one() -> Ratio<T> { |
944 | Ratio::new_raw(numer:One::one(), denom:One::one()) |
945 | } |
946 | |
947 | #[inline ] |
948 | fn is_one(&self) -> bool { |
949 | self.numer == self.denom |
950 | } |
951 | |
952 | #[inline ] |
953 | fn set_one(&mut self) { |
954 | self.numer.set_one(); |
955 | self.denom.set_one(); |
956 | } |
957 | } |
958 | |
959 | impl<T: Clone + Integer> Num for Ratio<T> { |
960 | type FromStrRadixErr = ParseRatioError; |
961 | |
962 | /// Parses `numer/denom` where the numbers are in base `radix`. |
963 | fn from_str_radix(s: &str, radix: u32) -> Result<Ratio<T>, ParseRatioError> { |
964 | if s.splitn(2, '/' ).count() == 2 { |
965 | let mut parts = s.splitn(2, '/' ).map(|ss| { |
966 | T::from_str_radix(ss, radix).map_err(|_| ParseRatioError { |
967 | kind: RatioErrorKind::ParseError, |
968 | }) |
969 | }); |
970 | let numer: T = parts.next().unwrap()?; |
971 | let denom: T = parts.next().unwrap()?; |
972 | if denom.is_zero() { |
973 | Err(ParseRatioError { |
974 | kind: RatioErrorKind::ZeroDenominator, |
975 | }) |
976 | } else { |
977 | Ok(Ratio::new(numer, denom)) |
978 | } |
979 | } else { |
980 | Err(ParseRatioError { |
981 | kind: RatioErrorKind::ParseError, |
982 | }) |
983 | } |
984 | } |
985 | } |
986 | |
987 | impl<T: Clone + Integer + Signed> Signed for Ratio<T> { |
988 | #[inline ] |
989 | fn abs(&self) -> Ratio<T> { |
990 | if self.is_negative() { |
991 | -self.clone() |
992 | } else { |
993 | self.clone() |
994 | } |
995 | } |
996 | |
997 | #[inline ] |
998 | fn abs_sub(&self, other: &Ratio<T>) -> Ratio<T> { |
999 | if *self <= *other { |
1000 | Zero::zero() |
1001 | } else { |
1002 | self - other |
1003 | } |
1004 | } |
1005 | |
1006 | #[inline ] |
1007 | fn signum(&self) -> Ratio<T> { |
1008 | if self.is_positive() { |
1009 | Self::one() |
1010 | } else if self.is_zero() { |
1011 | Self::zero() |
1012 | } else { |
1013 | -Self::one() |
1014 | } |
1015 | } |
1016 | |
1017 | #[inline ] |
1018 | fn is_positive(&self) -> bool { |
1019 | (self.numer.is_positive() && self.denom.is_positive()) |
1020 | || (self.numer.is_negative() && self.denom.is_negative()) |
1021 | } |
1022 | |
1023 | #[inline ] |
1024 | fn is_negative(&self) -> bool { |
1025 | (self.numer.is_negative() && self.denom.is_positive()) |
1026 | || (self.numer.is_positive() && self.denom.is_negative()) |
1027 | } |
1028 | } |
1029 | |
1030 | // String conversions |
1031 | macro_rules! impl_formatting { |
1032 | ($fmt_trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { |
1033 | impl<T: $fmt_trait + Clone + Integer> $fmt_trait for Ratio<T> { |
1034 | #[cfg(feature = "std" )] |
1035 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
1036 | let pre_pad = if self.denom.is_one() { |
1037 | format!($fmt_str, self.numer) |
1038 | } else { |
1039 | if f.alternate() { |
1040 | format!(concat!($fmt_str, "/" , $fmt_alt), self.numer, self.denom) |
1041 | } else { |
1042 | format!(concat!($fmt_str, "/" , $fmt_str), self.numer, self.denom) |
1043 | } |
1044 | }; |
1045 | // TODO: replace with strip_prefix, when stabalized |
1046 | let (pre_pad, non_negative) = { |
1047 | if pre_pad.starts_with("-" ) { |
1048 | (&pre_pad[1..], false) |
1049 | } else { |
1050 | (&pre_pad[..], true) |
1051 | } |
1052 | }; |
1053 | f.pad_integral(non_negative, $prefix, pre_pad) |
1054 | } |
1055 | #[cfg(not(feature = "std" ))] |
1056 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
1057 | let plus = if f.sign_plus() && self.numer >= T::zero() { |
1058 | "+" |
1059 | } else { |
1060 | "" |
1061 | }; |
1062 | if self.denom.is_one() { |
1063 | if f.alternate() { |
1064 | write!(f, concat!("{}" , $fmt_alt), plus, self.numer) |
1065 | } else { |
1066 | write!(f, concat!("{}" , $fmt_str), plus, self.numer) |
1067 | } |
1068 | } else { |
1069 | if f.alternate() { |
1070 | write!( |
1071 | f, |
1072 | concat!("{}" , $fmt_alt, "/" , $fmt_alt), |
1073 | plus, self.numer, self.denom |
1074 | ) |
1075 | } else { |
1076 | write!( |
1077 | f, |
1078 | concat!("{}" , $fmt_str, "/" , $fmt_str), |
1079 | plus, self.numer, self.denom |
1080 | ) |
1081 | } |
1082 | } |
1083 | } |
1084 | } |
1085 | }; |
1086 | } |
1087 | |
1088 | impl_formatting!(Display, "" , "{}" , "{:#}" ); |
1089 | impl_formatting!(Octal, "0o" , "{:o}" , "{:#o}" ); |
1090 | impl_formatting!(Binary, "0b" , "{:b}" , "{:#b}" ); |
1091 | impl_formatting!(LowerHex, "0x" , "{:x}" , "{:#x}" ); |
1092 | impl_formatting!(UpperHex, "0x" , "{:X}" , "{:#X}" ); |
1093 | impl_formatting!(LowerExp, "" , "{:e}" , "{:#e}" ); |
1094 | impl_formatting!(UpperExp, "" , "{:E}" , "{:#E}" ); |
1095 | |
1096 | impl<T: FromStr + Clone + Integer> FromStr for Ratio<T> { |
1097 | type Err = ParseRatioError; |
1098 | |
1099 | /// Parses `numer/denom` or just `numer`. |
1100 | fn from_str(s: &str) -> Result<Ratio<T>, ParseRatioError> { |
1101 | let mut split = s.splitn(2, '/' ); |
1102 | |
1103 | let n = split.next().ok_or(ParseRatioError { |
1104 | kind: RatioErrorKind::ParseError, |
1105 | })?; |
1106 | let num = FromStr::from_str(n).map_err(|_| ParseRatioError { |
1107 | kind: RatioErrorKind::ParseError, |
1108 | })?; |
1109 | |
1110 | let d = split.next().unwrap_or("1" ); |
1111 | let den = FromStr::from_str(d).map_err(|_| ParseRatioError { |
1112 | kind: RatioErrorKind::ParseError, |
1113 | })?; |
1114 | |
1115 | if Zero::is_zero(&den) { |
1116 | Err(ParseRatioError { |
1117 | kind: RatioErrorKind::ZeroDenominator, |
1118 | }) |
1119 | } else { |
1120 | Ok(Ratio::new(num, den)) |
1121 | } |
1122 | } |
1123 | } |
1124 | |
1125 | impl<T> Into<(T, T)> for Ratio<T> { |
1126 | fn into(self) -> (T, T) { |
1127 | (self.numer, self.denom) |
1128 | } |
1129 | } |
1130 | |
1131 | #[cfg (feature = "serde" )] |
1132 | impl<T> serde::Serialize for Ratio<T> |
1133 | where |
1134 | T: serde::Serialize + Clone + Integer + PartialOrd, |
1135 | { |
1136 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
1137 | where |
1138 | S: serde::Serializer, |
1139 | { |
1140 | (self.numer(), self.denom()).serialize(serializer) |
1141 | } |
1142 | } |
1143 | |
1144 | #[cfg (feature = "serde" )] |
1145 | impl<'de, T> serde::Deserialize<'de> for Ratio<T> |
1146 | where |
1147 | T: serde::Deserialize<'de> + Clone + Integer + PartialOrd, |
1148 | { |
1149 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
1150 | where |
1151 | D: serde::Deserializer<'de>, |
1152 | { |
1153 | use serde::de::Error; |
1154 | use serde::de::Unexpected; |
1155 | let (numer, denom): (T, T) = serde::Deserialize::deserialize(deserializer)?; |
1156 | if denom.is_zero() { |
1157 | Err(Error::invalid_value( |
1158 | Unexpected::Signed(0), |
1159 | &"a ratio with non-zero denominator" , |
1160 | )) |
1161 | } else { |
1162 | Ok(Ratio::new_raw(numer, denom)) |
1163 | } |
1164 | } |
1165 | } |
1166 | |
1167 | // FIXME: Bubble up specific errors |
1168 | #[derive (Copy, Clone, Debug, PartialEq)] |
1169 | pub struct ParseRatioError { |
1170 | kind: RatioErrorKind, |
1171 | } |
1172 | |
1173 | #[derive (Copy, Clone, Debug, PartialEq)] |
1174 | enum RatioErrorKind { |
1175 | ParseError, |
1176 | ZeroDenominator, |
1177 | } |
1178 | |
1179 | impl fmt::Display for ParseRatioError { |
1180 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1181 | self.kind.description().fmt(f) |
1182 | } |
1183 | } |
1184 | |
1185 | #[cfg (feature = "std" )] |
1186 | impl Error for ParseRatioError { |
1187 | #[allow (deprecated)] |
1188 | fn description(&self) -> &str { |
1189 | self.kind.description() |
1190 | } |
1191 | } |
1192 | |
1193 | impl RatioErrorKind { |
1194 | fn description(&self) -> &'static str { |
1195 | match *self { |
1196 | RatioErrorKind::ParseError => "failed to parse integer" , |
1197 | RatioErrorKind::ZeroDenominator => "zero value denominator" , |
1198 | } |
1199 | } |
1200 | } |
1201 | |
1202 | #[cfg (feature = "num-bigint" )] |
1203 | impl FromPrimitive for Ratio<BigInt> { |
1204 | fn from_i64(n: i64) -> Option<Self> { |
1205 | Some(Ratio::from_integer(n.into())) |
1206 | } |
1207 | |
1208 | fn from_i128(n: i128) -> Option<Self> { |
1209 | Some(Ratio::from_integer(n.into())) |
1210 | } |
1211 | |
1212 | fn from_u64(n: u64) -> Option<Self> { |
1213 | Some(Ratio::from_integer(n.into())) |
1214 | } |
1215 | |
1216 | fn from_u128(n: u128) -> Option<Self> { |
1217 | Some(Ratio::from_integer(n.into())) |
1218 | } |
1219 | |
1220 | fn from_f32(n: f32) -> Option<Self> { |
1221 | Ratio::from_float(n) |
1222 | } |
1223 | |
1224 | fn from_f64(n: f64) -> Option<Self> { |
1225 | Ratio::from_float(n) |
1226 | } |
1227 | } |
1228 | |
1229 | macro_rules! from_primitive_integer { |
1230 | ($typ:ty, $approx:ident) => { |
1231 | impl FromPrimitive for Ratio<$typ> { |
1232 | fn from_i64(n: i64) -> Option<Self> { |
1233 | <$typ as FromPrimitive>::from_i64(n).map(Ratio::from_integer) |
1234 | } |
1235 | |
1236 | fn from_i128(n: i128) -> Option<Self> { |
1237 | <$typ as FromPrimitive>::from_i128(n).map(Ratio::from_integer) |
1238 | } |
1239 | |
1240 | fn from_u64(n: u64) -> Option<Self> { |
1241 | <$typ as FromPrimitive>::from_u64(n).map(Ratio::from_integer) |
1242 | } |
1243 | |
1244 | fn from_u128(n: u128) -> Option<Self> { |
1245 | <$typ as FromPrimitive>::from_u128(n).map(Ratio::from_integer) |
1246 | } |
1247 | |
1248 | fn from_f32(n: f32) -> Option<Self> { |
1249 | $approx(n, 10e-20, 30) |
1250 | } |
1251 | |
1252 | fn from_f64(n: f64) -> Option<Self> { |
1253 | $approx(n, 10e-20, 30) |
1254 | } |
1255 | } |
1256 | }; |
1257 | } |
1258 | |
1259 | from_primitive_integer!(i8, approximate_float); |
1260 | from_primitive_integer!(i16, approximate_float); |
1261 | from_primitive_integer!(i32, approximate_float); |
1262 | from_primitive_integer!(i64, approximate_float); |
1263 | from_primitive_integer!(i128, approximate_float); |
1264 | from_primitive_integer!(isize, approximate_float); |
1265 | |
1266 | from_primitive_integer!(u8, approximate_float_unsigned); |
1267 | from_primitive_integer!(u16, approximate_float_unsigned); |
1268 | from_primitive_integer!(u32, approximate_float_unsigned); |
1269 | from_primitive_integer!(u64, approximate_float_unsigned); |
1270 | from_primitive_integer!(u128, approximate_float_unsigned); |
1271 | from_primitive_integer!(usize, approximate_float_unsigned); |
1272 | |
1273 | impl<T: Integer + Signed + Bounded + NumCast + Clone> Ratio<T> { |
1274 | pub fn approximate_float<F: FloatCore + NumCast>(f: F) -> Option<Ratio<T>> { |
1275 | // 1/10e-20 < 1/2**32 which seems like a good default, and 30 seems |
1276 | // to work well. Might want to choose something based on the types in the future, e.g. |
1277 | // T::max().recip() and T::bits() or something similar. |
1278 | let epsilon: F = <F as NumCast>::from(10e-20).expect(msg:"Can't convert 10e-20" ); |
1279 | approximate_float(val:f, max_error:epsilon, max_iterations:30) |
1280 | } |
1281 | } |
1282 | |
1283 | fn approximate_float<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>> |
1284 | where |
1285 | T: Integer + Signed + Bounded + NumCast + Clone, |
1286 | F: FloatCore + NumCast, |
1287 | { |
1288 | let negative: bool = val.is_sign_negative(); |
1289 | let abs_val: F = val.abs(); |
1290 | |
1291 | let r: Ratio = approximate_float_unsigned(abs_val, max_error, max_iterations)?; |
1292 | |
1293 | // Make negative again if needed |
1294 | Some(if negative { r.neg() } else { r }) |
1295 | } |
1296 | |
1297 | // No Unsigned constraint because this also works on positive integers and is called |
1298 | // like that, see above |
1299 | fn approximate_float_unsigned<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>> |
1300 | where |
1301 | T: Integer + Bounded + NumCast + Clone, |
1302 | F: FloatCore + NumCast, |
1303 | { |
1304 | // Continued fractions algorithm |
1305 | // http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac |
1306 | |
1307 | if val < F::zero() || val.is_nan() { |
1308 | return None; |
1309 | } |
1310 | |
1311 | let mut q = val; |
1312 | let mut n0 = T::zero(); |
1313 | let mut d0 = T::one(); |
1314 | let mut n1 = T::one(); |
1315 | let mut d1 = T::zero(); |
1316 | |
1317 | let t_max = T::max_value(); |
1318 | let t_max_f = <F as NumCast>::from(t_max.clone())?; |
1319 | |
1320 | // 1/epsilon > T::MAX |
1321 | let epsilon = t_max_f.recip(); |
1322 | |
1323 | // Overflow |
1324 | if q > t_max_f { |
1325 | return None; |
1326 | } |
1327 | |
1328 | for _ in 0..max_iterations { |
1329 | let a = match <T as NumCast>::from(q) { |
1330 | None => break, |
1331 | Some(a) => a, |
1332 | }; |
1333 | |
1334 | let a_f = match <F as NumCast>::from(a.clone()) { |
1335 | None => break, |
1336 | Some(a_f) => a_f, |
1337 | }; |
1338 | let f = q - a_f; |
1339 | |
1340 | // Prevent overflow |
1341 | if !a.is_zero() |
1342 | && (n1 > t_max.clone() / a.clone() |
1343 | || d1 > t_max.clone() / a.clone() |
1344 | || a.clone() * n1.clone() > t_max.clone() - n0.clone() |
1345 | || a.clone() * d1.clone() > t_max.clone() - d0.clone()) |
1346 | { |
1347 | break; |
1348 | } |
1349 | |
1350 | let n = a.clone() * n1.clone() + n0.clone(); |
1351 | let d = a.clone() * d1.clone() + d0.clone(); |
1352 | |
1353 | n0 = n1; |
1354 | d0 = d1; |
1355 | n1 = n.clone(); |
1356 | d1 = d.clone(); |
1357 | |
1358 | // Simplify fraction. Doing so here instead of at the end |
1359 | // allows us to get closer to the target value without overflows |
1360 | let g = Integer::gcd(&n1, &d1); |
1361 | if !g.is_zero() { |
1362 | n1 = n1 / g.clone(); |
1363 | d1 = d1 / g.clone(); |
1364 | } |
1365 | |
1366 | // Close enough? |
1367 | let (n_f, d_f) = match (<F as NumCast>::from(n), <F as NumCast>::from(d)) { |
1368 | (Some(n_f), Some(d_f)) => (n_f, d_f), |
1369 | _ => break, |
1370 | }; |
1371 | if (n_f / d_f - val).abs() < max_error { |
1372 | break; |
1373 | } |
1374 | |
1375 | // Prevent division by ~0 |
1376 | if f < epsilon { |
1377 | break; |
1378 | } |
1379 | q = f.recip(); |
1380 | } |
1381 | |
1382 | // Overflow |
1383 | if d1.is_zero() { |
1384 | return None; |
1385 | } |
1386 | |
1387 | Some(Ratio::new(n1, d1)) |
1388 | } |
1389 | |
1390 | #[cfg (not(feature = "num-bigint" ))] |
1391 | macro_rules! to_primitive_small { |
1392 | ($($type_name:ty)*) => ($( |
1393 | impl ToPrimitive for Ratio<$type_name> { |
1394 | fn to_i64(&self) -> Option<i64> { |
1395 | self.to_integer().to_i64() |
1396 | } |
1397 | |
1398 | fn to_i128(&self) -> Option<i128> { |
1399 | self.to_integer().to_i128() |
1400 | } |
1401 | |
1402 | fn to_u64(&self) -> Option<u64> { |
1403 | self.to_integer().to_u64() |
1404 | } |
1405 | |
1406 | fn to_u128(&self) -> Option<u128> { |
1407 | self.to_integer().to_u128() |
1408 | } |
1409 | |
1410 | fn to_f64(&self) -> Option<f64> { |
1411 | let float = self.numer.to_f64().unwrap() / self.denom.to_f64().unwrap(); |
1412 | if float.is_nan() { |
1413 | None |
1414 | } else { |
1415 | Some(float) |
1416 | } |
1417 | } |
1418 | } |
1419 | )*) |
1420 | } |
1421 | |
1422 | #[cfg (not(feature = "num-bigint" ))] |
1423 | to_primitive_small!(u8 i8 u16 i16 u32 i32); |
1424 | |
1425 | #[cfg (all(target_pointer_width = "32" , not(feature = "num-bigint" )))] |
1426 | to_primitive_small!(usize isize); |
1427 | |
1428 | #[cfg (not(feature = "num-bigint" ))] |
1429 | macro_rules! to_primitive_64 { |
1430 | ($($type_name:ty)*) => ($( |
1431 | impl ToPrimitive for Ratio<$type_name> { |
1432 | fn to_i64(&self) -> Option<i64> { |
1433 | self.to_integer().to_i64() |
1434 | } |
1435 | |
1436 | fn to_i128(&self) -> Option<i128> { |
1437 | self.to_integer().to_i128() |
1438 | } |
1439 | |
1440 | fn to_u64(&self) -> Option<u64> { |
1441 | self.to_integer().to_u64() |
1442 | } |
1443 | |
1444 | fn to_u128(&self) -> Option<u128> { |
1445 | self.to_integer().to_u128() |
1446 | } |
1447 | |
1448 | fn to_f64(&self) -> Option<f64> { |
1449 | let float = ratio_to_f64( |
1450 | self.numer as i128, |
1451 | self.denom as i128 |
1452 | ); |
1453 | if float.is_nan() { |
1454 | None |
1455 | } else { |
1456 | Some(float) |
1457 | } |
1458 | } |
1459 | } |
1460 | )*) |
1461 | } |
1462 | |
1463 | #[cfg (not(feature = "num-bigint" ))] |
1464 | to_primitive_64!(u64 i64); |
1465 | |
1466 | #[cfg (all(target_pointer_width = "64" , not(feature = "num-bigint" )))] |
1467 | to_primitive_64!(usize isize); |
1468 | |
1469 | #[cfg (feature = "num-bigint" )] |
1470 | impl<T: Clone + Integer + ToPrimitive + ToBigInt> ToPrimitive for Ratio<T> { |
1471 | fn to_i64(&self) -> Option<i64> { |
1472 | self.to_integer().to_i64() |
1473 | } |
1474 | |
1475 | fn to_i128(&self) -> Option<i128> { |
1476 | self.to_integer().to_i128() |
1477 | } |
1478 | |
1479 | fn to_u64(&self) -> Option<u64> { |
1480 | self.to_integer().to_u64() |
1481 | } |
1482 | |
1483 | fn to_u128(&self) -> Option<u128> { |
1484 | self.to_integer().to_u128() |
1485 | } |
1486 | |
1487 | fn to_f64(&self) -> Option<f64> { |
1488 | let float = match (self.numer.to_i64(), self.denom.to_i64()) { |
1489 | (Some(numer), Some(denom)) => ratio_to_f64( |
1490 | <i128 as From<_>>::from(numer), |
1491 | <i128 as From<_>>::from(denom), |
1492 | ), |
1493 | _ => { |
1494 | let numer: BigInt = self.numer.to_bigint()?; |
1495 | let denom: BigInt = self.denom.to_bigint()?; |
1496 | ratio_to_f64(numer, denom) |
1497 | } |
1498 | }; |
1499 | if float.is_nan() { |
1500 | None |
1501 | } else { |
1502 | Some(float) |
1503 | } |
1504 | } |
1505 | } |
1506 | |
1507 | trait Bits { |
1508 | fn bits(&self) -> u64; |
1509 | } |
1510 | |
1511 | #[cfg (feature = "num-bigint" )] |
1512 | impl Bits for BigInt { |
1513 | fn bits(&self) -> u64 { |
1514 | self.bits() |
1515 | } |
1516 | } |
1517 | |
1518 | impl Bits for i128 { |
1519 | fn bits(&self) -> u64 { |
1520 | (128 - self.wrapping_abs().leading_zeros()).into() |
1521 | } |
1522 | } |
1523 | |
1524 | /// Converts a ratio of `T` to an f64. |
1525 | /// |
1526 | /// In addition to stated trait bounds, `T` must be able to hold numbers 56 bits larger than |
1527 | /// the largest of `numer` and `denom`. This is automatically true if `T` is `BigInt`. |
1528 | fn ratio_to_f64<T: Bits + Clone + Integer + Signed + ShlAssign<usize> + ToPrimitive>( |
1529 | numer: T, |
1530 | denom: T, |
1531 | ) -> f64 { |
1532 | use core::f64::{INFINITY, MANTISSA_DIGITS, MAX_EXP, MIN_EXP, RADIX}; |
1533 | |
1534 | assert_eq!( |
1535 | RADIX, 2, |
1536 | "only floating point implementations with radix 2 are supported" |
1537 | ); |
1538 | |
1539 | // Inclusive upper and lower bounds to the range of exactly-representable ints in an f64. |
1540 | const MAX_EXACT_INT: i64 = 1i64 << MANTISSA_DIGITS; |
1541 | const MIN_EXACT_INT: i64 = -MAX_EXACT_INT; |
1542 | |
1543 | let flo_sign = numer.signum().to_f64().unwrap() / denom.signum().to_f64().unwrap(); |
1544 | if !flo_sign.is_normal() { |
1545 | return flo_sign; |
1546 | } |
1547 | |
1548 | // Fast track: both sides can losslessly be converted to f64s. In this case, letting the |
1549 | // FPU do the job is faster and easier. In any other case, converting to f64s may lead |
1550 | // to an inexact result: https://stackoverflow.com/questions/56641441/. |
1551 | if let (Some(n), Some(d)) = (numer.to_i64(), denom.to_i64()) { |
1552 | if MIN_EXACT_INT <= n && n <= MAX_EXACT_INT && MIN_EXACT_INT <= d && d <= MAX_EXACT_INT { |
1553 | return n.to_f64().unwrap() / d.to_f64().unwrap(); |
1554 | } |
1555 | } |
1556 | |
1557 | // Otherwise, the goal is to obtain a quotient with at least 55 bits. 53 of these bits will |
1558 | // be used as the mantissa of the resulting float, and the remaining two are for rounding. |
1559 | // There's an error of up to 1 on the number of resulting bits, so we may get either 55 or |
1560 | // 56 bits. |
1561 | let mut numer = numer.abs(); |
1562 | let mut denom = denom.abs(); |
1563 | let (is_diff_positive, absolute_diff) = match numer.bits().checked_sub(denom.bits()) { |
1564 | Some(diff) => (true, diff), |
1565 | None => (false, denom.bits() - numer.bits()), |
1566 | }; |
1567 | |
1568 | // Filter out overflows and underflows. After this step, the signed difference fits in an |
1569 | // isize. |
1570 | if is_diff_positive && absolute_diff > MAX_EXP as u64 { |
1571 | return INFINITY * flo_sign; |
1572 | } |
1573 | if !is_diff_positive && absolute_diff > -MIN_EXP as u64 + MANTISSA_DIGITS as u64 + 1 { |
1574 | return 0.0 * flo_sign; |
1575 | } |
1576 | let diff = if is_diff_positive { |
1577 | absolute_diff.to_isize().unwrap() |
1578 | } else { |
1579 | -absolute_diff.to_isize().unwrap() |
1580 | }; |
1581 | |
1582 | // Shift is chosen so that the quotient will have 55 or 56 bits. The exception is if the |
1583 | // quotient is going to be subnormal, in which case it may have fewer bits. |
1584 | let shift: isize = diff.max(MIN_EXP as isize) - MANTISSA_DIGITS as isize - 2; |
1585 | if shift >= 0 { |
1586 | denom <<= shift as usize |
1587 | } else { |
1588 | numer <<= -shift as usize |
1589 | }; |
1590 | |
1591 | let (quotient, remainder) = numer.div_rem(&denom); |
1592 | |
1593 | // This is guaranteed to fit since we've set up quotient to be at most 56 bits. |
1594 | let mut quotient = quotient.to_u64().unwrap(); |
1595 | let n_rounding_bits = { |
1596 | let quotient_bits = 64 - quotient.leading_zeros() as isize; |
1597 | let subnormal_bits = MIN_EXP as isize - shift; |
1598 | quotient_bits.max(subnormal_bits) - MANTISSA_DIGITS as isize |
1599 | } as usize; |
1600 | debug_assert!(n_rounding_bits == 2 || n_rounding_bits == 3); |
1601 | let rounding_bit_mask = (1u64 << n_rounding_bits) - 1; |
1602 | |
1603 | // Round to 53 bits with round-to-even. For rounding, we need to take into account both |
1604 | // our rounding bits and the division's remainder. |
1605 | let ls_bit = quotient & (1u64 << n_rounding_bits) != 0; |
1606 | let ms_rounding_bit = quotient & (1u64 << (n_rounding_bits - 1)) != 0; |
1607 | let ls_rounding_bits = quotient & (rounding_bit_mask >> 1) != 0; |
1608 | if ms_rounding_bit && (ls_bit || ls_rounding_bits || !remainder.is_zero()) { |
1609 | quotient += 1u64 << n_rounding_bits; |
1610 | } |
1611 | quotient &= !rounding_bit_mask; |
1612 | |
1613 | // The quotient is guaranteed to be exactly representable as it's now 53 bits + 2 or 3 |
1614 | // trailing zeros, so there is no risk of a rounding error here. |
1615 | let q_float = quotient as f64 * flo_sign; |
1616 | ldexp(q_float, shift as i32) |
1617 | } |
1618 | |
1619 | /// Multiply `x` by 2 to the power of `exp`. Returns an accurate result even if `2^exp` is not |
1620 | /// representable. |
1621 | fn ldexp(x: f64, exp: i32) -> f64 { |
1622 | use core::f64::{INFINITY, MANTISSA_DIGITS, MAX_EXP, RADIX}; |
1623 | |
1624 | assert_eq!( |
1625 | RADIX, 2, |
1626 | "only floating point implementations with radix 2 are supported" |
1627 | ); |
1628 | |
1629 | const EXPONENT_MASK: u64 = 0x7ff << 52; |
1630 | const MAX_UNSIGNED_EXPONENT: i32 = 0x7fe; |
1631 | const MIN_SUBNORMAL_POWER: i32 = MANTISSA_DIGITS as i32; |
1632 | |
1633 | if x.is_zero() || x.is_infinite() || x.is_nan() { |
1634 | return x; |
1635 | } |
1636 | |
1637 | // Filter out obvious over / underflows to make sure the resulting exponent fits in an isize. |
1638 | if exp > 3 * MAX_EXP { |
1639 | return INFINITY * x.signum(); |
1640 | } else if exp < -3 * MAX_EXP { |
1641 | return 0.0 * x.signum(); |
1642 | } |
1643 | |
1644 | // curr_exp is the x's *biased* exponent, and is in the [-54, MAX_UNSIGNED_EXPONENT] range. |
1645 | let (bits, curr_exp) = if !x.is_normal() { |
1646 | // If x is subnormal, we make it normal by multiplying by 2^53. This causes no loss of |
1647 | // precision or rounding. |
1648 | let normal_x = x * 2f64.powi(MIN_SUBNORMAL_POWER); |
1649 | let bits = normal_x.to_bits(); |
1650 | // This cast is safe because the exponent is at most 0x7fe, which fits in an i32. |
1651 | ( |
1652 | bits, |
1653 | ((bits & EXPONENT_MASK) >> 52) as i32 - MIN_SUBNORMAL_POWER, |
1654 | ) |
1655 | } else { |
1656 | let bits = x.to_bits(); |
1657 | let curr_exp = (bits & EXPONENT_MASK) >> 52; |
1658 | // This cast is safe because the exponent is at most 0x7fe, which fits in an i32. |
1659 | (bits, curr_exp as i32) |
1660 | }; |
1661 | |
1662 | // The addition can't overflow because exponent is between 0 and 0x7fe, and exp is between |
1663 | // -2*MAX_EXP and 2*MAX_EXP. |
1664 | let new_exp = curr_exp + exp; |
1665 | |
1666 | if new_exp > MAX_UNSIGNED_EXPONENT { |
1667 | INFINITY * x.signum() |
1668 | } else if new_exp > 0 { |
1669 | // Normal case: exponent is not too large nor subnormal. |
1670 | let new_bits = (bits & !EXPONENT_MASK) | ((new_exp as u64) << 52); |
1671 | f64::from_bits(new_bits) |
1672 | } else if new_exp >= -(MANTISSA_DIGITS as i32) { |
1673 | // Result is subnormal but may not be zero. |
1674 | // In this case, we increase the exponent by 54 to make it normal, then multiply the end |
1675 | // result by 2^-53. This results in a single multiplication with no prior rounding error, |
1676 | // so there is no risk of double rounding. |
1677 | let new_exp = new_exp + MIN_SUBNORMAL_POWER; |
1678 | debug_assert!(new_exp >= 0); |
1679 | let new_bits = (bits & !EXPONENT_MASK) | ((new_exp as u64) << 52); |
1680 | f64::from_bits(new_bits) * 2f64.powi(-MIN_SUBNORMAL_POWER) |
1681 | } else { |
1682 | // Result is zero. |
1683 | return 0.0 * x.signum(); |
1684 | } |
1685 | } |
1686 | |
1687 | #[cfg (test)] |
1688 | #[cfg (feature = "std" )] |
1689 | fn hash<T: Hash>(x: &T) -> u64 { |
1690 | use std::collections::hash_map::RandomState; |
1691 | use std::hash::BuildHasher; |
1692 | let mut hasher = <RandomState as BuildHasher>::Hasher::new(); |
1693 | x.hash(&mut hasher); |
1694 | hasher.finish() |
1695 | } |
1696 | |
1697 | #[cfg (test)] |
1698 | mod test { |
1699 | use super::ldexp; |
1700 | #[cfg (all(feature = "num-bigint" ))] |
1701 | use super::BigInt; |
1702 | #[cfg (feature = "num-bigint" )] |
1703 | use super::BigRational; |
1704 | use super::{Ratio, Rational64}; |
1705 | |
1706 | use core::f64; |
1707 | use core::i32; |
1708 | use core::i64; |
1709 | use core::str::FromStr; |
1710 | use num_integer::Integer; |
1711 | use num_traits::ToPrimitive; |
1712 | use num_traits::{FromPrimitive, One, Pow, Signed, Zero}; |
1713 | |
1714 | pub const _0: Rational64 = Ratio { numer: 0, denom: 1 }; |
1715 | pub const _1: Rational64 = Ratio { numer: 1, denom: 1 }; |
1716 | pub const _2: Rational64 = Ratio { numer: 2, denom: 1 }; |
1717 | pub const _NEG2: Rational64 = Ratio { |
1718 | numer: -2, |
1719 | denom: 1, |
1720 | }; |
1721 | pub const _8: Rational64 = Ratio { numer: 8, denom: 1 }; |
1722 | pub const _15: Rational64 = Ratio { |
1723 | numer: 15, |
1724 | denom: 1, |
1725 | }; |
1726 | pub const _16: Rational64 = Ratio { |
1727 | numer: 16, |
1728 | denom: 1, |
1729 | }; |
1730 | |
1731 | pub const _1_2: Rational64 = Ratio { numer: 1, denom: 2 }; |
1732 | pub const _1_8: Rational64 = Ratio { numer: 1, denom: 8 }; |
1733 | pub const _1_15: Rational64 = Ratio { |
1734 | numer: 1, |
1735 | denom: 15, |
1736 | }; |
1737 | pub const _1_16: Rational64 = Ratio { |
1738 | numer: 1, |
1739 | denom: 16, |
1740 | }; |
1741 | pub const _3_2: Rational64 = Ratio { numer: 3, denom: 2 }; |
1742 | pub const _5_2: Rational64 = Ratio { numer: 5, denom: 2 }; |
1743 | pub const _NEG1_2: Rational64 = Ratio { |
1744 | numer: -1, |
1745 | denom: 2, |
1746 | }; |
1747 | pub const _1_NEG2: Rational64 = Ratio { |
1748 | numer: 1, |
1749 | denom: -2, |
1750 | }; |
1751 | pub const _NEG1_NEG2: Rational64 = Ratio { |
1752 | numer: -1, |
1753 | denom: -2, |
1754 | }; |
1755 | pub const _1_3: Rational64 = Ratio { numer: 1, denom: 3 }; |
1756 | pub const _NEG1_3: Rational64 = Ratio { |
1757 | numer: -1, |
1758 | denom: 3, |
1759 | }; |
1760 | pub const _2_3: Rational64 = Ratio { numer: 2, denom: 3 }; |
1761 | pub const _NEG2_3: Rational64 = Ratio { |
1762 | numer: -2, |
1763 | denom: 3, |
1764 | }; |
1765 | pub const _MIN: Rational64 = Ratio { |
1766 | numer: i64::MIN, |
1767 | denom: 1, |
1768 | }; |
1769 | pub const _MIN_P1: Rational64 = Ratio { |
1770 | numer: i64::MIN + 1, |
1771 | denom: 1, |
1772 | }; |
1773 | pub const _MAX: Rational64 = Ratio { |
1774 | numer: i64::MAX, |
1775 | denom: 1, |
1776 | }; |
1777 | pub const _MAX_M1: Rational64 = Ratio { |
1778 | numer: i64::MAX - 1, |
1779 | denom: 1, |
1780 | }; |
1781 | pub const _BILLION: Rational64 = Ratio { |
1782 | numer: 1_000_000_000, |
1783 | denom: 1, |
1784 | }; |
1785 | |
1786 | #[cfg (feature = "num-bigint" )] |
1787 | pub fn to_big(n: Rational64) -> BigRational { |
1788 | Ratio::new( |
1789 | FromPrimitive::from_i64(n.numer).unwrap(), |
1790 | FromPrimitive::from_i64(n.denom).unwrap(), |
1791 | ) |
1792 | } |
1793 | #[cfg (not(feature = "num-bigint" ))] |
1794 | pub fn to_big(n: Rational64) -> Rational64 { |
1795 | Ratio::new( |
1796 | FromPrimitive::from_i64(n.numer).unwrap(), |
1797 | FromPrimitive::from_i64(n.denom).unwrap(), |
1798 | ) |
1799 | } |
1800 | |
1801 | #[test ] |
1802 | fn test_test_constants() { |
1803 | // check our constants are what Ratio::new etc. would make. |
1804 | assert_eq!(_0, Zero::zero()); |
1805 | assert_eq!(_1, One::one()); |
1806 | assert_eq!(_2, Ratio::from_integer(2)); |
1807 | assert_eq!(_1_2, Ratio::new(1, 2)); |
1808 | assert_eq!(_3_2, Ratio::new(3, 2)); |
1809 | assert_eq!(_NEG1_2, Ratio::new(-1, 2)); |
1810 | assert_eq!(_2, From::from(2)); |
1811 | } |
1812 | |
1813 | #[test ] |
1814 | fn test_new_reduce() { |
1815 | assert_eq!(Ratio::new(2, 2), One::one()); |
1816 | assert_eq!(Ratio::new(0, i32::MIN), Zero::zero()); |
1817 | assert_eq!(Ratio::new(i32::MIN, i32::MIN), One::one()); |
1818 | } |
1819 | #[test ] |
1820 | #[should_panic ] |
1821 | fn test_new_zero() { |
1822 | let _a = Ratio::new(1, 0); |
1823 | } |
1824 | |
1825 | #[test ] |
1826 | fn test_approximate_float() { |
1827 | assert_eq!(Ratio::from_f32(0.5f32), Some(Ratio::new(1i64, 2))); |
1828 | assert_eq!(Ratio::from_f64(0.5f64), Some(Ratio::new(1i32, 2))); |
1829 | assert_eq!(Ratio::from_f32(5f32), Some(Ratio::new(5i64, 1))); |
1830 | assert_eq!(Ratio::from_f64(5f64), Some(Ratio::new(5i32, 1))); |
1831 | assert_eq!(Ratio::from_f32(29.97f32), Some(Ratio::new(2997i64, 100))); |
1832 | assert_eq!(Ratio::from_f32(-29.97f32), Some(Ratio::new(-2997i64, 100))); |
1833 | |
1834 | assert_eq!(Ratio::<i8>::from_f32(63.5f32), Some(Ratio::new(127i8, 2))); |
1835 | assert_eq!(Ratio::<i8>::from_f32(126.5f32), Some(Ratio::new(126i8, 1))); |
1836 | assert_eq!(Ratio::<i8>::from_f32(127.0f32), Some(Ratio::new(127i8, 1))); |
1837 | assert_eq!(Ratio::<i8>::from_f32(127.5f32), None); |
1838 | assert_eq!(Ratio::<i8>::from_f32(-63.5f32), Some(Ratio::new(-127i8, 2))); |
1839 | assert_eq!( |
1840 | Ratio::<i8>::from_f32(-126.5f32), |
1841 | Some(Ratio::new(-126i8, 1)) |
1842 | ); |
1843 | assert_eq!( |
1844 | Ratio::<i8>::from_f32(-127.0f32), |
1845 | Some(Ratio::new(-127i8, 1)) |
1846 | ); |
1847 | assert_eq!(Ratio::<i8>::from_f32(-127.5f32), None); |
1848 | |
1849 | assert_eq!(Ratio::<u8>::from_f32(-127f32), None); |
1850 | assert_eq!(Ratio::<u8>::from_f32(127f32), Some(Ratio::new(127u8, 1))); |
1851 | assert_eq!(Ratio::<u8>::from_f32(127.5f32), Some(Ratio::new(255u8, 2))); |
1852 | assert_eq!(Ratio::<u8>::from_f32(256f32), None); |
1853 | |
1854 | assert_eq!(Ratio::<i64>::from_f64(-10e200), None); |
1855 | assert_eq!(Ratio::<i64>::from_f64(10e200), None); |
1856 | assert_eq!(Ratio::<i64>::from_f64(f64::INFINITY), None); |
1857 | assert_eq!(Ratio::<i64>::from_f64(f64::NEG_INFINITY), None); |
1858 | assert_eq!(Ratio::<i64>::from_f64(f64::NAN), None); |
1859 | assert_eq!( |
1860 | Ratio::<i64>::from_f64(f64::EPSILON), |
1861 | Some(Ratio::new(1, 4503599627370496)) |
1862 | ); |
1863 | assert_eq!(Ratio::<i64>::from_f64(0.0), Some(Ratio::new(0, 1))); |
1864 | assert_eq!(Ratio::<i64>::from_f64(-0.0), Some(Ratio::new(0, 1))); |
1865 | } |
1866 | |
1867 | #[test ] |
1868 | #[allow (clippy::eq_op)] |
1869 | fn test_cmp() { |
1870 | assert!(_0 == _0 && _1 == _1); |
1871 | assert!(_0 != _1 && _1 != _0); |
1872 | assert!(_0 < _1 && !(_1 < _0)); |
1873 | assert!(_1 > _0 && !(_0 > _1)); |
1874 | |
1875 | assert!(_0 <= _0 && _1 <= _1); |
1876 | assert!(_0 <= _1 && !(_1 <= _0)); |
1877 | |
1878 | assert!(_0 >= _0 && _1 >= _1); |
1879 | assert!(_1 >= _0 && !(_0 >= _1)); |
1880 | |
1881 | let _0_2: Rational64 = Ratio::new_raw(0, 2); |
1882 | assert_eq!(_0, _0_2); |
1883 | } |
1884 | |
1885 | #[test ] |
1886 | fn test_cmp_overflow() { |
1887 | use core::cmp::Ordering; |
1888 | |
1889 | // issue #7 example: |
1890 | let big = Ratio::new(128u8, 1); |
1891 | let small = big.recip(); |
1892 | assert!(big > small); |
1893 | |
1894 | // try a few that are closer together |
1895 | // (some matching numer, some matching denom, some neither) |
1896 | let ratios = [ |
1897 | Ratio::new(125_i8, 127_i8), |
1898 | Ratio::new(63_i8, 64_i8), |
1899 | Ratio::new(124_i8, 125_i8), |
1900 | Ratio::new(125_i8, 126_i8), |
1901 | Ratio::new(126_i8, 127_i8), |
1902 | Ratio::new(127_i8, 126_i8), |
1903 | ]; |
1904 | |
1905 | fn check_cmp(a: Ratio<i8>, b: Ratio<i8>, ord: Ordering) { |
1906 | #[cfg (feature = "std" )] |
1907 | println!("comparing {} and {}" , a, b); |
1908 | assert_eq!(a.cmp(&b), ord); |
1909 | assert_eq!(b.cmp(&a), ord.reverse()); |
1910 | } |
1911 | |
1912 | for (i, &a) in ratios.iter().enumerate() { |
1913 | check_cmp(a, a, Ordering::Equal); |
1914 | check_cmp(-a, a, Ordering::Less); |
1915 | for &b in &ratios[i + 1..] { |
1916 | check_cmp(a, b, Ordering::Less); |
1917 | check_cmp(-a, -b, Ordering::Greater); |
1918 | check_cmp(a.recip(), b.recip(), Ordering::Greater); |
1919 | check_cmp(-a.recip(), -b.recip(), Ordering::Less); |
1920 | } |
1921 | } |
1922 | } |
1923 | |
1924 | #[test ] |
1925 | fn test_to_integer() { |
1926 | assert_eq!(_0.to_integer(), 0); |
1927 | assert_eq!(_1.to_integer(), 1); |
1928 | assert_eq!(_2.to_integer(), 2); |
1929 | assert_eq!(_1_2.to_integer(), 0); |
1930 | assert_eq!(_3_2.to_integer(), 1); |
1931 | assert_eq!(_NEG1_2.to_integer(), 0); |
1932 | } |
1933 | |
1934 | #[test ] |
1935 | fn test_numer() { |
1936 | assert_eq!(_0.numer(), &0); |
1937 | assert_eq!(_1.numer(), &1); |
1938 | assert_eq!(_2.numer(), &2); |
1939 | assert_eq!(_1_2.numer(), &1); |
1940 | assert_eq!(_3_2.numer(), &3); |
1941 | assert_eq!(_NEG1_2.numer(), &(-1)); |
1942 | } |
1943 | #[test ] |
1944 | fn test_denom() { |
1945 | assert_eq!(_0.denom(), &1); |
1946 | assert_eq!(_1.denom(), &1); |
1947 | assert_eq!(_2.denom(), &1); |
1948 | assert_eq!(_1_2.denom(), &2); |
1949 | assert_eq!(_3_2.denom(), &2); |
1950 | assert_eq!(_NEG1_2.denom(), &2); |
1951 | } |
1952 | |
1953 | #[test ] |
1954 | fn test_is_integer() { |
1955 | assert!(_0.is_integer()); |
1956 | assert!(_1.is_integer()); |
1957 | assert!(_2.is_integer()); |
1958 | assert!(!_1_2.is_integer()); |
1959 | assert!(!_3_2.is_integer()); |
1960 | assert!(!_NEG1_2.is_integer()); |
1961 | } |
1962 | |
1963 | #[cfg (not(feature = "std" ))] |
1964 | use core::fmt::{self, Write}; |
1965 | #[cfg (not(feature = "std" ))] |
1966 | #[derive (Debug)] |
1967 | struct NoStdTester { |
1968 | cursor: usize, |
1969 | buf: [u8; NoStdTester::BUF_SIZE], |
1970 | } |
1971 | |
1972 | #[cfg (not(feature = "std" ))] |
1973 | impl NoStdTester { |
1974 | fn new() -> NoStdTester { |
1975 | NoStdTester { |
1976 | buf: [0; Self::BUF_SIZE], |
1977 | cursor: 0, |
1978 | } |
1979 | } |
1980 | |
1981 | fn clear(&mut self) { |
1982 | self.buf = [0; Self::BUF_SIZE]; |
1983 | self.cursor = 0; |
1984 | } |
1985 | |
1986 | const WRITE_ERR: &'static str = "Formatted output too long" ; |
1987 | const BUF_SIZE: usize = 32; |
1988 | } |
1989 | |
1990 | #[cfg (not(feature = "std" ))] |
1991 | impl Write for NoStdTester { |
1992 | fn write_str(&mut self, s: &str) -> fmt::Result { |
1993 | for byte in s.bytes() { |
1994 | self.buf[self.cursor] = byte; |
1995 | self.cursor += 1; |
1996 | if self.cursor >= self.buf.len() { |
1997 | return Err(fmt::Error {}); |
1998 | } |
1999 | } |
2000 | Ok(()) |
2001 | } |
2002 | } |
2003 | |
2004 | #[cfg (not(feature = "std" ))] |
2005 | impl PartialEq<str> for NoStdTester { |
2006 | fn eq(&self, other: &str) -> bool { |
2007 | let other = other.as_bytes(); |
2008 | for index in 0..self.cursor { |
2009 | if self.buf.get(index) != other.get(index) { |
2010 | return false; |
2011 | } |
2012 | } |
2013 | true |
2014 | } |
2015 | } |
2016 | |
2017 | macro_rules! assert_fmt_eq { |
2018 | ($fmt_args:expr, $string:expr) => { |
2019 | #[cfg(not(feature = "std" ))] |
2020 | { |
2021 | let mut tester = NoStdTester::new(); |
2022 | write!(tester, "{}" , $fmt_args).expect(NoStdTester::WRITE_ERR); |
2023 | assert_eq!(tester, *$string); |
2024 | tester.clear(); |
2025 | } |
2026 | #[cfg(feature = "std" )] |
2027 | { |
2028 | assert_eq!(std::fmt::format($fmt_args), $string); |
2029 | } |
2030 | }; |
2031 | } |
2032 | |
2033 | #[test ] |
2034 | fn test_show() { |
2035 | // Test: |
2036 | // :b :o :x, :X, :? |
2037 | // alternate or not (#) |
2038 | // positive and negative |
2039 | // padding |
2040 | // does not test precision (i.e. truncation) |
2041 | assert_fmt_eq!(format_args!("{}" , _2), "2" ); |
2042 | assert_fmt_eq!(format_args!("{:+}" , _2), "+2" ); |
2043 | assert_fmt_eq!(format_args!("{:-}" , _2), "2" ); |
2044 | assert_fmt_eq!(format_args!("{}" , _1_2), "1/2" ); |
2045 | assert_fmt_eq!(format_args!("{}" , -_1_2), "-1/2" ); // test negatives |
2046 | assert_fmt_eq!(format_args!("{}" , _0), "0" ); |
2047 | assert_fmt_eq!(format_args!("{}" , -_2), "-2" ); |
2048 | assert_fmt_eq!(format_args!("{:+}" , -_2), "-2" ); |
2049 | assert_fmt_eq!(format_args!("{:b}" , _2), "10" ); |
2050 | assert_fmt_eq!(format_args!("{:#b}" , _2), "0b10" ); |
2051 | assert_fmt_eq!(format_args!("{:b}" , _1_2), "1/10" ); |
2052 | assert_fmt_eq!(format_args!("{:+b}" , _1_2), "+1/10" ); |
2053 | assert_fmt_eq!(format_args!("{:-b}" , _1_2), "1/10" ); |
2054 | assert_fmt_eq!(format_args!("{:b}" , _0), "0" ); |
2055 | assert_fmt_eq!(format_args!("{:#b}" , _1_2), "0b1/0b10" ); |
2056 | // no std does not support padding |
2057 | #[cfg (feature = "std" )] |
2058 | assert_eq!(&format!("{:010b}" , _1_2), "0000001/10" ); |
2059 | #[cfg (feature = "std" )] |
2060 | assert_eq!(&format!("{:#010b}" , _1_2), "0b001/0b10" ); |
2061 | let half_i8: Ratio<i8> = Ratio::new(1_i8, 2_i8); |
2062 | assert_fmt_eq!(format_args!("{:b}" , -half_i8), "11111111/10" ); |
2063 | assert_fmt_eq!(format_args!("{:#b}" , -half_i8), "0b11111111/0b10" ); |
2064 | #[cfg (feature = "std" )] |
2065 | assert_eq!(&format!("{:05}" , Ratio::new(-1_i8, 1_i8)), "-0001" ); |
2066 | |
2067 | assert_fmt_eq!(format_args!("{:o}" , _8), "10" ); |
2068 | assert_fmt_eq!(format_args!("{:o}" , _1_8), "1/10" ); |
2069 | assert_fmt_eq!(format_args!("{:o}" , _0), "0" ); |
2070 | assert_fmt_eq!(format_args!("{:#o}" , _1_8), "0o1/0o10" ); |
2071 | #[cfg (feature = "std" )] |
2072 | assert_eq!(&format!("{:010o}" , _1_8), "0000001/10" ); |
2073 | #[cfg (feature = "std" )] |
2074 | assert_eq!(&format!("{:#010o}" , _1_8), "0o001/0o10" ); |
2075 | assert_fmt_eq!(format_args!("{:o}" , -half_i8), "377/2" ); |
2076 | assert_fmt_eq!(format_args!("{:#o}" , -half_i8), "0o377/0o2" ); |
2077 | |
2078 | assert_fmt_eq!(format_args!("{:x}" , _16), "10" ); |
2079 | assert_fmt_eq!(format_args!("{:x}" , _15), "f" ); |
2080 | assert_fmt_eq!(format_args!("{:x}" , _1_16), "1/10" ); |
2081 | assert_fmt_eq!(format_args!("{:x}" , _1_15), "1/f" ); |
2082 | assert_fmt_eq!(format_args!("{:x}" , _0), "0" ); |
2083 | assert_fmt_eq!(format_args!("{:#x}" , _1_16), "0x1/0x10" ); |
2084 | #[cfg (feature = "std" )] |
2085 | assert_eq!(&format!("{:010x}" , _1_16), "0000001/10" ); |
2086 | #[cfg (feature = "std" )] |
2087 | assert_eq!(&format!("{:#010x}" , _1_16), "0x001/0x10" ); |
2088 | assert_fmt_eq!(format_args!("{:x}" , -half_i8), "ff/2" ); |
2089 | assert_fmt_eq!(format_args!("{:#x}" , -half_i8), "0xff/0x2" ); |
2090 | |
2091 | assert_fmt_eq!(format_args!("{:X}" , _16), "10" ); |
2092 | assert_fmt_eq!(format_args!("{:X}" , _15), "F" ); |
2093 | assert_fmt_eq!(format_args!("{:X}" , _1_16), "1/10" ); |
2094 | assert_fmt_eq!(format_args!("{:X}" , _1_15), "1/F" ); |
2095 | assert_fmt_eq!(format_args!("{:X}" , _0), "0" ); |
2096 | assert_fmt_eq!(format_args!("{:#X}" , _1_16), "0x1/0x10" ); |
2097 | #[cfg (feature = "std" )] |
2098 | assert_eq!(format!("{:010X}" , _1_16), "0000001/10" ); |
2099 | #[cfg (feature = "std" )] |
2100 | assert_eq!(format!("{:#010X}" , _1_16), "0x001/0x10" ); |
2101 | assert_fmt_eq!(format_args!("{:X}" , -half_i8), "FF/2" ); |
2102 | assert_fmt_eq!(format_args!("{:#X}" , -half_i8), "0xFF/0x2" ); |
2103 | |
2104 | #[cfg (has_int_exp_fmt)] |
2105 | { |
2106 | assert_fmt_eq!(format_args!("{:e}" , -_2), "-2e0" ); |
2107 | assert_fmt_eq!(format_args!("{:#e}" , -_2), "-2e0" ); |
2108 | assert_fmt_eq!(format_args!("{:+e}" , -_2), "-2e0" ); |
2109 | assert_fmt_eq!(format_args!("{:e}" , _BILLION), "1e9" ); |
2110 | assert_fmt_eq!(format_args!("{:+e}" , _BILLION), "+1e9" ); |
2111 | assert_fmt_eq!(format_args!("{:e}" , _BILLION.recip()), "1e0/1e9" ); |
2112 | assert_fmt_eq!(format_args!("{:+e}" , _BILLION.recip()), "+1e0/1e9" ); |
2113 | |
2114 | assert_fmt_eq!(format_args!("{:E}" , -_2), "-2E0" ); |
2115 | assert_fmt_eq!(format_args!("{:#E}" , -_2), "-2E0" ); |
2116 | assert_fmt_eq!(format_args!("{:+E}" , -_2), "-2E0" ); |
2117 | assert_fmt_eq!(format_args!("{:E}" , _BILLION), "1E9" ); |
2118 | assert_fmt_eq!(format_args!("{:+E}" , _BILLION), "+1E9" ); |
2119 | assert_fmt_eq!(format_args!("{:E}" , _BILLION.recip()), "1E0/1E9" ); |
2120 | assert_fmt_eq!(format_args!("{:+E}" , _BILLION.recip()), "+1E0/1E9" ); |
2121 | } |
2122 | } |
2123 | |
2124 | mod arith { |
2125 | use super::super::{Ratio, Rational64}; |
2126 | use super::{to_big, _0, _1, _1_2, _2, _3_2, _5_2, _MAX, _MAX_M1, _MIN, _MIN_P1, _NEG1_2}; |
2127 | use core::fmt::Debug; |
2128 | use num_integer::Integer; |
2129 | use num_traits::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, NumAssign}; |
2130 | |
2131 | #[test ] |
2132 | fn test_add() { |
2133 | fn test(a: Rational64, b: Rational64, c: Rational64) { |
2134 | assert_eq!(a + b, c); |
2135 | assert_eq!( |
2136 | { |
2137 | let mut x = a; |
2138 | x += b; |
2139 | x |
2140 | }, |
2141 | c |
2142 | ); |
2143 | assert_eq!(to_big(a) + to_big(b), to_big(c)); |
2144 | assert_eq!(a.checked_add(&b), Some(c)); |
2145 | assert_eq!(to_big(a).checked_add(&to_big(b)), Some(to_big(c))); |
2146 | } |
2147 | fn test_assign(a: Rational64, b: i64, c: Rational64) { |
2148 | assert_eq!(a + b, c); |
2149 | assert_eq!( |
2150 | { |
2151 | let mut x = a; |
2152 | x += b; |
2153 | x |
2154 | }, |
2155 | c |
2156 | ); |
2157 | } |
2158 | |
2159 | test (_1, _1_2, _3_2); |
2160 | test (_1, _1, _2); |
2161 | test (_1_2, _3_2, _2); |
2162 | test (_1_2, _NEG1_2, _0); |
2163 | test_assign(_1_2, 1, _3_2); |
2164 | } |
2165 | |
2166 | #[test ] |
2167 | fn test_add_overflow() { |
2168 | // compares Ratio(1, T::max_value()) + Ratio(1, T::max_value()) |
2169 | // to Ratio(1+1, T::max_value()) for each integer type. |
2170 | // Previously, this calculation would overflow. |
2171 | fn test_add_typed_overflow<T>() |
2172 | where |
2173 | T: Integer + Bounded + Clone + Debug + NumAssign, |
2174 | { |
2175 | let _1_max = Ratio::new(T::one(), T::max_value()); |
2176 | let _2_max = Ratio::new(T::one() + T::one(), T::max_value()); |
2177 | assert_eq!(_1_max.clone() + _1_max.clone(), _2_max); |
2178 | assert_eq!( |
2179 | { |
2180 | let mut tmp = _1_max.clone(); |
2181 | tmp += _1_max; |
2182 | tmp |
2183 | }, |
2184 | _2_max |
2185 | ); |
2186 | } |
2187 | test_add_typed_overflow::<u8>(); |
2188 | test_add_typed_overflow::<u16>(); |
2189 | test_add_typed_overflow::<u32>(); |
2190 | test_add_typed_overflow::<u64>(); |
2191 | test_add_typed_overflow::<usize>(); |
2192 | test_add_typed_overflow::<u128>(); |
2193 | |
2194 | test_add_typed_overflow::<i8>(); |
2195 | test_add_typed_overflow::<i16>(); |
2196 | test_add_typed_overflow::<i32>(); |
2197 | test_add_typed_overflow::<i64>(); |
2198 | test_add_typed_overflow::<isize>(); |
2199 | test_add_typed_overflow::<i128>(); |
2200 | } |
2201 | |
2202 | #[test ] |
2203 | fn test_sub() { |
2204 | fn test(a: Rational64, b: Rational64, c: Rational64) { |
2205 | assert_eq!(a - b, c); |
2206 | assert_eq!( |
2207 | { |
2208 | let mut x = a; |
2209 | x -= b; |
2210 | x |
2211 | }, |
2212 | c |
2213 | ); |
2214 | assert_eq!(to_big(a) - to_big(b), to_big(c)); |
2215 | assert_eq!(a.checked_sub(&b), Some(c)); |
2216 | assert_eq!(to_big(a).checked_sub(&to_big(b)), Some(to_big(c))); |
2217 | } |
2218 | fn test_assign(a: Rational64, b: i64, c: Rational64) { |
2219 | assert_eq!(a - b, c); |
2220 | assert_eq!( |
2221 | { |
2222 | let mut x = a; |
2223 | x -= b; |
2224 | x |
2225 | }, |
2226 | c |
2227 | ); |
2228 | } |
2229 | |
2230 | test (_1, _1_2, _1_2); |
2231 | test (_3_2, _1_2, _1); |
2232 | test (_1, _NEG1_2, _3_2); |
2233 | test_assign(_1_2, 1, _NEG1_2); |
2234 | } |
2235 | |
2236 | #[test ] |
2237 | fn test_sub_overflow() { |
2238 | // compares Ratio(1, T::max_value()) - Ratio(1, T::max_value()) to T::zero() |
2239 | // for each integer type. Previously, this calculation would overflow. |
2240 | fn test_sub_typed_overflow<T>() |
2241 | where |
2242 | T: Integer + Bounded + Clone + Debug + NumAssign, |
2243 | { |
2244 | let _1_max: Ratio<T> = Ratio::new(T::one(), T::max_value()); |
2245 | assert!(T::is_zero(&(_1_max.clone() - _1_max.clone()).numer)); |
2246 | { |
2247 | let mut tmp: Ratio<T> = _1_max.clone(); |
2248 | tmp -= _1_max; |
2249 | assert!(T::is_zero(&tmp.numer)); |
2250 | } |
2251 | } |
2252 | test_sub_typed_overflow::<u8>(); |
2253 | test_sub_typed_overflow::<u16>(); |
2254 | test_sub_typed_overflow::<u32>(); |
2255 | test_sub_typed_overflow::<u64>(); |
2256 | test_sub_typed_overflow::<usize>(); |
2257 | test_sub_typed_overflow::<u128>(); |
2258 | |
2259 | test_sub_typed_overflow::<i8>(); |
2260 | test_sub_typed_overflow::<i16>(); |
2261 | test_sub_typed_overflow::<i32>(); |
2262 | test_sub_typed_overflow::<i64>(); |
2263 | test_sub_typed_overflow::<isize>(); |
2264 | test_sub_typed_overflow::<i128>(); |
2265 | } |
2266 | |
2267 | #[test ] |
2268 | fn test_mul() { |
2269 | fn test(a: Rational64, b: Rational64, c: Rational64) { |
2270 | assert_eq!(a * b, c); |
2271 | assert_eq!( |
2272 | { |
2273 | let mut x = a; |
2274 | x *= b; |
2275 | x |
2276 | }, |
2277 | c |
2278 | ); |
2279 | assert_eq!(to_big(a) * to_big(b), to_big(c)); |
2280 | assert_eq!(a.checked_mul(&b), Some(c)); |
2281 | assert_eq!(to_big(a).checked_mul(&to_big(b)), Some(to_big(c))); |
2282 | } |
2283 | fn test_assign(a: Rational64, b: i64, c: Rational64) { |
2284 | assert_eq!(a * b, c); |
2285 | assert_eq!( |
2286 | { |
2287 | let mut x = a; |
2288 | x *= b; |
2289 | x |
2290 | }, |
2291 | c |
2292 | ); |
2293 | } |
2294 | |
2295 | test (_1, _1_2, _1_2); |
2296 | test (_1_2, _3_2, Ratio::new(3, 4)); |
2297 | test (_1_2, _NEG1_2, Ratio::new(-1, 4)); |
2298 | test_assign(_1_2, 2, _1); |
2299 | } |
2300 | |
2301 | #[test ] |
2302 | fn test_mul_overflow() { |
2303 | fn test_mul_typed_overflow<T>() |
2304 | where |
2305 | T: Integer + Bounded + Clone + Debug + NumAssign + CheckedMul, |
2306 | { |
2307 | let two = T::one() + T::one(); |
2308 | let _3 = T::one() + T::one() + T::one(); |
2309 | |
2310 | // 1/big * 2/3 = 1/(max/4*3), where big is max/2 |
2311 | // make big = max/2, but also divisible by 2 |
2312 | let big = T::max_value() / two.clone() / two.clone() * two.clone(); |
2313 | let _1_big: Ratio<T> = Ratio::new(T::one(), big.clone()); |
2314 | let _2_3: Ratio<T> = Ratio::new(two.clone(), _3.clone()); |
2315 | assert_eq!(None, big.clone().checked_mul(&_3.clone())); |
2316 | let expected = Ratio::new(T::one(), big / two.clone() * _3.clone()); |
2317 | assert_eq!(expected.clone(), _1_big.clone() * _2_3.clone()); |
2318 | assert_eq!( |
2319 | Some(expected.clone()), |
2320 | _1_big.clone().checked_mul(&_2_3.clone()) |
2321 | ); |
2322 | assert_eq!(expected, { |
2323 | let mut tmp = _1_big; |
2324 | tmp *= _2_3; |
2325 | tmp |
2326 | }); |
2327 | |
2328 | // big/3 * 3 = big/1 |
2329 | // make big = max/2, but make it indivisible by 3 |
2330 | let big = T::max_value() / two / _3.clone() * _3.clone() + T::one(); |
2331 | assert_eq!(None, big.clone().checked_mul(&_3.clone())); |
2332 | let big_3 = Ratio::new(big.clone(), _3.clone()); |
2333 | let expected = Ratio::new(big, T::one()); |
2334 | assert_eq!(expected, big_3.clone() * _3.clone()); |
2335 | assert_eq!(expected, { |
2336 | let mut tmp = big_3; |
2337 | tmp *= _3; |
2338 | tmp |
2339 | }); |
2340 | } |
2341 | test_mul_typed_overflow::<u16>(); |
2342 | test_mul_typed_overflow::<u8>(); |
2343 | test_mul_typed_overflow::<u32>(); |
2344 | test_mul_typed_overflow::<u64>(); |
2345 | test_mul_typed_overflow::<usize>(); |
2346 | test_mul_typed_overflow::<u128>(); |
2347 | |
2348 | test_mul_typed_overflow::<i8>(); |
2349 | test_mul_typed_overflow::<i16>(); |
2350 | test_mul_typed_overflow::<i32>(); |
2351 | test_mul_typed_overflow::<i64>(); |
2352 | test_mul_typed_overflow::<isize>(); |
2353 | test_mul_typed_overflow::<i128>(); |
2354 | } |
2355 | |
2356 | #[test ] |
2357 | fn test_div() { |
2358 | fn test(a: Rational64, b: Rational64, c: Rational64) { |
2359 | assert_eq!(a / b, c); |
2360 | assert_eq!( |
2361 | { |
2362 | let mut x = a; |
2363 | x /= b; |
2364 | x |
2365 | }, |
2366 | c |
2367 | ); |
2368 | assert_eq!(to_big(a) / to_big(b), to_big(c)); |
2369 | assert_eq!(a.checked_div(&b), Some(c)); |
2370 | assert_eq!(to_big(a).checked_div(&to_big(b)), Some(to_big(c))); |
2371 | } |
2372 | fn test_assign(a: Rational64, b: i64, c: Rational64) { |
2373 | assert_eq!(a / b, c); |
2374 | assert_eq!( |
2375 | { |
2376 | let mut x = a; |
2377 | x /= b; |
2378 | x |
2379 | }, |
2380 | c |
2381 | ); |
2382 | } |
2383 | |
2384 | test (_1, _1_2, _2); |
2385 | test (_3_2, _1_2, _1 + _2); |
2386 | test (_1, _NEG1_2, _NEG1_2 + _NEG1_2 + _NEG1_2 + _NEG1_2); |
2387 | test_assign(_1, 2, _1_2); |
2388 | } |
2389 | |
2390 | #[test ] |
2391 | fn test_div_overflow() { |
2392 | fn test_div_typed_overflow<T>() |
2393 | where |
2394 | T: Integer + Bounded + Clone + Debug + NumAssign + CheckedMul, |
2395 | { |
2396 | let two = T::one() + T::one(); |
2397 | let _3 = T::one() + T::one() + T::one(); |
2398 | |
2399 | // 1/big / 3/2 = 1/(max/4*3), where big is max/2 |
2400 | // big ~ max/2, and big is divisible by 2 |
2401 | let big = T::max_value() / two.clone() / two.clone() * two.clone(); |
2402 | assert_eq!(None, big.clone().checked_mul(&_3.clone())); |
2403 | let _1_big: Ratio<T> = Ratio::new(T::one(), big.clone()); |
2404 | let _3_two: Ratio<T> = Ratio::new(_3.clone(), two.clone()); |
2405 | let expected = Ratio::new(T::one(), big / two.clone() * _3.clone()); |
2406 | assert_eq!(expected.clone(), _1_big.clone() / _3_two.clone()); |
2407 | assert_eq!( |
2408 | Some(expected.clone()), |
2409 | _1_big.clone().checked_div(&_3_two.clone()) |
2410 | ); |
2411 | assert_eq!(expected, { |
2412 | let mut tmp = _1_big; |
2413 | tmp /= _3_two; |
2414 | tmp |
2415 | }); |
2416 | |
2417 | // 3/big / 3 = 1/big where big is max/2 |
2418 | // big ~ max/2, and big is not divisible by 3 |
2419 | let big = T::max_value() / two / _3.clone() * _3.clone() + T::one(); |
2420 | assert_eq!(None, big.clone().checked_mul(&_3.clone())); |
2421 | let _3_big = Ratio::new(_3.clone(), big.clone()); |
2422 | let expected = Ratio::new(T::one(), big); |
2423 | assert_eq!(expected, _3_big.clone() / _3.clone()); |
2424 | assert_eq!(expected, { |
2425 | let mut tmp = _3_big; |
2426 | tmp /= _3; |
2427 | tmp |
2428 | }); |
2429 | } |
2430 | test_div_typed_overflow::<u8>(); |
2431 | test_div_typed_overflow::<u16>(); |
2432 | test_div_typed_overflow::<u32>(); |
2433 | test_div_typed_overflow::<u64>(); |
2434 | test_div_typed_overflow::<usize>(); |
2435 | test_div_typed_overflow::<u128>(); |
2436 | |
2437 | test_div_typed_overflow::<i8>(); |
2438 | test_div_typed_overflow::<i16>(); |
2439 | test_div_typed_overflow::<i32>(); |
2440 | test_div_typed_overflow::<i64>(); |
2441 | test_div_typed_overflow::<isize>(); |
2442 | test_div_typed_overflow::<i128>(); |
2443 | } |
2444 | |
2445 | #[test ] |
2446 | fn test_rem() { |
2447 | fn test(a: Rational64, b: Rational64, c: Rational64) { |
2448 | assert_eq!(a % b, c); |
2449 | assert_eq!( |
2450 | { |
2451 | let mut x = a; |
2452 | x %= b; |
2453 | x |
2454 | }, |
2455 | c |
2456 | ); |
2457 | assert_eq!(to_big(a) % to_big(b), to_big(c)) |
2458 | } |
2459 | fn test_assign(a: Rational64, b: i64, c: Rational64) { |
2460 | assert_eq!(a % b, c); |
2461 | assert_eq!( |
2462 | { |
2463 | let mut x = a; |
2464 | x %= b; |
2465 | x |
2466 | }, |
2467 | c |
2468 | ); |
2469 | } |
2470 | |
2471 | test (_3_2, _1, _1_2); |
2472 | test (_3_2, _1_2, _0); |
2473 | test (_5_2, _3_2, _1); |
2474 | test (_2, _NEG1_2, _0); |
2475 | test (_1_2, _2, _1_2); |
2476 | test_assign(_3_2, 1, _1_2); |
2477 | } |
2478 | |
2479 | #[test ] |
2480 | fn test_rem_overflow() { |
2481 | // tests that Ratio(1,2) % Ratio(1, T::max_value()) equals 0 |
2482 | // for each integer type. Previously, this calculation would overflow. |
2483 | fn test_rem_typed_overflow<T>() |
2484 | where |
2485 | T: Integer + Bounded + Clone + Debug + NumAssign, |
2486 | { |
2487 | let two = T::one() + T::one(); |
2488 | // value near to maximum, but divisible by two |
2489 | let max_div2 = T::max_value() / two.clone() * two.clone(); |
2490 | let _1_max: Ratio<T> = Ratio::new(T::one(), max_div2); |
2491 | let _1_two: Ratio<T> = Ratio::new(T::one(), two); |
2492 | assert!(T::is_zero(&(_1_two.clone() % _1_max.clone()).numer)); |
2493 | { |
2494 | let mut tmp: Ratio<T> = _1_two; |
2495 | tmp %= _1_max; |
2496 | assert!(T::is_zero(&tmp.numer)); |
2497 | } |
2498 | } |
2499 | test_rem_typed_overflow::<u8>(); |
2500 | test_rem_typed_overflow::<u16>(); |
2501 | test_rem_typed_overflow::<u32>(); |
2502 | test_rem_typed_overflow::<u64>(); |
2503 | test_rem_typed_overflow::<usize>(); |
2504 | test_rem_typed_overflow::<u128>(); |
2505 | |
2506 | test_rem_typed_overflow::<i8>(); |
2507 | test_rem_typed_overflow::<i16>(); |
2508 | test_rem_typed_overflow::<i32>(); |
2509 | test_rem_typed_overflow::<i64>(); |
2510 | test_rem_typed_overflow::<isize>(); |
2511 | test_rem_typed_overflow::<i128>(); |
2512 | } |
2513 | |
2514 | #[test ] |
2515 | fn test_neg() { |
2516 | fn test(a: Rational64, b: Rational64) { |
2517 | assert_eq!(-a, b); |
2518 | assert_eq!(-to_big(a), to_big(b)) |
2519 | } |
2520 | |
2521 | test (_0, _0); |
2522 | test (_1_2, _NEG1_2); |
2523 | test (-_1, _1); |
2524 | } |
2525 | #[test ] |
2526 | #[allow (clippy::eq_op)] |
2527 | fn test_zero() { |
2528 | assert_eq!(_0 + _0, _0); |
2529 | assert_eq!(_0 * _0, _0); |
2530 | assert_eq!(_0 * _1, _0); |
2531 | assert_eq!(_0 / _NEG1_2, _0); |
2532 | assert_eq!(_0 - _0, _0); |
2533 | } |
2534 | #[test ] |
2535 | #[should_panic ] |
2536 | fn test_div_0() { |
2537 | let _a = _1 / _0; |
2538 | } |
2539 | |
2540 | #[test ] |
2541 | fn test_checked_failures() { |
2542 | let big = Ratio::new(128u8, 1); |
2543 | let small = Ratio::new(1, 128u8); |
2544 | assert_eq!(big.checked_add(&big), None); |
2545 | assert_eq!(small.checked_sub(&big), None); |
2546 | assert_eq!(big.checked_mul(&big), None); |
2547 | assert_eq!(small.checked_div(&big), None); |
2548 | assert_eq!(_1.checked_div(&_0), None); |
2549 | } |
2550 | |
2551 | #[test ] |
2552 | fn test_checked_zeros() { |
2553 | assert_eq!(_0.checked_add(&_0), Some(_0)); |
2554 | assert_eq!(_0.checked_sub(&_0), Some(_0)); |
2555 | assert_eq!(_0.checked_mul(&_0), Some(_0)); |
2556 | assert_eq!(_0.checked_div(&_0), None); |
2557 | } |
2558 | |
2559 | #[test ] |
2560 | fn test_checked_min() { |
2561 | assert_eq!(_MIN.checked_add(&_MIN), None); |
2562 | assert_eq!(_MIN.checked_sub(&_MIN), Some(_0)); |
2563 | assert_eq!(_MIN.checked_mul(&_MIN), None); |
2564 | assert_eq!(_MIN.checked_div(&_MIN), Some(_1)); |
2565 | assert_eq!(_0.checked_add(&_MIN), Some(_MIN)); |
2566 | assert_eq!(_0.checked_sub(&_MIN), None); |
2567 | assert_eq!(_0.checked_mul(&_MIN), Some(_0)); |
2568 | assert_eq!(_0.checked_div(&_MIN), Some(_0)); |
2569 | assert_eq!(_1.checked_add(&_MIN), Some(_MIN_P1)); |
2570 | assert_eq!(_1.checked_sub(&_MIN), None); |
2571 | assert_eq!(_1.checked_mul(&_MIN), Some(_MIN)); |
2572 | assert_eq!(_1.checked_div(&_MIN), None); |
2573 | assert_eq!(_MIN.checked_add(&_0), Some(_MIN)); |
2574 | assert_eq!(_MIN.checked_sub(&_0), Some(_MIN)); |
2575 | assert_eq!(_MIN.checked_mul(&_0), Some(_0)); |
2576 | assert_eq!(_MIN.checked_div(&_0), None); |
2577 | assert_eq!(_MIN.checked_add(&_1), Some(_MIN_P1)); |
2578 | assert_eq!(_MIN.checked_sub(&_1), None); |
2579 | assert_eq!(_MIN.checked_mul(&_1), Some(_MIN)); |
2580 | assert_eq!(_MIN.checked_div(&_1), Some(_MIN)); |
2581 | } |
2582 | |
2583 | #[test ] |
2584 | fn test_checked_max() { |
2585 | assert_eq!(_MAX.checked_add(&_MAX), None); |
2586 | assert_eq!(_MAX.checked_sub(&_MAX), Some(_0)); |
2587 | assert_eq!(_MAX.checked_mul(&_MAX), None); |
2588 | assert_eq!(_MAX.checked_div(&_MAX), Some(_1)); |
2589 | assert_eq!(_0.checked_add(&_MAX), Some(_MAX)); |
2590 | assert_eq!(_0.checked_sub(&_MAX), Some(_MIN_P1)); |
2591 | assert_eq!(_0.checked_mul(&_MAX), Some(_0)); |
2592 | assert_eq!(_0.checked_div(&_MAX), Some(_0)); |
2593 | assert_eq!(_1.checked_add(&_MAX), None); |
2594 | assert_eq!(_1.checked_sub(&_MAX), Some(-_MAX_M1)); |
2595 | assert_eq!(_1.checked_mul(&_MAX), Some(_MAX)); |
2596 | assert_eq!(_1.checked_div(&_MAX), Some(_MAX.recip())); |
2597 | assert_eq!(_MAX.checked_add(&_0), Some(_MAX)); |
2598 | assert_eq!(_MAX.checked_sub(&_0), Some(_MAX)); |
2599 | assert_eq!(_MAX.checked_mul(&_0), Some(_0)); |
2600 | assert_eq!(_MAX.checked_div(&_0), None); |
2601 | assert_eq!(_MAX.checked_add(&_1), None); |
2602 | assert_eq!(_MAX.checked_sub(&_1), Some(_MAX_M1)); |
2603 | assert_eq!(_MAX.checked_mul(&_1), Some(_MAX)); |
2604 | assert_eq!(_MAX.checked_div(&_1), Some(_MAX)); |
2605 | } |
2606 | |
2607 | #[test ] |
2608 | fn test_checked_min_max() { |
2609 | assert_eq!(_MIN.checked_add(&_MAX), Some(-_1)); |
2610 | assert_eq!(_MIN.checked_sub(&_MAX), None); |
2611 | assert_eq!(_MIN.checked_mul(&_MAX), None); |
2612 | assert_eq!( |
2613 | _MIN.checked_div(&_MAX), |
2614 | Some(Ratio::new(_MIN.numer, _MAX.numer)) |
2615 | ); |
2616 | assert_eq!(_MAX.checked_add(&_MIN), Some(-_1)); |
2617 | assert_eq!(_MAX.checked_sub(&_MIN), None); |
2618 | assert_eq!(_MAX.checked_mul(&_MIN), None); |
2619 | assert_eq!(_MAX.checked_div(&_MIN), None); |
2620 | } |
2621 | } |
2622 | |
2623 | #[test ] |
2624 | fn test_round() { |
2625 | assert_eq!(_1_3.ceil(), _1); |
2626 | assert_eq!(_1_3.floor(), _0); |
2627 | assert_eq!(_1_3.round(), _0); |
2628 | assert_eq!(_1_3.trunc(), _0); |
2629 | |
2630 | assert_eq!(_NEG1_3.ceil(), _0); |
2631 | assert_eq!(_NEG1_3.floor(), -_1); |
2632 | assert_eq!(_NEG1_3.round(), _0); |
2633 | assert_eq!(_NEG1_3.trunc(), _0); |
2634 | |
2635 | assert_eq!(_2_3.ceil(), _1); |
2636 | assert_eq!(_2_3.floor(), _0); |
2637 | assert_eq!(_2_3.round(), _1); |
2638 | assert_eq!(_2_3.trunc(), _0); |
2639 | |
2640 | assert_eq!(_NEG2_3.ceil(), _0); |
2641 | assert_eq!(_NEG2_3.floor(), -_1); |
2642 | assert_eq!(_NEG2_3.round(), -_1); |
2643 | assert_eq!(_NEG2_3.trunc(), _0); |
2644 | |
2645 | assert_eq!(_1_2.ceil(), _1); |
2646 | assert_eq!(_1_2.floor(), _0); |
2647 | assert_eq!(_1_2.round(), _1); |
2648 | assert_eq!(_1_2.trunc(), _0); |
2649 | |
2650 | assert_eq!(_NEG1_2.ceil(), _0); |
2651 | assert_eq!(_NEG1_2.floor(), -_1); |
2652 | assert_eq!(_NEG1_2.round(), -_1); |
2653 | assert_eq!(_NEG1_2.trunc(), _0); |
2654 | |
2655 | assert_eq!(_1.ceil(), _1); |
2656 | assert_eq!(_1.floor(), _1); |
2657 | assert_eq!(_1.round(), _1); |
2658 | assert_eq!(_1.trunc(), _1); |
2659 | |
2660 | // Overflow checks |
2661 | |
2662 | let _neg1 = Ratio::from_integer(-1); |
2663 | let _large_rat1 = Ratio::new(i32::MAX, i32::MAX - 1); |
2664 | let _large_rat2 = Ratio::new(i32::MAX - 1, i32::MAX); |
2665 | let _large_rat3 = Ratio::new(i32::MIN + 2, i32::MIN + 1); |
2666 | let _large_rat4 = Ratio::new(i32::MIN + 1, i32::MIN + 2); |
2667 | let _large_rat5 = Ratio::new(i32::MIN + 2, i32::MAX); |
2668 | let _large_rat6 = Ratio::new(i32::MAX, i32::MIN + 2); |
2669 | let _large_rat7 = Ratio::new(1, i32::MIN + 1); |
2670 | let _large_rat8 = Ratio::new(1, i32::MAX); |
2671 | |
2672 | assert_eq!(_large_rat1.round(), One::one()); |
2673 | assert_eq!(_large_rat2.round(), One::one()); |
2674 | assert_eq!(_large_rat3.round(), One::one()); |
2675 | assert_eq!(_large_rat4.round(), One::one()); |
2676 | assert_eq!(_large_rat5.round(), _neg1); |
2677 | assert_eq!(_large_rat6.round(), _neg1); |
2678 | assert_eq!(_large_rat7.round(), Zero::zero()); |
2679 | assert_eq!(_large_rat8.round(), Zero::zero()); |
2680 | } |
2681 | |
2682 | #[test ] |
2683 | fn test_fract() { |
2684 | assert_eq!(_1.fract(), _0); |
2685 | assert_eq!(_NEG1_2.fract(), _NEG1_2); |
2686 | assert_eq!(_1_2.fract(), _1_2); |
2687 | assert_eq!(_3_2.fract(), _1_2); |
2688 | } |
2689 | |
2690 | #[test ] |
2691 | fn test_recip() { |
2692 | assert_eq!(_1 * _1.recip(), _1); |
2693 | assert_eq!(_2 * _2.recip(), _1); |
2694 | assert_eq!(_1_2 * _1_2.recip(), _1); |
2695 | assert_eq!(_3_2 * _3_2.recip(), _1); |
2696 | assert_eq!(_NEG1_2 * _NEG1_2.recip(), _1); |
2697 | |
2698 | assert_eq!(_3_2.recip(), _2_3); |
2699 | assert_eq!(_NEG1_2.recip(), _NEG2); |
2700 | assert_eq!(_NEG1_2.recip().denom(), &1); |
2701 | } |
2702 | |
2703 | #[test ] |
2704 | #[should_panic (expected = "division by zero" )] |
2705 | fn test_recip_fail() { |
2706 | let _a = Ratio::new(0, 1).recip(); |
2707 | } |
2708 | |
2709 | #[test ] |
2710 | fn test_pow() { |
2711 | fn test(r: Rational64, e: i32, expected: Rational64) { |
2712 | assert_eq!(r.pow(e), expected); |
2713 | assert_eq!(Pow::pow(r, e), expected); |
2714 | assert_eq!(Pow::pow(r, &e), expected); |
2715 | assert_eq!(Pow::pow(&r, e), expected); |
2716 | assert_eq!(Pow::pow(&r, &e), expected); |
2717 | #[cfg (feature = "num-bigint" )] |
2718 | test_big(r, e, expected); |
2719 | } |
2720 | |
2721 | #[cfg (feature = "num-bigint" )] |
2722 | fn test_big(r: Rational64, e: i32, expected: Rational64) { |
2723 | let r = BigRational::new_raw(r.numer.into(), r.denom.into()); |
2724 | let expected = BigRational::new_raw(expected.numer.into(), expected.denom.into()); |
2725 | assert_eq!((&r).pow(e), expected); |
2726 | assert_eq!(Pow::pow(r.clone(), e), expected); |
2727 | assert_eq!(Pow::pow(r.clone(), &e), expected); |
2728 | assert_eq!(Pow::pow(&r, e), expected); |
2729 | assert_eq!(Pow::pow(&r, &e), expected); |
2730 | } |
2731 | |
2732 | test (_1_2, 2, Ratio::new(1, 4)); |
2733 | test (_1_2, -2, Ratio::new(4, 1)); |
2734 | test (_1, 1, _1); |
2735 | test (_1, i32::MAX, _1); |
2736 | test (_1, i32::MIN, _1); |
2737 | test (_NEG1_2, 2, _1_2.pow(2i32)); |
2738 | test (_NEG1_2, 3, -_1_2.pow(3i32)); |
2739 | test (_3_2, 0, _1); |
2740 | test (_3_2, -1, _3_2.recip()); |
2741 | test (_3_2, 3, Ratio::new(27, 8)); |
2742 | } |
2743 | |
2744 | #[test ] |
2745 | #[cfg (feature = "std" )] |
2746 | fn test_to_from_str() { |
2747 | use std::string::{String, ToString}; |
2748 | fn test(r: Rational64, s: String) { |
2749 | assert_eq!(FromStr::from_str(&s), Ok(r)); |
2750 | assert_eq!(r.to_string(), s); |
2751 | } |
2752 | test (_1, "1" .to_string()); |
2753 | test (_0, "0" .to_string()); |
2754 | test (_1_2, "1/2" .to_string()); |
2755 | test (_3_2, "3/2" .to_string()); |
2756 | test (_2, "2" .to_string()); |
2757 | test (_NEG1_2, "-1/2" .to_string()); |
2758 | } |
2759 | #[test ] |
2760 | fn test_from_str_fail() { |
2761 | fn test(s: &str) { |
2762 | let rational: Result<Rational64, _> = FromStr::from_str(s); |
2763 | assert!(rational.is_err()); |
2764 | } |
2765 | |
2766 | let xs = ["0 /1" , "abc" , "" , "1/" , "--1/2" , "3/2/1" , "1/0" ]; |
2767 | for &s in xs.iter() { |
2768 | test (s); |
2769 | } |
2770 | } |
2771 | |
2772 | #[cfg (feature = "num-bigint" )] |
2773 | #[test ] |
2774 | fn test_from_float() { |
2775 | use num_traits::float::FloatCore; |
2776 | fn test<T: FloatCore>(given: T, (numer, denom): (&str, &str)) { |
2777 | let ratio: BigRational = Ratio::from_float(given).unwrap(); |
2778 | assert_eq!( |
2779 | ratio, |
2780 | Ratio::new( |
2781 | FromStr::from_str(numer).unwrap(), |
2782 | FromStr::from_str(denom).unwrap() |
2783 | ) |
2784 | ); |
2785 | } |
2786 | |
2787 | // f32 |
2788 | test (core::f32::consts::PI, ("13176795" , "4194304" )); |
2789 | test (2f32.powf(100.), ("1267650600228229401496703205376" , "1" )); |
2790 | test ( |
2791 | -(2f32.powf(100.)), |
2792 | ("-1267650600228229401496703205376" , "1" ), |
2793 | ); |
2794 | test ( |
2795 | 1.0 / 2f32.powf(100.), |
2796 | ("1" , "1267650600228229401496703205376" ), |
2797 | ); |
2798 | test (684729.48391f32, ("1369459" , "2" )); |
2799 | test (-8573.5918555f32, ("-4389679" , "512" )); |
2800 | |
2801 | // f64 |
2802 | test ( |
2803 | core::f64::consts::PI, |
2804 | ("884279719003555" , "281474976710656" ), |
2805 | ); |
2806 | test (2f64.powf(100.), ("1267650600228229401496703205376" , "1" )); |
2807 | test ( |
2808 | -(2f64.powf(100.)), |
2809 | ("-1267650600228229401496703205376" , "1" ), |
2810 | ); |
2811 | test (684729.48391f64, ("367611342500051" , "536870912" )); |
2812 | test (-8573.5918555f64, ("-4713381968463931" , "549755813888" )); |
2813 | test ( |
2814 | 1.0 / 2f64.powf(100.), |
2815 | ("1" , "1267650600228229401496703205376" ), |
2816 | ); |
2817 | } |
2818 | |
2819 | #[cfg (feature = "num-bigint" )] |
2820 | #[test ] |
2821 | fn test_from_float_fail() { |
2822 | use core::{f32, f64}; |
2823 | |
2824 | assert_eq!(Ratio::from_float(f32::NAN), None); |
2825 | assert_eq!(Ratio::from_float(f32::INFINITY), None); |
2826 | assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None); |
2827 | assert_eq!(Ratio::from_float(f64::NAN), None); |
2828 | assert_eq!(Ratio::from_float(f64::INFINITY), None); |
2829 | assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None); |
2830 | } |
2831 | |
2832 | #[test ] |
2833 | fn test_signed() { |
2834 | assert_eq!(_NEG1_2.abs(), _1_2); |
2835 | assert_eq!(_3_2.abs_sub(&_1_2), _1); |
2836 | assert_eq!(_1_2.abs_sub(&_3_2), Zero::zero()); |
2837 | assert_eq!(_1_2.signum(), One::one()); |
2838 | assert_eq!(_NEG1_2.signum(), -<Ratio<i64>>::one()); |
2839 | assert_eq!(_0.signum(), Zero::zero()); |
2840 | assert!(_NEG1_2.is_negative()); |
2841 | assert!(_1_NEG2.is_negative()); |
2842 | assert!(!_NEG1_2.is_positive()); |
2843 | assert!(!_1_NEG2.is_positive()); |
2844 | assert!(_1_2.is_positive()); |
2845 | assert!(_NEG1_NEG2.is_positive()); |
2846 | assert!(!_1_2.is_negative()); |
2847 | assert!(!_NEG1_NEG2.is_negative()); |
2848 | assert!(!_0.is_positive()); |
2849 | assert!(!_0.is_negative()); |
2850 | } |
2851 | |
2852 | #[test ] |
2853 | #[cfg (feature = "std" )] |
2854 | fn test_hash() { |
2855 | assert!(crate::hash(&_0) != crate::hash(&_1)); |
2856 | assert!(crate::hash(&_0) != crate::hash(&_3_2)); |
2857 | |
2858 | // a == b -> hash(a) == hash(b) |
2859 | let a = Rational64::new_raw(4, 2); |
2860 | let b = Rational64::new_raw(6, 3); |
2861 | assert_eq!(a, b); |
2862 | assert_eq!(crate::hash(&a), crate::hash(&b)); |
2863 | |
2864 | let a = Rational64::new_raw(123456789, 1000); |
2865 | let b = Rational64::new_raw(123456789 * 5, 5000); |
2866 | assert_eq!(a, b); |
2867 | assert_eq!(crate::hash(&a), crate::hash(&b)); |
2868 | } |
2869 | |
2870 | #[test ] |
2871 | fn test_into_pair() { |
2872 | assert_eq!((0, 1), _0.into()); |
2873 | assert_eq!((-2, 1), _NEG2.into()); |
2874 | assert_eq!((1, -2), _1_NEG2.into()); |
2875 | } |
2876 | |
2877 | #[test ] |
2878 | fn test_from_pair() { |
2879 | assert_eq!(_0, Ratio::from((0, 1))); |
2880 | assert_eq!(_1, Ratio::from((1, 1))); |
2881 | assert_eq!(_NEG2, Ratio::from((-2, 1))); |
2882 | assert_eq!(_1_NEG2, Ratio::from((1, -2))); |
2883 | } |
2884 | |
2885 | #[test ] |
2886 | fn ratio_iter_sum() { |
2887 | // generic function to assure the iter method can be called |
2888 | // for any Iterator with Item = Ratio<impl Integer> or Ratio<&impl Integer> |
2889 | fn iter_sums<T: Integer + Clone>(slice: &[Ratio<T>]) -> [Ratio<T>; 3] { |
2890 | let mut manual_sum = Ratio::new(T::zero(), T::one()); |
2891 | for ratio in slice { |
2892 | manual_sum = manual_sum + ratio; |
2893 | } |
2894 | [manual_sum, slice.iter().sum(), slice.iter().cloned().sum()] |
2895 | } |
2896 | // collect into array so test works on no_std |
2897 | let mut nums = [Ratio::new(0, 1); 1000]; |
2898 | for (i, r) in (0..1000).map(|n| Ratio::new(n, 500)).enumerate() { |
2899 | nums[i] = r; |
2900 | } |
2901 | let sums = iter_sums(&nums[..]); |
2902 | assert_eq!(sums[0], sums[1]); |
2903 | assert_eq!(sums[0], sums[2]); |
2904 | } |
2905 | |
2906 | #[test ] |
2907 | fn ratio_iter_product() { |
2908 | // generic function to assure the iter method can be called |
2909 | // for any Iterator with Item = Ratio<impl Integer> or Ratio<&impl Integer> |
2910 | fn iter_products<T: Integer + Clone>(slice: &[Ratio<T>]) -> [Ratio<T>; 3] { |
2911 | let mut manual_prod = Ratio::new(T::one(), T::one()); |
2912 | for ratio in slice { |
2913 | manual_prod = manual_prod * ratio; |
2914 | } |
2915 | [ |
2916 | manual_prod, |
2917 | slice.iter().product(), |
2918 | slice.iter().cloned().product(), |
2919 | ] |
2920 | } |
2921 | |
2922 | // collect into array so test works on no_std |
2923 | let mut nums = [Ratio::new(0, 1); 1000]; |
2924 | for (i, r) in (0..1000).map(|n| Ratio::new(n, 500)).enumerate() { |
2925 | nums[i] = r; |
2926 | } |
2927 | let products = iter_products(&nums[..]); |
2928 | assert_eq!(products[0], products[1]); |
2929 | assert_eq!(products[0], products[2]); |
2930 | } |
2931 | |
2932 | #[test ] |
2933 | fn test_num_zero() { |
2934 | let zero = Rational64::zero(); |
2935 | assert!(zero.is_zero()); |
2936 | |
2937 | let mut r = Rational64::new(123, 456); |
2938 | assert!(!r.is_zero()); |
2939 | assert_eq!(r + zero, r); |
2940 | |
2941 | r.set_zero(); |
2942 | assert!(r.is_zero()); |
2943 | } |
2944 | |
2945 | #[test ] |
2946 | fn test_num_one() { |
2947 | let one = Rational64::one(); |
2948 | assert!(one.is_one()); |
2949 | |
2950 | let mut r = Rational64::new(123, 456); |
2951 | assert!(!r.is_one()); |
2952 | assert_eq!(r * one, r); |
2953 | |
2954 | r.set_one(); |
2955 | assert!(r.is_one()); |
2956 | } |
2957 | |
2958 | #[test ] |
2959 | fn test_const() { |
2960 | const N: Ratio<i32> = Ratio::new_raw(123, 456); |
2961 | const N_NUMER: &i32 = N.numer(); |
2962 | const N_DENOM: &i32 = N.denom(); |
2963 | |
2964 | assert_eq!(N_NUMER, &123); |
2965 | assert_eq!(N_DENOM, &456); |
2966 | |
2967 | let r = N.reduced(); |
2968 | assert_eq!(r.numer(), &(123 / 3)); |
2969 | assert_eq!(r.denom(), &(456 / 3)); |
2970 | } |
2971 | |
2972 | #[test ] |
2973 | fn test_ratio_to_i64() { |
2974 | assert_eq!(5, Rational64::new(70, 14).to_u64().unwrap()); |
2975 | assert_eq!(-3, Rational64::new(-31, 8).to_i64().unwrap()); |
2976 | assert_eq!(None, Rational64::new(-31, 8).to_u64()); |
2977 | } |
2978 | |
2979 | #[test ] |
2980 | #[cfg (feature = "num-bigint" )] |
2981 | fn test_ratio_to_i128() { |
2982 | assert_eq!( |
2983 | 1i128 << 70, |
2984 | Ratio::<i128>::new(1i128 << 77, 1i128 << 7) |
2985 | .to_i128() |
2986 | .unwrap() |
2987 | ); |
2988 | } |
2989 | |
2990 | #[test ] |
2991 | #[cfg (feature = "num-bigint" )] |
2992 | fn test_big_ratio_to_f64() { |
2993 | assert_eq!( |
2994 | BigRational::new( |
2995 | "1234567890987654321234567890987654321234567890" |
2996 | .parse() |
2997 | .unwrap(), |
2998 | "3" .parse().unwrap() |
2999 | ) |
3000 | .to_f64(), |
3001 | Some(411522630329218100000000000000000000000000000f64) |
3002 | ); |
3003 | assert_eq!(Ratio::from_float(5e-324).unwrap().to_f64(), Some(5e-324)); |
3004 | assert_eq!( |
3005 | // subnormal |
3006 | BigRational::new(BigInt::one(), BigInt::one() << 1050).to_f64(), |
3007 | Some(2.0f64.powi(-50).powi(21)) |
3008 | ); |
3009 | assert_eq!( |
3010 | // definite underflow |
3011 | BigRational::new(BigInt::one(), BigInt::one() << 1100).to_f64(), |
3012 | Some(0.0) |
3013 | ); |
3014 | assert_eq!( |
3015 | BigRational::from(BigInt::one() << 1050).to_f64(), |
3016 | Some(core::f64::INFINITY) |
3017 | ); |
3018 | assert_eq!( |
3019 | BigRational::from((-BigInt::one()) << 1050).to_f64(), |
3020 | Some(core::f64::NEG_INFINITY) |
3021 | ); |
3022 | assert_eq!( |
3023 | BigRational::new( |
3024 | "1234567890987654321234567890" .parse().unwrap(), |
3025 | "987654321234567890987654321" .parse().unwrap() |
3026 | ) |
3027 | .to_f64(), |
3028 | Some(1.2499999893125f64) |
3029 | ); |
3030 | assert_eq!( |
3031 | BigRational::new_raw(BigInt::one(), BigInt::zero()).to_f64(), |
3032 | Some(core::f64::INFINITY) |
3033 | ); |
3034 | assert_eq!( |
3035 | BigRational::new_raw(-BigInt::one(), BigInt::zero()).to_f64(), |
3036 | Some(core::f64::NEG_INFINITY) |
3037 | ); |
3038 | assert_eq!( |
3039 | BigRational::new_raw(BigInt::zero(), BigInt::zero()).to_f64(), |
3040 | None |
3041 | ); |
3042 | } |
3043 | |
3044 | #[test ] |
3045 | fn test_ratio_to_f64() { |
3046 | assert_eq!(Ratio::<u8>::new(1, 2).to_f64(), Some(0.5f64)); |
3047 | assert_eq!(Rational64::new(1, 2).to_f64(), Some(0.5f64)); |
3048 | assert_eq!(Rational64::new(1, -2).to_f64(), Some(-0.5f64)); |
3049 | assert_eq!(Rational64::new(0, 2).to_f64(), Some(0.0f64)); |
3050 | assert_eq!(Rational64::new(0, -2).to_f64(), Some(-0.0f64)); |
3051 | assert_eq!(Rational64::new((1 << 57) + 1, 1 << 54).to_f64(), Some(8f64)); |
3052 | assert_eq!( |
3053 | Rational64::new((1 << 52) + 1, 1 << 52).to_f64(), |
3054 | Some(1.0000000000000002f64), |
3055 | ); |
3056 | assert_eq!( |
3057 | Rational64::new((1 << 60) + (1 << 8), 1 << 60).to_f64(), |
3058 | Some(1.0000000000000002f64), |
3059 | ); |
3060 | assert_eq!( |
3061 | Ratio::<i32>::new_raw(1, 0).to_f64(), |
3062 | Some(core::f64::INFINITY) |
3063 | ); |
3064 | assert_eq!( |
3065 | Ratio::<i32>::new_raw(-1, 0).to_f64(), |
3066 | Some(core::f64::NEG_INFINITY) |
3067 | ); |
3068 | assert_eq!(Ratio::<i32>::new_raw(0, 0).to_f64(), None); |
3069 | } |
3070 | |
3071 | #[test ] |
3072 | fn test_ldexp() { |
3073 | use core::f64::{INFINITY, MAX_EXP, MIN_EXP, NAN, NEG_INFINITY}; |
3074 | assert_eq!(ldexp(1.0, 0), 1.0); |
3075 | assert_eq!(ldexp(1.0, 1), 2.0); |
3076 | assert_eq!(ldexp(0.0, 1), 0.0); |
3077 | assert_eq!(ldexp(-0.0, 1), -0.0); |
3078 | |
3079 | // Cases where ldexp is equivalent to multiplying by 2^exp because there's no over- or |
3080 | // underflow. |
3081 | assert_eq!(ldexp(3.5, 5), 3.5 * 2f64.powi(5)); |
3082 | assert_eq!(ldexp(1.0, MAX_EXP - 1), 2f64.powi(MAX_EXP - 1)); |
3083 | assert_eq!(ldexp(2.77, MIN_EXP + 3), 2.77 * 2f64.powi(MIN_EXP + 3)); |
3084 | |
3085 | // Case where initial value is subnormal |
3086 | assert_eq!(ldexp(5e-324, 4), 5e-324 * 2f64.powi(4)); |
3087 | assert_eq!(ldexp(5e-324, 200), 5e-324 * 2f64.powi(200)); |
3088 | |
3089 | // Near underflow (2^exp is too small to represent, but not x*2^exp) |
3090 | assert_eq!(ldexp(4.0, MIN_EXP - 3), 2f64.powi(MIN_EXP - 1)); |
3091 | |
3092 | // Near overflow |
3093 | assert_eq!(ldexp(0.125, MAX_EXP + 3), 2f64.powi(MAX_EXP)); |
3094 | |
3095 | // Overflow and underflow cases |
3096 | assert_eq!(ldexp(1.0, MIN_EXP - 54), 0.0); |
3097 | assert_eq!(ldexp(-1.0, MIN_EXP - 54), -0.0); |
3098 | assert_eq!(ldexp(1.0, MAX_EXP), INFINITY); |
3099 | assert_eq!(ldexp(-1.0, MAX_EXP), NEG_INFINITY); |
3100 | |
3101 | // Special values |
3102 | assert_eq!(ldexp(INFINITY, 1), INFINITY); |
3103 | assert_eq!(ldexp(NEG_INFINITY, 1), NEG_INFINITY); |
3104 | assert!(ldexp(NAN, 1).is_nan()); |
3105 | } |
3106 | } |
3107 | |