From d1a8d1a79b375cd7cb3188b943060c8a7f5b826e Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 2 Jun 2023 22:55:47 -0400 Subject: [PATCH] Fix GCD; add LCM (#396) --- stdlib/math.codon | 67 ++++++++++++++++++-------- test/stdlib/math_test.codon | 93 +++++++++++++++++++++++++++---------- 2 files changed, 116 insertions(+), 44 deletions(-) diff --git a/stdlib/math.codon b/stdlib/math.codon index 9019d7f5..8c2c621b 100644 --- a/stdlib/math.codon +++ b/stdlib/math.codon @@ -516,18 +516,60 @@ def remainder(x: float, y: float) -> float: """ return _check2(x, y, _C.remainder(x, y)) -def gcd(a: float, b: float) -> float: - """ - gcd(float, float) -> float - - returns greatest common divisor of x and y. - """ +def _gcd2(a, b): a = abs(a) b = abs(b) while a: a, b = b % a, a return b +def gcd(*args): + """ + gcd(*args) + + returns greatest common divisor of arguments. + """ + if staticlen(args) == 0: + return 0 + + res = args[0] + T = type(res) + + if staticlen(args) == 1: + return abs(res) + + for i in range(1, len(args)): + if res == T(1): + break + res = _gcd2(res, args[i]) + + return res + +def lcm(*args): + """ + lcm(*args) + + returns least common multiple of arguments. + """ + def lcm2(a, b): + return abs((a // _gcd2(a, b)) * b) + + if staticlen(args) == 0: + return 1 + + res = args[0] + T = type(res) + + if staticlen(args) == 1: + return abs(res) + + for i in range(1, len(args)): + if not res: + break + res = lcm2(res, args[i]) + + return res + @pure def frexp(x: float) -> Tuple[float, int]: """ @@ -1195,19 +1237,6 @@ def remainder(x: float32, y: float32) -> float32: """ return _C.remainderf(x, y) -@overload -def gcd(a: float32, b: float32) -> float32: - """ - gcd(float32, float32) -> float32 - - returns greatest common divisor of x and y. - """ - a = abs(a) - b = abs(b) - while a: - a, b = b % a, a - return b - @overload @pure def frexp(x: float32) -> Tuple[float32, int]: diff --git a/test/stdlib/math_test.codon b/test/stdlib/math_test.codon index 056d978f..b4befc03 100644 --- a/test/stdlib/math_test.codon +++ b/test/stdlib/math_test.codon @@ -411,16 +411,73 @@ def test_remainder(): @test def test_gcd(): - assert math.gcd(0.0, 0.0) == 0 - assert math.gcd(1.0, 0.0) == 1 - assert math.gcd(-1.0, 0.0) == 1 - assert math.gcd(0.0, -1.0) == 1 - assert math.gcd(0.0, 1.0) == 1 - assert math.gcd(7.0, 1.0) == 1 - assert math.gcd(7.0, -1.0) == 1 - assert math.gcd(-23.0, 15.0) == 1 - assert math.gcd(120.0, 84.0) == 12 - assert math.gcd(84.0, -120.0) == 12 + assert math.gcd(0, 0) == 0 + assert math.gcd(1, 0) == 1 + assert math.gcd(-1, 0) == 1 + assert math.gcd(0, 1) == 1 + assert math.gcd(0, -1) == 1 + assert math.gcd(7, 1) == 1 + assert math.gcd(7, -1) == 1 + assert math.gcd(-23, 15) == 1 + assert math.gcd(120, 84) == 12 + assert math.gcd(84, -120) == 12 + assert math.gcd(Int[128]('1216342683557601535506311712'), Int[128]('436522681849110124616458784')) == Int[128](32) + + x = 43461 + y = 1064 + for c in (652560, 576559230): + a = x * c + b = y * c + assert math.gcd(a, b) == c + assert math.gcd(b, a) == c + assert math.gcd(-a, b) == c + assert math.gcd(b, -a) == c + assert math.gcd(a, -b) == c + assert math.gcd(-b, a) == c + assert math.gcd(-a, -b) == c + assert math.gcd(-b, -a) == c + + assert math.gcd() == 0 + assert math.gcd(120) == 120 + assert math.gcd(-120) == 120 + assert math.gcd(120, 84, 102) == 6 + assert math.gcd(120, 1, 84) == 1 + + +@test +def test_lcm(): + assert math.lcm(0, 0) == 0 + assert math.lcm(1, 0) == 0 + assert math.lcm(-1, 0) == 0 + assert math.lcm(0, 1) == 0 + assert math.lcm(0, -1) == 0 + assert math.lcm(7, 1) == 7 + assert math.lcm(7, -1) == 7 + assert math.lcm(-23, 15) == 345 + assert math.lcm(120, 84) == 840 + assert math.lcm(84, -120) == 840 + #assert math.lcm(1216342683557601535506311712, 436522681849110124616458784) == 16592536571065866494401400422922201534178938447014944 + + x = 4346103 + y = 1064501 + for c in (8171, 58657): + a = x * c + b = y * c + d = x * y * c + assert math.lcm(a, b) == d + assert math.lcm(b, a) == d + assert math.lcm(-a, b) == d + assert math.lcm(b, -a) == d + assert math.lcm(a, -b) == d + assert math.lcm(-b, a) == d + assert math.lcm(-a, -b) == d + assert math.lcm(-b, -a) == d + + assert math.lcm() == 1 + assert math.lcm(120) == 120 + assert math.lcm(-120) == 120 + assert math.lcm(120, 84, 102) == 14280 + assert math.lcm(120, 0, 84) == 0 @test @@ -714,6 +771,7 @@ test_gamma() test_lgamma() test_remainder() test_gcd() +test_lcm() test_frexp() test_modf() test_isclose() @@ -1131,20 +1189,6 @@ def test_float32_remainder(): assert math.isnan(math.remainder(NAN32, 1.0f32)) == True -@test -def test_float32_gcd(): - assert math.gcd(0.0f32, 0.0f32) == 0.0f32 - assert math.gcd(1.0f32, 0.0f32) == 1.0f32 - assert math.gcd(-1.0f32, 0.0f32) == 1.0f32 - assert math.gcd(0.0f32, -1.0f32) == 1.0f32 - assert math.gcd(0.0f32, 1.0f32) == 1.0f32 - assert math.gcd(7.0f32, 1.0f32) == 1.0f32 - assert math.gcd(7.0f32, -1.0f32) == 1.0f32 - assert math.gcd(-23.0f32, 15.0f32) == 1.0f32 - assert math.gcd(120.0f32, 84.0f32) == 12.0f32 - assert math.gcd(84.0f32, -120.0f32) == 12.0f32 - - @test def test_float32_frexp(): assert math.frexp(-2.0f32) == (-0.5f32, 2) @@ -1222,7 +1266,6 @@ test_float32_erfc() test_float32_gamma() test_float32_lgamma() test_float32_remainder() -test_float32_gcd() test_float32_frexp() test_float32_modf() test_float32_isclose()