blob: 2222045add34fddbe1aa89d83deebd713d01b75e [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
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000017def gcd(a, b):
18 """Calculate the Greatest Common Divisor of a and b.
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000019
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
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000028_RATIONAL_FORMAT = re.compile(
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000029 r'^\s*(?P<sign>[-+]?)(?P<num>\d+)'
30 r'(?:/(?P<denom>\d+)|\.(?P<decimal>\d+))?\s*$')
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000031
32
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000033class Rational(RationalAbc):
34 """This class implements rational numbers.
35
36 Rational(8, 6) will produce a rational number equivalent to
37 4/3. Both arguments must be Integral. The numerator defaults to 0
38 and the denominator defaults to 1 so that Rational(3) == 3 and
39 Rational() == 0.
40
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000041 Rationals can also be constructed from strings of the form
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000042 '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000043
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000044 """
45
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +000046 __slots__ = ('numerator', 'denominator')
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000047
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000048 # We're immutable, so use __new__ not __init__
49 def __new__(cls, numerator=0, denominator=1):
50 """Constructs a Rational.
51
Raymond Hettinger63c77b62008-01-27 10:13:57 +000052 Takes a string like '3/2' or '1.5', another Rational, or a
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000053 numerator/denominator pair.
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000054
55 """
56 self = super(Rational, cls).__new__(cls)
57
58 if denominator == 1:
59 if isinstance(numerator, basestring):
60 # Handle construction from strings.
61 input = numerator
62 m = _RATIONAL_FORMAT.match(input)
63 if m is None:
64 raise ValueError('Invalid literal for Rational: ' + input)
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000065 numerator = m.group('num')
66 decimal = m.group('decimal')
67 if decimal:
68 # The literal is a decimal number.
69 numerator = int(numerator + decimal)
70 denominator = 10**len(decimal)
71 else:
72 # The literal is an integer or fraction.
73 numerator = int(numerator)
74 # Default denominator to 1.
75 denominator = int(m.group('denom') or 1)
76
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000077 if m.group('sign') == '-':
78 numerator = -numerator
79
80 elif (not isinstance(numerator, numbers.Integral) and
81 isinstance(numerator, RationalAbc)):
82 # Handle copies from other rationals.
83 other_rational = numerator
84 numerator = other_rational.numerator
85 denominator = other_rational.denominator
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000086
87 if (not isinstance(numerator, numbers.Integral) or
88 not isinstance(denominator, numbers.Integral)):
89 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
90 " Both arguments must be integral." % locals())
91
92 if denominator == 0:
93 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
94
Jeffrey Yasskin3e1a3732008-01-27 05:40:35 +000095 g = gcd(numerator, denominator)
Raymond Hettinger7a6eacd2008-01-24 18:05:54 +000096 self.numerator = int(numerator // g)
97 self.denominator = int(denominator // g)
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +000098 return self
Jeffrey Yasskind7b00332008-01-15 07:46:24 +000099
100 @classmethod
101 def from_float(cls, f):
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000102 """Converts a finite float to a rational number, exactly.
103
104 Beware that Rational.from_float(0.3) != Rational(3, 10).
105
106 """
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000107 if not isinstance(f, float):
108 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
109 (cls.__name__, f, type(f).__name__))
110 if math.isnan(f) or math.isinf(f):
111 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
Jeffrey Yasskin3ea7b412008-01-27 23:08:46 +0000112 return cls(*f.as_integer_ratio())
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000113
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000114 @classmethod
115 def from_decimal(cls, dec):
116 """Converts a finite Decimal instance to a rational number, exactly."""
117 from decimal import Decimal
118 if not isinstance(dec, Decimal):
119 raise TypeError(
120 "%s.from_decimal() only takes Decimals, not %r (%s)" %
121 (cls.__name__, dec, type(dec).__name__))
122 if not dec.is_finite():
123 # Catches infinities and nans.
124 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
125 sign, digits, exp = dec.as_tuple()
126 digits = int(''.join(map(str, digits)))
127 if sign:
128 digits = -digits
129 if exp >= 0:
130 return cls(digits * 10 ** exp)
131 else:
132 return cls(digits, 10 ** -exp)
133
Raymond Hettingercf109262008-01-24 00:54:21 +0000134 @classmethod
135 def from_continued_fraction(cls, seq):
136 'Build a Rational from a continued fraction expessed as a sequence'
137 n, d = 1, 0
138 for e in reversed(seq):
139 n, d = d, n
140 n += e * d
Raymond Hettingerf336c8b2008-01-24 02:05:06 +0000141 return cls(n, d) if seq else cls(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000142
143 def as_continued_fraction(self):
144 'Return continued fraction expressed as a list'
145 n = self.numerator
146 d = self.denominator
147 cf = []
148 while d:
149 e = int(n // d)
150 cf.append(e)
151 n -= e * d
152 n, d = d, n
153 return cf
154
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000155 def approximate(self, max_denominator):
156 'Best rational approximation with a denominator <= max_denominator'
Raymond Hettingercf109262008-01-24 00:54:21 +0000157 # XXX First cut at algorithm
158 # Still needs rounding rules as specified at
159 # http://en.wikipedia.org/wiki/Continued_fraction
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000160 if self.denominator <= max_denominator:
161 return self
162 cf = self.as_continued_fraction()
Raymond Hettingereb461902008-01-24 02:00:25 +0000163 result = Rational(0)
Raymond Hettingercf109262008-01-24 00:54:21 +0000164 for i in range(1, len(cf)):
Raymond Hettinger9c6d81f2008-01-25 01:23:38 +0000165 new = self.from_continued_fraction(cf[:i])
Raymond Hettingercf109262008-01-24 00:54:21 +0000166 if new.denominator > max_denominator:
167 break
168 result = new
169 return result
170
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000171 def __repr__(self):
172 """repr(self)"""
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000173 return ('Rational(%r,%r)' % (self.numerator, self.denominator))
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000174
175 def __str__(self):
176 """str(self)"""
177 if self.denominator == 1:
178 return str(self.numerator)
179 else:
Jeffrey Yasskin45169fb2008-01-19 09:56:06 +0000180 return '%s/%s' % (self.numerator, self.denominator)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000181
182 def _operator_fallbacks(monomorphic_operator, fallback_operator):
183 """Generates forward and reverse operators given a purely-rational
184 operator and a function from the operator module.
185
186 Use this like:
187 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
188
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000189 In general, we want to implement the arithmetic operations so
190 that mixed-mode operations either call an implementation whose
191 author knew about the types of both arguments, or convert both
192 to the nearest built in type and do the operation there. In
193 Rational, that means that we define __add__ and __radd__ as:
194
195 def __add__(self, other):
Raymond Hettinger2df20a32008-01-31 22:07:16 +0000196 # Both types have numerators/denominator attributes,
197 # so do the operation directly
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000198 if isinstance(other, (int, long, Rational)):
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000199 return Rational(self.numerator * other.denominator +
200 other.numerator * self.denominator,
201 self.denominator * other.denominator)
Raymond Hettinger2df20a32008-01-31 22:07:16 +0000202 # float and complex don't have those operations, but we
203 # know about those types, so special case them.
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000204 elif isinstance(other, float):
205 return float(self) + other
206 elif isinstance(other, complex):
207 return complex(self) + other
Raymond Hettinger2df20a32008-01-31 22:07:16 +0000208 # Let the other type take over.
209 return NotImplemented
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000210
211 def __radd__(self, other):
212 # radd handles more types than add because there's
213 # nothing left to fall back to.
214 if isinstance(other, RationalAbc):
215 return Rational(self.numerator * other.denominator +
216 other.numerator * self.denominator,
217 self.denominator * other.denominator)
218 elif isinstance(other, Real):
219 return float(other) + float(self)
220 elif isinstance(other, Complex):
221 return complex(other) + complex(self)
Raymond Hettinger2df20a32008-01-31 22:07:16 +0000222 return NotImplemented
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000223
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
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000261 """
262 def forward(a, b):
Jeffrey Yasskinb23dea62008-01-31 07:44:11 +0000263 if isinstance(b, (int, long, Rational)):
Jeffrey Yasskind7b00332008-01-15 07:46:24 +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)
317 __div__, __rdiv__ = _operator_fallbacks(_div, operator.div)
318
Raymond Hettinger909e3342008-01-24 23:50:26 +0000319 def __floordiv__(a, b):
320 """a // b"""
321 # Will be math.floor(a / b) in 3.0.
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000322 div = a / b
323 if isinstance(div, RationalAbc):
324 # trunc(math.floor(div)) doesn't work if the rational is
325 # more precise than a float because the intermediate
326 # rounding may cross an integer boundary.
327 return div.numerator // div.denominator
328 else:
329 return math.floor(div)
330
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000331 def __rfloordiv__(b, a):
332 """a // b"""
333 # Will be math.floor(a / b) in 3.0.
Raymond Hettinger909e3342008-01-24 23:50:26 +0000334 div = a / b
335 if isinstance(div, RationalAbc):
336 # trunc(math.floor(div)) doesn't work if the rational is
337 # more precise than a float because the intermediate
338 # rounding may cross an integer boundary.
339 return div.numerator // div.denominator
340 else:
341 return math.floor(div)
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000342
343 def __mod__(a, b):
344 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000345 div = a // b
346 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000347
348 def __rmod__(b, a):
349 """a % b"""
Raymond Hettinger909e3342008-01-24 23:50:26 +0000350 div = a // b
351 return a - b * div
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000352
353 def __pow__(a, b):
354 """a ** b
355
356 If b is not an integer, the result will be a float or complex
357 since roots are generally irrational. If b is an integer, the
358 result will be rational.
359
360 """
361 if isinstance(b, RationalAbc):
362 if b.denominator == 1:
363 power = b.numerator
364 if power >= 0:
365 return Rational(a.numerator ** power,
366 a.denominator ** power)
367 else:
368 return Rational(a.denominator ** -power,
369 a.numerator ** -power)
370 else:
371 # A fractional power will generally produce an
372 # irrational number.
373 return float(a) ** float(b)
374 else:
375 return float(a) ** b
376
377 def __rpow__(b, a):
378 """a ** b"""
379 if b.denominator == 1 and b.numerator >= 0:
380 # If a is an int, keep it that way if possible.
381 return a ** b.numerator
382
383 if isinstance(a, RationalAbc):
384 return Rational(a.numerator, a.denominator) ** b
385
386 if b.denominator == 1:
387 return a ** b.numerator
388
389 return a ** float(b)
390
391 def __pos__(a):
392 """+a: Coerces a subclass instance to Rational"""
393 return Rational(a.numerator, a.denominator)
394
395 def __neg__(a):
396 """-a"""
397 return Rational(-a.numerator, a.denominator)
398
399 def __abs__(a):
400 """abs(a)"""
401 return Rational(abs(a.numerator), a.denominator)
402
403 def __trunc__(a):
404 """trunc(a)"""
405 if a.numerator < 0:
406 return -(-a.numerator // a.denominator)
407 else:
408 return a.numerator // a.denominator
409
Raymond Hettinger5b0e27e2008-01-24 19:30:19 +0000410 __int__ = __trunc__
411
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000412 def __hash__(self):
413 """hash(self)
414
415 Tricky because values that are exactly representable as a
416 float must have the same hash as that float.
417
418 """
Raymond Hettinger921cb5d2008-01-25 00:33:45 +0000419 # XXX since this method is expensive, consider caching the result
Jeffrey Yasskind7b00332008-01-15 07:46:24 +0000420 if self.denominator == 1:
421 # Get integers right.
422 return hash(self.numerator)
423 # Expensive check, but definitely correct.
424 if self == float(self):
425 return hash(float(self))
426 else:
427 # Use tuple's hash to avoid a high collision rate on
428 # simple fractions.
429 return hash((self.numerator, self.denominator))
430
431 def __eq__(a, b):
432 """a == b"""
433 if isinstance(b, RationalAbc):
434 return (a.numerator == b.numerator and
435 a.denominator == b.denominator)
436 if isinstance(b, numbers.Complex) and b.imag == 0:
437 b = b.real
438 if isinstance(b, float):
439 return a == a.from_float(b)
440 else:
441 # XXX: If b.__eq__ is implemented like this method, it may
442 # give the wrong answer after float(a) changes a's
443 # value. Better ways of doing this are welcome.
444 return float(a) == b
445
446 def _subtractAndCompareToZero(a, b, op):
447 """Helper function for comparison operators.
448
449 Subtracts b from a, exactly if possible, and compares the
450 result with 0 using op, in such a way that the comparison
451 won't recurse. If the difference raises a TypeError, returns
452 NotImplemented instead.
453
454 """
455 if isinstance(b, numbers.Complex) and b.imag == 0:
456 b = b.real
457 if isinstance(b, float):
458 b = a.from_float(b)
459 try:
460 # XXX: If b <: Real but not <: RationalAbc, this is likely
461 # to fall back to a float. If the actual values differ by
462 # less than MIN_FLOAT, this could falsely call them equal,
463 # which would make <= inconsistent with ==. Better ways of
464 # doing this are welcome.
465 diff = a - b
466 except TypeError:
467 return NotImplemented
468 if isinstance(diff, RationalAbc):
469 return op(diff.numerator, 0)
470 return op(diff, 0)
471
472 def __lt__(a, b):
473 """a < b"""
474 return a._subtractAndCompareToZero(b, operator.lt)
475
476 def __gt__(a, b):
477 """a > b"""
478 return a._subtractAndCompareToZero(b, operator.gt)
479
480 def __le__(a, b):
481 """a <= b"""
482 return a._subtractAndCompareToZero(b, operator.le)
483
484 def __ge__(a, b):
485 """a >= b"""
486 return a._subtractAndCompareToZero(b, operator.ge)
487
488 def __nonzero__(a):
489 """a != 0"""
490 return a.numerator != 0
Raymond Hettingera6216742008-01-25 00:21:54 +0000491
492 # support for pickling, copy, and deepcopy
493
494 def __reduce__(self):
495 return (self.__class__, (str(self),))
496
497 def __copy__(self):
498 if type(self) == Rational:
499 return self # I'm immutable; therefore I am my own clone
500 return self.__class__(self.numerator, self.denominator)
501
502 def __deepcopy__(self, memo):
503 if type(self) == Rational:
504 return self # My components are also immutable
505 return self.__class__(self.numerator, self.denominator)