blob: c913ec7611874d54931d76e4b6d265f4e6eeb8c8 [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
9
10__all__ = ["Rational"]
11
12RationalAbc = numbers.Rational
13
14
15def _gcd(a, b):
16 """Calculate the Greatest Common Divisor.
17
18 Unless b==0, the result will have the same sign as b (so that when
19 b is divided by it, the result comes out positive).
20 """
21 while b:
22 a, b = b, a%b
23 return a
24
25
26def _binary_float_to_ratio(x):
27 """x -> (top, bot), a pair of ints s.t. x = top/bot.
28
29 The conversion is done exactly, without rounding.
30 bot > 0 guaranteed.
31 Some form of binary fp is assumed.
32 Pass NaNs or infinities at your own risk.
33
34 >>> _binary_float_to_ratio(10.0)
35 (10, 1)
36 >>> _binary_float_to_ratio(0.0)
37 (0, 1)
38 >>> _binary_float_to_ratio(-.25)
39 (-1, 4)
40 """
41
42 if x == 0:
43 return 0, 1
44 f, e = math.frexp(x)
45 signbit = 1
46 if f < 0:
47 f = -f
48 signbit = -1
49 assert 0.5 <= f < 1.0
50 # x = signbit * f * 2**e exactly
51
52 # Suck up CHUNK bits at a time; 28 is enough so that we suck
53 # up all bits in 2 iterations for all known binary double-
54 # precision formats, and small enough to fit in an int.
55 CHUNK = 28
56 top = 0
57 # invariant: x = signbit * (top + f) * 2**e exactly
58 while f:
59 f = math.ldexp(f, CHUNK)
60 digit = trunc(f)
61 assert digit >> CHUNK == 0
62 top = (top << CHUNK) | digit
63 f = f - digit
64 assert 0.0 <= f < 1.0
65 e = e - CHUNK
66 assert top
67
68 # Add in the sign bit.
69 top = signbit * top
70
71 # now x = top * 2**e exactly; fold in 2**e
72 if e>0:
73 return (top * 2**e, 1)
74 else:
75 return (top, 2 ** -e)
76
77
78class Rational(RationalAbc):
79 """This class implements rational numbers.
80
81 Rational(8, 6) will produce a rational number equivalent to
82 4/3. Both arguments must be Integral. The numerator defaults to 0
83 and the denominator defaults to 1 so that Rational(3) == 3 and
84 Rational() == 0.
85
86 """
87
88 __slots__ = ('_numerator', '_denominator')
89
90 def __init__(self, numerator=0, denominator=1):
91 if (not isinstance(numerator, numbers.Integral) and
92 isinstance(numerator, RationalAbc) and
93 denominator == 1):
94 # Handle copies from other rationals.
95 other_rational = numerator
96 numerator = other_rational.numerator
97 denominator = other_rational.denominator
98
99 if (not isinstance(numerator, numbers.Integral) or
100 not isinstance(denominator, numbers.Integral)):
101 raise TypeError("Rational(%(numerator)s, %(denominator)s):"
102 " Both arguments must be integral." % locals())
103
104 if denominator == 0:
105 raise ZeroDivisionError('Rational(%s, 0)' % numerator)
106
107 g = _gcd(numerator, denominator)
108 self._numerator = int(numerator // g)
109 self._denominator = int(denominator // g)
110
111 @classmethod
112 def from_float(cls, f):
113 """Converts a float to a rational number, exactly."""
114 if not isinstance(f, float):
115 raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
116 (cls.__name__, f, type(f).__name__))
117 if math.isnan(f) or math.isinf(f):
118 raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
119 return cls(*_binary_float_to_ratio(f))
120
121 @property
122 def numerator(a):
123 return a._numerator
124
125 @property
126 def denominator(a):
127 return a._denominator
128
129 def __repr__(self):
130 """repr(self)"""
131 return ('rational.Rational(%r,%r)' %
132 (self.numerator, self.denominator))
133
134 def __str__(self):
135 """str(self)"""
136 if self.denominator == 1:
137 return str(self.numerator)
138 else:
139 return '(%s/%s)' % (self.numerator, self.denominator)
140
141 def _operator_fallbacks(monomorphic_operator, fallback_operator):
142 """Generates forward and reverse operators given a purely-rational
143 operator and a function from the operator module.
144
145 Use this like:
146 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
147
148 """
149 def forward(a, b):
150 if isinstance(b, RationalAbc):
151 # Includes ints.
152 return monomorphic_operator(a, b)
153 elif isinstance(b, float):
154 return fallback_operator(float(a), b)
155 elif isinstance(b, complex):
156 return fallback_operator(complex(a), b)
157 else:
158 return NotImplemented
159 forward.__name__ = '__' + fallback_operator.__name__ + '__'
160 forward.__doc__ = monomorphic_operator.__doc__
161
162 def reverse(b, a):
163 if isinstance(a, RationalAbc):
164 # Includes ints.
165 return monomorphic_operator(a, b)
166 elif isinstance(a, numbers.Real):
167 return fallback_operator(float(a), float(b))
168 elif isinstance(a, numbers.Complex):
169 return fallback_operator(complex(a), complex(b))
170 else:
171 return NotImplemented
172 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
173 reverse.__doc__ = monomorphic_operator.__doc__
174
175 return forward, reverse
176
177 def _add(a, b):
178 """a + b"""
179 return Rational(a.numerator * b.denominator +
180 b.numerator * a.denominator,
181 a.denominator * b.denominator)
182
183 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
184
185 def _sub(a, b):
186 """a - b"""
187 return Rational(a.numerator * b.denominator -
188 b.numerator * a.denominator,
189 a.denominator * b.denominator)
190
191 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
192
193 def _mul(a, b):
194 """a * b"""
195 return Rational(a.numerator * b.numerator, a.denominator * b.denominator)
196
197 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
198
199 def _div(a, b):
200 """a / b"""
201 return Rational(a.numerator * b.denominator,
202 a.denominator * b.numerator)
203
204 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000205
206 def __floordiv__(a, b):
207 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000208 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000209
210 def __rfloordiv__(b, a):
211 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000212 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000213
214 @classmethod
215 def _mod(cls, a, b):
216 div = a // b
217 return a - b * div
218
219 def __mod__(a, b):
220 """a % b"""
221 return a._mod(a, b)
222
223 def __rmod__(b, a):
224 """a % b"""
225 return b._mod(a, b)
226
227 def __pow__(a, b):
228 """a ** b
229
230 If b is not an integer, the result will be a float or complex
231 since roots are generally irrational. If b is an integer, the
232 result will be rational.
233
234 """
235 if isinstance(b, RationalAbc):
236 if b.denominator == 1:
237 power = b.numerator
238 if power >= 0:
239 return Rational(a.numerator ** power,
240 a.denominator ** power)
241 else:
242 return Rational(a.denominator ** -power,
243 a.numerator ** -power)
244 else:
245 # A fractional power will generally produce an
246 # irrational number.
247 return float(a) ** float(b)
248 else:
249 return float(a) ** b
250
251 def __rpow__(b, a):
252 """a ** b"""
253 if b.denominator == 1 and b.numerator >= 0:
254 # If a is an int, keep it that way if possible.
255 return a ** b.numerator
256
257 if isinstance(a, RationalAbc):
258 return Rational(a.numerator, a.denominator) ** b
259
260 if b.denominator == 1:
261 return a ** b.numerator
262
263 return a ** float(b)
264
265 def __pos__(a):
266 """+a: Coerces a subclass instance to Rational"""
267 return Rational(a.numerator, a.denominator)
268
269 def __neg__(a):
270 """-a"""
271 return Rational(-a.numerator, a.denominator)
272
273 def __abs__(a):
274 """abs(a)"""
275 return Rational(abs(a.numerator), a.denominator)
276
277 def __trunc__(a):
278 """trunc(a)"""
279 if a.numerator < 0:
280 return -(-a.numerator // a.denominator)
281 else:
282 return a.numerator // a.denominator
283
284 def __floor__(a):
285 """Will be math.floor(a) in 3.0."""
286 return a.numerator // a.denominator
287
288 def __ceil__(a):
289 """Will be math.ceil(a) in 3.0."""
290 # The negations cleverly convince floordiv to return the ceiling.
291 return -(-a.numerator // a.denominator)
292
293 def __round__(self, ndigits=None):
294 """Will be round(self, ndigits) in 3.0.
295
296 Rounds half toward even.
297 """
298 if ndigits is None:
299 floor, remainder = divmod(self.numerator, self.denominator)
300 if remainder * 2 < self.denominator:
301 return floor
302 elif remainder * 2 > self.denominator:
303 return floor + 1
304 # Deal with the half case:
305 elif floor % 2 == 0:
306 return floor
307 else:
308 return floor + 1
309 shift = 10**abs(ndigits)
310 # See _operator_fallbacks.forward to check that the results of
311 # these operations will always be Rational and therefore have
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000312 # round().
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000313 if ndigits > 0:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000314 return Rational(round(self * shift), shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000315 else:
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000316 return Rational(round(self / shift) * shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000317
318 def __hash__(self):
319 """hash(self)
320
321 Tricky because values that are exactly representable as a
322 float must have the same hash as that float.
323
324 """
325 if self.denominator == 1:
326 # Get integers right.
327 return hash(self.numerator)
328 # Expensive check, but definitely correct.
329 if self == float(self):
330 return hash(float(self))
331 else:
332 # Use tuple's hash to avoid a high collision rate on
333 # simple fractions.
334 return hash((self.numerator, self.denominator))
335
336 def __eq__(a, b):
337 """a == b"""
338 if isinstance(b, RationalAbc):
339 return (a.numerator == b.numerator and
340 a.denominator == b.denominator)
341 if isinstance(b, numbers.Complex) and b.imag == 0:
342 b = b.real
343 if isinstance(b, float):
344 return a == a.from_float(b)
345 else:
346 # XXX: If b.__eq__ is implemented like this method, it may
347 # give the wrong answer after float(a) changes a's
348 # value. Better ways of doing this are welcome.
349 return float(a) == b
350
351 def _subtractAndCompareToZero(a, b, op):
352 """Helper function for comparison operators.
353
354 Subtracts b from a, exactly if possible, and compares the
355 result with 0 using op, in such a way that the comparison
356 won't recurse. If the difference raises a TypeError, returns
357 NotImplemented instead.
358
359 """
360 if isinstance(b, numbers.Complex) and b.imag == 0:
361 b = b.real
362 if isinstance(b, float):
363 b = a.from_float(b)
364 try:
365 # XXX: If b <: Real but not <: RationalAbc, this is likely
366 # to fall back to a float. If the actual values differ by
367 # less than MIN_FLOAT, this could falsely call them equal,
368 # which would make <= inconsistent with ==. Better ways of
369 # doing this are welcome.
370 diff = a - b
371 except TypeError:
372 return NotImplemented
373 if isinstance(diff, RationalAbc):
374 return op(diff.numerator, 0)
375 return op(diff, 0)
376
377 def __lt__(a, b):
378 """a < b"""
379 return a._subtractAndCompareToZero(b, operator.lt)
380
381 def __gt__(a, b):
382 """a > b"""
383 return a._subtractAndCompareToZero(b, operator.gt)
384
385 def __le__(a, b):
386 """a <= b"""
387 return a._subtractAndCompareToZero(b, operator.le)
388
389 def __ge__(a, b):
390 """a >= b"""
391 return a._subtractAndCompareToZero(b, operator.ge)
392
393 def __bool__(a):
394 """a != 0"""
395 return a.numerator != 0