blob: 22ddbf2a569a9ab4a5cc94722f3770272e99cdc2 [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;
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
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000056 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
57 keywords, &list, &item, &lo, &hi))
58 return NULL;
59 index = internal_bisect_right(list, item, lo, hi);
60 if (index < 0)
61 return NULL;
62 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000063}
64
65PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000066"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000067\n\
68Return the index where to insert item x in list a, assuming a is sorted.\n\
69\n\
70The return value i is such that all e in a[:i] have e <= x, and all e in\n\
71a[i:] have e > x. So if x already appears in the list, i points just\n\
72beyond the rightmost x already there\n\
73\n\
74Optional args lo (default 0) and hi (default len(a)) bound the\n\
75slice of a to be searched.\n");
76
77static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000078insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000079{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000080 PyObject *list, *item, *result;
81 Py_ssize_t lo = 0;
82 Py_ssize_t hi = -1;
83 Py_ssize_t index;
84 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000085
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000086 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
87 keywords, &list, &item, &lo, &hi))
88 return NULL;
89 index = internal_bisect_right(list, item, lo, hi);
90 if (index < 0)
91 return NULL;
92 if (PyList_CheckExact(list)) {
93 if (PyList_Insert(list, index, item) < 0)
94 return NULL;
95 } else {
Martin v. Löwisafe55bb2011-10-09 10:38:36 +020096 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000097 if (result == NULL)
98 return NULL;
99 Py_DECREF(result);
100 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000101
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000102 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000103}
104
105PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000106"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000107\n\
108Insert item x in list a, and keep it sorted assuming a is sorted.\n\
109\n\
110If x is already in a, insert it to the right of the rightmost x.\n\
111\n\
112Optional args lo (default 0) and hi (default len(a)) bound the\n\
113slice of a to be searched.\n");
114
Benjamin Petersond6313712008-07-31 16:23:04 +0000115static Py_ssize_t
116internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000117{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000118 PyObject *litem;
Raymond Hettingerb6f17f52016-02-14 01:41:35 -0800119 Py_ssize_t mid;
120 int res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000121
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000122 if (lo < 0) {
123 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
124 return -1;
125 }
126 if (hi == -1) {
127 hi = PySequence_Size(list);
128 if (hi < 0)
129 return -1;
130 }
131 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100132 /* The (size_t)cast ensures that the addition and subsequent division
133 are performed as unsigned operations, avoiding difficulties from
134 signed overflow. (See issue 13496.) */
135 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000136 litem = PySequence_GetItem(list, mid);
137 if (litem == NULL)
138 return -1;
139 res = PyObject_RichCompareBool(litem, item, Py_LT);
140 Py_DECREF(litem);
141 if (res < 0)
142 return -1;
143 if (res)
144 lo = mid + 1;
145 else
146 hi = mid;
147 }
148 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000149}
150
151static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000152bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000153{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000154 PyObject *list, *item;
155 Py_ssize_t lo = 0;
156 Py_ssize_t hi = -1;
157 Py_ssize_t index;
158 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000159
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000160 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
161 keywords, &list, &item, &lo, &hi))
162 return NULL;
163 index = internal_bisect_left(list, item, lo, hi);
164 if (index < 0)
165 return NULL;
166 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000167}
168
169PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000170"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000171\n\
172Return the index where to insert item x in list a, assuming a is sorted.\n\
173\n\
174The return value i is such that all e in a[:i] have e < x, and all e in\n\
175a[i:] have e >= x. So if x already appears in the list, i points just\n\
176before the leftmost x already there.\n\
177\n\
178Optional args lo (default 0) and hi (default len(a)) bound the\n\
179slice of a to be searched.\n");
180
181static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000182insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000183{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000184 PyObject *list, *item, *result;
185 Py_ssize_t lo = 0;
186 Py_ssize_t hi = -1;
187 Py_ssize_t index;
188 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000189
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000190 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
191 keywords, &list, &item, &lo, &hi))
192 return NULL;
193 index = internal_bisect_left(list, item, lo, hi);
194 if (index < 0)
195 return NULL;
196 if (PyList_CheckExact(list)) {
197 if (PyList_Insert(list, index, item) < 0)
198 return NULL;
199 } else {
Antoine Pitroub7d033d2012-05-16 14:39:36 +0200200 result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000201 if (result == NULL)
202 return NULL;
203 Py_DECREF(result);
204 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000205
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000206 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000207}
208
209PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000210"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000211\n\
212Insert item x in list a, and keep it sorted assuming a is sorted.\n\
213\n\
214If x is already in a, insert it to the left of the leftmost x.\n\
215\n\
216Optional args lo (default 0) and hi (default len(a)) bound the\n\
217slice of a to be searched.\n");
218
219PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
220PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
221
222static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000223 {"bisect_right", (PyCFunction)bisect_right,
224 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
225 {"bisect", (PyCFunction)bisect_right,
226 METH_VARARGS|METH_KEYWORDS, bisect_doc},
227 {"insort_right", (PyCFunction)insort_right,
228 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
229 {"insort", (PyCFunction)insort_right,
230 METH_VARARGS|METH_KEYWORDS, insort_doc},
231 {"bisect_left", (PyCFunction)bisect_left,
232 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
233 {"insort_left", (PyCFunction)insort_left,
234 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
235 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000236};
237
238PyDoc_STRVAR(module_doc,
239"Bisection algorithms.\n\
240\n\
241This module provides support for maintaining a list in sorted order without\n\
242having to sort the list after each insertion. For long lists of items with\n\
243expensive comparison operations, this can be an improvement over the more\n\
244common approach.\n");
245
Raymond Hettinger0c410272004-01-05 10:13:35 +0000246
Martin v. Löwis1a214512008-06-11 05:26:20 +0000247static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000248 PyModuleDef_HEAD_INIT,
249 "_bisect",
250 module_doc,
251 -1,
252 bisect_methods,
253 NULL,
254 NULL,
255 NULL,
256 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000257};
258
259PyMODINIT_FUNC
260PyInit__bisect(void)
261{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000262 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000263}