test_bisect.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import sys
  2. import unittest
  3. from test import test_support
  4. from UserList import UserList
  5. # We do a bit of trickery here to be able to test both the C implementation
  6. # and the Python implementation of the module.
  7. # Make it impossible to import the C implementation anymore.
  8. sys.modules['_bisect'] = 0
  9. # We must also handle the case that bisect was imported before.
  10. if 'bisect' in sys.modules:
  11. del sys.modules['bisect']
  12. # Now we can import the module and get the pure Python implementation.
  13. import bisect as py_bisect
  14. # Restore everything to normal.
  15. del sys.modules['_bisect']
  16. del sys.modules['bisect']
  17. # This is now the module with the C implementation.
  18. import bisect as c_bisect
  19. class Range(object):
  20. """A trivial xrange()-like object without any integer width limitations."""
  21. def __init__(self, start, stop):
  22. self.start = start
  23. self.stop = stop
  24. self.last_insert = None
  25. def __len__(self):
  26. return self.stop - self.start
  27. def __getitem__(self, idx):
  28. n = self.stop - self.start
  29. if idx < 0:
  30. idx += n
  31. if idx >= n:
  32. raise IndexError(idx)
  33. return self.start + idx
  34. def insert(self, idx, item):
  35. self.last_insert = idx, item
  36. class TestBisect(unittest.TestCase):
  37. module = None
  38. def setUp(self):
  39. self.precomputedCases = [
  40. (self.module.bisect_right, [], 1, 0),
  41. (self.module.bisect_right, [1], 0, 0),
  42. (self.module.bisect_right, [1], 1, 1),
  43. (self.module.bisect_right, [1], 2, 1),
  44. (self.module.bisect_right, [1, 1], 0, 0),
  45. (self.module.bisect_right, [1, 1], 1, 2),
  46. (self.module.bisect_right, [1, 1], 2, 2),
  47. (self.module.bisect_right, [1, 1, 1], 0, 0),
  48. (self.module.bisect_right, [1, 1, 1], 1, 3),
  49. (self.module.bisect_right, [1, 1, 1], 2, 3),
  50. (self.module.bisect_right, [1, 1, 1, 1], 0, 0),
  51. (self.module.bisect_right, [1, 1, 1, 1], 1, 4),
  52. (self.module.bisect_right, [1, 1, 1, 1], 2, 4),
  53. (self.module.bisect_right, [1, 2], 0, 0),
  54. (self.module.bisect_right, [1, 2], 1, 1),
  55. (self.module.bisect_right, [1, 2], 1.5, 1),
  56. (self.module.bisect_right, [1, 2], 2, 2),
  57. (self.module.bisect_right, [1, 2], 3, 2),
  58. (self.module.bisect_right, [1, 1, 2, 2], 0, 0),
  59. (self.module.bisect_right, [1, 1, 2, 2], 1, 2),
  60. (self.module.bisect_right, [1, 1, 2, 2], 1.5, 2),
  61. (self.module.bisect_right, [1, 1, 2, 2], 2, 4),
  62. (self.module.bisect_right, [1, 1, 2, 2], 3, 4),
  63. (self.module.bisect_right, [1, 2, 3], 0, 0),
  64. (self.module.bisect_right, [1, 2, 3], 1, 1),
  65. (self.module.bisect_right, [1, 2, 3], 1.5, 1),
  66. (self.module.bisect_right, [1, 2, 3], 2, 2),
  67. (self.module.bisect_right, [1, 2, 3], 2.5, 2),
  68. (self.module.bisect_right, [1, 2, 3], 3, 3),
  69. (self.module.bisect_right, [1, 2, 3], 4, 3),
  70. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0),
  71. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 1),
  72. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1),
  73. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 3),
  74. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3),
  75. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 6),
  76. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6),
  77. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 10),
  78. (self.module.bisect_right, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10),
  79. (self.module.bisect_left, [], 1, 0),
  80. (self.module.bisect_left, [1], 0, 0),
  81. (self.module.bisect_left, [1], 1, 0),
  82. (self.module.bisect_left, [1], 2, 1),
  83. (self.module.bisect_left, [1, 1], 0, 0),
  84. (self.module.bisect_left, [1, 1], 1, 0),
  85. (self.module.bisect_left, [1, 1], 2, 2),
  86. (self.module.bisect_left, [1, 1, 1], 0, 0),
  87. (self.module.bisect_left, [1, 1, 1], 1, 0),
  88. (self.module.bisect_left, [1, 1, 1], 2, 3),
  89. (self.module.bisect_left, [1, 1, 1, 1], 0, 0),
  90. (self.module.bisect_left, [1, 1, 1, 1], 1, 0),
  91. (self.module.bisect_left, [1, 1, 1, 1], 2, 4),
  92. (self.module.bisect_left, [1, 2], 0, 0),
  93. (self.module.bisect_left, [1, 2], 1, 0),
  94. (self.module.bisect_left, [1, 2], 1.5, 1),
  95. (self.module.bisect_left, [1, 2], 2, 1),
  96. (self.module.bisect_left, [1, 2], 3, 2),
  97. (self.module.bisect_left, [1, 1, 2, 2], 0, 0),
  98. (self.module.bisect_left, [1, 1, 2, 2], 1, 0),
  99. (self.module.bisect_left, [1, 1, 2, 2], 1.5, 2),
  100. (self.module.bisect_left, [1, 1, 2, 2], 2, 2),
  101. (self.module.bisect_left, [1, 1, 2, 2], 3, 4),
  102. (self.module.bisect_left, [1, 2, 3], 0, 0),
  103. (self.module.bisect_left, [1, 2, 3], 1, 0),
  104. (self.module.bisect_left, [1, 2, 3], 1.5, 1),
  105. (self.module.bisect_left, [1, 2, 3], 2, 1),
  106. (self.module.bisect_left, [1, 2, 3], 2.5, 2),
  107. (self.module.bisect_left, [1, 2, 3], 3, 2),
  108. (self.module.bisect_left, [1, 2, 3], 4, 3),
  109. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0),
  110. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0),
  111. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 1),
  112. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 1),
  113. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 3),
  114. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 3),
  115. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 6),
  116. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 6),
  117. (self.module.bisect_left, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 10)
  118. ]
  119. def test_precomputed(self):
  120. for func, data, elem, expected in self.precomputedCases:
  121. self.assertEqual(func(data, elem), expected)
  122. self.assertEqual(func(UserList(data), elem), expected)
  123. def test_negative_lo(self):
  124. # Issue 3301
  125. mod = self.module
  126. self.assertRaises(ValueError, mod.bisect_left, [1, 2, 3], 5, -1, 3),
  127. self.assertRaises(ValueError, mod.bisect_right, [1, 2, 3], 5, -1, 3),
  128. self.assertRaises(ValueError, mod.insort_left, [1, 2, 3], 5, -1, 3),
  129. self.assertRaises(ValueError, mod.insort_right, [1, 2, 3], 5, -1, 3),
  130. def test_large_range(self):
  131. # Issue 13496
  132. mod = self.module
  133. n = sys.maxsize
  134. try:
  135. data = xrange(n-1)
  136. except OverflowError:
  137. self.skipTest("can't create a xrange() object of size `sys.maxsize`")
  138. self.assertEqual(mod.bisect_left(data, n-3), n-3)
  139. self.assertEqual(mod.bisect_right(data, n-3), n-2)
  140. self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3)
  141. self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2)
  142. def test_large_pyrange(self):
  143. # Same as above, but without C-imposed limits on range() parameters
  144. mod = self.module
  145. n = sys.maxsize
  146. data = Range(0, n-1)
  147. self.assertEqual(mod.bisect_left(data, n-3), n-3)
  148. self.assertEqual(mod.bisect_right(data, n-3), n-2)
  149. self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3)
  150. self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2)
  151. x = n - 100
  152. mod.insort_left(data, x, x - 50, x + 50)
  153. self.assertEqual(data.last_insert, (x, x))
  154. x = n - 200
  155. mod.insort_right(data, x, x - 50, x + 50)
  156. self.assertEqual(data.last_insert, (x + 1, x))
  157. def test_random(self, n=25):
  158. from random import randrange
  159. for i in xrange(n):
  160. data = [randrange(0, n, 2) for j in xrange(i)]
  161. data.sort()
  162. elem = randrange(-1, n+1)
  163. ip = self.module.bisect_left(data, elem)
  164. if ip < len(data):
  165. self.assertTrue(elem <= data[ip])
  166. if ip > 0:
  167. self.assertTrue(data[ip-1] < elem)
  168. ip = self.module.bisect_right(data, elem)
  169. if ip < len(data):
  170. self.assertTrue(elem < data[ip])
  171. if ip > 0:
  172. self.assertTrue(data[ip-1] <= elem)
  173. def test_optionalSlicing(self):
  174. for func, data, elem, expected in self.precomputedCases:
  175. for lo in xrange(4):
  176. lo = min(len(data), lo)
  177. for hi in xrange(3,8):
  178. hi = min(len(data), hi)
  179. ip = func(data, elem, lo, hi)
  180. self.assertTrue(lo <= ip <= hi)
  181. if func is self.module.bisect_left and ip < hi:
  182. self.assertTrue(elem <= data[ip])
  183. if func is self.module.bisect_left and ip > lo:
  184. self.assertTrue(data[ip-1] < elem)
  185. if func is self.module.bisect_right and ip < hi:
  186. self.assertTrue(elem < data[ip])
  187. if func is self.module.bisect_right and ip > lo:
  188. self.assertTrue(data[ip-1] <= elem)
  189. self.assertEqual(ip, max(lo, min(hi, expected)))
  190. def test_backcompatibility(self):
  191. self.assertEqual(self.module.bisect, self.module.bisect_right)
  192. def test_keyword_args(self):
  193. data = [10, 20, 30, 40, 50]
  194. self.assertEqual(self.module.bisect_left(a=data, x=25, lo=1, hi=3), 2)
  195. self.assertEqual(self.module.bisect_right(a=data, x=25, lo=1, hi=3), 2)
  196. self.assertEqual(self.module.bisect(a=data, x=25, lo=1, hi=3), 2)
  197. self.module.insort_left(a=data, x=25, lo=1, hi=3)
  198. self.module.insort_right(a=data, x=25, lo=1, hi=3)
  199. self.module.insort(a=data, x=25, lo=1, hi=3)
  200. self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50])
  201. class TestBisectPython(TestBisect):
  202. module = py_bisect
  203. class TestBisectC(TestBisect):
  204. module = c_bisect
  205. #==============================================================================
  206. class TestInsort(unittest.TestCase):
  207. module = None
  208. def test_vsBuiltinSort(self, n=500):
  209. from random import choice
  210. for insorted in (list(), UserList()):
  211. for i in xrange(n):
  212. digit = choice("0123456789")
  213. if digit in "02468":
  214. f = self.module.insort_left
  215. else:
  216. f = self.module.insort_right
  217. f(insorted, digit)
  218. self.assertEqual(sorted(insorted), insorted)
  219. def test_backcompatibility(self):
  220. self.assertEqual(self.module.insort, self.module.insort_right)
  221. def test_listDerived(self):
  222. class List(list):
  223. data = []
  224. def insert(self, index, item):
  225. self.data.insert(index, item)
  226. lst = List()
  227. self.module.insort_left(lst, 10)
  228. self.module.insort_right(lst, 5)
  229. self.assertEqual([5, 10], lst.data)
  230. class TestInsortPython(TestInsort):
  231. module = py_bisect
  232. class TestInsortC(TestInsort):
  233. module = c_bisect
  234. #==============================================================================
  235. class LenOnly:
  236. "Dummy sequence class defining __len__ but not __getitem__."
  237. def __len__(self):
  238. return 10
  239. class GetOnly:
  240. "Dummy sequence class defining __getitem__ but not __len__."
  241. def __getitem__(self, ndx):
  242. return 10
  243. class CmpErr:
  244. "Dummy element that always raises an error during comparison"
  245. def __cmp__(self, other):
  246. raise ZeroDivisionError
  247. class TestErrorHandling(unittest.TestCase):
  248. module = None
  249. def test_non_sequence(self):
  250. for f in (self.module.bisect_left, self.module.bisect_right,
  251. self.module.insort_left, self.module.insort_right):
  252. self.assertRaises(TypeError, f, 10, 10)
  253. def test_len_only(self):
  254. for f in (self.module.bisect_left, self.module.bisect_right,
  255. self.module.insort_left, self.module.insort_right):
  256. self.assertRaises(AttributeError, f, LenOnly(), 10)
  257. def test_get_only(self):
  258. for f in (self.module.bisect_left, self.module.bisect_right,
  259. self.module.insort_left, self.module.insort_right):
  260. self.assertRaises(AttributeError, f, GetOnly(), 10)
  261. def test_cmp_err(self):
  262. seq = [CmpErr(), CmpErr(), CmpErr()]
  263. for f in (self.module.bisect_left, self.module.bisect_right,
  264. self.module.insort_left, self.module.insort_right):
  265. self.assertRaises(ZeroDivisionError, f, seq, 10)
  266. def test_arg_parsing(self):
  267. for f in (self.module.bisect_left, self.module.bisect_right,
  268. self.module.insort_left, self.module.insort_right):
  269. self.assertRaises(TypeError, f, 10)
  270. class TestErrorHandlingPython(TestErrorHandling):
  271. module = py_bisect
  272. class TestErrorHandlingC(TestErrorHandling):
  273. module = c_bisect
  274. #==============================================================================
  275. libreftest = """
  276. Example from the Library Reference: Doc/library/bisect.rst
  277. The bisect() function is generally useful for categorizing numeric data.
  278. This example uses bisect() to look up a letter grade for an exam total
  279. (say) based on a set of ordered numeric breakpoints: 85 and up is an `A',
  280. 75..84 is a `B', etc.
  281. >>> grades = "FEDCBA"
  282. >>> breakpoints = [30, 44, 66, 75, 85]
  283. >>> from bisect import bisect
  284. >>> def grade(total):
  285. ... return grades[bisect(breakpoints, total)]
  286. ...
  287. >>> grade(66)
  288. 'C'
  289. >>> map(grade, [33, 99, 77, 44, 12, 88])
  290. ['E', 'A', 'B', 'D', 'F', 'A']
  291. """
  292. #------------------------------------------------------------------------------
  293. __test__ = {'libreftest' : libreftest}
  294. def test_main(verbose=None):
  295. from test import test_bisect
  296. test_classes = [TestBisectPython, TestBisectC,
  297. TestInsortPython, TestInsortC,
  298. TestErrorHandlingPython, TestErrorHandlingC]
  299. test_support.run_unittest(*test_classes)
  300. test_support.run_doctest(test_bisect, verbose)
  301. # verify reference counting
  302. if verbose and hasattr(sys, "gettotalrefcount"):
  303. import gc
  304. counts = [None] * 5
  305. for i in xrange(len(counts)):
  306. test_support.run_unittest(*test_classes)
  307. gc.collect()
  308. counts[i] = sys.gettotalrefcount()
  309. print counts
  310. if __name__ == "__main__":
  311. test_main(verbose=True)