From 62dca4f1c53ed670c62ed8b771a845bed75d418e Mon Sep 17 00:00:00 2001 From: Koni Marti Date: Sun, 8 Dec 2024 19:30:46 +0100 Subject: [PATCH] math: add gcd and lcm Add gcd and lcm functions to calculate the greatest common divisor (gcd) and the least common multiple (lcm) to the math module. This will also work for BigInts that implements its own gcd/lcm. --- lib/std/math/bigint.c3 | 10 +++++- lib/std/math/math.c3 | 60 ++++++++++++++++++++++++++++++++- test/unit/stdlib/math/bigint.c3 | 18 +++++++++- test/unit/stdlib/math/math.c3 | 26 +++++++++++++- 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/lib/std/math/bigint.c3 b/lib/std/math/bigint.c3 index c5a181f77..14ac8212f 100644 --- a/lib/std/math/bigint.c3 +++ b/lib/std/math/bigint.c3 @@ -328,7 +328,7 @@ fn BigInt BigInt.unary_minus(&self) } -macro void BigInt.div(self, BigInt other) +macro BigInt BigInt.div(self, BigInt other) { self.div_this(other); return self; @@ -831,6 +831,14 @@ fn BigInt BigInt.gcd(&self, BigInt other) return g; } +fn BigInt BigInt.lcm(&self, BigInt other) +{ + BigInt x = self.abs(); + BigInt y = other.abs(); + BigInt g = y.mult(x); + return g.div(x.gcd(y)); +} + <* @require bits >> 5 < MAX_LEN "Required bits > maxlength" *> diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 6375cbddc..cb5a52a3b 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -1183,4 +1183,62 @@ macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, di @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` *> -macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div); \ No newline at end of file +macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div); + +<* +@require types::is_int($typeof(a)) `The input must be an integer` +@require types::is_int($typeof(b)) `The input must be an integer` +*> +macro _gcd(a, b) @private +{ + if (a == 0) return b; + if (b == 0) return a; + + var $Type = $typeof(a); + $Type r, aa, ab; + aa = abs(a); + ab = abs(b); + while (ab != 0) + { + r = aa % ab; + aa = ab; + ab = r; + } + return aa; +} + +<* +Calculate the least common multiple for the provided arguments. + +@require $vacount >= 2 `At least two arguments are required.` +*> +macro lcm(...) +{ + $typeof($vaarg[0]) result = $vaarg[0]; + $for (var $i = 1; $i < $vacount; $i++) + $if $defined(result.lcm): + result = result.lcm($vaarg[$i]); + $else + result = (abs($vaarg[$i]) * abs(result)) / (_gcd($vaarg[$i], result)); + $endif + $endfor + return result; +} + +<* +Calculate the greatest common divisor for the provided arguments. + +@require $vacount >= 2 `At least two arguments are required.` +*> +macro gcd(...) +{ + $typeof($vaarg[0]) result = $vaarg[0]; + $for (var $i = 1; $i < $vacount; $i++) + $if $defined(result.gcd): + result = result.gcd($vaarg[$i]); + $else + result = _gcd($vaarg[$i], result); + $endif + $endfor + return result; +} diff --git a/test/unit/stdlib/math/bigint.c3 b/test/unit/stdlib/math/bigint.c3 index a63414e63..0117159db 100644 --- a/test/unit/stdlib/math/bigint.c3 +++ b/test/unit/stdlib/math/bigint.c3 @@ -42,4 +42,20 @@ fn void test_init_string_radix() assert(a.equals(bigint::from_int(0o123))); a.init_string_radix("123", 16)!!; assert(a.equals(bigint::from_int(0x123))); -} \ No newline at end of file +} + +fn void test_gcd() +{ + BigInt a = bigint::from_int(15); + BigInt b = bigint::from_int(20); + assert(a.gcd(b).equals(bigint::from_int(5))); + assert(math::gcd(a,b).equals(bigint::from_int(5))); +} + +fn void test_lcm() +{ + BigInt a = bigint::from_int(11); + BigInt b = bigint::from_int(17); + assert(a.lcm(b).equals(bigint::from_int(11*17))); + assert(math::lcm(a,b).equals(bigint::from_int(11*17))); +} diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index b0b00befd..3c5cf8b75 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -194,4 +194,28 @@ fn void! test_muldiv() ichar[<4>] k = {20, 30, 40, 50}; assert(k.muldiv(20,-10) == ichar[<4>]{-40,-60,-80,-100}); -} \ No newline at end of file +} + +fn void! test_gcd() @test +{ + assert(math::gcd(20,15) == 5); + assert(math::gcd(15,20) == 5); + assert(math::gcd(-15,20) == 5); + assert(math::gcd(15,-20) == 5); + assert(math::gcd(-15,-20) == 5); + assert(math::gcd(5,15,20) == 5); + assert(math::gcd(1,2,3) == 1); + assert(math::gcd(2,4,6,8) == 2); +} + +fn void! test_lcm() @test +{ + assert(math::lcm(4,5) == 20); + assert(math::lcm(6,10) == 30); + assert(math::lcm(-8,20) == 40); + assert(math::lcm(8,-20) == 40); + assert(math::lcm(-8,-20) == 40); + assert(math::lcm(11,17) == 11*17); + assert(math::lcm(11,17,227,263) == 11*17*227*263); +} +