test_binop.py 13 KB

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