]> git.lizzy.rs Git - rust.git/blob - src/libnum/rational.rs
afd4fa73ef6d848278e35b15d91aac8d3d06cd57
[rust.git] / src / libnum / rational.rs
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 use Integer;
14
15 use std::cmp;
16 use std::fmt;
17 use std::from_str::FromStr;
18 use std::num::{Zero,One,ToStrRadix,FromStrRadix,Round};
19 use bigint::{BigInt, BigUint, Sign, Plus, Minus};
20
21 /// Represents the ratio between 2 numbers.
22 #[deriving(Clone)]
23 #[allow(missing_doc)]
24 pub struct Ratio<T> {
25     priv numer: T,
26     priv denom: T
27 }
28
29 /// Alias for a `Ratio` of machine-sized integers.
30 pub type Rational = Ratio<int>;
31 pub type Rational32 = Ratio<i32>;
32 pub type Rational64 = Ratio<i64>;
33
34 /// Alias for arbitrary precision rationals.
35 pub type BigRational = Ratio<BigInt>;
36
37 impl<T: Clone + Integer + Ord>
38     Ratio<T> {
39     /// Create a ratio representing the integer `t`.
40     #[inline]
41     pub fn from_integer(t: T) -> Ratio<T> {
42         Ratio::new_raw(t, One::one())
43     }
44
45     /// Create a ratio without checking for `denom == 0` or reducing.
46     #[inline]
47     pub fn new_raw(numer: T, denom: T) -> Ratio<T> {
48         Ratio { numer: numer, denom: denom }
49     }
50
51     /// Create a new Ratio. Fails if `denom == 0`.
52     #[inline]
53     pub fn new(numer: T, denom: T) -> Ratio<T> {
54         if denom == Zero::zero() {
55             fail!("denominator == 0");
56         }
57         let mut ret = Ratio::new_raw(numer, denom);
58         ret.reduce();
59         ret
60     }
61
62     /// Convert to an integer.
63     #[inline]
64     pub fn to_integer(&self) -> T {
65         self.trunc().numer
66     }
67
68     /// Gets an immutable reference to the numerator.
69     #[inline]
70     pub fn numer<'a>(&'a self) -> &'a T {
71         &self.numer
72     }
73
74     /// Gets an immutable reference to the denominator.
75     #[inline]
76     pub fn denom<'a>(&'a self) -> &'a T {
77         &self.denom
78     }
79
80     /// Return true if the rational number is an integer (denominator is 1).
81     #[inline]
82     pub fn is_integer(&self) -> bool {
83         self.denom == One::one()
84     }
85
86     /// Put self into lowest terms, with denom > 0.
87     fn reduce(&mut self) {
88         let g : T = self.numer.gcd(&self.denom);
89
90         // FIXME(#6050): overloaded operators force moves with generic types
91         // self.numer /= g;
92         self.numer = self.numer / g;
93         // FIXME(#6050): overloaded operators force moves with generic types
94         // self.denom /= g;
95         self.denom = self.denom / g;
96
97         // keep denom positive!
98         if self.denom < Zero::zero() {
99             self.numer = -self.numer;
100             self.denom = -self.denom;
101         }
102     }
103
104     /// Return a `reduce`d copy of self.
105     pub fn reduced(&self) -> Ratio<T> {
106         let mut ret = self.clone();
107         ret.reduce();
108         ret
109     }
110
111     /// Return the reciprocal
112     #[inline]
113     pub fn recip(&self) -> Ratio<T> {
114         Ratio::new_raw(self.denom.clone(), self.numer.clone())
115     }
116 }
117
118 impl Ratio<BigInt> {
119     /// Converts a float into a rational number
120     pub fn from_float<T: Float>(f: T) -> Option<BigRational> {
121         if !f.is_finite() {
122             return None;
123         }
124         let (mantissa, exponent, sign) = f.integer_decode();
125         let bigint_sign: Sign = if sign == 1 { Plus } else { Minus };
126         if exponent < 0 {
127             let one: BigInt = One::one();
128             let denom: BigInt = one << ((-exponent) as uint);
129             let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
130             Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom))
131         } else {
132             let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
133             numer = numer << (exponent as uint);
134             Some(Ratio::from_integer(BigInt::from_biguint(bigint_sign, numer)))
135         }
136     }
137 }
138
139 /* Comparisons */
140
141 // comparing a/b and c/d is the same as comparing a*d and b*c, so we
142 // abstract that pattern. The following macro takes a trait and either
143 // a comma-separated list of "method name -> return value" or just
144 // "method name" (return value is bool in that case)
145 macro_rules! cmp_impl {
146     (impl $imp:ident, $($method:ident),+) => {
147         cmp_impl!(impl $imp, $($method -> bool),+)
148     };
149     // return something other than a Ratio<T>
150     (impl $imp:ident, $($method:ident -> $res:ty),+) => {
151         impl<T: Mul<T,T> + $imp> $imp for Ratio<T> {
152             $(
153                 #[inline]
154                 fn $method(&self, other: &Ratio<T>) -> $res {
155                     (self.numer * other.denom). $method (&(self.denom*other.numer))
156                 }
157             )+
158         }
159     };
160 }
161 cmp_impl!(impl Eq, eq, ne)
162 cmp_impl!(impl TotalEq, equals)
163 cmp_impl!(impl Ord, lt, gt, le, ge)
164 cmp_impl!(impl TotalOrd, cmp -> cmp::Ordering)
165
166 /* Arithmetic */
167 // a/b * c/d = (a*c)/(b*d)
168 impl<T: Clone + Integer + Ord>
169     Mul<Ratio<T>,Ratio<T>> for Ratio<T> {
170     #[inline]
171     fn mul(&self, rhs: &Ratio<T>) -> Ratio<T> {
172         Ratio::new(self.numer * rhs.numer, self.denom * rhs.denom)
173     }
174 }
175
176 // (a/b) / (c/d) = (a*d)/(b*c)
177 impl<T: Clone + Integer + Ord>
178     Div<Ratio<T>,Ratio<T>> for Ratio<T> {
179     #[inline]
180     fn div(&self, rhs: &Ratio<T>) -> Ratio<T> {
181         Ratio::new(self.numer * rhs.denom, self.denom * rhs.numer)
182     }
183 }
184
185 // Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern
186 macro_rules! arith_impl {
187     (impl $imp:ident, $method:ident) => {
188         impl<T: Clone + Integer + Ord>
189             $imp<Ratio<T>,Ratio<T>> for Ratio<T> {
190             #[inline]
191             fn $method(&self, rhs: &Ratio<T>) -> Ratio<T> {
192                 Ratio::new((self.numer * rhs.denom).$method(&(self.denom * rhs.numer)),
193                            self.denom * rhs.denom)
194             }
195         }
196     }
197 }
198
199 // a/b + c/d = (a*d + b*c)/(b*d
200 arith_impl!(impl Add, add)
201
202 // a/b - c/d = (a*d - b*c)/(b*d)
203 arith_impl!(impl Sub, sub)
204
205 // a/b % c/d = (a*d % b*c)/(b*d)
206 arith_impl!(impl Rem, rem)
207
208 impl<T: Clone + Integer + Ord>
209     Neg<Ratio<T>> for Ratio<T> {
210     #[inline]
211     fn neg(&self) -> Ratio<T> {
212         Ratio::new_raw(-self.numer, self.denom.clone())
213     }
214 }
215
216 /* Constants */
217 impl<T: Clone + Integer + Ord>
218     Zero for Ratio<T> {
219     #[inline]
220     fn zero() -> Ratio<T> {
221         Ratio::new_raw(Zero::zero(), One::one())
222     }
223
224     #[inline]
225     fn is_zero(&self) -> bool {
226         *self == Zero::zero()
227     }
228 }
229
230 impl<T: Clone + Integer + Ord>
231     One for Ratio<T> {
232     #[inline]
233     fn one() -> Ratio<T> {
234         Ratio::new_raw(One::one(), One::one())
235     }
236 }
237
238 impl<T: Clone + Integer + Ord>
239     Num for Ratio<T> {}
240
241 /* Utils */
242 impl<T: Clone + Integer + Ord>
243     Round for Ratio<T> {
244
245     fn floor(&self) -> Ratio<T> {
246         if *self < Zero::zero() {
247             Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom)
248         } else {
249             Ratio::from_integer(self.numer / self.denom)
250         }
251     }
252
253     fn ceil(&self) -> Ratio<T> {
254         if *self < Zero::zero() {
255             Ratio::from_integer(self.numer / self.denom)
256         } else {
257             Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom)
258         }
259     }
260
261     #[inline]
262     fn round(&self) -> Ratio<T> {
263         if *self < Zero::zero() {
264             Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom)
265         } else {
266             Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom)
267         }
268     }
269
270     #[inline]
271     fn trunc(&self) -> Ratio<T> {
272         Ratio::from_integer(self.numer / self.denom)
273     }
274
275     fn fract(&self) -> Ratio<T> {
276         Ratio::new_raw(self.numer % self.denom, self.denom.clone())
277     }
278 }
279
280 /* String conversions */
281 impl<T: fmt::Show> fmt::Show for Ratio<T> {
282     /// Renders as `numer/denom`.
283     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284         write!(f.buf, "{}/{}", self.numer, self.denom)
285     }
286 }
287 impl<T: ToStrRadix> ToStrRadix for Ratio<T> {
288     /// Renders as `numer/denom` where the numbers are in base `radix`.
289     fn to_str_radix(&self, radix: uint) -> ~str {
290         format!("{}/{}", self.numer.to_str_radix(radix), self.denom.to_str_radix(radix))
291     }
292 }
293
294 impl<T: FromStr + Clone + Integer + Ord>
295     FromStr for Ratio<T> {
296     /// Parses `numer/denom`.
297     fn from_str(s: &str) -> Option<Ratio<T>> {
298         let split: Vec<&str> = s.splitn('/', 1).collect();
299         if split.len() < 2 {
300             return None
301         }
302         let a_option: Option<T> = FromStr::from_str(split.as_slice()[0]);
303         a_option.and_then(|a| {
304             let b_option: Option<T> = FromStr::from_str(split.as_slice()[1]);
305             b_option.and_then(|b| {
306                 Some(Ratio::new(a.clone(), b.clone()))
307             })
308         })
309     }
310 }
311 impl<T: FromStrRadix + Clone + Integer + Ord>
312     FromStrRadix for Ratio<T> {
313     /// Parses `numer/denom` where the numbers are in base `radix`.
314     fn from_str_radix(s: &str, radix: uint) -> Option<Ratio<T>> {
315         let split: Vec<&str> = s.splitn('/', 1).collect();
316         if split.len() < 2 {
317             None
318         } else {
319             let a_option: Option<T> = FromStrRadix::from_str_radix(split.as_slice()[0],
320                                                                    radix);
321             a_option.and_then(|a| {
322                 let b_option: Option<T> =
323                     FromStrRadix::from_str_radix(split.as_slice()[1], radix);
324                 b_option.and_then(|b| {
325                     Some(Ratio::new(a.clone(), b.clone()))
326                 })
327             })
328         }
329     }
330 }
331
332 #[cfg(test)]
333 mod test {
334
335     use super::{Ratio, Rational, BigRational};
336     use std::num::{Zero, One, FromStrRadix, FromPrimitive, ToStrRadix};
337     use std::from_str::FromStr;
338
339     pub static _0 : Rational = Ratio { numer: 0, denom: 1};
340     pub static _1 : Rational = Ratio { numer: 1, denom: 1};
341     pub static _2: Rational = Ratio { numer: 2, denom: 1};
342     pub static _1_2: Rational = Ratio { numer: 1, denom: 2};
343     pub static _3_2: Rational = Ratio { numer: 3, denom: 2};
344     pub static _neg1_2: Rational =  Ratio { numer: -1, denom: 2};
345
346     pub fn to_big(n: Rational) -> BigRational {
347         Ratio::new(
348             FromPrimitive::from_int(n.numer).unwrap(),
349             FromPrimitive::from_int(n.denom).unwrap()
350         )
351     }
352
353     #[test]
354     fn test_test_constants() {
355         // check our constants are what Ratio::new etc. would make.
356         assert_eq!(_0, Zero::zero());
357         assert_eq!(_1, One::one());
358         assert_eq!(_2, Ratio::from_integer(2));
359         assert_eq!(_1_2, Ratio::new(1,2));
360         assert_eq!(_3_2, Ratio::new(3,2));
361         assert_eq!(_neg1_2, Ratio::new(-1,2));
362     }
363
364     #[test]
365     fn test_new_reduce() {
366         let one22 = Ratio::new(2i,2);
367
368         assert_eq!(one22, One::one());
369     }
370     #[test]
371     #[should_fail]
372     fn test_new_zero() {
373         let _a = Ratio::new(1,0);
374     }
375
376
377     #[test]
378     fn test_cmp() {
379         assert!(_0 == _0 && _1 == _1);
380         assert!(_0 != _1 && _1 != _0);
381         assert!(_0 < _1 && !(_1 < _0));
382         assert!(_1 > _0 && !(_0 > _1));
383
384         assert!(_0 <= _0 && _1 <= _1);
385         assert!(_0 <= _1 && !(_1 <= _0));
386
387         assert!(_0 >= _0 && _1 >= _1);
388         assert!(_1 >= _0 && !(_0 >= _1));
389     }
390
391
392     #[test]
393     fn test_to_integer() {
394         assert_eq!(_0.to_integer(), 0);
395         assert_eq!(_1.to_integer(), 1);
396         assert_eq!(_2.to_integer(), 2);
397         assert_eq!(_1_2.to_integer(), 0);
398         assert_eq!(_3_2.to_integer(), 1);
399         assert_eq!(_neg1_2.to_integer(), 0);
400     }
401
402
403     #[test]
404     fn test_numer() {
405         assert_eq!(_0.numer(), &0);
406         assert_eq!(_1.numer(), &1);
407         assert_eq!(_2.numer(), &2);
408         assert_eq!(_1_2.numer(), &1);
409         assert_eq!(_3_2.numer(), &3);
410         assert_eq!(_neg1_2.numer(), &(-1));
411     }
412     #[test]
413     fn test_denom() {
414         assert_eq!(_0.denom(), &1);
415         assert_eq!(_1.denom(), &1);
416         assert_eq!(_2.denom(), &1);
417         assert_eq!(_1_2.denom(), &2);
418         assert_eq!(_3_2.denom(), &2);
419         assert_eq!(_neg1_2.denom(), &2);
420     }
421
422
423     #[test]
424     fn test_is_integer() {
425         assert!(_0.is_integer());
426         assert!(_1.is_integer());
427         assert!(_2.is_integer());
428         assert!(!_1_2.is_integer());
429         assert!(!_3_2.is_integer());
430         assert!(!_neg1_2.is_integer());
431     }
432
433
434     mod arith {
435         use super::{_0, _1, _2, _1_2, _3_2, _neg1_2, to_big};
436         use super::super::{Ratio, Rational};
437
438         #[test]
439         fn test_add() {
440             fn test(a: Rational, b: Rational, c: Rational) {
441                 assert_eq!(a + b, c);
442                 assert_eq!(to_big(a) + to_big(b), to_big(c));
443             }
444
445             test(_1, _1_2, _3_2);
446             test(_1, _1, _2);
447             test(_1_2, _3_2, _2);
448             test(_1_2, _neg1_2, _0);
449         }
450
451         #[test]
452         fn test_sub() {
453             fn test(a: Rational, b: Rational, c: Rational) {
454                 assert_eq!(a - b, c);
455                 assert_eq!(to_big(a) - to_big(b), to_big(c))
456             }
457
458             test(_1, _1_2, _1_2);
459             test(_3_2, _1_2, _1);
460             test(_1, _neg1_2, _3_2);
461         }
462
463         #[test]
464         fn test_mul() {
465             fn test(a: Rational, b: Rational, c: Rational) {
466                 assert_eq!(a * b, c);
467                 assert_eq!(to_big(a) * to_big(b), to_big(c))
468             }
469
470             test(_1, _1_2, _1_2);
471             test(_1_2, _3_2, Ratio::new(3,4));
472             test(_1_2, _neg1_2, Ratio::new(-1, 4));
473         }
474
475         #[test]
476         fn test_div() {
477             fn test(a: Rational, b: Rational, c: Rational) {
478                 assert_eq!(a / b, c);
479                 assert_eq!(to_big(a) / to_big(b), to_big(c))
480             }
481
482             test(_1, _1_2, _2);
483             test(_3_2, _1_2, _1 + _2);
484             test(_1, _neg1_2, _neg1_2 + _neg1_2 + _neg1_2 + _neg1_2);
485         }
486
487         #[test]
488         fn test_rem() {
489             fn test(a: Rational, b: Rational, c: Rational) {
490                 assert_eq!(a % b, c);
491                 assert_eq!(to_big(a) % to_big(b), to_big(c))
492             }
493
494             test(_3_2, _1, _1_2);
495             test(_2, _neg1_2, _0);
496             test(_1_2, _2,  _1_2);
497         }
498
499         #[test]
500         fn test_neg() {
501             fn test(a: Rational, b: Rational) {
502                 assert_eq!(-a, b);
503                 assert_eq!(-to_big(a), to_big(b))
504             }
505
506             test(_0, _0);
507             test(_1_2, _neg1_2);
508             test(-_1, _1);
509         }
510         #[test]
511         fn test_zero() {
512             assert_eq!(_0 + _0, _0);
513             assert_eq!(_0 * _0, _0);
514             assert_eq!(_0 * _1, _0);
515             assert_eq!(_0 / _neg1_2, _0);
516             assert_eq!(_0 - _0, _0);
517         }
518         #[test]
519         #[should_fail]
520         fn test_div_0() {
521             let _a =  _1 / _0;
522         }
523     }
524
525     #[test]
526     fn test_round() {
527         assert_eq!(_1_2.ceil(), _1);
528         assert_eq!(_1_2.floor(), _0);
529         assert_eq!(_1_2.round(), _1);
530         assert_eq!(_1_2.trunc(), _0);
531
532         assert_eq!(_neg1_2.ceil(), _0);
533         assert_eq!(_neg1_2.floor(), -_1);
534         assert_eq!(_neg1_2.round(), -_1);
535         assert_eq!(_neg1_2.trunc(), _0);
536
537         assert_eq!(_1.ceil(), _1);
538         assert_eq!(_1.floor(), _1);
539         assert_eq!(_1.round(), _1);
540         assert_eq!(_1.trunc(), _1);
541     }
542
543     #[test]
544     fn test_fract() {
545         assert_eq!(_1.fract(), _0);
546         assert_eq!(_neg1_2.fract(), _neg1_2);
547         assert_eq!(_1_2.fract(), _1_2);
548         assert_eq!(_3_2.fract(), _1_2);
549     }
550
551     #[test]
552     fn test_recip() {
553         assert_eq!(_1 * _1.recip(), _1);
554         assert_eq!(_2 * _2.recip(), _1);
555         assert_eq!(_1_2 * _1_2.recip(), _1);
556         assert_eq!(_3_2 * _3_2.recip(), _1);
557         assert_eq!(_neg1_2 * _neg1_2.recip(), _1);
558     }
559
560     #[test]
561     fn test_to_from_str() {
562         fn test(r: Rational, s: ~str) {
563             assert_eq!(FromStr::from_str(s), Some(r));
564             assert_eq!(r.to_str(), s);
565         }
566         test(_1, ~"1/1");
567         test(_0, ~"0/1");
568         test(_1_2, ~"1/2");
569         test(_3_2, ~"3/2");
570         test(_2, ~"2/1");
571         test(_neg1_2, ~"-1/2");
572     }
573     #[test]
574     fn test_from_str_fail() {
575         fn test(s: &str) {
576             let rational: Option<Rational> = FromStr::from_str(s);
577             assert_eq!(rational, None);
578         }
579
580         let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1"];
581         for &s in xs.iter() {
582             test(s);
583         }
584     }
585
586     #[test]
587     fn test_to_from_str_radix() {
588         fn test(r: Rational, s: ~str, n: uint) {
589             assert_eq!(FromStrRadix::from_str_radix(s, n), Some(r));
590             assert_eq!(r.to_str_radix(n), s);
591         }
592         fn test3(r: Rational, s: ~str) { test(r, s, 3) }
593         fn test16(r: Rational, s: ~str) { test(r, s, 16) }
594
595         test3(_1, ~"1/1");
596         test3(_0, ~"0/1");
597         test3(_1_2, ~"1/2");
598         test3(_3_2, ~"10/2");
599         test3(_2, ~"2/1");
600         test3(_neg1_2, ~"-1/2");
601         test3(_neg1_2 / _2, ~"-1/11");
602
603         test16(_1, ~"1/1");
604         test16(_0, ~"0/1");
605         test16(_1_2, ~"1/2");
606         test16(_3_2, ~"3/2");
607         test16(_2, ~"2/1");
608         test16(_neg1_2, ~"-1/2");
609         test16(_neg1_2 / _2, ~"-1/4");
610         test16(Ratio::new(13,15), ~"d/f");
611         test16(_1_2*_1_2*_1_2*_1_2, ~"1/10");
612     }
613
614     #[test]
615     fn test_from_str_radix_fail() {
616         fn test(s: &str) {
617             let radix: Option<Rational> = FromStrRadix::from_str_radix(s, 3);
618             assert_eq!(radix, None);
619         }
620
621         let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1", "3/2"];
622         for &s in xs.iter() {
623             test(s);
624         }
625     }
626
627     #[test]
628     fn test_from_float() {
629         fn test<T: Float>(given: T, (numer, denom): (&str, &str)) {
630             let ratio: BigRational = Ratio::from_float(given).unwrap();
631             assert_eq!(ratio, Ratio::new(
632                 FromStr::from_str(numer).unwrap(),
633                 FromStr::from_str(denom).unwrap()));
634         }
635
636         // f32
637         test(3.14159265359f32, ("13176795", "4194304"));
638         test(2f32.powf(&100.), ("1267650600228229401496703205376", "1"));
639         test(-2f32.powf(&100.), ("-1267650600228229401496703205376", "1"));
640         test(1.0 / 2f32.powf(&100.), ("1", "1267650600228229401496703205376"));
641         test(684729.48391f32, ("1369459", "2"));
642         test(-8573.5918555f32, ("-4389679", "512"));
643
644         // f64
645         test(3.14159265359f64, ("3537118876014453", "1125899906842624"));
646         test(2f64.powf(&100.), ("1267650600228229401496703205376", "1"));
647         test(-2f64.powf(&100.), ("-1267650600228229401496703205376", "1"));
648         test(684729.48391f64, ("367611342500051", "536870912"));
649         test(-8573.5918555, ("-4713381968463931", "549755813888"));
650         test(1.0 / 2f64.powf(&100.), ("1", "1267650600228229401496703205376"));
651     }
652
653     #[test]
654     fn test_from_float_fail() {
655         use std::{f32, f64};
656
657         assert_eq!(Ratio::from_float(f32::NAN), None);
658         assert_eq!(Ratio::from_float(f32::INFINITY), None);
659         assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None);
660         assert_eq!(Ratio::from_float(f64::NAN), None);
661         assert_eq!(Ratio::from_float(f64::INFINITY), None);
662         assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None);
663     }
664 }