1. Armin Rigo
  2. cpython-withatomic

Commits

Guido van Rossum  committed ba005e4

A completely new Rat.py by Sjoerd.

  • Participants
  • Parent commits 5058166
  • Branches legacy-trunk

Comments (0)

Files changed (1)

File Demo/classes/Rat.py

View file
  • Ignore whitespace
-# Rational numbers
+'''\
+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 rat(num, den):
-	if type(num) == FloatType or type(den) == FloatType:
-		return num/den
-	return Rat(num, den)
-
-
 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):
+	def __init__(self, num, den = 1):
 		if den == 0:
 			raise ZeroDivisionError, 'rat(x, 0)'
-		if type(den) == FloatType or type(num) == FloatType:
-			g = float(den)
+
+		# 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
+			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)
+		return 'Rat(%s,%s)' % (self.__num, self.__den)
 
 	def __str__(self):
-		if self.den == 1:
-			return str(self.num)
+		if self.__den == 1:
+			return str(self.__num)
 		else:
-			return '%s/%s' % (self.num, self.den)
+			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):
-		c = a-b
-		if c.num < 0:
+		diff = a - b
+		if diff.__num < 0:
 			return -1
-		if c.num > 0:
+		elif diff.__num > 0:
 			return 1
-		return 0
+		else:
+			return 0
 
-	def __float__(self):
-		return float(self.num) / float(self.den)
+	def __rcmp__(b, a):
+		   return cmp(Rat(a), b)
 
-	def __long__(self):
-		return long(self.num) / long(self.den)
+	# a != 0
+	def __nonzero__(a):
+		return a.__num != 0
 
-	def __int__(self):
-		return int(self.num / self.den)
-
+	# coercion
 	def __coerce__(a, b):
-		t = type(b)
-		if t == IntType:
-			return a, Rat(b, 1)
-		if t == LongType:
-			return a, Rat(b, 1L)
-		if t == FloatType:
-			return a, Rat(b, 1.0)
-		if t == InstanceType and a.__class__ == b.__class__:
-			return a, b
-		raise TypeError, 'Rat.__coerce__: bad other arg'
-
-	def __add__(a, b):
-		return rat(a.num*b.den + b.num*a.den, a.den*b.den)
-
-	def __sub__(a, b):
-		return rat(a.num*b.den - b.num*a.den, a.den*b.den)
-
-	def __mul__(a, b):
-		return rat(a.num*b.num, a.den*b.den)
-
-	def __div__(a, b):
-		return rat(a.num*b.den, a.den*b.num)
-
-	def __neg__(self):
-		return rat(-self.num, self.den)
-
+		return a, Rat(b)
 
 def test():
-	print Rat(-1L, 1)
-	print Rat(1, -1)
-	a = Rat(1, 10)
-	print int(a), long(a), float(a)
-	b = Rat(2, 5)
+	'''\
+	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 rat(0, 1)
 	print a+1
 	print a+1L
 	print a+1.0
 	try:
-		print Rat(1, 0)
+		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)
 
-test()
+if __name__ == '__main__':
+    test()