SF Patch #864863: Bisect C implementation
(Contributed by Dmitry Vasiliev.)
This commit is contained in:
parent
23a0f4ed21
commit
0c4102760c
|
@ -305,6 +305,10 @@ details.
|
||||||
supports transparency, this makes it possible to use a transparent background.
|
supports transparency, this makes it possible to use a transparent background.
|
||||||
(Contributed by J\"org Lehmann.)
|
(Contributed by J\"org Lehmann.)
|
||||||
|
|
||||||
|
\item The \module{bisect} module now has an underlying C implementation
|
||||||
|
for improved performance.
|
||||||
|
(Contributed by Dmitry Vasiliev.)
|
||||||
|
|
||||||
\item The \module{heapq} module has been converted to C. The resulting
|
\item The \module{heapq} module has been converted to C. The resulting
|
||||||
ten-fold improvement in speed makes the module suitable for handling
|
ten-fold improvement in speed makes the module suitable for handling
|
||||||
high volumes of data.
|
high volumes of data.
|
||||||
|
|
|
@ -76,3 +76,9 @@ def bisect_left(a, x, lo=0, hi=None):
|
||||||
if a[mid] < x: lo = mid+1
|
if a[mid] < x: lo = mid+1
|
||||||
else: hi = mid
|
else: hi = mid
|
||||||
return lo
|
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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
from test import test_support
|
from test import test_support
|
||||||
from bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect
|
from bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect
|
||||||
|
from UserList import UserList
|
||||||
|
|
||||||
class TestBisect(unittest.TestCase):
|
class TestBisect(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ class TestBisect(unittest.TestCase):
|
||||||
def test_precomputed(self):
|
def test_precomputed(self):
|
||||||
for func, data, elem, expected in self.precomputedCases:
|
for func, data, elem, expected in self.precomputedCases:
|
||||||
self.assertEqual(func(data, elem), expected)
|
self.assertEqual(func(data, elem), expected)
|
||||||
|
self.assertEqual(func(UserList(data), elem), expected)
|
||||||
|
|
||||||
def test_random(self, n=25):
|
def test_random(self, n=25):
|
||||||
from random import randrange
|
from random import randrange
|
||||||
|
@ -132,22 +134,17 @@ class TestBisect(unittest.TestCase):
|
||||||
|
|
||||||
class TestInsort(unittest.TestCase):
|
class TestInsort(unittest.TestCase):
|
||||||
|
|
||||||
def test_vsListSort(self, n=500):
|
def test_vsBuiltinSort(self, n=500):
|
||||||
from random import choice
|
from random import choice
|
||||||
digits = "0123456789"
|
for insorted in (list(), UserList()):
|
||||||
raw = []
|
for i in xrange(n):
|
||||||
insorted = []
|
digit = choice("0123456789")
|
||||||
for i in range(n):
|
if digit in "02468":
|
||||||
digit = choice(digits)
|
f = insort_left
|
||||||
raw.append(digit)
|
else:
|
||||||
if digit in "02468":
|
f = insort_right
|
||||||
f = insort_left
|
f(insorted, digit)
|
||||||
else:
|
self.assertEqual(sorted(insorted), insorted)
|
||||||
f = insort_right
|
|
||||||
f(insorted, digit)
|
|
||||||
sorted = raw[:]
|
|
||||||
sorted.sort()
|
|
||||||
self.assertEqual(sorted, insorted)
|
|
||||||
|
|
||||||
def test_backcompatibility(self):
|
def test_backcompatibility(self):
|
||||||
self.assertEqual(insort, insort_right)
|
self.assertEqual(insort, insort_right)
|
||||||
|
|
|
@ -565,6 +565,7 @@ Bill Tutt
|
||||||
Doobee R. Tzeck
|
Doobee R. Tzeck
|
||||||
Lionel Ulmer
|
Lionel Ulmer
|
||||||
Hector Urtubia
|
Hector Urtubia
|
||||||
|
Dmitry Vasiliev
|
||||||
Frank Vercruesse
|
Frank Vercruesse
|
||||||
Jaap Vermeulen
|
Jaap Vermeulen
|
||||||
Al Vezza
|
Al Vezza
|
||||||
|
|
|
@ -210,7 +210,8 @@ Library
|
||||||
- Plugged a minor hole in tempfile.mktemp() due to the use of
|
- Plugged a minor hole in tempfile.mktemp() due to the use of
|
||||||
os.path.exists(), switched to using os.lstat() directly if possible.
|
os.path.exists(), switched to using os.lstat() directly if possible.
|
||||||
|
|
||||||
- heapq.py has been converted to C for improved performance
|
- bisect.py and heapq.py now have underlying C implementations
|
||||||
|
for better performance
|
||||||
|
|
||||||
- traceback.format_exc has been added (similar to print_exc but it returns
|
- traceback.format_exc has been added (similar to print_exc but it returns
|
||||||
a string).
|
a string).
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
/* Bisection algorithms. Drop in replacement for bisect.py
|
||||||
|
|
||||||
|
Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
internal_bisect_right(PyObject *list, PyObject *item, int lo, int hi)
|
||||||
|
{
|
||||||
|
PyObject *litem;
|
||||||
|
int mid, res;
|
||||||
|
|
||||||
|
if (hi == -1) {
|
||||||
|
hi = PySequence_Size(list);
|
||||||
|
if (hi < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (lo < hi) {
|
||||||
|
mid = (lo + hi) / 2;
|
||||||
|
litem = PySequence_GetItem(list, mid);
|
||||||
|
if (litem == NULL)
|
||||||
|
return -1;
|
||||||
|
res = PyObject_RichCompareBool(item, litem, Py_LT);
|
||||||
|
Py_DECREF(litem);
|
||||||
|
if (res < 0)
|
||||||
|
return -1;
|
||||||
|
if (res)
|
||||||
|
hi = mid;
|
||||||
|
else
|
||||||
|
lo = mid + 1;
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
bisect_right(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *list, *item;
|
||||||
|
int lo = 0;
|
||||||
|
int hi = -1;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO|ii:bisect_right",
|
||||||
|
&list, &item, &lo, &hi))
|
||||||
|
return NULL;
|
||||||
|
index = internal_bisect_right(list, item, lo, hi);
|
||||||
|
if (index < 0)
|
||||||
|
return NULL;
|
||||||
|
return PyInt_FromLong(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(bisect_right_doc,
|
||||||
|
"bisect_right(list, item[, lo[, hi]]) -> index\n\
|
||||||
|
\n\
|
||||||
|
Return the index where to insert item x in list a, assuming a is sorted.\n\
|
||||||
|
\n\
|
||||||
|
The return value i is such that all e in a[:i] have e <= x, and all e in\n\
|
||||||
|
a[i:] have e > x. So if x already appears in the list, i points just\n\
|
||||||
|
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");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
insort_right(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *list, *item;
|
||||||
|
int lo = 0;
|
||||||
|
int hi = -1;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO|ii:insort_right",
|
||||||
|
&list, &item, &lo, &hi))
|
||||||
|
return NULL;
|
||||||
|
index = internal_bisect_right(list, item, lo, hi);
|
||||||
|
if (index < 0)
|
||||||
|
return NULL;
|
||||||
|
if (PyList_Check(list)) {
|
||||||
|
if (PyList_Insert(list, index, item) < 0)
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (PyObject_CallMethod(list, "insert", "iO", index, item)
|
||||||
|
== NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(insort_right_doc,
|
||||||
|
"insort_right(list, item[, lo[, hi]])\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");
|
||||||
|
|
||||||
|
static int
|
||||||
|
internal_bisect_left(PyObject *list, PyObject *item, int lo, int hi)
|
||||||
|
{
|
||||||
|
PyObject *litem;
|
||||||
|
int mid, res;
|
||||||
|
|
||||||
|
if (hi == -1) {
|
||||||
|
hi = PySequence_Size(list);
|
||||||
|
if (hi < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (lo < hi) {
|
||||||
|
mid = (lo + hi) / 2;
|
||||||
|
litem = PySequence_GetItem(list, mid);
|
||||||
|
if (litem == NULL)
|
||||||
|
return -1;
|
||||||
|
res = PyObject_RichCompareBool(litem, item, Py_LT);
|
||||||
|
Py_DECREF(litem);
|
||||||
|
if (res < 0)
|
||||||
|
return -1;
|
||||||
|
if (res)
|
||||||
|
lo = mid + 1;
|
||||||
|
else
|
||||||
|
hi = mid;
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
bisect_left(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *list, *item;
|
||||||
|
int lo = 0;
|
||||||
|
int hi = -1;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO|ii:bisect_left",
|
||||||
|
&list, &item, &lo, &hi))
|
||||||
|
return NULL;
|
||||||
|
index = internal_bisect_left(list, item, lo, hi);
|
||||||
|
if (index < 0)
|
||||||
|
return NULL;
|
||||||
|
return PyInt_FromLong(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(bisect_left_doc,
|
||||||
|
"bisect_left(list, item[, lo[, hi]]) -> index\n\
|
||||||
|
\n\
|
||||||
|
Return the index where to insert item x in list a, assuming a is sorted.\n\
|
||||||
|
\n\
|
||||||
|
The return value i is such that all e in a[:i] have e < x, and all e in\n\
|
||||||
|
a[i:] have e >= x. So if x already appears in the list, i points just\n\
|
||||||
|
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");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
insort_left(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *list, *item;
|
||||||
|
int lo = 0;
|
||||||
|
int hi = -1;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO|ii:insort_left",
|
||||||
|
&list, &item, &lo, &hi))
|
||||||
|
return NULL;
|
||||||
|
index = internal_bisect_left(list, item, lo, hi);
|
||||||
|
if (index < 0)
|
||||||
|
return NULL;
|
||||||
|
if (PyList_Check(list)) {
|
||||||
|
if (PyList_Insert(list, index, item) < 0)
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (PyObject_CallMethod(list, "insert", "iO", index, item)
|
||||||
|
== NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(insort_left_doc,
|
||||||
|
"insort_left(list, item[, lo[, hi]])\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");
|
||||||
|
|
||||||
|
PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
|
||||||
|
PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
|
||||||
|
|
||||||
|
static PyMethodDef bisect_methods[] = {
|
||||||
|
{"bisect_right", (PyCFunction)bisect_right,
|
||||||
|
METH_VARARGS, bisect_right_doc},
|
||||||
|
{"bisect", (PyCFunction)bisect_right,
|
||||||
|
METH_VARARGS, bisect_doc},
|
||||||
|
{"insort_right", (PyCFunction)insort_right,
|
||||||
|
METH_VARARGS, insort_right_doc},
|
||||||
|
{"insort", (PyCFunction)insort_right,
|
||||||
|
METH_VARARGS, insort_doc},
|
||||||
|
{"bisect_left", (PyCFunction)bisect_left,
|
||||||
|
METH_VARARGS, bisect_left_doc},
|
||||||
|
{"insort_left", (PyCFunction)insort_left,
|
||||||
|
METH_VARARGS, insort_left_doc},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
PyDoc_STRVAR(module_doc,
|
||||||
|
"Bisection algorithms.\n\
|
||||||
|
\n\
|
||||||
|
This module provides support for maintaining a list in sorted order without\n\
|
||||||
|
having to sort the list after each insertion. For long lists of items with\n\
|
||||||
|
expensive comparison operations, this can be an improvement over the more\n\
|
||||||
|
common approach.\n");
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
init_bisect(void)
|
||||||
|
{
|
||||||
|
PyObject *m;
|
||||||
|
|
||||||
|
m = Py_InitModule3("_bisect", bisect_methods, module_doc);
|
||||||
|
}
|
||||||
|
|
|
@ -93,6 +93,10 @@ LINK32=link.exe
|
||||||
# Name "pythoncore - Win32 Debug"
|
# Name "pythoncore - Win32 Debug"
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\Modules\_bisectmodule.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=..\..\Modules\_codecsmodule.c
|
SOURCE=..\..\Modules\_codecsmodule.c
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
|
@ -47,6 +47,7 @@ extern void initzipimport(void);
|
||||||
extern void init_random(void);
|
extern void init_random(void);
|
||||||
extern void inititertools(void);
|
extern void inititertools(void);
|
||||||
extern void initheapq(void);
|
extern void initheapq(void);
|
||||||
|
extern void init_bisect(void);
|
||||||
extern void init_symtable(void);
|
extern void init_symtable(void);
|
||||||
extern void initmmap(void);
|
extern void initmmap(void);
|
||||||
extern void init_csv(void);
|
extern void init_csv(void);
|
||||||
|
@ -106,6 +107,7 @@ struct _inittab _PyImport_Inittab[] = {
|
||||||
{"_weakref", init_weakref},
|
{"_weakref", init_weakref},
|
||||||
{"_hotshot", init_hotshot},
|
{"_hotshot", init_hotshot},
|
||||||
{"_random", init_random},
|
{"_random", init_random},
|
||||||
|
{"_bisect", init_bisect},
|
||||||
{"heapq", initheapq},
|
{"heapq", initheapq},
|
||||||
{"itertools", inititertools},
|
{"itertools", inititertools},
|
||||||
{"_symtable", init_symtable},
|
{"_symtable", init_symtable},
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -322,6 +322,8 @@ class PyBuildExt(build_ext):
|
||||||
exts.append( Extension("_random", ["_randommodule.c"]) )
|
exts.append( Extension("_random", ["_randommodule.c"]) )
|
||||||
# fast iterator tools implemented in C
|
# fast iterator tools implemented in C
|
||||||
exts.append( Extension("itertools", ["itertoolsmodule.c"]) )
|
exts.append( Extension("itertools", ["itertoolsmodule.c"]) )
|
||||||
|
# bisect
|
||||||
|
exts.append( Extension("_bisect", ["_bisectmodule.c"]) )
|
||||||
# heapq
|
# heapq
|
||||||
exts.append( Extension("heapq", ["heapqmodule.c"]) )
|
exts.append( Extension("heapq", ["heapqmodule.c"]) )
|
||||||
# operator.add() and similar goodies
|
# operator.add() and similar goodies
|
||||||
|
|
Loading…
Reference in New Issue