]> git.lizzy.rs Git - rust.git/blob - src/libnum/rational.rs
e6b63f23741dc136555cd25fa29720b8aa558cd7
[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     numer: T,
26     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(#5992): assignment operator overloads
91         // self.numer /= g;
92         self.numer = self.numer / g;
93         // FIXME(#5992): assignment operator overloads
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 Ord, lt, gt, le, ge)
163 cmp_impl!(impl TotalEq, )
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.get(0));
303         a_option.and_then(|a| {
304             let b_option: Option<T> = FromStr::from_str(*split.get(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(
320                 *split.get(0),
321                 radix);
322             a_option.and_then(|a| {
323                 let b_option: Option<T> =
324                     FromStrRadix::from_str_radix(*split.get(1), radix);
325                 b_option.and_then(|b| {
326                     Some(Ratio::new(a.clone(), b.clone()))
327                 })
328             })
329         }
330     }
331 }
332
333 #[cfg(test)]
334 mod test {
335
336     use super::{Ratio, Rational, BigRational};
337     use std::num::{Zero, One, FromStrRadix, FromPrimitive, ToStrRadix};
338     use std::from_str::FromStr;
339
340     pub static _0 : Rational = Ratio { numer: 0, denom: 1};
341     pub static _1 : Rational = Ratio { numer: 1, denom: 1};
342     pub static _2: Rational = Ratio { numer: 2, denom: 1};
343     pub static _1_2: Rational = Ratio { numer: 1, denom: 2};
344     pub static _3_2: Rational = Ratio { numer: 3, denom: 2};
345     pub static _neg1_2: Rational =  Ratio { numer: -1, denom: 2};
346
347     pub fn to_big(n: Rational) -> BigRational {
348         Ratio::new(
349             FromPrimitive::from_int(n.numer).unwrap(),
350             FromPrimitive::from_int(n.denom).unwrap()
351         )
352     }
353
354     #[test]
355     fn test_test_constants() {
356         // check our constants are what Ratio::new etc. would make.
357         assert_eq!(_0, Zero::zero());
358         assert_eq!(_1, One::one());
359         assert_eq!(_2, Ratio::from_integer(2));
360         assert_eq!(_1_2, Ratio::new(1,2));
361         assert_eq!(_3_2, Ratio::new(3,2));
362         assert_eq!(_neg1_2, Ratio::new(-1,2));
363     }
364
365     #[test]
366     fn test_new_reduce() {
367         let one22 = Ratio::new(2i,2);
368
369         assert_eq!(one22, One::one());
370     }
371     #[test]
372     #[should_fail]
373     fn test_new_zero() {
374         let _a = Ratio::new(1,0);
375     }
376
377
378     #[test]
379     fn test_cmp() {
380         assert!(_0 == _0 && _1 == _1);
381         assert!(_0 != _1 && _1 != _0);
382         assert!(_0 < _1 && !(_1 < _0));
383         assert!(_1 > _0 && !(_0 > _1));
384
385         assert!(_0 <= _0 && _1 <= _1);
386         assert!(_0 <= _1 && !(_1 <= _0));
387
388         assert!(_0 >= _0 && _1 >= _1);
389         assert!(_1 >= _0 && !(_0 >= _1));
390     }
391
392
393     #[test]
394     fn test_to_integer() {
395         assert_eq!(_0.to_integer(), 0);
396         assert_eq!(_1.to_integer(), 1);
397         assert_eq!(_2.to_integer(), 2);
398         assert_eq!(_1_2.to_integer(), 0);
399         assert_eq!(_3_2.to_integer(), 1);
400         assert_eq!(_neg1_2.to_integer(), 0);
401     }
402
403
404     #[test]
405     fn test_numer() {
406         assert_eq!(_0.numer(), &0);
407         assert_eq!(_1.numer(), &1);
408         assert_eq!(_2.numer(), &2);
409         assert_eq!(_1_2.numer(), &1);
410         assert_eq!(_3_2.numer(), &3);
411         assert_eq!(_neg1_2.numer(), &(-1));
412     }
413     #[test]
414     fn test_denom() {
415         assert_eq!(_0.denom(), &1);
416         assert_eq!(_1.denom(), &1);
417         assert_eq!(_2.denom(), &1);
418         assert_eq!(_1_2.denom(), &2);
419         assert_eq!(_3_2.denom(), &2);
420         assert_eq!(_neg1_2.denom(), &2);
421     }
422
423
424     #[test]
425     fn test_is_integer() {
426         assert!(_0.is_integer());
427         assert!(_1.is_integer());
428         assert!(_2.is_integer());
429         assert!(!_1_2.is_integer());
430         assert!(!_3_2.is_integer());
431         assert!(!_neg1_2.is_integer());
432     }
433
434
435     mod arith {
436         use super::{_0, _1, _2, _1_2, _3_2, _neg1_2, to_big};
437         use super::super::{Ratio, Rational};
438
439         #[test]
440         fn test_add() {
441             fn test(a: Rational, b: Rational, c: Rational) {
442                 assert_eq!(a + b, c);
443                 assert_eq!(to_big(a) + to_big(b), to_big(c));
444             }
445
446             test(_1, _1_2, _3_2);
447             test(_1, _1, _2);
448             test(_1_2, _3_2, _2);
449             test(_1_2, _neg1_2, _0);
450         }
451
452         #[test]
453         fn test_sub() {
454             fn test(a: Rational, b: Rational, c: Rational) {
455                 assert_eq!(a - b, c);
456                 assert_eq!(to_big(a) - to_big(b), to_big(c))
457             }
458
459             test(_1, _1_2, _1_2);
460             test(_3_2, _1_2, _1);
461             test(_1, _neg1_2, _3_2);
462         }
463
464         #[test]
465         fn test_mul() {
466             fn test(a: Rational, b: Rational, c: Rational) {
467                 assert_eq!(a * b, c);
468                 assert_eq!(to_big(a) * to_big(b), to_big(c))
469             }
470
471             test(_1, _1_2, _1_2);
472             test(_1_2, _3_2, Ratio::new(3,4));
473             test(_1_2, _neg1_2, Ratio::new(-1, 4));
474         }
475
476         #[test]
477         fn test_div() {
478             fn test(a: Rational, b: Rational, c: Rational) {
479                 assert_eq!(a / b, c);
480                 assert_eq!(to_big(a) / to_big(b), to_big(c))
481             }
482
483             test(_1, _1_2, _2);
484             test(_3_2, _1_2, _1 + _2);
485             test(_1, _neg1_2, _neg1_2 + _neg1_2 + _neg1_2 + _neg1_2);
486         }
487
488         #[test]
489         fn test_rem() {
490             fn test(a: Rational, b: Rational, c: Rational) {
491                 assert_eq!(a % b, c);
492                 assert_eq!(to_big(a) % to_big(b), to_big(c))
493             }
494
495             test(_3_2, _1, _1_2);
496             test(_2, _neg1_2, _0);
497             test(_1_2, _2,  _1_2);
498         }
499
500         #[test]
501         fn test_neg() {
502             fn test(a: Rational, b: Rational) {
503                 assert_eq!(-a, b);
504                 assert_eq!(-to_big(a), to_big(b))
505             }
506
507             test(_0, _0);
508             test(_1_2, _neg1_2);
509             test(-_1, _1);
510         }
511         #[test]
512         fn test_zero() {
513             assert_eq!(_0 + _0, _0);
514             assert_eq!(_0 * _0, _0);
515             assert_eq!(_0 * _1, _0);
516             assert_eq!(_0 / _neg1_2, _0);
517             assert_eq!(_0 - _0, _0);
518         }
519         #[test]
520         #[should_fail]
521         fn test_div_0() {
522             let _a =  _1 / _0;
523         }
524     }
525
526     #[test]
527     fn test_round() {
528         assert_eq!(_1_2.ceil(), _1);
529         assert_eq!(_1_2.floor(), _0);
530         assert_eq!(_1_2.round(), _1);
531         assert_eq!(_1_2.trunc(), _0);
532
533         assert_eq!(_neg1_2.ceil(), _0);
534         assert_eq!(_neg1_2.floor(), -_1);
535         assert_eq!(_neg1_2.round(), -_1);
536         assert_eq!(_neg1_2.trunc(), _0);
537
538         assert_eq!(_1.ceil(), _1);
539         assert_eq!(_1.floor(), _1);
540         assert_eq!(_1.round(), _1);
541         assert_eq!(_1.trunc(), _1);
542     }
543
544     #[test]
545     fn test_fract() {
546         assert_eq!(_1.fract(), _0);
547         assert_eq!(_neg1_2.fract(), _neg1_2);
548         assert_eq!(_1_2.fract(), _1_2);
549         assert_eq!(_3_2.fract(), _1_2);
550     }
551
552     #[test]
553     fn test_recip() {
554         assert_eq!(_1 * _1.recip(), _1);
555         assert_eq!(_2 * _2.recip(), _1);
556         assert_eq!(_1_2 * _1_2.recip(), _1);
557         assert_eq!(_3_2 * _3_2.recip(), _1);
558         assert_eq!(_neg1_2 * _neg1_2.recip(), _1);
559     }
560
561     #[test]
562     fn test_to_from_str() {
563         fn test(r: Rational, s: ~str) {
564             assert_eq!(FromStr::from_str(s), Some(r));
565             assert_eq!(r.to_str(), s);
566         }
567         test(_1, ~"1/1");
568         test(_0, ~"0/1");
569         test(_1_2, ~"1/2");
570         test(_3_2, ~"3/2");
571         test(_2, ~"2/1");
572         test(_neg1_2, ~"-1/2");
573     }
574     #[test]
575     fn test_from_str_fail() {
576         fn test(s: &str) {
577             let rational: Option<Rational> = FromStr::from_str(s);
578             assert_eq!(rational, None);
579         }
580
581         let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1"];
582         for &s in xs.iter() {
583             test(s);
584         }
585     }
586
587     #[test]
588     fn test_to_from_str_radix() {
589         fn test(r: Rational, s: ~str, n: uint) {
590             assert_eq!(FromStrRadix::from_str_radix(s, n), Some(r));
591             assert_eq!(r.to_str_radix(n), s);
592         }
593         fn test3(r: Rational, s: ~str) { test(r, s, 3) }
594         fn test16(r: Rational, s: ~str) { test(r, s, 16) }
595
596         test3(_1, ~"1/1");
597         test3(_0, ~"0/1");
598         test3(_1_2, ~"1/2");
599         test3(_3_2, ~"10/2");
600         test3(_2, ~"2/1");
601         test3(_neg1_2, ~"-1/2");
602         test3(_neg1_2 / _2, ~"-1/11");
603
604         test16(_1, ~"1/1");
605         test16(_0, ~"0/1");
606         test16(_1_2, ~"1/2");
607         test16(_3_2, ~"3/2");
608         test16(_2, ~"2/1");
609         test16(_neg1_2, ~"-1/2");
610         test16(_neg1_2 / _2, ~"-1/4");
611         test16(Ratio::new(13,15), ~"d/f");
612         test16(_1_2*_1_2*_1_2*_1_2, ~"1/10");
613     }
614
615     #[test]
616     fn test_from_str_radix_fail() {
617         fn test(s: &str) {
618             let radix: Option<Rational> = FromStrRadix::from_str_radix(s, 3);
619             assert_eq!(radix, None);
620         }
621
622         let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1", "3/2"];
623         for &s in xs.iter() {
624             test(s);
625         }
626     }
627
628     #[test]
629     fn test_from_float() {
630         fn test<T: Float>(given: T, (numer, denom): (&str, &str)) {
631             let ratio: BigRational = Ratio::from_float(given).unwrap();
632             assert_eq!(ratio, Ratio::new(
633                 FromStr::from_str(numer).unwrap(),
634                 FromStr::from_str(denom).unwrap()));
635         }
636
637         // f32
638         test(3.14159265359f32, ("13176795", "4194304"));
639         test(2f32.powf(&100.), ("1267650600228229401496703205376", "1"));
640         test(-2f32.powf(&100.), ("-1267650600228229401496703205376", "1"));
641         test(1.0 / 2f32.powf(&100.), ("1", "1267650600228229401496703205376"));
642         test(684729.48391f32, ("1369459", "2"));
643         test(-8573.5918555f32, ("-4389679", "512"));
644
645         // f64
646         test(3.14159265359f64, ("3537118876014453", "1125899906842624"));
647         test(2f64.powf(&100.), ("1267650600228229401496703205376", "1"));
648         test(-2f64.powf(&100.), ("-1267650600228229401496703205376", "1"));
649         test(684729.48391f64, ("367611342500051", "536870912"));
650         test(-8573.5918555, ("-4713381968463931", "549755813888"));
651         test(1.0 / 2f64.powf(&100.), ("1", "1267650600228229401496703205376"));
652     }
653
654     #[test]
655     fn test_from_float_fail() {
656         use std::{f32, f64};
657
658         assert_eq!(Ratio::from_float(f32::NAN), None);
659         assert_eq!(Ratio::from_float(f32::INFINITY), None);
660         assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None);
661         assert_eq!(Ratio::from_float(f64::NAN), None);
662         assert_eq!(Ratio::from_float(f64::INFINITY), None);
663         assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None);
664     }
665 }