diff options
| author | HarmoGlace <23212967+HarmoGlace@users.noreply.github.com> | 2023-04-19 16:18:31 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-19 16:18:31 +0200 |
| commit | 1e934def566832c2722c63d5b48df631996c6e88 (patch) | |
| tree | f2894dd81e8a8a2d00c5333ef0900d4ad1104bf9 | |
| parent | dc3017955a67e5509f6bc33cb9b4833806da4c22 (diff) | |
Add `gcd` and `lcm` calculation methods (#789)
| -rw-r--r-- | library/src/compute/calc.rs | 68 | ||||
| -rw-r--r-- | tests/typ/compute/calc.typ | 24 |
2 files changed, 92 insertions, 0 deletions
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs index 9ab18dbf..62c529de 100644 --- a/library/src/compute/calc.rs +++ b/library/src/compute/calc.rs @@ -27,6 +27,8 @@ pub fn module() -> Module { scope.define("fact", fact); scope.define("perm", perm); scope.define("binom", binom); + scope.define("gcd", gcd); + scope.define("lcm", lcm); scope.define("floor", floor); scope.define("ceil", ceil); scope.define("round", round); @@ -522,6 +524,72 @@ fn binomial(n: u64, k: u64) -> Option<i64> { i64::try_from(result).ok() } +/// Calculate the greatest common divisor of two integers. +/// +/// ## Example +/// ```example +/// #calc.gcd(7, 42) +/// ``` +/// +/// Display: Greatest Common Divisor +/// Category: calculate +/// Returns: integer +#[func] +pub fn gcd( + /// The first integer. + a: i64, + /// The second integer. + b: i64, +) -> Value { + Value::Int(calculate_gcd(a, b).into()) +} + +/// Calculates the greatest common divisor of two integers +/// It is always non-negative. +fn calculate_gcd(mut a: i64, mut b: i64) -> i64 { + while b != 0 { + let temp = b; + b = a % b; + a = temp; + } + + a.abs() +} + +/// Calculate the least common multiple of two integers. +/// +/// ## Example +/// ```example +/// #calc.lcm(96, 13) +/// ``` +/// +/// Display: Least Common Multiple +/// Category: calculate +/// Returns: integer +#[func] +pub fn lcm( + /// The first integer. + a: i64, + /// The second integer. + b: i64, +) -> Value { + calculate_lcm(a, b) + .map(Value::Int) + .ok_or("the return value is too large") + .at(args.span)? +} + +/// Calculates the least common multiple between two non-zero integers +/// Returns None if the value cannot be computed. +/// It is always non-negative. +fn calculate_lcm(a: i64, b: i64) -> Option<i64> { + if a == b { + return Some(a.abs()); + } + + a.checked_div(calculate_gcd(a, b))?.checked_mul(b).map(|v| v.abs()) +} + /// Round a number down to the nearest integer. /// /// If the number is already an integer, it is returned unchanged. diff --git a/tests/typ/compute/calc.typ b/tests/typ/compute/calc.typ index c9e23542..6af5189b 100644 --- a/tests/typ/compute/calc.typ +++ b/tests/typ/compute/calc.typ @@ -147,6 +147,30 @@ #test(calc.binom(6, 2), 15) --- +// Test the `gcd` function. +#test(calc.gcd(112, 77), 7) +#test(calc.gcd(12, 96), 12) +#test(calc.gcd(13, 9), 1) +#test(calc.gcd(13, -9), 1) +#test(calc.gcd(272557, 272557), 272557) +#test(calc.gcd(0, 0), 0) +#test(calc.gcd(7, 0), 7) + +--- +// Test the `lcm` function. +#test(calc.lcm(112, 77), 1232) +#test(calc.lcm(12, 96), 96) +#test(calc.lcm(13, 9), 117) +#test(calc.lcm(13, -9), 117) +#test(calc.lcm(272557, 272557), 272557) +#test(calc.lcm(0, 0), 0) +#test(calc.lcm(8, 0), 0) + +--- +// Error: 10-41 the return value is too large +#calc.lcm(15486487489457, 4874879896543) + +--- // Error: 10-12 expected at least one value #calc.min() |
