blob: 6002964a88c8fc7f5c8bccf535d07c046f15f026 [file] [log] [blame]
Guido van Rossum7736b5b2008-01-15 21:44:53 +00001# Originally contributed by Sjoerd Mullender.
2# Significantly modified by Jeffrey Yasskin <jyasskin at gmail.com>.
3
4"""Rational, infinite-precision, real numbers."""
5
Guido van Rossum7736b5b2008-01-15 21:44:53 +00006import math
7import numbers
8import operator
Christian Heimes587c2bf2008-01-19 16:21:02 +00009import re
Guido van Rossum7736b5b2008-01-15 21:44:53 +000010
11__all__ = ["Rational"]
12
13RationalAbc = numbers.Rational
14
15
Christian Heimesaf98da12008-01-27 15:18:18 +000016def gcd(a, b):
17 """Calculate the Greatest Common Divisor of a and b.
Guido van Rossum7736b5b2008-01-15 21:44:53 +000018
19 Unless b==0, the result will have the same sign as b (so that when
20 b is divided by it, the result comes out positive).
21 """
22 while b:
23 a, b = b, a%b
24 return a
25
26
Christian Heimes292d3512008-02-03 16:51:08 +000027_RATIONAL_FORMAT = re.compile(r"""
28 \A\s* # optional whitespace at the start, then
29 (?P<sign>[-+]?) # an optional sign, then
30 (?=\d|\.\d) # lookahead for digit or .digit
31 (?P<num>\d*) # numerator (possibly empty)
32 (?: # followed by an optional
33 /(?P<denom>\d+) # / and denominator
34 | # or
35 \.(?P<decimal>\d*) # decimal point and fractional part
36 )?
37 \s*\Z # and optional whitespace to finish
38""", re.VERBOSE)
Christian Heimes587c2bf2008-01-19 16:21:02 +000039
40
Guido van Rossum7736b5b2008-01-15 21:44:53 +000041class Rational(RationalAbc):
42 """This class implements rational numbers.
43
44 Rational(8, 6) will produce a rational number equivalent to
45 4/3. Both arguments must be Integral. The numerator defaults to 0
46 and the denominator defaults to 1 so that Rational(3) == 3 and
47 Rational() == 0.
48
Christian Heimes587c2bf2008-01-19 16:21:02 +000049 Rationals can also be constructed from strings of the form
Christian Heimesaf98da12008-01-27 15:18:18 +000050 '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Christian Heimes587c2bf2008-01-19 16:21:02 +000051
Guido van Rossum7736b5b2008-01-15 21:44:53 +000052 """
53
Christian Heimes400adb02008-02-01 08:12:03 +000054 __slots__ = ('_numerator', '_denominator')
Guido van Rossum7736b5b2008-01-15 21:44:53 +000055
Christian Heimes587c2bf2008-01-19 16:21:02 +000056 # We're immutable, so use __new__ not __init__
57 def __new__(cls, numerator=0, denominator=1):
58 """Constructs a Rational.
59
Christian Heimesaf98da12008-01-27 15:18:18 +000060 Takes a string like '3/2' or '1.5', another Rational, or a
61 numerator/denominator pair.
Christian Heimes587c2bf2008-01-19 16:21:02 +000062
63 """
64 self = super(Rational, cls).__new__(cls)
65
66 if denominator == 1:
67 if isinstance(numerator, str):
68 # Handle construction from strings.
69 input = numerator
70 m = _RATIONAL_FORMAT.match(input)
71 if m is None:
72 raise ValueError('Invalid literal for Rational: ' + input)
Christian Heimesaf98da12008-01-27 15:18:18 +000073 numerator = m.group('num')
74 decimal = m.group('decimal')
75 if decimal:
76 # The literal is a decimal number.
77 numerator = int(numerator + decimal)
78 denominator = 10**len(decimal)
79 else:
80 # The literal is an integer or fraction.
81 numerator = int(numerator)
82 # Default denominator to 1.
83 denominator = int(m.group('denom') or 1)
84
Christian Heimes587c2bf2008-01-19 16:21:02 +000085 if m.group('sign') == '-':
86 numerator = -numerator
87
88 elif (not isinstance(numerator, numbers.Integral) and
89 isinstance(numerator, RationalAbc)):
90 # Handle copies from other rationals.
91 other_rational = numerator
92 numerator = other_rational.numerator
93 denominator = other_rational.denominator
Guido van Rossum7736b5b2008-01-15 21:44:53 +000094
95 if (not isinstance(numerator, numbers.Integral) or
96 not isinstance(denominator, numbers.Integral)):
97 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
98 " Both arguments must be integral." % locals())
99
100 if denominator == 0:
101 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
102
Christian Heimesaf98da12008-01-27 15:18:18 +0000103 g = gcd(numerator, denominator)
Christian Heimes400adb02008-02-01 08:12:03 +0000104 self._numerator = int(numerator // g)
105 self._denominator = int(denominator // g)
Christian Heimes587c2bf2008-01-19 16:21:02 +0000106 return self
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000107
108 @classmethod
109 def from_float(cls, f):
Christian Heimes587c2bf2008-01-19 16:21:02 +0000110 """Converts a finite float to a rational number, exactly.
111
112 Beware that Rational.from_float(0.3) != Rational(3, 10).
113
114 """
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000115 if not isinstance(f, float):
116 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
117 (cls.__name__, f, type(f).__name__))
118 if math.isnan(f) or math.isinf(f):
119 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
Christian Heimes26855632008-01-27 23:50:43 +0000120 return cls(*f.as_integer_ratio())
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000121
Christian Heimes587c2bf2008-01-19 16:21:02 +0000122 @classmethod
123 def from_decimal(cls, dec):
124 """Converts a finite Decimal instance to a rational number, exactly."""
125 from decimal import Decimal
126 if not isinstance(dec, Decimal):
127 raise TypeError(
128 "%s.from_decimal() only takes Decimals, not %r (%s)" %
129 (cls.__name__, dec, type(dec).__name__))
130 if not dec.is_finite():
131 # Catches infinities and nans.
132 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
133 sign, digits, exp = dec.as_tuple()
134 digits = int(''.join(map(str, digits)))
135 if sign:
136 digits = -digits
137 if exp >= 0:
138 return cls(digits * 10 ** exp)
139 else:
140 return cls(digits, 10 ** -exp)
141
Christian Heimesbbffeb62008-01-24 09:42:52 +0000142 @classmethod
143 def from_continued_fraction(cls, seq):
144 'Build a Rational from a continued fraction expessed as a sequence'
145 n, d = 1, 0
146 for e in reversed(seq):
147 n, d = d, n
148 n += e * d
149 return cls(n, d) if seq else cls(0)
150
151 def as_continued_fraction(self):
152 'Return continued fraction expressed as a list'
153 n = self.numerator
154 d = self.denominator
155 cf = []
156 while d:
157 e = int(n // d)
158 cf.append(e)
159 n -= e * d
160 n, d = d, n
161 return cf
162
Christian Heimes969fe572008-01-25 11:23:10 +0000163 def approximate(self, max_denominator):
164 'Best rational approximation with a denominator <= max_denominator'
Christian Heimesbbffeb62008-01-24 09:42:52 +0000165 # XXX First cut at algorithm
166 # Still needs rounding rules as specified at
167 # http://en.wikipedia.org/wiki/Continued_fraction
Christian Heimes969fe572008-01-25 11:23:10 +0000168 if self.denominator <= max_denominator:
169 return self
170 cf = self.as_continued_fraction()
Christian Heimesbbffeb62008-01-24 09:42:52 +0000171 result = Rational(0)
172 for i in range(1, len(cf)):
Christian Heimes969fe572008-01-25 11:23:10 +0000173 new = self.from_continued_fraction(cf[:i])
Christian Heimesbbffeb62008-01-24 09:42:52 +0000174 if new.denominator > max_denominator:
175 break
176 result = new
177 return result
178
Christian Heimes400adb02008-02-01 08:12:03 +0000179 @property
180 def numerator(a):
181 return a._numerator
182
183 @property
184 def denominator(a):
185 return a._denominator
186
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000187 def __repr__(self):
188 """repr(self)"""
Christian Heimes587c2bf2008-01-19 16:21:02 +0000189 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000190
191 def __str__(self):
192 """str(self)"""
193 if self.denominator == 1:
194 return str(self.numerator)
195 else:
Christian Heimes587c2bf2008-01-19 16:21:02 +0000196 return '%s/%s' % (self.numerator, self.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000197
198 def _operator_fallbacks(monomorphic_operator, fallback_operator):
199 """Generates forward and reverse operators given a purely-rational
200 operator and a function from the operator module.
201
202 Use this like:
203 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
204
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000205 In general, we want to implement the arithmetic operations so
206 that mixed-mode operations either call an implementation whose
207 author knew about the types of both arguments, or convert both
208 to the nearest built in type and do the operation there. In
209 Rational, that means that we define __add__ and __radd__ as:
210
211 def __add__(self, other):
Christian Heimes400adb02008-02-01 08:12:03 +0000212 # Both types have numerators/denominator attributes,
213 # so do the operation directly
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000214 if isinstance(other, (int, Rational)):
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000215 return Rational(self.numerator * other.denominator +
216 other.numerator * self.denominator,
217 self.denominator * other.denominator)
Christian Heimes400adb02008-02-01 08:12:03 +0000218 # float and complex don't have those operations, but we
219 # know about those types, so special case them.
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000220 elif isinstance(other, float):
221 return float(self) + other
222 elif isinstance(other, complex):
223 return complex(self) + other
Christian Heimes400adb02008-02-01 08:12:03 +0000224 # Let the other type take over.
225 return NotImplemented
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000226
227 def __radd__(self, other):
228 # radd handles more types than add because there's
229 # nothing left to fall back to.
230 if isinstance(other, RationalAbc):
231 return Rational(self.numerator * other.denominator +
232 other.numerator * self.denominator,
233 self.denominator * other.denominator)
234 elif isinstance(other, Real):
235 return float(other) + float(self)
236 elif isinstance(other, Complex):
237 return complex(other) + complex(self)
Christian Heimes400adb02008-02-01 08:12:03 +0000238 return NotImplemented
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000239
240
241 There are 5 different cases for a mixed-type addition on
242 Rational. I'll refer to all of the above code that doesn't
243 refer to Rational, float, or complex as "boilerplate". 'r'
244 will be an instance of Rational, which is a subtype of
245 RationalAbc (r : Rational <: RationalAbc), and b : B <:
246 Complex. The first three involve 'r + b':
247
248 1. If B <: Rational, int, float, or complex, we handle
249 that specially, and all is well.
250 2. If Rational falls back to the boilerplate code, and it
251 were to return a value from __add__, we'd miss the
252 possibility that B defines a more intelligent __radd__,
253 so the boilerplate should return NotImplemented from
254 __add__. In particular, we don't handle RationalAbc
255 here, even though we could get an exact answer, in case
256 the other type wants to do something special.
257 3. If B <: Rational, Python tries B.__radd__ before
258 Rational.__add__. This is ok, because it was
259 implemented with knowledge of Rational, so it can
260 handle those instances before delegating to Real or
261 Complex.
262
263 The next two situations describe 'b + r'. We assume that b
264 didn't know about Rational in its implementation, and that it
265 uses similar boilerplate code:
266
267 4. If B <: RationalAbc, then __radd_ converts both to the
268 builtin rational type (hey look, that's us) and
269 proceeds.
270 5. Otherwise, __radd__ tries to find the nearest common
271 base ABC, and fall back to its builtin type. Since this
272 class doesn't subclass a concrete type, there's no
273 implementation to fall back to, so we need to try as
274 hard as possible to return an actual value, or the user
275 will get a TypeError.
276
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000277 """
278 def forward(a, b):
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000279 if isinstance(b, (int, Rational)):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000280 return monomorphic_operator(a, b)
281 elif isinstance(b, float):
282 return fallback_operator(float(a), b)
283 elif isinstance(b, complex):
284 return fallback_operator(complex(a), b)
285 else:
286 return NotImplemented
287 forward.__name__ = '__' + fallback_operator.__name__ + '__'
288 forward.__doc__ = monomorphic_operator.__doc__
289
290 def reverse(b, a):
291 if isinstance(a, RationalAbc):
292 # Includes ints.
293 return monomorphic_operator(a, b)
294 elif isinstance(a, numbers.Real):
295 return fallback_operator(float(a), float(b))
296 elif isinstance(a, numbers.Complex):
297 return fallback_operator(complex(a), complex(b))
298 else:
299 return NotImplemented
300 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
301 reverse.__doc__ = monomorphic_operator.__doc__
302
303 return forward, reverse
304
305 def _add(a, b):
306 """a + b"""
307 return Rational(a.numerator * b.denominator +
308 b.numerator * a.denominator,
309 a.denominator * b.denominator)
310
311 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
312
313 def _sub(a, b):
314 """a - b"""
315 return Rational(a.numerator * b.denominator -
316 b.numerator * a.denominator,
317 a.denominator * b.denominator)
318
319 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
320
321 def _mul(a, b):
322 """a * b"""
323 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
324
325 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
326
327 def _div(a, b):
328 """a / b"""
329 return Rational(a.numerator * b.denominator,
330 a.denominator * b.numerator)
331
332 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000333
334 def __floordiv__(a, b):
335 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000336 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000337
338 def __rfloordiv__(b, a):
339 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000340 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000341
Christian Heimes969fe572008-01-25 11:23:10 +0000342 def __mod__(a, b):
343 """a % b"""
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000344 div = a // b
345 return a - b * div
346
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000347 def __rmod__(b, a):
348 """a % b"""
Christian Heimes969fe572008-01-25 11:23:10 +0000349 div = a // b
350 return a - b * div
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000351
352 def __pow__(a, b):
353 """a ** b
354
355 If b is not an integer, the result will be a float or complex
356 since roots are generally irrational. If b is an integer, the
357 result will be rational.
358
359 """
360 if isinstance(b, RationalAbc):
361 if b.denominator == 1:
362 power = b.numerator
363 if power >= 0:
364 return Rational(a.numerator ** power,
365 a.denominator ** power)
366 else:
367 return Rational(a.denominator ** -power,
368 a.numerator ** -power)
369 else:
370 # A fractional power will generally produce an
371 # irrational number.
372 return float(a) ** float(b)
373 else:
374 return float(a) ** b
375
376 def __rpow__(b, a):
377 """a ** b"""
378 if b.denominator == 1 and b.numerator >= 0:
379 # If a is an int, keep it that way if possible.
380 return a ** b.numerator
381
382 if isinstance(a, RationalAbc):
383 return Rational(a.numerator, a.denominator) ** b
384
385 if b.denominator == 1:
386 return a ** b.numerator
387
388 return a ** float(b)
389
390 def __pos__(a):
391 """+a: Coerces a subclass instance to Rational"""
392 return Rational(a.numerator, a.denominator)
393
394 def __neg__(a):
395 """-a"""
396 return Rational(-a.numerator, a.denominator)
397
398 def __abs__(a):
399 """abs(a)"""
400 return Rational(abs(a.numerator), a.denominator)
401
402 def __trunc__(a):
403 """trunc(a)"""
404 if a.numerator < 0:
405 return -(-a.numerator // a.denominator)
406 else:
407 return a.numerator // a.denominator
408
409 def __floor__(a):
410 """Will be math.floor(a) in 3.0."""
411 return a.numerator // a.denominator
412
413 def __ceil__(a):
414 """Will be math.ceil(a) in 3.0."""
415 # The negations cleverly convince floordiv to return the ceiling.
416 return -(-a.numerator // a.denominator)
417
418 def __round__(self, ndigits=None):
419 """Will be round(self, ndigits) in 3.0.
420
421 Rounds half toward even.
422 """
423 if ndigits is None:
424 floor, remainder = divmod(self.numerator, self.denominator)
425 if remainder * 2 < self.denominator:
426 return floor
427 elif remainder * 2 > self.denominator:
428 return floor + 1
429 # Deal with the half case:
430 elif floor % 2 == 0:
431 return floor
432 else:
433 return floor + 1
434 shift = 10**abs(ndigits)
435 # See _operator_fallbacks.forward to check that the results of
436 # these operations will always be Rational and therefore have
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000437 # round().
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000438 if ndigits > 0:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000439 return Rational(round(self * shift), shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000440 else:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000441 return Rational(round(self / shift) * shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000442
443 def __hash__(self):
444 """hash(self)
445
446 Tricky because values that are exactly representable as a
447 float must have the same hash as that float.
448
449 """
Christian Heimes969fe572008-01-25 11:23:10 +0000450 # XXX since this method is expensive, consider caching the result
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000451 if self.denominator == 1:
452 # Get integers right.
453 return hash(self.numerator)
454 # Expensive check, but definitely correct.
455 if self == float(self):
456 return hash(float(self))
457 else:
458 # Use tuple's hash to avoid a high collision rate on
459 # simple fractions.
460 return hash((self.numerator, self.denominator))
461
462 def __eq__(a, b):
463 """a == b"""
464 if isinstance(b, RationalAbc):
465 return (a.numerator == b.numerator and
466 a.denominator == b.denominator)
467 if isinstance(b, numbers.Complex) and b.imag == 0:
468 b = b.real
469 if isinstance(b, float):
470 return a == a.from_float(b)
471 else:
472 # XXX: If b.__eq__ is implemented like this method, it may
473 # give the wrong answer after float(a) changes a's
474 # value. Better ways of doing this are welcome.
475 return float(a) == b
476
477 def _subtractAndCompareToZero(a, b, op):
478 """Helper function for comparison operators.
479
480 Subtracts b from a, exactly if possible, and compares the
481 result with 0 using op, in such a way that the comparison
482 won't recurse. If the difference raises a TypeError, returns
483 NotImplemented instead.
484
485 """
486 if isinstance(b, numbers.Complex) and b.imag == 0:
487 b = b.real
488 if isinstance(b, float):
489 b = a.from_float(b)
490 try:
491 # XXX: If b <: Real but not <: RationalAbc, this is likely
492 # to fall back to a float. If the actual values differ by
493 # less than MIN_FLOAT, this could falsely call them equal,
494 # which would make <= inconsistent with ==. Better ways of
495 # doing this are welcome.
496 diff = a - b
497 except TypeError:
498 return NotImplemented
499 if isinstance(diff, RationalAbc):
500 return op(diff.numerator, 0)
501 return op(diff, 0)
502
503 def __lt__(a, b):
504 """a < b"""
505 return a._subtractAndCompareToZero(b, operator.lt)
506
507 def __gt__(a, b):
508 """a > b"""
509 return a._subtractAndCompareToZero(b, operator.gt)
510
511 def __le__(a, b):
512 """a <= b"""
513 return a._subtractAndCompareToZero(b, operator.le)
514
515 def __ge__(a, b):
516 """a >= b"""
517 return a._subtractAndCompareToZero(b, operator.ge)
518
519 def __bool__(a):
520 """a != 0"""
521 return a.numerator != 0
Christian Heimes969fe572008-01-25 11:23:10 +0000522
523 # support for pickling, copy, and deepcopy
524
525 def __reduce__(self):
526 return (self.__class__, (str(self),))
527
528 def __copy__(self):
529 if type(self) == Rational:
530 return self # I'm immutable; therefore I am my own clone
531 return self.__class__(self.numerator, self.denominator)
532
533 def __deepcopy__(self, memo):
534 if type(self) == Rational:
535 return self # My components are also immutable
536 return self.__class__(self.numerator, self.denominator)