diff --git a/README.md b/README.md
index 716b910..ba95f0d 100644
--- a/README.md
+++ b/README.md
@@ -10,43 +10,49 @@ The library supports both targets: Erlang and JavaScript.
## Quickstart
```gleam
+import gleam/float
+import gleam/iterator
+import gleam/option.{Some}
import gleam_community/maths/arithmetics
-import gleam_community/maths/combinatorics
+import gleam_community/maths/combinatorics.{WithoutRepetitions}
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
import gleam_community/maths/predicates
-import gleam/float
-import gleam/int
+import gleeunit/should
-pub fn main() {
+pub fn example() {
// Evaluate the sine function
- elementary.sin(elementary.pi())
- // Returns Float: 0.0
+ let result = elementary.sin(elementary.pi())
+
+ // Set the relative and absolute tolerance
+ let assert Ok(absolute_tol) = elementary.power(10.0, -6.0)
+ let relative_tol = 0.0
+
+ // Check that the value is very close to 0.0
+ // That is, if 'result' is within +/- 10^(-6)
+ predicates.is_close(result, 0.0, relative_tol, absolute_tol)
+ |> should.be_true()
// Find the greatest common divisor
arithmetics.gcd(54, 24)
- // Returns Int: 6
+ |> should.equal(6)
// Find the minimum and maximum of a list
piecewise.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
- // Returns Tuple: Ok(#(3.0, 50.0))
-
- // Find the list indices of the smallest value
- piecewise.arg_minimum([10, 3, 50, 20, 3], int.compare)
- // Returns List: Ok([1, 4])
+ |> should.equal(Ok(#(3.0, 50.0)))
// Determine if a number is fractional
predicates.is_fractional(0.3333)
- // Returns Bool: True
+ |> should.equal(True)
- // Determine if 28 is a power of 3
- predicates.is_power(28, 3)
- // Returns Bool: False
-
- // Generate all k = 1 combinations of [1, 2]
- combinatorics.list_combination([1, 2], 1)
- // Returns List: Ok([[1], [2]])
+ // Generate all k = 2 combinations of [1, 2, 3]
+ let assert Ok(combinations) =
+ combinatorics.list_combination([1, 2, 3], 2, Some(WithoutRepetitions))
+ combinations
+ |> iterator.to_list()
+ |> should.equal([[1, 2], [1, 3], [2, 3]])
}
+
```
## Installation
diff --git a/src/gleam_community/maths/arithmetics.gleam b/src/gleam_community/maths/arithmetics.gleam
index a205116..dfa3c8d 100644
--- a/src/gleam_community/maths/arithmetics.gleam
+++ b/src/gleam_community/maths/arithmetics.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -58,8 +58,8 @@ import gleam_community/maths/piecewise
///
///
/// The function calculates the greatest common divisor of two integers
-/// $$x, y \in \mathbb{Z}$$. The greatest common divisor is the largest positive
-/// integer that is divisible by both $$x$$ and $$y$$.
+/// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive
+/// integer that is divisible by both \\(x\\) and \\(y\\).
///
///
/// Example:
@@ -108,15 +108,15 @@ fn do_gcd(x: Int, y: Int) -> Int {
///
///
///
-/// Given two integers, $$x$$ (dividend) and $$y$$ (divisor), the Euclidean modulo
-/// of $$x$$ by $$y$$, denoted as $$x \mod y$$, is the remainder $$r$$ of the
-/// division of $$x$$ by $$y$$, such that:
+/// Given two integers, \\(x\\) (dividend) and \\(y\\) (divisor), the Euclidean modulo
+/// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the
+/// division of \\(x\\) by \\(y\\), such that:
///
/// \\[
/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|,
/// \\]
///
-/// where $$q$$ is an integer that represents the quotient of the division.
+/// where \\(q\\) is an integer that represents the quotient of the division.
///
/// The Euclidean modulo function of two numbers, is the remainder operation most
/// commonly utilized in mathematics. This differs from the standard truncating
@@ -169,8 +169,8 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
///
///
/// The function calculates the least common multiple of two integers
-/// $$x, y \in \mathbb{Z}$$. The least common multiple is the smallest positive
-/// integer that has both $$x$$ and $$y$$ as factors.
+/// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive
+/// integer that has both \\(x\\) and \\(y\\) as factors.
///
///
/// Example:
@@ -305,9 +305,9 @@ pub fn proper_divisors(n: Int) -> List(Int) {
/// \sum_{i=1}^n w_i x_i
/// \\]
///
-/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is
-/// the value in the input list indexed by $$i$$, while the $$w_i \in \mathbb{R}$$
-/// are corresponding weights ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
+/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
+/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -362,8 +362,8 @@ pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float
/// \sum_{i=1}^n x_i
/// \\]
///
-/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is
-/// the value in the input list indexed by $$i$$.
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
+/// the value in the input list indexed by \\(i\\).
///
///
/// Example:
@@ -411,9 +411,9 @@ pub fn int_sum(arr: List(Int)) -> Int {
/// \prod_{i=1}^n x_i^{w_i}
/// \\]
///
-/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is
-/// the value in the input list indexed by $$i$$, while the $$w_i \in \mathbb{R}$$
-/// are corresponding weights ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
+/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
+/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -486,8 +486,8 @@ pub fn float_product(
/// \prod_{i=1}^n x_i
/// \\]
///
-/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is
-/// the value in the input list indexed by $$i$$.
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
+/// the value in the input list indexed by \\(i\\).
///
///
/// Example:
@@ -535,10 +535,10 @@ pub fn int_product(arr: List(Int)) -> Int {
/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
-/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$
-/// elements. That is, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$
-/// is the value in the input list indexed by $$i$$. The value $$v_j$$ is thus the
-/// sum of the $$1$$ to $$j$$ first elements in the given list.
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
+/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\)
+/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
+/// sum of the \\(1\\) to \\(j\\) first elements in the given list.
///
///
/// Example:
@@ -585,10 +585,10 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
-/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$
-/// elements. That is, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$
-/// is the value in the input list indexed by $$i$$. The value $$v_j$$ is thus the
-/// sum of the $$1$$ to $$j$$ first elements in the given list.
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
+/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\)
+/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
+/// sum of the \\(1\\) to \\(j\\) first elements in the given list.
///
///
/// Example:
@@ -635,10 +635,10 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
-/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of
-/// $$n$$ elements. That is, $$n$$ is the length of the list and
-/// $$x_i \in \mathbb{R}$$ is the value in the input list indexed by $$i$$. The
-/// value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
+/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
+/// \\(x_i \in \mathbb{R}\\) is the value in the input list indexed by \\(i\\). The
+/// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the
/// given list.
///
///
@@ -687,10 +687,10 @@ pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
-/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of
-/// $$n$$ elements. That is, $$n$$ is the length of the list and
-/// $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$. The
-/// value $$v_j$$ is thus the product of the $$1$$ to $$j$$ first elements in the
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
+/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
+/// \\(x_i \in \mathbb{Z}\\) is the value in the input list indexed by \\(i\\). The
+/// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the
/// given list.
///
///
diff --git a/src/gleam_community/maths/combinatorics.gleam b/src/gleam_community/maths/combinatorics.gleam
index f459ef6..b026c4d 100644
--- a/src/gleam_community/maths/combinatorics.gleam
+++ b/src/gleam_community/maths/combinatorics.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -23,7 +23,8 @@
////
//// ---
////
-//// Combinatorics: A module that offers mathematical functions related to counting, arrangements, and combinations.
+//// Combinatorics: A module that offers mathematical functions related to counting, arrangements,
+//// and permutations/combinations.
////
//// * **Combinatorial functions**
//// * [`combination`](#combination)
@@ -34,8 +35,17 @@
//// * [`cartesian_product`](#cartesian_product)
////
+import gleam/iterator
import gleam/list
+import gleam/option
import gleam/set
+import gleam_community/maths/conversion
+import gleam_community/maths/elementary
+
+pub type CombinatoricsMode {
+ WithRepetitions
+ WithoutRepetitions
+}
///
///
-/// A combinatorial function for computing the number of a $$k$$-combinations of $$n$$ elements:
+/// A combinatorial function for computing the number of \\(k\\)-combinations of \\(n\\) elements.
+///
+/// **Without Repetitions:**
///
/// \\[
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
/// \\]
-/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
+/// Also known as "\\(n\\) choose \\(k\\)" or the binomial coefficient.
///
-/// The implementation uses the effecient iterative multiplicative formula for the computation.
+/// **With Repetitions:**
///
+/// \\[
+/// C^*(n, k) = \binom{n + k - 1}{k} = \frac{(n + k - 1)!}{k! (n - 1)!}
+/// \\]
+/// Also known as the "stars and bars" problem in combinatorics.
+///
+/// The implementation uses an efficient iterative multiplicative formula for computing the result.
+///
+///
+/// Details
+///
+/// A \\(k\\)-combination is a sequence of \\(k\\) elements selected from \\(n\\) elements where
+/// the order of selection does not matter. For example, consider selecting 2 elements from a list
+/// of 3 elements: `["A", "B", "C"]`:
+///
+/// - For \\(k\\)-combinations (without repetitions), where order does not matter, the possible
+/// selections are:
+/// - `["A", "B"]`
+/// - `["A", "C"]`
+/// - `["B", "C"]`
+///
+/// - For \\(k\\)-combinations (with repetitions), where order does not matter but elements can
+/// repeat, the possible selections are:
+/// - `["A", "A"], ["A", "B"], ["A", "C"]`
+/// - `["B", "B"], ["B", "C"], ["C", "C"]`
+///
+/// - On the contrary, for \\(k\\)-permutations (without repetitions), the order matters, so the
+/// possible selections are:
+/// - `["A", "B"], ["B", "A"]`
+/// - `["A", "C"], ["C", "A"]`
+/// - `["B", "C"], ["C", "B"]`
+///
///
/// Example:
///
+/// import gleam/option
/// import gleeunit/should
/// import gleam_community/maths/combinatorics
///
/// pub fn example() {
/// // Invalid input gives an error
-/// // Error on: n = -1 < 0
-/// combinatorics.combination(-1, 1)
+/// combinatorics.combination(-1, 1, option.None)
/// |> should.be_error()
-///
-/// // Valid input returns a result
-/// combinatorics.combination(4, 0)
+///
+/// // Valid input: n = 4 and k = 0
+/// combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(1))
-///
-/// combinatorics.combination(4, 4)
+///
+/// // Valid input: k = n (n = 4, k = 4)
+/// combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(1))
-///
-/// combinatorics.combination(4, 2)
-/// |> should.equal(Ok(6))
-/// }
+///
+/// // Valid input: combinations with repetition (n = 2, k = 3)
+/// combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
+/// |> should.equal(Ok(4))
///
///
///
@@ -81,35 +125,45 @@ import gleam/set
/// Back to top ↑
///
///
-///
-pub fn combination(n: Int, k: Int) -> Result(Int, String) {
- case n < 0 {
- True ->
- "Invalid input argument: n < 0. Valid input is n > 0."
- |> Error
- False ->
- case k < 0 || k > n {
- True ->
- 0
- |> Ok
- False ->
- case k == 0 || k == n {
- True ->
- 1
- |> Ok
- False -> {
- let min = case k < n - k {
- True -> k
- False -> n - k
- }
- list.range(1, min)
- |> list.fold(1, fn(acc: Int, x: Int) -> Int {
- acc * { n + 1 - x } / x
- })
- |> Ok
- }
- }
+///
+pub fn combination(
+ n: Int,
+ k: Int,
+ mode: option.Option(CombinatoricsMode),
+) -> Result(Int, String) {
+ case n, k {
+ _, _ if n < 0 ->
+ "Invalid input argument: n < 0. Valid input is n >= 0." |> Error
+ _, _ if k < 0 ->
+ "Invalid input argument: k < 0. Valid input is k >= 0." |> Error
+ _, _ -> {
+ case mode {
+ option.Some(WithRepetitions) -> combination_with_repetitions(n, k)
+ _ -> combination_without_repetitions(n, k)
}
+ }
+ }
+}
+
+fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, String) {
+ { n + k - 1 }
+ |> combination_without_repetitions(k)
+}
+
+fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, String) {
+ case n, k {
+ _, _ if k == 0 || k == n -> {
+ 1 |> Ok
+ }
+ _, _ -> {
+ let min = case k < n - k {
+ True -> k
+ False -> n - k
+ }
+ list.range(1, min)
+ |> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * { n + 1 - x } / x })
+ |> Ok
+ }
}
}
@@ -119,8 +173,8 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
///
///
///
-/// A combinatorial function for computing the total number of combinations of $$n$$
-/// elements, that is $$n!$$.
+/// A combinatorial function for computing the total number of combinations of \\(n\\)
+/// elements, that is \\(n!\\).
///
///
/// Example:
@@ -133,21 +187,12 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
/// combinatorics.factorial(-1)
/// |> should.be_error()
///
-/// // Valid input returns a result
+/// // Valid input returns a result (n = 0)
/// combinatorics.factorial(0)
/// |> should.equal(Ok(1))
-///
-/// combinatorics.factorial(1)
-/// |> should.equal(Ok(1))
-///
-/// combinatorics.factorial(2)
-/// |> should.equal(Ok(2))
-///
+///
/// combinatorics.factorial(3)
/// |> should.equal(Ok(6))
-///
-/// combinatorics.factorial(4)
-/// |> should.equal(Ok(24))
/// }
///
///
@@ -158,23 +203,20 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
///
///
pub fn factorial(n) -> Result(Int, String) {
- case n < 0 {
- True ->
- "Invalid input argument: n < 0. Valid input is n > 0."
+ case n {
+ _ if n < 0 ->
+ "Invalid input argument: n < 0. Valid input is n >= 0."
|> Error
- False ->
- case n {
- 0 ->
- 1
- |> Ok
- 1 ->
- 1
- |> Ok
- _ ->
- list.range(1, n)
- |> list.fold(1, fn(acc: Int, x: Int) { acc * x })
- |> Ok
- }
+ 0 ->
+ 1
+ |> Ok
+ 1 ->
+ 1
+ |> Ok
+ _ ->
+ list.range(1, n)
+ |> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * x })
+ |> Ok
}
}
@@ -184,33 +226,66 @@ pub fn factorial(n) -> Result(Int, String) {
///
///
///
-/// A combinatorial function for computing the number of $$k$$-permuations (without repetitions)
-/// of $$n$$ elements:
+/// A combinatorial function for computing the number of \\(k\\)-permutations.
+///
+/// **Without** repetitions:
///
/// \\[
-/// P(n, k) = \frac{n!}{(n - k)!}
+/// P(n, k) = \binom{n}{k} \cdot k! = \frac{n!}{(n - k)!}
/// \\]
+///
+/// **With** repetitions:
+///
+/// \\[
+/// P^*(n, k) = n^k
+/// \\]
+///
+/// The implementation uses an efficient iterative multiplicative formula for computing the result.
+///
+///
+/// Details
+///
+/// A \\(k\\)-permutation (without repetitions) is a sequence of \\(k\\) elements selected from \
+/// \\(n\\) elements where the order of selection matters. For example, consider selecting 2
+/// elements from a list of 3 elements: `["A", "B", "C"]`:
+///
+/// - For \\(k\\)-permutations (without repetitions), the order matters, so the possible selections
+/// are:
+/// - `["A", "B"], ["B", "A"]`
+/// - `["A", "C"], ["C", "A"]`
+/// - `["B", "C"], ["C", "B"]`
+///
+/// - For \\(k\\)-permutations (with repetitions), the order also matters, but we have repeated
+/// selections:
+/// - `["A", "A"], ["A", "B"], ["A", "C"]`
+/// - `["B", "A"], ["B", "B"], ["B", "C"]`
+/// - `["C", "A"], ["C", "B"], ["C", "C"]`
///
+/// - On the contrary, for \\(k\\)-combinations (without repetitions), where order does not matter,
+/// the possible selections are:
+/// - `["A", "B"]`
+/// - `["A", "C"]`
+/// - `["B", "C"]`
+///
+///
///
/// Example:
-///
+///
+/// import gleam/option
/// import gleeunit/should
/// import gleam_community/maths/combinatorics
///
/// pub fn example() {
/// // Invalid input gives an error
-/// // Error on: n = -1 < 0
-/// combinatorics.permutation(-1, 1)
+/// combinatorics.permutation(-1, 1, option.None)
/// |> should.be_error()
///
-/// // Valid input returns a result
-/// combinatorics.permutation(4, 0)
+/// // Valid input returns a result (n = 4, k = 0)
+/// combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(1))
///
-/// combinatorics.permutation(4, 4)
-/// |> should.equal(Ok(1))
-///
-/// combinatorics.permutation(4, 2)
+/// // Valid input returns the correct number of permutations (n = 4, k = 2)
+/// combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(12))
/// }
///
@@ -221,50 +296,83 @@ pub fn factorial(n) -> Result(Int, String) {
///
///
///
-pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
- case n < 0 {
- True ->
- "Invalid input argument: n < 0. Valid input is n > 0."
- |> Error
- False ->
- case k < 0 || k > n {
- True ->
- 0
- |> Ok
- False ->
- case k == n {
- True ->
- 1
- |> Ok
- False -> {
- let assert Ok(v1) = factorial(n)
- let assert Ok(v2) = factorial(n - k)
- v1 / v2
- |> Ok
- }
- }
+pub fn permutation(
+ n: Int,
+ k: Int,
+ mode: option.Option(CombinatoricsMode),
+) -> Result(Int, String) {
+ case n, k {
+ _, _ if n < 0 ->
+ "Invalid input argument: n < 0. Valid input is n >= 0." |> Error
+ _, _ if k < 0 ->
+ "Invalid input argument: k < 0. Valid input is k >= 0." |> Error
+ _, _ -> {
+ case mode {
+ option.Some(WithRepetitions) -> permutation_with_repetitions(n, k)
+ _ -> permutation_without_repetitions(n, k)
}
+ }
}
}
+fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, String) {
+ case n, k {
+ _, _ if k < 0 || k > n -> {
+ 0 |> Ok
+ }
+ _, _ if k == 0 -> {
+ 1 |> Ok
+ }
+ _, _ ->
+ list.range(0, k - 1)
+ |> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * { n - x } })
+ |> Ok
+ }
+}
+
+fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, String) {
+ let n_float = conversion.int_to_float(n)
+ let k_float = conversion.int_to_float(k)
+ // 'n' ank 'k' are positive integers, so no errors here...
+ let assert Ok(result) = elementary.power(n_float, k_float)
+ result
+ |> conversion.float_to_int()
+ |> Ok
+}
+
///
///
-/// Generate all $$k$$-combinations based on a given list.
+/// Generates all possible combinations of \\(k\\) elements selected from a given list of size
+/// \\(n\\).
+///
+/// The function can handle cases with and without repetitions
+/// (see more details [here](#combination)). Also, note that repeated elements are treated as
+/// distinct.
///
///
/// Example:
///
-/// import gleeunit/should
/// import gleam/set
+/// import gleam/option
+/// import gleam/iterator
+/// import gleeunit/should
/// import gleam_community/maths/combinatorics
///
/// pub fn example () {
-/// let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 3)
+/// // Generate all 3-combinations without repetition
+/// let assert Ok(result) =
+/// combinatorics.list_combination(
+/// [1, 2, 3, 4],
+/// 3,
+/// option.Some(combinatorics.WithoutRepetitions),
+/// )
+///
/// result
+/// |> iterator.to_list()
/// |> set.from_list()
/// |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
/// }
@@ -276,35 +384,87 @@ pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
///
///
///
-pub fn list_combination(arr: List(a), k: Int) -> Result(List(List(a)), String) {
- case k < 0 {
- True ->
- "Invalid input argument: k < 0. Valid input is k > 0."
+pub fn list_combination(
+ arr: List(a),
+ k: Int,
+ mode: option.Option(CombinatoricsMode),
+) -> Result(iterator.Iterator(List(a)), String) {
+ case k {
+ _ if k < 0 ->
+ "Invalid input argument: k < 0. Valid input is k >= 0."
|> Error
- False -> {
- case k > list.length(arr) {
- True ->
- "Invalid input argument: k > length(arr). Valid input is 0 < k <= length(arr)."
- |> Error
- False -> {
- do_list_combination(arr, k, [])
- |> Ok
- }
+ _ ->
+ case mode {
+ option.Some(WithRepetitions) ->
+ list_combination_with_repetitions(arr, k)
+ _ -> list_combination_without_repetitions(arr, k)
}
+ }
+}
+
+fn list_combination_without_repetitions(
+ arr: List(a),
+ k: Int,
+) -> Result(iterator.Iterator(List(a)), String) {
+ case k, list.length(arr) {
+ _, arr_length if k > arr_length -> {
+ "Invalid input argument: k > length(arr). Valid input is 0 <= k <= length(arr)."
+ |> Error
+ }
+ // Special case: When k = n, then the entire list is the only valid combination
+ _, arr_length if k == arr_length -> {
+ iterator.single(arr) |> Ok
+ }
+ _, _ -> {
+ Ok(
+ do_list_combination_without_repetitions(iterator.from_list(arr), k, []),
+ )
}
}
}
-fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
+fn do_list_combination_without_repetitions(
+ arr: iterator.Iterator(a),
+ k: Int,
+ prefix: List(a),
+) -> iterator.Iterator(List(a)) {
case k {
- 0 -> [list.reverse(prefix)]
+ 0 -> iterator.single(list.reverse(prefix))
_ ->
- case arr {
- [] -> []
- [x, ..xs] -> {
- let with_x = do_list_combination(xs, k - 1, [x, ..prefix])
- let without_x = do_list_combination(xs, k, prefix)
- list.append(with_x, without_x)
+ case arr |> iterator.step {
+ iterator.Done -> iterator.empty()
+ iterator.Next(x, xs) -> {
+ let with_x =
+ do_list_combination_without_repetitions(xs, k - 1, [x, ..prefix])
+ let without_x = do_list_combination_without_repetitions(xs, k, prefix)
+ iterator.concat([with_x, without_x])
+ }
+ }
+ }
+}
+
+fn list_combination_with_repetitions(
+ arr: List(a),
+ k: Int,
+) -> Result(iterator.Iterator(List(a)), String) {
+ Ok(do_list_combination_with_repetitions(iterator.from_list(arr), k, []))
+}
+
+fn do_list_combination_with_repetitions(
+ arr: iterator.Iterator(a),
+ k: Int,
+ prefix: List(a),
+) -> iterator.Iterator(List(a)) {
+ case k {
+ 0 -> iterator.single(list.reverse(prefix))
+ _ ->
+ case arr |> iterator.step {
+ iterator.Done -> iterator.empty()
+ iterator.Next(x, xs) -> {
+ let with_x =
+ do_list_combination_with_repetitions(arr, k - 1, [x, ..prefix])
+ let without_x = do_list_combination_with_repetitions(xs, k, prefix)
+ iterator.concat([with_x, without_x])
}
}
}
@@ -316,43 +476,44 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
///
///
///
-/// Generate all permutations of a given list.
+/// Generates all possible permutations of \\(k\\) elements selected from a given list of size
+/// \\(n\\).
///
-/// Repeated elements are treated as distinct for the
-/// purpose of permutations, so two identical elements
-/// for example will appear "both ways round". This
-/// means lists with repeated elements return the same
-/// number of permutations as ones without.
-///
-/// N.B. The output of this function is a list of size
-/// factorial in the size of the input list. Caution is
-/// advised on input lists longer than ~11 elements, which
-/// may cause the VM to use unholy amounts of memory for
-/// the output.
+/// The function can handle cases with and without repetitions
+/// (see more details [here](#permutation)). Also, note that repeated elements are treated as
+/// distinct.
///
///
/// Example:
///
-/// import gleeunit/should
/// import gleam/set
+/// import gleam/option
+/// import gleam/iterator
+/// import gleeunit/should
/// import gleam_community/maths/combinatorics
///
/// pub fn example () {
-/// [1, 2, 3]
-/// |> combinatorics.list_permutation()
+/// // Generate all 3-permutations without repetition
+/// let assert Ok(result) =
+/// combinatorics.list_permutation(
+/// [1, 2, 3],
+/// 3,
+/// option.Some(combinatorics.WithoutRepetitions),
+/// )
+///
+/// result
+/// |> iterator.to_list()
/// |> set.from_list()
-/// |> should.equal(set.from_list([
-/// [1, 2, 3],
-/// [2, 1, 3],
-/// [3, 1, 2],
-/// [1, 3, 2],
-/// [2, 3, 1],
-/// [3, 2, 1],
-/// ]))
-///
-/// [1.0, 1.0]
-/// |> combinatorics.list_permutation()
-/// |> should.equal([[1.0, 1.0], [1.0, 1.0]])
+/// |> should.equal(
+/// set.from_list([
+/// [1, 2, 3],
+/// [2, 1, 3],
+/// [3, 1, 2],
+/// [1, 3, 2],
+/// [2, 3, 1],
+/// [3, 2, 1],
+/// ]),
+/// )
/// }
///
///
@@ -362,16 +523,96 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
///
///
///
-pub fn list_permutation(arr: List(a)) -> List(List(a)) {
- case arr {
- [] -> [[]]
- _ -> {
- use x <- list.flat_map(arr)
- // `x` is drawn from the list `arr` above,
- // so Ok(...) can be safely asserted as the result of `list.pop` below
- let assert Ok(#(_, remaining)) = list.pop(arr, fn(y) { x == y })
- list.map(list_permutation(remaining), fn(perm) { [x, ..perm] })
+///
+pub fn list_permutation(
+ arr: List(a),
+ k: Int,
+ mode: option.Option(CombinatoricsMode),
+) -> Result(iterator.Iterator(List(a)), String) {
+ case k {
+ _ if k < 0 ->
+ "Invalid input argument: k < 0. Valid input is k >= 0."
+ |> Error
+ _ ->
+ case mode {
+ option.Some(WithRepetitions) ->
+ list_permutation_with_repetitions(arr, k)
+ _ -> list_permutation_without_repetitions(arr, k)
+ }
+ }
+}
+
+fn remove_first_by_index(
+ arr: iterator.Iterator(#(Int, a)),
+ index_to_remove: Int,
+) -> iterator.Iterator(#(Int, a)) {
+ iterator.flat_map(arr, fn(arg) {
+ let #(index, element) = arg
+ case index == index_to_remove {
+ True -> iterator.empty()
+ False -> iterator.single(#(index, element))
}
+ })
+}
+
+fn list_permutation_without_repetitions(
+ arr: List(a),
+ k: Int,
+) -> Result(iterator.Iterator(List(a)), String) {
+ case k, list.length(arr) {
+ _, arr_length if k > arr_length -> {
+ "Invalid input argument: k > length(arr). Valid input is 0 <= k <= length(arr)."
+ |> Error
+ }
+ _, _ -> {
+ let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
+ Ok(do_list_permutation_without_repetitions(
+ iterator.from_list(indexed_arr),
+ k,
+ ))
+ }
+ }
+}
+
+fn do_list_permutation_without_repetitions(
+ arr: iterator.Iterator(#(Int, a)),
+ k: Int,
+) -> iterator.Iterator(List(a)) {
+ case k {
+ 0 -> iterator.single([])
+ _ ->
+ iterator.flat_map(arr, fn(arg) {
+ let #(index, element) = arg
+ let remaining = remove_first_by_index(arr, index)
+ let permutations =
+ do_list_permutation_without_repetitions(remaining, k - 1)
+ iterator.map(permutations, fn(permutation) { [element, ..permutation] })
+ })
+ }
+}
+
+fn list_permutation_with_repetitions(
+ arr: List(a),
+ k: Int,
+) -> Result(iterator.Iterator(List(a)), String) {
+ let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
+ Ok(do_list_permutation_with_repetitions(indexed_arr, k))
+}
+
+fn do_list_permutation_with_repetitions(
+ arr: List(#(Int, a)),
+ k: Int,
+) -> iterator.Iterator(List(a)) {
+ case k {
+ 0 -> iterator.single([])
+ _ ->
+ iterator.flat_map(arr |> iterator.from_list, fn(arg) {
+ let #(_, element) = arg
+ // Allow the same element (by index) to be reused in future recursive calls
+ let permutations = do_list_permutation_with_repetitions(arr, k - 1)
+ // Prepend the current element to each generated permutation
+ iterator.map(permutations, fn(permutation) { [element, ..permutation] })
+ })
}
}
@@ -386,18 +627,22 @@ pub fn list_permutation(arr: List(a)) -> List(List(a)) {
///
/// Example:
///
+/// import gleam/set
/// import gleeunit/should
-/// import gleam/list
/// import gleam_community/maths/combinatorics
///
/// pub fn example () {
-/// []
-/// |> combinatorics.cartesian_product([])
-/// |> should.equal([])
+/// // Cartesian product of two empty sets
+/// set.from_list([])
+/// |> combinatorics.cartesian_product(set.from_list([]))
+/// |> should.equal(set.from_list([]))
///
-/// [1.0, 10.0]
-/// |> combinatorics.cartesian_product([1.0, 2.0])
-/// |> should.equal([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)])
+/// // Cartesian product of two sets with numeric values
+/// set.from_list([1.0, 10.0])
+/// |> combinatorics.cartesian_product(set.from_list([1.0, 2.0]))
+/// |> should.equal(
+/// set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
+/// )
/// }
///
///
@@ -407,13 +652,7 @@ pub fn list_permutation(arr: List(a)) -> List(List(a)) {
///
///
///
-pub fn cartesian_product(xarr: List(a), yarr: List(a)) -> List(#(a, a)) {
- let xset: set.Set(a) =
- xarr
- |> set.from_list()
- let yset: set.Set(a) =
- yarr
- |> set.from_list()
+pub fn cartesian_product(xset: set.Set(a), yset: set.Set(a)) -> set.Set(#(a, a)) {
xset
|> set.fold(
set.new(),
@@ -427,5 +666,4 @@ pub fn cartesian_product(xarr: List(a), yarr: List(a)) -> List(#(a, a)) {
)
},
)
- |> set.to_list()
}
diff --git a/src/gleam_community/maths/conversion.gleam b/src/gleam_community/maths/conversion.gleam
index 9ac9036..1355938 100644
--- a/src/gleam_community/maths/conversion.gleam
+++ b/src/gleam_community/maths/conversion.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -76,7 +76,8 @@ pub fn int_to_float(x: Int) -> Float {
///
///
/// The function returns the integral part of a given floating point value.
-/// That is, everything after the decimal point of a given floating point value is discarded and only the integer value before the decimal point is returned.
+/// That is, everything after the decimal point of a given floating point value is discarded
+/// and only the integer value before the decimal point is returned.
///
///
/// Example
@@ -119,7 +120,7 @@ fn do_to_int(a: Float) -> Int
///
///
/// Convert a value in degrees to a value measured in radians.
-/// That is, $$1 \text{ degrees } = \frac{\pi}{180} \text{ radians }$$.
+/// That is, \\(1 \text{ degrees } = \frac{\pi}{180} \text{ radians }\\).
///
///
/// Example
@@ -151,7 +152,7 @@ pub fn degrees_to_radians(x: Float) -> Float {
///
///
/// Convert a value in degrees to a value measured in radians.
-/// That is, $$1 \text{ radians } = \frac{180}{\pi} \text{ degrees }$$.
+/// That is, \\(1 \text{ radians } = \frac{180}{\pi} \text{ degrees }\\).
///
///
/// Example
diff --git a/src/gleam_community/maths/elementary.gleam b/src/gleam_community/maths/elementary.gleam
index e130e07..25e6b47 100644
--- a/src/gleam_community/maths/elementary.gleam
+++ b/src/gleam_community/maths/elementary.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -70,8 +70,8 @@ import gleam/option
/// \forall x \in \[-1, 1\], \\; \cos^{-1}{(x)} = y \in \[0, \pi \]
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\[-1, 1\]$$ as input and returns a
-/// numeric value $$y$$ that lies in the range $$\[0, \pi \]$$ (an angle in radians).
+/// The function takes a number \\(x\\) in its domain \\(\[-1, 1\]\\) as input and returns a
+/// numeric value \\(y\\) that lies in the range \\(\[0, \pi \]\\) (an angle in radians).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -125,8 +125,8 @@ fn do_acos(a: Float) -> Float
/// \forall x \in [1, +\infty\), \\; \cosh^{-1}{(x)} = y \in \[0, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\[1, +\infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\[0, +\infty\)$$ (an angle in radians).
+/// The function takes a number \\(x\\) in its domain \\(\[1, +\infty\)\\) as input and returns
+/// a numeric value \\(y\\) that lies in the range \\(\[0, +\infty\)\\) (an angle in radians).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -177,9 +177,9 @@ fn do_acosh(a: Float) -> Float
/// \forall x \in \[-1, 1\], \\; \sin^{-1}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\[-1, 1\]$$ as input and returns a numeric
-/// value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians).
-/// If the input value is outside the domain of the function an error is returned.
+/// The function takes a number \\(x\\) in its domain \\(\[-1, 1\]\\) as input and returns a numeric
+/// value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\) (an angle in
+/// radians). If the input value is outside the domain of the function an error is returned.
///
///
/// Example
@@ -232,8 +232,9 @@ fn do_asin(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \sinh^{-1}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, +\infty\)$$ (an angle in radians).
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
+/// returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, +\infty\)\\) (an angle in
+/// radians).
///
///
/// Example
@@ -270,11 +271,12 @@ fn do_asinh(a: Float) -> Float
/// The inverse tangent function:
///
/// \\[
-/// \forall x \in \(-\infty, \infty\), \\; \tan^{-1}{(x)} = y \in \[-\frac{\pi}{2}, \frac{\pi}{2}\]
+/// \forall x \in \(-\infty, \infty\), \\; \tan^{-1}{(x)} = y \in \[-\frac{\pi}{2}, \frac{\pi}{2}\]
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians).
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
+/// returns a numeric value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\)
+/// (an angle in radians).
///
///
/// Example
@@ -323,8 +325,8 @@ fn do_atan(a: Float) -> Float
/// \\]
///
/// The function returns the angle in radians from the x-axis to the line containing the
-/// origin $$\(0, 0\)$$ and a point given as input with coordinates $$\(x, y\)$$. The numeric value
-/// returned by $$\text{atan2}(y, x)$$ is in the range $$\[-\pi, \pi\]$$.
+/// origin \\(\(0, 0\)\\) and a point given as input with coordinates \\(\(x, y\)\\). The numeric
+/// value returned by \\(\text{atan2}(y, x)\\) is in the range \\(\[-\pi, \pi\]\\).
///
///
/// Example
@@ -364,8 +366,8 @@ fn do_atan2(a: Float, b: Float) -> Float
/// \forall x \in \(-1, 1\), \\; \tanh^{-1}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-1, 1\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$ (an angle in radians).
+/// The function takes a number \\(x\\) in its domain \\(\(-1, 1\)\\) as input and returns
+/// a numeric value \\(y\\) that lies in the range \\(\(-\infty, \infty\)\\) (an angle in radians).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -419,8 +421,8 @@ fn do_atanh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \cos{(x)} = y \in \[-1, 1\]
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ (an angle in radians)
-/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
+/// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
///
///
/// Example
@@ -463,9 +465,9 @@ fn do_cos(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \cosh{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ as input (an angle in radians)
-/// and returns a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$.
-/// If the input value is too large an overflow error might occur.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle
+/// in radians) and returns a numeric value \\(y\\) that lies in the range
+/// \\(\(-\infty, \infty\)\\). If the input value is too large an overflow error might occur.
///
///
/// Example
@@ -505,8 +507,8 @@ fn do_cosh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \sin{(x)} = y \in \[-1, 1\]
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ (an angle in radians)
-/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
+/// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
///
///
/// Example
@@ -549,10 +551,9 @@ fn do_sin(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \sinh{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input
-/// (an angle in radians) and returns a numeric value $$y$$ that lies in the range
-/// $$\(-\infty, +\infty\)$$. If the input value is too large an overflow error might
-/// occur.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input
+/// (an angle in radians) and returns a numeric value \\(y\\) that lies in the range
+/// \\(\(-\infty, +\infty\)\\). If the input value is too large an overflow error might occur.
///
///
/// Example
@@ -592,9 +593,9 @@ fn do_sinh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \tan{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input
-/// (an angle in radians) and returns a numeric value $$y$$ that lies in the range
-/// $$\(-\infty, +\infty\)$$.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input
+/// (an angle in radians) and returns a numeric value \\(y\\) that lies in the range
+/// \\(\(-\infty, +\infty\)\\).
///
///
/// Example
@@ -634,8 +635,8 @@ fn do_tan(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \tanh{(x)} = y \in \[-1, 1\]
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ as input (an angle in radians)
-/// and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$.
+/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle
+/// in radians) and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
///
///
/// Example
@@ -681,9 +682,9 @@ fn do_tanh(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; e^{(x)} = y \in \(0, +\infty\)
/// \\]
///
-/// $$e \approx 2.71828\dots$$ is Eulers' number.
+/// \\(e \approx 2.71828\dots\\) is Eulers' number.
///
-/// Note: If the input value $$x$$ is too large an overflow error might occur.
+/// Note: If the input value \\(x\\) is too large an overflow error might occur.
///
///
/// Example
@@ -723,8 +724,8 @@ fn do_exponential(a: Float) -> Float
/// \forall x \in \(0, \infty\), \\; \log_{e}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$.
+/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns
+/// a numeric value \\(y\\) that lies in the range \\(\(-\infty, \infty\)\\).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -773,14 +774,14 @@ fn do_natural_logarithm(a: Float) -> Float
///
///
///
-/// The base $$b$$ logarithm function (computed through the "change of base" formula):
+/// The base \\(b\\) logarithm function (computed through the "change of base" formula):
///
/// \\[
/// \forall x \in \(0, \infty\) \textnormal{ and } b > 1, \\; \log_{b}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ and a base $$b > 1$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$.
+/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) and a base \\(b > 1\\)
+/// as input and returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, \infty\)\\).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -848,8 +849,8 @@ pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String)
/// \forall x \in \(0, \infty), \\; \log_{2}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$.
+/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns a
+/// numeric value \\(y\\) that lies in the range \\(\(-\infty, \infty\)\\).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -903,8 +904,8 @@ fn do_logarithm_2(a: Float) -> Float
/// \forall x \in \(0, \infty), \\; \log_{10}{(x)} = y \in \(-\infty, +\infty\)
/// \\]
///
-/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns
-/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$.
+/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns a
+/// numeric value \\(y\\) that lies in the range \\(\(-\infty, \infty\)\\).
/// If the input value is outside the domain of the function an error is returned.
///
///
@@ -958,14 +959,14 @@ fn do_logarithm_10(a: Float) -> Float
///
///
///
-/// The exponentiation function: $$y = x^{a}$$.
+/// The exponentiation function: \\(y = x^{a}\\).
///
/// Note that the function is not defined if:
-/// 1. The base is negative ($$x < 0$$) and the exponent is fractional
-/// ($$a = \frac{n}{m}$$ is an irrreducible fraction). An error will be returned
+/// 1. The base is negative (\\(x < 0\\)) and the exponent is fractional
+/// (\\(a = \frac{n}{m}\\) is an irrreducible fraction). An error will be returned
/// as an imaginary number will otherwise have to be returned.
-/// 2. The base is zero ($$x = 0$$) and the exponent is negative ($$a < 0$$) then the
-/// expression is equivalent to the exponent $$y$$ divided by $$0$$ and an
+/// 2. The base is zero (\\(x = 0\\)) and the exponent is negative (\\(a < 0\\)) then the
+/// expression is equivalent to the exponent \\(y\\) divided by \\(0\\) and an
/// error will have to be returned as the expression is otherwise undefined.
///
///
@@ -1024,10 +1025,10 @@ fn do_ceiling(a: Float) -> Float
///
///
///
-/// The square root function: $$y = \sqrt[2]{x} = x^{\frac{1}{2}}$$.
+/// The square root function: \\(y = \sqrt[2]{x} = x^{\frac{1}{2}}\\).
///
/// Note that the function is not defined if:
-/// 1. The input is negative ($$x < 0$$). An error will be returned
+/// 1. The input is negative (\\(x < 0\\)). An error will be returned
/// as an imaginary number will otherwise have to be returned.
///
///
@@ -1076,10 +1077,10 @@ pub fn square_root(x: Float) -> Result(Float, String) {
///
///
///
-/// The cube root function: $$y = \sqrt[3]{x} = x^{\frac{1}{3}}$$.
+/// The cube root function: \\(y = \sqrt[3]{x} = x^{\frac{1}{3}}\\).
///
/// Note that the function is not defined if:
-/// 1. The input is negative ($$x < 0$$). An error will be returned
+/// 1. The input is negative (\\(x < 0\\)). An error will be returned
/// as an imaginary number will otherwise have to be returned.
///
///
@@ -1128,10 +1129,10 @@ pub fn cube_root(x: Float) -> Result(Float, String) {
///
///
///
-/// The $$n$$'th root function: $$y = \sqrt[n]{x} = x^{\frac{1}{n}}$$.
+/// The \\(n\\)'th root function: \\(y = \sqrt[n]{x} = x^{\frac{1}{n}}\\).
///
/// Note that the function is not defined if:
-/// 1. The input is negative ($$x < 0$$). An error will be returned
+/// 1. The input is negative (\\(x < 0\\)). An error will be returned
/// as an imaginary number will otherwise have to be returned.
///
///
@@ -1189,7 +1190,7 @@ pub fn nth_root(x: Float, n: Int) -> Result(Float, String) {
///
///
///
-/// The mathematical constant pi: $$\pi \approx 3.1415\dots$$
+/// The mathematical constant pi: \\(\pi \approx 3.1415\dots\\)
///
///
///
-/// Euler's number $$e \approx 2.71828\dots$$.
+/// Euler's number \\(e \approx 2.71828\dots\\).
///
///
/// Example
diff --git a/src/gleam_community/maths/metrics.gleam b/src/gleam_community/maths/metrics.gleam
index 286044c..f4862eb 100644
--- a/src/gleam_community/maths/metrics.gleam
+++ b/src/gleam_community/maths/metrics.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -126,15 +126,15 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) {
///
///
///
-/// Calculate the (weighted) $$p$$-norm of a list (representing a vector):
+/// Calculate the (weighted) \\(p\\)-norm of a list (representing a vector):
///
/// \\[
/// \left( \sum_{i=1}^n w_{i} \left|x_{i}\right|^{p} \right)^{\frac{1}{p}}
/// \\]
///
-/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in
-/// the input list indexed by $$i$$, while $$w_i \in \mathbb{R}_{+}$$ is
-/// a corresponding positive weight ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the list and \\(x_i\\) is the value in
+/// the input list indexed by \\(i\\), while \\(w_i \in \mathbb{R}_{+}\\) is
+/// a corresponding positive weight (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -246,10 +246,10 @@ pub fn norm(
/// \sum_{i=1}^n w_{i} \left|x_i - y_i \right|
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the
-/// values in the respective input lists indexed by $$i$$, while the
-/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
+/// values in the respective input lists indexed by \\(i\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -306,13 +306,13 @@ pub fn manhattan_distance(
/// \left( \sum_{i=1}^n w_{i} \left|x_i - y_i \right|^{p} \right)^{\frac{1}{p}}
/// \\]
///
-/// In the formula, $$p >= 1$$ is the order, $$n$$ is the length of the two lists
-/// and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i$$.
-/// The $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(p >= 1\\) is the order, \\(n\\) is the length of the two lists
+/// and \\(x_i, y_i\\) are the values in the respective input lists indexed by \\(i\\).
+/// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
/// The Minkowski distance is a generalization of both the Euclidean distance
-/// ($$p=2$$) and the Manhattan distance ($$p = 1$$).
+/// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)).
///
///
/// Example:
@@ -396,10 +396,10 @@ pub fn minkowski_distance(
/// \left( \sum_{i=1}^n w_{i} \left|x_i - y_i \right|^{2} \right)^{\frac{1}{2}}
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the
-/// values in the respective input lists indexed by $$i$$, while the
-/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
+/// values in the respective input lists indexed by \\(i\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -455,8 +455,8 @@ pub fn euclidean_distance(
/// \text{max}_{i=1}^n \left|x_i - y_i \right|
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the
-/// values in the respective input lists indexed by $$i$$.
+/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
+/// values in the respective input lists indexed by \\(i\\).
///
///
/// Example:
@@ -517,8 +517,8 @@ pub fn chebyshev_distance(
/// \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i
/// \\]
///
-/// In the formula, $$n$$ is the sample size (the length of the list) and $$x_i$$
-/// is the sample point in the input list indexed by $$i$$.
+/// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\)
+/// is the sample point in the input list indexed by \\(i\\).
///
///
/// Example:
@@ -637,11 +637,11 @@ fn do_median(
/// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})
/// \\]
///
-/// In the formula, $$n$$ is the sample size (the length of the list) and $$x_i$$
-/// is the sample point in the input list indexed by $$i$$.
-/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta
-/// Degrees of Freedom", and is by default set to $$d = 0$$, which gives a biased
-/// estimate of the sample variance. Setting $$d = 1$$ gives an unbiased estimate.
+/// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\)
+/// is the sample point in the input list indexed by \\(i\\).
+/// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta
+/// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased
+/// estimate of the sample variance. Setting \\(d = 1\\) gives an unbiased estimate.
///
///
/// Example:
@@ -713,11 +713,11 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) {
/// s = \left(\frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})\right)^{\frac{1}{2}}
/// \\]
///
-/// In the formula, $$n$$ is the sample size (the length of the list) and $$x_i$$
-/// is the sample point in the input list indexed by $$i$$.
-/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta
-/// Degrees of Freedom", and is by default set to $$d = 0$$, which gives a biased
-/// estimate of the sample standard deviation. Setting $$d = 1$$ gives an unbiased
+/// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\)
+/// is the sample point in the input list indexed by \\(i\\).
+/// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta
+/// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased
+/// estimate of the sample standard deviation. Setting \\(d = 1\\) gives an unbiased
/// estimate.
///
///
@@ -785,14 +785,14 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String)
///
/// where:
///
-/// - $$X$$ and $$Y$$ are two sets being compared,
-/// - $$|X \cap Y|$$ represents the size of the intersection of the two sets
-/// - $$|X \cup Y|$$ denotes the size of the union of the two sets
+/// - \\(X\\) and \\(Y\\) are two sets being compared,
+/// - \\(|X \cap Y|\\) represents the size of the intersection of the two sets
+/// - \\(|X \cup Y|\\) denotes the size of the union of the two sets
///
/// The value of the Jaccard index ranges from 0 to 1, where 0 indicates that the
/// two sets share no elements and 1 indicates that the sets are identical. The
/// Jaccard index is a special case of the [Tversky index](#tversky_index) (with
-/// $$\alpha=\beta=1$$).
+/// \\(\alpha=\beta=1\\)).
///
///
/// Example:
@@ -835,16 +835,16 @@ pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// \\]
///
/// where:
-/// - $$X$$ and $$Y$$ are two sets being compared
-/// - $$|X \cap Y|$$ is the size of the intersection of the two sets (i.e., the
+/// - \\(X\\) and \\(Y\\) are two sets being compared
+/// - \\(|X \cap Y|\\) is the size of the intersection of the two sets (i.e., the
/// number of elements common to both sets)
-/// - $$|X|$$ and $$|Y|$$ are the sizes of the sets $$X$$ and $$Y$$, respectively
+/// - \\(|X|\\) and \\(|Y|\\) are the sizes of the sets \\(X\\) and \\(Y\\), respectively
///
/// The coefficient ranges from 0 to 1, where 0 indicates no similarity (the sets
/// share no elements) and 1 indicates perfect similarity (the sets are identical).
/// The higher the coefficient, the greater the similarity between the two sets.
/// The Sørensen-Dice coefficient is a special case of the
-/// [Tversky index](#tversky_index) (with $$\alpha=\beta=0.5$$).
+/// [Tversky index](#tversky_index) (with \\(\alpha=\beta=0.5\\)).
///
///
/// Example:
@@ -880,8 +880,8 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
///
///
/// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice
-/// coefficient, which adds flexibility through two parameters, $$\alpha$$ and
-/// $$\beta$$, allowing for asymmetric similarity measures between sets. The
+/// coefficient, which adds flexibility through two parameters, \\(\alpha\\) and
+/// \\(\beta\\), allowing for asymmetric similarity measures between sets. The
/// Tversky index is defined as:
///
/// \\[
@@ -890,18 +890,18 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
///
/// where:
///
-/// - $$X$$ and $$Y$$ are the sets being compared
-/// - $$|X - Y|$$ and $$|Y - X|$$ are the sizes of the relative complements of
-/// $$Y$$ in $$X$$ and $$X$$ in $$Y$$, respectively,
-/// - $$\alpha$$ and $$\beta$$ are parameters that weigh the relative importance
-/// of the elements unique to $$X$$ and $$Y$$
+/// - \\(X\\) and \\(Y\\) are the sets being compared
+/// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of
+/// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively,
+/// - \\(\alpha\\) and \\(\beta\\) are parameters that weigh the relative importance
+/// of the elements unique to \\(X\\) and \\(Y\\)
///
-/// The Tversky index reduces to the Jaccard index when $$\alpha = \beta = 1$$ and
-/// to the Sørensen-Dice coefficient when $$\alpha = \beta = 0.5$$. In general, the
+/// The Tversky index reduces to the Jaccard index when \\(\alpha = \beta = 1\\) and
+/// to the Sørensen-Dice coefficient when \\(\alpha = \beta = 0.5\\). In general, the
/// Tversky index can take on any non-negative value, including 0. The index equals
/// 0 when there is no intersection between the two sets, indicating no similarity.
/// However, unlike similarity measures bounded strictly between 0 and 1, the
-/// Tversky index does not have a strict upper limit of 1 when $$\alpha \neq \beta$$.
+/// Tversky index does not have a strict upper limit of 1 when \\(\alpha \neq \beta\\).
///
///
/// Example:
@@ -982,9 +982,9 @@ pub fn tversky_index(
///
/// where:
///
-/// - $$X$$ and $$Y$$ are the sets being compared
-/// - $$|X \cap Y|$$ is the size of the intersection of the sets
-/// - $$\min(|X|, |Y|)$$ is the size of the smaller set among $$X$$ and $$Y$$
+/// - \\(X\\) and \\(Y\\) are the sets being compared
+/// - \\(|X \cap Y|\\) is the size of the intersection of the sets
+/// - \\(\min(|X|, |Y|)\\) is the size of the smaller set among \\(X\\) and \\(Y\\)
///
/// The coefficient ranges from 0 to 1, where 0 indicates no overlap and 1
/// indicates that the smaller set is a suyset of the larger set. This
@@ -1043,10 +1043,10 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// \\; \in \\; \left[-1, 1\right]
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists and $$x_i$$, $$y_i$$ are
-/// the values in the respective input lists indexed by $$i$$, while the
-/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the two lists and \\(x_i\\), \\(y_i\\) are
+/// the values in the respective input lists indexed by \\(i\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
/// The cosine similarity provides a value between -1 and 1, where 1 means the
/// vectors are in the same direction, -1 means they are in exactly opposite
@@ -1143,10 +1143,10 @@ pub fn cosine_similarity(
/// {\left| x_i \right| + \left| y_i \right|}
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists, and $$x_i, y_i$$ are the
-/// values in the respective input lists indexed by $$i$$, while the
-/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the
+/// values in the respective input lists indexed by \\(i\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
///
/// Example:
@@ -1231,12 +1231,12 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
/// {\sum_{i=1}^n w_{i}\left| x_i + y_i \right|}
/// \\]
///
-/// In the formula, $$n$$ is the length of the two lists, and $$x_i, y_i$$ are the values
-/// in the respective input lists indexed by $$i$$, while the
-/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights
-/// ($$w_i = 1.0\\;\forall i=1...n$$ by default).
+/// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the values
+/// in the respective input lists indexed by \\(i\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
+/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
///
-/// The Bray-Curtis distance is in the range $$[0, 1]$$ if all entries $$x_i, y_i$$ are
+/// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are
/// positive.
///
///
diff --git a/src/gleam_community/maths/piecewise.gleam b/src/gleam_community/maths/piecewise.gleam
index 8496c0b..36a18dc 100644
--- a/src/gleam_community/maths/piecewise.gleam
+++ b/src/gleam_community/maths/piecewise.gleam
@@ -1,6 +1,6 @@
-////
-////
-////
+////
+////
+////
////
@@ -66,24 +66,26 @@ import gleam_community/maths/elementary
///
///
///
-/// The ceiling function rounds a given input value $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is larger than or equal to the input $$x$$.
+/// The ceiling function rounds a given input value \\(x \in \mathbb{R}\\) to the nearest integer
+/// value (at the specified digit) that is larger than or equal to the input \\(x\\).
///
-/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundUp`.
+/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round)
+/// with rounding mode `RoundUp`.
///
///
/// Details
///
-/// For example, $$12.0654$$ is rounded to:
-/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`)
-/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`)
+/// For example, \\(12.0654\\) is rounded to:
+/// - \\(13.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
+/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.066\\) for 3 digits after the decimal point (`digits = 3`)
///
/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// For example, $$12.0654$$ is rounded to:
-/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`)
-/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// For example, \\(12.0654\\) is rounded to:
+/// - \\(20.0\\) for 1 digit places before the decimal point (`digit = -1`)
+/// - \\(100.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(1000.0\\) for 3 digits before the decimal point (`digits = -3`)
///
///
///
@@ -122,23 +124,26 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float {
///
///
///
-/// The floor function rounds input $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is less than or equal to the input $$x$$.
+/// The floor function rounds input \\(x \in \mathbb{R}\\) to the nearest integer value (at the
+/// specified digit) that is less than or equal to the input \\(x\\).
///
-/// Note: The floor function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundDown`.
+/// Note: The floor function is used as an alias for the rounding function [`round`](#round)
+/// with rounding mode `RoundDown`.
///
///
/// Details
///
-/// For example, $$12.0654$$ is rounded to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`)
-/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// For example, \\(12.0654\\) is rounded to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
+/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
///
///
@@ -177,23 +182,27 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float {
///
///
///
-/// The truncate function rounds a given input $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is less than or equal to the absolute value of the input $$x$$.
+/// The truncate function rounds a given input \\(x \in \mathbb{R}\\) to the nearest integer
+/// value (at the specified digit) that is less than or equal to the absolute value of the
+/// input \\(x\\).
///
-/// Note: The truncate function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundToZero`.
+/// Note: The truncate function is used as an alias for the rounding function [`round`](#round)
+/// with rounding mode `RoundToZero`.
///
///
/// Details
///
-/// For example, $$12.0654$$ is rounded to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`)
-/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// For example, \\(12.0654\\) is rounded to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
+/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
///
///
@@ -232,84 +241,103 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
///
///
///
-/// The function rounds a float to a specific number of digits (after the decimal place or before if negative) using a specified rounding mode.
+/// The function rounds a float to a specific number of digits (after the decimal place or before
+/// if negative) using a specified rounding mode.
///
/// Valid rounding modes include:
-/// - `RoundNearest` (default): The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded to the nearest even integer.
-/// - `RoundTiesAway`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++ rounding behavior).
-/// - `RoundTiesUp`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded towards $$+\infty$$ (Java/JavaScript rounding behaviour).
-/// - `RoundToZero`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is less than or equal to the absolute value of the input $$x$$. An alias for this rounding mode is [`truncate`](#truncate).
-/// - `RoundDown`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is less than or equal to the input $$x$$. An alias for this rounding mode is [`floor`](#floor).
-/// - `RoundUp`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is larger than or equal to the input $$x$$. An alias for this rounding mode is [`ceiling`](#ceiling).
+/// - `RoundNearest` (default): The input \\(x\\) is rounded to the nearest integer value (at the
+/// specified digit) with ties (fractional values of 0.5) being rounded to the nearest even
+/// integer.
+/// - `RoundTiesAway`: The input \\(x\\) is rounded to the nearest integer value (at the
+/// specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++
+/// rounding behavior).
+/// - `RoundTiesUp`: The input \\(x\\) is rounded to the nearest integer value (at the specified
+/// digit) with ties (fractional values of 0.5) being rounded towards \\(+\infty\\)
+/// (Java/JavaScript rounding behaviour).
+/// - `RoundToZero`: The input \\(x\\) is rounded to the nearest integer value (at the specified
+/// digit) that is less than or equal to the absolute value of the input \\(x\\). An alias for
+/// this rounding mode is [`truncate`](#truncate).
+/// - `RoundDown`: The input \\(x\\) is rounded to the nearest integer value (at the specified
+/// digit) that is less than or equal to the input \\(x\\). An alias for this rounding mode is
+/// [`floor`](#floor).
+/// - `RoundUp`: The input \\(x\\) is rounded to the nearest integer value (at the specified
+/// digit) that is larger than or equal to the input \\(x\\). An alias for this rounding mode
+/// is [`ceiling`](#ceiling).
///
///
/// Details
///
-/// The `RoundNearest` rounding mode, rounds $$12.0654$$ to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`)
-/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundNearest` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
+/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
-/// The `RoundTiesAway` rounding mode, rounds $$12.0654$$ to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`)
-/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundTiesAway` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
+/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
-/// The `RoundTiesUp` rounding mode, rounds $$12.0654$$ to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.1$$ for 1 digits after the decimal point (`digits = 1`)
-/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundTiesUp` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.1\\) for 1 digits after the decimal point (`digits = 1`)
+/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
-/// The `RoundToZero` rounding mode, rounds $$12.0654$$ to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.0$$ for 1 digit after the decimal point (`digits = 1`)
-/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundToZero` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.0\\) for 1 digit after the decimal point (`digits = 1`)
+/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
-/// The `RoundDown` rounding mode, rounds $$12.0654$$ to:
-/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`)
-/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundDown` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
+/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`)
-/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
+/// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
///
-/// The `RoundUp` rounding mode, rounds $$12.0654$$ to:
-/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`)
-/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`)
-/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`)
-/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`)
+/// The `RoundUp` rounding mode, rounds \\(12.0654\\) to:
+/// - \\(13.0\\) for 0 digits after the decimal point (`digits = 0`)
+/// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
+/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
+/// - \\(12.066\\) for 3 digits after the decimal point (`digits = 3`)
///
-/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point.
-/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`)
-/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`)
-/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`)
+/// It is also possible to specify a negative number of digits. In that case, the negative
+/// number refers to the digits before the decimal point.
+/// - \\(20.0\\) for 1 digit places before the decimal point (`digit = -1`)
+/// - \\(100.0\\) for 2 digits before the decimal point (`digits = -2`)
+/// - \\(1000.0\\) for 3 digits before the decimal point (`digits = -3`)
///
///
///
@@ -475,7 +503,7 @@ fn do_ceiling(a: Float) -> Float
/// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}.
/// \\]
///
-/// The function takes an input $$x$$ and returns a positive float value.
+/// The function takes an input \\(x\\) and returns a positive float value.
///
///
///
@@ -504,7 +532,7 @@ pub fn float_absolute_value(x: Float) -> Float {
/// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}.
/// \\]
///
-/// The function takes an input $$x$$ and returns a positive integer value.
+/// The function takes an input \\(x\\) and returns a positive integer value.
///
///
///
@@ -533,7 +561,7 @@ pub fn int_absolute_value(x: Int) -> Int {
/// \forall x, y \in \mathbb{R}, \\; |x - y| \in \mathbb{R}_{+}.
/// \\]
///
-/// The function takes two inputs $$x$$ and $$y$$ and returns a positive float
+/// The function takes two inputs \\(x\\) and \\(y\\) and returns a positive float
/// value which is the the absolute difference of the inputs.
///
///
@@ -574,7 +602,7 @@ pub fn float_absolute_difference(a: Float, b: Float) -> Float {
/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}.
/// \\]
///
-/// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer
+/// The function takes two inputs \\(x\\) and \\(y\\) and returns a positive integer
/// value which is the the absolute difference of the inputs.
///
///
@@ -609,7 +637,7 @@ pub fn int_absolute_difference(a: Int, b: Int) -> Int {
///
///
///
-/// The function takes an input $$x \in \mathbb{R}$$ and returns the sign of
+/// The function takes an input \\(x \in \mathbb{R}\\) and returns the sign of
/// the input, indicating whether it is positive (+1.0), negative (-1.0), or
/// zero (0.0).
///
@@ -645,7 +673,7 @@ fn do_float_sign(a: Float) -> Float
///
///
///
-/// The function takes an input $$x \in \mathbb{Z}$$ and returns the sign of
+/// The function takes an input \\(x \in \mathbb{Z}\\) and returns the sign of
/// the input, indicating whether it is positive (+1), negative (-1), or zero
/// (0).
///
@@ -681,8 +709,8 @@ fn do_int_sign(a: Int) -> Int
///
///
///
-/// The function takes two arguments $$x, y \in \mathbb{R}$$ and returns $$x$$
-/// such that it has the same sign as $$y$$.
+/// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\)
+/// such that it has the same sign as \\(y\\).
///
///
///
-/// The function takes two arguments $$x, y \in \mathbb{Z}$$ and returns $$x$$
-/// such that it has the same sign as $$y$$.
+/// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\)
+/// such that it has the same sign as \\(y\\).
///
///
///
-/// The function flips the sign of a given input value $$x \in \mathbb{R}$$.
+/// The function flips the sign of a given input value \\(x \in \mathbb{R}\\).
///
///
///
-/// The function flips the sign of a given input value $$x \in \mathbb{Z}$$.
+/// The function flips the sign of a given input value \\(x \in \mathbb{Z}\\).
///
///