17db96d56Sopenharmony_ci/* Bisection algorithms. Drop in replacement for bisect.py
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciConverted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
47db96d56Sopenharmony_ci*/
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ci#define PY_SSIZE_T_CLEAN
77db96d56Sopenharmony_ci#include "Python.h"
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci/*[clinic input]
107db96d56Sopenharmony_cimodule _bisect
117db96d56Sopenharmony_ci[clinic start generated code]*/
127db96d56Sopenharmony_ci/*[clinic end generated code: output=da39a3ee5e6b4b0d input=4d56a2b2033b462b]*/
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci#include "clinic/_bisectmodule.c.h"
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_citypedef struct {
177db96d56Sopenharmony_ci    PyObject *str_insert;
187db96d56Sopenharmony_ci} bisect_state;
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_cistatic inline bisect_state*
217db96d56Sopenharmony_ciget_bisect_state(PyObject *module)
227db96d56Sopenharmony_ci{
237db96d56Sopenharmony_ci    void *state = PyModule_GetState(module);
247db96d56Sopenharmony_ci    assert(state != NULL);
257db96d56Sopenharmony_ci    return (bisect_state *)state;
267db96d56Sopenharmony_ci}
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_cistatic inline Py_ssize_t
297db96d56Sopenharmony_ciinternal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
307db96d56Sopenharmony_ci                      PyObject* key)
317db96d56Sopenharmony_ci{
327db96d56Sopenharmony_ci    PyObject *litem;
337db96d56Sopenharmony_ci    Py_ssize_t mid;
347db96d56Sopenharmony_ci    int res;
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_ci    if (lo < 0) {
377db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
387db96d56Sopenharmony_ci        return -1;
397db96d56Sopenharmony_ci    }
407db96d56Sopenharmony_ci    if (hi == -1) {
417db96d56Sopenharmony_ci        hi = PySequence_Size(list);
427db96d56Sopenharmony_ci        if (hi < 0)
437db96d56Sopenharmony_ci            return -1;
447db96d56Sopenharmony_ci    }
457db96d56Sopenharmony_ci    while (lo < hi) {
467db96d56Sopenharmony_ci        /* The (size_t)cast ensures that the addition and subsequent division
477db96d56Sopenharmony_ci           are performed as unsigned operations, avoiding difficulties from
487db96d56Sopenharmony_ci           signed overflow.  (See issue 13496.) */
497db96d56Sopenharmony_ci        mid = ((size_t)lo + hi) / 2;
507db96d56Sopenharmony_ci        litem = PySequence_GetItem(list, mid);
517db96d56Sopenharmony_ci        if (litem == NULL)
527db96d56Sopenharmony_ci            return -1;
537db96d56Sopenharmony_ci        if (key != Py_None) {
547db96d56Sopenharmony_ci            PyObject *newitem = PyObject_CallOneArg(key, litem);
557db96d56Sopenharmony_ci            if (newitem == NULL) {
567db96d56Sopenharmony_ci                Py_DECREF(litem);
577db96d56Sopenharmony_ci                return -1;
587db96d56Sopenharmony_ci            }
597db96d56Sopenharmony_ci            Py_SETREF(litem, newitem);
607db96d56Sopenharmony_ci        }
617db96d56Sopenharmony_ci        res = PyObject_RichCompareBool(item, litem, Py_LT);
627db96d56Sopenharmony_ci        Py_DECREF(litem);
637db96d56Sopenharmony_ci        if (res < 0)
647db96d56Sopenharmony_ci            return -1;
657db96d56Sopenharmony_ci        if (res)
667db96d56Sopenharmony_ci            hi = mid;
677db96d56Sopenharmony_ci        else
687db96d56Sopenharmony_ci            lo = mid + 1;
697db96d56Sopenharmony_ci    }
707db96d56Sopenharmony_ci    return lo;
717db96d56Sopenharmony_ci}
727db96d56Sopenharmony_ci
737db96d56Sopenharmony_ci/*[clinic input]
747db96d56Sopenharmony_ci_bisect.bisect_right -> Py_ssize_t
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ci    a: object
777db96d56Sopenharmony_ci    x: object
787db96d56Sopenharmony_ci    lo: Py_ssize_t = 0
797db96d56Sopenharmony_ci    hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
807db96d56Sopenharmony_ci    *
817db96d56Sopenharmony_ci    key: object = None
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ciReturn the index where to insert item x in list a, assuming a is sorted.
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ciThe return value i is such that all e in a[:i] have e <= x, and all e in
867db96d56Sopenharmony_cia[i:] have e > x.  So if x already appears in the list, a.insert(i, x) will
877db96d56Sopenharmony_ciinsert just after the rightmost x already there.
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ciOptional args lo (default 0) and hi (default len(a)) bound the
907db96d56Sopenharmony_cislice of a to be searched.
917db96d56Sopenharmony_ci[clinic start generated code]*/
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_cistatic Py_ssize_t
947db96d56Sopenharmony_ci_bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
957db96d56Sopenharmony_ci                          Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
967db96d56Sopenharmony_ci/*[clinic end generated code: output=3a4bc09cc7c8a73d input=40fcc5afa06ae593]*/
977db96d56Sopenharmony_ci{
987db96d56Sopenharmony_ci    return internal_bisect_right(a, x, lo, hi, key);
997db96d56Sopenharmony_ci}
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ci/*[clinic input]
1027db96d56Sopenharmony_ci_bisect.insort_right
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ci    a: object
1057db96d56Sopenharmony_ci    x: object
1067db96d56Sopenharmony_ci    lo: Py_ssize_t = 0
1077db96d56Sopenharmony_ci    hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
1087db96d56Sopenharmony_ci    *
1097db96d56Sopenharmony_ci    key: object = None
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ciInsert item x in list a, and keep it sorted assuming a is sorted.
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ciIf x is already in a, insert it to the right of the rightmost x.
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ciOptional args lo (default 0) and hi (default len(a)) bound the
1167db96d56Sopenharmony_cislice of a to be searched.
1177db96d56Sopenharmony_ci[clinic start generated code]*/
1187db96d56Sopenharmony_ci
1197db96d56Sopenharmony_cistatic PyObject *
1207db96d56Sopenharmony_ci_bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
1217db96d56Sopenharmony_ci                          Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
1227db96d56Sopenharmony_ci/*[clinic end generated code: output=ac3bf26d07aedda2 input=44e1708e26b7b802]*/
1237db96d56Sopenharmony_ci{
1247db96d56Sopenharmony_ci    PyObject *result, *key_x;
1257db96d56Sopenharmony_ci    Py_ssize_t index;
1267db96d56Sopenharmony_ci
1277db96d56Sopenharmony_ci    if (key == Py_None) {
1287db96d56Sopenharmony_ci        index = internal_bisect_right(a, x, lo, hi, key);
1297db96d56Sopenharmony_ci    } else {
1307db96d56Sopenharmony_ci        key_x = PyObject_CallOneArg(key, x);
1317db96d56Sopenharmony_ci        if (key_x == NULL) {
1327db96d56Sopenharmony_ci            return NULL;
1337db96d56Sopenharmony_ci        }
1347db96d56Sopenharmony_ci        index = internal_bisect_right(a, key_x, lo, hi, key);
1357db96d56Sopenharmony_ci        Py_DECREF(key_x);
1367db96d56Sopenharmony_ci    }
1377db96d56Sopenharmony_ci    if (index < 0)
1387db96d56Sopenharmony_ci        return NULL;
1397db96d56Sopenharmony_ci    if (PyList_CheckExact(a)) {
1407db96d56Sopenharmony_ci        if (PyList_Insert(a, index, x) < 0)
1417db96d56Sopenharmony_ci            return NULL;
1427db96d56Sopenharmony_ci    }
1437db96d56Sopenharmony_ci    else {
1447db96d56Sopenharmony_ci        bisect_state *state = get_bisect_state(module);
1457db96d56Sopenharmony_ci        result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
1467db96d56Sopenharmony_ci        if (result == NULL)
1477db96d56Sopenharmony_ci            return NULL;
1487db96d56Sopenharmony_ci        Py_DECREF(result);
1497db96d56Sopenharmony_ci    }
1507db96d56Sopenharmony_ci
1517db96d56Sopenharmony_ci    Py_RETURN_NONE;
1527db96d56Sopenharmony_ci}
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_cistatic inline Py_ssize_t
1557db96d56Sopenharmony_ciinternal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
1567db96d56Sopenharmony_ci                     PyObject *key)
1577db96d56Sopenharmony_ci{
1587db96d56Sopenharmony_ci    PyObject *litem;
1597db96d56Sopenharmony_ci    Py_ssize_t mid;
1607db96d56Sopenharmony_ci    int res;
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    if (lo < 0) {
1637db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
1647db96d56Sopenharmony_ci        return -1;
1657db96d56Sopenharmony_ci    }
1667db96d56Sopenharmony_ci    if (hi == -1) {
1677db96d56Sopenharmony_ci        hi = PySequence_Size(list);
1687db96d56Sopenharmony_ci        if (hi < 0)
1697db96d56Sopenharmony_ci            return -1;
1707db96d56Sopenharmony_ci    }
1717db96d56Sopenharmony_ci    while (lo < hi) {
1727db96d56Sopenharmony_ci        /* The (size_t)cast ensures that the addition and subsequent division
1737db96d56Sopenharmony_ci           are performed as unsigned operations, avoiding difficulties from
1747db96d56Sopenharmony_ci           signed overflow.  (See issue 13496.) */
1757db96d56Sopenharmony_ci        mid = ((size_t)lo + hi) / 2;
1767db96d56Sopenharmony_ci        litem = PySequence_GetItem(list, mid);
1777db96d56Sopenharmony_ci        if (litem == NULL)
1787db96d56Sopenharmony_ci            return -1;
1797db96d56Sopenharmony_ci        if (key != Py_None) {
1807db96d56Sopenharmony_ci            PyObject *newitem = PyObject_CallOneArg(key, litem);
1817db96d56Sopenharmony_ci            if (newitem == NULL) {
1827db96d56Sopenharmony_ci                Py_DECREF(litem);
1837db96d56Sopenharmony_ci                return -1;
1847db96d56Sopenharmony_ci            }
1857db96d56Sopenharmony_ci            Py_SETREF(litem, newitem);
1867db96d56Sopenharmony_ci        }
1877db96d56Sopenharmony_ci        res = PyObject_RichCompareBool(litem, item, Py_LT);
1887db96d56Sopenharmony_ci        Py_DECREF(litem);
1897db96d56Sopenharmony_ci        if (res < 0)
1907db96d56Sopenharmony_ci            return -1;
1917db96d56Sopenharmony_ci        if (res)
1927db96d56Sopenharmony_ci            lo = mid + 1;
1937db96d56Sopenharmony_ci        else
1947db96d56Sopenharmony_ci            hi = mid;
1957db96d56Sopenharmony_ci    }
1967db96d56Sopenharmony_ci    return lo;
1977db96d56Sopenharmony_ci}
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci
2007db96d56Sopenharmony_ci/*[clinic input]
2017db96d56Sopenharmony_ci_bisect.bisect_left -> Py_ssize_t
2027db96d56Sopenharmony_ci
2037db96d56Sopenharmony_ci    a: object
2047db96d56Sopenharmony_ci    x: object
2057db96d56Sopenharmony_ci    lo: Py_ssize_t = 0
2067db96d56Sopenharmony_ci    hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
2077db96d56Sopenharmony_ci    *
2087db96d56Sopenharmony_ci    key: object = None
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ciReturn the index where to insert item x in list a, assuming a is sorted.
2117db96d56Sopenharmony_ci
2127db96d56Sopenharmony_ciThe return value i is such that all e in a[:i] have e < x, and all e in
2137db96d56Sopenharmony_cia[i:] have e >= x.  So if x already appears in the list, a.insert(i, x) will
2147db96d56Sopenharmony_ciinsert just before the leftmost x already there.
2157db96d56Sopenharmony_ci
2167db96d56Sopenharmony_ciOptional args lo (default 0) and hi (default len(a)) bound the
2177db96d56Sopenharmony_cislice of a to be searched.
2187db96d56Sopenharmony_ci[clinic start generated code]*/
2197db96d56Sopenharmony_ci
2207db96d56Sopenharmony_cistatic Py_ssize_t
2217db96d56Sopenharmony_ci_bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
2227db96d56Sopenharmony_ci                         Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
2237db96d56Sopenharmony_ci/*[clinic end generated code: output=70749d6e5cae9284 input=90dd35b50ceb05e3]*/
2247db96d56Sopenharmony_ci{
2257db96d56Sopenharmony_ci    return internal_bisect_left(a, x, lo, hi, key);
2267db96d56Sopenharmony_ci}
2277db96d56Sopenharmony_ci
2287db96d56Sopenharmony_ci
2297db96d56Sopenharmony_ci/*[clinic input]
2307db96d56Sopenharmony_ci_bisect.insort_left
2317db96d56Sopenharmony_ci
2327db96d56Sopenharmony_ci    a: object
2337db96d56Sopenharmony_ci    x: object
2347db96d56Sopenharmony_ci    lo: Py_ssize_t = 0
2357db96d56Sopenharmony_ci    hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
2367db96d56Sopenharmony_ci    *
2377db96d56Sopenharmony_ci    key: object = None
2387db96d56Sopenharmony_ci
2397db96d56Sopenharmony_ciInsert item x in list a, and keep it sorted assuming a is sorted.
2407db96d56Sopenharmony_ci
2417db96d56Sopenharmony_ciIf x is already in a, insert it to the left of the leftmost x.
2427db96d56Sopenharmony_ci
2437db96d56Sopenharmony_ciOptional args lo (default 0) and hi (default len(a)) bound the
2447db96d56Sopenharmony_cislice of a to be searched.
2457db96d56Sopenharmony_ci[clinic start generated code]*/
2467db96d56Sopenharmony_ci
2477db96d56Sopenharmony_cistatic PyObject *
2487db96d56Sopenharmony_ci_bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
2497db96d56Sopenharmony_ci                         Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
2507db96d56Sopenharmony_ci/*[clinic end generated code: output=b1d33e5e7ffff11e input=3ab65d8784f585b1]*/
2517db96d56Sopenharmony_ci{
2527db96d56Sopenharmony_ci    PyObject *result, *key_x;
2537db96d56Sopenharmony_ci    Py_ssize_t index;
2547db96d56Sopenharmony_ci
2557db96d56Sopenharmony_ci    if (key == Py_None) {
2567db96d56Sopenharmony_ci        index = internal_bisect_left(a, x, lo, hi, key);
2577db96d56Sopenharmony_ci    } else {
2587db96d56Sopenharmony_ci        key_x = PyObject_CallOneArg(key, x);
2597db96d56Sopenharmony_ci        if (key_x == NULL) {
2607db96d56Sopenharmony_ci            return NULL;
2617db96d56Sopenharmony_ci        }
2627db96d56Sopenharmony_ci        index = internal_bisect_left(a, key_x, lo, hi, key);
2637db96d56Sopenharmony_ci        Py_DECREF(key_x);
2647db96d56Sopenharmony_ci    }
2657db96d56Sopenharmony_ci    if (index < 0)
2667db96d56Sopenharmony_ci        return NULL;
2677db96d56Sopenharmony_ci    if (PyList_CheckExact(a)) {
2687db96d56Sopenharmony_ci        if (PyList_Insert(a, index, x) < 0)
2697db96d56Sopenharmony_ci            return NULL;
2707db96d56Sopenharmony_ci    } else {
2717db96d56Sopenharmony_ci        bisect_state *state = get_bisect_state(module);
2727db96d56Sopenharmony_ci        result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
2737db96d56Sopenharmony_ci        if (result == NULL)
2747db96d56Sopenharmony_ci            return NULL;
2757db96d56Sopenharmony_ci        Py_DECREF(result);
2767db96d56Sopenharmony_ci    }
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci    Py_RETURN_NONE;
2797db96d56Sopenharmony_ci}
2807db96d56Sopenharmony_ci
2817db96d56Sopenharmony_cistatic PyMethodDef bisect_methods[] = {
2827db96d56Sopenharmony_ci    _BISECT_BISECT_RIGHT_METHODDEF
2837db96d56Sopenharmony_ci    _BISECT_INSORT_RIGHT_METHODDEF
2847db96d56Sopenharmony_ci    _BISECT_BISECT_LEFT_METHODDEF
2857db96d56Sopenharmony_ci    _BISECT_INSORT_LEFT_METHODDEF
2867db96d56Sopenharmony_ci    {NULL, NULL} /* sentinel */
2877db96d56Sopenharmony_ci};
2887db96d56Sopenharmony_ci
2897db96d56Sopenharmony_ciPyDoc_STRVAR(module_doc,
2907db96d56Sopenharmony_ci"Bisection algorithms.\n\
2917db96d56Sopenharmony_ci\n\
2927db96d56Sopenharmony_ciThis module provides support for maintaining a list in sorted order without\n\
2937db96d56Sopenharmony_cihaving to sort the list after each insertion. For long lists of items with\n\
2947db96d56Sopenharmony_ciexpensive comparison operations, this can be an improvement over the more\n\
2957db96d56Sopenharmony_cicommon approach.\n");
2967db96d56Sopenharmony_ci
2977db96d56Sopenharmony_cistatic int
2987db96d56Sopenharmony_cibisect_clear(PyObject *module)
2997db96d56Sopenharmony_ci{
3007db96d56Sopenharmony_ci    bisect_state *state = get_bisect_state(module);
3017db96d56Sopenharmony_ci    Py_CLEAR(state->str_insert);
3027db96d56Sopenharmony_ci    return 0;
3037db96d56Sopenharmony_ci}
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_cistatic void
3067db96d56Sopenharmony_cibisect_free(void *module)
3077db96d56Sopenharmony_ci{
3087db96d56Sopenharmony_ci    bisect_clear((PyObject *)module);
3097db96d56Sopenharmony_ci}
3107db96d56Sopenharmony_ci
3117db96d56Sopenharmony_cistatic int
3127db96d56Sopenharmony_cibisect_modexec(PyObject *m)
3137db96d56Sopenharmony_ci{
3147db96d56Sopenharmony_ci    bisect_state *state = get_bisect_state(m);
3157db96d56Sopenharmony_ci    state->str_insert = PyUnicode_InternFromString("insert");
3167db96d56Sopenharmony_ci    if (state->str_insert == NULL) {
3177db96d56Sopenharmony_ci        return -1;
3187db96d56Sopenharmony_ci    }
3197db96d56Sopenharmony_ci    return 0;
3207db96d56Sopenharmony_ci}
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_cistatic PyModuleDef_Slot bisect_slots[] = {
3237db96d56Sopenharmony_ci    {Py_mod_exec, bisect_modexec},
3247db96d56Sopenharmony_ci    {0, NULL}
3257db96d56Sopenharmony_ci};
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_cistatic struct PyModuleDef _bisectmodule = {
3287db96d56Sopenharmony_ci    PyModuleDef_HEAD_INIT,
3297db96d56Sopenharmony_ci    .m_name = "_bisect",
3307db96d56Sopenharmony_ci    .m_size = sizeof(bisect_state),
3317db96d56Sopenharmony_ci    .m_doc = module_doc,
3327db96d56Sopenharmony_ci    .m_methods = bisect_methods,
3337db96d56Sopenharmony_ci    .m_slots = bisect_slots,
3347db96d56Sopenharmony_ci    .m_clear = bisect_clear,
3357db96d56Sopenharmony_ci    .m_free = bisect_free,
3367db96d56Sopenharmony_ci};
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_ciPyMODINIT_FUNC
3397db96d56Sopenharmony_ciPyInit__bisect(void)
3407db96d56Sopenharmony_ci{
3417db96d56Sopenharmony_ci    return PyModuleDef_Init(&_bisectmodule);
3427db96d56Sopenharmony_ci}
343