blob: 25b9f020bf74de3520fab35c2033a8c440339a65 [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
Christian Heimes3feef612008-02-11 06:19:17 +00004"""Fraction, infinite-precision, real numbers."""
Guido van Rossum7736b5b2008-01-15 21:44:53 +00005
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
Christian Heimes3feef612008-02-11 06:19:17 +000011__all__ = ["Fraction"]
Guido van Rossum7736b5b2008-01-15 21:44:53 +000012
Guido van Rossum7736b5b2008-01-15 21:44:53 +000013
14
Christian Heimesaf98da12008-01-27 15:18:18 +000015def gcd(a, b):
16 """Calculate the Greatest Common Divisor of a and b.
Guido van Rossum7736b5b2008-01-15 21:44:53 +000017
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
Christian Heimes292d3512008-02-03 16:51:08 +000026_RATIONAL_FORMAT = re.compile(r"""
27 \A\s* # optional whitespace at the start, then
28 (?P<sign>[-+]?) # an optional sign, then
29 (?=\d|\.\d) # lookahead for digit or .digit
30 (?P<num>\d*) # numerator (possibly empty)
31 (?: # followed by an optional
32 /(?P<denom>\d+) # / and denominator
33 | # or
34 \.(?P<decimal>\d*) # decimal point and fractional part
35 )?
36 \s*\Z # and optional whitespace to finish
37""", re.VERBOSE)
Christian Heimes587c2bf2008-01-19 16:21:02 +000038
39
Christian Heimes3feef612008-02-11 06:19:17 +000040class Fraction(numbers.Rational):
Guido van Rossum7736b5b2008-01-15 21:44:53 +000041 """This class implements rational numbers.
42
Christian Heimes3feef612008-02-11 06:19:17 +000043 Fraction(8, 6) will produce a rational number equivalent to
Guido van Rossum7736b5b2008-01-15 21:44:53 +000044 4/3. Both arguments must be Integral. The numerator defaults to 0
Christian Heimes3feef612008-02-11 06:19:17 +000045 and the denominator defaults to 1 so that Fraction(3) == 3 and
46 Fraction() == 0.
Guido van Rossum7736b5b2008-01-15 21:44:53 +000047
Christian Heimes3feef612008-02-11 06:19:17 +000048 Fraction can also be constructed from strings of the form
Christian Heimesaf98da12008-01-27 15:18:18 +000049 '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces.
Christian Heimes587c2bf2008-01-19 16:21:02 +000050
Guido van Rossum7736b5b2008-01-15 21:44:53 +000051 """
52
Christian Heimes400adb02008-02-01 08:12:03 +000053 __slots__ = ('_numerator', '_denominator')
Guido van Rossum7736b5b2008-01-15 21:44:53 +000054
Christian Heimes587c2bf2008-01-19 16:21:02 +000055 # We're immutable, so use __new__ not __init__
56 def __new__(cls, numerator=0, denominator=1):
57 """Constructs a Rational.
58
Christian Heimesaf98da12008-01-27 15:18:18 +000059 Takes a string like '3/2' or '1.5', another Rational, or a
60 numerator/denominator pair.
Christian Heimes587c2bf2008-01-19 16:21:02 +000061
62 """
Christian Heimes3feef612008-02-11 06:19:17 +000063 self = super(Fraction, cls).__new__(cls)
Christian Heimes587c2bf2008-01-19 16:21:02 +000064
65 if denominator == 1:
66 if isinstance(numerator, str):
67 # Handle construction from strings.
68 input = numerator
69 m = _RATIONAL_FORMAT.match(input)
70 if m is None:
Christian Heimes3feef612008-02-11 06:19:17 +000071 raise ValueError('Invalid literal for Fraction: ' + input)
Christian Heimesaf98da12008-01-27 15:18:18 +000072 numerator = m.group('num')
73 decimal = m.group('decimal')
74 if decimal:
75 # The literal is a decimal number.
76 numerator = int(numerator + decimal)
77 denominator = 10**len(decimal)
78 else:
79 # The literal is an integer or fraction.
80 numerator = int(numerator)
81 # Default denominator to 1.
82 denominator = int(m.group('denom') or 1)
83
Christian Heimes587c2bf2008-01-19 16:21:02 +000084 if m.group('sign') == '-':
85 numerator = -numerator
86
87 elif (not isinstance(numerator, numbers.Integral) and
Christian Heimes3feef612008-02-11 06:19:17 +000088 isinstance(numerator, numbers.Rational)):
Christian Heimes587c2bf2008-01-19 16:21:02 +000089 # Handle copies from other rationals.
90 other_rational = numerator
91 numerator = other_rational.numerator
92 denominator = other_rational.denominator
Guido van Rossum7736b5b2008-01-15 21:44:53 +000093
94 if (not isinstance(numerator, numbers.Integral) or
95 not isinstance(denominator, numbers.Integral)):
Christian Heimes3feef612008-02-11 06:19:17 +000096 raise TypeError("Fraction(%(numerator)s, %(denominator)s):"
Guido van Rossum7736b5b2008-01-15 21:44:53 +000097 " Both arguments must be integral." % locals())
98
99 if denominator == 0:
Christian Heimes3feef612008-02-11 06:19:17 +0000100 raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000101
Christian Heimesaf98da12008-01-27 15:18:18 +0000102 g = gcd(numerator, denominator)
Christian Heimes400adb02008-02-01 08:12:03 +0000103 self._numerator = int(numerator // g)
104 self._denominator = int(denominator // g)
Christian Heimes587c2bf2008-01-19 16:21:02 +0000105 return self
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000106
107 @classmethod
108 def from_float(cls, f):
Christian Heimes587c2bf2008-01-19 16:21:02 +0000109 """Converts a finite float to a rational number, exactly.
110
Christian Heimes3feef612008-02-11 06:19:17 +0000111 Beware that Fraction.from_float(0.3) != Fraction(3, 10).
Christian Heimes587c2bf2008-01-19 16:21:02 +0000112
113 """
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000114 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__))
Christian Heimes26855632008-01-27 23:50:43 +0000119 return cls(*f.as_integer_ratio())
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000120
Christian Heimes587c2bf2008-01-19 16:21:02 +0000121 @classmethod
122 def from_decimal(cls, dec):
123 """Converts a finite Decimal instance to a rational number, exactly."""
124 from decimal import Decimal
125 if not isinstance(dec, Decimal):
126 raise TypeError(
127 "%s.from_decimal() only takes Decimals, not %r (%s)" %
128 (cls.__name__, dec, type(dec).__name__))
129 if not dec.is_finite():
130 # Catches infinities and nans.
131 raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
132 sign, digits, exp = dec.as_tuple()
133 digits = int(''.join(map(str, digits)))
134 if sign:
135 digits = -digits
136 if exp >= 0:
137 return cls(digits * 10 ** exp)
138 else:
139 return cls(digits, 10 ** -exp)
140
Christian Heimesbbffeb62008-01-24 09:42:52 +0000141 @classmethod
142 def from_continued_fraction(cls, seq):
Christian Heimes3feef612008-02-11 06:19:17 +0000143 'Build a Fraction from a continued fraction expessed as a sequence'
Christian Heimesbbffeb62008-01-24 09:42:52 +0000144 n, d = 1, 0
145 for e in reversed(seq):
146 n, d = d, n
147 n += e * d
148 return cls(n, d) if seq else cls(0)
149
150 def as_continued_fraction(self):
151 'Return continued fraction expressed as a list'
152 n = self.numerator
153 d = self.denominator
154 cf = []
155 while d:
156 e = int(n // d)
157 cf.append(e)
158 n -= e * d
159 n, d = d, n
160 return cf
161
Christian Heimes969fe572008-01-25 11:23:10 +0000162 def approximate(self, max_denominator):
163 'Best rational approximation with a denominator <= max_denominator'
Christian Heimesbbffeb62008-01-24 09:42:52 +0000164 # XXX First cut at algorithm
165 # Still needs rounding rules as specified at
166 # http://en.wikipedia.org/wiki/Continued_fraction
Christian Heimes969fe572008-01-25 11:23:10 +0000167 if self.denominator <= max_denominator:
168 return self
169 cf = self.as_continued_fraction()
Christian Heimes3feef612008-02-11 06:19:17 +0000170 result = Fraction(0)
Christian Heimesbbffeb62008-01-24 09:42:52 +0000171 for i in range(1, len(cf)):
Christian Heimes969fe572008-01-25 11:23:10 +0000172 new = self.from_continued_fraction(cf[:i])
Christian Heimesbbffeb62008-01-24 09:42:52 +0000173 if new.denominator > max_denominator:
174 break
175 result = new
176 return result
177
Christian Heimes400adb02008-02-01 08:12:03 +0000178 @property
179 def numerator(a):
180 return a._numerator
181
182 @property
183 def denominator(a):
184 return a._denominator
185
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000186 def __repr__(self):
187 """repr(self)"""
Christian Heimes3feef612008-02-11 06:19:17 +0000188 return ('Fraction(%r,%r)' % (self.numerator, self.denominator))
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000189
190 def __str__(self):
191 """str(self)"""
192 if self.denominator == 1:
193 return str(self.numerator)
194 else:
Christian Heimes587c2bf2008-01-19 16:21:02 +0000195 return '%s/%s' % (self.numerator, self.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000196
197 def _operator_fallbacks(monomorphic_operator, fallback_operator):
198 """Generates forward and reverse operators given a purely-rational
199 operator and a function from the operator module.
200
201 Use this like:
202 __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
203
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000204 In general, we want to implement the arithmetic operations so
205 that mixed-mode operations either call an implementation whose
206 author knew about the types of both arguments, or convert both
207 to the nearest built in type and do the operation there. In
Christian Heimes3feef612008-02-11 06:19:17 +0000208 Fraction, that means that we define __add__ and __radd__ as:
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000209
210 def __add__(self, other):
Christian Heimes400adb02008-02-01 08:12:03 +0000211 # Both types have numerators/denominator attributes,
212 # so do the operation directly
Christian Heimes3feef612008-02-11 06:19:17 +0000213 if isinstance(other, (int, Fraction)):
214 return Fraction(self.numerator * other.denominator +
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000215 other.numerator * self.denominator,
216 self.denominator * other.denominator)
Christian Heimes400adb02008-02-01 08:12:03 +0000217 # float and complex don't have those operations, but we
218 # know about those types, so special case them.
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000219 elif isinstance(other, float):
220 return float(self) + other
221 elif isinstance(other, complex):
222 return complex(self) + other
Christian Heimes400adb02008-02-01 08:12:03 +0000223 # Let the other type take over.
224 return NotImplemented
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000225
226 def __radd__(self, other):
227 # radd handles more types than add because there's
228 # nothing left to fall back to.
Christian Heimes3feef612008-02-11 06:19:17 +0000229 if isinstance(other, numbers.Rational):
230 return Fraction(self.numerator * other.denominator +
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000231 other.numerator * self.denominator,
232 self.denominator * other.denominator)
233 elif isinstance(other, Real):
234 return float(other) + float(self)
235 elif isinstance(other, Complex):
236 return complex(other) + complex(self)
Christian Heimes400adb02008-02-01 08:12:03 +0000237 return NotImplemented
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000238
239
240 There are 5 different cases for a mixed-type addition on
Christian Heimes3feef612008-02-11 06:19:17 +0000241 Fraction. I'll refer to all of the above code that doesn't
242 refer to Fraction, float, or complex as "boilerplate". 'r'
243 will be an instance of Fraction, which is a subtype of
244 Rational (r : Fraction <: Rational), and b : B <:
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000245 Complex. The first three involve 'r + b':
246
Christian Heimes3feef612008-02-11 06:19:17 +0000247 1. If B <: Fraction, int, float, or complex, we handle
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000248 that specially, and all is well.
Christian Heimes3feef612008-02-11 06:19:17 +0000249 2. If Fraction falls back to the boilerplate code, and it
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000250 were to return a value from __add__, we'd miss the
251 possibility that B defines a more intelligent __radd__,
252 so the boilerplate should return NotImplemented from
Christian Heimes3feef612008-02-11 06:19:17 +0000253 __add__. In particular, we don't handle Rational
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000254 here, even though we could get an exact answer, in case
255 the other type wants to do something special.
Christian Heimes3feef612008-02-11 06:19:17 +0000256 3. If B <: Fraction, Python tries B.__radd__ before
257 Fraction.__add__. This is ok, because it was
258 implemented with knowledge of Fraction, so it can
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000259 handle those instances before delegating to Real or
260 Complex.
261
262 The next two situations describe 'b + r'. We assume that b
Christian Heimes3feef612008-02-11 06:19:17 +0000263 didn't know about Fraction in its implementation, and that it
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000264 uses similar boilerplate code:
265
Christian Heimes3feef612008-02-11 06:19:17 +0000266 4. If B <: Rational, then __radd_ converts both to the
Christian Heimes7b3ce6a2008-01-31 14:31:45 +0000267 builtin rational type (hey look, that's us) and
268 proceeds.
269 5. Otherwise, __radd__ tries to find the nearest common
270 base ABC, and fall back to its builtin type. Since this
271 class doesn't subclass a concrete type, there's no
272 implementation to fall back to, so we need to try as
273 hard as possible to return an actual value, or the user
274 will get a TypeError.
275
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000276 """
277 def forward(a, b):
Christian Heimes3feef612008-02-11 06:19:17 +0000278 if isinstance(b, (int, Fraction)):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000279 return monomorphic_operator(a, b)
280 elif isinstance(b, float):
281 return fallback_operator(float(a), b)
282 elif isinstance(b, complex):
283 return fallback_operator(complex(a), b)
284 else:
285 return NotImplemented
286 forward.__name__ = '__' + fallback_operator.__name__ + '__'
287 forward.__doc__ = monomorphic_operator.__doc__
288
289 def reverse(b, a):
Christian Heimes3feef612008-02-11 06:19:17 +0000290 if isinstance(a, numbers.Rational):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000291 # Includes ints.
292 return monomorphic_operator(a, b)
293 elif isinstance(a, numbers.Real):
294 return fallback_operator(float(a), float(b))
295 elif isinstance(a, numbers.Complex):
296 return fallback_operator(complex(a), complex(b))
297 else:
298 return NotImplemented
299 reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
300 reverse.__doc__ = monomorphic_operator.__doc__
301
302 return forward, reverse
303
304 def _add(a, b):
305 """a + b"""
Christian Heimes3feef612008-02-11 06:19:17 +0000306 return Fraction(a.numerator * b.denominator +
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000307 b.numerator * a.denominator,
308 a.denominator * b.denominator)
309
310 __add__, __radd__ = _operator_fallbacks(_add, operator.add)
311
312 def _sub(a, b):
313 """a - b"""
Christian Heimes3feef612008-02-11 06:19:17 +0000314 return Fraction(a.numerator * b.denominator -
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000315 b.numerator * a.denominator,
316 a.denominator * b.denominator)
317
318 __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
319
320 def _mul(a, b):
321 """a * b"""
Christian Heimes3feef612008-02-11 06:19:17 +0000322 return Fraction(a.numerator * b.numerator, a.denominator * b.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000323
324 __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
325
326 def _div(a, b):
327 """a / b"""
Christian Heimes3feef612008-02-11 06:19:17 +0000328 return Fraction(a.numerator * b.denominator,
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000329 a.denominator * b.numerator)
330
331 __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000332
333 def __floordiv__(a, b):
334 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000335 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000336
337 def __rfloordiv__(b, a):
338 """a // b"""
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000339 return math.floor(a / b)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000340
Christian Heimes969fe572008-01-25 11:23:10 +0000341 def __mod__(a, b):
342 """a % b"""
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000343 div = a // b
344 return a - b * div
345
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000346 def __rmod__(b, a):
347 """a % b"""
Christian Heimes969fe572008-01-25 11:23:10 +0000348 div = a // b
349 return a - b * div
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000350
351 def __pow__(a, b):
352 """a ** b
353
354 If b is not an integer, the result will be a float or complex
355 since roots are generally irrational. If b is an integer, the
356 result will be rational.
357
358 """
Christian Heimes3feef612008-02-11 06:19:17 +0000359 if isinstance(b, numbers.Rational):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000360 if b.denominator == 1:
361 power = b.numerator
362 if power >= 0:
Christian Heimes3feef612008-02-11 06:19:17 +0000363 return Fraction(a.numerator ** power,
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000364 a.denominator ** power)
365 else:
Christian Heimes3feef612008-02-11 06:19:17 +0000366 return Fraction(a.denominator ** -power,
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000367 a.numerator ** -power)
368 else:
369 # A fractional power will generally produce an
370 # irrational number.
371 return float(a) ** float(b)
372 else:
373 return float(a) ** b
374
375 def __rpow__(b, a):
376 """a ** b"""
377 if b.denominator == 1 and b.numerator >= 0:
378 # If a is an int, keep it that way if possible.
379 return a ** b.numerator
380
Christian Heimes3feef612008-02-11 06:19:17 +0000381 if isinstance(a, numbers.Rational):
382 return Fraction(a.numerator, a.denominator) ** b
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000383
384 if b.denominator == 1:
385 return a ** b.numerator
386
387 return a ** float(b)
388
389 def __pos__(a):
Christian Heimes3feef612008-02-11 06:19:17 +0000390 """+a: Coerces a subclass instance to Fraction"""
391 return Fraction(a.numerator, a.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000392
393 def __neg__(a):
394 """-a"""
Christian Heimes3feef612008-02-11 06:19:17 +0000395 return Fraction(-a.numerator, a.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000396
397 def __abs__(a):
398 """abs(a)"""
Christian Heimes3feef612008-02-11 06:19:17 +0000399 return Fraction(abs(a.numerator), a.denominator)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000400
401 def __trunc__(a):
402 """trunc(a)"""
403 if a.numerator < 0:
404 return -(-a.numerator // a.denominator)
405 else:
406 return a.numerator // a.denominator
407
408 def __floor__(a):
409 """Will be math.floor(a) in 3.0."""
410 return a.numerator // a.denominator
411
412 def __ceil__(a):
413 """Will be math.ceil(a) in 3.0."""
414 # The negations cleverly convince floordiv to return the ceiling.
415 return -(-a.numerator // a.denominator)
416
417 def __round__(self, ndigits=None):
418 """Will be round(self, ndigits) in 3.0.
419
420 Rounds half toward even.
421 """
422 if ndigits is None:
423 floor, remainder = divmod(self.numerator, self.denominator)
424 if remainder * 2 < self.denominator:
425 return floor
426 elif remainder * 2 > self.denominator:
427 return floor + 1
428 # Deal with the half case:
429 elif floor % 2 == 0:
430 return floor
431 else:
432 return floor + 1
433 shift = 10**abs(ndigits)
434 # See _operator_fallbacks.forward to check that the results of
Christian Heimes3feef612008-02-11 06:19:17 +0000435 # these operations will always be Fraction and therefore have
Jeffrey Yasskin9893de12008-01-17 07:36:30 +0000436 # round().
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000437 if ndigits > 0:
Christian Heimes3feef612008-02-11 06:19:17 +0000438 return Fraction(round(self * shift), shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000439 else:
Christian Heimes3feef612008-02-11 06:19:17 +0000440 return Fraction(round(self / shift) * shift)
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000441
442 def __hash__(self):
443 """hash(self)
444
445 Tricky because values that are exactly representable as a
446 float must have the same hash as that float.
447
448 """
Christian Heimes969fe572008-01-25 11:23:10 +0000449 # XXX since this method is expensive, consider caching the result
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000450 if self.denominator == 1:
451 # Get integers right.
452 return hash(self.numerator)
453 # Expensive check, but definitely correct.
454 if self == float(self):
455 return hash(float(self))
456 else:
457 # Use tuple's hash to avoid a high collision rate on
458 # simple fractions.
459 return hash((self.numerator, self.denominator))
460
461 def __eq__(a, b):
462 """a == b"""
Christian Heimes3feef612008-02-11 06:19:17 +0000463 if isinstance(b, numbers.Rational):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000464 return (a.numerator == b.numerator and
465 a.denominator == b.denominator)
466 if isinstance(b, numbers.Complex) and b.imag == 0:
467 b = b.real
468 if isinstance(b, float):
469 return a == a.from_float(b)
470 else:
471 # XXX: If b.__eq__ is implemented like this method, it may
472 # give the wrong answer after float(a) changes a's
473 # value. Better ways of doing this are welcome.
474 return float(a) == b
475
476 def _subtractAndCompareToZero(a, b, op):
477 """Helper function for comparison operators.
478
479 Subtracts b from a, exactly if possible, and compares the
480 result with 0 using op, in such a way that the comparison
481 won't recurse. If the difference raises a TypeError, returns
482 NotImplemented instead.
483
484 """
485 if isinstance(b, numbers.Complex) and b.imag == 0:
486 b = b.real
487 if isinstance(b, float):
488 b = a.from_float(b)
489 try:
Christian Heimes3feef612008-02-11 06:19:17 +0000490 # XXX: If b <: Real but not <: Rational, this is likely
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000491 # to fall back to a float. If the actual values differ by
492 # less than MIN_FLOAT, this could falsely call them equal,
493 # which would make <= inconsistent with ==. Better ways of
494 # doing this are welcome.
495 diff = a - b
496 except TypeError:
497 return NotImplemented
Christian Heimes3feef612008-02-11 06:19:17 +0000498 if isinstance(diff, numbers.Rational):
Guido van Rossum7736b5b2008-01-15 21:44:53 +0000499 return op(diff.numerator, 0)
500 return op(diff, 0)
501
502 def __lt__(a, b):
503 """a < b"""
504 return a._subtractAndCompareToZero(b, operator.lt)
505
506 def __gt__(a, b):
507 """a > b"""
508 return a._subtractAndCompareToZero(b, operator.gt)
509
510 def __le__(a, b):
511 """a <= b"""
512 return a._subtractAndCompareToZero(b, operator.le)
513
514 def __ge__(a, b):
515 """a >= b"""
516 return a._subtractAndCompareToZero(b, operator.ge)
517
518 def __bool__(a):
519 """a != 0"""
520 return a.numerator != 0
Christian Heimes969fe572008-01-25 11:23:10 +0000521
522 # support for pickling, copy, and deepcopy
523
524 def __reduce__(self):
525 return (self.__class__, (str(self),))
526
527 def __copy__(self):
Christian Heimes3feef612008-02-11 06:19:17 +0000528 if type(self) == Fraction:
Christian Heimes969fe572008-01-25 11:23:10 +0000529 return self # I'm immutable; therefore I am my own clone
530 return self.__class__(self.numerator, self.denominator)
531
532 def __deepcopy__(self, memo):
Christian Heimes3feef612008-02-11 06:19:17 +0000533 if type(self) == Fraction:
Christian Heimes969fe572008-01-25 11:23:10 +0000534 return self # My components are also immutable
535 return self.__class__(self.numerator, self.denominator)