Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Optimize :func:`math.integer.lcm` for multiple arguments.
72 changes: 46 additions & 26 deletions Modules/mathintegermodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,41 +114,61 @@ math_integer_lcm_impl(PyObject *module, PyObject * const *args,
Py_ssize_t args_length)
/*[clinic end generated code: output=3e88889b866ccc28 input=261bddc85a136bdf]*/
{
PyObject *res, *x;
Py_ssize_t i;

if (args_length == 0) {
return PyLong_FromLong(1);
}
res = PyNumber_Index(args[0]);
if (res == NULL) {
return NULL;
}
if (args_length == 1) {
Py_SETREF(res, PyNumber_Absolute(res));
return res;
}

PyObject *zero = _PyLong_GetZero(); // borrowed ref
for (i = 1; i < args_length; i++) {
x = PyNumber_Index(args[i]);
if (x == NULL) {
Py_DECREF(res);
return NULL;
/* Combine intermediate results in size-balanced order: a new value
is merged with stacked values while it has at least 3/4 as many
digits, so the sizes of stack entries grow at least 4/3 times
per entry. Small arguments are thus combined with each other
before touching a much larger partial result. The stack depth
is bounded by log(PY_SSIZE_T_MAX)/log(4/3) plus one possible
zero entry on top: 153 on 64-bit and 76 on 32-bit platforms. */
PyObject *stack[24 * sizeof(Py_ssize_t)];
Py_ssize_t sizes[24 * sizeof(Py_ssize_t)];
PyObject *res;
int top = 0;
for (Py_ssize_t i = 0; ; i++) {
res = PyNumber_Index(args[i]);
if (res == NULL) {
goto error;
}
if (res == zero) {
/* Fast path: just check arguments.
It is okay to use identity comparison here. */
Py_DECREF(x);
continue;
Py_ssize_t dres = _PyLong_DigitCount((PyLongObject *)res);
while (top > 0 && dres >= sizes[top-1] - sizes[top-1] / 4) {
top--;
Py_SETREF(res, long_lcm(res, stack[top]));
Py_DECREF(stack[top]);
if (res == NULL) {
goto error;
}
dres = _PyLong_DigitCount((PyLongObject *)res);
}
Py_SETREF(res, long_lcm(res, x));
Py_DECREF(x);
if (i + 1 >= args_length) {
break;
}
assert(top < (int)Py_ARRAY_LENGTH(stack));
sizes[top] = dres;
stack[top++] = res;
}
while (top > 0) {
top--;
Py_SETREF(res, long_lcm(res, stack[top]));
Py_DECREF(stack[top]);
if (res == NULL) {
return NULL;
goto error;
}
}
if (args_length == 1) {
Py_SETREF(res, PyNumber_Absolute(res));
}
return res;

error:
while (top > 0) {
top--;
Py_DECREF(stack[top]);
}
return NULL;
}


Expand Down
Loading