blob: 7dd73f94750b3d6cdf9c69792393cb6f7400b29c [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
Raymond Hettingerde2e4482018-10-08 08:02:41 -070011static inline 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;
Raymond Hettingerb6f17f52016-02-14 01:41:35 -080015 Py_ssize_t mid;
16 int res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000017
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000018 if (lo < 0) {
19 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
20 return -1;
21 }
22 if (hi == -1) {
23 hi = PySequence_Size(list);
24 if (hi < 0)
25 return -1;
26 }
27 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010028 /* The (size_t)cast ensures that the addition and subsequent division
29 are performed as unsigned operations, avoiding difficulties from
30 signed overflow. (See issue 13496.) */
31 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000032 litem = PySequence_GetItem(list, mid);
33 if (litem == NULL)
34 return -1;
35 res = PyObject_RichCompareBool(item, litem, Py_LT);
36 Py_DECREF(litem);
37 if (res < 0)
38 return -1;
39 if (res)
40 hi = mid;
41 else
42 lo = mid + 1;
43 }
44 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000045}
46
47static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000048bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000049{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000050 PyObject *list, *item;
51 Py_ssize_t lo = 0;
52 Py_ssize_t hi = -1;
53 Py_ssize_t index;
54 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000055
Raymond Hettingerde2e4482018-10-08 08:02:41 -070056 if (kw == NULL && PyTuple_GET_SIZE(args) == 2) {
57 list = PyTuple_GET_ITEM(args, 0);
58 item = PyTuple_GET_ITEM(args, 1);
59 }
60 else {
61 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
62 keywords, &list, &item, &lo, &hi))
63 return NULL;
64 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000065 index = internal_bisect_right(list, item, lo, hi);
66 if (index < 0)
67 return NULL;
68 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000069}
70
71PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000072"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000073\n\
74Return the index where to insert item x in list a, assuming a is sorted.\n\
75\n\
76The return value i is such that all e in a[:i] have e <= x, and all e in\n\
77a[i:] have e > x. So if x already appears in the list, i points just\n\
78beyond the rightmost x already there\n\
79\n\
80Optional args lo (default 0) and hi (default len(a)) bound the\n\
81slice of a to be searched.\n");
82
83static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000084insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000085{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000086 PyObject *list, *item, *result;
87 Py_ssize_t lo = 0;
88 Py_ssize_t hi = -1;
89 Py_ssize_t index;
90 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000091
Raymond Hettingerde2e4482018-10-08 08:02:41 -070092 if (kw == NULL && PyTuple_GET_SIZE(args) == 2) {
93 list = PyTuple_GET_ITEM(args, 0);
94 item = PyTuple_GET_ITEM(args, 1);
95 }
96 else {
97 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
98 keywords, &list, &item, &lo, &hi))
99 return NULL;
100 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000101 index = internal_bisect_right(list, item, lo, hi);
102 if (index < 0)
103 return NULL;
104 if (PyList_CheckExact(list)) {
105 if (PyList_Insert(list, index, item) < 0)
106 return NULL;
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700107 }
108 else {
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200109 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000110 if (result == NULL)
111 return NULL;
112 Py_DECREF(result);
113 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000114
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000115 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000116}
117
118PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000119"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000120\n\
121Insert item x in list a, and keep it sorted assuming a is sorted.\n\
122\n\
123If x is already in a, insert it to the right of the rightmost x.\n\
124\n\
125Optional args lo (default 0) and hi (default len(a)) bound the\n\
126slice of a to be searched.\n");
127
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700128static inline Py_ssize_t
Benjamin Petersond6313712008-07-31 16:23:04 +0000129internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000130{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000131 PyObject *litem;
Raymond Hettingerb6f17f52016-02-14 01:41:35 -0800132 Py_ssize_t mid;
133 int res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000134
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000135 if (lo < 0) {
136 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
137 return -1;
138 }
139 if (hi == -1) {
140 hi = PySequence_Size(list);
141 if (hi < 0)
142 return -1;
143 }
144 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100145 /* The (size_t)cast ensures that the addition and subsequent division
146 are performed as unsigned operations, avoiding difficulties from
147 signed overflow. (See issue 13496.) */
148 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000149 litem = PySequence_GetItem(list, mid);
150 if (litem == NULL)
151 return -1;
152 res = PyObject_RichCompareBool(litem, item, Py_LT);
153 Py_DECREF(litem);
154 if (res < 0)
155 return -1;
156 if (res)
157 lo = mid + 1;
158 else
159 hi = mid;
160 }
161 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000162}
163
164static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000165bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000166{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000167 PyObject *list, *item;
168 Py_ssize_t lo = 0;
169 Py_ssize_t hi = -1;
170 Py_ssize_t index;
171 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000172
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700173 if (kw == NULL && PyTuple_GET_SIZE(args) == 2) {
174 list = PyTuple_GET_ITEM(args, 0);
175 item = PyTuple_GET_ITEM(args, 1);
176 }
177 else {
178 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
179 keywords, &list, &item, &lo, &hi))
180 return NULL;
181 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000182 index = internal_bisect_left(list, item, lo, hi);
183 if (index < 0)
184 return NULL;
185 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000186}
187
188PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000189"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000190\n\
191Return the index where to insert item x in list a, assuming a is sorted.\n\
192\n\
193The return value i is such that all e in a[:i] have e < x, and all e in\n\
194a[i:] have e >= x. So if x already appears in the list, i points just\n\
195before the leftmost x already there.\n\
196\n\
197Optional args lo (default 0) and hi (default len(a)) bound the\n\
198slice of a to be searched.\n");
199
200static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000201insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000202{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000203 PyObject *list, *item, *result;
204 Py_ssize_t lo = 0;
205 Py_ssize_t hi = -1;
206 Py_ssize_t index;
207 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000208
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700209 if (kw == NULL && PyTuple_GET_SIZE(args) == 2) {
210 list = PyTuple_GET_ITEM(args, 0);
211 item = PyTuple_GET_ITEM(args, 1);
212 } else {
213 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
214 keywords, &list, &item, &lo, &hi))
215 return NULL;
216 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000217 index = internal_bisect_left(list, item, lo, hi);
218 if (index < 0)
219 return NULL;
220 if (PyList_CheckExact(list)) {
221 if (PyList_Insert(list, index, item) < 0)
222 return NULL;
223 } else {
Antoine Pitroub7d033d2012-05-16 14:39:36 +0200224 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000225 if (result == NULL)
226 return NULL;
227 Py_DECREF(result);
228 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000229
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000230 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000231}
232
233PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000234"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000235\n\
236Insert item x in list a, and keep it sorted assuming a is sorted.\n\
237\n\
238If x is already in a, insert it to the left of the leftmost x.\n\
239\n\
240Optional args lo (default 0) and hi (default len(a)) bound the\n\
241slice of a to be searched.\n");
242
Raymond Hettinger0c410272004-01-05 10:13:35 +0000243static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000244 {"bisect_right", (PyCFunction)bisect_right,
245 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000246 {"insort_right", (PyCFunction)insort_right,
247 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000248 {"bisect_left", (PyCFunction)bisect_left,
249 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
250 {"insort_left", (PyCFunction)insort_left,
251 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
252 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000253};
254
255PyDoc_STRVAR(module_doc,
256"Bisection algorithms.\n\
257\n\
258This module provides support for maintaining a list in sorted order without\n\
259having to sort the list after each insertion. For long lists of items with\n\
260expensive comparison operations, this can be an improvement over the more\n\
261common approach.\n");
262
Raymond Hettinger0c410272004-01-05 10:13:35 +0000263
Martin v. Löwis1a214512008-06-11 05:26:20 +0000264static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000265 PyModuleDef_HEAD_INIT,
266 "_bisect",
267 module_doc,
268 -1,
269 bisect_methods,
270 NULL,
271 NULL,
272 NULL,
273 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000274};
275
276PyMODINIT_FUNC
277PyInit__bisect(void)
278{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000279 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000280}