Merge branch 'main' into list-at-hotfix

This commit is contained in:
Nicklas Sindlev Andersen 2024-08-12 22:03:52 +02:00 committed by GitHub
commit 725f437135
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1843 additions and 292 deletions

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">
////<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.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" 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.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script>
//// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, {
@ -44,6 +44,9 @@
import gleam/int
import gleam/list
import gleam/option
import gleam/pair
import gleam/result
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
@ -54,8 +57,9 @@ import gleam_community/maths/piecewise
/// </a>
/// </div>
///
/// The function calculates the greatest common multiple of two integers $$x, y \in \mathbb{Z}$$.
/// The greatest common multiple is the largest positive integer that is divisible by both $$x$$ and $$y$$.
/// The function calculates the greatest common divisor of two integers
/// $$x, y \in \mathbb{Z}$$. The greatest common divisor is the largest positive
/// integer that is divisible by both $$x$$ and $$y$$.
///
/// <details>
/// <summary>Example:</summary>
@ -64,10 +68,10 @@ import gleam_community/maths/piecewise
/// import gleam_community/maths/arithmetics
///
/// pub fn example() {
/// arithmetics.lcm(1, 1)
/// arithmetics.gcd(1, 1)
/// |> should.equal(1)
///
/// arithmetics.lcm(100, 10)
/// arithmetics.gcd(100, 10)
/// |> should.equal(10)
///
/// arithmetics.gcd(-36, -17)
@ -104,8 +108,9 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// </div>
///
///
/// Given two integers, $$x$$ (dividend) and $$y$$ (divisor), the Euclidean modulo of $$x$$ by $$y$$,
/// denoted as $$x \mod y$$, is the remainder $$r$$ of the division of $$x$$ by $$y$$, such that:
/// Given two integers, $$x$$ (dividend) and $$y$$ (divisor), the Euclidean modulo
/// of $$x$$ by $$y$$, denoted as $$x \mod y$$, is the remainder $$r$$ of the
/// division of $$x$$ by $$y$$, such that:
///
/// \\[
/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|,
@ -113,13 +118,15 @@ fn do_gcd(x: Int, y: Int) -> Int {
///
/// where $$q$$ is an integer that represents the quotient of the division.
///
/// The Euclidean modulo function of two numbers, is the remainder operation most commonly utilized in
/// mathematics. This differs from the standard truncating modulo operation frequently employed in
/// programming via the `%` operator. Unlike the `%` operator, which may return negative results
/// depending on the divisor's sign, the Euclidean modulo function is designed to
/// always yield a positive outcome, ensuring consistency with mathematical conventions.
/// The Euclidean modulo function of two numbers, is the remainder operation most
/// commonly utilized in mathematics. This differs from the standard truncating
/// modulo operation frequently employed in programming via the `%` operator.
/// Unlike the `%` operator, which may return negative results depending on the
/// divisor's sign, the Euclidean modulo function is designed to always yield a
/// positive outcome, ensuring consistency with mathematical conventions.
///
/// Note that like the Gleam division operator `/` this will return `0` if one of the arguments is `0`.
/// Note that like the Gleam division operator `/` this will return `0` if one of
/// the arguments is `0`.
///
///
/// <details>
@ -161,8 +168,9 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
/// </a>
/// </div>
///
/// The function calculates the least common multiple of two integers $$x, y \in \mathbb{Z}$$.
/// The least common multiple is the smallest positive integer that has both $$x$$ and $$y$$ as factors.
/// The function calculates the least common multiple of two integers
/// $$x, y \in \mathbb{Z}$$. The least common multiple is the smallest positive
/// integer that has both $$x$$ and $$y$$ as factors.
///
/// <details>
/// <summary>Example:</summary>
@ -200,7 +208,8 @@ pub fn lcm(x: Int, y: Int) -> Int {
/// </a>
/// </div>
///
/// The function returns all the positive divisors of an integer, including the number iteself.
/// The function returns all the positive divisors of an integer, including the
/// number itself.
///
/// <details>
/// <summary>Example:</summary>
@ -251,7 +260,8 @@ fn find_divisors(n: Int) -> List(Int) {
/// </a>
/// </div>
///
/// The function returns all the positive divisors of an integer, excluding the number iteself.
/// The function returns all the positive divisors of an integer, excluding the
/// number iteself.
///
/// <details>
/// <summary>Example:</summary>
@ -289,29 +299,32 @@ pub fn proper_divisors(n: Int) -> List(Int) {
/// </a>
/// </div>
///
/// Calculcate the sum of the elements in a list:
/// Calculate the (weighted) sum of the elements in a list:
///
/// \\[
/// \sum_{i=1}^n 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 the value in the input list indexed by $$i$$.
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is
/// the value in the input list indexed by $$i$$, while the $$w_i \in \mathbb{R}$$
/// are corresponding weights ($$w_i = 1.0\\;\forall i=1...n$$ by default).
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam/option
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> arithmetics.float_sum()
/// |> arithmetics.float_sum(option.None)
/// |> should.equal(0.0)
///
/// // Valid input returns a result
/// [1.0, 2.0, 3.0]
/// |> arithmetics.float_sum()
/// |> arithmetics.float_sum(option.None)
/// |> should.equal(6.0)
/// }
/// </details>
@ -322,12 +335,18 @@ pub fn proper_divisors(n: Int) -> List(Int) {
/// </a>
/// </div>
///
pub fn float_sum(arr: List(Float)) -> Float {
case arr {
[] -> 0.0
_ ->
pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float {
case arr, weights {
[], _ -> 0.0
_, option.None ->
arr
|> list.fold(0.0, fn(acc: Float, a: Float) -> Float { a +. acc })
_, option.Some(warr) -> {
list.zip(arr, warr)
|> list.fold(0.0, fn(acc: Float, a: #(Float, Float)) -> Float {
pair.first(a) *. pair.second(a) +. acc
})
}
}
}
@ -337,13 +356,14 @@ pub fn float_sum(arr: List(Float)) -> Float {
/// </a>
/// </div>
///
/// Calculcate the sum of the elements in a list:
/// Calculate the sum of the elements in a list:
///
/// \\[
/// \sum_{i=1}^n x_i
/// \\]
///
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is
/// the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
@ -385,29 +405,32 @@ pub fn int_sum(arr: List(Int)) -> Int {
/// </a>
/// </div>
///
/// Calculcate the product of the elements in a list:
/// Calculate the (weighted) product of the elements in a list:
///
/// \\[
/// \prod_{i=1}^n x_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 the value in the input list indexed by $$i$$.
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is
/// the value in the input list indexed by $$i$$, while the $$w_i \in \mathbb{R}$$
/// are corresponding weights ($$w_i = 1.0\\;\forall i=1...n$$ by default).
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam/option
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns 0.0
/// // An empty list returns 1.0
/// []
/// |> arithmetics.float_product()
/// |> should.equal(0.0)
/// |> arithmetics.float_product(option.None)
/// |> should.equal(1.0)
///
/// // Valid input returns a result
/// [1.0, 2.0, 3.0]
/// |> arithmetics.float_product()
/// |> arithmetics.float_product(option.None)
/// |> should.equal(6.0)
/// }
/// </details>
@ -418,12 +441,36 @@ pub fn int_sum(arr: List(Int)) -> Int {
/// </a>
/// </div>
///
pub fn float_product(arr: List(Float)) -> Float {
case arr {
[] -> 1.0
_ ->
pub fn float_product(
arr: List(Float),
weights: option.Option(List(Float)),
) -> Result(Float, String) {
case arr, weights {
[], _ ->
1.0
|> Ok
_, option.None ->
arr
|> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
|> Ok
_, option.Some(warr) -> {
let results =
list.zip(arr, warr)
|> list.map(fn(a: #(Float, Float)) -> Result(Float, String) {
pair.first(a)
|> elementary.power(pair.second(a))
})
|> result.all
case results {
Ok(prods) ->
prods
|> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
|> Ok
Error(msg) ->
msg
|> Error
}
}
}
}
@ -433,13 +480,14 @@ pub fn float_product(arr: List(Float)) -> Float {
/// </a>
/// </div>
///
/// Calculcate the product of the elements in a list:
/// Calculate the product of the elements in a list:
///
/// \\[
/// \prod_{i=1}^n x_i
/// \\]
///
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// In the formula, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is
/// the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
@ -448,10 +496,10 @@ pub fn float_product(arr: List(Float)) -> Float {
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns 0
/// // An empty list returns 1
/// []
/// |> arithmetics.int_product()
/// |> should.equal(0)
/// |> should.equal(1)
///
/// // Valid input returns a result
/// [1, 2, 3]
@ -481,15 +529,16 @@ pub fn int_product(arr: List(Int)) -> Int {
/// </a>
/// </div>
///
/// Calculcate the cumulative sum of the elements in a list:
/// Calculate the cumulative sum of the elements in a list:
///
/// \\[
/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$ elements.
/// That is, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$
/// elements. That is, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$
/// is the value in the input list indexed by $$i$$. The value $$v_j$$ is thus the
/// sum of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
@ -530,15 +579,16 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
/// </a>
/// </div>
///
/// Calculcate the cumulative sum of the elements in a list:
/// Calculate the cumulative sum of the elements in a list:
///
/// \\[
/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$ elements.
/// That is, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative sum of $$n$$
/// elements. That is, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$
/// is the value in the input list indexed by $$i$$. The value $$v_j$$ is thus the
/// sum of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
@ -579,15 +629,17 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
/// </a>
/// </div>
///
/// Calculcate the cumulative product of the elements in a list:
/// Calculate the cumulative product of the elements in a list:
///
/// \\[
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of $$n$$ elements.
/// That is, $$n$$ is the length of the list and $$x_i \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of
/// $$n$$ elements. That is, $$n$$ is the length of the list and
/// $$x_i \in \mathbb{R}$$ is the value in the input list indexed by $$i$$. The
/// value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the
/// given list.
///
/// <details>
/// <summary>Example:</summary>
@ -614,7 +666,7 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
/// </a>
/// </div>
///
pub fn float_cumumlative_product(arr: List(Float)) -> List(Float) {
pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
case arr {
[] -> []
_ ->
@ -629,15 +681,17 @@ pub fn float_cumumlative_product(arr: List(Float)) -> List(Float) {
/// </a>
/// </div>
///
/// Calculcate the cumulative product of the elements in a list:
/// Calculate the cumulative product of the elements in a list:
///
/// \\[
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\]
///
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of $$n$$ elements.
/// That is, $$n$$ is the length of the list and $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the product of the $$1$$ to $$j$$ first elements in the given list.
/// In the formula, $$v_j$$ is the $$j$$'th element in the cumulative product of
/// $$n$$ elements. That is, $$n$$ is the length of the list and
/// $$x_i \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$. The
/// value $$v_j$$ is thus the product of the $$1$$ to $$j$$ first elements in the
/// given list.
///
/// <details>
/// <summary>Example:</summary>

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">
////<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.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" 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.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script>
//// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, {

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">
////<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.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" 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.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script>
//// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, {

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">
////<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.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" 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.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script>
//// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, {

File diff suppressed because it is too large Load diff

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">
////<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.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" 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.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
////<script>
//// document.addEventListener("DOMContentLoaded", function() {
//// renderMathInElement(document.body, {
@ -490,7 +490,7 @@ fn do_ceiling(a: Float) -> Float
/// The absolute value:
///
/// \\[
/// \forall x, y \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.
@ -519,7 +519,7 @@ pub fn float_absolute_value(x: Float) -> Float {
/// The absolute value:
///
/// \\[
/// \forall x, y \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.
@ -592,7 +592,8 @@ pub fn float_absolute_difference(a: Float, b: Float) -> Float {
/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}.
/// \\]
///
/// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer value which is the the absolute difference of the inputs.
/// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer
/// value which is the the absolute difference of the inputs.
///
/// <details>
/// <summary>Example:</summary>
@ -698,7 +699,8 @@ fn do_int_sign(a: Int) -> Int
/// </a>
/// </div>
///
/// The function takes two arguments $$x, y \in \mathbb{R}$$ and returns $$x$$ such that it has the same sign as $$y$$.
/// The function takes two arguments $$x, y \in \mathbb{R}$$ and returns $$x$$
/// such that it has the same sign as $$y$$.
///
/// <div style="text-align: right;">
/// <a href="#">
@ -723,7 +725,8 @@ pub fn float_copy_sign(x: Float, y: Float) -> Float {
/// </a>
/// </div>
///
/// The function takes two arguments $$x, y \in \mathbb{Z}$$ and returns $$x$$ such that it has the same sign as $$y$$.
/// The function takes two arguments $$x, y \in \mathbb{Z}$$ and returns $$x$$
/// such that it has the same sign as $$y$$.
///
/// <div style="text-align: right;">
/// <a href="#">

View file

@ -23,7 +23,8 @@
////
//// ---
////
//// Predicates: A module containing functions for testing various mathematical properties of numbers.
//// Predicates: A module containing functions for testing various mathematical
//// properties of numbers.
////
//// * **Tests**
//// * [`is_close`](#is_close)
@ -33,6 +34,7 @@
//// * [`is_perfect`](#is_perfect)
//// * [`is_even`](#is_even)
//// * [`is_odd`](#is_odd)
//// * [`is_prime`](#is_prime)
import gleam/pair
import gleam/int
@ -49,8 +51,9 @@ import gleam_community/maths/arithmetics
/// </div>
///
/// 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 values.
/// The equivalance of the two given values are then determined based on the equation:
/// $$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
/// the equation:
///
/// \\[
/// \|a - b\| \leq (a_{tol} + r_{tol} \cdot \|b\|)
@ -61,7 +64,7 @@ import gleam_community/maths/arithmetics
/// <summary>Example</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example () {
/// let val: Float = 99.
@ -108,14 +111,15 @@ fn float_absolute_difference(a: Float, b: Float) -> Float {
/// </a>
/// </div>
///
/// Determine if a list of values are close to or equivalent to a another list of reference values.
/// Determine if a list of values are close to or equivalent to a another list of
/// reference values.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam/list
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example () {
/// let val: Float = 99.
@ -126,7 +130,7 @@ fn float_absolute_difference(a: Float, b: Float) -> Float {
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
/// let rtol: Float = 0.01
/// let atol: Float = 0.10
/// tests.all_close(xarr, yarr, rtol, atol)
/// predicates.all_close(xarr, yarr, rtol, atol)
/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
/// case zarr {
/// Ok(arr) ->
@ -175,19 +179,20 @@ pub fn all_close(
///
/// Determine if a given value is fractional.
///
/// `True` is returned if the given value is fractional, otherwise `False` is returned.
/// `True` is returned if the given value is fractional, otherwise `False` is
/// returned.
///
/// <details>
/// <summary>Example</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example () {
/// tests.is_fractional(0.3333)
/// predicates.is_fractional(0.3333)
/// |> should.equal(True)
///
/// tests.is_fractional(1.0)
/// predicates.is_fractional(1.0)
/// |> should.equal(False)
/// }
/// </details>
@ -212,21 +217,22 @@ fn do_ceiling(a: Float) -> Float
/// </a>
/// </div>
///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a power of another integer value $$y \in \mathbb{Z}$$.
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a
/// power of another integer value $$y \in \mathbb{Z}$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// // Check if 4 is a power of 2 (it is)
/// tests.is_power(4, 2)
/// predicates.is_power(4, 2)
/// |> should.equal(True)
///
/// // Check if 5 is a power of 2 (it is not)
/// tests.is_power(5, 2)
/// predicates.is_power(5, 2)
/// |> should.equal(False)
/// }
/// </details>
@ -251,7 +257,9 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// </a>
/// </div>
///
/// 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 positive divisors.
/// 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
/// positive divisors.
///
/// <details>
/// <summary>Details</summary>
@ -266,13 +274,13 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// tests.is_perfect(6)
/// predicates.is_perfect(6)
/// |> should.equal(True)
///
/// tests.is_perfect(28)
/// predicates.is_perfect(28)
/// |> should.equal(True)
/// }
/// </details>
@ -308,13 +316,13 @@ fn do_sum(arr: List(Int)) -> Int {
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// tests.is_even(-3)
/// predicates.is_even(-3)
/// |> should.equal(False)
///
/// tests.is_even(-4)
/// predicates.is_even(-4)
/// |> should.equal(True)
/// }
/// </details>
@ -341,13 +349,13 @@ pub fn is_even(x: Int) -> Bool {
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// tests.is_odd(-3)
/// predicates.is_odd(-3)
/// |> should.equal(True)
///
/// tests.is_odd(-4)
/// predicates.is_odd(-4)
/// |> should.equal(False)
/// }
/// </details>
@ -361,3 +369,97 @@ pub fn is_even(x: Int) -> Bool {
pub fn is_odd(x: Int) -> Bool {
x % 2 != 0
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// 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
/// positive divisors other than 1 and itself.
///
/// 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
/// as prime. However, the probability of such errors decreases with more testing
/// iterations (the function uses 64 iterations internally, which is typically
/// more than sufficient). The Miller-Rabin test is particularly useful for large
/// numbers.
///
/// <details>
/// <summary>Details</summary>
///
/// Examples of prime numbers:
/// - $$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$$.
/// - $$4$$ is not a prime number since it has divisors other than $$1$$ and itself, such as $$2$$.
///
/// </details>
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// predicates.is_prime(2)
/// |> should.equal(True)
///
/// predicates.is_prime(4)
/// |> should.equal(False)
///
/// // Test the 2nd Carmichael number
/// predicates.is_prime(1105)
/// |> should.equal(False)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn is_prime(x: Int) -> Bool {
case x {
x if x < 2 -> {
False
}
x if x == 2 -> {
True
}
_ -> {
miller_rabin_test(x, 64)
}
}
}
fn miller_rabin_test(n: Int, k: Int) -> Bool {
case n, k {
_, 0 -> True
_, _ -> {
// Generate a random int in the range [2, n]
let random_candidate: Int = 2 + int.random(n - 2)
case powmod_with_check(random_candidate, n - 1, n) == 1 {
True -> miller_rabin_test(n, k - 1)
False -> False
}
}
}
}
fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
case exponent, { exponent % 2 } == 0 {
0, _ -> 1
_, True -> {
let x: Int = powmod_with_check(base, exponent / 2, modulus)
case { x * x } % modulus, x != 1 && x != { modulus - 1 } {
1, True -> 0
_, _ -> { x * x } % modulus
}
}
_, _ -> { base * powmod_with_check(base, exponent - 1, modulus) } % modulus
}
}

View file

@ -23,7 +23,8 @@
////
//// ---
////
//// Sequences: A module containing functions for generating various types of sequences, ranges and intervals.
//// Sequences: A module containing functions for generating various types of
//// sequences, ranges and intervals.
////
//// * **Ranges and intervals**
//// * [`arange`](#arange)
@ -42,8 +43,10 @@ import gleam/list
/// </a>
/// </div>
///
/// The function returns a list with evenly spaced values within a given interval based on a start, stop value and a given increment (step-length) between consecutive values.
/// The list returned includes the given start value but excludes the stop value.
/// The function returns a list with evenly spaced values within a given interval
/// based on a start, stop value and a given increment (step-length) between
/// consecutive values. The list returned includes the given start value but
/// excludes the stop value.
///
/// <details>
/// <summary>Example:</summary>
@ -98,7 +101,8 @@ pub fn arange(start: Float, stop: Float, step: Float) -> List(Float) {
/// </a>
/// </div>
///
/// Generate a linearly spaced list of points over a specified interval. The endpoint of the interval can optionally be included/excluded.
/// Generate a linearly spaced list of points over a specified interval. The
/// endpoint of the interval can optionally be included/excluded.
///
/// <details>
/// <summary>Example:</summary>
@ -176,7 +180,8 @@ pub fn linear_space(
/// </a>
/// </div>
///
/// Generate a logarithmically spaced list of points over a specified interval. The endpoint of the interval can optionally be included/excluded.
/// Generate a logarithmically spaced list of points over a specified interval. The
/// endpoint of the interval can optionally be included/excluded.
///
/// <details>
/// <summary>Example:</summary>
@ -236,8 +241,11 @@ pub fn logarithmic_space(
/// </a>
/// </div>
///
/// The function returns a list of numbers spaced evenly on a log scale (a geometric progression). Each point in the list is a constant multiple of the previous.
/// The function is similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints specified directly.
/// The function returns a list of numbers spaced evenly on a log scale (a
/// geometric progression). Each point in the list is a constant multiple of the
/// previous. The function is similar to the
/// [`logarithmic_space`](#logarithmic_space) function, but with endpoints
/// specified directly.
///
/// <details>
/// <summary>Example:</summary>
@ -283,7 +291,7 @@ pub fn geometric_space(
) -> Result(List(Float), String) {
case start == 0.0 || stop == 0.0 {
True ->
""
"Invalid input: Neither 'start' nor 'stop' can be zero, as they must be non-zero for logarithmic calculations."
|> Error
False ->
case num > 0 {

View file

@ -100,8 +100,8 @@ pub fn erf(x: Float) -> Float {
/// </a>
/// </div>
///
/// 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 gamma function over the real numbers. The function is essentially equal to
/// the factorial for any positive integer argument: $$\Gamma(n) = (n - 1)!$$
///
/// The implemented gamma function is approximated through Lanczos approximation
/// using the same coefficients used by the GNU Scientific Library.
@ -174,7 +174,7 @@ pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, String) {
}
False ->
"Invlaid input argument: a <= 0 or x < 0. Valid input is a > 0 and x >= 0."
"Invalid input argument: a <= 0 or x < 0. Valid input is a > 0 and x >= 0."
|> Error
}
}

View file

@ -1,5 +1,6 @@
import gleam_community/maths/arithmetics
import gleeunit/should
import gleam/option
pub fn int_gcd_test() {
arithmetics.gcd(1, 1)
@ -100,16 +101,16 @@ pub fn int_divisors_test() {
pub fn float_list_sum_test() {
// An empty list returns 0
[]
|> arithmetics.float_sum()
|> arithmetics.float_sum(option.None)
|> should.equal(0.0)
// Valid input returns a result
[1.0, 2.0, 3.0]
|> arithmetics.float_sum()
|> arithmetics.float_sum(option.None)
|> should.equal(6.0)
[-2.0, 4.0, 6.0]
|> arithmetics.float_sum()
|> arithmetics.float_sum(option.None)
|> should.equal(8.0)
}
@ -132,17 +133,17 @@ pub fn int_list_sum_test() {
pub fn float_list_product_test() {
// An empty list returns 0
[]
|> arithmetics.float_product()
|> should.equal(1.0)
|> arithmetics.float_product(option.None)
|> should.equal(Ok(1.0))
// Valid input returns a result
[1.0, 2.0, 3.0]
|> arithmetics.float_product()
|> should.equal(6.0)
|> arithmetics.float_product(option.None)
|> should.equal(Ok(6.0))
[-2.0, 4.0, 6.0]
|> arithmetics.float_product()
|> should.equal(-48.0)
|> arithmetics.float_product(option.None)
|> should.equal(Ok(-48.0))
}
pub fn int_list_product_test() {
@ -196,16 +197,16 @@ pub fn int_list_cumulative_sum_test() {
pub fn float_list_cumulative_product_test() {
// An empty lists returns an empty list
[]
|> arithmetics.float_cumumlative_product()
|> arithmetics.float_cumulative_product()
|> should.equal([])
// Valid input returns a result
[1.0, 2.0, 3.0]
|> arithmetics.float_cumumlative_product()
|> arithmetics.float_cumulative_product()
|> should.equal([1.0, 2.0, 6.0])
[-2.0, 4.0, 6.0]
|> arithmetics.float_cumumlative_product()
|> arithmetics.float_cumulative_product()
|> should.equal([-2.0, -8.0, -48.0])
}

View file

@ -2,160 +2,292 @@ import gleam_community/maths/elementary
import gleam_community/maths/metrics
import gleam_community/maths/predicates
import gleeunit/should
import gleam/set
import gleam/option
pub fn float_list_norm_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// An empty lists returns 0.0
[]
|> metrics.norm(1.0)
|> should.equal(0.0)
|> metrics.norm(1.0, option.None)
|> should.equal(Ok(0.0))
// Check that the function agrees, at some arbitrary input
// points, with known function values
[1.0, 1.0, 1.0]
|> metrics.norm(1.0)
let assert Ok(result) =
[1.0, 1.0, 1.0]
|> metrics.norm(1.0, option.None)
result
|> predicates.is_close(3.0, 0.0, tol)
|> should.be_true()
[1.0, 1.0, 1.0]
|> metrics.norm(-1.0)
let assert Ok(result) =
[1.0, 1.0, 1.0]
|> metrics.norm(-1.0, option.None)
result
|> predicates.is_close(0.3333333333333333, 0.0, tol)
|> should.be_true()
[-1.0, -1.0, -1.0]
|> metrics.norm(-1.0)
let assert Ok(result) =
[-1.0, -1.0, -1.0]
|> metrics.norm(-1.0, option.None)
result
|> predicates.is_close(0.3333333333333333, 0.0, tol)
|> should.be_true()
[-1.0, -1.0, -1.0]
|> metrics.norm(1.0)
let assert Ok(result) =
[-1.0, -1.0, -1.0]
|> metrics.norm(1.0, option.None)
result
|> predicates.is_close(3.0, 0.0, tol)
|> should.be_true()
[-1.0, -2.0, -3.0]
|> metrics.norm(-10.0)
let assert Ok(result) =
[-1.0, -2.0, -3.0]
|> metrics.norm(-10.0, option.None)
result
|> predicates.is_close(0.9999007044905545, 0.0, tol)
|> should.be_true()
[-1.0, -2.0, -3.0]
|> metrics.norm(-100.0)
let assert Ok(result) =
[-1.0, -2.0, -3.0]
|> metrics.norm(-100.0, option.None)
result
|> predicates.is_close(1.0, 0.0, tol)
|> should.be_true()
[-1.0, -2.0, -3.0]
|> metrics.norm(2.0)
let assert Ok(result) =
[-1.0, -2.0, -3.0]
|> metrics.norm(2.0, option.None)
result
|> predicates.is_close(3.7416573867739413, 0.0, tol)
|> should.be_true()
}
pub fn float_list_manhatten_test() {
pub fn float_list_manhattan_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Empty lists returns 0.0
metrics.manhatten_distance([], [])
|> should.equal(Ok(0.0))
// Differing lengths returns error
metrics.manhatten_distance([], [1.0])
// Empty lists returns an error
metrics.manhattan_distance([], [], option.None)
|> should.be_error()
// Manhatten distance (p = 1)
let assert Ok(result) = metrics.manhatten_distance([0.0, 0.0], [1.0, 2.0])
// Differing lengths returns error
metrics.manhattan_distance([], [1.0], option.None)
|> should.be_error()
// Try with valid input (same as Minkowski distance with p = 1)
let assert Ok(result) =
metrics.manhattan_distance([0.0, 0.0], [1.0, 2.0], option.None)
result
|> predicates.is_close(3.0, 0.0, tol)
|> should.be_true()
metrics.manhattan_distance([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
|> should.equal(Ok(9.0))
metrics.manhattan_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 1.0, 1.0]),
)
|> should.equal(Ok(9.0))
metrics.manhattan_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 2.0, 3.0]),
)
|> should.equal(Ok(18.0))
// Try invalid input with weights (different sized lists returns an error)
metrics.manhattan_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([7.0, 8.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.manhattan_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}
// pub fn int_list_manhatten_test() {
// // Empty lists returns 0
// metrics.int_manhatten_distance([], [])
// |> should.equal(Ok(0))
// // Differing lengths returns error
// metrics.int_manhatten_distance([], [1])
// |> should.be_error()
// let assert Ok(result) = metrics.int_manhatten_distance([0, 0], [1, 2])
// result
// |> should.equal(3)
// }
pub fn float_list_minkowski_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Empty lists returns 0.0
metrics.minkowski_distance([], [], 1.0)
|> should.equal(Ok(0.0))
// Empty lists returns an error
metrics.minkowski_distance([], [], 1.0, option.None)
|> should.be_error()
// Differing lengths returns error
metrics.minkowski_distance([], [1.0], 1.0)
metrics.minkowski_distance([], [1.0], 1.0, option.None)
|> should.be_error()
// Test order < 1
metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0)
metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0, option.None)
|> should.be_error()
// Check that the function agrees, at some arbitrary input
// points, with known function values
let assert Ok(result) =
metrics.minkowski_distance([1.0, 1.0], [1.0, 1.0], 1.0)
metrics.minkowski_distance([1.0, 1.0], [1.0, 1.0], 1.0, option.None)
result
|> predicates.is_close(0.0, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0)
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0, option.None)
result
|> predicates.is_close(1.0717734625362931, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 100.0)
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 100.0, option.None)
result
|> predicates.is_close(1.0069555500567189, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0)
metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0, option.None)
result
|> predicates.is_close(1.0717734625362931, 0.0, tol)
|> should.be_true()
// Euclidean distance (p = 2)
let assert Ok(result) =
metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 2.0)
metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 2.0, option.None)
result
|> predicates.is_close(2.23606797749979, 0.0, tol)
|> should.be_true()
// Manhatten distance (p = 1)
// Manhattan distance (p = 1)
let assert Ok(result) =
metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0)
metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0, option.None)
result
|> predicates.is_close(3.0, 0.0, tol)
|> should.be_true()
// Try different valid input
let assert Ok(result) =
metrics.minkowski_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
4.0,
option.None,
)
result
|> predicates.is_close(3.9482220388574776, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.minkowski_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
4.0,
option.Some([1.0, 1.0, 1.0]),
)
result
|> predicates.is_close(3.9482220388574776, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.minkowski_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
4.0,
option.Some([1.0, 2.0, 3.0]),
)
result
|> predicates.is_close(4.6952537402198615, 0.0, tol)
|> should.be_true()
// Try invalid input with weights (different sized lists returns an error)
metrics.minkowski_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
2.0,
option.Some([7.0, 8.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.minkowski_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
2.0,
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}
pub fn float_list_euclidean_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Empty lists returns 0.0
metrics.euclidean_distance([], [])
|> should.equal(Ok(0.0))
// Differing lengths returns error
metrics.euclidean_distance([], [1.0])
// Empty lists returns an error
metrics.euclidean_distance([], [], option.None)
|> should.be_error()
// Euclidean distance (p = 2)
let assert Ok(result) = metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0])
// Differing lengths returns error
metrics.euclidean_distance([], [1.0], option.None)
|> should.be_error()
// Try with valid input (same as Minkowski distance with p = 2)
let assert Ok(result) =
metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0], option.None)
result
|> predicates.is_close(2.23606797749979, 0.0, tol)
|> should.be_true()
// Try different valid input
let assert Ok(result) =
metrics.euclidean_distance([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
result
|> predicates.is_close(5.196152422706632, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.euclidean_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 1.0, 1.0]),
)
result
|> predicates.is_close(5.196152422706632, 0.0, tol)
|> should.be_true()
let assert Ok(result) =
metrics.euclidean_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 2.0, 3.0]),
)
result
|> predicates.is_close(7.3484692283495345, 0.0, tol)
|> should.be_true()
// Try invalid input with weights (different sized lists returns an error)
metrics.euclidean_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([7.0, 8.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.euclidean_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}
pub fn example_mean_test() {
pub fn mean_test() {
// An empty list returns an error
[]
|> metrics.mean()
@ -167,7 +299,7 @@ pub fn example_mean_test() {
|> should.equal(Ok(2.0))
}
pub fn example_median_test() {
pub fn median_test() {
// An empty list returns an error
[]
|> metrics.median()
@ -183,7 +315,7 @@ pub fn example_median_test() {
|> should.equal(Ok(2.5))
}
pub fn example_variance_test() {
pub fn variance_test() {
// Degrees of freedom
let ddof: Int = 1
@ -198,7 +330,7 @@ pub fn example_variance_test() {
|> should.equal(Ok(1.0))
}
pub fn example_standard_deviation_test() {
pub fn standard_deviation_test() {
// Degrees of freedom
let ddof: Int = 1
@ -212,3 +344,330 @@ pub fn example_standard_deviation_test() {
|> metrics.standard_deviation(ddof)
|> should.equal(Ok(1.0))
}
pub fn jaccard_index_test() {
metrics.jaccard_index(set.from_list([]), set.from_list([]))
|> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.jaccard_index(set_a, set_b)
|> should.equal(4.0 /. 10.0)
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10])
metrics.jaccard_index(set_c, set_d)
|> should.equal(0.0 /. 11.0)
let set_e: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"])
let set_f: set.Set(String) =
set.from_list(["monkey", "rhino", "ostrich", "salmon"])
metrics.jaccard_index(set_e, set_f)
|> should.equal(1.0 /. 7.0)
}
pub fn sorensen_dice_coefficient_test() {
metrics.sorensen_dice_coefficient(set.from_list([]), set.from_list([]))
|> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.sorensen_dice_coefficient(set_a, set_b)
|> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 })
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10])
metrics.sorensen_dice_coefficient(set_c, set_d)
|> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 })
let set_e: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"])
let set_f: set.Set(String) =
set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
metrics.sorensen_dice_coefficient(set_e, set_f)
|> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
}
pub fn overlap_coefficient_test() {
metrics.overlap_coefficient(set.from_list([]), set.from_list([]))
|> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.overlap_coefficient(set_a, set_b)
|> should.equal(4.0 /. 7.0)
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10])
metrics.overlap_coefficient(set_c, set_d)
|> should.equal(0.0 /. 5.0)
let set_e: set.Set(String) =
set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
let set_f: set.Set(String) =
set.from_list(["monkey", "bird", "ostrich", "salmon"])
metrics.overlap_coefficient(set_e, set_f)
|> should.equal(2.0 /. 4.0)
}
pub fn cosine_similarity_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Empty lists returns an error
metrics.cosine_similarity([], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.cosine_similarity([1.0, 2.0, 3.0], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.cosine_similarity([], [1.0, 2.0, 3.0], option.None)
|> should.be_error()
// Different sized lists returns an error
metrics.cosine_similarity([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
|> should.be_error()
// Two orthogonal vectors (represented by lists)
metrics.cosine_similarity([-1.0, 1.0, 0.0], [1.0, 1.0, -1.0], option.None)
|> should.equal(Ok(0.0))
// Two identical (parallel) vectors (represented by lists)
metrics.cosine_similarity([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], option.None)
|> should.equal(Ok(1.0))
// Two parallel, but oppositely oriented vectors (represented by lists)
metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None)
|> should.equal(Ok(-1.0))
// Try with arbitrary valid input
let assert Ok(result) =
metrics.cosine_similarity([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
result
|> predicates.is_close(0.9746318461970762, 0.0, tol)
|> should.be_true()
// Try valid input with weights
let assert Ok(result) =
metrics.cosine_similarity(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 1.0, 1.0]),
)
result
|> predicates.is_close(0.9746318461970762, 0.0, tol)
|> should.be_true()
// Try with different weights
let assert Ok(result) =
metrics.cosine_similarity(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([1.0, 2.0, 3.0]),
)
result
|> predicates.is_close(0.9855274566525745, 0.0, tol)
|> should.be_true()
// Try invalid input with weights (different sized lists returns an error)
metrics.cosine_similarity(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([7.0, 8.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.cosine_similarity(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}
pub fn chebyshev_distance_test() {
// Empty lists returns an error
metrics.chebyshev_distance([], [])
|> should.be_error()
// One empty list returns an error
metrics.chebyshev_distance([1.0, 2.0, 3.0], [])
|> should.be_error()
// One empty list returns an error
metrics.chebyshev_distance([], [1.0, 2.0, 3.0])
|> should.be_error()
// Different sized lists returns an error
metrics.chebyshev_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0])
|> should.be_error()
// Try different types of valid input
metrics.chebyshev_distance([1.0, 0.0], [0.0, 2.0])
|> should.equal(Ok(2.0))
metrics.chebyshev_distance([1.0, 0.0], [2.0, 0.0])
|> should.equal(Ok(1.0))
metrics.chebyshev_distance([1.0, 0.0], [-2.0, 0.0])
|> should.equal(Ok(3.0))
metrics.chebyshev_distance([-5.0, -10.0, -3.0], [-1.0, -12.0, -3.0])
|> should.equal(Ok(4.0))
metrics.chebyshev_distance([1.0, 2.0, 3.0], [1.0, 2.0, 3.0])
|> should.equal(Ok(0.0))
}
pub fn levenshtein_distance_test() {
// Try different types of valid input...
// Requires 5 insertions to transform the empty string into "hello"
metrics.levenshtein_distance("", "hello")
|> should.equal(5)
// Requires 5 deletions to remove all characters from "hello" to match the empty string
metrics.levenshtein_distance("hello", "")
|> should.equal(5)
// Requires 2 deletions to remove two 'b's and 1 substitution to change 'b' to 'a'
metrics.levenshtein_distance("bbb", "a")
|> should.equal(3)
// Requires 2 insertions to add two 'b's and 1 substitution to change 'a' to 'b'
metrics.levenshtein_distance("a", "bbb")
|> should.equal(3)
// No changes needed, since the strings are identical
metrics.levenshtein_distance("hello", "hello")
|> should.equal(0)
// Requires 1 substitution to change 'a' to 'u'
metrics.levenshtein_distance("cat", "cut")
|> should.equal(1)
// Requires 2 substitutions (k -> s, e -> i) and 1 insertion (g at the end)
metrics.levenshtein_distance("kitten", "sitting")
|> should.equal(3)
// Some more complex cases, involving multiple insertions, deletions, and substitutions
metrics.levenshtein_distance("gggtatccat", "cctaggtccct")
|> should.equal(6)
metrics.levenshtein_distance(
"This is a longer string",
"This is also a much longer string",
)
|> should.equal(10)
}
pub fn canberra_distance_test() {
// Empty lists returns an error
metrics.canberra_distance([], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.canberra_distance([1.0, 2.0, 3.0], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.canberra_distance([], [1.0, 2.0, 3.0], option.None)
|> should.be_error()
// Different sized lists returns an error
metrics.canberra_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
|> should.be_error()
// Try different types of valid input
metrics.canberra_distance([0.0, 0.0], [0.0, 0.0], option.None)
|> should.equal(Ok(0.0))
metrics.canberra_distance([1.0, 2.0], [-2.0, -1.0], option.None)
|> should.equal(Ok(2.0))
metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.None)
|> should.equal(Ok(2.0))
metrics.canberra_distance([1.0, 0.0], [2.0, 0.0], option.None)
|> should.equal(Ok(1.0 /. 3.0))
metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 1.0]))
|> should.equal(Ok(2.0))
metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 0.5]))
|> should.equal(Ok(1.5))
metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([0.5, 0.5]))
|> should.equal(Ok(1.0))
// Different sized lists (weights) returns an error
metrics.canberra_distance(
[1.0, 2.0, 3.0],
[1.0, 2.0, 3.0],
option.Some([1.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.canberra_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}
pub fn braycurtis_distance_test() {
// Empty lists returns an error
metrics.braycurtis_distance([], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.braycurtis_distance([1.0, 2.0, 3.0], [], option.None)
|> should.be_error()
// One empty list returns an error
metrics.braycurtis_distance([], [1.0, 2.0, 3.0], option.None)
|> should.be_error()
// Different sized lists returns an error
metrics.braycurtis_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
|> should.be_error()
// Try different types of valid input
metrics.braycurtis_distance([0.0, 0.0], [0.0, 0.0], option.None)
|> should.equal(Ok(0.0))
metrics.braycurtis_distance([1.0, 2.0], [-2.0, -1.0], option.None)
|> should.equal(Ok(3.0))
metrics.braycurtis_distance([1.0, 0.0], [0.0, 2.0], option.None)
|> should.equal(Ok(1.0))
metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.None)
|> should.equal(Ok(0.4))
metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([1.0, 1.0]))
|> should.equal(Ok(0.4))
metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.5, 1.0]))
|> should.equal(Ok(0.375))
metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.25, 0.25]))
|> should.equal(Ok(0.4))
// Different sized lists (weights) returns an error
metrics.braycurtis_distance(
[1.0, 2.0, 3.0],
[1.0, 2.0, 3.0],
option.Some([1.0]),
)
|> should.be_error()
// Try invalid input with weights that are negative
metrics.braycurtis_distance(
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
option.Some([-7.0, -8.0, -9.0]),
)
|> should.be_error()
}

View file

@ -136,3 +136,41 @@ pub fn int_is_perfect_test() {
predicates.is_perfect(13)
|> should.equal(False)
}
pub fn int_is_prime_test() {
// Test a negative integer, i.e., not a natural number
predicates.is_prime(-7)
|> should.equal(False)
predicates.is_prime(1)
|> should.equal(False)
predicates.is_prime(2)
|> should.equal(True)
predicates.is_prime(3)
|> should.equal(True)
predicates.is_prime(5)
|> should.equal(True)
predicates.is_prime(7)
|> should.equal(True)
predicates.is_prime(11)
|> should.equal(True)
predicates.is_prime(42)
|> should.equal(False)
predicates.is_prime(7919)
|> should.equal(True)
// Test 1st Carmichael number
predicates.is_prime(561)
|> should.equal(False)
// Test 2nd Carmichael number
predicates.is_prime(1105)
|> should.equal(False)
}