diff options
author | Guido van Rossum <guido@python.org> | 2001-08-08 22:27:20 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2001-08-08 22:27:20 (GMT) |
commit | 64deef2b171fcb4ed33fd940c8884671ddb4de22 (patch) | |
tree | 9255850a528ce4be6c4b686cc2b09321e5dbae0d /Lib/test/test_binop.py | |
parent | dc91b99f23610573c275cb70f987bcf56eee0d23 (diff) | |
download | cpython-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.py | 340 |
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) |