blob: 4d0a2e4c1c6a1d51ae37f5af967c8bdd7750b2a6 [file] [log] [blame]
Raymond Hettinger0c410272004-01-05 10:13:35 +00001/* Bisection algorithms. Drop in replacement for bisect.py
2
3Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
4*/
5
6#include "Python.h"
7
Benjamin Petersond6313712008-07-31 16:23:04 +00008static Py_ssize_t
Martin v. Löwisad0a4622006-02-16 14:30:23 +00009internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +000010{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000011 PyObject *litem;
12 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000013
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000014 if (lo < 0) {
15 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
16 return -1;
17 }
18 if (hi == -1) {
19 hi = PySequence_Size(list);
20 if (hi < 0)
21 return -1;
22 }
23 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010024 /* The (size_t)cast ensures that the addition and subsequent division
25 are performed as unsigned operations, avoiding difficulties from
26 signed overflow. (See issue 13496.) */
27 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000028 litem = PySequence_GetItem(list, mid);
29 if (litem == NULL)
30 return -1;
31 res = PyObject_RichCompareBool(item, litem, Py_LT);
32 Py_DECREF(litem);
33 if (res < 0)
34 return -1;
35 if (res)
36 hi = mid;
37 else
38 lo = mid + 1;
39 }
40 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000041}
42
43static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000044bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000045{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000046 PyObject *list, *item;
47 Py_ssize_t lo = 0;
48 Py_ssize_t hi = -1;
49 Py_ssize_t index;
50 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000051
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000052 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
53 keywords, &list, &item, &lo, &hi))
54 return NULL;
55 index = internal_bisect_right(list, item, lo, hi);
56 if (index < 0)
57 return NULL;
58 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000059}
60
61PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000062"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000063\n\
64Return the index where to insert item x in list a, assuming a is sorted.\n\
65\n\
66The return value i is such that all e in a[:i] have e <= x, and all e in\n\
67a[i:] have e > x. So if x already appears in the list, i points just\n\
68beyond the rightmost x already there\n\
69\n\
70Optional args lo (default 0) and hi (default len(a)) bound the\n\
71slice of a to be searched.\n");
72
73static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000074insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000075{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000076 PyObject *list, *item, *result;
77 Py_ssize_t lo = 0;
78 Py_ssize_t hi = -1;
79 Py_ssize_t index;
80 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000081
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000082 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
83 keywords, &list, &item, &lo, &hi))
84 return NULL;
85 index = internal_bisect_right(list, item, lo, hi);
86 if (index < 0)
87 return NULL;
88 if (PyList_CheckExact(list)) {
89 if (PyList_Insert(list, index, item) < 0)
90 return NULL;
91 } else {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +020092 _Py_IDENTIFIER(insert);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +020093
94 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000095 if (result == NULL)
96 return NULL;
97 Py_DECREF(result);
98 }
Raymond Hettinger0c410272004-01-05 10:13:35 +000099
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000100 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000101}
102
103PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000104"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000105\n\
106Insert item x in list a, and keep it sorted assuming a is sorted.\n\
107\n\
108If x is already in a, insert it to the right of the rightmost x.\n\
109\n\
110Optional args lo (default 0) and hi (default len(a)) bound the\n\
111slice of a to be searched.\n");
112
Benjamin Petersond6313712008-07-31 16:23:04 +0000113static Py_ssize_t
114internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000115{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000116 PyObject *litem;
117 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000118
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000119 if (lo < 0) {
120 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
121 return -1;
122 }
123 if (hi == -1) {
124 hi = PySequence_Size(list);
125 if (hi < 0)
126 return -1;
127 }
128 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100129 /* The (size_t)cast ensures that the addition and subsequent division
130 are performed as unsigned operations, avoiding difficulties from
131 signed overflow. (See issue 13496.) */
132 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000133 litem = PySequence_GetItem(list, mid);
134 if (litem == NULL)
135 return -1;
136 res = PyObject_RichCompareBool(litem, item, Py_LT);
137 Py_DECREF(litem);
138 if (res < 0)
139 return -1;
140 if (res)
141 lo = mid + 1;
142 else
143 hi = mid;
144 }
145 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000146}
147
148static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000149bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000150{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000151 PyObject *list, *item;
152 Py_ssize_t lo = 0;
153 Py_ssize_t hi = -1;
154 Py_ssize_t index;
155 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000156
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000157 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
158 keywords, &list, &item, &lo, &hi))
159 return NULL;
160 index = internal_bisect_left(list, item, lo, hi);
161 if (index < 0)
162 return NULL;
163 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000164}
165
166PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000167"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000168\n\
169Return the index where to insert item x in list a, assuming a is sorted.\n\
170\n\
171The return value i is such that all e in a[:i] have e < x, and all e in\n\
172a[i:] have e >= x. So if x already appears in the list, i points just\n\
173before the leftmost x already there.\n\
174\n\
175Optional args lo (default 0) and hi (default len(a)) bound the\n\
176slice of a to be searched.\n");
177
178static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000179insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000180{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000181 PyObject *list, *item, *result;
182 Py_ssize_t lo = 0;
183 Py_ssize_t hi = -1;
184 Py_ssize_t index;
185 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000186
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000187 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
188 keywords, &list, &item, &lo, &hi))
189 return NULL;
190 index = internal_bisect_left(list, item, lo, hi);
191 if (index < 0)
192 return NULL;
193 if (PyList_CheckExact(list)) {
194 if (PyList_Insert(list, index, item) < 0)
195 return NULL;
196 } else {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200197 _Py_IDENTIFIER(insert);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200198
199 result = _PyObject_CallMethodId(list, &PyId_insert, "iO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000200 if (result == NULL)
201 return NULL;
202 Py_DECREF(result);
203 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000204
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000205 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000206}
207
208PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000209"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000210\n\
211Insert item x in list a, and keep it sorted assuming a is sorted.\n\
212\n\
213If x is already in a, insert it to the left of the leftmost x.\n\
214\n\
215Optional args lo (default 0) and hi (default len(a)) bound the\n\
216slice of a to be searched.\n");
217
218PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
219PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
220
221static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000222 {"bisect_right", (PyCFunction)bisect_right,
223 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
224 {"bisect", (PyCFunction)bisect_right,
225 METH_VARARGS|METH_KEYWORDS, bisect_doc},
226 {"insort_right", (PyCFunction)insort_right,
227 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
228 {"insort", (PyCFunction)insort_right,
229 METH_VARARGS|METH_KEYWORDS, insort_doc},
230 {"bisect_left", (PyCFunction)bisect_left,
231 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
232 {"insort_left", (PyCFunction)insort_left,
233 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
234 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000235};
236
237PyDoc_STRVAR(module_doc,
238"Bisection algorithms.\n\
239\n\
240This module provides support for maintaining a list in sorted order without\n\
241having to sort the list after each insertion. For long lists of items with\n\
242expensive comparison operations, this can be an improvement over the more\n\
243common approach.\n");
244
Raymond Hettinger0c410272004-01-05 10:13:35 +0000245
Martin v. Löwis1a214512008-06-11 05:26:20 +0000246static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000247 PyModuleDef_HEAD_INIT,
248 "_bisect",
249 module_doc,
250 -1,
251 bisect_methods,
252 NULL,
253 NULL,
254 NULL,
255 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000256};
257
258PyMODINIT_FUNC
259PyInit__bisect(void)
260{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000261 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000262}