| 1 | """Tests for binary operators on subtypes of built-in types."""
|
|---|
| 2 |
|
|---|
| 3 | import unittest
|
|---|
| 4 | from test import test_support
|
|---|
| 5 |
|
|---|
| 6 | def gcd(a, b):
|
|---|
| 7 | """Greatest common divisor using Euclid's algorithm."""
|
|---|
| 8 | while a:
|
|---|
| 9 | a, b = b%a, a
|
|---|
| 10 | return b
|
|---|
| 11 |
|
|---|
| 12 | def isint(x):
|
|---|
| 13 | """Test whether an object is an instance of int or long."""
|
|---|
| 14 | return isinstance(x, int) or isinstance(x, long)
|
|---|
| 15 |
|
|---|
| 16 | def 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 |
|
|---|
| 23 | def isRat(x):
|
|---|
| 24 | """Test wheter an object is an instance of the Rat class."""
|
|---|
| 25 | return isinstance(x, Rat)
|
|---|
| 26 |
|
|---|
| 27 | class 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 |
|
|---|
| 210 | class 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 |
|
|---|
| 313 | future_test = """
|
|---|
| 314 | from __future__ import division
|
|---|
| 315 | self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
|
|---|
| 316 | self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
|
|---|
| 317 | self.assertEqual(2 / Rat(5), Rat(2, 5))
|
|---|
| 318 | self.assertEqual(3.0 * Rat(1, 2), 1.5)
|
|---|
| 319 | self.assertEqual(Rat(1, 2) * 3.0, 1.5)
|
|---|
| 320 | self.assertEqual(eval('1/2'), 0.5)
|
|---|
| 321 | """
|
|---|
| 322 |
|
|---|
| 323 | def test_main():
|
|---|
| 324 | test_support.run_unittest(RatTestCase)
|
|---|
| 325 |
|
|---|
| 326 |
|
|---|
| 327 | if __name__ == "__main__":
|
|---|
| 328 | test_main()
|
|---|