Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0c41027

Browse files
committed
SF Patch #864863: Bisect C implementation
(Contributed by Dmitry Vasiliev.)
1 parent 23a0f4e commit 0c41027

9 files changed

Lines changed: 261 additions & 16 deletions

File tree

Doc/whatsnew/whatsnew24.tex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ \section{New, Improved, and Deprecated Modules}
305305
supports transparency, this makes it possible to use a transparent background.
306306
(Contributed by J\"org Lehmann.)
307307

308+
\item The \module{bisect} module now has an underlying C implementation
309+
for improved performance.
310+
(Contributed by Dmitry Vasiliev.)
311+
308312
\item The \module{heapq} module has been converted to C. The resulting
309313
ten-fold improvement in speed makes the module suitable for handling
310314
high volumes of data.

Lib/bisect.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,9 @@ def bisect_left(a, x, lo=0, hi=None):
7676
if a[mid] < x: lo = mid+1
7777
else: hi = mid
7878
return lo
79+
80+
# Overwrite above definitions with a fast C implementation
81+
try:
82+
from _bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect
83+
except ImportError:
84+
pass

Lib/test/test_bisect.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
from test import test_support
33
from bisect import bisect_right, bisect_left, insort_left, insort_right, insort, bisect
4+
from UserList import UserList
45

56
class TestBisect(unittest.TestCase):
67

@@ -89,6 +90,7 @@ class TestBisect(unittest.TestCase):
8990
def test_precomputed(self):
9091
for func, data, elem, expected in self.precomputedCases:
9192
self.assertEqual(func(data, elem), expected)
93+
self.assertEqual(func(UserList(data), elem), expected)
9294

9395
def test_random(self, n=25):
9496
from random import randrange
@@ -132,22 +134,17 @@ def test_backcompatibility(self):
132134

133135
class TestInsort(unittest.TestCase):
134136

135-
def test_vsListSort(self, n=500):
137+
def test_vsBuiltinSort(self, n=500):
136138
from random import choice
137-
digits = "0123456789"
138-
raw = []
139-
insorted = []
140-
for i in range(n):
141-
digit = choice(digits)
142-
raw.append(digit)
143-
if digit in "02468":
144-
f = insort_left
145-
else:
146-
f = insort_right
147-
f(insorted, digit)
148-
sorted = raw[:]
149-
sorted.sort()
150-
self.assertEqual(sorted, insorted)
139+
for insorted in (list(), UserList()):
140+
for i in xrange(n):
141+
digit = choice("0123456789")
142+
if digit in "02468":
143+
f = insort_left
144+
else:
145+
f = insort_right
146+
f(insorted, digit)
147+
self.assertEqual(sorted(insorted), insorted)
151148

152149
def test_backcompatibility(self):
153150
self.assertEqual(insort, insort_right)

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ Bill Tutt
565565
Doobee R. Tzeck
566566
Lionel Ulmer
567567
Hector Urtubia
568+
Dmitry Vasiliev
568569
Frank Vercruesse
569570
Jaap Vermeulen
570571
Al Vezza

Misc/NEWS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ Library
210210
- Plugged a minor hole in tempfile.mktemp() due to the use of
211211
os.path.exists(), switched to using os.lstat() directly if possible.
212212

213-
- heapq.py has been converted to C for improved performance
213+
- bisect.py and heapq.py now have underlying C implementations
214+
for better performance
214215

215216
- traceback.format_exc has been added (similar to print_exc but it returns
216217
a string).

Modules/_bisectmodule.c

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/* Bisection algorithms. Drop in replacement for bisect.py
2+
3+
Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
4+
*/
5+
6+
#include "Python.h"
7+
8+
static int
9+
internal_bisect_right(PyObject *list, PyObject *item, int lo, int hi)
10+
{
11+
PyObject *litem;
12+
int mid, res;
13+
14+
if (hi == -1) {
15+
hi = PySequence_Size(list);
16+
if (hi < 0)
17+
return -1;
18+
}
19+
while (lo < hi) {
20+
mid = (lo + hi) / 2;
21+
litem = PySequence_GetItem(list, mid);
22+
if (litem == NULL)
23+
return -1;
24+
res = PyObject_RichCompareBool(item, litem, Py_LT);
25+
Py_DECREF(litem);
26+
if (res < 0)
27+
return -1;
28+
if (res)
29+
hi = mid;
30+
else
31+
lo = mid + 1;
32+
}
33+
return lo;
34+
}
35+
36+
static PyObject *
37+
bisect_right(PyObject *self, PyObject *args)
38+
{
39+
PyObject *list, *item;
40+
int lo = 0;
41+
int hi = -1;
42+
int index;
43+
44+
if (!PyArg_ParseTuple(args, "OO|ii:bisect_right",
45+
&list, &item, &lo, &hi))
46+
return NULL;
47+
index = internal_bisect_right(list, item, lo, hi);
48+
if (index < 0)
49+
return NULL;
50+
return PyInt_FromLong(index);
51+
}
52+
53+
PyDoc_STRVAR(bisect_right_doc,
54+
"bisect_right(list, item[, lo[, hi]]) -> index\n\
55+
\n\
56+
Return the index where to insert item x in list a, assuming a is sorted.\n\
57+
\n\
58+
The return value i is such that all e in a[:i] have e <= x, and all e in\n\
59+
a[i:] have e > x. So if x already appears in the list, i points just\n\
60+
beyond the rightmost x already there\n\
61+
\n\
62+
Optional args lo (default 0) and hi (default len(a)) bound the\n\
63+
slice of a to be searched.\n");
64+
65+
static PyObject *
66+
insort_right(PyObject *self, PyObject *args)
67+
{
68+
PyObject *list, *item;
69+
int lo = 0;
70+
int hi = -1;
71+
int index;
72+
73+
if (!PyArg_ParseTuple(args, "OO|ii:insort_right",
74+
&list, &item, &lo, &hi))
75+
return NULL;
76+
index = internal_bisect_right(list, item, lo, hi);
77+
if (index < 0)
78+
return NULL;
79+
if (PyList_Check(list)) {
80+
if (PyList_Insert(list, index, item) < 0)
81+
return NULL;
82+
} else {
83+
if (PyObject_CallMethod(list, "insert", "iO", index, item)
84+
== NULL)
85+
return NULL;
86+
}
87+
88+
Py_RETURN_NONE;
89+
}
90+
91+
PyDoc_STRVAR(insort_right_doc,
92+
"insort_right(list, item[, lo[, hi]])\n\
93+
\n\
94+
Insert item x in list a, and keep it sorted assuming a is sorted.\n\
95+
\n\
96+
If x is already in a, insert it to the right of the rightmost x.\n\
97+
\n\
98+
Optional args lo (default 0) and hi (default len(a)) bound the\n\
99+
slice of a to be searched.\n");
100+
101+
static int
102+
internal_bisect_left(PyObject *list, PyObject *item, int lo, int hi)
103+
{
104+
PyObject *litem;
105+
int mid, res;
106+
107+
if (hi == -1) {
108+
hi = PySequence_Size(list);
109+
if (hi < 0)
110+
return -1;
111+
}
112+
while (lo < hi) {
113+
mid = (lo + hi) / 2;
114+
litem = PySequence_GetItem(list, mid);
115+
if (litem == NULL)
116+
return -1;
117+
res = PyObject_RichCompareBool(litem, item, Py_LT);
118+
Py_DECREF(litem);
119+
if (res < 0)
120+
return -1;
121+
if (res)
122+
lo = mid + 1;
123+
else
124+
hi = mid;
125+
}
126+
return lo;
127+
}
128+
129+
static PyObject *
130+
bisect_left(PyObject *self, PyObject *args)
131+
{
132+
PyObject *list, *item;
133+
int lo = 0;
134+
int hi = -1;
135+
int index;
136+
137+
if (!PyArg_ParseTuple(args, "OO|ii:bisect_left",
138+
&list, &item, &lo, &hi))
139+
return NULL;
140+
index = internal_bisect_left(list, item, lo, hi);
141+
if (index < 0)
142+
return NULL;
143+
return PyInt_FromLong(index);
144+
}
145+
146+
PyDoc_STRVAR(bisect_left_doc,
147+
"bisect_left(list, item[, lo[, hi]]) -> index\n\
148+
\n\
149+
Return the index where to insert item x in list a, assuming a is sorted.\n\
150+
\n\
151+
The return value i is such that all e in a[:i] have e < x, and all e in\n\
152+
a[i:] have e >= x. So if x already appears in the list, i points just\n\
153+
before the leftmost x already there.\n\
154+
\n\
155+
Optional args lo (default 0) and hi (default len(a)) bound the\n\
156+
slice of a to be searched.\n");
157+
158+
static PyObject *
159+
insort_left(PyObject *self, PyObject *args)
160+
{
161+
PyObject *list, *item;
162+
int lo = 0;
163+
int hi = -1;
164+
int index;
165+
166+
if (!PyArg_ParseTuple(args, "OO|ii:insort_left",
167+
&list, &item, &lo, &hi))
168+
return NULL;
169+
index = internal_bisect_left(list, item, lo, hi);
170+
if (index < 0)
171+
return NULL;
172+
if (PyList_Check(list)) {
173+
if (PyList_Insert(list, index, item) < 0)
174+
return NULL;
175+
} else {
176+
if (PyObject_CallMethod(list, "insert", "iO", index, item)
177+
== NULL)
178+
return NULL;
179+
}
180+
181+
Py_RETURN_NONE;
182+
}
183+
184+
PyDoc_STRVAR(insort_left_doc,
185+
"insort_left(list, item[, lo[, hi]])\n\
186+
\n\
187+
Insert item x in list a, and keep it sorted assuming a is sorted.\n\
188+
\n\
189+
If x is already in a, insert it to the left of the leftmost x.\n\
190+
\n\
191+
Optional args lo (default 0) and hi (default len(a)) bound the\n\
192+
slice of a to be searched.\n");
193+
194+
PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
195+
PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
196+
197+
static PyMethodDef bisect_methods[] = {
198+
{"bisect_right", (PyCFunction)bisect_right,
199+
METH_VARARGS, bisect_right_doc},
200+
{"bisect", (PyCFunction)bisect_right,
201+
METH_VARARGS, bisect_doc},
202+
{"insort_right", (PyCFunction)insort_right,
203+
METH_VARARGS, insort_right_doc},
204+
{"insort", (PyCFunction)insort_right,
205+
METH_VARARGS, insort_doc},
206+
{"bisect_left", (PyCFunction)bisect_left,
207+
METH_VARARGS, bisect_left_doc},
208+
{"insort_left", (PyCFunction)insort_left,
209+
METH_VARARGS, insort_left_doc},
210+
{NULL, NULL} /* sentinel */
211+
};
212+
213+
PyDoc_STRVAR(module_doc,
214+
"Bisection algorithms.\n\
215+
\n\
216+
This module provides support for maintaining a list in sorted order without\n\
217+
having to sort the list after each insertion. For long lists of items with\n\
218+
expensive comparison operations, this can be an improvement over the more\n\
219+
common approach.\n");
220+
221+
PyMODINIT_FUNC
222+
init_bisect(void)
223+
{
224+
PyObject *m;
225+
226+
m = Py_InitModule3("_bisect", bisect_methods, module_doc);
227+
}
228+

PC/VC6/pythoncore.dsp

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PC/config.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ extern void initzipimport(void);
4747
extern void init_random(void);
4848
extern void inititertools(void);
4949
extern void initheapq(void);
50+
extern void init_bisect(void);
5051
extern void init_symtable(void);
5152
extern void initmmap(void);
5253
extern void init_csv(void);
@@ -106,6 +107,7 @@ struct _inittab _PyImport_Inittab[] = {
106107
{"_weakref", init_weakref},
107108
{"_hotshot", init_hotshot},
108109
{"_random", init_random},
110+
{"_bisect", init_bisect},
109111
{"heapq", initheapq},
110112
{"itertools", inititertools},
111113
{"_symtable", init_symtable},

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ def detect_modules(self):
322322
exts.append( Extension("_random", ["_randommodule.c"]) )
323323
# fast iterator tools implemented in C
324324
exts.append( Extension("itertools", ["itertoolsmodule.c"]) )
325+
# bisect
326+
exts.append( Extension("_bisect", ["_bisectmodule.c"]) )
325327
# heapq
326328
exts.append( Extension("heapq", ["heapqmodule.c"]) )
327329
# operator.add() and similar goodies

0 commit comments

Comments
 (0)