blob: 06002a3146f3e06520768097f41ee1879fc6c577 [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 Heimes587c2bf2008-01-19 16:21:02 +000027_RATIONAL_FORMAT = re.compile(
Christian Heimesaf98da12008-01-27 15:18:18 +000028 r'^\s*(?P<sign>[-+]?)(?P<num>\d+)'
29 r'(?:/(?P<denom>\d+)|\.(?P<decimal>\d+))?\s*$')
Christian Heimes587c2bf2008-01-19 16:21:02 +000030
31
Guido van Rossum7736b5b2008-01-15 21:44:53 +000032class Rational(RationalAbc):
33 """This class implements rational numbers.
34
35 Rational(8, 6) will produce a rational number equivalent to
36 4/3. Both arguments must be Integral. The numerator defaults to 0
37 and the denominator defaults to 1 so that Rational(3) == 3 and
38 Rational() == 0.
39
Christian Heimes587c2bf2008-01-19 16:21:02 +000040 Rationals can also be constructed from strings of the form
Christian Heimesaf98da12008-01-27 15:18:18 +000041 '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Christian Heimes587c2bf2008-01-19 16:21:02 +000042
Guido van Rossum7736b5b2008-01-15 21:44:53 +000043 """
44
Christian Heimes969fe572008-01-25 11:23:10 +000045 __slots__ = ('numerator', 'denominator')
Guido van Rossum7736b5b2008-01-15 21:44:53 +000046
Christian Heimes587c2bf2008-01-19 16:21:02 +000047 # We're immutable, so use __new__ not __init__
48 def __new__(cls, numerator=0, denominator=1):
49 """Constructs a Rational.
50
Christian Heimesaf98da12008-01-27 15:18:18 +000051 Takes a string like '3/2' or '1.5', another Rational, or a
52 numerator/denominator pair.
Christian Heimes587c2bf2008-01-19 16:21:02 +000053
54 """
55 self = super(Rational, cls).__new__(cls)
56
57 if denominator == 1:
58 if isinstance(numerator, str):
59 # Handle construction from strings.
60 input = numerator
61 m = _RATIONAL_FORMAT.match(input)
62 if m is None:
63 raise ValueError('Invalid literal for Rational: ' + input)
Christian Heimesaf98da12008-01-27 15:18:18 +000064 numerator = m.group('num')
65 decimal = m.group('decimal')
66 if decimal:
67 # The literal is a decimal number.
68 numerator = int(numerator + decimal)
69 denominator = 10**len(decimal)
70 else:
71 # The literal is an integer or fraction.
72 numerator = int(numerator)
73 # Default denominator to 1.
74 denominator = int(m.group('denom') or 1)
75
Christian Heimes587c2bf2008-01-19 16:21:02 +000076 if m.group('sign') == '-':
77 numerator = -numerator
78
79 elif (not isinstance(numerator, numbers.Integral) and
80 isinstance(numerator, RationalAbc)):
81 # Handle copies from other rationals.
82 other_rational = numerator
83 numerator = other_rational.numerator
84 denominator = other_rational.denominator
Guido van Rossum7736b5b2008-01-15 21:44:53 +000085
86 if (not isinstance(numerator, numbers.Integral) or
87 not isinstance(denominator, numbers.Integral)):
88 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
89 " Both arguments must be integral." % locals())
90
91 if denominator == 0:
92 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
93
Christian Heimesaf98da12008-01-27 15:18:18 +000094 g = gcd(numerator, denominator)
Christian Heimes969fe572008-01-25 11:23:10 +000095 self.numerator = int(numerator // g)
96 self.denominator = int(denominator // g)
Christian Heimes587c2bf2008-01-19 16:21:02 +000097 return self
Guido van Rossum7736b5b2008-01-15 21:44:53 +000098
99 @classmethod
100 def from_float(cls, f):
Christian Heimes587c2bf2008-01-19 16:21:02 +0000101 """Converts a finite float to a rational number, exactly.
102
103 Beware that Rational.from_float(0.3) != Rational(3, 10).
104
105 """
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000106 if not isinstance(f, float):
107 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
108 (cls.__name__, f, type(f).__name__))
109 if math.isnan(f) or math.isinf(f):
110 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
Christian Heimes26855632008-01-27 23:50:43 +0000111 return cls(*f.as_integer_ratio())
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000112
Christian Heimes587c2bf2008-01-19 16:21:02 +0000113 @classmethod
114 def from_decimal(cls, dec):
115 """Converts a finite Decimal instance to a rational number, exactly."""
116 from decimal import Decimal
117 if not isinstance(dec, Decimal):
118 raise TypeError(
119 "%s.from_decimal() only takes Decimals, not %r (%s)" %
120 (cls.__name__, dec, type(dec).__name__))
121 if not dec.is_finite():
122 # Catches infinities and nans.
123 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
124 sign, digits, exp = dec.as_tuple()
125 digits = int(''.join(map(str, digits)))
126 if sign:
127 digits = -digits
128 if exp >= 0:
129 return cls(digits * 10 ** exp)
130 else:
131 return cls(digits, 10 ** -exp)
132
Christian Heimesbbffeb62008-01-24 09:42:52 +0000133 @classmethod
134 def from_continued_fraction(cls, seq):
135 'Build a Rational from a continued fraction expessed as a sequence'
136 n, d = 1, 0
137 for e in reversed(seq):
138 n, d = d, n
139 n += e * d
140 return cls(n, d) if seq else cls(0)
141
142 def as_continued_fraction(self):
143 'Return continued fraction expressed as a list'
144 n = self.numerator
145 d = self.denominator
146 cf = []
147 while d:
148 e = int(n // d)
149 cf.append(e)
150 n -= e * d
151 n, d = d, n
152 return cf
153
Christian Heimes969fe572008-01-25 11:23:10 +0000154 def approximate(self, max_denominator):
155 'Best rational approximation with a denominator <= max_denominator'
Christian Heimesbbffeb62008-01-24 09:42:52 +0000156 # XXX First cut at algorithm
157 # Still needs rounding rules as specified at
158 # http://en.wikipedia.org/wiki/Continued_fraction
Christian Heimes969fe572008-01-25 11:23:10 +0000159 if self.denominator <= max_denominator:
160 return self
161 cf = self.as_continued_fraction()
Christian Heimesbbffeb62008-01-24 09:42:52 +0000162 result = Rational(0)
163 for i in range(1, len(cf)):
Christian Heimes969fe572008-01-25 11:23:10 +0000164 new = self.from_continued_fraction(cf[:i])
Christian Heimesbbffeb62008-01-24 09:42:52 +0000165 if new.denominator > max_denominator:
166 break
167 result = new
168 return result
169
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000170 def __repr__(self):
171 """repr(self)"""
Christian Heimes587c2bf2008-01-19 16:21:02 +0000172 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000173
174 def __str__(self):
175 """str(self)"""
176 if self.denominator == 1:
177 return str(self.numerator)
178 else:
Christian Heimes587c2bf2008-01-19 16:21:02 +0000179 return '%s/%s' % (self.numerator, self.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000180
181 def _operator_fallbacks(monomorphic_operator, fallback_operator):
182 """Generates forward and reverse operators given a purely-rational
183 operator and a function from the operator module.
184
185 Use this like:
186 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
187
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000188 In general, we want to implement the arithmetic operations so
189 that mixed-mode operations either call an implementation whose
190 author knew about the types of both arguments, or convert both
191 to the nearest built in type and do the operation there. In
192 Rational, that means that we define __add__ and __radd__ as:
193
194 def __add__(self, other):
195 if isinstance(other, (int, Rational)):
196 # Do the real operation.
197 return Rational(self.numerator * other.denominator +
198 other.numerator * self.denominator,
199 self.denominator * other.denominator)
200 # float and complex don't follow this protocol, and
201 # Rational knows about them, so special case them.
202 elif isinstance(other, float):
203 return float(self) + other
204 elif isinstance(other, complex):
205 return complex(self) + other
206 else:
207 # Let the other type take over.
208 return NotImplemented
209
210 def __radd__(self, other):
211 # radd handles more types than add because there's
212 # nothing left to fall back to.
213 if isinstance(other, RationalAbc):
214 return Rational(self.numerator * other.denominator +
215 other.numerator * self.denominator,
216 self.denominator * other.denominator)
217 elif isinstance(other, Real):
218 return float(other) + float(self)
219 elif isinstance(other, Complex):
220 return complex(other) + complex(self)
221 else:
222 return NotImplemented
223
224
225 There are 5 different cases for a mixed-type addition on
226 Rational. I'll refer to all of the above code that doesn't
227 refer to Rational, float, or complex as "boilerplate". 'r'
228 will be an instance of Rational, which is a subtype of
229 RationalAbc (r : Rational <: RationalAbc), and b : B <:
230 Complex. The first three involve 'r + b':
231
232 1. If B <: Rational, int, float, or complex, we handle
233 that specially, and all is well.
234 2. If Rational falls back to the boilerplate code, and it
235 were to return a value from __add__, we'd miss the
236 possibility that B defines a more intelligent __radd__,
237 so the boilerplate should return NotImplemented from
238 __add__. In particular, we don't handle RationalAbc
239 here, even though we could get an exact answer, in case
240 the other type wants to do something special.
241 3. If B <: Rational, Python tries B.__radd__ before
242 Rational.__add__. This is ok, because it was
243 implemented with knowledge of Rational, so it can
244 handle those instances before delegating to Real or
245 Complex.
246
247 The next two situations describe 'b + r'. We assume that b
248 didn't know about Rational in its implementation, and that it
249 uses similar boilerplate code:
250
251 4. If B <: RationalAbc, then __radd_ converts both to the
252 builtin rational type (hey look, that's us) and
253 proceeds.
254 5. Otherwise, __radd__ tries to find the nearest common
255 base ABC, and fall back to its builtin type. Since this
256 class doesn't subclass a concrete type, there's no
257 implementation to fall back to, so we need to try as
258 hard as possible to return an actual value, or the user
259 will get a TypeError.
260
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000261 """
262 def forward(a, b):
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000263 if isinstance(b, (int, Rational)):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000264 return monomorphic_operator(a, b)
265 elif isinstance(b, float):
266 return fallback_operator(float(a), b)
267 elif isinstance(b, complex):
268 return fallback_operator(complex(a), b)
269 else:
270 return NotImplemented
271 forward.__name__ = '__' + fallback_operator.__name__ + '__'
272 forward.__doc__ = monomorphic_operator.__doc__
273
274 def reverse(b, a):
275 if isinstance(a, RationalAbc):
276 # Includes ints.
277 return monomorphic_operator(a, b)
278 elif isinstance(a, numbers.Real):
279 return fallback_operator(float(a), float(b))
280 elif isinstance(a, numbers.Complex):
281 return fallback_operator(complex(a), complex(b))
282 else:
283 return NotImplemented
284 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
285 reverse.__doc__ = monomorphic_operator.__doc__
286
287 return forward, reverse
288
289 def _add(a, b):
290 """a + b"""
291 return Rational(a.numerator * b.denominator +
292 b.numerator * a.denominator,
293 a.denominator * b.denominator)
294
295 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
296
297 def _sub(a, b):
298 """a - b"""
299 return Rational(a.numerator * b.denominator -
300 b.numerator * a.denominator,
301 a.denominator * b.denominator)
302
303 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
304
305 def _mul(a, b):
306 """a * b"""
307 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
308
309 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
310
311 def _div(a, b):
312 """a / b"""
313 return Rational(a.numerator * b.denominator,
314 a.denominator * b.numerator)
315
316 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000317
318 def __floordiv__(a, b):
319 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000320 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000321
322 def __rfloordiv__(b, a):
323 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000324 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000325
Christian Heimes969fe572008-01-25 11:23:10 +0000326 def __mod__(a, b):
327 """a % b"""
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000328 div = a // b
329 return a - b * div
330
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000331 def __rmod__(b, a):
332 """a % b"""
Christian Heimes969fe572008-01-25 11:23:10 +0000333 div = a // b
334 return a - b * div
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000335
336 def __pow__(a, b):
337 """a ** b
338
339 If b is not an integer, the result will be a float or complex
340 since roots are generally irrational. If b is an integer, the
341 result will be rational.
342
343 """
344 if isinstance(b, RationalAbc):
345 if b.denominator == 1:
346 power = b.numerator
347 if power >= 0:
348 return Rational(a.numerator ** power,
349 a.denominator ** power)
350 else:
351 return Rational(a.denominator ** -power,
352 a.numerator ** -power)
353 else:
354 # A fractional power will generally produce an
355 # irrational number.
356 return float(a) ** float(b)
357 else:
358 return float(a) ** b
359
360 def __rpow__(b, a):
361 """a ** b"""
362 if b.denominator == 1 and b.numerator >= 0:
363 # If a is an int, keep it that way if possible.
364 return a ** b.numerator
365
366 if isinstance(a, RationalAbc):
367 return Rational(a.numerator, a.denominator) ** b
368
369 if b.denominator == 1:
370 return a ** b.numerator
371
372 return a ** float(b)
373
374 def __pos__(a):
375 """+a: Coerces a subclass instance to Rational"""
376 return Rational(a.numerator, a.denominator)
377
378 def __neg__(a):
379 """-a"""
380 return Rational(-a.numerator, a.denominator)
381
382 def __abs__(a):
383 """abs(a)"""
384 return Rational(abs(a.numerator), a.denominator)
385
386 def __trunc__(a):
387 """trunc(a)"""
388 if a.numerator < 0:
389 return -(-a.numerator // a.denominator)
390 else:
391 return a.numerator // a.denominator
392
Christian Heimes969fe572008-01-25 11:23:10 +0000393 __int__ = __trunc__
394
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000395 def __floor__(a):
396 """Will be math.floor(a) in 3.0."""
397 return a.numerator // a.denominator
398
399 def __ceil__(a):
400 """Will be math.ceil(a) in 3.0."""
401 # The negations cleverly convince floordiv to return the ceiling.
402 return -(-a.numerator // a.denominator)
403
404 def __round__(self, ndigits=None):
405 """Will be round(self, ndigits) in 3.0.
406
407 Rounds half toward even.
408 """
409 if ndigits is None:
410 floor, remainder = divmod(self.numerator, self.denominator)
411 if remainder * 2 < self.denominator:
412 return floor
413 elif remainder * 2 > self.denominator:
414 return floor + 1
415 # Deal with the half case:
416 elif floor % 2 == 0:
417 return floor
418 else:
419 return floor + 1
420 shift = 10**abs(ndigits)
421 # See _operator_fallbacks.forward to check that the results of
422 # these operations will always be Rational and therefore have
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000423 # round().
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000424 if ndigits > 0:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000425 return Rational(round(self * shift), shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000426 else:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000427 return Rational(round(self / shift) * shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000428
429 def __hash__(self):
430 """hash(self)
431
432 Tricky because values that are exactly representable as a
433 float must have the same hash as that float.
434
435 """
Christian Heimes969fe572008-01-25 11:23:10 +0000436 # XXX since this method is expensive, consider caching the result
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000437 if self.denominator == 1:
438 # Get integers right.
439 return hash(self.numerator)
440 # Expensive check, but definitely correct.
441 if self == float(self):
442 return hash(float(self))
443 else:
444 # Use tuple's hash to avoid a high collision rate on
445 # simple fractions.
446 return hash((self.numerator, self.denominator))
447
448 def __eq__(a, b):
449 """a == b"""
450 if isinstance(b, RationalAbc):
451 return (a.numerator == b.numerator and
452 a.denominator == b.denominator)
453 if isinstance(b, numbers.Complex) and b.imag == 0:
454 b = b.real
455 if isinstance(b, float):
456 return a == a.from_float(b)
457 else:
458 # XXX: If b.__eq__ is implemented like this method, it may
459 # give the wrong answer after float(a) changes a's
460 # value. Better ways of doing this are welcome.
461 return float(a) == b
462
463 def _subtractAndCompareToZero(a, b, op):
464 """Helper function for comparison operators.
465
466 Subtracts b from a, exactly if possible, and compares the
467 result with 0 using op, in such a way that the comparison
468 won't recurse. If the difference raises a TypeError, returns
469 NotImplemented instead.
470
471 """
472 if isinstance(b, numbers.Complex) and b.imag == 0:
473 b = b.real
474 if isinstance(b, float):
475 b = a.from_float(b)
476 try:
477 # XXX: If b <: Real but not <: RationalAbc, this is likely
478 # to fall back to a float. If the actual values differ by
479 # less than MIN_FLOAT, this could falsely call them equal,
480 # which would make <= inconsistent with ==. Better ways of
481 # doing this are welcome.
482 diff = a - b
483 except TypeError:
484 return NotImplemented
485 if isinstance(diff, RationalAbc):
486 return op(diff.numerator, 0)
487 return op(diff, 0)
488
489 def __lt__(a, b):
490 """a < b"""
491 return a._subtractAndCompareToZero(b, operator.lt)
492
493 def __gt__(a, b):
494 """a > b"""
495 return a._subtractAndCompareToZero(b, operator.gt)
496
497 def __le__(a, b):
498 """a <= b"""
499 return a._subtractAndCompareToZero(b, operator.le)
500
501 def __ge__(a, b):
502 """a >= b"""
503 return a._subtractAndCompareToZero(b, operator.ge)
504
505 def __bool__(a):
506 """a != 0"""
507 return a.numerator != 0
Christian Heimes969fe572008-01-25 11:23:10 +0000508
509 # support for pickling, copy, and deepcopy
510
511 def __reduce__(self):
512 return (self.__class__, (str(self),))
513
514 def __copy__(self):
515 if type(self) == Rational:
516 return self # I'm immutable; therefore I am my own clone
517 return self.__class__(self.numerator, self.denominator)
518
519 def __deepcopy__(self, memo):
520 if type(self) == Rational:
521 return self # My components are also immutable
522 return self.__class__(self.numerator, self.denominator)