blob: da75ab1f2671d299bd6f8a636dbf7ffd1f073a2b [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
27def _binary_float_to_ratio(x):
28 """x -> (top, bot), a pair of ints s.t. x = top/bot.
29
30 The conversion is done exactly, without rounding.
31 bot > 0 guaranteed.
32 Some form of binary fp is assumed.
33 Pass NaNs or infinities at your own risk.
34
35 >>> _binary_float_to_ratio(10.0)
36 (10, 1)
37 >>> _binary_float_to_ratio(0.0)
38 (0, 1)
39 >>> _binary_float_to_ratio(-.25)
40 (-1, 4)
41 """
Christian Heimesaf98da12008-01-27 15:18:18 +000042 # XXX Move this to floatobject.c with a name like
43 # float.as_integer_ratio()
Guido van Rossum7736b5b2008-01-15 21:44:53 +000044
45 if x == 0:
46 return 0, 1
47 f, e = math.frexp(x)
48 signbit = 1
49 if f < 0:
50 f = -f
51 signbit = -1
52 assert 0.5 <= f < 1.0
53 # x = signbit * f * 2**e exactly
54
55 # Suck up CHUNK bits at a time; 28 is enough so that we suck
56 # up all bits in 2 iterations for all known binary double-
57 # precision formats, and small enough to fit in an int.
58 CHUNK = 28
59 top = 0
60 # invariant: x = signbit * (top + f) * 2**e exactly
61 while f:
62 f = math.ldexp(f, CHUNK)
63 digit = trunc(f)
64 assert digit >> CHUNK == 0
65 top = (top << CHUNK) | digit
66 f = f - digit
67 assert 0.0 <= f < 1.0
68 e = e - CHUNK
69 assert top
70
71 # Add in the sign bit.
72 top = signbit * top
73
74 # now x = top * 2**e exactly; fold in 2**e
75 if e>0:
76 return (top * 2**e, 1)
77 else:
78 return (top, 2 ** -e)
79
80
Christian Heimes587c2bf2008-01-19 16:21:02 +000081_RATIONAL_FORMAT = re.compile(
Christian Heimesaf98da12008-01-27 15:18:18 +000082 r'^\s*(?P<sign>[-+]?)(?P<num>\d+)'
83 r'(?:/(?P<denom>\d+)|\.(?P<decimal>\d+))?\s*$')
Christian Heimes587c2bf2008-01-19 16:21:02 +000084
85
Guido van Rossum7736b5b2008-01-15 21:44:53 +000086class Rational(RationalAbc):
87 """This class implements rational numbers.
88
89 Rational(8, 6) will produce a rational number equivalent to
90 4/3. Both arguments must be Integral. The numerator defaults to 0
91 and the denominator defaults to 1 so that Rational(3) == 3 and
92 Rational() == 0.
93
Christian Heimes587c2bf2008-01-19 16:21:02 +000094 Rationals can also be constructed from strings of the form
Christian Heimesaf98da12008-01-27 15:18:18 +000095 '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Christian Heimes587c2bf2008-01-19 16:21:02 +000096
Guido van Rossum7736b5b2008-01-15 21:44:53 +000097 """
98
Christian Heimes969fe572008-01-25 11:23:10 +000099 __slots__ = ('numerator', 'denominator')
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000100
Christian Heimes587c2bf2008-01-19 16:21:02 +0000101 # We're immutable, so use __new__ not __init__
102 def __new__(cls, numerator=0, denominator=1):
103 """Constructs a Rational.
104
Christian Heimesaf98da12008-01-27 15:18:18 +0000105 Takes a string like '3/2' or '1.5', another Rational, or a
106 numerator/denominator pair.
Christian Heimes587c2bf2008-01-19 16:21:02 +0000107
108 """
109 self = super(Rational, cls).__new__(cls)
110
111 if denominator == 1:
112 if isinstance(numerator, str):
113 # Handle construction from strings.
114 input = numerator
115 m = _RATIONAL_FORMAT.match(input)
116 if m is None:
117 raise ValueError('Invalid literal for Rational: ' + input)
Christian Heimesaf98da12008-01-27 15:18:18 +0000118 numerator = m.group('num')
119 decimal = m.group('decimal')
120 if decimal:
121 # The literal is a decimal number.
122 numerator = int(numerator + decimal)
123 denominator = 10**len(decimal)
124 else:
125 # The literal is an integer or fraction.
126 numerator = int(numerator)
127 # Default denominator to 1.
128 denominator = int(m.group('denom') or 1)
129
Christian Heimes587c2bf2008-01-19 16:21:02 +0000130 if m.group('sign') == '-':
131 numerator = -numerator
132
133 elif (not isinstance(numerator, numbers.Integral) and
134 isinstance(numerator, RationalAbc)):
135 # Handle copies from other rationals.
136 other_rational = numerator
137 numerator = other_rational.numerator
138 denominator = other_rational.denominator
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000139
140 if (not isinstance(numerator, numbers.Integral) or
141 not isinstance(denominator, numbers.Integral)):
142 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
143 " Both arguments must be integral." % locals())
144
145 if denominator == 0:
146 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
147
Christian Heimesaf98da12008-01-27 15:18:18 +0000148 g = gcd(numerator, denominator)
Christian Heimes969fe572008-01-25 11:23:10 +0000149 self.numerator = int(numerator // g)
150 self.denominator = int(denominator // g)
Christian Heimes587c2bf2008-01-19 16:21:02 +0000151 return self
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000152
153 @classmethod
154 def from_float(cls, f):
Christian Heimes587c2bf2008-01-19 16:21:02 +0000155 """Converts a finite float to a rational number, exactly.
156
157 Beware that Rational.from_float(0.3) != Rational(3, 10).
158
159 """
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000160 if not isinstance(f, float):
161 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
162 (cls.__name__, f, type(f).__name__))
163 if math.isnan(f) or math.isinf(f):
164 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
165 return cls(*_binary_float_to_ratio(f))
166
Christian Heimes587c2bf2008-01-19 16:21:02 +0000167 @classmethod
168 def from_decimal(cls, dec):
169 """Converts a finite Decimal instance to a rational number, exactly."""
170 from decimal import Decimal
171 if not isinstance(dec, Decimal):
172 raise TypeError(
173 "%s.from_decimal() only takes Decimals, not %r (%s)" %
174 (cls.__name__, dec, type(dec).__name__))
175 if not dec.is_finite():
176 # Catches infinities and nans.
177 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
178 sign, digits, exp = dec.as_tuple()
179 digits = int(''.join(map(str, digits)))
180 if sign:
181 digits = -digits
182 if exp >= 0:
183 return cls(digits * 10 ** exp)
184 else:
185 return cls(digits, 10 ** -exp)
186
Christian Heimesbbffeb62008-01-24 09:42:52 +0000187 @classmethod
188 def from_continued_fraction(cls, seq):
189 'Build a Rational from a continued fraction expessed as a sequence'
190 n, d = 1, 0
191 for e in reversed(seq):
192 n, d = d, n
193 n += e * d
194 return cls(n, d) if seq else cls(0)
195
196 def as_continued_fraction(self):
197 'Return continued fraction expressed as a list'
198 n = self.numerator
199 d = self.denominator
200 cf = []
201 while d:
202 e = int(n // d)
203 cf.append(e)
204 n -= e * d
205 n, d = d, n
206 return cf
207
Christian Heimes969fe572008-01-25 11:23:10 +0000208 def approximate(self, max_denominator):
209 'Best rational approximation with a denominator <= max_denominator'
Christian Heimesbbffeb62008-01-24 09:42:52 +0000210 # XXX First cut at algorithm
211 # Still needs rounding rules as specified at
212 # http://en.wikipedia.org/wiki/Continued_fraction
Christian Heimes969fe572008-01-25 11:23:10 +0000213 if self.denominator <= max_denominator:
214 return self
215 cf = self.as_continued_fraction()
Christian Heimesbbffeb62008-01-24 09:42:52 +0000216 result = Rational(0)
217 for i in range(1, len(cf)):
Christian Heimes969fe572008-01-25 11:23:10 +0000218 new = self.from_continued_fraction(cf[:i])
Christian Heimesbbffeb62008-01-24 09:42:52 +0000219 if new.denominator > max_denominator:
220 break
221 result = new
222 return result
223
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000224 def __repr__(self):
225 """repr(self)"""
Christian Heimes587c2bf2008-01-19 16:21:02 +0000226 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000227
228 def __str__(self):
229 """str(self)"""
230 if self.denominator == 1:
231 return str(self.numerator)
232 else:
Christian Heimes587c2bf2008-01-19 16:21:02 +0000233 return '%s/%s' % (self.numerator, self.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000234
Christian Heimes969fe572008-01-25 11:23:10 +0000235 """ XXX This section needs a lot more commentary
236
237 * Explain the typical sequence of checks, calls, and fallbacks.
238 * Explain the subtle reasons why this logic was needed.
239 * It is not clear how common cases are handled (for example, how
240 does the ratio of two huge integers get converted to a float
241 without overflowing the long-->float conversion.
242
243 """
244
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000245 def _operator_fallbacks(monomorphic_operator, fallback_operator):
246 """Generates forward and reverse operators given a purely-rational
247 operator and a function from the operator module.
248
249 Use this like:
250 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
251
252 """
253 def forward(a, b):
254 if isinstance(b, RationalAbc):
255 # Includes ints.
256 return monomorphic_operator(a, b)
257 elif isinstance(b, float):
258 return fallback_operator(float(a), b)
259 elif isinstance(b, complex):
260 return fallback_operator(complex(a), b)
261 else:
262 return NotImplemented
263 forward.__name__ = '__' + fallback_operator.__name__ + '__'
264 forward.__doc__ = monomorphic_operator.__doc__
265
266 def reverse(b, a):
267 if isinstance(a, RationalAbc):
268 # Includes ints.
269 return monomorphic_operator(a, b)
270 elif isinstance(a, numbers.Real):
271 return fallback_operator(float(a), float(b))
272 elif isinstance(a, numbers.Complex):
273 return fallback_operator(complex(a), complex(b))
274 else:
275 return NotImplemented
276 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
277 reverse.__doc__ = monomorphic_operator.__doc__
278
279 return forward, reverse
280
281 def _add(a, b):
282 """a + b"""
283 return Rational(a.numerator * b.denominator +
284 b.numerator * a.denominator,
285 a.denominator * b.denominator)
286
287 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
288
289 def _sub(a, b):
290 """a - b"""
291 return Rational(a.numerator * b.denominator -
292 b.numerator * a.denominator,
293 a.denominator * b.denominator)
294
295 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
296
297 def _mul(a, b):
298 """a * b"""
299 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
300
301 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
302
303 def _div(a, b):
304 """a / b"""
305 return Rational(a.numerator * b.denominator,
306 a.denominator * b.numerator)
307
308 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000309
310 def __floordiv__(a, b):
311 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000312 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000313
314 def __rfloordiv__(b, a):
315 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000316 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000317
Christian Heimes969fe572008-01-25 11:23:10 +0000318 def __mod__(a, b):
319 """a % b"""
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000320 div = a // b
321 return a - b * div
322
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000323 def __rmod__(b, a):
324 """a % b"""
Christian Heimes969fe572008-01-25 11:23:10 +0000325 div = a // b
326 return a - b * div
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000327
328 def __pow__(a, b):
329 """a ** b
330
331 If b is not an integer, the result will be a float or complex
332 since roots are generally irrational. If b is an integer, the
333 result will be rational.
334
335 """
336 if isinstance(b, RationalAbc):
337 if b.denominator == 1:
338 power = b.numerator
339 if power >= 0:
340 return Rational(a.numerator ** power,
341 a.denominator ** power)
342 else:
343 return Rational(a.denominator ** -power,
344 a.numerator ** -power)
345 else:
346 # A fractional power will generally produce an
347 # irrational number.
348 return float(a) ** float(b)
349 else:
350 return float(a) ** b
351
352 def __rpow__(b, a):
353 """a ** b"""
354 if b.denominator == 1 and b.numerator >= 0:
355 # If a is an int, keep it that way if possible.
356 return a ** b.numerator
357
358 if isinstance(a, RationalAbc):
359 return Rational(a.numerator, a.denominator) ** b
360
361 if b.denominator == 1:
362 return a ** b.numerator
363
364 return a ** float(b)
365
366 def __pos__(a):
367 """+a: Coerces a subclass instance to Rational"""
368 return Rational(a.numerator, a.denominator)
369
370 def __neg__(a):
371 """-a"""
372 return Rational(-a.numerator, a.denominator)
373
374 def __abs__(a):
375 """abs(a)"""
376 return Rational(abs(a.numerator), a.denominator)
377
378 def __trunc__(a):
379 """trunc(a)"""
380 if a.numerator < 0:
381 return -(-a.numerator // a.denominator)
382 else:
383 return a.numerator // a.denominator
384
Christian Heimes969fe572008-01-25 11:23:10 +0000385 __int__ = __trunc__
386
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000387 def __floor__(a):
388 """Will be math.floor(a) in 3.0."""
389 return a.numerator // a.denominator
390
391 def __ceil__(a):
392 """Will be math.ceil(a) in 3.0."""
393 # The negations cleverly convince floordiv to return the ceiling.
394 return -(-a.numerator // a.denominator)
395
396 def __round__(self, ndigits=None):
397 """Will be round(self, ndigits) in 3.0.
398
399 Rounds half toward even.
400 """
401 if ndigits is None:
402 floor, remainder = divmod(self.numerator, self.denominator)
403 if remainder * 2 < self.denominator:
404 return floor
405 elif remainder * 2 > self.denominator:
406 return floor + 1
407 # Deal with the half case:
408 elif floor % 2 == 0:
409 return floor
410 else:
411 return floor + 1
412 shift = 10**abs(ndigits)
413 # See _operator_fallbacks.forward to check that the results of
414 # these operations will always be Rational and therefore have
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000415 # round().
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000416 if ndigits > 0:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000417 return Rational(round(self * shift), shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000418 else:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000419 return Rational(round(self / shift) * shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000420
421 def __hash__(self):
422 """hash(self)
423
424 Tricky because values that are exactly representable as a
425 float must have the same hash as that float.
426
427 """
Christian Heimes969fe572008-01-25 11:23:10 +0000428 # XXX since this method is expensive, consider caching the result
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000429 if self.denominator == 1:
430 # Get integers right.
431 return hash(self.numerator)
432 # Expensive check, but definitely correct.
433 if self == float(self):
434 return hash(float(self))
435 else:
436 # Use tuple's hash to avoid a high collision rate on
437 # simple fractions.
438 return hash((self.numerator, self.denominator))
439
440 def __eq__(a, b):
441 """a == b"""
442 if isinstance(b, RationalAbc):
443 return (a.numerator == b.numerator and
444 a.denominator == b.denominator)
445 if isinstance(b, numbers.Complex) and b.imag == 0:
446 b = b.real
447 if isinstance(b, float):
448 return a == a.from_float(b)
449 else:
450 # XXX: If b.__eq__ is implemented like this method, it may
451 # give the wrong answer after float(a) changes a's
452 # value. Better ways of doing this are welcome.
453 return float(a) == b
454
455 def _subtractAndCompareToZero(a, b, op):
456 """Helper function for comparison operators.
457
458 Subtracts b from a, exactly if possible, and compares the
459 result with 0 using op, in such a way that the comparison
460 won't recurse. If the difference raises a TypeError, returns
461 NotImplemented instead.
462
463 """
464 if isinstance(b, numbers.Complex) and b.imag == 0:
465 b = b.real
466 if isinstance(b, float):
467 b = a.from_float(b)
468 try:
469 # XXX: If b <: Real but not <: RationalAbc, this is likely
470 # to fall back to a float. If the actual values differ by
471 # less than MIN_FLOAT, this could falsely call them equal,
472 # which would make <= inconsistent with ==. Better ways of
473 # doing this are welcome.
474 diff = a - b
475 except TypeError:
476 return NotImplemented
477 if isinstance(diff, RationalAbc):
478 return op(diff.numerator, 0)
479 return op(diff, 0)
480
481 def __lt__(a, b):
482 """a < b"""
483 return a._subtractAndCompareToZero(b, operator.lt)
484
485 def __gt__(a, b):
486 """a > b"""
487 return a._subtractAndCompareToZero(b, operator.gt)
488
489 def __le__(a, b):
490 """a <= b"""
491 return a._subtractAndCompareToZero(b, operator.le)
492
493 def __ge__(a, b):
494 """a >= b"""
495 return a._subtractAndCompareToZero(b, operator.ge)
496
497 def __bool__(a):
498 """a != 0"""
499 return a.numerator != 0
Christian Heimes969fe572008-01-25 11:23:10 +0000500
501 # support for pickling, copy, and deepcopy
502
503 def __reduce__(self):
504 return (self.__class__, (str(self),))
505
506 def __copy__(self):
507 if type(self) == Rational:
508 return self # I'm immutable; therefore I am my own clone
509 return self.__class__(self.numerator, self.denominator)
510
511 def __deepcopy__(self, memo):
512 if type(self) == Rational:
513 return self # My components are also immutable
514 return self.__class__(self.numerator, self.denominator)