blob: ffcd994cfaee462dce21e9e00cb6b7e270d3fdac [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
Raymond Hettinger527eee22008-07-24 05:38:48 +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 Pitrouc83ea132010-05-09 14:46:46 +000011 PyObject *litem;
12 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000013
Antoine Pitrouc83ea132010-05-09 14:46:46 +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 Dickinson0407e962012-04-15 16:43:19 +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.) */
Mark Dickinson0407e962012-04-15 16:43:19 +010027 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 PyInt_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 Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 {
92 result = PyObject_CallMethod(list, "insert", "nO",
93 index, item);
94 if (result == NULL)
95 return NULL;
96 Py_DECREF(result);
97 }
Raymond Hettinger0c410272004-01-05 10:13:35 +000098
Antoine Pitrouc83ea132010-05-09 14:46:46 +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
Raymond Hettinger527eee22008-07-24 05:38:48 +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 Pitrouc83ea132010-05-09 14:46:46 +0000115 PyObject *litem;
116 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000117
Antoine Pitrouc83ea132010-05-09 14:46:46 +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 Dickinson0407e962012-04-15 16:43:19 +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 Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 PyInt_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 Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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 {
196 result = PyObject_CallMethod(list, "insert", "iO",
197 index, item);
198 if (result == NULL)
199 return NULL;
200 Py_DECREF(result);
201 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000202
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000203 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000204}
205
206PyDoc_STRVAR(insort_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000207"insort_left(a, x[, lo[, hi]])\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000208\n\
209Insert item x in list a, and keep it sorted assuming a is sorted.\n\
210\n\
211If x is already in a, insert it to the left of the leftmost x.\n\
212\n\
213Optional args lo (default 0) and hi (default len(a)) bound the\n\
214slice of a to be searched.\n");
215
216PyDoc_STRVAR(bisect_doc, "Alias for bisect_right().\n");
217PyDoc_STRVAR(insort_doc, "Alias for insort_right().\n");
218
219static PyMethodDef bisect_methods[] = {
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000220 {"bisect_right", (PyCFunction)bisect_right,
221 METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
222 {"bisect", (PyCFunction)bisect_right,
223 METH_VARARGS|METH_KEYWORDS, bisect_doc},
224 {"insort_right", (PyCFunction)insort_right,
225 METH_VARARGS|METH_KEYWORDS, insort_right_doc},
226 {"insort", (PyCFunction)insort_right,
227 METH_VARARGS|METH_KEYWORDS, insort_doc},
228 {"bisect_left", (PyCFunction)bisect_left,
229 METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
230 {"insort_left", (PyCFunction)insort_left,
231 METH_VARARGS|METH_KEYWORDS, insort_left_doc},
232 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000233};
234
235PyDoc_STRVAR(module_doc,
236"Bisection algorithms.\n\
237\n\
238This module provides support for maintaining a list in sorted order without\n\
239having to sort the list after each insertion. For long lists of items with\n\
240expensive comparison operations, this can be an improvement over the more\n\
241common approach.\n");
242
243PyMODINIT_FUNC
244init_bisect(void)
245{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000246 Py_InitModule3("_bisect", bisect_methods, module_doc);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000247}