| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | """Tests for binary operators on subtypes of built-in types.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2008-05-20 21:35:26 +00:00
										 |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def gcd(a, b): | 
					
						
							|  |  |  |     """Greatest common divisor using Euclid's algorithm.""" | 
					
						
							|  |  |  |     while a: | 
					
						
							|  |  |  |         a, b = b%a, a | 
					
						
							|  |  |  |     return b | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def isint(x): | 
					
						
							|  |  |  |     """Test whether an object is an instance of int or long.""" | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |     return isinstance(x, int) or isinstance(x, int) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def isnum(x): | 
					
						
							|  |  |  |     """Test whether an object is an instance of a built-in numeric type.""" | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |     for T in int, int, float, complex: | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         if isinstance(x, T): | 
					
						
							|  |  |  |             return 1 | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def isRat(x): | 
					
						
							|  |  |  |     """Test wheter an object is an instance of the Rat class.""" | 
					
						
							|  |  |  |     return isinstance(x, Rat) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Rat(object): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Rational number implemented as a normalized pair of longs.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __slots__ = ['_Rat__num', '_Rat__den'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |     def __init__(self, num=0, den=1): | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         """Constructor: Rat([num[, den]]).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The arguments must be ints or longs, and default to (0, 1)."""
 | 
					
						
							|  |  |  |         if not isint(num): | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |             raise TypeError("Rat numerator must be int or long (%r)" % num) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         if not isint(den): | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |             raise TypeError("Rat denominator must be int or long (%r)" % den) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         # But the zero is always on | 
					
						
							|  |  |  |         if den == 0: | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |             raise ZeroDivisionError("zero denominator") | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         g = gcd(den, num) | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |         self.__num = int(num//g) | 
					
						
							|  |  |  |         self.__den = int(den//g) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _get_num(self): | 
					
						
							|  |  |  |         """Accessor function for read-only 'num' attribute of Rat.""" | 
					
						
							|  |  |  |         return self.__num | 
					
						
							| 
									
										
										
										
											2001-09-06 21:56:42 +00:00
										 |  |  |     num = property(_get_num, None) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _get_den(self): | 
					
						
							|  |  |  |         """Accessor function for read-only 'den' attribute of Rat.""" | 
					
						
							|  |  |  |         return self.__den | 
					
						
							| 
									
										
										
										
											2001-09-06 21:56:42 +00:00
										 |  |  |     den = property(_get_den, None) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         """Convert a Rat to an string resembling a Rat constructor call.""" | 
					
						
							|  |  |  |         return "Rat(%d, %d)" % (self.__num, self.__den) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							|  |  |  |         """Convert a Rat to a string resembling a decimal numeric value.""" | 
					
						
							|  |  |  |         return str(float(self)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __float__(self): | 
					
						
							|  |  |  |         """Convert a Rat to a float.""" | 
					
						
							|  |  |  |         return self.__num*1.0/self.__den | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __int__(self): | 
					
						
							|  |  |  |         """Convert a Rat to an int; self.den must be 1.""" | 
					
						
							|  |  |  |         if self.__den == 1: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return int(self.__num) | 
					
						
							|  |  |  |             except OverflowError: | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |                 raise OverflowError("%s too large to convert to int" % | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |                                       repr(self)) | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |         raise ValueError("can't convert %s to int" % repr(self)) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __long__(self): | 
					
						
							|  |  |  |         """Convert a Rat to an long; self.den must be 1.""" | 
					
						
							|  |  |  |         if self.__den == 1: | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |             return int(self.__num) | 
					
						
							| 
									
										
										
										
											2007-08-29 23:37:32 +00:00
										 |  |  |         raise ValueError("can't convert %s to long" % repr(self)) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __add__(self, other): | 
					
						
							|  |  |  |         """Add two Rats, or a Rat and a number.""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(self.__num*other.__den + other.__num*self.__den, | 
					
						
							|  |  |  |                        self.__den*other.__den) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return float(self) + other | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __radd__ = __add__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __sub__(self, other): | 
					
						
							|  |  |  |         """Subtract two Rats, or a Rat and a number.""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(self.__num*other.__den - other.__num*self.__den, | 
					
						
							|  |  |  |                        self.__den*other.__den) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return float(self) - other | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __rsub__(self, other): | 
					
						
							|  |  |  |         """Subtract two Rats, or a Rat and a number (reversed args).""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(other.__num*self.__den - self.__num*other.__den, | 
					
						
							|  |  |  |                        self.__den*other.__den) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return other - float(self) | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __mul__(self, other): | 
					
						
							|  |  |  |         """Multiply two Rats, or a Rat and a number.""" | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(self.__num*other.__num, self.__den*other.__den) | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             return Rat(self.__num*other, self.__den) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return float(self)*other | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __rmul__ = __mul__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __truediv__(self, other): | 
					
						
							|  |  |  |         """Divide two Rats, or a Rat and a number.""" | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(self.__num*other.__den, self.__den*other.__num) | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             return Rat(self.__num, self.__den*other) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return float(self) / other | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __rtruediv__(self, other): | 
					
						
							|  |  |  |         """Divide two Rats, or a Rat and a number (reversed args).""" | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return Rat(other.__num*self.__den, other.__den*self.__num) | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             return Rat(other*self.__den, self.__num) | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return other / float(self) | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __floordiv__(self, other): | 
					
						
							|  |  |  |         """Divide two Rats, returning the floored result.""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         elif not isRat(other): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         x = self/other | 
					
						
							|  |  |  |         return x.__num // x.__den | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __rfloordiv__(self, other): | 
					
						
							|  |  |  |         """Divide two Rats, returning the floored result (reversed args).""" | 
					
						
							|  |  |  |         x = other/self | 
					
						
							|  |  |  |         return x.__num // x.__den | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __divmod__(self, other): | 
					
						
							|  |  |  |         """Divide two Rats, returning quotient and remainder.""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         elif not isRat(other): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         x = self//other | 
					
						
							|  |  |  |         return (x, self - other * x) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __rdivmod__(self, other): | 
					
						
							| 
									
										
										
										
											2005-04-30 05:50:19 +00:00
										 |  |  |         """Divide two Rats, returning quotient and remainder (reversed args).""" | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         if isint(other): | 
					
						
							|  |  |  |             other = Rat(other) | 
					
						
							|  |  |  |         elif not isRat(other): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return divmod(other, self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __mod__(self, other): | 
					
						
							|  |  |  |         """Take one Rat modulo another.""" | 
					
						
							|  |  |  |         return divmod(self, other)[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __rmod__(self, other): | 
					
						
							|  |  |  |         """Take one Rat modulo another (reversed args).""" | 
					
						
							|  |  |  |         return divmod(other, self)[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         """Compare two Rats for equality.""" | 
					
						
							|  |  |  |         if isint(other): | 
					
						
							|  |  |  |             return self.__den == 1 and self.__num == other | 
					
						
							|  |  |  |         if isRat(other): | 
					
						
							|  |  |  |             return self.__num == other.__num and self.__den == other.__den | 
					
						
							|  |  |  |         if isnum(other): | 
					
						
							|  |  |  |             return float(self) == other | 
					
						
							|  |  |  |         return NotImplemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __ne__(self, other): | 
					
						
							|  |  |  |         """Compare two Rats for inequality.""" | 
					
						
							|  |  |  |         return not self == other | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RatTestCase(unittest.TestCase): | 
					
						
							|  |  |  |     """Unit tests for Rat class and its support utilities.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_gcd(self): | 
					
						
							|  |  |  |         self.assertEqual(gcd(10, 12), 2) | 
					
						
							|  |  |  |         self.assertEqual(gcd(10, 15), 5) | 
					
						
							|  |  |  |         self.assertEqual(gcd(10, 11), 1) | 
					
						
							|  |  |  |         self.assertEqual(gcd(100, 15), 5) | 
					
						
							|  |  |  |         self.assertEqual(gcd(-10, 2), -2) | 
					
						
							|  |  |  |         self.assertEqual(gcd(10, -2), 2) | 
					
						
							|  |  |  |         self.assertEqual(gcd(-10, -2), -2) | 
					
						
							|  |  |  |         for i in range(1, 20): | 
					
						
							|  |  |  |             for j in range(1, 20): | 
					
						
							|  |  |  |                 self.assert_(gcd(i, j) > 0) | 
					
						
							|  |  |  |                 self.assert_(gcd(-i, j) < 0) | 
					
						
							|  |  |  |                 self.assert_(gcd(i, -j) > 0) | 
					
						
							|  |  |  |                 self.assert_(gcd(-i, -j) < 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_constructor(self): | 
					
						
							|  |  |  |         a = Rat(10, 15) | 
					
						
							|  |  |  |         self.assertEqual(a.num, 2) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 3) | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |         a = Rat(10, 15) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  |         self.assertEqual(a.num, 2) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 3) | 
					
						
							|  |  |  |         a = Rat(10, -15) | 
					
						
							|  |  |  |         self.assertEqual(a.num, -2) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 3) | 
					
						
							|  |  |  |         a = Rat(-10, 15) | 
					
						
							|  |  |  |         self.assertEqual(a.num, -2) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 3) | 
					
						
							|  |  |  |         a = Rat(-10, -15) | 
					
						
							|  |  |  |         self.assertEqual(a.num, 2) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 3) | 
					
						
							|  |  |  |         a = Rat(7) | 
					
						
							|  |  |  |         self.assertEqual(a.num, 7) | 
					
						
							|  |  |  |         self.assertEqual(a.den, 1) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             a = Rat(1, 0) | 
					
						
							|  |  |  |         except ZeroDivisionError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.fail("Rat(1, 0) didn't raise ZeroDivisionError") | 
					
						
							|  |  |  |         for bad in "0", 0.0, 0j, (), [], {}, None, Rat, unittest: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 a = Rat(bad) | 
					
						
							|  |  |  |             except TypeError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.fail("Rat(%r) didn't raise TypeError" % bad) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 a = Rat(1, bad) | 
					
						
							|  |  |  |             except TypeError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.fail("Rat(1, %r) didn't raise TypeError" % bad) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_add(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(2, 3) + Rat(1, 3), 1) | 
					
						
							|  |  |  |         self.assertEqual(Rat(2, 3) + 1, Rat(5, 3)) | 
					
						
							|  |  |  |         self.assertEqual(1 + Rat(2, 3), Rat(5, 3)) | 
					
						
							|  |  |  |         self.assertEqual(1.0 + Rat(1, 2), 1.5) | 
					
						
							|  |  |  |         self.assertEqual(Rat(1, 2) + 1.0, 1.5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_sub(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(7, 2) - Rat(7, 5), Rat(21, 10)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(7, 5) - 1, Rat(2, 5)) | 
					
						
							|  |  |  |         self.assertEqual(1 - Rat(3, 5), Rat(2, 5)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(3, 2) - 1.0, 0.5) | 
					
						
							|  |  |  |         self.assertEqual(1.0 - Rat(1, 2), 0.5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_mul(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(2, 3) * Rat(5, 7), Rat(10, 21)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) * 3, 10) | 
					
						
							|  |  |  |         self.assertEqual(3 * Rat(10, 3), 10) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 5) * 0.5, 1.0) | 
					
						
							|  |  |  |         self.assertEqual(0.5 * Rat(10, 5), 1.0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_div(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) / 3, Rat(10, 9)) | 
					
						
							|  |  |  |         self.assertEqual(2 / Rat(5), Rat(2, 5)) | 
					
						
							|  |  |  |         self.assertEqual(3.0 * Rat(1, 2), 1.5) | 
					
						
							|  |  |  |         self.assertEqual(Rat(1, 2) * 3.0, 1.5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_floordiv(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(10) // Rat(4), 2) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) // Rat(4, 3), 2) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10) // 4, 2) | 
					
						
							|  |  |  |         self.assertEqual(10 // Rat(4), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_eq(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(10), Rat(20, 2)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10), 10) | 
					
						
							|  |  |  |         self.assertEqual(10, Rat(10)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10), 10.0) | 
					
						
							|  |  |  |         self.assertEqual(10.0, Rat(10)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-28 23:34:00 +00:00
										 |  |  |     def test_true_div(self): | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3)) | 
					
						
							|  |  |  |         self.assertEqual(Rat(10, 3) / 3, Rat(10, 9)) | 
					
						
							|  |  |  |         self.assertEqual(2 / Rat(5), Rat(2, 5)) | 
					
						
							|  |  |  |         self.assertEqual(3.0 * Rat(1, 2), 1.5) | 
					
						
							|  |  |  |         self.assertEqual(Rat(1, 2) * 3.0, 1.5) | 
					
						
							|  |  |  |         self.assertEqual(eval('1/2'), 0.5) | 
					
						
							| 
									
										
										
										
											2001-08-08 22:27:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # XXX Ran out of steam; TO DO: divmod, div, future division | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-20 21:33:42 +00:00
										 |  |  | def test_main(): | 
					
						
							| 
									
										
										
										
											2008-05-20 21:35:26 +00:00
										 |  |  |     support.run_unittest(RatTestCase) | 
					
						
							| 
									
										
										
										
											2001-09-20 21:33:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     test_main() |