From 9e0017e86ed28ccc2f855807f387a1e451260d85 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 24 Dec 2024 14:20:16 -0500 Subject: [PATCH] Add Int:factorial() and n:choose(k) --- docs/integers.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++ environment.c | 2 ++ stdlib/integers.c | 33 +++++++++++++++++++++++++++++ stdlib/integers.h | 2 ++ 4 files changed, 91 insertions(+) diff --git a/docs/integers.md b/docs/integers.md index fe1fbc0..b69ed2b 100644 --- a/docs/integers.md +++ b/docs/integers.md @@ -122,6 +122,60 @@ Each integer type has its own version of the following functions. Functions can be called either on the type itself: `Int.sqrt(x)` or as a method call: `x:sqrt()`. Method call syntax is preferred. +### `choose` + +**Description:** +Computes the binomial coefficient of the given numbers (the equivalent of `n` +choose `k` in combinatorics). This is equal to `n:factorial()/(k:factorial() * +(n-k):factorial())`. + +**Signature:** +```tomo +func choose(n: Int, k: Int -> Int) +``` + +**Parameters:** + +- `n`: The number of things to choose from. +- `k`: The number of things to be chosen. + +**Returns:** +The binomial coefficient, equivalent to the number of ways to uniquely choose +`k` objects from among `n` objects, ignoring order. + +**Example:** +```tomo +>> 4:choose(2) += 6 +``` + +--- + +### `factorial` + +**Description:** +Computes the factorial of an integer. + +**Signature:** +```tomo +func factorial(n: Int -> Text) +``` + +**Parameters:** + +- `n`: The integer to compute the factorial of. + +**Returns:** +The factorial of the given integer. + +**Example:** +```tomo +>> 10:factorial() += 3628800 +``` + +--- + ### `format` **Description:** diff --git a/environment.c b/environment.c index 8c207d1..bda4c0d 100644 --- a/environment.c +++ b/environment.c @@ -119,8 +119,10 @@ env_t *new_compilation_unit(CORD libname) {"bit_and", "Int$bit_and", "func(x,y:Int -> Int)"}, {"bit_or", "Int$bit_or", "func(x,y:Int -> Int)"}, {"bit_xor", "Int$bit_xor", "func(x,y:Int -> Int)"}, + {"choose", "Int$choose", "func(x,y:Int -> Int)"}, {"clamped", "Int$clamped", "func(x,low,high:Int -> Int)"}, {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, + {"factorial", "Int$factorial", "func(x:Int -> Int)"}, {"format", "Int$format", "func(i:Int, digits=0 -> Text)"}, {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, {"parse", "Int$parse", "func(text:Text -> Int?)"}, diff --git a/stdlib/integers.c b/stdlib/integers.c index 33e967c..e534437 100644 --- a/stdlib/integers.c +++ b/stdlib/integers.c @@ -382,6 +382,39 @@ public Int_t Int$prev_prime(Int_t x) return Int$from_mpz(p); } +public Int_t Int$choose(Int_t n, Int_t k) +{ + if unlikely (Int$compare_value(n, I_small(0)) < 0) + fail("Negative inputs are not supported for choose()"); + + mpz_t ret; + mpz_init(ret); + + int64_t k_i64 = Int_to_Int64(k, false); + if unlikely (k_i64 < 0) + fail("Negative inputs are not supported for choose()"); + + if likely (n.small & 1) { + mpz_bin_uiui(ret, (unsigned long)(n.small >> 2), (unsigned long)k_i64); + } else { + mpz_t n_mpz; + mpz_init_set_int(n_mpz, n); + mpz_bin_ui(ret, n_mpz, (unsigned long)k_i64); + } + return Int$from_mpz(ret); +} + +public Int_t Int$factorial(Int_t n) +{ + mpz_t ret; + mpz_init(ret); + int64_t n_i64 = Int_to_Int64(n, false); + if unlikely (n_i64 < 0) + fail("Factorials are not defined for negative numbers"); + mpz_fac_ui(ret, (unsigned long)n_i64); + return Int$from_mpz(ret); +} + static bool Int$is_none(const void *i, const TypeInfo_t*) { return ((Int_t*)i)->small == 0; diff --git a/stdlib/integers.h b/stdlib/integers.h index 2f6b512..033a687 100644 --- a/stdlib/integers.h +++ b/stdlib/integers.h @@ -144,6 +144,8 @@ Int_t Int$slow_negated(Int_t x); bool Int$is_prime(Int_t x, Int_t reps); Int_t Int$next_prime(Int_t x); Int_t Int$prev_prime(Int_t x); +Int_t Int$choose(Int_t n, Int_t k); +Int_t Int$factorial(Int_t n); extern const TypeInfo_t Int$info;