blob: 02b55d1b41de269799474403cfaf60b81f4cd75d [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
Martin v. Löwise75fc142013-11-07 18:46:53 +01009_Py_IDENTIFIER(insert);
10
Benjamin Petersond6313712008-07-31 16:23:04 +000011static Py_ssize_t
Martin v. Löwisad0a4622006-02-16 14:30:23 +000012internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +000013{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000014 PyObject *litem;
15 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000016
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000017 if (lo < 0) {
18 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
19 return -1;
20 }
21 if (hi == -1) {
22 hi = PySequence_Size(list);
23 if (hi < 0)
24 return -1;
25 }
26 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010027 /* The (size_t)cast ensures that the addition and subsequent division
28 are performed as unsigned operations, avoiding difficulties from
29 signed overflow. (See issue 13496.) */
30 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000031 litem = PySequence_GetItem(list, mid);
32 if (litem == NULL)
33 return -1;
34 res = PyObject_RichCompareBool(item, litem, Py_LT);
35 Py_DECREF(litem);
36 if (res < 0)
37 return -1;
38 if (res)
39 hi = mid;
40 else
41 lo = mid + 1;
42 }
43 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000044}
45
46static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000047bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000048{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000049 PyObject *list, *item;
50 Py_ssize_t lo = 0;
51 Py_ssize_t hi = -1;
52 Py_ssize_t index;
53 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000054
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000055 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
56 keywords, &list, &item, &lo, &hi))
57 return NULL;
58 index = internal_bisect_right(list, item, lo, hi);
59 if (index < 0)
60 return NULL;
61 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000062}
63
64PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000065"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000066\n\
67Return the index where to insert item x in list a, assuming a is sorted.\n\
68\n\
69The return value i is such that all e in a[:i] have e <= x, and all e in\n\
70a[i:] have e > x. So if x already appears in the list, i points just\n\
71beyond the rightmost x already there\n\
72\n\
73Optional args lo (default 0) and hi (default len(a)) bound the\n\
74slice of a to be searched.\n");
75
76static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000077insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000078{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000079 PyObject *list, *item, *result;
80 Py_ssize_t lo = 0;
81 Py_ssize_t hi = -1;
82 Py_ssize_t index;
83 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000084
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000085 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
86 keywords, &list, &item, &lo, &hi))
87 return NULL;
88 index = internal_bisect_right(list, item, lo, hi);
89 if (index < 0)
90 return NULL;
91 if (PyList_CheckExact(list)) {
92 if (PyList_Insert(list, index, item) < 0)
93 return NULL;
94 } else {
Martin v. Löwisafe55bb2011-10-09 10:38:36 +020095 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 {
Antoine Pitroub7d033d2012-05-16 14:39:36 +0200198 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000199 if (result == NULL)
200 return NULL;
201 Py_DECREF(result);
202 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000203
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000204 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000205}
206
207PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000208"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000209\n\
210Insert item x in list a, and keep it sorted assuming a is sorted.\n\
211\n\
212If x is already in a, insert it to the left of the leftmost x.\n\
213\n\
214Optional args lo (default 0) and hi (default len(a)) bound the\n\
215slice of a to be searched.\n");
216
217PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
218PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
219
220static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000221 {"bisect_right", (PyCFunction)bisect_right,
222 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
223 {"bisect", (PyCFunction)bisect_right,
224 METH_VARARGS|METH_KEYWORDS, bisect_doc},
225 {"insort_right", (PyCFunction)insort_right,
226 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
227 {"insort", (PyCFunction)insort_right,
228 METH_VARARGS|METH_KEYWORDS, insort_doc},
229 {"bisect_left", (PyCFunction)bisect_left,
230 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
231 {"insort_left", (PyCFunction)insort_left,
232 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
233 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000234};
235
236PyDoc_STRVAR(module_doc,
237"Bisection algorithms.\n\
238\n\
239This module provides support for maintaining a list in sorted order without\n\
240having to sort the list after each insertion. For long lists of items with\n\
241expensive comparison operations, this can be an improvement over the more\n\
242common approach.\n");
243
Raymond Hettinger0c410272004-01-05 10:13:35 +0000244
Martin v. Löwis1a214512008-06-11 05:26:20 +0000245static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000246 PyModuleDef_HEAD_INIT,
247 "_bisect",
248 module_doc,
249 -1,
250 bisect_methods,
251 NULL,
252 NULL,
253 NULL,
254 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000255};
256
257PyMODINIT_FUNC
258PyInit__bisect(void)
259{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000260 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000261}