summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_binop.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2001-08-08 22:27:20 (GMT)
committerGuido van Rossum <guido@python.org>2001-08-08 22:27:20 (GMT)
commit64deef2b171fcb4ed33fd940c8884671ddb4de22 (patch)
tree9255850a528ce4be6c4b686cc2b09321e5dbae0d /Lib/test/test_binop.py
parentdc91b99f23610573c275cb70f987bcf56eee0d23 (diff)
downloadcpython-64deef2b171fcb4ed33fd940c8884671ddb4de22.zip
cpython-64deef2b171fcb4ed33fd940c8884671ddb4de22.tar.gz
cpython-64deef2b171fcb4ed33fd940c8884671ddb4de22.tar.bz2
A test suite for binary operators, disguised as a rational number
class.
Diffstat (limited to 'Lib/test/test_binop.py')
-rw-r--r--Lib/test/test_binop.py340
1 files changed, 340 insertions, 0 deletions
diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py
new file mode 100644
index 0000000..fc7ed94e5
--- /dev/null
+++ b/Lib/test/test_binop.py
@@ -0,0 +1,340 @@
+"""Tests for binary operators on subtypes of built-in types."""
+
+import test_support
+import unittest
+
+class getset(object):
+
+ """Define a get/set attribute descriptor.
+
+ This will eventually become a built-in."""
+
+ def __init__(self, get, set=None):
+ self.__get = get
+ self.__set = set
+
+ def __get__(self, inst, type=None):
+ return self.__get(inst)
+
+ def __set__(self, inst, value):
+ if self.__set is None:
+ raise AttributeError, "this attribute is read-only"
+ return self.__set(inst, value)
+
+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."""
+ return isinstance(x, int) or isinstance(x, long)
+
+def isnum(x):
+ """Test whether an object is an instance of a built-in numeric type."""
+ for T in int, long, float, complex:
+ 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']
+
+ def __init__(self, num=0L, den=1L):
+ """Constructor: Rat([num[, den]]).
+
+ The arguments must be ints or longs, and default to (0, 1)."""
+ if not isint(num):
+ raise TypeError, "Rat numerator must be int or long (%r)" % num
+ if not isint(den):
+ raise TypeError, "Rat denominator must be int or long (%r)" % den
+ # But the zero is always on
+ if den == 0:
+ raise ZeroDivisionError, "zero denominator"
+ g = gcd(den, num)
+ self.__num = long(num/g)
+ self.__den = long(den/g)
+
+ def _get_num(self):
+ """Accessor function for read-only 'num' attribute of Rat."""
+ return self.__num
+ num = getset(_get_num, None)
+
+ def _get_den(self):
+ """Accessor function for read-only 'den' attribute of Rat."""
+ return self.__den
+ den = getset(_get_den, None)
+
+ 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:
+ raise OverflowError, ("%s too large to convert to int" %
+ repr(self))
+ raise ValueError, "can't convert %s to int" % repr(self)
+
+ def __long__(self):
+ """Convert a Rat to an long; self.den must be 1."""
+ if self.__den == 1:
+ return long(self.__num)
+ raise ValueError, "can't convert %s to long" % repr(self)
+
+ 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
+
+ __div__ = __truediv__
+
+ 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
+
+ __rdiv__ = __rtruediv__
+
+ 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):
+ "Divide two Rats, returning quotient and remainder (reversed args)."""
+ 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)
+ a = Rat(10L, 15L)
+ 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))
+
+ def test_future_div(self):
+ exec future_test
+
+ # XXX Ran out of steam; TO DO: divmod, div, future division
+
+future_test = """
+from __future__ import division
+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)
+"""
+
+test_support.run_unittest(RatTestCase)