Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-28754: AC on bisect. #177

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Lib/bisect.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def insort_right(a, x, lo=0, hi=None):

if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
if hi is None or hi == -1:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
Expand All @@ -32,7 +32,7 @@ def bisect_right(a, x, lo=0, hi=None):

if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
if hi is None or hi == -1:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
Expand All @@ -51,7 +51,7 @@ def insort_left(a, x, lo=0, hi=None):

if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
if hi is None or hi == -1:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
Expand All @@ -73,7 +73,7 @@ def bisect_left(a, x, lo=0, hi=None):

if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
if hi is None or hi == -1:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_bisect.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ def test_optionalSlicing(self):
self.assertTrue(data[ip-1] <= elem)
self.assertEqual(ip, max(lo, min(hi, expected)))

def test_hi_backcompatbility(self):
for func, data, elem, expected in self.precomputedCases:
self.assertEqual(func(data, elem, 0, -1),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't check that passing -1 and None is the same as not specifying the hi argument. Compare results with func(data, elem).

func(data, elem, 0, None))

def test_backcompatibility(self):
self.assertEqual(self.module.bisect, self.module.bisect_right)

Expand Down
231 changes: 139 additions & 92 deletions Modules/_bisectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
#define PY_SSIZE_T_CLEAN
#include "Python.h"

static int
ssize_t_converter(PyObject *obj, void *ptr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssize_t_converter is not used for parsing arguments. It is used only in optional_ssize_t_converter. Wouldn't be better to inline it?

{
Py_ssize_t val;

val = PyLong_AsSsize_t(obj);
if (val == -1 && PyErr_Occurred()) {
return 0;
}
*(Py_ssize_t *)ptr = val;
return 1;
}

static int
optional_ssize_t_converter(PyObject *obj, void *ptr)
{
if (obj != Py_None) {
return ssize_t_converter(obj, ptr);
}
else {
*(Py_ssize_t *)ptr = -1;
return 1;
}
}


#include "clinic/_bisectmodule.c.h"

/*[clinic input]
module bisect
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d0e256c42a9e4c13]*/

_Py_IDENTIFIER(insert);

static Py_ssize_t
Expand Down Expand Up @@ -44,56 +77,79 @@ internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t
return lo;
}

/*[python input]
class hi_parameter_converter(CConverter):
type = 'Py_ssize_t'
converter = 'optional_ssize_t_converter'

[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=53c711121eb30d3f]*/


/*[clinic input]
bisect.bisect_right
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the return converter for simplifying the code.

bisect.bisect_right -> Py_ssize_t

The body of bisect_bisect_right_impl() would be just:

return internal_bisect_right(a, x, lo, hi);


a: object
x: object
lo: Py_ssize_t(c_default='0') = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c_default='0' is redundant.

hi: hi_parameter(c_default='-1') = None

Return the index where to insert item x in list a, assuming a is sorted.

The return value i is such that all e in a[:i] have e <= x, and all e in
a[i:] have e > x. So if x already appears in the list, i points just
beyond the rightmost x already there.

Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
[clinic start generated code]*/

static PyObject *
bisect_right(PyObject *self, PyObject *args, PyObject *kw)
bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi)
/*[clinic end generated code: output=a2fb3e3261e46954 input=94e1e505e7f1ced8]*/
{
PyObject *list, *item;
Py_ssize_t lo = 0;
Py_ssize_t hi = -1;
Py_ssize_t index;
static char *keywords[] = {"a", "x", "lo", "hi", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_right",
keywords, &list, &item, &lo, &hi))
return NULL;
index = internal_bisect_right(list, item, lo, hi);
index = internal_bisect_right(a, x, lo, hi);
if (index < 0)
return NULL;
return PyLong_FromSsize_t(index);
}

PyDoc_STRVAR(bisect_right_doc,
"bisect_right(a, x[, lo[, hi]]) -> index\n\
\n\
Return the index where to insert item x in list a, assuming a is sorted.\n\
\n\
The return value i is such that all e in a[:i] have e <= x, and all e in\n\
a[i:] have e > x. So if x already appears in the list, i points just\n\
beyond the rightmost x already there\n\
\n\
Optional args lo (default 0) and hi (default len(a)) bound the\n\
slice of a to be searched.\n");
/*[clinic input]
bisect.insort_right

a: object
x: object
lo: Py_ssize_t(c_default='0') = 0
hi: hi_parameter(c_default='-1') = None

Insert item x in list a, and keep it sorted assuming a is sorted.

If x is already in a, insert it to the right of the rightmost x.

Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.

[clinic start generated code]*/

static PyObject *
insort_right(PyObject *self, PyObject *args, PyObject *kw)
bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi)
/*[clinic end generated code: output=6e0b99c731a11c1a input=6790b22da4643197]*/
{
PyObject *list, *item, *result;
Py_ssize_t lo = 0;
Py_ssize_t hi = -1;
PyObject *result;
Py_ssize_t index;
static char *keywords[] = {"a", "x", "lo", "hi", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_right",
keywords, &list, &item, &lo, &hi))
return NULL;
index = internal_bisect_right(list, item, lo, hi);
index = internal_bisect_right(a, x, lo, hi);
if (index < 0)
return NULL;
if (PyList_CheckExact(list)) {
if (PyList_Insert(list, index, item) < 0)
if (PyList_CheckExact(a)) {
if (PyList_Insert(a, index, x) < 0)
return NULL;
} else {
result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
result = _PyObject_CallMethodId(a, &PyId_insert, "nO", index, x);
if (result == NULL)
return NULL;
Py_DECREF(result);
Expand All @@ -102,16 +158,6 @@ insort_right(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}

PyDoc_STRVAR(insort_right_doc,
"insort_right(a, x[, lo[, hi]])\n\
\n\
Insert item x in list a, and keep it sorted assuming a is sorted.\n\
\n\
If x is already in a, insert it to the right of the rightmost x.\n\
\n\
Optional args lo (default 0) and hi (default len(a)) bound the\n\
slice of a to be searched.\n");

static Py_ssize_t
internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi)
{
Expand Down Expand Up @@ -148,56 +194,71 @@ internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t h
return lo;
}

/*[clinic input]
bisect.bisect_left

a: object
x: object
lo: Py_ssize_t(c_default='0') = 0
hi: hi_parameter(c_default='-1') = None

Return the index where to insert item x in list a, assuming a is sorted.

The return value i is such that all e in a[:i] have e < x, and all e in
a[i:] have e >= x. So if x already appears in the list, i points just
before the leftmost x already there.

Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.

[clinic start generated code]*/

static PyObject *
bisect_left(PyObject *self, PyObject *args, PyObject *kw)
bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi)
/*[clinic end generated code: output=27a0228c4a0a5fa2 input=fc1e8f6081ccfd7c]*/
{
PyObject *list, *item;
Py_ssize_t lo = 0;
Py_ssize_t hi = -1;
Py_ssize_t index;
static char *keywords[] = {"a", "x", "lo", "hi", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:bisect_left",
keywords, &list, &item, &lo, &hi))
return NULL;
index = internal_bisect_left(list, item, lo, hi);
index = internal_bisect_left(a, x, lo, hi);
if (index < 0)
return NULL;
return PyLong_FromSsize_t(index);
}

PyDoc_STRVAR(bisect_left_doc,
"bisect_left(a, x[, lo[, hi]]) -> index\n\
\n\
Return the index where to insert item x in list a, assuming a is sorted.\n\
\n\
The return value i is such that all e in a[:i] have e < x, and all e in\n\
a[i:] have e >= x. So if x already appears in the list, i points just\n\
before the leftmost x already there.\n\
\n\
Optional args lo (default 0) and hi (default len(a)) bound the\n\
slice of a to be searched.\n");
/*[clinic input]
bisect.insort_left

a: object
x: object
lo: Py_ssize_t(c_default='0') = 0
hi: hi_parameter(c_default='-1') = None

Insert item x in list a, and keep it sorted assuming a is sorted.

If x is already in a, insert it to the left of the leftmost x.

Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.

[clinic start generated code]*/

static PyObject *
insort_left(PyObject *self, PyObject *args, PyObject *kw)
bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi)
/*[clinic end generated code: output=aa0228af6970ec52 input=582456c4727c5716]*/
{
PyObject *list, *item, *result;
Py_ssize_t lo = 0;
Py_ssize_t hi = -1;
PyObject *result;
Py_ssize_t index;
static char *keywords[] = {"a", "x", "lo", "hi", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|nn:insort_left",
keywords, &list, &item, &lo, &hi))
return NULL;
index = internal_bisect_left(list, item, lo, hi);
index = internal_bisect_left(a, x, lo, hi);
if (index < 0)
return NULL;
if (PyList_CheckExact(list)) {
if (PyList_Insert(list, index, item) < 0)
if (PyList_CheckExact(a)) {
if (PyList_Insert(a, index, x) < 0)
return NULL;
} else {
result = _PyObject_CallMethodId(list, &PyId_insert, "nO", index, item);
result = _PyObject_CallMethodId(a, &PyId_insert, "nO", index, x);
if (result == NULL)
return NULL;
Py_DECREF(result);
Expand All @@ -206,25 +267,11 @@ insort_left(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}

PyDoc_STRVAR(insort_left_doc,
"insort_left(a, x[, lo[, hi]])\n\
\n\
Insert item x in list a, and keep it sorted assuming a is sorted.\n\
\n\
If x is already in a, insert it to the left of the leftmost x.\n\
\n\
Optional args lo (default 0) and hi (default len(a)) bound the\n\
slice of a to be searched.\n");

static PyMethodDef bisect_methods[] = {
{"bisect_right", (PyCFunction)bisect_right,
METH_VARARGS|METH_KEYWORDS, bisect_right_doc},
{"insort_right", (PyCFunction)insort_right,
METH_VARARGS|METH_KEYWORDS, insort_right_doc},
{"bisect_left", (PyCFunction)bisect_left,
METH_VARARGS|METH_KEYWORDS, bisect_left_doc},
{"insort_left", (PyCFunction)insort_left,
METH_VARARGS|METH_KEYWORDS, insort_left_doc},
BISECT_BISECT_RIGHT_METHODDEF
BISECT_INSORT_RIGHT_METHODDEF
BISECT_BISECT_LEFT_METHODDEF
BISECT_INSORT_LEFT_METHODDEF
{NULL, NULL} /* sentinel */
};

Expand Down
Loading