Index: Doc/library/bisect.rst =================================================================== --- Doc/library/bisect.rst (revision 64829) +++ Doc/library/bisect.rst (working copy) @@ -16,52 +16,68 @@ The following functions are provided: +.. function:: bisect(list, item[, lo[, hi[, cmp[, key[, reverse[, right]]]]]]) -.. function:: bisect_left(list, item[, lo[, hi]]) - Locate the proper insertion point for *item* in *list* to maintain sorted order. The parameters *lo* and *hi* may be used to specify a subset of the list which - should be considered; by default the entire list is used. If *item* is already - present in *list*, the insertion point will be before (to the left of) any - existing entries. The return value is suitable for use as the first parameter - to ``list.insert()``. This assumes that *list* is already sorted. + should be considered; by default the entire list is used. This assumes that + *list* is already sorted. + + If *item* (or something that compares equal to *item*) is already in *list*, + the insertion point will be at one end of the group of the existing entries: + to the right if *right* is ``True`` or ommitted, to the left if *right* is + ``False``. + + The optional arguments *cmp*, *key*, and *reverse* have the same meaning as + those for the ``list.sort()`` method (described in sectionĘ3.6.4). + Changed in version 2.7: *cmp*, *key*, *reverse*, and *right* parameters added. + .. versionadded:: 2.1 + +.. function:: bisect_left(list, item[, lo[, hi]]) -.. function:: bisect_right(list, item[, lo[, hi]]) + A wrapper around :func:`bisect`, but with the *right* parameter set to False. + Returns an insertion point just before (to the left of) any existing entries + of *item* in *list* - Similar to :func:`bisect_left`, but returns an insertion point which comes after - (to the right of) any existing entries of *item* in *list*. + Changed in version 2.7: *cmp*, *key*, and *reverse* parameters added. .. versionadded:: 2.1 +.. function:: bisect_right(list, item[, lo[, hi[, cmp[, key[, reverse]]]]]) -.. function:: bisect(...) + A wrapper around :func:`bisect`, but with the *right* parameter forced to True. + Returns an insertion point just after (to the right of) any existing entries + of *item* in *list*. This function is mostly for backwards compatibility. - Alias for :func:`bisect_right`. + Changed in version 2.7: *cmp*, *key*, and *reverse* parameters added. + .. versionadded:: 2.1 -.. function:: insort_left(list, item[, lo[, hi]]) +.. function:: insort(list, item[, lo[, hi[, cmp[, key[, reverse[, right]]]]]]) Insert *item* in *list* in sorted order. This is equivalent to - ``list.insert(bisect.bisect_left(list, item, lo, hi), item)``. This assumes - that *list* is already sorted. + ``list.insert(bisect.bisect(list, item, lo, hi, cmp, key, reverse, right), item)``. + This assumes that *list* is already sorted. - .. versionadded:: 2.1 +.. function:: insort_left(list, item[, lo[, hi[, cmp[, key[, reverse]]]]]) + Like insort, except it uses bisect_left instead of bisect. -.. function:: insort_right(list, item[, lo[, hi]]) + Changed in version 2.7: *cmp*, *key*, and *reverse* parameters added. - Similar to :func:`insort_left`, but inserting *item* in *list* after any - existing entries of *item*. - .. versionadded:: 2.1 -.. function:: insort(...) +.. function:: insort_right(list, item[, lo[, hi[, cmp[, key[, reverse]]]]]) - Alias for :func:`insort_right`. + Like insort, except it uses bisect_right instead of bisect. + + Changed in version 2.7: *cmp*, *key*, and *reverse* parameters added. + + .. versionadded:: 2.1 Examples Index: Lib/bisect.py =================================================================== --- Lib/bisect.py (revision 64829) +++ Lib/bisect.py (working copy) @@ -1,84 +1,127 @@ -"""Bisection algorithms.""" +#!/usr/bin/env python +"""Array bisection algorithms for binary search and insert.""" -def insort_right(a, x, lo=0, hi=None): - """Insert item x in list a, and keep it sorted assuming a is sorted. +import __builtin__ - If x is already in a, insert it to the right of the rightmost x. - +def bisect(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False, right=True): + """Return the index to insert item x in sorted sequence a that will keep + a sorted. + + The return value i is such that all e in a[:i] have e < x, and all e in + a[i:] have e > x. If right is True or omitted, then i is just beyond the + rightmost index that compares equally to x; if False, then i is just before + the leftmost index that compares equally to x. (right parameter new in this + version) + Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). """ - - if hi is None: + if cmp is None: # Allow cmp and key to be None, like sorted() + cmp = __builtin__.cmp + if key is None: + key = lambda k: k + if hi is None: # can't set hi to the right value in defaults hi = len(a) + target = key(x) while lo < hi: mid = (lo+hi)//2 - if x < a[mid]: hi = mid - else: lo = mid+1 - a.insert(lo, x) + current = key(a[mid]) + if right: + if not reverse and cmp(target, current) < 0 or reverse and cmp(current,target) < 0: + hi = mid + else: + lo = mid+1 + else: + if not reverse and cmp(current, target) < 0 or reverse and cmp(target,current) < 0: + lo = mid+1 + else: + hi = mid + return lo -insort = insort_right # backward compatibility +def insort(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False, right=True): + """Insert item x in list a, and keep it sorted, assuming a is sorted. -def bisect_right(a, x, lo=0, hi=None): - """Return the index where to insert item x in list a, assuming a is sorted. + If x is already in a: if right is True or omitted, then x is inserted just + beyond the rightmost index that compares equally to x; if False, then x is + inserted just before the leftmost index that compares equally to x. (right + parameter new in this version) + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + + (new in this version) if x is already in a: if right is True or omitted, + then x is inserted just beyond the rightmost index that compares equally to + x; if false, then x is inserted just before the leftmost index that compares + equally to x. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). + """ + a.insert(bisect(a, x, lo, hi, cmp, key, reverse, right), x) + +def bisect_right(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False): + """Return the index where to insert item x in sequence a, assuming a is + sorted. + The return value i is such that all e in a[:i] have e <= x, and all e in - a[i:] have e > x. So if x already appears in the list, a.insert(x) will - insert just after the rightmost x already there. - + a[i:] have e > x. So if x already appears in the sequence, i points just + beyond the rightmost x already there. + Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). """ + return bisect(a, x, lo, hi, cmp, key, reverse, True) + +def bisect_left(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False): + """Return the index where to insert item x in sequence a, assuming a is sorted. + + The return value i is such that all e in a[:i] have e < x, and all e in + a[i:] have e => x. So if x already appears in the sequence, i points just + before the leftmost x already there. + + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). + """ + return bisect(a, x, lo, hi, cmp, key, reverse, False) - if hi is None: - hi = len(a) - while lo < hi: - mid = (lo+hi)//2 - if x < a[mid]: hi = mid - else: lo = mid+1 - return lo +def insort_right(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False): + """Insert item x in list a, and keep it sorted, assuming a is sorted. -bisect = bisect_right # backward compatibility + If x is already in a, insert it to the right of the rightmost x. -def insort_left(a, x, lo=0, hi=None): - """Insert item x in list a, and keep it sorted assuming a is sorted. - - If x is already in a, insert it to the left of the leftmost x. - Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). """ + insort(a, x, lo, hi, cmp, key, reverse, True) + +def insort_left(a, x, lo=0, hi=None, cmp=None, key=None, reverse=False): + """Insert item x in list a, and keep it sorted, assuming a is sorted. - if hi is None: - hi = len(a) - while lo < hi: - mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 - else: hi = mid - a.insert(lo, x) + If x is already in a, insert it to the left of the leftmost x. - -def bisect_left(a, x, lo=0, hi=None): - """Return the index where to insert item x in list a, assuming a is sorted. - - The return value i is such that all e in a[:i] have e < x, and all e in - a[i:] have e >= x. So if x already appears in the list, a.insert(x) will - insert just before the leftmost x already there. - Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + (new in this version) Optional args cmp, key, and reverse work like they do + for the built-in function sorted(). """ + insort(a, x, lo, hi, cmp, key, reverse, False) - if hi is None: - hi = len(a) - while lo < hi: - mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 - else: hi = mid - return lo - # Overwrite above definitions with a fast C implementation -try: - from _bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect -except ImportError: - pass +# Cut out because there's no C implementation for this one yet. +#try: +# from _bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect +#except ImportError: +# pass Index: Lib/test/test_bisect.py =================================================================== --- Lib/test/test_bisect.py (revision 64829) +++ Lib/test/test_bisect.py (working copy) @@ -149,9 +149,6 @@ self.failUnless(data[ip-1] <= elem) self.assertEqual(ip, max(lo, min(hi, expected))) - def test_backcompatibility(self): - self.assertEqual(self.module.bisect, self.module.bisect_right) - def test_keyword_args(self): data = [10, 20, 30, 40, 50] self.assertEqual(self.module.bisect_left(a=data, x=25, lo=1, hi=3), 2) @@ -162,6 +159,24 @@ self.module.insort(a=data, x=25, lo=1, hi=3) self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50]) + def test_handedness(self): + self.assertEqual(self.module.bisect([1,2,3,3,4,5], 3), 4) + self.assertEqual(self.module.bisect([1,2,3,3,4,5], 3, right=True), 4) + self.assertEqual(self.module.bisect([1,2,3,3,4,5], 3, right=False), 2) + + def test_reverse(self): + self.assertEqual(self.module.bisect([5,4,3,3,2,1], 3, reverse=True), 4) + self.assertEqual(self.module.bisect([5,4,3,3,2,1], 3, reverse=True, right=False), 2) + + def test_key(self): + self.assertEqual(self.module.bisect(['A','B','c','d','e'], 'D', key=lambda k: k.lower()), 4) + self.assertEqual(self.module.bisect(['A','B','c','d','e'], 'D', key=lambda k: k.lower(), right=False), 3) + + def test_cmp(self): + self.assertEqual(self.module.bisect(['A','B','c','d','e'], 'D', cmp = lambda a,b: cmp(a.lower(), b.lower())), 4) + self.assertEqual(self.module.bisect(['A','B','c','d','e'], 'D', cmp = lambda a,b: cmp(a.lower(), b.lower()), right=False), 3) + + class TestBisectPython(TestBisect): module = py_bisect @@ -185,8 +200,6 @@ f(insorted, digit) self.assertEqual(sorted(insorted), insorted) - def test_backcompatibility(self): - self.assertEqual(self.module.insort, self.module.insort_right) class TestInsortPython(TestInsort): module = py_bisect