aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-12-24 14:20:16 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-12-24 14:20:16 -0500
commit9e0017e86ed28ccc2f855807f387a1e451260d85 (patch)
tree7fe1b0b931dc82777d8b471e95147eb1edfda184
parentf4b105456ad5f0949800d19acdb3403de26d7678 (diff)
Add Int:factorial() and n:choose(k)
-rw-r--r--docs/integers.md54
-rw-r--r--environment.c2
-rw-r--r--stdlib/integers.c33
-rw-r--r--stdlib/integers.h2
4 files changed, 91 insertions, 0 deletions
diff --git a/docs/integers.md b/docs/integers.md
index fe1fbc0c..b69ed2b9 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 8c207d15..bda4c0d4 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 33e967cb..e5344370 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 2f6b5125..033a6873 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;