blob: faca8cfbcafa9482dd922449bcd37f3e0a58ee5c [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
Antoine Pitroua103b962012-05-16 14:37:54 +02006#define PY_SSIZE_T_CLEAN
Raymond Hettinger0c410272004-01-05 10:13:35 +00007#include "Python.h"
8
Benjamin Petersond6313712008-07-31 16:23:04 +00009static Py_ssize_t
Martin v. Löwisad0a4622006-02-16 14:30:23 +000010internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +000011{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000012 PyObject *litem;
13 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000014
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000015 if (lo < 0) {
16 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
17 return -1;
18 }
19 if (hi == -1) {
20 hi = PySequence_Size(list);
21 if (hi < 0)
22 return -1;
23 }
24 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010025 /* The (size_t)cast ensures that the addition and subsequent division
26 are performed as unsigned operations, avoiding difficulties from
27 signed overflow. (See issue 13496.) */
28 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000029 litem = PySequence_GetItem(list, mid);
30 if (litem == NULL)
31 return -1;
32 res = PyObject_RichCompareBool(item, litem, Py_LT);
33 Py_DECREF(litem);
34 if (res < 0)
35 return -1;
36 if (res)
37 hi = mid;
38 else
39 lo = mid + 1;
40 }
41 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000042}
43
44static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000045bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000046{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000047 PyObject *list, *item;
48 Py_ssize_t lo = 0;
49 Py_ssize_t hi = -1;
50 Py_ssize_t index;
51 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000052
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000053 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
54 keywords, &list, &item, &lo, &hi))
55 return NULL;
56 index = internal_bisect_right(list, item, lo, hi);
57 if (index < 0)
58 return NULL;
59 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000060}
61
62PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000063"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000064\n\
65Return the index where to insert item x in list a, assuming a is sorted.\n\
66\n\
67The return value i is such that all e in a[:i] have e <= x, and all e in\n\
68a[i:] have e > x. So if x already appears in the list, i points just\n\
69beyond the rightmost x already there\n\
70\n\
71Optional args lo (default 0) and hi (default len(a)) bound the\n\
72slice of a to be searched.\n");
73
74static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000075insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000076{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000077 PyObject *list, *item, *result;
78 Py_ssize_t lo = 0;
79 Py_ssize_t hi = -1;
80 Py_ssize_t index;
81 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000082
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000083 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
84 keywords, &list, &item, &lo, &hi))
85 return NULL;
86 index = internal_bisect_right(list, item, lo, hi);
87 if (index < 0)
88 return NULL;
89 if (PyList_CheckExact(list)) {
90 if (PyList_Insert(list, index, item) < 0)
91 return NULL;
92 } else {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +020093 _Py_IDENTIFIER(insert);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +020094
95 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000096 if (result == NULL)
97 return NULL;
98 Py_DECREF(result);
99 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000100
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000101 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000102}
103
104PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000105"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000106\n\
107Insert item x in list a, and keep it sorted assuming a is sorted.\n\
108\n\
109If x is already in a, insert it to the right of the rightmost x.\n\
110\n\
111Optional args lo (default 0) and hi (default len(a)) bound the\n\
112slice of a to be searched.\n");
113
Benjamin Petersond6313712008-07-31 16:23:04 +0000114static Py_ssize_t
115internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000116{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000117 PyObject *litem;
118 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000119
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000120 if (lo < 0) {
121 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
122 return -1;
123 }
124 if (hi == -1) {
125 hi = PySequence_Size(list);
126 if (hi < 0)
127 return -1;
128 }
129 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100130 /* The (size_t)cast ensures that the addition and subsequent division
131 are performed as unsigned operations, avoiding difficulties from
132 signed overflow. (See issue 13496.) */
133 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000134 litem = PySequence_GetItem(list, mid);
135 if (litem == NULL)
136 return -1;
137 res = PyObject_RichCompareBool(litem, item, Py_LT);
138 Py_DECREF(litem);
139 if (res < 0)
140 return -1;
141 if (res)
142 lo = mid + 1;
143 else
144 hi = mid;
145 }
146 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000147}
148
149static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000150bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000151{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000152 PyObject *list, *item;
153 Py_ssize_t lo = 0;
154 Py_ssize_t hi = -1;
155 Py_ssize_t index;
156 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000157
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000158 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
159 keywords, &list, &item, &lo, &hi))
160 return NULL;
161 index = internal_bisect_left(list, item, lo, hi);
162 if (index < 0)
163 return NULL;
164 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000165}
166
167PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000168"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000169\n\
170Return the index where to insert item x in list a, assuming a is sorted.\n\
171\n\
172The return value i is such that all e in a[:i] have e < x, and all e in\n\
173a[i:] have e >= x. So if x already appears in the list, i points just\n\
174before the leftmost x already there.\n\
175\n\
176Optional args lo (default 0) and hi (default len(a)) bound the\n\
177slice of a to be searched.\n");
178
179static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000180insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000181{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000182 PyObject *list, *item, *result;
183 Py_ssize_t lo = 0;
184 Py_ssize_t hi = -1;
185 Py_ssize_t index;
186 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000187
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000188 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
189 keywords, &list, &item, &lo, &hi))
190 return NULL;
191 index = internal_bisect_left(list, item, lo, hi);
192 if (index < 0)
193 return NULL;
194 if (PyList_CheckExact(list)) {
195 if (PyList_Insert(list, index, item) < 0)
196 return NULL;
197 } else {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200198 _Py_IDENTIFIER(insert);
Antoine Pitroub7d033d2012-05-16 14:39:36 +0200199 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", 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}