diff -r e7eed20f2da7 Lib/fractions.py --- a/Lib/fractions.py Sun Oct 13 11:04:36 2013 +0100 +++ b/Lib/fractions.py Sun Oct 13 11:39:24 2013 +0100 @@ -6,15 +6,16 @@ from decimal import Decimal import math import numbers +import functools import operator import re import sys -__all__ = ['Fraction', 'gcd'] +__all__ = ['Fraction', 'gcd','lcm'] -def gcd(a, b): +def _gcd(a, b): """Calculate the Greatest Common Divisor of a and b. Unless b==0, the result will have the same sign as b (so that when @@ -24,6 +25,21 @@ a, b = b, a%b return a +def gcd(*args): + return functools.reduce(_gcd,args) + +def _lcm(a, b): + """Calculate the Least Common Multiple of a and b.""" + if a * b == 0: + return 0 + + return a * b // gcd(a,b) + +def lcm(*args): + return functools.reduce(_lcm,args) + + + # Constants related to the hash implementation; hash(x) is based # on the reduction of x modulo the prime _PyHASH_MODULUS. _PyHASH_MODULUS = sys.hash_info.modulus diff -r e7eed20f2da7 Lib/test/test_fractions.py --- a/Lib/test/test_fractions.py Sun Oct 13 11:04:36 2013 +0100 +++ b/Lib/test/test_fractions.py Sun Oct 13 11:39:24 2013 +0100 @@ -12,6 +12,7 @@ from pickle import dumps, loads F = fractions.Fraction gcd = fractions.gcd +lcm = fractions.lcm class DummyFloat(object): """Dummy float class for testing comparisons with Fractions""" @@ -94,6 +95,48 @@ self.assertEqual(12, gcd(120, 84)) self.assertEqual(-12, gcd(84, -120)) +class LcmTest(unittest.TestCase): + """The Least Common Multiple (LCM) of two integers (a,b) is the smallest + integer that both a and b divide into. + + Note 1: LCM(a,b) * GCD(a,b) = a * b + + Note 2: GCD(a,b) is often written as HCF(a,b) (Highest Common Factor) + """ + + def test_lcm_int(self): + """ lcm should return an int, not a float """ + self.assertTrue(isinstance(lcm(1, 2), int)) + + def test_lcm(self): + """ test the basic cases, swapping arguments as lcm is symmetric.""" + for answer,a,b in [(0,0,0), + (1,1,1), + (2,1,2), + (6,2,3), + (4,2,4), + (30,5,6), + (30,10,15)]: + self.assertEqual(answer,lcm(a,b)) + self.assertEqual(answer,lcm(b,a)) + + def test_lcm_with_gcd(self): + """ lcm(a,b) * gcd(a,b) = a*b which allows an automatic co-test """ + for a in range(20): + for b in range(20): + self.assertEqual(a*b, lcm(a, b) * gcd(a, b)) + + def test_lcm_nary(self): + """lcm can take any number of integer arguments""" + self.assertEqual(0,lcm(0,0,0)) + self.assertEqual(60,lcm(15,20,30)) + self.assertEqual(6,lcm(1,2,3)) + + def test_gcd_nary(self): + """gcd can take any number of integer arguments""" + self.assertEqual(0,gcd(0,0,0)) + self.assertEqual(2,gcd(2,4,6)) + def _components(r): return (r.numerator, r.denominator) @@ -606,7 +649,7 @@ self.assertRaises(AttributeError, setattr, r, 'a', 10) def test_main(): - run_unittest(FractionTest, GcdTest) + run_unittest(FractionTest, GcdTest,LcmTest) if __name__ == '__main__': test_main()