'''\ This module implements rational numbers. The entry point of this module is the function rat(numerator, denominator) If either numerator or denominator is of an integral or rational type, the result is a rational number, else, the result is the simplest of the types float and complex which can hold numerator/denominator. If denominator is omitted, it defaults to 1. Rational numbers can be used in calculations with any other numeric type. The result of the calculation will be rational if possible. There is also a test function with calling sequence test() The documentation string of the test function contains the expected output. ''' # Contributed by Sjoerd Mullender from types import * def gcd(a, b): '''Calculate the Greatest Common Divisor.''' while b: a, b = b, a%b return a def rat(num, den = 1): # must check complex before float if isinstance(num, complex) or isinstance(den, complex): # numerator or denominator is complex: return a complex return complex(num) / complex(den) if isinstance(num, float) or isinstance(den, float): # numerator or denominator is float: return a float return float(num) / float(den) # otherwise return a rational return Rat(num, den) class Rat: '''This class implements rational numbers.''' def __init__(self, num, den = 1): if den == 0: raise ZeroDivisionError, 'rat(x, 0)' # normalize # must check complex before float if (isinstance(num, complex) or isinstance(den, complex)): # numerator or denominator is complex: # normalized form has denominator == 1+0j self.__num = complex(num) / complex(den) self.__den = complex(1) return if isinstance(num, float) or isinstance(den, float): # numerator or denominator is float: # normalized form has denominator == 1.0 self.__num = float(num) / float(den) self.__den = 1.0 return if (isinstance(num, self.__class__) or isinstance(den, self.__class__)): # numerator or denominator is rational new = num / den if not isinstance(new, self.__class__): self.__num = new if isinstance(new, complex): self.__den = complex(1) else: self.__den = 1.0 else: self.__num = new.__num self.__den = new.__den else: # make sure numerator and denominator don't # have common factors # this also makes sure that denominator > 0 g = gcd(num, den) self.__num = num / g self.__den = den / g # try making numerator and denominator of IntType if they fit try: numi = int(self.__num) deni = int(self.__den) except (OverflowError, TypeError): pass else: if self.__num == numi and self.__den == deni: self.__num = numi self.__den = deni def __repr__(self): return 'Rat(%s,%s)' % (self.__num, self.__den) def __str__(self): if self.__den == 1: return str(self.__num) else: return '(%s/%s)' % (str(self.__num), str(self.__den)) # a + b def __add__(a, b): try: return rat(a.__num * b.__den + b.__num * a.__den, a.__den * b.__den) except OverflowError: return rat(long(a.__num) * long(b.__den) + long(b.__num) * long(a.__den), long(a.__den) * long(b.__den)) def __radd__(b, a): return Rat(a) + b # a - b def __sub__(a, b): try: return rat(a.__num * b.__den - b.__num * a.__den, a.__den * b.__den) except OverflowError: return rat(long(a.__num) * long(b.__den) - long(b.__num) * long(a.__den), long(a.__den) * long(b.__den)) def __rsub__(b, a): return Rat(a) - b # a * b def __mul__(a, b): try: return rat(a.__num * b.__num, a.__den * b.__den) except OverflowError: return rat(long(a.__num) * long(b.__num), long(a.__den) * long(b.__den)) def __rmul__(b, a): return Rat(a) * b # a / b def __div__(a, b): try: return rat(a.__num * b.__den, a.__den * b.__num) except OverflowError: return rat(long(a.__num) * long(b.__den), long(a.__den) * long(b.__num)) def __rdiv__(b, a): return Rat(a) / b # a % b def __mod__(a, b): div = a / b try: div = int(div) except OverflowError: div = long(div) return a - b * div def __rmod__(b, a): return Rat(a) % b # a ** b def __pow__(a, b): if b.__den != 1: if isinstance(a.__num, complex): a = complex(a) else: a = float(a) if isinstance(b.__num, complex): b = complex(b) else: b = float(b) return a ** b try: return rat(a.__num ** b.__num, a.__den ** b.__num) except OverflowError: return rat(long(a.__num) ** b.__num, long(a.__den) ** b.__num) def __rpow__(b, a): return Rat(a) ** b # -a def __neg__(a): try: return rat(-a.__num, a.__den) except OverflowError: # a.__num == sys.maxint return rat(-long(a.__num), a.__den) # abs(a) def __abs__(a): return rat(abs(a.__num), a.__den) # int(a) def __int__(a): return int(a.__num / a.__den) # long(a) def __long__(a): return long(a.__num) / long(a.__den) # float(a) def __float__(a): return float(a.__num) / float(a.__den) # complex(a) def __complex__(a): return complex(a.__num) / complex(a.__den) # cmp(a,b) def __cmp__(a, b): diff = Rat(a - b) if diff.__num < 0: return -1 elif diff.__num > 0: return 1 else: return 0 def __rcmp__(b, a): return cmp(Rat(a), b) # a != 0 def __nonzero__(a): return a.__num != 0 # coercion def __coerce__(a, b): return a, Rat(b) def test(): '''\ Test function for rat module. The expected output is (module some differences in floating precission): -1 -1 0 0L 0.1 (0.1+0j) [Rat(1,2), Rat(-3,10), Rat(1,25), Rat(1,4)] [Rat(-3,10), Rat(1,25), Rat(1,4), Rat(1,2)] 0 (11/10) (11/10) 1.1 OK 2 1.5 (3/2) (1.5+1.5j) (15707963/5000000) 2 2 2.0 (2+0j) 4 0 4 1 4 0 3.5 0.5 3.0 1.33333333333 2.82842712475 1 (7/2) (1/2) 3 (4/3) 2.82842712475 1 (3.5+1.5j) (0.5-1.5j) (3+3j) (0.666666666667-0.666666666667j) (1.43248815986+2.43884761145j) 1 1.5 1 1.5 (1.5+0j) 3.5 -0.5 3.0 0.75 2.25 -1 3.0 0.0 2.25 1.0 1.83711730709 0 3.0 0.0 2.25 1.0 1.83711730709 1 (3+1.5j) -1.5j (2.25+2.25j) (0.5-0.5j) (1.50768393746+1.04970907623j) -1 (3/2) 1 1.5 (1.5+0j) (7/2) (-1/2) 3 (3/4) (9/4) -1 3.0 0.0 2.25 1.0 1.83711730709 -1 3 0 (9/4) 1 1.83711730709 0 (3+1.5j) -1.5j (2.25+2.25j) (0.5-0.5j) (1.50768393746+1.04970907623j) -1 (1.5+1.5j) (1.5+1.5j) (3.5+1.5j) (-0.5+1.5j) (3+3j) (0.75+0.75j) 4.5j -1 (3+1.5j) 1.5j (2.25+2.25j) (1+1j) (1.18235814075+2.85446505899j) 1 (3+1.5j) 1.5j (2.25+2.25j) (1+1j) (1.18235814075+2.85446505899j) 1 (3+3j) 0j 4.5j (1+0j) (-0.638110484918+0.705394566962j) 0 ''' print rat(-1L, 1) print rat(1, -1) a = rat(1, 10) print int(a), long(a), float(a), complex(a) b = rat(2, 5) l = [a+b, a-b, a*b, a/b] print l l.sort() print l print rat(0, 1) print a+1 print a+1L print a+1.0 try: print rat(1, 0) raise SystemError, 'should have been ZeroDivisionError' except ZeroDivisionError: print 'OK' print rat(2), rat(1.5), rat(3, 2), rat(1.5+1.5j), rat(31415926,10000000) list = [2, 1.5, rat(3,2), 1.5+1.5j] for i in list: print i, if not isinstance(i, complex): print int(i), float(i), print complex(i) print for j in list: print i + j, i - j, i * j, i / j, i ** j, if not (isinstance(i, complex) or isinstance(j, complex)): print cmp(i, j) print if __name__ == '__main__': test()