blob: 55543b6d2d249391b6da9e6c0c9e0c4e887c1ef1 [file] [log] [blame]
'''\
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()