blob: 93d0eed41e4a173fcdf367eff2a5f9d79934d261 [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
6#include "Python.h"
7
Benjamin Petersond6313712008-07-31 16:23:04 +00008static Py_ssize_t
Martin v. Löwisad0a4622006-02-16 14:30:23 +00009internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +000010{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000011 PyObject *litem;
12 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000013
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000014 if (lo < 0) {
15 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
16 return -1;
17 }
18 if (hi == -1) {
19 hi = PySequence_Size(list);
20 if (hi < 0)
21 return -1;
22 }
23 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010024 /* The (size_t)cast ensures that the addition and subsequent division
25 are performed as unsigned operations, avoiding difficulties from
26 signed overflow. (See issue 13496.) */
27 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000028 litem = PySequence_GetItem(list, mid);
29 if (litem == NULL)
30 return -1;
31 res = PyObject_RichCompareBool(item, litem, Py_LT);
32 Py_DECREF(litem);
33 if (res < 0)
34 return -1;
35 if (res)
36 hi = mid;
37 else
38 lo = mid + 1;
39 }
40 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000041}
42
43static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000044bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000045{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000046 PyObject *list, *item;
47 Py_ssize_t lo = 0;
48 Py_ssize_t hi = -1;
49 Py_ssize_t index;
50 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000051
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000052 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
53 keywords, &list, &item, &lo, &hi))
54 return NULL;
55 index = internal_bisect_right(list, item, lo, hi);
56 if (index < 0)
57 return NULL;
58 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000059}
60
61PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000062"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000063\n\
64Return the index where to insert item x in list a, assuming a is sorted.\n\
65\n\
66The return value i is such that all e in a[:i] have e <= x, and all e in\n\
67a[i:] have e > x. So if x already appears in the list, i points just\n\
68beyond the rightmost x already there\n\
69\n\
70Optional args lo (default 0) and hi (default len(a)) bound the\n\
71slice of a to be searched.\n");
72
73static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000074insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000075{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000076 PyObject *list, *item, *result;
77 Py_ssize_t lo = 0;
78 Py_ssize_t hi = -1;
79 Py_ssize_t index;
80 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000081
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000082 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
83 keywords, &list, &item, &lo, &hi))
84 return NULL;
85 index = internal_bisect_right(list, item, lo, hi);
86 if (index < 0)
87 return NULL;
88 if (PyList_CheckExact(list)) {
89 if (PyList_Insert(list, index, item) < 0)
90 return NULL;
91 } else {
Raymond Hettinger6b3d72c2010-09-01 08:56:10 +000092 result = PyObject_CallMethod(list, "insert", "nO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000093 if (result == NULL)
94 return NULL;
95 Py_DECREF(result);
96 }
Raymond Hettinger0c410272004-01-05 10:13:35 +000097
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000098 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +000099}
100
101PyDoc_STRVAR(insort_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000102"insort_right(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000103\n\
104Insert item x in list a, and keep it sorted assuming a is sorted.\n\
105\n\
106If x is already in a, insert it to the right of the rightmost x.\n\
107\n\
108Optional args lo (default 0) and hi (default len(a)) bound the\n\
109slice of a to be searched.\n");
110
Benjamin Petersond6313712008-07-31 16:23:04 +0000111static Py_ssize_t
112internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000113{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000114 PyObject *litem;
115 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000116
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000117 if (lo < 0) {
118 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
119 return -1;
120 }
121 if (hi == -1) {
122 hi = PySequence_Size(list);
123 if (hi < 0)
124 return -1;
125 }
126 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100127 /* The (size_t)cast ensures that the addition and subsequent division
128 are performed as unsigned operations, avoiding difficulties from
129 signed overflow. (See issue 13496.) */
130 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000131 litem = PySequence_GetItem(list, mid);
132 if (litem == NULL)
133 return -1;
134 res = PyObject_RichCompareBool(litem, item, Py_LT);
135 Py_DECREF(litem);
136 if (res < 0)
137 return -1;
138 if (res)
139 lo = mid + 1;
140 else
141 hi = mid;
142 }
143 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000144}
145
146static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000147bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000148{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000149 PyObject *list, *item;
150 Py_ssize_t lo = 0;
151 Py_ssize_t hi = -1;
152 Py_ssize_t index;
153 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000154
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000155 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
156 keywords, &list, &item, &lo, &hi))
157 return NULL;
158 index = internal_bisect_left(list, item, lo, hi);
159 if (index < 0)
160 return NULL;
161 return PyLong_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000162}
163
164PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000165"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000166\n\
167Return the index where to insert item x in list a, assuming a is sorted.\n\
168\n\
169The return value i is such that all e in a[:i] have e < x, and all e in\n\
170a[i:] have e >= x. So if x already appears in the list, i points just\n\
171before the leftmost x already there.\n\
172\n\
173Optional args lo (default 0) and hi (default len(a)) bound the\n\
174slice of a to be searched.\n");
175
176static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000177insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000178{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000179 PyObject *list, *item, *result;
180 Py_ssize_t lo = 0;
181 Py_ssize_t hi = -1;
182 Py_ssize_t index;
183 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000184
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000185 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
186 keywords, &list, &item, &lo, &hi))
187 return NULL;
188 index = internal_bisect_left(list, item, lo, hi);
189 if (index < 0)
190 return NULL;
191 if (PyList_CheckExact(list)) {
192 if (PyList_Insert(list, index, item) < 0)
193 return NULL;
194 } else {
Raymond Hettinger6b3d72c2010-09-01 08:56:10 +0000195 result = PyObject_CallMethod(list, "insert", "iO", index, item);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000196 if (result == NULL)
197 return NULL;
198 Py_DECREF(result);
199 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000200
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000201 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000202}
203
204PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000205"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000206\n\
207Insert item x in list a, and keep it sorted assuming a is sorted.\n\
208\n\
209If x is already in a, insert it to the left of the leftmost x.\n\
210\n\
211Optional args lo (default 0) and hi (default len(a)) bound the\n\
212slice of a to be searched.\n");
213
214PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
215PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
216
217static PyMethodDef bisect_methods[] = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000218 {"bisect_right", (PyCFunction)bisect_right,
219 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
220 {"bisect", (PyCFunction)bisect_right,
221 METH_VARARGS|METH_KEYWORDS, bisect_doc},
222 {"insort_right", (PyCFunction)insort_right,
223 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
224 {"insort", (PyCFunction)insort_right,
225 METH_VARARGS|METH_KEYWORDS, insort_doc},
226 {"bisect_left", (PyCFunction)bisect_left,
227 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
228 {"insort_left", (PyCFunction)insort_left,
229 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
230 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000231};
232
233PyDoc_STRVAR(module_doc,
234"Bisection algorithms.\n\
235\n\
236This module provides support for maintaining a list in sorted order without\n\
237having to sort the list after each insertion. For long lists of items with\n\
238expensive comparison operations, this can be an improvement over the more\n\
239common approach.\n");
240
Raymond Hettinger0c410272004-01-05 10:13:35 +0000241
Martin v. Löwis1a214512008-06-11 05:26:20 +0000242static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000243 PyModuleDef_HEAD_INIT,
244 "_bisect",
245 module_doc,
246 -1,
247 bisect_methods,
248 NULL,
249 NULL,
250 NULL,
251 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000252};
253
254PyMODINIT_FUNC
255PyInit__bisect(void)
256{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000257 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000258}