diff -x entries -ur trunk-old/Lib/bisect.py trunk/Lib/bisect.py --- trunk-old/Lib/bisect.py 2009-10-25 21:39:44.000000000 +0200 +++ trunk/Lib/bisect.py 2009-11-01 19:19:16.000000000 +0200 @@ -1,27 +1,34 @@ """Bisection algorithms.""" -def insort_right(a, x, lo=0, hi=None): +def insort_right(a, x, lo=0, hi=None, key=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 right of the rightmost x. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + Optional keyword arg key specifies a function of one argument + that is used to extract a comparison key for each compared element. + It should be considered keyword-only for Py3k compatibility. """ if lo < 0: raise ValueError('lo must be non-negative') if hi is None: hi = len(a) + if key is None: + key = lambda x:x + xval = key(x) while lo < hi: mid = (lo+hi)//2 - if x < a[mid]: hi = mid + if xval < key(a[mid]): hi = mid else: lo = mid+1 a.insert(lo, x) insort = insort_right # backward compatibility -def bisect_right(a, x, lo=0, hi=None): +def bisect_right(a, x, lo=0, hi=None, key=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 @@ -30,41 +37,55 @@ Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + Optional keyword arg key specifies a function of one argument + that is used to extract a comparison key for each compared element. + It should be considered keyword-only for Py3k compatibility. """ if lo < 0: raise ValueError('lo must be non-negative') if hi is None: hi = len(a) + if key is None: + key = lambda x:x + xval = key(x) while lo < hi: mid = (lo+hi)//2 - if x < a[mid]: hi = mid + if xval < key(a[mid]): hi = mid else: lo = mid+1 return lo bisect = bisect_right # backward compatibility -def insort_left(a, x, lo=0, hi=None): +def insort_left(a, x, lo=0, hi=None, key=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. + + Optional keyword arg key specifies a function of one argument + that is used to extract a comparison key for each compared element. + It should be considered keyword-only for Py3k compatibility. """ if lo < 0: raise ValueError('lo must be non-negative') if hi is None: hi = len(a) + if key is None: + key = lambda x:x + xval = key(x) while lo < hi: mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 + if key(a[mid]) < xval: lo = mid+1 else: hi = mid a.insert(lo, x) -def bisect_left(a, x, lo=0, hi=None): +def bisect_left(a, x, lo=0, hi=None, key=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 @@ -73,15 +94,22 @@ Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. + + Optional keyword arg key specifies a function of one argument + that is used to extract a comparison key for each compared element. + It should be considered keyword-only for Py3k compatibility. """ if lo < 0: raise ValueError('lo must be non-negative') if hi is None: hi = len(a) + if key is None: + key = lambda x:x + xval = key(x) while lo < hi: mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 + if key(a[mid]) < xval: lo = mid+1 else: hi = mid return lo diff -x entries -ur trunk-old/Lib/test/test_bisect.py trunk/Lib/test/test_bisect.py --- trunk-old/Lib/test/test_bisect.py 2009-10-25 21:37:38.000000000 +0200 +++ trunk/Lib/test/test_bisect.py 2009-11-01 19:26:45.000000000 +0200 @@ -170,6 +170,86 @@ self.module.insort(a=data, x=25, lo=1, hi=3) self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50]) + def test_key(self): + data = [-10, -20, -30, -40, -50] + self.assertEqual(self.module.bisect_left(data, -25, key=abs), 2) + self.assertEqual(self.module.bisect_right(data, -25, key=abs), 2) + self.assertEqual(self.module.bisect(data, -25, key=abs), 2) + self.module.insort_left(data, -25, key=abs) + self.module.insort_right(data, -25, key=abs) + self.module.insort(data, -25, key=abs) + self.assertEqual(data, [-10, -20, -25, -25, -25, -30, -40, -50]) + + data = [15, 25, 35, 45, 55, 65, 75, 85] + + key = lambda x: x//10 + + self.assertEqual(self.module.bisect_left(data, 22, key=key), 1) + self.assertEqual(self.module.bisect_right(data, 22, key=key), 2) + self.assertEqual(self.module.bisect(data, 22, key=key), 2) + + self.assertEqual(self.module.bisect_left(data, 27, key=key), 1) + self.assertEqual(self.module.bisect_right(data, 27, key=key), 2) + self.assertEqual(self.module.bisect(data, 27, key=key), 2) + + self.module.insort_left(data, 22, key=key) + self.module.insort_right(data, 28, key=key) + self.module.insort(data, 29, key=key) + + + self.module.insort_left(data, 27, key=key) + self.module.insort_right(data, 23, key=key) + self.module.insort(data, 24, key=key) + + self.module.insort_left(data, 41, key=key) + self.module.insort_right(data, 42, key=key) + self.module.insort(data, 43, key=key) + + self.module.insort_left(data, 31, key=key) + self.module.insort_right(data, 32, key=key) + self.module.insort(data, 33, key=key) + + self.module.insort_left(data, 36, key=key) + self.module.insort_right(data, 37, key=key) + self.module.insort(data, 38, key=key) + + self.module.insort_left(data, 46, key=key) + self.module.insort_right(data, 47, key=key) + self.module.insort(data, 48, key=key) + + self.assertEqual(data, [15, 27, 22, 25, 28, 29, 23, 24, + 36, 31, 35, 32, 33, 37, 38, 46, + 41, 45, 42, 43, 47, 48, 55, + 65, 75, 85]) + + #self.assertRaises(TypeError, self.module.bisect, data, 25, 1, 3, key) + #self.assertRaises(TypeError, self.module.bisect_left, + # data, 25, 1, 3, key) + #self.assertRaises(TypeError, self.module.bisect_right, + # data, 25, 1, 3, key) + + #self.assertRaises(TypeError, self.module.insort, data, 25, 1, 3, key) + #self.assertRaises(TypeError, self.module.insort_left, + # data, 25, 1, 3, key) + #self.assertRaises(TypeError, self.module.insort_right, + # data, 25, 1, 3, key) + + def test_key_none(self): + data = [10, 20, 30, 40, 50] + self.assertEqual(self.module.bisect_left(data, 25, key=None), + self.module.bisect_left(data, 25)) + self.assertEqual(self.module.bisect_right(data, 25, key=None), + self.module.bisect_right(data, 25)) + self.assertEqual(self.module.bisect(data, 25, key=None), + self.module.bisect(data, 25)) + + self.module.insort_left(data, 25, key=None) + self.module.insort_right(data, 25, key=None) + self.module.insort(data, 25, key=None) + + self.assertEqual(data, [10, 20, 25, 25, 25, 30, 40, 50]) + + class TestBisectPython(TestBisect): module = py_bisect diff -x entries -ur trunk-old/Modules/_bisectmodule.c trunk/Modules/_bisectmodule.c --- trunk-old/Modules/_bisectmodule.c 2009-10-25 21:40:17.000000000 +0200 +++ trunk/Modules/_bisectmodule.c 2009-11-01 19:20:41.000000000 +0200 @@ -6,11 +6,12 @@ #include "Python.h" static Py_ssize_t -internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi) +internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) { PyObject *litem; + PyObject *item_val = NULL, *litem_val; Py_ssize_t mid, res; - + if (lo < 0) { PyErr_SetString(PyExc_ValueError, "lo must be non-negative"); return -1; @@ -20,20 +21,45 @@ if (hi < 0) return -1; } + if (key != Py_None) { + item_val = PyObject_CallFunctionObjArgs(key, item, NULL); + if (item_val == NULL) + return -1; + } while (lo < hi) { mid = (lo + hi) / 2; + litem = PySequence_GetItem(list, mid); - if (litem == NULL) + if (litem == NULL) { + Py_XDECREF(item_val); return -1; - res = PyObject_RichCompareBool(item, litem, Py_LT); + } + + if (key != Py_None) { + litem_val = PyObject_CallFunctionObjArgs(key, litem, NULL); + if (litem_val == NULL) + res = -1; + else { + res = PyObject_RichCompareBool(item_val, litem_val, Py_LT); + Py_DECREF(litem_val); + } + } + else + res = PyObject_RichCompareBool(item, litem, Py_LT); Py_DECREF(litem); - if (res < 0) + + if (res < 0) { + Py_XDECREF(item_val); return -1; + } + if (res) hi = mid; else lo = mid + 1; } + + Py_XDECREF(item_val); return lo; } @@ -41,22 +67,23 @@ bisect_right(PyObject *self, PyObject *args, PyObject *kw) { PyObject *list, *item; + PyObject *key = Py_None; Py_ssize_t lo = 0; Py_ssize_t hi = -1; Py_ssize_t index; - static char *keywords[] = {"a", "x", "lo", "hi", NULL}; + static char *keywords[] = {"a", "x", "lo", "hi", "key", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right", - keywords, &list, &item, &lo, &hi)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nnO:bisect_right", + keywords, &list, &item, &lo, &hi, &key)) return NULL; - index = internal_bisect_right(list, item, lo, hi); + index = internal_bisect_right(list, item, lo, hi, key); if (index < 0) return NULL; return PyInt_FromSsize_t(index); } PyDoc_STRVAR(bisect_right_doc, -"bisect_right(a, x[, lo[, hi]]) -> index\n\ +"bisect_right(a, x, lo=0, hi=.., key=None) -> index\n\ \n\ Return the index where to insert item x in list a, assuming a is sorted.\n\ \n\ @@ -65,21 +92,26 @@ beyond the rightmost x already there\n\ \n\ Optional args lo (default 0) and hi (default len(a)) bound the\n\ -slice of a to be searched.\n"); +slice of a to be searched.\n\ +\n\ +Optional keyword arg key specifies a function of one argument\n\ +that is used to extract a comparison key for each compared element.\n\ +It should be considered keyword-only for Py3k compatibility.\n"); static PyObject * insort_right(PyObject *self, PyObject *args, PyObject *kw) { PyObject *list, *item, *result; + PyObject *key = Py_None; Py_ssize_t lo = 0; Py_ssize_t hi = -1; Py_ssize_t index; - static char *keywords[] = {"a", "x", "lo", "hi", NULL}; + static char *keywords[] = {"a", "x", "lo", "hi", "key", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right", - keywords, &list, &item, &lo, &hi)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nnO:insort_right", + keywords, &list, &item, &lo, &hi, &key)) return NULL; - index = internal_bisect_right(list, item, lo, hi); + index = internal_bisect_right(list, item, lo, hi, key); if (index < 0) return NULL; if (PyList_CheckExact(list)) { @@ -97,19 +129,24 @@ } PyDoc_STRVAR(insort_right_doc, -"insort_right(a, x[, lo[, hi]])\n\ +"insort_right(a, x, lo=0, hi=.., key=None)\n\ \n\ Insert item x in list a, and keep it sorted assuming a is sorted.\n\ \n\ If x is already in a, insert it to the right of the rightmost x.\n\ \n\ Optional args lo (default 0) and hi (default len(a)) bound the\n\ -slice of a to be searched.\n"); +slice of a to be searched.\n\ +\n\ +Optional keyword arg key specifies a function of one argument\n\ +that is used to extract a comparison key for each compared element.\n\ +It should be considered keyword-only for Py3k compatibility.\n"); static Py_ssize_t -internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi) +internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) { PyObject *litem; + PyObject *item_val = NULL, *litem_val; Py_ssize_t mid, res; if (lo < 0) { @@ -121,20 +158,45 @@ if (hi < 0) return -1; } + if (key != Py_None) { + item_val = PyObject_CallFunctionObjArgs(key, item, NULL); + if (item_val == NULL) + return -1; + } while (lo < hi) { mid = (lo + hi) / 2; + litem = PySequence_GetItem(list, mid); - if (litem == NULL) + if (litem == NULL) { + Py_XDECREF(item_val); return -1; - res = PyObject_RichCompareBool(litem, item, Py_LT); + } + + if (key != Py_None) { + litem_val = PyObject_CallFunctionObjArgs(key, litem, NULL); + if (litem_val == NULL) + res = -1; + else { + res = PyObject_RichCompareBool(litem_val, item_val, Py_LT); + Py_DECREF(litem_val); + } + } + else + res = PyObject_RichCompareBool(litem, item, Py_LT); Py_DECREF(litem); - if (res < 0) + + if (res < 0) { + Py_XDECREF(item_val); return -1; + } + if (res) lo = mid + 1; else hi = mid; } + + Py_XDECREF(item_val); return lo; } @@ -142,22 +204,23 @@ bisect_left(PyObject *self, PyObject *args, PyObject *kw) { PyObject *list, *item; + PyObject *key = Py_None; Py_ssize_t lo = 0; Py_ssize_t hi = -1; Py_ssize_t index; - static char *keywords[] = {"a", "x", "lo", "hi", NULL}; + static char *keywords[] = {"a", "x", "lo", "hi", "key", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left", - keywords, &list, &item, &lo, &hi)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nnO:bisect_left", + keywords, &list, &item, &lo, &hi, &key)) return NULL; - index = internal_bisect_left(list, item, lo, hi); + index = internal_bisect_left(list, item, lo, hi, key); if (index < 0) return NULL; return PyInt_FromSsize_t(index); } PyDoc_STRVAR(bisect_left_doc, -"bisect_left(a, x[, lo[, hi]]) -> index\n\ +"bisect_left(a, x, lo=0, hi=.., key=None) -> index\n\ \n\ Return the index where to insert item x in list a, assuming a is sorted.\n\ \n\ @@ -166,21 +229,26 @@ before the leftmost x already there.\n\ \n\ Optional args lo (default 0) and hi (default len(a)) bound the\n\ -slice of a to be searched.\n"); +slice of a to be searched.\n\ +\n\ +Optional keyword arg key specifies a function of one argument\n\ +that is used to extract a comparison key for each compared element.\n\ +It should be considered keyword-only for Py3k compatibility.\n"); static PyObject * insort_left(PyObject *self, PyObject *args, PyObject *kw) { PyObject *list, *item, *result; + PyObject *key = Py_None; Py_ssize_t lo = 0; Py_ssize_t hi = -1; Py_ssize_t index; - static char *keywords[] = {"a", "x", "lo", "hi", NULL}; + static char *keywords[] = {"a", "x", "lo", "hi", "key", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left", - keywords, &list, &item, &lo, &hi)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nnO:insort_left", + keywords, &list, &item, &lo, &hi, &key)) return NULL; - index = internal_bisect_left(list, item, lo, hi); + index = internal_bisect_left(list, item, lo, hi, key); if (index < 0) return NULL; if (PyList_CheckExact(list)) { @@ -198,14 +266,18 @@ } PyDoc_STRVAR(insort_left_doc, -"insort_left(a, x[, lo[, hi]])\n\ +"insort_left(a, x, lo=0, hi=.., key=None)\n\ \n\ Insert item x in list a, and keep it sorted assuming a is sorted.\n\ \n\ If x is already in a, insert it to the left of the leftmost x.\n\ \n\ Optional args lo (default 0) and hi (default len(a)) bound the\n\ -slice of a to be searched.\n"); +slice of a to be searched.\n\ +\n\ +Optional keyword arg key specifies a function of one argument\n\ +that is used to extract a comparison key for each compared element.\n\ +It should be considered keyword-only for Py3k compatibility.\n"); PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n"); PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");