Staging
v0.5.1
https://github.com/python/cpython
Raw File
Tip revision: 5f55933b3a78b7f3557ac81a2411a1b50bffab33 authored by Georg Brandl on 05 March 2011, 13:54:46 UTC
Close 2.1 branch.
Tip revision: 5f55933
Rat.py
'''\
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 type(num) is ComplexType or type(den) is ComplexType:
		# numerator or denominator is complex: return a complex
		return complex(num) / complex(den)
	if type(num) is FloatType or type(den) is FloatType:
		# 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 type(num) is ComplexType or type(den) is ComplexType:
			# numerator or denominator is complex:
			# normalized form has denominator == 1+0j
			self.__num = complex(num) / complex(den)
			self.__den = complex(1)
			return
		if type(num) is FloatType or type(den) is FloatType:
			# numerator or denominator is float:
			# normalized form has denominator == 1.0
			self.__num = float(num) / float(den)
			self.__den = 1.0
			return
		if (type(num) is InstanceType and
		    num.__class__ is self.__class__) or \
		   (type(den) is InstanceType and
		    den.__class__ is self.__class__):
			# numerator or denominator is rational
			new = num / den
			if type(new) is not InstanceType or \
			   new.__class__ is not self.__class__:
				self.__num = new
				if type(new) is ComplexType:
					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 type(a.__num) is ComplexType:
				a = complex(a)
			else:
				a = float(a)
			if type(b.__num) is ComplexType:
				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 type(i) is not ComplexType:
			print int(i), float(i),
		print complex(i)
		print
		for j in list:
			print i + j, i - j, i * j, i / j, i ** j, cmp(i, j)

if __name__ == '__main__':
    test()
back to top