PageRenderTime 32ms CodeModel.GetById 8ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/test/test_binop.py

http://unladen-swallow.googlecode.com/
Python | 328 lines | 325 code | 2 blank | 1 comment | 2 complexity | 78ee66ac9bf4640497794c76f2678d68 MD5 | raw file
  1"""Tests for binary operators on subtypes of built-in types."""
  2
  3import unittest
  4from test import test_support
  5
  6def gcd(a, b):
  7    """Greatest common divisor using Euclid's algorithm."""
  8    while a:
  9        a, b = b%a, a
 10    return b
 11
 12def isint(x):
 13    """Test whether an object is an instance of int or long."""
 14    return isinstance(x, int) or isinstance(x, long)
 15
 16def isnum(x):
 17    """Test whether an object is an instance of a built-in numeric type."""
 18    for T in int, long, float, complex:
 19        if isinstance(x, T):
 20            return 1
 21    return 0
 22
 23def isRat(x):
 24    """Test wheter an object is an instance of the Rat class."""
 25    return isinstance(x, Rat)
 26
 27class Rat(object):
 28
 29    """Rational number implemented as a normalized pair of longs."""
 30
 31    __slots__ = ['_Rat__num', '_Rat__den']
 32
 33    def __init__(self, num=0L, den=1L):
 34        """Constructor: Rat([num[, den]]).
 35
 36        The arguments must be ints or longs, and default to (0, 1)."""
 37        if not isint(num):
 38            raise TypeError, "Rat numerator must be int or long (%r)" % num
 39        if not isint(den):
 40            raise TypeError, "Rat denominator must be int or long (%r)" % den
 41        # But the zero is always on
 42        if den == 0:
 43            raise ZeroDivisionError, "zero denominator"
 44        g = gcd(den, num)
 45        self.__num = long(num//g)
 46        self.__den = long(den//g)
 47
 48    def _get_num(self):
 49        """Accessor function for read-only 'num' attribute of Rat."""
 50        return self.__num
 51    num = property(_get_num, None)
 52
 53    def _get_den(self):
 54        """Accessor function for read-only 'den' attribute of Rat."""
 55        return self.__den
 56    den = property(_get_den, None)
 57
 58    def __repr__(self):
 59        """Convert a Rat to an string resembling a Rat constructor call."""
 60        return "Rat(%d, %d)" % (self.__num, self.__den)
 61
 62    def __str__(self):
 63        """Convert a Rat to a string resembling a decimal numeric value."""
 64        return str(float(self))
 65
 66    def __float__(self):
 67        """Convert a Rat to a float."""
 68        return self.__num*1.0/self.__den
 69
 70    def __int__(self):
 71        """Convert a Rat to an int; self.den must be 1."""
 72        if self.__den == 1:
 73            try:
 74                return int(self.__num)
 75            except OverflowError:
 76                raise OverflowError, ("%s too large to convert to int" %
 77                                      repr(self))
 78        raise ValueError, "can't convert %s to int" % repr(self)
 79
 80    def __long__(self):
 81        """Convert a Rat to an long; self.den must be 1."""
 82        if self.__den == 1:
 83            return long(self.__num)
 84        raise ValueError, "can't convert %s to long" % repr(self)
 85
 86    def __add__(self, other):
 87        """Add two Rats, or a Rat and a number."""
 88        if isint(other):
 89            other = Rat(other)
 90        if isRat(other):
 91            return Rat(self.__num*other.__den + other.__num*self.__den,
 92                       self.__den*other.__den)
 93        if isnum(other):
 94            return float(self) + other
 95        return NotImplemented
 96
 97    __radd__ = __add__
 98
 99    def __sub__(self, other):
100        """Subtract two Rats, or a Rat and a number."""
101        if isint(other):
102            other = Rat(other)
103        if isRat(other):
104            return Rat(self.__num*other.__den - other.__num*self.__den,
105                       self.__den*other.__den)
106        if isnum(other):
107            return float(self) - other
108        return NotImplemented
109
110    def __rsub__(self, other):
111        """Subtract two Rats, or a Rat and a number (reversed args)."""
112        if isint(other):
113            other = Rat(other)
114        if isRat(other):
115            return Rat(other.__num*self.__den - self.__num*other.__den,
116                       self.__den*other.__den)
117        if isnum(other):
118            return other - float(self)
119        return NotImplemented
120
121    def __mul__(self, other):
122        """Multiply two Rats, or a Rat and a number."""
123        if isRat(other):
124            return Rat(self.__num*other.__num, self.__den*other.__den)
125        if isint(other):
126            return Rat(self.__num*other, self.__den)
127        if isnum(other):
128            return float(self)*other
129        return NotImplemented
130
131    __rmul__ = __mul__
132
133    def __truediv__(self, other):
134        """Divide two Rats, or a Rat and a number."""
135        if isRat(other):
136            return Rat(self.__num*other.__den, self.__den*other.__num)
137        if isint(other):
138            return Rat(self.__num, self.__den*other)
139        if isnum(other):
140            return float(self) / other
141        return NotImplemented
142
143    __div__ = __truediv__
144
145    def __rtruediv__(self, other):
146        """Divide two Rats, or a Rat and a number (reversed args)."""
147        if isRat(other):
148            return Rat(other.__num*self.__den, other.__den*self.__num)
149        if isint(other):
150            return Rat(other*self.__den, self.__num)
151        if isnum(other):
152            return other / float(self)
153        return NotImplemented
154
155    __rdiv__ = __rtruediv__
156
157    def __floordiv__(self, other):
158        """Divide two Rats, returning the floored result."""
159        if isint(other):
160            other = Rat(other)
161        elif not isRat(other):
162            return NotImplemented
163        x = self/other
164        return x.__num // x.__den
165
166    def __rfloordiv__(self, other):
167        """Divide two Rats, returning the floored result (reversed args)."""
168        x = other/self
169        return x.__num // x.__den
170
171    def __divmod__(self, other):
172        """Divide two Rats, returning quotient and remainder."""
173        if isint(other):
174            other = Rat(other)
175        elif not isRat(other):
176            return NotImplemented
177        x = self//other
178        return (x, self - other * x)
179
180    def __rdivmod__(self, other):
181        """Divide two Rats, returning quotient and remainder (reversed args)."""
182        if isint(other):
183            other = Rat(other)
184        elif not isRat(other):
185            return NotImplemented
186        return divmod(other, self)
187
188    def __mod__(self, other):
189        """Take one Rat modulo another."""
190        return divmod(self, other)[1]
191
192    def __rmod__(self, other):
193        """Take one Rat modulo another (reversed args)."""
194        return divmod(other, self)[1]
195
196    def __eq__(self, other):
197        """Compare two Rats for equality."""
198        if isint(other):
199            return self.__den == 1 and self.__num == other
200        if isRat(other):
201            return self.__num == other.__num and self.__den == other.__den
202        if isnum(other):
203            return float(self) == other
204        return NotImplemented
205
206    def __ne__(self, other):
207        """Compare two Rats for inequality."""
208        return not self == other
209
210class RatTestCase(unittest.TestCase):
211    """Unit tests for Rat class and its support utilities."""
212
213    def test_gcd(self):
214        self.assertEqual(gcd(10, 12), 2)
215        self.assertEqual(gcd(10, 15), 5)
216        self.assertEqual(gcd(10, 11), 1)
217        self.assertEqual(gcd(100, 15), 5)
218        self.assertEqual(gcd(-10, 2), -2)
219        self.assertEqual(gcd(10, -2), 2)
220        self.assertEqual(gcd(-10, -2), -2)
221        for i in range(1, 20):
222            for j in range(1, 20):
223                self.assert_(gcd(i, j) > 0)
224                self.assert_(gcd(-i, j) < 0)
225                self.assert_(gcd(i, -j) > 0)
226                self.assert_(gcd(-i, -j) < 0)
227
228    def test_constructor(self):
229        a = Rat(10, 15)
230        self.assertEqual(a.num, 2)
231        self.assertEqual(a.den, 3)
232        a = Rat(10L, 15L)
233        self.assertEqual(a.num, 2)
234        self.assertEqual(a.den, 3)
235        a = Rat(10, -15)
236        self.assertEqual(a.num, -2)
237        self.assertEqual(a.den, 3)
238        a = Rat(-10, 15)
239        self.assertEqual(a.num, -2)
240        self.assertEqual(a.den, 3)
241        a = Rat(-10, -15)
242        self.assertEqual(a.num, 2)
243        self.assertEqual(a.den, 3)
244        a = Rat(7)
245        self.assertEqual(a.num, 7)
246        self.assertEqual(a.den, 1)
247        try:
248            a = Rat(1, 0)
249        except ZeroDivisionError:
250            pass
251        else:
252            self.fail("Rat(1, 0) didn't raise ZeroDivisionError")
253        for bad in "0", 0.0, 0j, (), [], {}, None, Rat, unittest:
254            try:
255                a = Rat(bad)
256            except TypeError:
257                pass
258            else:
259                self.fail("Rat(%r) didn't raise TypeError" % bad)
260            try:
261                a = Rat(1, bad)
262            except TypeError:
263                pass
264            else:
265                self.fail("Rat(1, %r) didn't raise TypeError" % bad)
266
267    def test_add(self):
268        self.assertEqual(Rat(2, 3) + Rat(1, 3), 1)
269        self.assertEqual(Rat(2, 3) + 1, Rat(5, 3))
270        self.assertEqual(1 + Rat(2, 3), Rat(5, 3))
271        self.assertEqual(1.0 + Rat(1, 2), 1.5)
272        self.assertEqual(Rat(1, 2) + 1.0, 1.5)
273
274    def test_sub(self):
275        self.assertEqual(Rat(7, 2) - Rat(7, 5), Rat(21, 10))
276        self.assertEqual(Rat(7, 5) - 1, Rat(2, 5))
277        self.assertEqual(1 - Rat(3, 5), Rat(2, 5))
278        self.assertEqual(Rat(3, 2) - 1.0, 0.5)
279        self.assertEqual(1.0 - Rat(1, 2), 0.5)
280
281    def test_mul(self):
282        self.assertEqual(Rat(2, 3) * Rat(5, 7), Rat(10, 21))
283        self.assertEqual(Rat(10, 3) * 3, 10)
284        self.assertEqual(3 * Rat(10, 3), 10)
285        self.assertEqual(Rat(10, 5) * 0.5, 1.0)
286        self.assertEqual(0.5 * Rat(10, 5), 1.0)
287
288    def test_div(self):
289        self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
290        self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
291        self.assertEqual(2 / Rat(5), Rat(2, 5))
292        self.assertEqual(3.0 * Rat(1, 2), 1.5)
293        self.assertEqual(Rat(1, 2) * 3.0, 1.5)
294
295    def test_floordiv(self):
296        self.assertEqual(Rat(10) // Rat(4), 2)
297        self.assertEqual(Rat(10, 3) // Rat(4, 3), 2)
298        self.assertEqual(Rat(10) // 4, 2)
299        self.assertEqual(10 // Rat(4), 2)
300
301    def test_eq(self):
302        self.assertEqual(Rat(10), Rat(20, 2))
303        self.assertEqual(Rat(10), 10)
304        self.assertEqual(10, Rat(10))
305        self.assertEqual(Rat(10), 10.0)
306        self.assertEqual(10.0, Rat(10))
307
308    def test_future_div(self):
309        exec future_test
310
311    # XXX Ran out of steam; TO DO: divmod, div, future division
312
313future_test = """
314from __future__ import division
315self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
316self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
317self.assertEqual(2 / Rat(5), Rat(2, 5))
318self.assertEqual(3.0 * Rat(1, 2), 1.5)
319self.assertEqual(Rat(1, 2) * 3.0, 1.5)
320self.assertEqual(eval('1/2'), 0.5)
321"""
322
323def test_main():
324    test_support.run_unittest(RatTestCase)
325
326
327if __name__ == "__main__":
328    test_main()