test_binop.py 10 KB

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