From f2ee21d858bc03dd801b97afe60ee2ea289e2fe9 Mon Sep 17 00:00:00 2001 From: ananthan-123 Date: Wed, 19 Feb 2020 23:51:37 +0530 Subject: bpo-39479:Add math.lcm() function: Least Common Multiple (#18547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update math.rst * Update math.rst * updated whats new * Update test_math.py * Update mathmodule.c * Update mathmodule.c.h * Update ACKS * 📜🤖 Added by blurb_it. * Update 3.9.rst * Update 2020-02-18-12-37-16.bpo-39479.j3UcCq.rst * Update math.rst * Update 2020-02-18-12-37-16.bpo-39479.j3UcCq.rst * Update test_math.py * Update ACKS * Update mathmodule.c.h * Update mathmodule.c * Update mathmodule.c.h * Update mathmodule.c.h Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Doc/library/math.rst | 9 ++++ Doc/whatsnew/3.9.rst | 3 ++ Lib/test/test_math.py | 35 ++++++++++++++ Misc/ACKS | 1 + .../2020-02-18-12-37-16.bpo-39479.j3UcCq.rst | 1 + Modules/clinic/mathmodule.c.h | 32 ++++++++++++- Modules/mathmodule.c | 54 ++++++++++++++++++++++ 7 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2020-02-18-12-37-16.bpo-39479.j3UcCq.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index c4c1800..d8ac352 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -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 diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 66eb9e7..161675d 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -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`.) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index b3301f6..ad8273d 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -974,6 +974,41 @@ class MathTests(unittest.TestCase): 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) diff --git a/Misc/ACKS b/Misc/ACKS index e8ce303..b11ee9c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -45,6 +45,7 @@ Rose Ames A. Amoroso Mark Anacker Shashwat Anand +Ananthakrishnan Anders Andersen Tycho Andersen John Anderson diff --git a/Misc/NEWS.d/next/Library/2020-02-18-12-37-16.bpo-39479.j3UcCq.rst b/Misc/NEWS.d/next/Library/2020-02-18-12-37-16.bpo-39479.j3UcCq.rst new file mode 100644 index 0000000..6f16623 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-02-18-12-37-16.bpo-39479.j3UcCq.rst @@ -0,0 +1 @@ +Add :func:`math.lcm` function: least common multiple. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index f95d291..df45a1a 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -85,6 +85,36 @@ PyDoc_STRVAR(math_factorial__doc__, #define MATH_FACTORIAL_METHODDEF \ {"factorial", (PyCFunction)math_factorial, METH_O, math_factorial__doc__}, +PyDoc_STRVAR(math_lcm__doc__, +"lcm($module, x, y, /)\n" +"--\n" +"\n" +"least common multiple of x and y"); + +#define MATH_LCM_METHODDEF \ + {"lcm", (PyCFunction)(void(*)(void))math_lcm, METH_FASTCALL, math_lcm__doc__}, + +static PyObject * +math_lcm_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("lcm", nargs, 2, 2)) { + goto exit; + } + a = args[0]; + b = args[1]; + return_value = math_lcm_impl(module, a, b); + +exit: + return return_value; +} + PyDoc_STRVAR(math_trunc__doc__, "trunc($module, x, /)\n" "--\n" @@ -895,4 +925,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=9b51d215dbcac060 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f8daa185c043a7b7 input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 309f229..f74b7e1 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2018,6 +2018,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=efb3d7b7334b7118]*/ +{ + 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 x: object @@ -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 -- cgit v0.12