Skip to content

Commit 559e7f1

Browse files
bpo-39648: Expand math.gcd() and math.lcm() to handle multiple arguments. (GH-18604)
* bpo-39648: Expand math.gcd() and math.lcm() to handle multiple arguments. * Simplify fast path. * Difine lcm() without arguments returning 1. * Apply suggestions from code review Co-Authored-By: Mark Dickinson <dickinsm@gmail.com> Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
1 parent fbe2e0b commit 559e7f1

File tree

6 files changed

+171
-175
lines changed

6 files changed

+171
-175
lines changed

Doc/library/math.rst

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,23 +126,19 @@ Number-theoretic and representation functions
126126
<https://code.activestate.com/recipes/393090/>`_\.
127127

128128

129-
.. function:: gcd(a, b)
129+
.. function:: gcd(*integers)
130130

131-
Return the greatest common divisor of the integers *a* and *b*. If either
132-
*a* or *b* is nonzero, then the value of ``gcd(a, b)`` is the largest
133-
positive integer that divides both *a* and *b*. ``gcd(0, 0)`` returns
134-
``0``.
131+
Return the greatest common divisor of the specified integer arguments.
132+
If any of the arguments is nonzero, then the returned value is the largest
133+
positive integer that is a divisor af all arguments. If all arguments
134+
are zero, then the returned value is ``0``. ``gcd()`` without arguments
135+
returns ``0``.
135136

136137
.. versionadded:: 3.5
137138

138-
139-
.. function:: lcm(a, b)
140-
141-
Return the least common multiple of integers *a* and *b*. The value of
142-
``lcm(a, b)`` is the smallest nonnegative integer that is a multiple of
143-
both *a* and *b*. If either *a* or *b* is zero then ``lcm(a, b)`` is zero.
144-
145-
.. versionadded:: 3.9
139+
.. versionchanged:: 3.9
140+
Added support for an arbitrary number of arguments. Formerly, only two
141+
arguments were supported.
146142

147143

148144
.. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
@@ -210,6 +206,17 @@ Number-theoretic and representation functions
210206
.. versionadded:: 3.8
211207

212208

209+
.. function:: lcm(*integers)
210+
211+
Return the least common multiple of the specified integer arguments.
212+
If all arguments are nonzero, then the returned value is the smallest
213+
positive integer that is a multiple of all arguments. If any of the arguments
214+
is zero, then the returned value is ``0``. ``lcm()`` without arguments
215+
returns ``1``.
216+
217+
.. versionadded:: 3.9
218+
219+
213220
.. function:: ldexp(x, i)
214221

215222
Return ``x * (2**i)``. This is essentially the inverse of function

Doc/whatsnew/3.9.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,13 @@ import attempts.
216216
math
217217
----
218218

219-
Add :func:`math.lcm`: return the least common multiple of *a* and *b*.
220-
(Contributed by Ananthakrishnan in :issue:`39479`.)
219+
Expanded the :func:`math.gcd` function to handle multiple arguments.
220+
Formerly, it only supported two arguments.
221+
(Contributed by Serhiy Storchaka in :issue:`39648`.)
222+
223+
Add :func:`math.lcm`: return the least common multiple of specified arguments.
224+
(Contributed by Mark Dickinson, Ananthakrishnan and Serhiy Storchaka in
225+
:issue:`39479` and :issue:`39648`.)
221226

222227
Add :func:`math.nextafter`: return the next floating-point value after *x*
223228
towards *y*.

Lib/test/test_math.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -705,33 +705,32 @@ def testGcd(self):
705705
self.assertEqual(gcd(84, -120), 12)
706706
self.assertEqual(gcd(1216342683557601535506311712,
707707
436522681849110124616458784), 32)
708-
c = 652560
708+
709709
x = 434610456570399902378880679233098819019853229470286994367836600566
710710
y = 1064502245825115327754847244914921553977
711-
a = x * c
712-
b = y * c
713-
self.assertEqual(gcd(a, b), c)
714-
self.assertEqual(gcd(b, a), c)
715-
self.assertEqual(gcd(-a, b), c)
716-
self.assertEqual(gcd(b, -a), c)
717-
self.assertEqual(gcd(a, -b), c)
718-
self.assertEqual(gcd(-b, a), c)
719-
self.assertEqual(gcd(-a, -b), c)
720-
self.assertEqual(gcd(-b, -a), c)
721-
c = 576559230871654959816130551884856912003141446781646602790216406874
722-
a = x * c
723-
b = y * c
724-
self.assertEqual(gcd(a, b), c)
725-
self.assertEqual(gcd(b, a), c)
726-
self.assertEqual(gcd(-a, b), c)
727-
self.assertEqual(gcd(b, -a), c)
728-
self.assertEqual(gcd(a, -b), c)
729-
self.assertEqual(gcd(-b, a), c)
730-
self.assertEqual(gcd(-a, -b), c)
731-
self.assertEqual(gcd(-b, -a), c)
732-
711+
for c in (652560,
712+
576559230871654959816130551884856912003141446781646602790216406874):
713+
a = x * c
714+
b = y * c
715+
self.assertEqual(gcd(a, b), c)
716+
self.assertEqual(gcd(b, a), c)
717+
self.assertEqual(gcd(-a, b), c)
718+
self.assertEqual(gcd(b, -a), c)
719+
self.assertEqual(gcd(a, -b), c)
720+
self.assertEqual(gcd(-b, a), c)
721+
self.assertEqual(gcd(-a, -b), c)
722+
self.assertEqual(gcd(-b, -a), c)
723+
724+
self.assertEqual(gcd(), 0)
725+
self.assertEqual(gcd(120), 120)
726+
self.assertEqual(gcd(-120), 120)
727+
self.assertEqual(gcd(120, 84, 102), 6)
728+
self.assertEqual(gcd(120, 1, 84), 1)
729+
730+
self.assertRaises(TypeError, gcd, 120.0)
733731
self.assertRaises(TypeError, gcd, 120.0, 84)
734732
self.assertRaises(TypeError, gcd, 120, 84.0)
733+
self.assertRaises(TypeError, gcd, 120, 1, 84.0)
735734
self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12)
736735

737736
def testHypot(self):
@@ -989,9 +988,9 @@ def test_lcm(self):
989988
self.assertEqual(lcm(1216342683557601535506311712,
990989
436522681849110124616458784),
991990
16592536571065866494401400422922201534178938447014944)
991+
992992
x = 43461045657039990237
993993
y = 10645022458251153277
994-
995994
for c in (652560,
996995
57655923087165495981):
997996
a = x * c
@@ -1005,9 +1004,18 @@ def test_lcm(self):
10051004
self.assertEqual(lcm(-b, a), d)
10061005
self.assertEqual(lcm(-a, -b), d)
10071006
self.assertEqual(lcm(-b, -a), d)
1008-
self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840)
1007+
1008+
self.assertEqual(lcm(), 1)
1009+
self.assertEqual(lcm(120), 120)
1010+
self.assertEqual(lcm(-120), 120)
1011+
self.assertEqual(lcm(120, 84, 102), 14280)
1012+
self.assertEqual(lcm(120, 0, 84), 0)
1013+
1014+
self.assertRaises(TypeError, lcm, 120.0)
10091015
self.assertRaises(TypeError, lcm, 120.0, 84)
10101016
self.assertRaises(TypeError, lcm, 120, 84.0)
1017+
self.assertRaises(TypeError, lcm, 120, 0, 84.0)
1018+
self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840)
10111019

10121020
def testLdexp(self):
10131021
self.assertRaises(TypeError, math.ldexp)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expanded :func:`math.gcd` and :func:`math.lcm` to handle multiple arguments.

Modules/clinic/mathmodule.c.h

Lines changed: 1 addition & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)