blob: 0d3ea2f04f11e58804e00452894680b1751cf669 [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
17def _gcd(a, b):
18 """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 """
43
44 if x == 0:
45 return 0, 1
46 f, e = math.frexp(x)
47 signbit = 1
48 if f < 0:
49 f = -f
50 signbit = -1
51 assert 0.5 <= f < 1.0
52 # x = signbit * f * 2**e exactly
53
54 # Suck up CHUNK bits at a time; 28 is enough so that we suck
55 # up all bits in 2 iterations for all known binary double-
56 # precision formats, and small enough to fit in an int.
57 CHUNK = 28
58 top = 0
59 # invariant: x = signbit * (top + f) * 2**e exactly
60 while f:
61 f = math.ldexp(f, CHUNK)
62 digit = trunc(f)
63 assert digit >> CHUNK == 0
64 top = (top << CHUNK) | digit
65 f = f - digit
66 assert 0.0 <= f < 1.0
67 e = e - CHUNK
68 assert top
69
70 # Add in the sign bit.
71 top = signbit * top
72
73 # now x = top * 2**e exactly; fold in 2**e
74 if e>0:
75 return (top * 2**e, 1)
76 else:
77 return (top, 2 ** -e)
78
79
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000080_RATIONAL_FORMAT = re.compile(
81 r'^\s*(?P<sign>[-+]?)(?P<num>\d+)(?:/(?P<denom>\d+))?\s*$')
82
83
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000084class Rational(RationalAbc):
85 """This class implements rational numbers.
86
87 Rational(8, 6) will produce a rational number equivalent to
88 4/3. Both arguments must be Integral. The numerator defaults to 0
89 and the denominator defaults to 1 so that Rational(3) == 3 and
90 Rational() == 0.
91
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000092 Rationals can also be constructed from strings of the form
93 '[-+]?[0-9]+(/[0-9]+)?', optionally surrounded by spaces.
94
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000095 """
96
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +000097 __slots__ = ('numerator', 'denominator')
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000098
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000099 # We're immutable, so use __new__ not __init__
100 def __new__(cls, numerator=0, denominator=1):
101 """Constructs a Rational.
102
103 Takes a string, another Rational, or a numerator/denominator pair.
104
105 """
106 self = super(Rational, cls).__new__(cls)
107
108 if denominator == 1:
109 if isinstance(numerator, basestring):
110 # Handle construction from strings.
111 input = numerator
112 m = _RATIONAL_FORMAT.match(input)
113 if m is None:
114 raise ValueError('Invalid literal for Rational: ' + input)
115 numerator = int(m.group('num'))
116 # Default denominator to 1. That's the only optional group.
117 denominator = int(m.group('denom') or 1)
118 if m.group('sign') == '-':
119 numerator = -numerator
120
121 elif (not isinstance(numerator, numbers.Integral) and
122 isinstance(numerator, RationalAbc)):
123 # Handle copies from other rationals.
124 other_rational = numerator
125 numerator = other_rational.numerator
126 denominator = other_rational.denominator
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000127
128 if (not isinstance(numerator, numbers.Integral) or
129 not isinstance(denominator, numbers.Integral)):
130 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
131 " Both arguments must be integral." % locals())
132
133 if denominator == 0:
134 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
135
136 g = _gcd(numerator, denominator)
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +0000137 self.numerator = int(numerator // g)
138 self.denominator = int(denominator // g)
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000139 return self
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000140
141 @classmethod
142 def from_float(cls, f):
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000143 """Converts a finite float to a rational number, exactly.
144
145 Beware that Rational.from_float(0.3) != Rational(3, 10).
146
147 """
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000148 if not isinstance(f, float):
149 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
150 (cls.__name__, f, type(f).__name__))
151 if math.isnan(f) or math.isinf(f):
152 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
153 return cls(*_binary_float_to_ratio(f))
154
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000155 @classmethod
156 def from_decimal(cls, dec):
157 """Converts a finite Decimal instance to a rational number, exactly."""
158 from decimal import Decimal
159 if not isinstance(dec, Decimal):
160 raise TypeError(
161 "%s.from_decimal() only takes Decimals, not %r (%s)" %
162 (cls.__name__, dec, type(dec).__name__))
163 if not dec.is_finite():
164 # Catches infinities and nans.
165 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
166 sign, digits, exp = dec.as_tuple()
167 digits = int(''.join(map(str, digits)))
168 if sign:
169 digits = -digits
170 if exp >= 0:
171 return cls(digits * 10 ** exp)
172 else:
173 return cls(digits, 10 ** -exp)
174
Raymond Hettingercf109262008-01-24 00:54:21 +0000175 @classmethod
176 def from_continued_fraction(cls, seq):
177 'Build a Rational from a continued fraction expessed as a sequence'
178 n, d = 1, 0
179 for e in reversed(seq):
180 n, d = d, n
181 n += e * d
Raymond Hettingerf336c8b2008-01-24 02:05:06 +0000182 return cls(n, d) if seq else cls(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000183
184 def as_continued_fraction(self):
185 'Return continued fraction expressed as a list'
186 n = self.numerator
187 d = self.denominator
188 cf = []
189 while d:
190 e = int(n // d)
191 cf.append(e)
192 n -= e * d
193 n, d = d, n
194 return cf
195
196 @classmethod
197 def approximate_from_float(cls, f, max_denominator):
198 'Best rational approximation to f with a denominator <= max_denominator'
199 # XXX First cut at algorithm
200 # Still needs rounding rules as specified at
201 # http://en.wikipedia.org/wiki/Continued_fraction
202 cf = cls.from_float(f).as_continued_fraction()
Raymond Hettingereb461902008-01-24 02:00:25 +0000203 result = Rational(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000204 for i in range(1, len(cf)):
205 new = cls.from_continued_fraction(cf[:i])
206 if new.denominator > max_denominator:
207 break
208 result = new
209 return result
210
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000211 def __repr__(self):
212 """repr(self)"""
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000213 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000214
215 def __str__(self):
216 """str(self)"""
217 if self.denominator == 1:
218 return str(self.numerator)
219 else:
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000220 return '%s/%s' % (self.numerator, self.denominator)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000221
222 def _operator_fallbacks(monomorphic_operator, fallback_operator):
223 """Generates forward and reverse operators given a purely-rational
224 operator and a function from the operator module.
225
226 Use this like:
227 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
228
229 """
230 def forward(a, b):
231 if isinstance(b, RationalAbc):
232 # Includes ints.
233 return monomorphic_operator(a, b)
234 elif isinstance(b, float):
235 return fallback_operator(float(a), b)
236 elif isinstance(b, complex):
237 return fallback_operator(complex(a), b)
238 else:
239 return NotImplemented
240 forward.__name__ = '__' + fallback_operator.__name__ + '__'
241 forward.__doc__ = monomorphic_operator.__doc__
242
243 def reverse(b, a):
244 if isinstance(a, RationalAbc):
245 # Includes ints.
246 return monomorphic_operator(a, b)
247 elif isinstance(a, numbers.Real):
248 return fallback_operator(float(a), float(b))
249 elif isinstance(a, numbers.Complex):
250 return fallback_operator(complex(a), complex(b))
251 else:
252 return NotImplemented
253 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
254 reverse.__doc__ = monomorphic_operator.__doc__
255
256 return forward, reverse
257
258 def _add(a, b):
259 """a + b"""
260 return Rational(a.numerator * b.denominator +
261 b.numerator * a.denominator,
262 a.denominator * b.denominator)
263
264 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
265
266 def _sub(a, b):
267 """a - b"""
268 return Rational(a.numerator * b.denominator -
269 b.numerator * a.denominator,
270 a.denominator * b.denominator)
271
272 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
273
274 def _mul(a, b):
275 """a * b"""
276 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
277
278 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
279
280 def _div(a, b):
281 """a / b"""
282 return Rational(a.numerator * b.denominator,
283 a.denominator * b.numerator)
284
285 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
286 __div__, __rdiv__ = _operator_fallbacks(_div, operator.div)
287
Raymond Hettinger909e3342008-01-24 23:50:26 +0000288 def __floordiv__(a, b):
289 """a // b"""
290 # Will be math.floor(a / b) in 3.0.
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000291 div = a / b
292 if isinstance(div, RationalAbc):
293 # trunc(math.floor(div)) doesn't work if the rational is
294 # more precise than a float because the intermediate
295 # rounding may cross an integer boundary.
296 return div.numerator // div.denominator
297 else:
298 return math.floor(div)
299
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000300 def __rfloordiv__(b, a):
301 """a // b"""
302 # Will be math.floor(a / b) in 3.0.
Raymond Hettinger909e3342008-01-24 23:50:26 +0000303 div = a / b
304 if isinstance(div, RationalAbc):
305 # trunc(math.floor(div)) doesn't work if the rational is
306 # more precise than a float because the intermediate
307 # rounding may cross an integer boundary.
308 return div.numerator // div.denominator
309 else:
310 return math.floor(div)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000311
312 def __mod__(a, b):
313 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000314 div = a // b
315 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000316
317 def __rmod__(b, a):
318 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000319 div = a // b
320 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000321
322 def __pow__(a, b):
323 """a ** b
324
325 If b is not an integer, the result will be a float or complex
326 since roots are generally irrational. If b is an integer, the
327 result will be rational.
328
329 """
330 if isinstance(b, RationalAbc):
331 if b.denominator == 1:
332 power = b.numerator
333 if power >= 0:
334 return Rational(a.numerator ** power,
335 a.denominator ** power)
336 else:
337 return Rational(a.denominator ** -power,
338 a.numerator ** -power)
339 else:
340 # A fractional power will generally produce an
341 # irrational number.
342 return float(a) ** float(b)
343 else:
344 return float(a) ** b
345
346 def __rpow__(b, a):
347 """a ** b"""
348 if b.denominator == 1 and b.numerator >= 0:
349 # If a is an int, keep it that way if possible.
350 return a ** b.numerator
351
352 if isinstance(a, RationalAbc):
353 return Rational(a.numerator, a.denominator) ** b
354
355 if b.denominator == 1:
356 return a ** b.numerator
357
358 return a ** float(b)
359
360 def __pos__(a):
361 """+a: Coerces a subclass instance to Rational"""
362 return Rational(a.numerator, a.denominator)
363
364 def __neg__(a):
365 """-a"""
366 return Rational(-a.numerator, a.denominator)
367
368 def __abs__(a):
369 """abs(a)"""
370 return Rational(abs(a.numerator), a.denominator)
371
372 def __trunc__(a):
373 """trunc(a)"""
374 if a.numerator < 0:
375 return -(-a.numerator // a.denominator)
376 else:
377 return a.numerator // a.denominator
378
Raymond Hettinger5b0e27e2008-01-24 19:30:19 +0000379 __int__ = __trunc__
380
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000381 def __floor__(a):
382 """Will be math.floor(a) in 3.0."""
383 return a.numerator // a.denominator
384
385 def __ceil__(a):
386 """Will be math.ceil(a) in 3.0."""
387 # The negations cleverly convince floordiv to return the ceiling.
388 return -(-a.numerator // a.denominator)
389
390 def __round__(self, ndigits=None):
391 """Will be round(self, ndigits) in 3.0.
392
393 Rounds half toward even.
394 """
395 if ndigits is None:
396 floor, remainder = divmod(self.numerator, self.denominator)
397 if remainder * 2 < self.denominator:
398 return floor
399 elif remainder * 2 > self.denominator:
400 return floor + 1
401 # Deal with the half case:
402 elif floor % 2 == 0:
403 return floor
404 else:
405 return floor + 1
406 shift = 10**abs(ndigits)
407 # See _operator_fallbacks.forward to check that the results of
408 # these operations will always be Rational and therefore have
409 # __round__().
410 if ndigits > 0:
411 return Rational((self * shift).__round__(), shift)
412 else:
413 return Rational((self / shift).__round__() * shift)
414
415 def __hash__(self):
416 """hash(self)
417
418 Tricky because values that are exactly representable as a
419 float must have the same hash as that float.
420
421 """
422 if self.denominator == 1:
423 # Get integers right.
424 return hash(self.numerator)
425 # Expensive check, but definitely correct.
426 if self == float(self):
427 return hash(float(self))
428 else:
429 # Use tuple's hash to avoid a high collision rate on
430 # simple fractions.
431 return hash((self.numerator, self.denominator))
432
433 def __eq__(a, b):
434 """a == b"""
435 if isinstance(b, RationalAbc):
436 return (a.numerator == b.numerator and
437 a.denominator == b.denominator)
438 if isinstance(b, numbers.Complex) and b.imag == 0:
439 b = b.real
440 if isinstance(b, float):
441 return a == a.from_float(b)
442 else:
443 # XXX: If b.__eq__ is implemented like this method, it may
444 # give the wrong answer after float(a) changes a's
445 # value. Better ways of doing this are welcome.
446 return float(a) == b
447
448 def _subtractAndCompareToZero(a, b, op):
449 """Helper function for comparison operators.
450
451 Subtracts b from a, exactly if possible, and compares the
452 result with 0 using op, in such a way that the comparison
453 won't recurse. If the difference raises a TypeError, returns
454 NotImplemented instead.
455
456 """
457 if isinstance(b, numbers.Complex) and b.imag == 0:
458 b = b.real
459 if isinstance(b, float):
460 b = a.from_float(b)
461 try:
462 # XXX: If b <: Real but not <: RationalAbc, this is likely
463 # to fall back to a float. If the actual values differ by
464 # less than MIN_FLOAT, this could falsely call them equal,
465 # which would make <= inconsistent with ==. Better ways of
466 # doing this are welcome.
467 diff = a - b
468 except TypeError:
469 return NotImplemented
470 if isinstance(diff, RationalAbc):
471 return op(diff.numerator, 0)
472 return op(diff, 0)
473
474 def __lt__(a, b):
475 """a < b"""
476 return a._subtractAndCompareToZero(b, operator.lt)
477
478 def __gt__(a, b):
479 """a > b"""
480 return a._subtractAndCompareToZero(b, operator.gt)
481
482 def __le__(a, b):
483 """a <= b"""
484 return a._subtractAndCompareToZero(b, operator.le)
485
486 def __ge__(a, b):
487 """a >= b"""
488 return a._subtractAndCompareToZero(b, operator.ge)
489
490 def __nonzero__(a):
491 """a != 0"""
492 return a.numerator != 0