blob: eae29784dc61d663a56bdda11b2704dcb8a4f252 [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 {
Raymond Hettinger6b3d72c2010-09-01 08:56:10 +000093 result = PyObject_CallMethod(list, "insert", "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000094 if (result == NULL)
95 return NULL;
96 Py_DECREF(result);
97 }
Raymond Hettinger0c410272004-01-05 10:13:35 +000098
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000099 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000100}
101
102PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000103"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000104\n\
105Insert item x in list a, and keep it sorted assuming a is sorted.\n\
106\n\
107If x is already in a, insert it to the right of the rightmost x.\n\
108\n\
109Optional args lo (default 0) and hi (default len(a)) bound the\n\
110slice of a to be searched.\n");
111
Benjamin Petersond6313712008-07-31 16:23:04 +0000112static Py_ssize_t
113internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000114{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000115 PyObject *litem;
116 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000117
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000118 if (lo < 0) {
119 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
120 return -1;
121 }
122 if (hi == -1) {
123 hi = PySequence_Size(list);
124 if (hi < 0)
125 return -1;
126 }
127 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100128 /* The (size_t)cast ensures that the addition and subsequent division
129 are performed as unsigned operations, avoiding difficulties from
130 signed overflow. (See issue 13496.) */
131 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000132 litem = PySequence_GetItem(list, mid);
133 if (litem == NULL)
134 return -1;
135 res = PyObject_RichCompareBool(litem, item, Py_LT);
136 Py_DECREF(litem);
137 if (res < 0)
138 return -1;
139 if (res)
140 lo = mid + 1;
141 else
142 hi = mid;
143 }
144 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000145}
146
147static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000148bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000149{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000150 PyObject *list, *item;
151 Py_ssize_t lo = 0;
152 Py_ssize_t hi = -1;
153 Py_ssize_t index;
154 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000155
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000156 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
157 keywords, &list, &item, &lo, &hi))
158 return NULL;
159 index = internal_bisect_left(list, item, lo, hi);
160 if (index < 0)
161 return NULL;
162 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000163}
164
165PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000166"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000167\n\
168Return the index where to insert item x in list a, assuming a is sorted.\n\
169\n\
170The return value i is such that all e in a[:i] have e < x, and all e in\n\
171a[i:] have e >= x. So if x already appears in the list, i points just\n\
172before the leftmost x already there.\n\
173\n\
174Optional args lo (default 0) and hi (default len(a)) bound the\n\
175slice of a to be searched.\n");
176
177static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000178insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000179{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000180 PyObject *list, *item, *result;
181 Py_ssize_t lo = 0;
182 Py_ssize_t hi = -1;
183 Py_ssize_t index;
184 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000185
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000186 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
187 keywords, &list, &item, &lo, &hi))
188 return NULL;
189 index = internal_bisect_left(list, item, lo, hi);
190 if (index < 0)
191 return NULL;
192 if (PyList_CheckExact(list)) {
193 if (PyList_Insert(list, index, item) < 0)
194 return NULL;
195 } else {
Antoine Pitroua103b962012-05-16 14:37:54 +0200196 result = PyObject_CallMethod(list, "insert", "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000197 if (result == NULL)
198 return NULL;
199 Py_DECREF(result);
200 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000201
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000202 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000203}
204
205PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000206"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000207\n\
208Insert item x in list a, and keep it sorted assuming a is sorted.\n\
209\n\
210If x is already in a, insert it to the left of the leftmost x.\n\
211\n\
212Optional args lo (default 0) and hi (default len(a)) bound the\n\
213slice of a to be searched.\n");
214
215PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
216PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
217
218static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000219 {"bisect_right", (PyCFunction)bisect_right,
220 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
221 {"bisect", (PyCFunction)bisect_right,
222 METH_VARARGS|METH_KEYWORDS, bisect_doc},
223 {"insort_right", (PyCFunction)insort_right,
224 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
225 {"insort", (PyCFunction)insort_right,
226 METH_VARARGS|METH_KEYWORDS, insort_doc},
227 {"bisect_left", (PyCFunction)bisect_left,
228 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
229 {"insort_left", (PyCFunction)insort_left,
230 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
231 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000232};
233
234PyDoc_STRVAR(module_doc,
235"Bisection algorithms.\n\
236\n\
237This module provides support for maintaining a list in sorted order without\n\
238having to sort the list after each insertion. For long lists of items with\n\
239expensive comparison operations, this can be an improvement over the more\n\
240common approach.\n");
241
Raymond Hettinger0c410272004-01-05 10:13:35 +0000242
Martin v. Löwis1a214512008-06-11 05:26:20 +0000243static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000244 PyModuleDef_HEAD_INIT,
245 "_bisect",
246 module_doc,
247 -1,
248 bisect_methods,
249 NULL,
250 NULL,
251 NULL,
252 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000253};
254
255PyMODINIT_FUNC
256PyInit__bisect(void)
257{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000258 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000259}