Merge pull request #26 from NicklasXYZ/main

Update the combinatorics and sequences module
This commit is contained in:
Nicklas Sindlev Andersen 2024-08-17 01:16:37 +02:00 committed by GitHub
commit 7fd4d74b2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1630 additions and 714 deletions

View file

@ -10,43 +10,49 @@ The library supports both targets: Erlang and JavaScript.
## Quickstart ## Quickstart
```gleam ```gleam
import gleam/float
import gleam/iterator
import gleam/option.{Some}
import gleam_community/maths/arithmetics 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/elementary
import gleam_community/maths/piecewise import gleam_community/maths/piecewise
import gleam_community/maths/predicates import gleam_community/maths/predicates
import gleam/float import gleeunit/should
import gleam/int
pub fn main() { pub fn example() {
// Evaluate the sine function // Evaluate the sine function
elementary.sin(elementary.pi()) let result = elementary.sin(elementary.pi())
// Returns Float: 0.0
// 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 // Find the greatest common divisor
arithmetics.gcd(54, 24) arithmetics.gcd(54, 24)
// Returns Int: 6 |> should.equal(6)
// Find the minimum and maximum of a list // Find the minimum and maximum of a list
piecewise.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare) piecewise.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
// Returns Tuple: Ok(#(3.0, 50.0)) |> should.equal(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])
// Determine if a number is fractional // Determine if a number is fractional
predicates.is_fractional(0.3333) predicates.is_fractional(0.3333)
// Returns Bool: True |> should.equal(True)
// Determine if 28 is a power of 3 // Generate all k = 2 combinations of [1, 2, 3]
predicates.is_power(28, 3) let assert Ok(combinations) =
// Returns Bool: False combinatorics.list_combination([1, 2, 3], 2, Some(WithoutRepetitions))
combinations
// Generate all k = 1 combinations of [1, 2] |> iterator.to_list()
combinatorics.list_combination([1, 2], 1) |> should.equal([[1, 2], [1, 3], [2, 3]])
// Returns List: Ok([[1], [2]])
} }
``` ```
## Installation ## Installation

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -58,8 +58,8 @@ import gleam_community/maths/piecewise
/// </div> /// </div>
/// ///
/// The function calculates the greatest common divisor of two integers /// The function calculates the greatest common divisor of two integers
/// $$x, y \in \mathbb{Z}$$. The greatest common divisor is the largest positive /// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive
/// integer that is divisible by both $$x$$ and $$y$$. /// integer that is divisible by both \\(x\\) and \\(y\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -108,15 +108,15 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// </div> /// </div>
/// ///
/// ///
/// Given two integers, $$x$$ (dividend) and $$y$$ (divisor), the Euclidean modulo /// 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 /// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the
/// division of $$x$$ by $$y$$, such that: /// division of \\(x\\) by \\(y\\), such that:
/// ///
/// \\[ /// \\[
/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|, /// 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 /// The Euclidean modulo function of two numbers, is the remainder operation most
/// commonly utilized in mathematics. This differs from the standard truncating /// commonly utilized in mathematics. This differs from the standard truncating
@ -169,8 +169,8 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
/// </div> /// </div>
/// ///
/// The function calculates the least common multiple of two integers /// The function calculates the least common multiple of two integers
/// $$x, y \in \mathbb{Z}$$. The least common multiple is the smallest positive /// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive
/// integer that has both $$x$$ and $$y$$ as factors. /// integer that has both \\(x\\) and \\(y\\) as factors.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -305,9 +305,9 @@ pub fn proper_divisors(n: Int) -> List(Int) {
/// \sum_{i=1}^n w_i x_i /// \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 /// 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}$$ /// 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). /// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -362,8 +362,8 @@ pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float
/// \sum_{i=1}^n x_i /// \sum_{i=1}^n x_i
/// \\] /// \\]
/// ///
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is /// 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$$. /// the value in the input list indexed by \\(i\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -411,9 +411,9 @@ pub fn int_sum(arr: List(Int)) -> Int {
/// \prod_{i=1}^n x_i^{w_i} /// \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 /// 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}$$ /// 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). /// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -486,8 +486,8 @@ pub fn float_product(
/// \prod_{i=1}^n x_i /// \prod_{i=1}^n x_i
/// \\] /// \\]
/// ///
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is /// 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$$. /// the value in the input list indexed by \\(i\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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 /// 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$$ /// 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}$$ /// 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 /// 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. /// sum of the \\(1\\) to \\(j\\) first elements in the given list.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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 /// 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$$ /// 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}$$ /// 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 /// 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. /// sum of the \\(1\\) to \\(j\\) first elements in the given list.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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 /// 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 /// 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 /// \\(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 /// \\(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 /// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the
/// given list. /// given list.
/// ///
/// <details> /// <details>
@ -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 /// 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 /// 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 /// \\(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 /// \\(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 /// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the
/// given list. /// given list.
/// ///
/// <details> /// <details>

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -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** //// * **Combinatorial functions**
//// * [`combination`](#combination) //// * [`combination`](#combination)
@ -34,8 +35,17 @@
//// * [`cartesian_product`](#cartesian_product) //// * [`cartesian_product`](#cartesian_product)
//// ////
import gleam/iterator
import gleam/list import gleam/list
import gleam/option
import gleam/set import gleam/set
import gleam_community/maths/conversion
import gleam_community/maths/elementary
pub type CombinatoricsMode {
WithRepetitions
WithoutRepetitions
}
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues"> /// <a href="https://github.com/gleam-community/maths/issues">
@ -43,37 +53,71 @@ import gleam/set
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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)!} /// 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>
/// <summary>Details</summary>
///
/// 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"]`
/// </details>
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/option
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/combinatorics /// import gleam_community/maths/combinatorics
/// ///
/// pub fn example() { /// pub fn example() {
/// // Invalid input gives an error /// // Invalid input gives an error
/// // Error on: n = -1 < 0 /// combinatorics.combination(-1, 1, option.None)
/// combinatorics.combination(-1, 1)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Valid input returns a result /// // Valid input: n = 4 and k = 0
/// combinatorics.combination(4, 0) /// combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(1)) /// |> 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)) /// |> should.equal(Ok(1))
/// ///
/// combinatorics.combination(4, 2) /// // Valid input: combinations with repetition (n = 2, k = 3)
/// |> should.equal(Ok(6)) /// combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
/// } /// |> should.equal(Ok(4))
/// </details> /// </details>
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
@ -81,35 +125,45 @@ import gleam/set
/// <small>Back to top </small> /// <small>Back to top </small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn combination(n: Int, k: Int) -> Result(Int, String) { pub fn combination(
case n < 0 { n: Int,
True -> k: Int,
"Invalid input argument: n < 0. Valid input is n > 0." mode: option.Option(CombinatoricsMode),
|> Error ) -> Result(Int, String) {
False -> case n, k {
case k < 0 || k > n { _, _ if n < 0 ->
True -> "Invalid input argument: n < 0. Valid input is n >= 0." |> Error
0 _, _ if k < 0 ->
|> Ok "Invalid input argument: k < 0. Valid input is k >= 0." |> Error
False -> _, _ -> {
case k == 0 || k == n { case mode {
True -> option.Some(WithRepetitions) -> combination_with_repetitions(n, k)
1 _ -> combination_without_repetitions(n, k)
|> 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
}
}
} }
}
}
}
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> /// </a>
/// </div> /// </div>
/// ///
/// A combinatorial function for computing the total number of combinations of $$n$$ /// A combinatorial function for computing the total number of combinations of \\(n\\)
/// elements, that is $$n!$$. /// elements, that is \\(n!\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -133,21 +187,12 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
/// combinatorics.factorial(-1) /// combinatorics.factorial(-1)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Valid input returns a result /// // Valid input returns a result (n = 0)
/// combinatorics.factorial(0) /// combinatorics.factorial(0)
/// |> should.equal(Ok(1)) /// |> should.equal(Ok(1))
/// ///
/// combinatorics.factorial(1)
/// |> should.equal(Ok(1))
///
/// combinatorics.factorial(2)
/// |> should.equal(Ok(2))
///
/// combinatorics.factorial(3) /// combinatorics.factorial(3)
/// |> should.equal(Ok(6)) /// |> should.equal(Ok(6))
///
/// combinatorics.factorial(4)
/// |> should.equal(Ok(24))
/// } /// }
/// </details> /// </details>
/// ///
@ -158,23 +203,20 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) {
/// </div> /// </div>
/// ///
pub fn factorial(n) -> Result(Int, String) { pub fn factorial(n) -> Result(Int, String) {
case n < 0 { case n {
True -> _ if n < 0 ->
"Invalid input argument: n < 0. Valid input is n > 0." "Invalid input argument: n < 0. Valid input is n >= 0."
|> Error |> Error
False -> 0 ->
case n { 1
0 -> |> Ok
1 1 ->
|> Ok 1
1 -> |> Ok
1 _ ->
|> Ok list.range(1, n)
_ -> |> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * x })
list.range(1, n) |> Ok
|> list.fold(1, fn(acc: Int, x: Int) { acc * x })
|> Ok
}
} }
} }
@ -184,33 +226,66 @@ pub fn factorial(n) -> Result(Int, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A combinatorial function for computing the number of $$k$$-permuations (without repetitions) /// A combinatorial function for computing the number of \\(k\\)-permutations.
/// of $$n$$ elements: ///
/// **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>
/// <summary>Details</summary>
///
/// 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"]`
/// </details>
///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/option
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/combinatorics /// import gleam_community/maths/combinatorics
/// ///
/// pub fn example() { /// pub fn example() {
/// // Invalid input gives an error /// // Invalid input gives an error
/// // Error on: n = -1 < 0 /// combinatorics.permutation(-1, 1, option.None)
/// combinatorics.permutation(-1, 1)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Valid input returns a result /// // Valid input returns a result (n = 4, k = 0)
/// combinatorics.permutation(4, 0) /// combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
/// |> should.equal(Ok(1)) /// |> should.equal(Ok(1))
/// ///
/// combinatorics.permutation(4, 4) /// // Valid input returns the correct number of permutations (n = 4, k = 2)
/// |> should.equal(Ok(1)) /// combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
///
/// combinatorics.permutation(4, 2)
/// |> should.equal(Ok(12)) /// |> should.equal(Ok(12))
/// } /// }
/// </details> /// </details>
@ -221,50 +296,83 @@ pub fn factorial(n) -> Result(Int, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn permutation(n: Int, k: Int) -> Result(Int, String) { pub fn permutation(
case n < 0 { n: Int,
True -> k: Int,
"Invalid input argument: n < 0. Valid input is n > 0." mode: option.Option(CombinatoricsMode),
|> Error ) -> Result(Int, String) {
False -> case n, k {
case k < 0 || k > n { _, _ if n < 0 ->
True -> "Invalid input argument: n < 0. Valid input is n >= 0." |> Error
0 _, _ if k < 0 ->
|> Ok "Invalid input argument: k < 0. Valid input is k >= 0." |> Error
False -> _, _ -> {
case k == n { case mode {
True -> option.Some(WithRepetitions) -> permutation_with_repetitions(n, k)
1 _ -> permutation_without_repetitions(n, k)
|> Ok
False -> {
let assert Ok(v1) = factorial(n)
let assert Ok(v2) = factorial(n - k)
v1 / v2
|> Ok
}
}
} }
}
} }
} }
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
}
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues"> /// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleeunit/should
/// import gleam/set /// import gleam/set
/// import gleam/option
/// import gleam/iterator
/// import gleeunit/should
/// import gleam_community/maths/combinatorics /// import gleam_community/maths/combinatorics
/// ///
/// pub fn example () { /// 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 /// result
/// |> iterator.to_list()
/// |> set.from_list() /// |> set.from_list()
/// |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])) /// |> 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) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn list_combination(arr: List(a), k: Int) -> Result(List(List(a)), String) { pub fn list_combination(
case k < 0 { arr: List(a),
True -> k: Int,
"Invalid input argument: k < 0. Valid input is k > 0." 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 |> Error
False -> { _ ->
case k > list.length(arr) { case mode {
True -> option.Some(WithRepetitions) ->
"Invalid input argument: k > length(arr). Valid input is 0 < k <= length(arr)." list_combination_with_repetitions(arr, k)
|> Error _ -> list_combination_without_repetitions(arr, k)
False -> {
do_list_combination(arr, k, [])
|> Ok
}
} }
}
}
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 { case k {
0 -> [list.reverse(prefix)] 0 -> iterator.single(list.reverse(prefix))
_ -> _ ->
case arr { case arr |> iterator.step {
[] -> [] iterator.Done -> iterator.empty()
[x, ..xs] -> { iterator.Next(x, xs) -> {
let with_x = do_list_combination(xs, k - 1, [x, ..prefix]) let with_x =
let without_x = do_list_combination(xs, k, prefix) do_list_combination_without_repetitions(xs, k - 1, [x, ..prefix])
list.append(with_x, without_x) 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)) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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 /// The function can handle cases with and without repetitions
/// purpose of permutations, so two identical elements /// (see more details [here](#permutation)). Also, note that repeated elements are treated as
/// for example will appear "both ways round". This /// distinct.
/// 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.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleeunit/should
/// import gleam/set /// import gleam/set
/// import gleam/option
/// import gleam/iterator
/// import gleeunit/should
/// import gleam_community/maths/combinatorics /// import gleam_community/maths/combinatorics
/// ///
/// pub fn example () { /// pub fn example () {
/// [1, 2, 3] /// // Generate all 3-permutations without repetition
/// |> combinatorics.list_permutation() /// let assert Ok(result) =
/// combinatorics.list_permutation(
/// [1, 2, 3],
/// 3,
/// option.Some(combinatorics.WithoutRepetitions),
/// )
///
/// result
/// |> iterator.to_list()
/// |> set.from_list() /// |> set.from_list()
/// |> should.equal(set.from_list([ /// |> should.equal(
/// [1, 2, 3], /// set.from_list([
/// [2, 1, 3], /// [1, 2, 3],
/// [3, 1, 2], /// [2, 1, 3],
/// [1, 3, 2], /// [3, 1, 2],
/// [2, 3, 1], /// [1, 3, 2],
/// [3, 2, 1], /// [2, 3, 1],
/// ])) /// [3, 2, 1],
/// /// ]),
/// [1.0, 1.0] /// )
/// |> combinatorics.list_permutation()
/// |> should.equal([[1.0, 1.0], [1.0, 1.0]])
/// } /// }
/// </details> /// </details>
/// ///
@ -362,16 +523,96 @@ fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn list_permutation(arr: List(a)) -> List(List(a)) { ///
case arr { pub fn list_permutation(
[] -> [[]] arr: List(a),
_ -> { k: Int,
use x <- list.flat_map(arr) mode: option.Option(CombinatoricsMode),
// `x` is drawn from the list `arr` above, ) -> Result(iterator.Iterator(List(a)), String) {
// so Ok(...) can be safely asserted as the result of `list.pop` below case k {
let assert Ok(#(_, remaining)) = list.pop(arr, fn(y) { x == y }) _ if k < 0 ->
list.map(list_permutation(remaining), fn(perm) { [x, ..perm] }) "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)) {
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/set
/// import gleeunit/should /// import gleeunit/should
/// import gleam/list
/// import gleam_community/maths/combinatorics /// import gleam_community/maths/combinatorics
/// ///
/// pub fn example () { /// pub fn example () {
/// [] /// // Cartesian product of two empty sets
/// |> combinatorics.cartesian_product([]) /// set.from_list([])
/// |> should.equal([]) /// |> combinatorics.cartesian_product(set.from_list([]))
/// |> should.equal(set.from_list([]))
/// ///
/// [1.0, 10.0] /// // Cartesian product of two sets with numeric values
/// |> combinatorics.cartesian_product([1.0, 2.0]) /// set.from_list([1.0, 10.0])
/// |> should.equal([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.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)]),
/// )
/// } /// }
/// </details> /// </details>
/// ///
@ -407,13 +652,7 @@ pub fn list_permutation(arr: List(a)) -> List(List(a)) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn cartesian_product(xarr: List(a), yarr: List(a)) -> List(#(a, a)) { pub fn cartesian_product(xset: set.Set(a), yset: set.Set(a)) -> set.Set(#(a, a)) {
let xset: set.Set(a) =
xarr
|> set.from_list()
let yset: set.Set(a) =
yarr
|> set.from_list()
xset xset
|> set.fold( |> set.fold(
set.new(), set.new(),
@ -427,5 +666,4 @@ pub fn cartesian_product(xarr: List(a), yarr: List(a)) -> List(#(a, a)) {
) )
}, },
) )
|> set.to_list()
} }

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -76,7 +76,8 @@ pub fn int_to_float(x: Int) -> Float {
/// </div> /// </div>
/// ///
/// The function returns the integral part of a given floating point value. /// 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.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -119,7 +120,7 @@ fn do_to_int(a: Float) -> Int
/// </div> /// </div>
/// ///
/// Convert a value in degrees to a value measured in radians. /// 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 }\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -151,7 +152,7 @@ pub fn degrees_to_radians(x: Float) -> Float {
/// </div> /// </div>
/// ///
/// Convert a value in degrees to a value measured in radians. /// 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 }\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -70,8 +70,8 @@ import gleam/option
/// \forall x \in \[-1, 1\], \\; \cos^{-1}{(x)} = y \in \[0, \pi \] /// \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 /// 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). /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -125,8 +125,8 @@ fn do_acos(a: Float) -> Float
/// \forall x \in [1, +\infty\), \\; \cosh^{-1}{(x)} = y \in \[0, +\infty\) /// \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 /// 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). /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -177,9 +177,9 @@ fn do_acosh(a: Float) -> Float
/// \forall x \in \[-1, 1\], \\; \sin^{-1}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// 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). /// value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\) (an angle in
/// If the input value is outside the domain of the function an error is returned. /// radians). If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -232,8 +232,9 @@ fn do_asin(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \sinh^{-1}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
/// a numeric value $$y$$ that lies in the range $$\(-\infty, +\infty\)$$ (an angle in radians). /// returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, +\infty\)\\) (an angle in
/// radians).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -270,11 +271,12 @@ fn do_asinh(a: Float) -> Float
/// The inverse tangent function: /// 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 /// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
/// a numeric value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians). /// returns a numeric value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\)
/// (an angle in radians).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -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 /// 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 /// origin \\(\(0, 0\)\\) and a point given as input with coordinates \\(\(x, y\)\\). The numeric
/// returned by $$\text{atan2}(y, x)$$ is in the range $$\[-\pi, \pi\]$$. /// value returned by \\(\text{atan2}(y, x)\\) is in the range \\(\[-\pi, \pi\]\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -364,8 +366,8 @@ fn do_atan2(a: Float, b: Float) -> Float
/// \forall x \in \(-1, 1\), \\; \tanh^{-1}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// 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). /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -419,8 +421,8 @@ fn do_atanh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \cos{(x)} = y \in \[-1, 1\] /// \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) /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -463,9 +465,9 @@ fn do_cos(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \cosh{(x)} = y \in \(-\infty, +\infty\) /// \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) /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle
/// and returns a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. /// in radians) and returns a numeric value \\(y\\) that lies in the range
/// If the input value is too large an overflow error might occur. /// \\(\(-\infty, \infty\)\\). If the input value is too large an overflow error might occur.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -505,8 +507,8 @@ fn do_cosh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \sin{(x)} = y \in \[-1, 1\] /// \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) /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -549,10 +551,9 @@ fn do_sin(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \sinh{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(-\infty, +\infty\), \\; \sinh{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input /// 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 /// (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 /// \\(\(-\infty, +\infty\)\\). If the input value is too large an overflow error might occur.
/// occur.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -592,9 +593,9 @@ fn do_sinh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \tan{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(-\infty, +\infty\), \\; \tan{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input /// 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 /// (an angle in radians) and returns a numeric value \\(y\\) that lies in the range
/// $$\(-\infty, +\infty\)$$. /// \\(\(-\infty, +\infty\)\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -634,8 +635,8 @@ fn do_tan(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \tanh{(x)} = y \in \[-1, 1\] /// \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) /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle
/// and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// in radians) and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -681,9 +682,9 @@ fn do_tanh(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; e^{(x)} = y \in \(0, +\infty\) /// \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.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
@ -723,8 +724,8 @@ fn do_exponential(a: Float) -> Float
/// \forall x \in \(0, \infty\), \\; \log_{e}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// 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\)$$. /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -773,14 +774,14 @@ fn do_natural_logarithm(a: Float) -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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\) /// \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 /// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) and a base \\(b > 1\\)
/// a numeric value $$y$$ that lies in the range $$\(-\infty, \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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -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\) /// \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 /// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns a
/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -903,8 +904,8 @@ fn do_logarithm_2(a: Float) -> Float
/// \forall x \in \(0, \infty), \\; \log_{10}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns a
/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -958,14 +959,14 @@ fn do_logarithm_10(a: Float) -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The exponentiation function: $$y = x^{a}$$. /// The exponentiation function: \\(y = x^{a}\\).
/// ///
/// Note that the function is not defined if: /// Note that the function is not defined if:
/// 1. The base is negative ($$x < 0$$) and the exponent is fractional /// 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 /// (\\(a = \frac{n}{m}\\) is an irrreducible fraction). An error will be returned
/// as an imaginary number will otherwise have to 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 /// 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 /// expression is equivalent to the exponent \\(y\\) divided by \\(0\\) and an
/// error will have to be returned as the expression is otherwise undefined. /// error will have to be returned as the expression is otherwise undefined.
/// ///
/// <details> /// <details>
@ -1024,10 +1025,10 @@ fn do_ceiling(a: Float) -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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: /// 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. /// as an imaginary number will otherwise have to be returned.
/// ///
/// <details> /// <details>
@ -1076,10 +1077,10 @@ pub fn square_root(x: Float) -> Result(Float, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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: /// 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. /// as an imaginary number will otherwise have to be returned.
/// ///
/// <details> /// <details>
@ -1128,10 +1129,10 @@ pub fn cube_root(x: Float) -> Result(Float, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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: /// 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. /// as an imaginary number will otherwise have to be returned.
/// ///
/// <details> /// <details>
@ -1189,7 +1190,7 @@ pub fn nth_root(x: Float, n: Int) -> Result(Float, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The mathematical constant pi: $$\pi \approx 3.1415\dots$$ /// The mathematical constant pi: \\(\pi \approx 3.1415\dots\\)
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -1211,7 +1212,7 @@ fn do_pi() -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The mathematical constant tau: $$\tau = 2 \cdot \pi \approx 6.283\dots$$ /// The mathematical constant tau: \\(\tau = 2 \cdot \pi \approx 6.283\dots\\)
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -1229,7 +1230,7 @@ pub fn tau() -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Euler's number $$e \approx 2.71828\dots$$. /// Euler's number \\(e \approx 2.71828\dots\\).
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -126,15 +126,15 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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}} /// \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 /// 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 /// 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). /// a corresponding positive weight (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -246,10 +246,10 @@ pub fn norm(
/// \sum_{i=1}^n w_{i} \left|x_i - y_i \right| /// \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 /// 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 /// values in the respective input lists indexed by \\(i\\), while the
/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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}} /// \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 /// 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$$. /// 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 /// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// The Minkowski distance is a generalization of both the Euclidean distance /// 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\\)).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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}} /// \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 /// 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 /// values in the respective input lists indexed by \\(i\\), while the
/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -455,8 +455,8 @@ pub fn euclidean_distance(
/// \text{max}_{i=1}^n \left|x_i - y_i \right| /// \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 /// 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$$. /// values in the respective input lists indexed by \\(i\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -517,8 +517,8 @@ pub fn chebyshev_distance(
/// \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i /// \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$$ /// 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$$. /// is the sample point in the input list indexed by \\(i\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -637,11 +637,11 @@ fn do_median(
/// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x}) /// 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$$ /// 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$$. /// is the sample point in the input list indexed by \\(i\\).
/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta /// 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 /// 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. /// estimate of the sample variance. Setting \\(d = 1\\) gives an unbiased estimate.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -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}} /// 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$$ /// 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$$. /// is the sample point in the input list indexed by \\(i\\).
/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta /// 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 /// 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 of the sample standard deviation. Setting \\(d = 1\\) gives an unbiased
/// estimate. /// estimate.
/// ///
/// <details> /// <details>
@ -785,14 +785,14 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String)
/// ///
/// where: /// where:
/// ///
/// - $$X$$ and $$Y$$ are two sets being compared, /// - \\(X\\) and \\(Y\\) are two sets being compared,
/// - $$|X \cap Y|$$ represents the size of the intersection of the two sets /// - \\(|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 \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 /// 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 /// 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 /// Jaccard index is a special case of the [Tversky index](#tversky_index) (with
/// $$\alpha=\beta=1$$). /// \\(\alpha=\beta=1\\)).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -835,16 +835,16 @@ pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// \\] /// \\]
/// ///
/// where: /// where:
/// - $$X$$ and $$Y$$ are two sets being compared /// - \\(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 \cap Y|\\) is the size of the intersection of the two sets (i.e., the
/// number of elements common to both sets) /// 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 /// 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). /// 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 higher the coefficient, the greater the similarity between the two sets.
/// The Sørensen-Dice coefficient is a special case of the /// 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\\)).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -880,8 +880,8 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// </div> /// </div>
/// ///
/// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice /// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice
/// coefficient, which adds flexibility through two parameters, $$\alpha$$ and /// coefficient, which adds flexibility through two parameters, \\(\alpha\\) and
/// $$\beta$$, allowing for asymmetric similarity measures between sets. The /// \\(\beta\\), allowing for asymmetric similarity measures between sets. The
/// Tversky index is defined as: /// Tversky index is defined as:
/// ///
/// \\[ /// \\[
@ -890,18 +890,18 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// ///
/// where: /// where:
/// ///
/// - $$X$$ and $$Y$$ are the sets being compared /// - \\(X\\) and \\(Y\\) are the sets being compared
/// - $$|X - Y|$$ and $$|Y - X|$$ are the sizes of the relative complements of /// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of
/// $$Y$$ in $$X$$ and $$X$$ in $$Y$$, respectively, /// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively,
/// - $$\alpha$$ and $$\beta$$ are parameters that weigh the relative importance /// - \\(\alpha\\) and \\(\beta\\) are parameters that weigh the relative importance
/// of the elements unique to $$X$$ and $$Y$$ /// of the elements unique to \\(X\\) and \\(Y\\)
/// ///
/// The Tversky index reduces to the Jaccard index when $$\alpha = \beta = 1$$ and /// 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 /// 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 /// 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. /// 0 when there is no intersection between the two sets, indicating no similarity.
/// However, unlike similarity measures bounded strictly between 0 and 1, the /// 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\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -982,9 +982,9 @@ pub fn tversky_index(
/// ///
/// where: /// where:
/// ///
/// - $$X$$ and $$Y$$ are the sets being compared /// - \\(X\\) and \\(Y\\) are the sets being compared
/// - $$|X \cap Y|$$ is the size of the intersection of the sets /// - \\(|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$$ /// - \\(\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 /// 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 /// 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 \\; \left[-1, 1\right]
/// \\] /// \\]
/// ///
/// In the formula, $$n$$ is the length of the two lists and $$x_i$$, $$y_i$$ are /// 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 /// the values in the respective input lists indexed by \\(i\\), while the
/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// The cosine similarity provides a value between -1 and 1, where 1 means the /// 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 /// 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|} /// {\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 /// 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 /// values in the respective input lists indexed by \\(i\\), while the
/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -1231,12 +1231,12 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
/// {\sum_{i=1}^n w_{i}\left| x_i + y_i \right|} /// {\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 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 /// in the respective input lists indexed by \\(i\\), while the
/// $$w_i \in \mathbb{R}_{+}$$ are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// ($$w_i = 1.0\\;\forall i=1...n$$ by default). /// (\\(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. /// positive.
/// ///
/// <details> /// <details>

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -66,24 +66,26 @@ import gleam_community/maths/elementary
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example, $$12.0654$$ is rounded to: /// For example, \\(12.0654\\) is rounded to:
/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(13.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) /// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// 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: /// For example, \\(12.0654\\) is rounded to:
/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) /// - \\(20.0\\) for 1 digit places before the decimal point (`digit = -1`)
/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(100.0\\) for 2 digits before the decimal point (`digits = -2`)
/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(1000.0\\) for 3 digits before the decimal point (`digits = -3`)
/// ///
/// </details> /// </details>
/// ///
@ -122,23 +124,26 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example, $$12.0654$$ is rounded to: /// For example, \\(12.0654\\) is rounded to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) /// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
/// ///
/// </details> /// </details>
/// ///
@ -177,23 +182,27 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example, $$12.0654$$ is rounded to: /// For example, \\(12.0654\\) is rounded to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) /// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(0.0\\) for 2 digits before the decimal point (`digits = -2`)
/// - \\(0.0\\) for 3 digits before the decimal point (`digits = -3`)
/// ///
/// </details> /// </details>
/// ///
@ -232,84 +241,103 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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: /// 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. /// - `RoundNearest` (default): The input \\(x\\) is rounded to the nearest integer value (at the
/// - `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). /// specified digit) with ties (fractional values of 0.5) being rounded to the nearest even
/// - `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). /// integer.
/// - `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). /// - `RoundTiesAway`: The input \\(x\\) is rounded to the nearest integer value (at the
/// - `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). /// specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++
/// - `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). /// 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> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// The `RoundNearest` rounding mode, rounds $$12.0654$$ to: /// The `RoundNearest` rounding mode, rounds \\(12.0654\\) to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) /// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(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: /// The `RoundTiesAway` rounding mode, rounds \\(12.0654\\) to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) /// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(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: /// The `RoundTiesUp` rounding mode, rounds \\(12.0654\\) to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.1$$ for 1 digits after the decimal point (`digits = 1`) /// - \\(12.1\\) for 1 digits after the decimal point (`digits = 1`)
/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(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: /// The `RoundToZero` rounding mode, rounds \\(12.0654\\) to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.0$$ for 1 digit after the decimal point (`digits = 1`) /// - \\(12.0\\) for 1 digit after the decimal point (`digits = 1`)
/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(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: /// The `RoundDown` rounding mode, rounds \\(12.0654\\) to:
/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(12.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) /// - \\(12.0\\) for 1 digits after the decimal point (`digits = 1`)
/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) /// number refers to the digits before the decimal point.
/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`)
/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(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: /// The `RoundUp` rounding mode, rounds \\(12.0654\\) to:
/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) /// - \\(13.0\\) for 0 digits after the decimal point (`digits = 0`)
/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) /// - \\(12.1\\) for 1 digit after the decimal point (`digits = 1`)
/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) /// - \\(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. /// It is also possible to specify a negative number of digits. In that case, the negative
/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) /// number refers to the digits before the decimal point.
/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) /// - \\(20.0\\) for 1 digit places before the decimal point (`digit = -1`)
/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) /// - \\(100.0\\) for 2 digits before the decimal point (`digits = -2`)
/// - \\(1000.0\\) for 3 digits before the decimal point (`digits = -3`)
/// ///
/// </details> /// </details>
/// ///
@ -475,7 +503,7 @@ fn do_ceiling(a: Float) -> Float
/// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. /// \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.
/// ///
/// </details> /// </details>
/// ///
@ -504,7 +532,7 @@ pub fn float_absolute_value(x: Float) -> Float {
/// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. /// \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.
/// ///
/// </details> /// </details>
/// ///
@ -533,7 +561,7 @@ pub fn int_absolute_value(x: Int) -> Int {
/// \forall x, y \in \mathbb{R}, \\; |x - y| \in \mathbb{R}_{+}. /// \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. /// value which is the the absolute difference of the inputs.
/// ///
/// <details> /// <details>
@ -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}_{+}. /// \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. /// value which is the the absolute difference of the inputs.
/// ///
/// <details> /// <details>
@ -609,7 +637,7 @@ pub fn int_absolute_difference(a: Int, b: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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 /// the input, indicating whether it is positive (+1.0), negative (-1.0), or
/// zero (0.0). /// zero (0.0).
/// ///
@ -645,7 +673,7 @@ fn do_float_sign(a: Float) -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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 /// the input, indicating whether it is positive (+1), negative (-1), or zero
/// (0). /// (0).
/// ///
@ -681,8 +709,8 @@ fn do_int_sign(a: Int) -> Int
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function takes two arguments $$x, y \in \mathbb{R}$$ and returns $$x$$ /// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\)
/// such that it has the same sign as $$y$$. /// such that it has the same sign as \\(y\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -707,8 +735,8 @@ pub fn float_copy_sign(x: Float, y: Float) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function takes two arguments $$x, y \in \mathbb{Z}$$ and returns $$x$$ /// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\)
/// such that it has the same sign as $$y$$. /// such that it has the same sign as \\(y\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -733,7 +761,7 @@ pub fn int_copy_sign(x: Int, y: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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}\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -751,7 +779,7 @@ pub fn float_flip_sign(x: Float) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// 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}\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
/// <a href="#"> /// <a href="#">
@ -769,8 +797,8 @@ pub fn int_flip_sign(x: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The minimum function takes two arguments $$x, y$$ along with a function /// The minimum function takes two arguments \\(x, y\\) along with a function
/// for comparing $$x, y$$. The function returns the smallest of the two given /// for comparing \\(x, y\\). The function returns the smallest of the two given
/// values. /// values.
/// ///
/// <details> /// <details>
@ -815,8 +843,8 @@ pub fn minimum(x: a, y: a, compare: fn(a, a) -> order.Order) -> a {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The maximum function takes two arguments $$x, y$$ along with a function /// The maximum function takes two arguments \\(x, y\\) along with a function
/// for comparing $$x, y$$. The function returns the largest of the two given /// for comparing \\(x, y\\). The function returns the largest of the two given
/// values. /// values.
/// ///
/// <details> /// <details>
@ -855,8 +883,8 @@ pub fn maximum(x: a, y: a, compare: fn(a, a) -> order.Order) -> a {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The minmax function takes two arguments $$x, y$$ along with a function /// The minmax function takes two arguments \\(x, y\\) along with a function
/// for comparing $$x, y$$. The function returns a tuple with the smallest /// for comparing \\(x, y\\). The function returns a tuple with the smallest
/// value first and largest second. /// value first and largest second.
/// ///
/// <details> /// <details>

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -54,8 +54,8 @@ import gleam_community/maths/piecewise
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Determine if a given value $$a$$ is close to or equivalent to a reference value /// Determine if a given value \\(a\\) is close to or equivalent to a reference value
/// $$b$$ based on supplied relative $$r_{tol}$$ and absolute $$a_{tol}$$ tolerance /// \\(b\\) based on supplied relative \\(r_{tol}\\) and absolute \\(a_{tol}\\) tolerance
/// values. The equivalance of the two given values are then determined based on /// values. The equivalance of the two given values are then determined based on
/// the equation: /// the equation:
/// ///
@ -221,8 +221,8 @@ fn do_ceiling(a: Float) -> Float
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
/// power of another integer value $$y \in \mathbb{Z}$$. /// power of another integer value \\(y \in \mathbb{Z}\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -261,7 +261,7 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value $$n \in \mathbb{Z}$$ is a /// A function that tests whether a given integer value \\(n \in \mathbb{Z}\\) is a
/// perfect number. A number is perfect if it is equal to the sum of its proper /// perfect number. A number is perfect if it is equal to the sum of its proper
/// positive divisors. /// positive divisors.
/// ///
@ -269,8 +269,8 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example: /// For example:
/// - $$6$$ is a perfect number since the divisors of 6 are $$1 + 2 + 3 = 6$$. /// - \\(6\\) is a perfect number since the divisors of 6 are \\(1 + 2 + 3 = 6\\).
/// - $$28$$ is a perfect number since the divisors of 28 are $$1 + 2 + 4 + 7 + 14 = 28$$ /// - \\(28\\) is a perfect number since the divisors of 28 are \\(1 + 2 + 4 + 7 + 14 = 28\\).
/// ///
/// </details> /// </details>
/// ///
@ -314,7 +314,7 @@ fn do_sum(arr: List(Int)) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is even. /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -347,7 +347,7 @@ pub fn is_even(x: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is odd. /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -380,11 +380,11 @@ pub fn is_odd(x: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
/// prime number. A prime number is a natural number greater than 1 that has no /// prime number. A prime number is a natural number greater than 1 that has no
/// positive divisors other than 1 and itself. /// positive divisors other than 1 and itself.
/// ///
/// The function uses the Miller-Rabin primality test to assess if $$x$$ is prime. /// The function uses the Miller-Rabin primality test to assess if \\(x\\) is prime.
/// It is a probabilistic test, so it can mistakenly identify a composite number /// It is a probabilistic test, so it can mistakenly identify a composite number
/// as prime. However, the probability of such errors decreases with more testing /// as prime. However, the probability of such errors decreases with more testing
/// iterations (the function uses 64 iterations internally, which is typically /// iterations (the function uses 64 iterations internally, which is typically
@ -395,9 +395,10 @@ pub fn is_odd(x: Int) -> Bool {
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// Examples of prime numbers: /// Examples of prime numbers:
/// - $$2$$ is a prime number since it has only two divisors: $$1$$ and $$2$$. /// - \\(2\\) is a prime number since it has only two divisors: \\(1\\) and \\(2\\).
/// - $$7$$ is a prime number since it has only two divisors: $$1$$ and $$7$$. /// - \\(7\\) is a prime number since it has only two divisors: \\(1\\) and \\(7\\).
/// - $$4$$ is not a prime number since it has divisors other than $$1$$ and itself, such as $$2$$. /// - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such
/// as \\(2\\).
/// ///
/// </details> /// </details>
/// ///
@ -474,8 +475,8 @@ fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given real number $$x \in \mathbb{R}$$ is strictly /// A function that tests whether a given real number \\(x \in \mathbb{R}\\) is strictly
/// between two other real numbers, $$a,b \in \mathbb{R}$$, such that $$a < x < b$$. /// between two other real numbers, \\(a,b \in \mathbb{R}\\), such that \\(a < x < b\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -511,15 +512,15 @@ pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer $$n \in \mathbb{Z}$$ is divisible by another /// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another
/// integer $$d \in \mathbb{Z}$$, such that $$n \mod d = 0$$. /// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\).
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example: /// For example:
/// - $$n = 10$$ is divisible by $$d = 2$$ because $$10 \mod 2 = 0$$. /// - \\(n = 10\\) is divisible by \\(d = 2\\) because \\(10 \mod 2 = 0\\).
/// - $$n = 7$$ is not divisible by $$d = 3$$ because $$7 \mod 3 \neq 0$$. /// - \\(n = 7\\) is not divisible by \\(d = 3\\) because \\(7 \mod 3 \neq 0\\).
/// ///
/// </details> /// </details>
/// ///
@ -554,15 +555,16 @@ pub fn is_divisible(n: Int, d: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer $$m \in \mathbb{Z}$$ is a multiple of another /// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another
/// integer $$k \in \mathbb{Z}$$, such that $$m = k \times q \quad q \in \mathbb{Z}$$. /// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \times q\\), with \\(q \in \mathbb{Z}\\).
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// For example: /// For example:
/// - $$m = 15$$ is a multiple of $$k = 5$$ because $$15 = 5 \times 3$$. /// - \\(m = 15\\) is a multiple of \\(k = 5\\) because \\(15 = 5 \times 3\\).
/// - $$m = 14$$ is not a multiple of $$k = 5$$ because $$14 \div 5$$ does not yield an integer quotient. /// - \\(m = 14\\) is not a multiple of \\(k = 5\\) because \\(14 \div 5\\) does not yield an
/// integer quotient.
/// ///
/// </details> /// </details>
/// ///

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -33,7 +33,7 @@
//// * [`geometric_space`](#geometric_space) //// * [`geometric_space`](#geometric_space)
//// ////
import gleam/list import gleam/iterator
import gleam_community/maths/conversion import gleam_community/maths/conversion
import gleam_community/maths/elementary import gleam_community/maths/elementary
import gleam_community/maths/piecewise import gleam_community/maths/piecewise
@ -44,29 +44,32 @@ import gleam_community/maths/piecewise
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns a list with evenly spaced values within a given interval /// The function returns an iterator generating evenly spaced values within a given interval.
/// based on a start, stop value and a given increment (step-length) between /// based on a start value but excludes the stop value. The spacing between values is determined
/// consecutive values. The list returned includes the given start value but /// by the step size provided. The function supports both positive and negative step values.
/// excludes the stop value. ///
///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/iterator
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/sequences /// import gleam_community/maths/sequences
/// ///
/// pub fn example () { /// pub fn example () {
/// sequences.arange(1.0, 5.0, 1.0) /// sequences.arange(1.0, 5.0, 1.0)
/// |> iterator.to_list()
/// |> should.equal([1.0, 2.0, 3.0, 4.0]) /// |> should.equal([1.0, 2.0, 3.0, 4.0])
/// ///
/// // No points returned since /// // No points returned since
/// // start smaller than stop and positive step /// // start is smaller than stop and the step is positive
/// sequences.arange(5.0, 1.0, 1.0) /// sequences.arange(5.0, 1.0, 1.0)
/// |> iterator.to_list()
/// |> should.equal([]) /// |> should.equal([])
/// ///
/// // Points returned since /// // Points returned since
/// // start smaller than stop but negative step /// // start smaller than stop but negative step
/// sequences.arange(5.0, 1.0, -1.0) /// sequences.arange(5.0, 1.0, -1.0)
/// |> iterator.to_list()
/// |> should.equal([5.0, 4.0, 3.0, 2.0]) /// |> should.equal([5.0, 4.0, 3.0, 2.0])
/// } /// }
/// </details> /// </details>
@ -77,19 +80,29 @@ import gleam_community/maths/piecewise
/// </a> /// </a>
/// </div> /// </div>
/// ///
pub fn arange(start: Float, stop: Float, step: Float) -> List(Float) { pub fn arange(
start: Float,
stop: Float,
step: Float,
) -> iterator.Iterator(Float) {
case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 { case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 {
True -> [] True -> iterator.empty()
False -> { False -> {
let direction: Float = case start <=. stop { let direction = case start <=. stop {
True -> 1.0 True -> {
False -> -1.0 1.0
}
False -> {
-1.0
}
} }
let step_abs: Float = piecewise.float_absolute_value(step) let step_abs = piecewise.float_absolute_value(step)
let num: Float = piecewise.float_absolute_value(start -. stop) /. step_abs let num =
piecewise.float_absolute_value(start -. stop) /. step_abs
|> conversion.float_to_int()
list.range(0, conversion.float_to_int(num) - 1) iterator.range(0, num - 1)
|> list.map(fn(i: Int) -> Float { |> iterator.map(fn(i: Int) {
start +. conversion.int_to_float(i) *. step_abs *. direction start +. conversion.int_to_float(i) *. step_abs *. direction
}) })
} }
@ -102,22 +115,30 @@ pub fn arange(start: Float, stop: Float, step: Float) -> List(Float) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Generate a linearly spaced list of points over a specified interval. The /// The function returns an iterator for generating linearly spaced points over a specified
/// endpoint of the interval can optionally be included/excluded. /// interval. The endpoint of the interval can optionally be included/excluded. The number of
/// /// points and whether the endpoint is included determine the spacing between values.
///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/iterator
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/elementary /// import gleam_community/maths/elementary
/// import gleam_community/maths/sequences /// import gleam_community/maths/sequences
/// import gleam_community/maths/predicates /// import gleam_community/maths/predicates
/// ///
/// pub fn example () { /// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0) /// let assert Ok(tol) = elementary.power(10.0, -6.0)
/// let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True) /// let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
/// let assert Ok(result) = /// let assert Ok(result) =
/// predicates.all_close(linspace, [10.0, 20.0, 30.0, 40.0, 50.0], 0.0, tol) /// predicates.all_close(
/// linspace |> iterator.to_list(),
/// [10.0, 12.5, 15.0, 17.5, 20.0],
/// 0.0,
/// tol,
/// )
///
/// result /// result
/// |> list.all(fn(x) { x == True }) /// |> list.all(fn(x) { x == True })
/// |> should.be_true() /// |> should.be_true()
@ -139,38 +160,32 @@ pub fn linear_space(
stop: Float, stop: Float,
num: Int, num: Int,
endpoint: Bool, endpoint: Bool,
) -> Result(List(Float), String) { ) -> Result(iterator.Iterator(Float), String) {
let direction: Float = case start <=. stop { let direction: Float = case start <=. stop {
True -> 1.0 True -> 1.0
False -> -1.0 False -> -1.0
} }
case num > 0 {
True ->
case endpoint {
True -> {
let increment: Float =
piecewise.float_absolute_value(start -. stop)
/. conversion.int_to_float(num - 1)
list.range(0, num - 1)
|> list.map(fn(i: Int) -> Float {
start +. conversion.int_to_float(i) *. increment *. direction
})
|> Ok
}
False -> {
let increment: Float =
piecewise.float_absolute_value(start -. stop)
/. conversion.int_to_float(num)
list.range(0, num - 1)
|> list.map(fn(i: Int) -> Float {
start +. conversion.int_to_float(i) *. increment *. direction
})
|> Ok
}
}
let increment = case endpoint {
True -> {
piecewise.float_absolute_value(start -. stop)
/. conversion.int_to_float(num - 1)
}
False -> {
piecewise.float_absolute_value(start -. stop)
/. conversion.int_to_float(num)
}
}
case num > 0 {
True -> {
iterator.range(0, num - 1)
|> iterator.map(fn(i: Int) -> Float {
start +. conversion.int_to_float(i) *. increment *. direction
})
|> Ok
}
False -> False ->
"Invalid input: num < 0. Valid input is num > 0." "Invalid input: num < 1. Valid input is num >= 1."
|> Error |> Error
} }
} }
@ -181,22 +196,29 @@ pub fn linear_space(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Generate a logarithmically spaced list of points over a specified interval. The /// The function returns an iterator of logarithmically spaced points over a specified interval.
/// endpoint of the interval can optionally be included/excluded. /// The endpoint of the interval can optionally be included/excluded. The number of points, base,
/// /// and whether the endpoint is included determine the spacing between values.
///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/iterator
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/elementary /// import gleam_community/maths/elementary
/// import gleam_community/maths/sequences /// import gleam_community/maths/sequences
/// import gleam_community/maths/predicates /// import gleam_community/maths/predicates
/// ///
/// pub fn example () { /// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0) /// let assert Ok(tol) = elementary.power(10.0, -6.0)
/// let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0) /// let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
/// let assert Ok(result) = /// let assert Ok(result) =
/// predicates.all_close(logspace, [10.0, 100.0, 1000.0], 0.0, tol) /// predicates.all_close(
/// logspace |> iterator.to_list(),
/// [10.0, 100.0, 1000.0],
/// 0.0,
/// tol,
/// )
/// result /// result
/// |> list.all(fn(x) { x == True }) /// |> list.all(fn(x) { x == True })
/// |> should.be_true() /// |> should.be_true()
@ -219,19 +241,19 @@ pub fn logarithmic_space(
num: Int, num: Int,
endpoint: Bool, endpoint: Bool,
base: Float, base: Float,
) -> Result(List(Float), String) { ) -> Result(iterator.Iterator(Float), String) {
case num > 0 { case num > 0 {
True -> { True -> {
let assert Ok(linspace) = linear_space(start, stop, num, endpoint) let assert Ok(linspace) = linear_space(start, stop, num, endpoint)
linspace linspace
|> list.map(fn(i: Float) -> Float { |> iterator.map(fn(i: Float) -> Float {
let assert Ok(result) = elementary.power(base, i) let assert Ok(result) = elementary.power(base, i)
result result
}) })
|> Ok |> Ok
} }
False -> False ->
"Invalid input: num < 0. Valid input is num > 0." "Invalid input: num < 1. Valid input is num >= 1."
|> Error |> Error
} }
} }
@ -242,25 +264,30 @@ pub fn logarithmic_space(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns a list of numbers spaced evenly on a log scale (a /// The function returns an iterator of numbers spaced evenly on a log scale (a geometric
/// geometric progression). Each point in the list is a constant multiple of the /// progression). Each point in the list is a constant multiple of the previous. The function is
/// previous. The function is similar to the /// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
/// [`logarithmic_space`](#logarithmic_space) function, but with endpoints
/// specified directly. /// specified directly.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
/// import gleam/iterator
/// import gleeunit/should /// import gleeunit/should
/// import gleam_community/maths/elementary /// import gleam_community/maths/elementary
/// import gleam_community/maths/sequences /// import gleam_community/maths/sequences
/// import gleam_community/maths/predicates /// import gleam_community/maths/predicates
/// ///
/// pub fn example () { /// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0) /// let assert Ok(tol) = elementary.power(10.0, -6.0)
/// let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True) /// let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
/// let assert Ok(result) = /// let assert Ok(result) =
/// predicates.all_close(logspace, [10.0, 100.0, 1000.0], 0.0, tol) /// predicates.all_close(
/// logspace |> iterator.to_list(),
/// [10.0, 100.0, 1000.0],
/// 0.0,
/// tol,
/// )
/// result /// result
/// |> list.all(fn(x) { x == True }) /// |> list.all(fn(x) { x == True })
/// |> should.be_true() /// |> should.be_true()
@ -289,7 +316,7 @@ pub fn geometric_space(
stop: Float, stop: Float,
num: Int, num: Int,
endpoint: Bool, endpoint: Bool,
) -> Result(List(Float), String) { ) -> Result(iterator.Iterator(Float), String) {
case start == 0.0 || stop == 0.0 { case start == 0.0 || stop == 0.0 {
True -> True ->
"Invalid input: Neither 'start' nor 'stop' can be zero, as they must be non-zero for logarithmic calculations." "Invalid input: Neither 'start' nor 'stop' can be zero, as they must be non-zero for logarithmic calculations."
@ -302,7 +329,7 @@ pub fn geometric_space(
logarithmic_space(log_start, log_stop, num, endpoint, 10.0) logarithmic_space(log_start, log_stop, num, endpoint, 10.0)
} }
False -> False ->
"Invalid input: num < 0. Valid input is num > 0." "Invalid input: num < 1. Valid input is num >= 1."
|> Error |> Error
} }
} }

View file

@ -1,6 +1,6 @@
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> ////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> ////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script> ////<script>
//// document.addEventListener("DOMContentLoaded", function() { //// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, { //// renderMathInElement(document.body, {
@ -8,12 +8,12 @@
//// // auto-render specific keys, e.g.: //// // auto-render specific keys, e.g.:
//// delimiters: [ //// delimiters: [
//// {left: '$$', right: '$$', display: false}, //// {left: '$$', right: '$$', display: false},
//// // {left: '$', right: '$', display: false}, //// {left: '$', right: '$', display: false},
//// // {left: '\\(', right: '\\)', display: false}, //// {left: '\\(', right: '\\)', display: false},
//// {left: '\\[', right: '\\]', display: true} //// {left: '\\[', right: '\\]', display: true}
//// ], //// ],
//// // rendering keys, e.g.: //// // rendering keys, e.g.:
//// throwOnError : false //// throwOnError : true
//// }); //// });
//// }); //// });
////</script> ////</script>
@ -101,7 +101,7 @@ pub fn erf(x: Float) -> Float {
/// </div> /// </div>
/// ///
/// The gamma function over the real numbers. The function is essentially equal to /// The gamma function over the real numbers. The function is essentially equal to
/// the factorial for any positive integer argument: $$\Gamma(n) = (n - 1)!$$ /// the factorial for any positive integer argument: \\(\Gamma(n) = (n - 1)!\\)
/// ///
/// The implemented gamma function is approximated through Lanczos approximation /// The implemented gamma function is approximated through Lanczos approximation
/// using the same coefficients used by the GNU Scientific Library. /// using the same coefficients used by the GNU Scientific Library.

View file

@ -1,14 +1,16 @@
import gleam/iterator
import gleam/list import gleam/list
import gleam/option
import gleam/set import gleam/set
import gleam_community/maths/combinatorics import gleam_community/maths/combinatorics
import gleeunit/should import gleeunit/should
pub fn int_factorial_test() { pub fn int_factorial_test() {
// Invalid input gives an error // Invalid input gives an error (factorial of negative number)
combinatorics.factorial(-1) combinatorics.factorial(-1)
|> should.be_error() |> should.be_error()
// Valid input returns a result // Valid inputs for factorial of small numbers
combinatorics.factorial(0) combinatorics.factorial(0)
|> should.equal(Ok(1)) |> should.equal(Ok(1))
@ -26,56 +28,120 @@ pub fn int_factorial_test() {
} }
pub fn int_combination_test() { pub fn int_combination_test() {
// Invalid input gives an error // Invalid input: k < 0 should return an error
// Error on: n = -1 < 0 combinatorics.combination(1, -1, option.None)
combinatorics.combination(-1, 1)
|> should.be_error() |> should.be_error()
// Valid input returns a result // Invalid input: n < 0 should return an error
combinatorics.combination(4, 0) combinatorics.combination(-1, 1, option.None)
|> should.be_error()
// Valid input: k > n without repetition gives 0 combinations
combinatorics.combination(2, 3, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(0))
// Valid input: k > n with repetition allowed
combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(4))
// Valid input: zero combinations (k=0) should always yield 1 combination
combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(1)) |> should.equal(Ok(1))
combinatorics.combination(4, 4) combinatorics.combination(4, 0, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(1)) |> should.equal(Ok(1))
combinatorics.combination(4, 2) // Valid input: k = n without repetition gives 1 combination
combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(1))
// Valid input: k = n with repetition allows more combinations
combinatorics.combination(4, 4, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(35))
// Valid input: k < n without and with repetition
combinatorics.combination(4, 2, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(6)) |> should.equal(Ok(6))
combinatorics.combination(7, 5) combinatorics.combination(4, 2, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(10))
// Valid input with larger values of n and k
combinatorics.combination(7, 5, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(21)) |> should.equal(Ok(21))
// NOTE: Tests with the 'combination' function that produce values that
// exceed precision of the JavaScript 'Number' primitive will result in combinatorics.combination(7, 5, option.Some(combinatorics.WithRepetitions))
// errors |> should.equal(Ok(462))
// NOTE: Tests with the 'combination' function that produce values that exceed
// precision of the JavaScript 'Number' primitive will result in errors
} }
pub fn math_permutation_test() { pub fn math_permutation_test() {
// Invalid input gives an error // Invalid input: k < 0 should return an error
// Error on: n = -1 < 0 combinatorics.permutation(1, -1, option.None)
combinatorics.permutation(-1, 1)
|> should.be_error() |> should.be_error()
// Valid input returns a result // Invalid input: n < 0 should return an error
combinatorics.permutation(4, 0) combinatorics.permutation(-1, 1, option.None)
|> should.be_error()
// Valid input: k > n without repetition gives 0 permutations
combinatorics.permutation(2, 3, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(0))
// Valid input: k > n with repetition allowed gives non-zero permutations
combinatorics.permutation(2, 3, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(8))
// Valid input: k = 0 should always yield 1 permutation
combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(1)) |> should.equal(Ok(1))
combinatorics.permutation(4, 4) combinatorics.permutation(4, 0, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(1)) |> should.equal(Ok(1))
combinatorics.permutation(4, 2) // Valid input: k = n permutations without repetition
combinatorics.permutation(4, 4, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(24))
// Valid input: k = n permutations with repetition
combinatorics.permutation(4, 4, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(256))
// Valid input: k < n permutations without and with repetition
combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(12)) |> should.equal(Ok(12))
combinatorics.permutation(4, 2, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(16))
// Valid input with larger values of n and k
combinatorics.permutation(6, 2, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(30))
combinatorics.permutation(6, 2, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(36))
combinatorics.permutation(6, 3, option.Some(combinatorics.WithoutRepetitions))
|> should.equal(Ok(120))
combinatorics.permutation(6, 3, option.Some(combinatorics.WithRepetitions))
|> should.equal(Ok(216))
} }
pub fn list_cartesian_product_test() { pub fn list_cartesian_product_test() {
// An empty lists returns an empty list // An empty list returns an empty list as the Cartesian product
[] let xset = set.from_list([])
|> combinatorics.cartesian_product([]) let yset = set.from_list([])
|> should.equal([]) let expected_result = set.from_list([])
xset
|> combinatorics.cartesian_product(yset)
|> should.equal(expected_result)
// Test with some arbitrary inputs // Cartesian product of two sets with the same elements
[1, 2, 3] let xset = set.from_list([1, 2, 3])
|> combinatorics.cartesian_product([1, 2, 3]) let yset = set.from_list([1, 2, 3])
|> set.from_list() let expected_result =
|> should.equal(
set.from_list([ set.from_list([
#(1, 1), #(1, 1),
#(1, 2), #(1, 2),
@ -86,38 +152,140 @@ pub fn list_cartesian_product_test() {
#(3, 1), #(3, 1),
#(3, 2), #(3, 2),
#(3, 3), #(3, 3),
]), ])
) xset
|> combinatorics.cartesian_product(yset)
|> should.equal(expected_result)
[1.0, 10.0] // Cartesian product with floating-point numbers
|> combinatorics.cartesian_product([1.0, 2.0]) let xset = set.from_list([1.0, 10.0])
|> set.from_list() let yset = set.from_list([1.0, 2.0])
|> should.equal( let expected_result =
set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]), set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)])
) xset
|> combinatorics.cartesian_product(yset)
|> should.equal(expected_result)
// Cartesian product of sets with different sizes
let xset = set.from_list([1.0, 10.0, 100.0])
let yset = set.from_list([1.0, 2.0])
let expected_result =
set.from_list([
#(1.0, 1.0),
#(1.0, 2.0),
#(10.0, 1.0),
#(10.0, 2.0),
#(100.0, 1.0),
#(100.0, 2.0),
])
xset
|> combinatorics.cartesian_product(yset)
|> should.equal(expected_result)
// Cartesian product with different types (strings)
let xset = set.from_list(["a", "y", "z"])
let yset = set.from_list(["a", "x"])
let expected_result =
set.from_list([
#("a", "a"),
#("a", "x"),
#("y", "a"),
#("y", "x"),
#("z", "a"),
#("z", "x"),
])
xset
|> combinatorics.cartesian_product(yset)
|> should.equal(expected_result)
} }
pub fn list_permutation_test() { pub fn list_permutation_test() {
// An empty lists returns one (empty) permutation // Invalid input: k < 0 should return an error for an empty list
[] []
|> combinatorics.list_permutation() |> combinatorics.list_permutation(-1, option.None)
|> should.be_error()
// Invalid input: k > n should return an error without repetition
[1, 2]
|> combinatorics.list_permutation(
3,
option.Some(combinatorics.WithoutRepetitions),
)
|> should.be_error()
// Valid input: An empty list returns a single empty permutation
let assert Ok(permutations) =
[]
|> combinatorics.list_permutation(0, option.None)
permutations
|> iterator.to_list()
|> should.equal([[]]) |> should.equal([[]])
// Singleton returns one (singleton) permutation // Singleton list returns a single permutation regardless of repetition settings
// Also works regardless of type of list elements let assert Ok(permutations) =
["a"] ["a"]
|> combinatorics.list_permutation() |> combinatorics.list_permutation(
1,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> should.equal([["a"]]) |> should.equal([["a"]])
// Test with some arbitrary inputs let assert Ok(permutations) =
[1, 2] ["a"]
|> combinatorics.list_permutation() |> combinatorics.list_permutation(
1,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> should.equal([["a"]])
// 4-permutations of a single element repeats it 4 times
let assert Ok(permutations) =
["a"]
|> combinatorics.list_permutation(
4,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> should.equal([["a", "a", "a", "a"]])
// 2-permutations of [1, 2] without repetition
let assert Ok(permutations) =
[1, 2]
|> combinatorics.list_permutation(
2,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> set.from_list() |> set.from_list()
|> should.equal(set.from_list([[1, 2], [2, 1]])) |> should.equal(set.from_list([[1, 2], [2, 1]]))
// Test with some arbitrary inputs // 2-permutations of [1, 2] with repetition
[1, 2, 3] let assert Ok(permutations) =
|> combinatorics.list_permutation() [1, 2]
|> combinatorics.list_permutation(
2,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> set.from_list()
|> should.equal(set.from_list([[1, 1], [1, 2], [2, 2], [2, 1]]))
// 3-permutations of [1, 2, 3] without repetition
let assert Ok(permutations) =
[1, 2, 3]
|> combinatorics.list_permutation(
3,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> set.from_list() |> set.from_list()
|> should.equal( |> should.equal(
set.from_list([ set.from_list([
@ -130,57 +298,403 @@ pub fn list_permutation_test() {
]), ]),
) )
// Repeated elements are treated as distinct for the // 3-permutations of [1, 2, 3] with repetition
// purpose of permutations, so two identical elements let assert Ok(permutations) =
// will appear "both ways round" [1, 2, 3]
[1.0, 1.0] |> combinatorics.list_permutation(
|> combinatorics.list_permutation() 3,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> set.from_list()
|> should.equal(
set.from_list([
[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2],
[1, 3, 3],
[2, 1, 1],
[2, 1, 2],
[2, 1, 3],
[2, 2, 1],
[2, 2, 2],
[2, 2, 3],
[2, 3, 1],
[2, 3, 2],
[2, 3, 3],
[3, 1, 1],
[3, 1, 2],
[3, 1, 3],
[3, 2, 1],
[3, 2, 2],
[3, 2, 3],
[3, 3, 1],
[3, 3, 2],
[3, 3, 3],
]),
)
// Repeated elements are treated as distinct in permutations without repetition
let assert Ok(permutations) =
[1.0, 1.0]
|> combinatorics.list_permutation(
2,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> should.equal([[1.0, 1.0], [1.0, 1.0]]) |> should.equal([[1.0, 1.0], [1.0, 1.0]])
// This means lists with repeated elements return the // Repeated elements allow more possibilities when repetition is allowed
// same number of permutations as ones without let assert Ok(permutations) =
["l", "e", "t", "t", "e", "r", "s"] [1.0, 1.0]
|> combinatorics.list_permutation() |> combinatorics.list_permutation(
2,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> should.equal([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]])
// Large inputs: Ensure the correct number of permutations is generated
let assert Ok(permutations) =
["a", "b", "c", "d", "e"]
|> combinatorics.list_permutation(
5,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> list.length() |> list.length()
|> should.equal(5040) |> should.equal(120)
let assert Ok(permutations) =
["a", "b", "c", "d", "e"]
|> combinatorics.list_permutation(
5,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> list.length()
|> should.equal(3125)
}
pub fn permutation_alignment_test() {
// Test: Number of generated permutations should match the expected count
// Without repetitions
let arr = ["a", "b", "c", "d", "e", "f"]
let length = list.length(arr)
let assert Ok(number_of_permutations) =
combinatorics.permutation(
length,
length,
option.Some(combinatorics.WithoutRepetitions),
)
let assert Ok(permutations) =
arr
|> combinatorics.list_permutation(
length,
option.Some(combinatorics.WithoutRepetitions),
)
permutations
|> iterator.to_list()
|> list.length()
|> should.equal(number_of_permutations)
// With repetitions
let arr = ["a", "b", "c", "d", "e", "f"]
let length = list.length(arr)
let assert Ok(number_of_permutations) =
combinatorics.permutation(
length,
length,
option.Some(combinatorics.WithRepetitions),
)
let assert Ok(permutations) =
arr
|> combinatorics.list_permutation(
length,
option.Some(combinatorics.WithRepetitions),
)
permutations
|> iterator.to_list()
|> list.length()
|> should.equal(number_of_permutations)
} }
pub fn list_combination_test() { pub fn list_combination_test() {
// A negative number returns an error // Invalid input: k < 0 should return an error for an empty list
[] []
|> combinatorics.list_combination(-1) |> combinatorics.list_combination(-1, option.None)
|> should.be_error() |> should.be_error()
// k is larger than given input list returns an error // Invalid input: k > n should return an error without repetition
[1, 2] [1, 2]
|> combinatorics.list_combination(3) |> combinatorics.list_combination(
3,
option.Some(combinatorics.WithoutRepetitions),
)
|> should.be_error() |> should.be_error()
// An empty lists returns an empty list
[]
|> combinatorics.list_combination(0)
|> should.equal(Ok([[]]))
// Test with some arbitrary inputs // Valid input: k > n with repetition allowed
[1, 2] let assert Ok(combinations) =
|> combinatorics.list_combination(1) [1, 2]
|> should.equal(Ok([[1], [2]])) |> combinatorics.list_combination(
3,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]])
// Test with some arbitrary inputs // Valid input: Empty list should return a single empty combination
[1, 2] let assert Ok(combinations) =
|> combinatorics.list_combination(2) []
|> should.equal(Ok([[1, 2]])) |> combinatorics.list_combination(
0,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[]])
// Test with some arbitrary inputs let assert Ok(combinations) =
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 2) []
result |> combinatorics.list_combination(
0,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[]])
// 1-combination of [1, 2] without and with repetition
let assert Ok(combinations) =
[1, 2]
|> combinatorics.list_combination(
1,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1], [2]])
let assert Ok(combinations) =
[1, 2]
|> combinatorics.list_combination(
1,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1], [2]])
// 2-combination of [1, 2] without and with repetition
let assert Ok(combinations) =
[1, 2]
|> combinatorics.list_combination(
2,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1, 2]])
let assert Ok(combinations) =
[1, 2]
|> combinatorics.list_combination(
2,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1, 1], [1, 2], [2, 2]])
// 2-combination of [1, 2, 3, 4] without and with repetition
let assert Ok(combinations) =
[1, 2, 3, 4]
|> combinatorics.list_combination(
2,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> set.from_list() |> set.from_list()
|> should.equal( |> should.equal(
set.from_list([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]), set.from_list([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]),
) )
// Test with some arbitrary inputs let assert Ok(combinations) =
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 3) [1, 2, 3, 4]
result |> combinatorics.list_combination(
2,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> set.from_list()
|> should.equal(
set.from_list([
[1, 1],
[1, 2],
[1, 3],
[1, 4],
[2, 2],
[2, 3],
[2, 4],
[3, 3],
[3, 4],
[4, 4],
]),
)
// 3-combination of [1, 2, 3, 4] without repetition
let assert Ok(combinations) =
[1, 2, 3, 4]
|> combinatorics.list_combination(
3,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> set.from_list() |> set.from_list()
|> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])) |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
// 3-combination of [1, 2, 3] with repetition
let assert Ok(combinations) =
[1, 2, 3]
|> combinatorics.list_combination(
3,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> set.from_list()
|> should.equal(
set.from_list([
[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 2],
[1, 2, 3],
[1, 3, 3],
[2, 2, 2],
[2, 2, 3],
[2, 3, 3],
[3, 3, 3],
]),
)
// Combinations treat repeated elements as distinct in certain scenarios
let assert Ok(combinations) =
[1.0, 1.0]
|> combinatorics.list_combination(
2,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1.0, 1.0]])
// Repetition creates more possibilities even with identical elements
let assert Ok(combinations) =
[1.0, 1.0]
|> combinatorics.list_combination(
2,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> should.equal([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]])
// Large input: Ensure correct number of combinations is generated
let assert Ok(combinations) =
["a", "b", "c", "d", "e"]
|> combinatorics.list_combination(
5,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> list.length()
|> should.equal(1)
let assert Ok(combinations) =
["a", "b", "c", "d", "e"]
|> combinatorics.list_combination(
5,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> list.length()
|> should.equal(126)
}
pub fn combination_alignment_test() {
// Test: Number of generated combinations should match the expected count
// Without repetitions
let arr = ["a", "b", "c", "d", "e", "f"]
let length = list.length(arr)
let assert Ok(number_of_combinations) =
combinatorics.combination(
length,
length,
option.Some(combinatorics.WithoutRepetitions),
)
let assert Ok(combinations) =
arr
|> combinatorics.list_combination(
length,
option.Some(combinatorics.WithoutRepetitions),
)
combinations
|> iterator.to_list()
|> list.length()
|> should.equal(number_of_combinations)
// With repetitions
let arr = ["a", "b", "c", "d", "e", "f"]
let length = list.length(arr)
let assert Ok(number_of_combinations) =
combinatorics.combination(
length,
length,
option.Some(combinatorics.WithRepetitions),
)
let assert Ok(combinations) =
arr
|> combinatorics.list_combination(
length,
option.Some(combinatorics.WithRepetitions),
)
combinations
|> iterator.to_list()
|> list.length()
|> should.equal(number_of_combinations)
}
pub fn example_test() {
// Cartesian product of two empty sets
set.from_list([])
|> combinatorics.cartesian_product(set.from_list([]))
|> should.equal(set.from_list([]))
// 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)]),
)
} }

View file

@ -1,3 +1,4 @@
import gleam/iterator
import gleam/list import gleam/list
import gleam_community/maths/elementary import gleam_community/maths/elementary
import gleam_community/maths/predicates import gleam_community/maths/predicates
@ -12,14 +13,24 @@ pub fn float_list_linear_space_test() {
// ---> With endpoint included // ---> With endpoint included
let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True) let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, 20.0, 30.0, 40.0, 50.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, 20.0, 30.0, 40.0, 50.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True) let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, 12.5, 15.0, 17.5, 20.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, 12.5, 15.0, 17.5, 20.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -29,7 +40,12 @@ pub fn float_list_linear_space_test() {
// ----> Without endpoint included // ----> Without endpoint included
let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, False) let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, False)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, 18.0, 26.0, 34.0, 42.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, 18.0, 26.0, 34.0, 42.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -37,7 +53,12 @@ pub fn float_list_linear_space_test() {
let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, False) let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, False)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, 12.0, 14.0, 16.0, 18.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, 12.0, 14.0, 16.0, 18.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -46,7 +67,12 @@ pub fn float_list_linear_space_test() {
// Try with negative stop // Try with negative stop
let assert Ok(linspace) = sequences.linear_space(10.0, -50.0, 5, False) let assert Ok(linspace) = sequences.linear_space(10.0, -50.0, 5, False)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, -2.0, -14.0, -26.0, -38.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, -2.0, -14.0, -26.0, -38.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -54,7 +80,12 @@ pub fn float_list_linear_space_test() {
let assert Ok(linspace) = sequences.linear_space(10.0, -20.0, 5, True) let assert Ok(linspace) = sequences.linear_space(10.0, -20.0, 5, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [10.0, 2.5, -5.0, -12.5, -20.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[10.0, 2.5, -5.0, -12.5, -20.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -63,7 +94,12 @@ pub fn float_list_linear_space_test() {
// Try with negative start // Try with negative start
let assert Ok(linspace) = sequences.linear_space(-10.0, 50.0, 5, False) let assert Ok(linspace) = sequences.linear_space(-10.0, 50.0, 5, False)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [-10.0, 2.0, 14.0, 26.0, 38.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[-10.0, 2.0, 14.0, 26.0, 38.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -71,7 +107,12 @@ pub fn float_list_linear_space_test() {
let assert Ok(linspace) = sequences.linear_space(-10.0, 20.0, 5, True) let assert Ok(linspace) = sequences.linear_space(-10.0, 20.0, 5, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(linspace, [-10.0, -2.5, 5.0, 12.5, 20.0], 0.0, tol) predicates.all_close(
linspace |> iterator.to_list(),
[-10.0, -2.5, 5.0, 12.5, 20.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
@ -90,7 +131,12 @@ pub fn float_list_logarithmic_space_test() {
// - Positive start, stop, base // - Positive start, stop, base
let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0) let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 100.0, 1000.0], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 100.0, 1000.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -99,7 +145,12 @@ pub fn float_list_logarithmic_space_test() {
let assert Ok(logspace) = let assert Ok(logspace) =
sequences.logarithmic_space(1.0, 3.0, 3, True, -10.0) sequences.logarithmic_space(1.0, 3.0, 3, True, -10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [-10.0, 100.0, -1000.0], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[-10.0, 100.0, -1000.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -108,7 +159,12 @@ pub fn float_list_logarithmic_space_test() {
let assert Ok(logspace) = let assert Ok(logspace) =
sequences.logarithmic_space(1.0, -3.0, 3, True, -10.0) sequences.logarithmic_space(1.0, -3.0, 3, True, -10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [-10.0, -0.1, -0.001], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[-10.0, -0.1, -0.001],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -117,7 +173,12 @@ pub fn float_list_logarithmic_space_test() {
let assert Ok(logspace) = let assert Ok(logspace) =
sequences.logarithmic_space(1.0, -3.0, 3, True, 10.0) sequences.logarithmic_space(1.0, -3.0, 3, True, 10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 0.1, 0.001], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 0.1, 0.001],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -126,7 +187,12 @@ pub fn float_list_logarithmic_space_test() {
let assert Ok(logspace) = let assert Ok(logspace) =
sequences.logarithmic_space(-1.0, 3.0, 3, True, 10.0) sequences.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [0.1, 10.0, 1000.0], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[0.1, 10.0, 1000.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -136,7 +202,12 @@ pub fn float_list_logarithmic_space_test() {
let assert Ok(logspace) = let assert Ok(logspace) =
sequences.logarithmic_space(1.0, 3.0, 3, False, 10.0) sequences.logarithmic_space(1.0, 3.0, 3, False, 10.0)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 46.41588834, 215.443469], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 46.41588834, 215.443469],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -154,7 +225,12 @@ pub fn float_list_geometric_space_test() {
// - Positive start, stop // - Positive start, stop
let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True) let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 100.0, 1000.0], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 100.0, 1000.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -162,7 +238,12 @@ pub fn float_list_geometric_space_test() {
// - Positive start, negative stop // - Positive start, negative stop
let assert Ok(logspace) = sequences.geometric_space(10.0, 0.001, 3, True) let assert Ok(logspace) = sequences.geometric_space(10.0, 0.001, 3, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 0.1, 0.001], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 0.1, 0.001],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -170,7 +251,12 @@ pub fn float_list_geometric_space_test() {
// - Positive stop, negative start // - Positive stop, negative start
let assert Ok(logspace) = sequences.geometric_space(0.1, 1000.0, 3, True) let assert Ok(logspace) = sequences.geometric_space(0.1, 1000.0, 3, True)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [0.1, 10.0, 1000.0], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[0.1, 10.0, 1000.0],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -179,7 +265,12 @@ pub fn float_list_geometric_space_test() {
// - Positive start, stop // - Positive start, stop
let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, False) let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, False)
let assert Ok(result) = let assert Ok(result) =
predicates.all_close(logspace, [10.0, 46.41588834, 215.443469], 0.0, tol) predicates.all_close(
logspace |> iterator.to_list(),
[10.0, 46.41588834, 215.443469],
0.0,
tol,
)
result result
|> list.all(fn(x) { x == True }) |> list.all(fn(x) { x == True })
|> should.be_true() |> should.be_true()
@ -199,31 +290,39 @@ pub fn float_list_geometric_space_test() {
pub fn float_list_arange_test() { pub fn float_list_arange_test() {
// Positive start, stop, step // Positive start, stop, step
sequences.arange(1.0, 5.0, 1.0) sequences.arange(1.0, 5.0, 1.0)
|> iterator.to_list()
|> should.equal([1.0, 2.0, 3.0, 4.0]) |> should.equal([1.0, 2.0, 3.0, 4.0])
sequences.arange(1.0, 5.0, 0.5) sequences.arange(1.0, 5.0, 0.5)
|> iterator.to_list()
|> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]) |> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
sequences.arange(1.0, 2.0, 0.25) sequences.arange(1.0, 2.0, 0.25)
|> iterator.to_list()
|> should.equal([1.0, 1.25, 1.5, 1.75]) |> should.equal([1.0, 1.25, 1.5, 1.75])
// Reverse (switch start/stop largest/smallest value) // Reverse (switch start/stop largest/smallest value)
sequences.arange(5.0, 1.0, 1.0) sequences.arange(5.0, 1.0, 1.0)
|> iterator.to_list()
|> should.equal([]) |> should.equal([])
// Reverse negative step // Reverse negative step
sequences.arange(5.0, 1.0, -1.0) sequences.arange(5.0, 1.0, -1.0)
|> iterator.to_list()
|> should.equal([5.0, 4.0, 3.0, 2.0]) |> should.equal([5.0, 4.0, 3.0, 2.0])
// Positive start, negative stop, step // Positive start, negative stop, step
sequences.arange(5.0, -1.0, -1.0) sequences.arange(5.0, -1.0, -1.0)
|> iterator.to_list()
|> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0]) |> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
// Negative start, stop, step // Negative start, stop, step
sequences.arange(-5.0, -1.0, -1.0) sequences.arange(-5.0, -1.0, -1.0)
|> iterator.to_list()
|> should.equal([]) |> should.equal([])
// Negative start, stop, positive step // Negative start, stop, positive step
sequences.arange(-5.0, -1.0, 1.0) sequences.arange(-5.0, -1.0, 1.0)
|> iterator.to_list()
|> should.equal([-5.0, -4.0, -3.0, -2.0]) |> should.equal([-5.0, -4.0, -3.0, -2.0])
} }