blob: 4798c124eefa10e6fb36aeaf451a70b80441130f [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.) */
27 printf("lo: %d\n", lo);
28 printf("hi: %d\n", hi);
29 printf("mid: %d\n", mid);
30 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouc83ea132010-05-09 14:46:46 +000031 litem = PySequence_GetItem(list, mid);
32 if (litem == NULL)
33 return -1;
34 res = PyObject_RichCompareBool(item, litem, Py_LT);
35 Py_DECREF(litem);
36 if (res < 0)
37 return -1;
38 if (res)
39 hi = mid;
40 else
41 lo = mid + 1;
42 }
43 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000044}
45
46static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000047bisect_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000048{
Antoine Pitrouc83ea132010-05-09 14:46:46 +000049 PyObject *list, *item;
50 Py_ssize_t lo = 0;
51 Py_ssize_t hi = -1;
52 Py_ssize_t index;
53 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000054
Antoine Pitrouc83ea132010-05-09 14:46:46 +000055 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
56 keywords, &list, &item, &lo, &hi))
57 return NULL;
58 index = internal_bisect_right(list, item, lo, hi);
59 if (index < 0)
60 return NULL;
61 return PyInt_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +000062}
63
64PyDoc_STRVAR(bisect_right_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +000065"bisect_right(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +000066\n\
67Return the index where to insert item x in list a, assuming a is sorted.\n\
68\n\
69The return value i is such that all e in a[:i] have e <= x, and all e in\n\
70a[i:] have e > x. So if x already appears in the list, i points just\n\
71beyond the rightmost x already there\n\
72\n\
73Optional args lo (default 0) and hi (default len(a)) bound the\n\
74slice of a to be searched.\n");
75
76static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +000077insort_right(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +000078{
Antoine Pitrouc83ea132010-05-09 14:46:46 +000079 PyObject *list, *item, *result;
80 Py_ssize_t lo = 0;
81 Py_ssize_t hi = -1;
82 Py_ssize_t index;
83 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +000084
Antoine Pitrouc83ea132010-05-09 14:46:46 +000085 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
86 keywords, &list, &item, &lo, &hi))
87 return NULL;
88 index = internal_bisect_right(list, item, lo, hi);
89 if (index < 0)
90 return NULL;
91 if (PyList_CheckExact(list)) {
92 if (PyList_Insert(list, index, item) < 0)
93 return NULL;
94 } else {
95 result = PyObject_CallMethod(list, "insert", "nO",
96 index, item);
97 if (result == NULL)
98 return NULL;
99 Py_DECREF(result);
100 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000101
Antoine Pitrouc83ea132010-05-09 14:46:46 +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
Raymond Hettinger527eee22008-07-24 05:38:48 +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 Pitrouc83ea132010-05-09 14:46:46 +0000118 PyObject *litem;
119 Py_ssize_t mid, res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000120
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000121 if (lo < 0) {
122 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
123 return -1;
124 }
125 if (hi == -1) {
126 hi = PySequence_Size(list);
127 if (hi < 0)
128 return -1;
129 }
130 while (lo < hi) {
Mark Dickinson0407e962012-04-15 16:43:19 +0100131 /* The (size_t)cast ensures that the addition and subsequent division
132 are performed as unsigned operations, avoiding difficulties from
133 signed overflow. (See issue 13496.) */
134 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000135 litem = PySequence_GetItem(list, mid);
136 if (litem == NULL)
137 return -1;
138 res = PyObject_RichCompareBool(litem, item, Py_LT);
139 Py_DECREF(litem);
140 if (res < 0)
141 return -1;
142 if (res)
143 lo = mid + 1;
144 else
145 hi = mid;
146 }
147 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000148}
149
150static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000151bisect_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000152{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000153 PyObject *list, *item;
154 Py_ssize_t lo = 0;
155 Py_ssize_t hi = -1;
156 Py_ssize_t index;
157 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000158
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000159 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
160 keywords, &list, &item, &lo, &hi))
161 return NULL;
162 index = internal_bisect_left(list, item, lo, hi);
163 if (index < 0)
164 return NULL;
165 return PyInt_FromSsize_t(index);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000166}
167
168PyDoc_STRVAR(bisect_left_doc,
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000169"bisect_left(a, x[, lo[, hi]]) -> index\n\
Raymond Hettinger0c410272004-01-05 10:13:35 +0000170\n\
171Return the index where to insert item x in list a, assuming a is sorted.\n\
172\n\
173The return value i is such that all e in a[:i] have e < x, and all e in\n\
174a[i:] have e >= x. So if x already appears in the list, i points just\n\
175before the leftmost x already there.\n\
176\n\
177Optional args lo (default 0) and hi (default len(a)) bound the\n\
178slice of a to be searched.\n");
179
180static PyObject *
Raymond Hettingercc9a9512005-10-05 11:39:12 +0000181insort_left(PyObject *self, PyObject *args, PyObject *kw)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000182{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000183 PyObject *list, *item, *result;
184 Py_ssize_t lo = 0;
185 Py_ssize_t hi = -1;
186 Py_ssize_t index;
187 static char *keywords[] = {"a", "x", "lo", "hi", NULL};
Raymond Hettinger0c410272004-01-05 10:13:35 +0000188
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000189 if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
190 keywords, &list, &item, &lo, &hi))
191 return NULL;
192 index = internal_bisect_left(list, item, lo, hi);
193 if (index < 0)
194 return NULL;
195 if (PyList_CheckExact(list)) {
196 if (PyList_Insert(list, index, item) < 0)
197 return NULL;
198 } else {
199 result = PyObject_CallMethod(list, "insert", "iO",
200 index, item);
201 if (result == NULL)
202 return NULL;
203 Py_DECREF(result);
204 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000205
Antoine Pitrouc83ea132010-05-09 14:46:46 +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 Pitrouc83ea132010-05-09 14:46:46 +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
246PyMODINIT_FUNC
247init_bisect(void)
248{
Antoine Pitrouc83ea132010-05-09 14:46:46 +0000249 Py_InitModule3("_bisect", bisect_methods, module_doc);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000250}