blob: e2083dba4afe95fb9a5e6042d2423a071539c577 [file] [log] [blame]
Jeffrey Yasskind7b00332008-01-15 07:46:24 +00001# Originally contributed by Sjoerd Mullender.
2# Significantly modified by Jeffrey Yasskin <jyasskin at gmail.com>.
3
4"""Rational, infinite-precision, real numbers."""
5
6from __future__ import division
7import math
8import numbers
9import operator
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000010import re
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000011
12__all__ = ["Rational"]
13
14RationalAbc = numbers.Rational
15
16
Raymond Hettinger7ea82252008-01-25 01:13:12 +000017def _gcd(a, b): # XXX This is a useful function. Consider making it public.
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000018 """Calculate the Greatest Common Divisor.
19
20 Unless b==0, the result will have the same sign as b (so that when
21 b is divided by it, the result comes out positive).
22 """
23 while b:
24 a, b = b, a%b
25 return a
26
27
28def _binary_float_to_ratio(x):
29 """x -> (top, bot), a pair of ints s.t. x = top/bot.
30
31 The conversion is done exactly, without rounding.
32 bot > 0 guaranteed.
33 Some form of binary fp is assumed.
34 Pass NaNs or infinities at your own risk.
35
36 >>> _binary_float_to_ratio(10.0)
37 (10, 1)
38 >>> _binary_float_to_ratio(0.0)
39 (0, 1)
40 >>> _binary_float_to_ratio(-.25)
41 (-1, 4)
42 """
Raymond Hettinger921cb5d2008-01-25 00:33:45 +000043 # XXX Consider moving this to to floatobject.c
44 # with a name like float.as_intger_ratio()
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000045
46 if x == 0:
47 return 0, 1
48 f, e = math.frexp(x)
49 signbit = 1
50 if f < 0:
51 f = -f
52 signbit = -1
53 assert 0.5 <= f < 1.0
54 # x = signbit * f * 2**e exactly
55
56 # Suck up CHUNK bits at a time; 28 is enough so that we suck
57 # up all bits in 2 iterations for all known binary double-
58 # precision formats, and small enough to fit in an int.
59 CHUNK = 28
60 top = 0
61 # invariant: x = signbit * (top + f) * 2**e exactly
62 while f:
63 f = math.ldexp(f, CHUNK)
64 digit = trunc(f)
65 assert digit >> CHUNK == 0
66 top = (top << CHUNK) | digit
67 f = f - digit
68 assert 0.0 <= f < 1.0
69 e = e - CHUNK
70 assert top
71
72 # Add in the sign bit.
73 top = signbit * top
74
75 # now x = top * 2**e exactly; fold in 2**e
76 if e>0:
77 return (top * 2**e, 1)
78 else:
79 return (top, 2 ** -e)
80
81
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000082_RATIONAL_FORMAT = re.compile(
83 r'^\s*(?P<sign>[-+]?)(?P<num>\d+)(?:/(?P<denom>\d+))?\s*$')
84
Raymond Hettinger9ec7bc32008-01-25 01:46:33 +000085# XXX Consider accepting decimal strings as input since they are exact.
86# Rational("2.01") --> s="2.01" ; Rational.from_decimal(Decimal(s)) --> Rational(201, 100)"
87# If you want to avoid going through the decimal module, just parse the string directly:
88# s.partition('.') --> ('2', '.', '01') --> Rational(int('2'+'01'), 10**len('01')) --> Rational(201, 100)
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000089
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000090class Rational(RationalAbc):
91 """This class implements rational numbers.
92
93 Rational(8, 6) will produce a rational number equivalent to
94 4/3. Both arguments must be Integral. The numerator defaults to 0
95 and the denominator defaults to 1 so that Rational(3) == 3 and
96 Rational() == 0.
97
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000098 Rationals can also be constructed from strings of the form
99 '[-+]?[0-9]+(/[0-9]+)?', optionally surrounded by spaces.
100
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000101 """
102
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +0000103 __slots__ = ('numerator', 'denominator')
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000104
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000105 # We're immutable, so use __new__ not __init__
106 def __new__(cls, numerator=0, denominator=1):
107 """Constructs a Rational.
108
109 Takes a string, another Rational, or a numerator/denominator pair.
110
111 """
112 self = super(Rational, cls).__new__(cls)
113
114 if denominator == 1:
115 if isinstance(numerator, basestring):
116 # Handle construction from strings.
117 input = numerator
118 m = _RATIONAL_FORMAT.match(input)
119 if m is None:
120 raise ValueError('Invalid literal for Rational: ' + input)
121 numerator = int(m.group('num'))
122 # Default denominator to 1. That's the only optional group.
123 denominator = int(m.group('denom') or 1)
124 if m.group('sign') == '-':
125 numerator = -numerator
126
127 elif (not isinstance(numerator, numbers.Integral) and
128 isinstance(numerator, RationalAbc)):
129 # Handle copies from other rationals.
130 other_rational = numerator
131 numerator = other_rational.numerator
132 denominator = other_rational.denominator
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000133
134 if (not isinstance(numerator, numbers.Integral) or
135 not isinstance(denominator, numbers.Integral)):
136 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
137 " Both arguments must be integral." % locals())
138
139 if denominator == 0:
140 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
141
142 g = _gcd(numerator, denominator)
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +0000143 self.numerator = int(numerator // g)
144 self.denominator = int(denominator // g)
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000145 return self
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000146
147 @classmethod
148 def from_float(cls, f):
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000149 """Converts a finite float to a rational number, exactly.
150
151 Beware that Rational.from_float(0.3) != Rational(3, 10).
152
153 """
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000154 if not isinstance(f, float):
155 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
156 (cls.__name__, f, type(f).__name__))
157 if math.isnan(f) or math.isinf(f):
158 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
159 return cls(*_binary_float_to_ratio(f))
160
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000161 @classmethod
162 def from_decimal(cls, dec):
163 """Converts a finite Decimal instance to a rational number, exactly."""
164 from decimal import Decimal
165 if not isinstance(dec, Decimal):
166 raise TypeError(
167 "%s.from_decimal() only takes Decimals, not %r (%s)" %
168 (cls.__name__, dec, type(dec).__name__))
169 if not dec.is_finite():
170 # Catches infinities and nans.
171 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
172 sign, digits, exp = dec.as_tuple()
173 digits = int(''.join(map(str, digits)))
174 if sign:
175 digits = -digits
176 if exp >= 0:
177 return cls(digits * 10 ** exp)
178 else:
179 return cls(digits, 10 ** -exp)
180
Raymond Hettingercf109262008-01-24 00:54:21 +0000181 @classmethod
182 def from_continued_fraction(cls, seq):
183 'Build a Rational from a continued fraction expessed as a sequence'
184 n, d = 1, 0
185 for e in reversed(seq):
186 n, d = d, n
187 n += e * d
Raymond Hettingerf336c8b2008-01-24 02:05:06 +0000188 return cls(n, d) if seq else cls(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000189
190 def as_continued_fraction(self):
191 'Return continued fraction expressed as a list'
192 n = self.numerator
193 d = self.denominator
194 cf = []
195 while d:
196 e = int(n // d)
197 cf.append(e)
198 n -= e * d
199 n, d = d, n
200 return cf
201
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000202 def approximate(self, max_denominator):
203 'Best rational approximation with a denominator <= max_denominator'
Raymond Hettingercf109262008-01-24 00:54:21 +0000204 # XXX First cut at algorithm
205 # Still needs rounding rules as specified at
206 # http://en.wikipedia.org/wiki/Continued_fraction
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000207 if self.denominator <= max_denominator:
208 return self
209 cf = self.as_continued_fraction()
Raymond Hettingereb461902008-01-24 02:00:25 +0000210 result = Rational(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000211 for i in range(1, len(cf)):
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000212 new = self.from_continued_fraction(cf[:i])
Raymond Hettingercf109262008-01-24 00:54:21 +0000213 if new.denominator > max_denominator:
214 break
215 result = new
216 return result
217
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000218 def __repr__(self):
219 """repr(self)"""
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000220 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000221
222 def __str__(self):
223 """str(self)"""
224 if self.denominator == 1:
225 return str(self.numerator)
226 else:
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000227 return '%s/%s' % (self.numerator, self.denominator)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000228
Raymond Hettinger921cb5d2008-01-25 00:33:45 +0000229 """ XXX This section needs a lot more commentary
230
231 * Explain the typical sequence of checks, calls, and fallbacks.
232 * Explain the subtle reasons why this logic was needed.
233 * It is not clear how common cases are handled (for example, how
234 does the ratio of two huge integers get converted to a float
235 without overflowing the long-->float conversion.
236
237 """
238
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000239 def _operator_fallbacks(monomorphic_operator, fallback_operator):
240 """Generates forward and reverse operators given a purely-rational
241 operator and a function from the operator module.
242
243 Use this like:
244 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
245
246 """
247 def forward(a, b):
248 if isinstance(b, RationalAbc):
249 # Includes ints.
250 return monomorphic_operator(a, b)
251 elif isinstance(b, float):
252 return fallback_operator(float(a), b)
253 elif isinstance(b, complex):
254 return fallback_operator(complex(a), b)
255 else:
256 return NotImplemented
257 forward.__name__ = '__' + fallback_operator.__name__ + '__'
258 forward.__doc__ = monomorphic_operator.__doc__
259
260 def reverse(b, a):
261 if isinstance(a, RationalAbc):
262 # Includes ints.
263 return monomorphic_operator(a, b)
264 elif isinstance(a, numbers.Real):
265 return fallback_operator(float(a), float(b))
266 elif isinstance(a, numbers.Complex):
267 return fallback_operator(complex(a), complex(b))
268 else:
269 return NotImplemented
270 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
271 reverse.__doc__ = monomorphic_operator.__doc__
272
273 return forward, reverse
274
275 def _add(a, b):
276 """a + b"""
277 return Rational(a.numerator * b.denominator +
278 b.numerator * a.denominator,
279 a.denominator * b.denominator)
280
281 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
282
283 def _sub(a, b):
284 """a - b"""
285 return Rational(a.numerator * b.denominator -
286 b.numerator * a.denominator,
287 a.denominator * b.denominator)
288
289 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
290
291 def _mul(a, b):
292 """a * b"""
293 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
294
295 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
296
297 def _div(a, b):
298 """a / b"""
299 return Rational(a.numerator * b.denominator,
300 a.denominator * b.numerator)
301
302 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
303 __div__, __rdiv__ = _operator_fallbacks(_div, operator.div)
304
Raymond Hettinger909e3342008-01-24 23:50:26 +0000305 def __floordiv__(a, b):
306 """a // b"""
307 # Will be math.floor(a / b) in 3.0.
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000308 div = a / b
309 if isinstance(div, RationalAbc):
310 # trunc(math.floor(div)) doesn't work if the rational is
311 # more precise than a float because the intermediate
312 # rounding may cross an integer boundary.
313 return div.numerator // div.denominator
314 else:
315 return math.floor(div)
316
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000317 def __rfloordiv__(b, a):
318 """a // b"""
319 # Will be math.floor(a / b) in 3.0.
Raymond Hettinger909e3342008-01-24 23:50:26 +0000320 div = a / b
321 if isinstance(div, RationalAbc):
322 # trunc(math.floor(div)) doesn't work if the rational is
323 # more precise than a float because the intermediate
324 # rounding may cross an integer boundary.
325 return div.numerator // div.denominator
326 else:
327 return math.floor(div)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000328
329 def __mod__(a, b):
330 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000331 div = a // b
332 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000333
334 def __rmod__(b, a):
335 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000336 div = a // b
337 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000338
339 def __pow__(a, b):
340 """a ** b
341
342 If b is not an integer, the result will be a float or complex
343 since roots are generally irrational. If b is an integer, the
344 result will be rational.
345
346 """
347 if isinstance(b, RationalAbc):
348 if b.denominator == 1:
349 power = b.numerator
350 if power >= 0:
351 return Rational(a.numerator ** power,
352 a.denominator ** power)
353 else:
354 return Rational(a.denominator ** -power,
355 a.numerator ** -power)
356 else:
357 # A fractional power will generally produce an
358 # irrational number.
359 return float(a) ** float(b)
360 else:
361 return float(a) ** b
362
363 def __rpow__(b, a):
364 """a ** b"""
365 if b.denominator == 1 and b.numerator >= 0:
366 # If a is an int, keep it that way if possible.
367 return a ** b.numerator
368
369 if isinstance(a, RationalAbc):
370 return Rational(a.numerator, a.denominator) ** b
371
372 if b.denominator == 1:
373 return a ** b.numerator
374
375 return a ** float(b)
376
377 def __pos__(a):
378 """+a: Coerces a subclass instance to Rational"""
379 return Rational(a.numerator, a.denominator)
380
381 def __neg__(a):
382 """-a"""
383 return Rational(-a.numerator, a.denominator)
384
385 def __abs__(a):
386 """abs(a)"""
387 return Rational(abs(a.numerator), a.denominator)
388
389 def __trunc__(a):
390 """trunc(a)"""
391 if a.numerator < 0:
392 return -(-a.numerator // a.denominator)
393 else:
394 return a.numerator // a.denominator
395
Raymond Hettinger5b0e27e2008-01-24 19:30:19 +0000396 __int__ = __trunc__
397
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000398 def __floor__(a):
399 """Will be math.floor(a) in 3.0."""
400 return a.numerator // a.denominator
401
402 def __ceil__(a):
403 """Will be math.ceil(a) in 3.0."""
404 # The negations cleverly convince floordiv to return the ceiling.
405 return -(-a.numerator // a.denominator)
406
407 def __round__(self, ndigits=None):
408 """Will be round(self, ndigits) in 3.0.
409
410 Rounds half toward even.
411 """
412 if ndigits is None:
413 floor, remainder = divmod(self.numerator, self.denominator)
414 if remainder * 2 < self.denominator:
415 return floor
416 elif remainder * 2 > self.denominator:
417 return floor + 1
418 # Deal with the half case:
419 elif floor % 2 == 0:
420 return floor
421 else:
422 return floor + 1
423 shift = 10**abs(ndigits)
424 # See _operator_fallbacks.forward to check that the results of
425 # these operations will always be Rational and therefore have
426 # __round__().
427 if ndigits > 0:
428 return Rational((self * shift).__round__(), shift)
429 else:
430 return Rational((self / shift).__round__() * shift)
431
432 def __hash__(self):
433 """hash(self)
434
435 Tricky because values that are exactly representable as a
436 float must have the same hash as that float.
437
438 """
Raymond Hettinger921cb5d2008-01-25 00:33:45 +0000439 # XXX since this method is expensive, consider caching the result
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000440 if self.denominator == 1:
441 # Get integers right.
442 return hash(self.numerator)
443 # Expensive check, but definitely correct.
444 if self == float(self):
445 return hash(float(self))
446 else:
447 # Use tuple's hash to avoid a high collision rate on
448 # simple fractions.
449 return hash((self.numerator, self.denominator))
450
451 def __eq__(a, b):
452 """a == b"""
453 if isinstance(b, RationalAbc):
454 return (a.numerator == b.numerator and
455 a.denominator == b.denominator)
456 if isinstance(b, numbers.Complex) and b.imag == 0:
457 b = b.real
458 if isinstance(b, float):
459 return a == a.from_float(b)
460 else:
461 # XXX: If b.__eq__ is implemented like this method, it may
462 # give the wrong answer after float(a) changes a's
463 # value. Better ways of doing this are welcome.
464 return float(a) == b
465
466 def _subtractAndCompareToZero(a, b, op):
467 """Helper function for comparison operators.
468
469 Subtracts b from a, exactly if possible, and compares the
470 result with 0 using op, in such a way that the comparison
471 won't recurse. If the difference raises a TypeError, returns
472 NotImplemented instead.
473
474 """
475 if isinstance(b, numbers.Complex) and b.imag == 0:
476 b = b.real
477 if isinstance(b, float):
478 b = a.from_float(b)
479 try:
480 # XXX: If b <: Real but not <: RationalAbc, this is likely
481 # to fall back to a float. If the actual values differ by
482 # less than MIN_FLOAT, this could falsely call them equal,
483 # which would make <= inconsistent with ==. Better ways of
484 # doing this are welcome.
485 diff = a - b
486 except TypeError:
487 return NotImplemented
488 if isinstance(diff, RationalAbc):
489 return op(diff.numerator, 0)
490 return op(diff, 0)
491
492 def __lt__(a, b):
493 """a < b"""
494 return a._subtractAndCompareToZero(b, operator.lt)
495
496 def __gt__(a, b):
497 """a > b"""
498 return a._subtractAndCompareToZero(b, operator.gt)
499
500 def __le__(a, b):
501 """a <= b"""
502 return a._subtractAndCompareToZero(b, operator.le)
503
504 def __ge__(a, b):
505 """a >= b"""
506 return a._subtractAndCompareToZero(b, operator.ge)
507
508 def __nonzero__(a):
509 """a != 0"""
510 return a.numerator != 0
Raymond Hettingera6216742008-01-25 00:21:54 +0000511
512 # support for pickling, copy, and deepcopy
513
514 def __reduce__(self):
515 return (self.__class__, (str(self),))
516
517 def __copy__(self):
518 if type(self) == Rational:
519 return self # I'm immutable; therefore I am my own clone
520 return self.__class__(self.numerator, self.denominator)
521
522 def __deepcopy__(self, memo):
523 if type(self) == Rational:
524 return self # My components are also immutable
525 return self.__class__(self.numerator, self.denominator)