Skip to content

bpo-39479:Add math.lcm() function: Least Common Multiple #18547

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

Merged
merged 18 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from 13 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
9 changes: 9 additions & 0 deletions Doc/library/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,15 @@ Number-theoretic and representation functions
.. versionadded:: 3.5


.. function:: lcm(a, b)

Return the least common multiple of integers *a* and *b*. The value of
``lcm(a, b)`` is the smallest nonnegative integer that is a multiple of
both *a* and *b*. If either *a* or *b* is zero then ``lcm(a, b)`` is zero.

.. versionadded:: 3.9


.. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)

Return ``True`` if the values *a* and *b* are close to each other and
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ import attempts.
math
----

Add :func:`math.lcm`: return the least common multiple of *a* and *b*.
(Contributed by Ananthakrishnan in :issue:`39479`.)

Add :func:`math.nextafter`: return the next floating-point value after *x*
towards *y*.
(Contributed by Victor Stinner in :issue:`39288`.)
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,41 @@ def __index__(self):
with self.assertRaises(TypeError):
math.isqrt(value)

def test_lcm(self):
lcm = math.lcm
self.assertEqual(lcm(0, 0), 0)
self.assertEqual(lcm(1, 0), 0)
self.assertEqual(lcm(-1, 0), 0)
self.assertEqual(lcm(0, 1), 0)
self.assertEqual(lcm(0, -1), 0)
self.assertEqual(lcm(7, 1), 7)
self.assertEqual(lcm(7, -1), 7)
self.assertEqual(lcm(-23, 15), 345)
self.assertEqual(lcm(120, 84), 840)
self.assertEqual(lcm(84, -120), 840)
self.assertEqual(lcm(1216342683557601535506311712,
436522681849110124616458784),
16592536571065866494401400422922201534178938447014944)
x = 43461045657039990237
y = 10645022458251153277

for c in (652560,
57655923087165495981):
a = x * c
b = y * c
d = x * y * c
self.assertEqual(lcm(a, b), d)
self.assertEqual(lcm(b, a), d)
self.assertEqual(lcm(-a, b), d)
self.assertEqual(lcm(b, -a), d)
self.assertEqual(lcm(a, -b), d)
self.assertEqual(lcm(-b, a), d)
self.assertEqual(lcm(-a, -b), d)
self.assertEqual(lcm(-b, -a), d)
self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840)
self.assertRaises(TypeError, lcm, 120.0, 84)
self.assertRaises(TypeError, lcm, 120, 84.0)

def testLdexp(self):
self.assertRaises(TypeError, math.ldexp)
self.ftest('ldexp(0,1)', math.ldexp(0,1), 0)
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ PS: In the standard Python distribution, this file is encoded in UTF-8
and the list is in rough alphabetical order by last names.

Aahz
Ananthakrishnan
Edison Abahurire
Michael Abbott
Rajiv Abraham
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`math.lcm` function: least common multiple.
32 changes: 31 additions & 1 deletion Modules/clinic/mathmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2017,6 +2017,59 @@ math_factorial(PyObject *module, PyObject *arg)
}


/*[clinic input]
math.lcm
x as a: object
y as b: object
/
least common multiple of x and y
[clinic start generated code]*/

static PyObject *
math_lcm_impl(PyObject *module, PyObject *a, PyObject *b)
/*[clinic end generated code: output=6f83fb6d671074ba input=bd41b785dc2a4ff1]*/
{
PyObject *g, *m, *f, *ab;

a = PyNumber_Index(a);
if (a == NULL) {
return NULL;
}
b = PyNumber_Index(b);
if (b == NULL) {
Py_DECREF(a);
return NULL;
}
if (_PyLong_Sign(a) == 0 || _PyLong_Sign(b) == 0) {
Py_DECREF(a);
Py_DECREF(b);
return PyLong_FromLong(0);
}
g = _PyLong_GCD(a, b);
if (g == NULL) {
Py_DECREF(a);
Py_DECREF(b);
return NULL;
}
f = PyNumber_FloorDivide(a, g);
Py_DECREF(g);
Py_DECREF(a);
if (f == NULL) {
Py_DECREF(b);
return NULL;
}
m = PyNumber_Multiply(f, b);
Py_DECREF(f);
Py_DECREF(b);
if (m == NULL) {
return NULL;
}
ab = PyNumber_Absolute(m);
Py_DECREF(m);
return ab;
}


/*[clinic input]
math.trunc

Expand Down Expand Up @@ -3362,6 +3415,7 @@ static PyMethodDef math_methods[] = {
MATH_ISINF_METHODDEF
MATH_ISNAN_METHODDEF
MATH_ISQRT_METHODDEF
MATH_LCM_METHODDEF
MATH_LDEXP_METHODDEF
{"lgamma", math_lgamma, METH_O, math_lgamma_doc},
MATH_LOG_METHODDEF
Expand Down