blob: 82d800d9a8790f4450fb16a8cccc6696f0f4e033 [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
Shantanu3a855b22020-05-17 20:38:35 -07009/*[clinic input]
10module _bisect
11[clinic start generated code]*/
12/*[clinic end generated code: output=da39a3ee5e6b4b0d input=4d56a2b2033b462b]*/
13
14#include "clinic/_bisectmodule.c.h"
15
Martin v. Löwise75fc142013-11-07 18:46:53 +010016_Py_IDENTIFIER(insert);
17
Raymond Hettingerde2e4482018-10-08 08:02:41 -070018static inline Py_ssize_t
Martin v. Löwisad0a4622006-02-16 14:30:23 +000019internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +000020{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000021 PyObject *litem;
Raymond Hettingerb6f17f52016-02-14 01:41:35 -080022 Py_ssize_t mid;
23 int res;
Raymond Hettinger0c410272004-01-05 10:13:35 +000024
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000025 if (lo < 0) {
26 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
27 return -1;
28 }
29 if (hi == -1) {
30 hi = PySequence_Size(list);
31 if (hi < 0)
32 return -1;
33 }
34 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +010035 /* The (size_t)cast ensures that the addition and subsequent division
36 are performed as unsigned operations, avoiding difficulties from
37 signed overflow. (See issue 13496.) */
38 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000039 litem = PySequence_GetItem(list, mid);
40 if (litem == NULL)
41 return -1;
42 res = PyObject_RichCompareBool(item, litem, Py_LT);
43 Py_DECREF(litem);
44 if (res < 0)
45 return -1;
46 if (res)
47 hi = mid;
48 else
49 lo = mid + 1;
50 }
51 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +000052}
53
Shantanu3a855b22020-05-17 20:38:35 -070054/*[clinic input]
55_bisect.bisect_right -> Py_ssize_t
Raymond Hettinger0c410272004-01-05 10:13:35 +000056
Shantanu3a855b22020-05-17 20:38:35 -070057 a: object
58 x: object
59 lo: Py_ssize_t = 0
60 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
61
62Return the index where to insert item x in list a, assuming a is sorted.
63
64The return value i is such that all e in a[:i] have e <= x, and all e in
65a[i:] have e > x. So if x already appears in the list, i points just
66beyond the rightmost x already there
67
68Optional args lo (default 0) and hi (default len(a)) bound the
69slice of a to be searched.
70[clinic start generated code]*/
71
72static Py_ssize_t
73_bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
74 Py_ssize_t lo, Py_ssize_t hi)
75/*[clinic end generated code: output=419e150cf1d2a235 input=e72212b282c83375]*/
76{
77 return internal_bisect_right(a, x, lo, hi);
Raymond Hettinger0c410272004-01-05 10:13:35 +000078}
79
Shantanu3a855b22020-05-17 20:38:35 -070080/*[clinic input]
81_bisect.insort_right
82
83 a: object
84 x: object
85 lo: Py_ssize_t = 0
86 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
87
88Insert item x in list a, and keep it sorted assuming a is sorted.
89
90If x is already in a, insert it to the right of the rightmost x.
91
92Optional args lo (default 0) and hi (default len(a)) bound the
93slice of a to be searched.
94[clinic start generated code]*/
Raymond Hettinger0c410272004-01-05 10:13:35 +000095
96static PyObject *
Shantanu3a855b22020-05-17 20:38:35 -070097_bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
98 Py_ssize_t lo, Py_ssize_t hi)
99/*[clinic end generated code: output=c2caa3d4cd02035a input=d1c45bfa68182669]*/
Raymond Hettinger0c410272004-01-05 10:13:35 +0000100{
Shantanu3a855b22020-05-17 20:38:35 -0700101 PyObject *result;
102 Py_ssize_t index = internal_bisect_right(a, x, lo, hi);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000103 if (index < 0)
104 return NULL;
Shantanu3a855b22020-05-17 20:38:35 -0700105 if (PyList_CheckExact(a)) {
106 if (PyList_Insert(a, index, x) < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000107 return NULL;
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700108 }
109 else {
Shantanu3a855b22020-05-17 20:38:35 -0700110 result = _PyObject_CallMethodId(a, &PyId_insert, "nO", index, x);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000111 if (result == NULL)
112 return NULL;
113 Py_DECREF(result);
114 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000115
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000116 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000117}
118
Raymond Hettingerde2e4482018-10-08 08:02:41 -0700119static inline Py_ssize_t
Benjamin Petersond6313712008-07-31 16:23:04 +0000120internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
Raymond Hettinger0c410272004-01-05 10:13:35 +0000121{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000122 PyObject *litem;
Raymond Hettingerb6f17f52016-02-14 01:41:35 -0800123 Py_ssize_t mid;
124 int res;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000125
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000126 if (lo < 0) {
127 PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
128 return -1;
129 }
130 if (hi == -1) {
131 hi = PySequence_Size(list);
132 if (hi < 0)
133 return -1;
134 }
135 while (lo < hi) {
Mark Dickinsona13b1092012-04-15 16:30:35 +0100136 /* The (size_t)cast ensures that the addition and subsequent division
137 are performed as unsigned operations, avoiding difficulties from
138 signed overflow. (See issue 13496.) */
139 mid = ((size_t)lo + hi) / 2;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000140 litem = PySequence_GetItem(list, mid);
141 if (litem == NULL)
142 return -1;
143 res = PyObject_RichCompareBool(litem, item, Py_LT);
144 Py_DECREF(litem);
145 if (res < 0)
146 return -1;
147 if (res)
148 lo = mid + 1;
149 else
150 hi = mid;
151 }
152 return lo;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000153}
154
Raymond Hettinger0c410272004-01-05 10:13:35 +0000155
Shantanu3a855b22020-05-17 20:38:35 -0700156/*[clinic input]
157_bisect.bisect_left -> Py_ssize_t
158
159 a: object
160 x: object
161 lo: Py_ssize_t = 0
162 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
163
164Return the index where to insert item x in list a, assuming a is sorted.
165
166The return value i is such that all e in a[:i] have e < x, and all e in
167a[i:] have e >= x. So if x already appears in the list, i points just
168before the leftmost x already there.
169
170Optional args lo (default 0) and hi (default len(a)) bound the
171slice of a to be searched.
172[clinic start generated code]*/
173
174static Py_ssize_t
175_bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
176 Py_ssize_t lo, Py_ssize_t hi)
177/*[clinic end generated code: output=af82168bc2856f24 input=2bd90f34afe5609f]*/
178{
179 return internal_bisect_left(a, x, lo, hi);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000180}
181
Shantanu3a855b22020-05-17 20:38:35 -0700182
183/*[clinic input]
184_bisect.insort_left
185
186 a: object
187 x: object
188 lo: Py_ssize_t = 0
189 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
190
191Insert item x in list a, and keep it sorted assuming a is sorted.
192
193If x is already in a, insert it to the left of the leftmost x.
194
195Optional args lo (default 0) and hi (default len(a)) bound the
196slice of a to be searched.
197[clinic start generated code]*/
Raymond Hettinger0c410272004-01-05 10:13:35 +0000198
199static PyObject *
Shantanu3a855b22020-05-17 20:38:35 -0700200_bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
201 Py_ssize_t lo, Py_ssize_t hi)
202/*[clinic end generated code: output=9e8356c0844a182b input=bc4583308bce00cc]*/
Raymond Hettinger0c410272004-01-05 10:13:35 +0000203{
Shantanu3a855b22020-05-17 20:38:35 -0700204 PyObject *result;
205 Py_ssize_t index = internal_bisect_left(a, x, lo, hi);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000206 if (index < 0)
207 return NULL;
Shantanu3a855b22020-05-17 20:38:35 -0700208 if (PyList_CheckExact(a)) {
209 if (PyList_Insert(a, index, x) < 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000210 return NULL;
211 } else {
Shantanu3a855b22020-05-17 20:38:35 -0700212 result = _PyObject_CallMethodId(a, &PyId_insert, "nO", index, x);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000213 if (result == NULL)
214 return NULL;
215 Py_DECREF(result);
216 }
Raymond Hettinger0c410272004-01-05 10:13:35 +0000217
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000218 Py_RETURN_NONE;
Raymond Hettinger0c410272004-01-05 10:13:35 +0000219}
220
Raymond Hettinger0c410272004-01-05 10:13:35 +0000221static PyMethodDef bisect_methods[] = {
Shantanu3a855b22020-05-17 20:38:35 -0700222 _BISECT_BISECT_RIGHT_METHODDEF
223 _BISECT_INSORT_RIGHT_METHODDEF
224 _BISECT_BISECT_LEFT_METHODDEF
225 _BISECT_INSORT_LEFT_METHODDEF
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000226 {NULL, NULL} /* sentinel */
Raymond Hettinger0c410272004-01-05 10:13:35 +0000227};
228
229PyDoc_STRVAR(module_doc,
230"Bisection algorithms.\n\
231\n\
232This module provides support for maintaining a list in sorted order without\n\
233having to sort the list after each insertion. For long lists of items with\n\
234expensive comparison operations, this can be an improvement over the more\n\
235common approach.\n");
236
Raymond Hettinger0c410272004-01-05 10:13:35 +0000237
Martin v. Löwis1a214512008-06-11 05:26:20 +0000238static struct PyModuleDef _bisectmodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000239 PyModuleDef_HEAD_INIT,
240 "_bisect",
241 module_doc,
242 -1,
243 bisect_methods,
244 NULL,
245 NULL,
246 NULL,
247 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +0000248};
249
250PyMODINIT_FUNC
251PyInit__bisect(void)
252{
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000253 return PyModule_Create(&_bisectmodule);
Raymond Hettinger0c410272004-01-05 10:13:35 +0000254}