mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-14 23:49:01 +00:00
Refactor
This commit is contained in:
parent
77e13b9254
commit
1e31a8c38b
29 changed files with 8517 additions and 9547 deletions
30
README.md
30
README.md
|
@ -11,46 +11,44 @@ The library supports both targets: Erlang and JavaScript.
|
||||||
|
|
||||||
```gleam
|
```gleam
|
||||||
import gleam/float
|
import gleam/float
|
||||||
import gleam/iterator
|
import gleam/yielder
|
||||||
import gleam/option.{Some}
|
import gleam_community/maths
|
||||||
import gleam_community/maths/arithmetics
|
|
||||||
import gleam_community/maths/combinatorics.{WithoutRepetitions}
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/piecewise
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleeunit/should
|
import gleeunit/should
|
||||||
|
|
||||||
pub fn example() {
|
pub fn example() {
|
||||||
// Evaluate the sine function
|
// Evaluate the sine function
|
||||||
let result = elementary.sin(elementary.pi())
|
let result = maths.sin(maths.pi())
|
||||||
|
|
||||||
// Set the relative and absolute tolerance
|
// Set the relative and absolute tolerance
|
||||||
let assert Ok(absolute_tol) = elementary.power(10.0, -6.0)
|
let assert Ok(absolute_tol) = float.power(10.0, -6.0)
|
||||||
let relative_tol = 0.0
|
let relative_tol = 0.0
|
||||||
|
|
||||||
// Check that the value is very close to 0.0
|
// Check that the value is very close to 0.0
|
||||||
// That is, if 'result' is within +/- 10^(-6)
|
// That is, if 'result' is within +/- 10^(-6)
|
||||||
predicates.is_close(result, 0.0, relative_tol, absolute_tol)
|
maths.is_close(result, 0.0, relative_tol, absolute_tol)
|
||||||
|> should.be_true()
|
|> should.be_true()
|
||||||
|
|
||||||
// Find the greatest common divisor
|
// Find the greatest common divisor
|
||||||
arithmetics.gcd(54, 24)
|
maths.gcd(54, 24)
|
||||||
|> should.equal(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)
|
maths.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
|
||||||
|> should.equal(Ok(#(3.0, 50.0)))
|
|> should.equal(Ok(#(3.0, 50.0)))
|
||||||
|
|
||||||
// Determine if a number is fractional
|
// Determine if a number is fractional
|
||||||
predicates.is_fractional(0.3333)
|
maths.is_fractional(0.3333)
|
||||||
|> should.equal(True)
|
|> should.equal(True)
|
||||||
|
|
||||||
// Generate all k = 2 combinations of [1, 2, 3]
|
// Generate all k = 2 combinations of [1, 2, 3]
|
||||||
let assert Ok(combinations) =
|
let assert Ok(combinations) = maths.list_combination([1, 2, 3], 2)
|
||||||
combinatorics.list_combination([1, 2, 3], 2, Some(WithoutRepetitions))
|
|
||||||
combinations
|
combinations
|
||||||
|> iterator.to_list()
|
|> yielder.to_list()
|
||||||
|> should.equal([[1, 2], [1, 3], [2, 3]])
|
|> should.equal([[1, 2], [1, 3], [2, 3]])
|
||||||
|
|
||||||
|
// Compute the Cosine Similarity between two (orthogonal) vectors
|
||||||
|
maths.cosine_similarity([#(-1.0, 1.0), #(1.0, 1.0), #(0.0, -1.0)])
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -8,6 +8,7 @@ gleam = ">= 0.32.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gleam_stdlib = "~> 0.38"
|
gleam_stdlib = "~> 0.38"
|
||||||
|
gleam_yielder = ">= 1.1.0 and < 2.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gleeunit = "~> 1.0"
|
gleeunit = "~> 1.0"
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# You typically do not need to edit this file
|
# You typically do not need to edit this file
|
||||||
|
|
||||||
packages = [
|
packages = [
|
||||||
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
|
{ name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" },
|
||||||
|
{ name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
|
||||||
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[requirements]
|
[requirements]
|
||||||
gleam_stdlib = { version = "~> 0.38" }
|
gleam_stdlib = { version = "~> 0.38" }
|
||||||
|
gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" }
|
||||||
gleeunit = { version = "~> 1.0" }
|
gleeunit = { version = "~> 1.0" }
|
||||||
|
|
5675
src/gleam_community/maths.gleam
Normal file
5675
src/gleam_community/maths.gleam
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,720 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Arithmetics: A module containing a collection of fundamental mathematical functions relating to simple arithmetics (addition, subtraction, multiplication, etc.), but also number theory.
|
|
||||||
////
|
|
||||||
//// * **Division functions**
|
|
||||||
//// * [`gcd`](#gcd)
|
|
||||||
//// * [`lcm`](#lcm)
|
|
||||||
//// * [`divisors`](#divisors)
|
|
||||||
//// * [`proper_divisors`](#proper_divisors)
|
|
||||||
//// * [`int_euclidean_modulo`](#int_euclidean_modulo)
|
|
||||||
//// * **Sums and products**
|
|
||||||
//// * [`float_sum`](#float_sum)
|
|
||||||
//// * [`int_sum`](#int_sum)
|
|
||||||
//// * [`float_product`](#float_product)
|
|
||||||
//// * [`int_product`](#int_product)
|
|
||||||
//// * [`float_cumulative_sum`](#float_cumulative_sum)
|
|
||||||
//// * [`int_cumulative_sum`](#int_cumulative_sum)
|
|
||||||
//// * [`float_cumulative_product`](#float_cumulative_product)
|
|
||||||
//// * [`int_cumulative_product`](#int_cumulative_product)
|
|
||||||
////
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// arithmetics.gcd(1, 1)
|
|
||||||
/// |> should.equal(1)
|
|
||||||
///
|
|
||||||
/// arithmetics.gcd(100, 10)
|
|
||||||
/// |> should.equal(10)
|
|
||||||
///
|
|
||||||
/// arithmetics.gcd(-36, -17)
|
|
||||||
/// |> should.equal(1)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn gcd(x: Int, y: Int) -> Int {
|
|
||||||
let absx = piecewise.int_absolute_value(x)
|
|
||||||
let absy = piecewise.int_absolute_value(y)
|
|
||||||
do_gcd(absx, absy)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_gcd(x: Int, y: Int) -> Int {
|
|
||||||
case x == 0 {
|
|
||||||
True -> y
|
|
||||||
False -> {
|
|
||||||
let assert Ok(z) = int.modulo(y, x)
|
|
||||||
do_gcd(z, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </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:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// 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`.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// arithmetics.euclidean_modulo(15, 4)
|
|
||||||
/// |> should.equal(3)
|
|
||||||
///
|
|
||||||
/// arithmetics.euclidean_modulo(-3, -2)
|
|
||||||
/// |> should.equal(1)
|
|
||||||
///
|
|
||||||
/// arithmetics.euclidean_modulo(5, 0)
|
|
||||||
/// |> should.equal(0)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
|
|
||||||
case x % y, x, y {
|
|
||||||
_, 0, _ -> 0
|
|
||||||
_, _, 0 -> 0
|
|
||||||
md, _, _ if md < 0 -> md + int.absolute_value(y)
|
|
||||||
md, _, _ -> md
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// arithmetics.lcm(1, 1)
|
|
||||||
/// |> should.equal(1)
|
|
||||||
///
|
|
||||||
/// arithmetics.lcm(100, 10)
|
|
||||||
/// |> should.equal(100)
|
|
||||||
///
|
|
||||||
/// arithmetics.lcm(-36, -17)
|
|
||||||
/// |> should.equal(612)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn lcm(x: Int, y: Int) -> Int {
|
|
||||||
let absx = piecewise.int_absolute_value(x)
|
|
||||||
let absy = piecewise.int_absolute_value(y)
|
|
||||||
absx * absy / do_gcd(absx, absy)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns all the positive divisors of an integer, including the
|
|
||||||
/// number itself.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// arithmetics.divisors(4)
|
|
||||||
/// |> should.equal([1, 2, 4])
|
|
||||||
///
|
|
||||||
/// arithmetics.divisors(6)
|
|
||||||
/// |> should.equal([1, 2, 3, 6])
|
|
||||||
///
|
|
||||||
/// arithmetics.proper_divisors(13)
|
|
||||||
/// |> should.equal([1, 13])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn divisors(n: Int) -> List(Int) {
|
|
||||||
find_divisors(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_divisors(n: Int) -> List(Int) {
|
|
||||||
let nabs = piecewise.float_absolute_value(conversion.int_to_float(n))
|
|
||||||
let assert Ok(sqrt_result) = elementary.square_root(nabs)
|
|
||||||
let max = conversion.float_to_int(sqrt_result) + 1
|
|
||||||
list.range(2, max)
|
|
||||||
|> list.fold([1, n], fn(acc, i) {
|
|
||||||
case n % i == 0 {
|
|
||||||
True -> [i, n / i, ..acc]
|
|
||||||
False -> acc
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|> list.unique()
|
|
||||||
|> list.sort(int.compare)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns all the positive divisors of an integer, excluding the
|
|
||||||
/// number iteself.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// arithmetics.proper_divisors(4)
|
|
||||||
/// |> should.equal([1, 2])
|
|
||||||
///
|
|
||||||
/// arithmetics.proper_divisors(6)
|
|
||||||
/// |> should.equal([1, 2, 3])
|
|
||||||
///
|
|
||||||
/// arithmetics.proper_divisors(13)
|
|
||||||
/// |> should.equal([1])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn proper_divisors(n: Int) -> List(Int) {
|
|
||||||
let divisors = find_divisors(n)
|
|
||||||
divisors
|
|
||||||
|> list.take(list.length(divisors) - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Calculate the (weighted) sum of the elements in a list:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// \sum_{i=1}^n w_i x_i
|
|
||||||
/// \\]
|
|
||||||
///
|
|
||||||
/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
|
|
||||||
/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
|
|
||||||
/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
|
|
||||||
///
|
|
||||||
/// <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(option.None)
|
|
||||||
/// |> should.equal(0.0)
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1.0, 2.0, 3.0]
|
|
||||||
/// |> arithmetics.float_sum(option.None)
|
|
||||||
/// |> should.equal(6.0)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
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, a) { a +. acc })
|
|
||||||
_, option.Some(warr) -> {
|
|
||||||
list.zip(arr, warr)
|
|
||||||
|> list.fold(0.0, fn(acc, a) { pair.first(a) *. pair.second(a) +. acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // An empty list returns 0
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.int_sum()
|
|
||||||
/// |> should.equal(0)
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1, 2, 3]
|
|
||||||
/// |> arithmetics.int_sum()
|
|
||||||
/// |> should.equal(6)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_sum(arr: List(Int)) -> Int {
|
|
||||||
case arr {
|
|
||||||
[] -> 0
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.fold(0, fn(acc, a) { a + acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Calculate the (weighted) product of the elements in a list:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// \prod_{i=1}^n x_i^{w_i}
|
|
||||||
/// \\]
|
|
||||||
///
|
|
||||||
/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
|
|
||||||
/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
|
|
||||||
/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // An empty list returns 1.0
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.float_product(option.None)
|
|
||||||
/// |> should.equal(1.0)
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1.0, 2.0, 3.0]
|
|
||||||
/// |> arithmetics.float_product(option.None)
|
|
||||||
/// |> should.equal(6.0)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn float_product(
|
|
||||||
arr: List(Float),
|
|
||||||
weights: option.Option(List(Float)),
|
|
||||||
) -> Result(Float, Nil) {
|
|
||||||
case arr, weights {
|
|
||||||
[], _ ->
|
|
||||||
1.0
|
|
||||||
|> Ok
|
|
||||||
_, option.None ->
|
|
||||||
arr
|
|
||||||
|> list.fold(1.0, fn(acc, a) { a *. acc })
|
|
||||||
|> Ok
|
|
||||||
_, option.Some(warr) -> {
|
|
||||||
list.zip(arr, warr)
|
|
||||||
|> list.map(fn(a: #(Float, Float)) -> Result(Float, Nil) {
|
|
||||||
pair.first(a)
|
|
||||||
|> elementary.power(pair.second(a))
|
|
||||||
})
|
|
||||||
|> result.all
|
|
||||||
|> result.map(fn(prods) {
|
|
||||||
prods
|
|
||||||
|> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // An empty list returns 1
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.int_product()
|
|
||||||
/// |> should.equal(1)
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1, 2, 3]
|
|
||||||
/// |> arithmetics.int_product()
|
|
||||||
/// |> should.equal(6)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_product(arr: List(Int)) -> Int {
|
|
||||||
case arr {
|
|
||||||
[] -> 1
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.fold(1, fn(acc, a) { a * acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.float_cumulative_sum()
|
|
||||||
/// |> should.equal([])
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1.0, 2.0, 3.0]
|
|
||||||
/// |> arithmetics.float_cumulative_sum()
|
|
||||||
/// |> should.equal([1.0, 3.0, 6.0])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
|
|
||||||
case arr {
|
|
||||||
[] -> []
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.scan(0.0, fn(acc, a) { a +. acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.int_cumulative_sum()
|
|
||||||
/// |> should.equal([])
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1, 2, 3]
|
|
||||||
/// |> arithmetics.int_cumulative_sum()
|
|
||||||
/// |> should.equal([1, 3, 6])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
|
|
||||||
case arr {
|
|
||||||
[] -> []
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.scan(0, fn(acc, a) { a + acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // An empty list returns an error
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.float_cumulative_product()
|
|
||||||
/// |> should.equal([])
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1.0, 2.0, 3.0]
|
|
||||||
/// |> arithmetics.float_cumulative_product()
|
|
||||||
/// |> should.equal([1.0, 2.0, 6.0])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
|
|
||||||
case arr {
|
|
||||||
[] -> []
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.scan(1.0, fn(acc, a) { a *. acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/arithmetics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // An empty list returns an error
|
|
||||||
/// []
|
|
||||||
/// |> arithmetics.int_cumulative_product()
|
|
||||||
/// |> should.equal([])
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result
|
|
||||||
/// [1, 2, 3]
|
|
||||||
/// |> arithmetics.int_cumulative_product()
|
|
||||||
/// |> should.equal([1, 2, 6])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_cumulative_product(arr: List(Int)) -> List(Int) {
|
|
||||||
case arr {
|
|
||||||
[] -> []
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.scan(1, fn(acc, a) { a * acc })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,630 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Combinatorics: A module that offers mathematical functions related to counting, arrangements,
|
|
||||||
//// and permutations/combinations.
|
|
||||||
////
|
|
||||||
//// * **Combinatorial functions**
|
|
||||||
//// * [`combination`](#combination)
|
|
||||||
//// * [`factorial`](#factorial)
|
|
||||||
//// * [`permutation`](#permutation)
|
|
||||||
//// * [`list_combination`](#list_combination)
|
|
||||||
//// * [`list_permutation`](#list_permutation)
|
|
||||||
//// * [`cartesian_product`](#cartesian_product)
|
|
||||||
////
|
|
||||||
|
|
||||||
import gleam/iterator
|
|
||||||
import gleam/list
|
|
||||||
import gleam/option
|
|
||||||
import gleam/set
|
|
||||||
import gleam_community/maths/conversion
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
|
|
||||||
pub type CombinatoricsMode {
|
|
||||||
WithRepetitions
|
|
||||||
WithoutRepetitions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 combinatorial function for computing the number of \\(k\\)-combinations of \\(n\\) elements.
|
|
||||||
///
|
|
||||||
/// **Without Repetitions:**
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
|
||||||
/// \\]
|
|
||||||
/// Also known as "\\(n\\) choose \\(k\\)" or the binomial coefficient.
|
|
||||||
///
|
|
||||||
/// **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>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// // Invalid input gives an error
|
|
||||||
/// combinatorics.combination(-1, 1, option.None)
|
|
||||||
/// |> should.be_error()
|
|
||||||
///
|
|
||||||
/// // Valid input: n = 4 and k = 0
|
|
||||||
/// combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
|
|
||||||
/// |> should.equal(Ok(1))
|
|
||||||
///
|
|
||||||
/// // Valid input: k = n (n = 4, k = 4)
|
|
||||||
/// combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
|
|
||||||
/// |> should.equal(Ok(1))
|
|
||||||
///
|
|
||||||
/// // Valid input: combinations with repetition (n = 2, k = 3)
|
|
||||||
/// combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
|
|
||||||
/// |> should.equal(Ok(4))
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn combination(
|
|
||||||
n: Int,
|
|
||||||
k: Int,
|
|
||||||
mode: option.Option(CombinatoricsMode),
|
|
||||||
) -> Result(Int, Nil) {
|
|
||||||
case n, k {
|
|
||||||
_, _ if n < 0 -> Error(Nil)
|
|
||||||
_, _ if k < 0 -> Error(Nil)
|
|
||||||
_, _ -> {
|
|
||||||
case mode {
|
|
||||||
option.Some(WithRepetitions) -> combination_with_repetitions(n, k)
|
|
||||||
_ -> combination_without_repetitions(n, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
|
|
||||||
combination_without_repetitions(n + k - 1, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
|
|
||||||
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, x) { acc * { n + 1 - x } / x })
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 combinatorial function for computing the total number of combinations of \\(n\\)
|
|
||||||
/// elements, that is \\(n!\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// // Invalid input gives an error
|
|
||||||
/// combinatorics.factorial(-1)
|
|
||||||
/// |> should.be_error()
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result (n = 0)
|
|
||||||
/// combinatorics.factorial(0)
|
|
||||||
/// |> should.equal(Ok(1))
|
|
||||||
///
|
|
||||||
/// combinatorics.factorial(3)
|
|
||||||
/// |> should.equal(Ok(6))
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn factorial(n) -> Result(Int, Nil) {
|
|
||||||
case n {
|
|
||||||
_ if n < 0 -> Error(Nil)
|
|
||||||
0 -> Ok(1)
|
|
||||||
1 -> Ok(1)
|
|
||||||
_ ->
|
|
||||||
list.range(1, n)
|
|
||||||
|> list.fold(1, fn(acc, x) { acc * x })
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 combinatorial function for computing the number of \\(k\\)-permutations.
|
|
||||||
///
|
|
||||||
/// **Without** repetitions:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// 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>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// // Invalid input gives an error
|
|
||||||
/// combinatorics.permutation(-1, 1, option.None)
|
|
||||||
/// |> should.be_error()
|
|
||||||
///
|
|
||||||
/// // Valid input returns a result (n = 4, k = 0)
|
|
||||||
/// combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
|
|
||||||
/// |> should.equal(Ok(1))
|
|
||||||
///
|
|
||||||
/// // Valid input returns the correct number of permutations (n = 4, k = 2)
|
|
||||||
/// combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
|
|
||||||
/// |> should.equal(Ok(12))
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn permutation(
|
|
||||||
n: Int,
|
|
||||||
k: Int,
|
|
||||||
mode: option.Option(CombinatoricsMode),
|
|
||||||
) -> Result(Int, Nil) {
|
|
||||||
case n, k, mode {
|
|
||||||
_, _, _ if n < 0 -> Error(Nil)
|
|
||||||
_, _, _ if k < 0 -> Error(Nil)
|
|
||||||
_, _, option.Some(WithRepetitions) -> permutation_with_repetitions(n, k)
|
|
||||||
_, _, _ -> permutation_without_repetitions(n, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
|
|
||||||
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, x) { acc * { n - x } })
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
|
|
||||||
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;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/set
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // Generate all 3-combinations without repetition
|
|
||||||
/// let assert Ok(result) =
|
|
||||||
/// combinatorics.list_combination(
|
|
||||||
/// [1, 2, 3, 4],
|
|
||||||
/// 3,
|
|
||||||
/// option.Some(combinatorics.WithoutRepetitions),
|
|
||||||
/// )
|
|
||||||
///
|
|
||||||
/// result
|
|
||||||
/// |> iterator.to_list()
|
|
||||||
/// |> set.from_list()
|
|
||||||
/// |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn list_combination(
|
|
||||||
arr: List(a),
|
|
||||||
k: Int,
|
|
||||||
mode: option.Option(CombinatoricsMode),
|
|
||||||
) -> Result(iterator.Iterator(List(a)), Nil) {
|
|
||||||
case k, mode {
|
|
||||||
_, _ if k < 0 -> Error(Nil)
|
|
||||||
_, option.Some(WithRepetitions) -> list_combination_with_repetitions(arr, k)
|
|
||||||
_, _ -> list_combination_without_repetitions(arr, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_combination_without_repetitions(
|
|
||||||
arr: List(a),
|
|
||||||
k: Int,
|
|
||||||
) -> Result(iterator.Iterator(List(a)), Nil) {
|
|
||||||
case k, list.length(arr) {
|
|
||||||
_, arr_length if k > arr_length -> Error(Nil)
|
|
||||||
// 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_without_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_without_repetitions(xs, k - 1, [x, ..prefix])
|
|
||||||
let without_x = do_list_combination_without_repetitions(xs, k, prefix)
|
|
||||||
iterator.concat([with_x, without_x])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_combination_with_repetitions(
|
|
||||||
arr: List(a),
|
|
||||||
k: Int,
|
|
||||||
) -> Result(iterator.Iterator(List(a)), Nil) {
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Generates all possible permutations of \\(k\\) elements selected from a given list of size
|
|
||||||
/// \\(n\\).
|
|
||||||
///
|
|
||||||
/// The function can handle cases with and without repetitions
|
|
||||||
/// (see more details [here](#permutation)). Also, note that repeated elements are treated as
|
|
||||||
/// distinct.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/set
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // Generate all 3-permutations without repetition
|
|
||||||
/// let assert Ok(result) =
|
|
||||||
/// combinatorics.list_permutation(
|
|
||||||
/// [1, 2, 3],
|
|
||||||
/// 3,
|
|
||||||
/// option.Some(combinatorics.WithoutRepetitions),
|
|
||||||
/// )
|
|
||||||
///
|
|
||||||
/// result
|
|
||||||
/// |> iterator.to_list()
|
|
||||||
/// |> set.from_list()
|
|
||||||
/// |> should.equal(
|
|
||||||
/// set.from_list([
|
|
||||||
/// [1, 2, 3],
|
|
||||||
/// [2, 1, 3],
|
|
||||||
/// [3, 1, 2],
|
|
||||||
/// [1, 3, 2],
|
|
||||||
/// [2, 3, 1],
|
|
||||||
/// [3, 2, 1],
|
|
||||||
/// ]),
|
|
||||||
/// )
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
///
|
|
||||||
pub fn list_permutation(
|
|
||||||
arr: List(a),
|
|
||||||
k: Int,
|
|
||||||
mode: option.Option(CombinatoricsMode),
|
|
||||||
) -> Result(iterator.Iterator(List(a)), Nil) {
|
|
||||||
case k, mode {
|
|
||||||
_, _ if k < 0 -> Error(Nil)
|
|
||||||
_, option.Some(WithRepetitions) ->
|
|
||||||
Ok(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)), Nil) {
|
|
||||||
case k, list.length(arr) {
|
|
||||||
_, arr_length if k > arr_length -> Error(Nil)
|
|
||||||
_, _ -> {
|
|
||||||
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,
|
|
||||||
) -> iterator.Iterator(List(a)) {
|
|
||||||
let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
|
|
||||||
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] })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Generate a list containing all combinations of pairs of elements coming from two given lists.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/set
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/combinatorics
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// // 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)]),
|
|
||||||
/// )
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn cartesian_product(xset: set.Set(a), yset: set.Set(b)) -> set.Set(#(a, b)) {
|
|
||||||
xset
|
|
||||||
|> set.fold(set.new(), fn(accumulator0: set.Set(#(a, b)), member0: a) {
|
|
||||||
set.fold(yset, accumulator0, fn(accumulator1: set.Set(#(a, b)), member1: b) {
|
|
||||||
set.insert(accumulator1, #(member0, member1))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Conversion: A module containing various functions for converting between types and quantities.
|
|
||||||
////
|
|
||||||
//// * **Misc. functions**
|
|
||||||
//// * [`float_to_int`](#float_to_int)
|
|
||||||
//// * [`int_to_float`](#int_to_float)
|
|
||||||
//// * [`degrees_to_radians`](#degrees_to_radians)
|
|
||||||
//// * [`radians_to_degrees`](#radians_to_degrees)
|
|
||||||
////
|
|
||||||
|
|
||||||
import gleam/int
|
|
||||||
|
|
||||||
/// <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 produces a number of type `Float` from an `Int`.
|
|
||||||
///
|
|
||||||
/// Note: The function is equivalent to the `int.to_float` function in the Gleam stdlib.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/conversion
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// conversion.int_to_float(-1)
|
|
||||||
/// |> should.equal(-1.0)
|
|
||||||
///
|
|
||||||
/// conversion.int_to_float(1)
|
|
||||||
/// |> should.equal(1.0)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn int_to_float(x: Int) -> Float {
|
|
||||||
int.to_float(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam/option
|
|
||||||
/// import gleam_community/maths/conversion
|
|
||||||
/// import gleam_community/maths/piecewise
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// conversion.float_to_int(12.0654)
|
|
||||||
/// |> should.equal(12)
|
|
||||||
///
|
|
||||||
/// // Note: Making the following function call is equivalent
|
|
||||||
/// // but instead of returning a value of type 'Int' a value
|
|
||||||
/// // of type 'Float' is returned.
|
|
||||||
/// piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundToZero))
|
|
||||||
/// |> should.equal(Ok(12.0))
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn float_to_int(x: Float) -> Int {
|
|
||||||
do_to_int(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
@external(erlang, "erlang", "trunc")
|
|
||||||
@external(javascript, "../../maths.mjs", "truncate")
|
|
||||||
fn do_to_int(a: Float) -> Int
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Convert a value in degrees to a value measured in radians.
|
|
||||||
/// That is, \\(1 \text{ degrees } = \frac{\pi}{180} \text{ radians }\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/conversion
|
|
||||||
/// import gleam_community/maths/elementary
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// conversion.degrees_to_radians(360.)
|
|
||||||
/// |> should.equal(2. *. elementary.pi())
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn degrees_to_radians(x: Float) -> Float {
|
|
||||||
x *. do_pi() /. 180.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>
|
|
||||||
///
|
|
||||||
/// Convert a value in degrees to a value measured in radians.
|
|
||||||
/// That is, \\(1 \text{ radians } = \frac{180}{\pi} \text{ degrees }\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/conversion
|
|
||||||
/// import gleam_community/maths/elementary
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// conversion.radians_to_degrees(0.0)
|
|
||||||
/// |> should.equal(0.0)
|
|
||||||
///
|
|
||||||
/// conversion.radians_to_degrees(2. *. elementary.pi())
|
|
||||||
/// |> should.equal(360.)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn radians_to_degrees(x: Float) -> Float {
|
|
||||||
x *. 180.0 /. do_pi()
|
|
||||||
}
|
|
||||||
|
|
||||||
@external(erlang, "math", "pi")
|
|
||||||
@external(javascript, "../../maths.mjs", "pi")
|
|
||||||
fn do_pi() -> Float
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,590 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Predicates: A module containing functions for testing various mathematical
|
|
||||||
//// properties of numbers.
|
|
||||||
////
|
|
||||||
//// * **Tests**
|
|
||||||
//// * [`is_close`](#is_close)
|
|
||||||
//// * [`list_all_close`](#all_close)
|
|
||||||
//// * [`is_fractional`](#is_fractional)
|
|
||||||
//// * [`is_between`](#is_between)
|
|
||||||
//// * [`is_power`](#is_power)
|
|
||||||
//// * [`is_perfect`](#is_perfect)
|
|
||||||
//// * [`is_even`](#is_even)
|
|
||||||
//// * [`is_odd`](#is_odd)
|
|
||||||
//// * [`is_divisible`](#is_divisible)
|
|
||||||
//// * [`is_multiple`](#is_multiple)
|
|
||||||
//// * [`is_prime`](#is_prime)
|
|
||||||
////
|
|
||||||
|
|
||||||
import gleam/int
|
|
||||||
import gleam/list
|
|
||||||
import gleam/option
|
|
||||||
import gleam/pair
|
|
||||||
import gleam_community/maths/arithmetics
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/piecewise
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </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:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// \|a - b\| \leq (a_{tol} + r_{tol} \cdot \|b\|)
|
|
||||||
/// \\]
|
|
||||||
///
|
|
||||||
/// `True` is returned if statement holds, otherwise `False` is returned.
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// let val = 99.
|
|
||||||
/// let ref_val = 100.
|
|
||||||
/// // We set 'atol' and 'rtol' such that the values are equivalent
|
|
||||||
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
|
||||||
/// let rtol = 0.01
|
|
||||||
/// let atol = 0.10
|
|
||||||
/// floatx.is_close(val, ref_val, rtol, atol)
|
|
||||||
/// |> should.be_true()
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_close(a: Float, b: Float, rtol: Float, atol: Float) -> Bool {
|
|
||||||
let x = float_absolute_difference(a, b)
|
|
||||||
let y = atol +. rtol *. float_absolute_value(b)
|
|
||||||
case x <=. y {
|
|
||||||
True -> True
|
|
||||||
False -> False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn float_absolute_value(x: Float) -> Float {
|
|
||||||
case x >. 0.0 {
|
|
||||||
True -> x
|
|
||||||
False -> -1.0 *. x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn float_absolute_difference(a: Float, b: Float) -> Float {
|
|
||||||
a -. b
|
|
||||||
|> float_absolute_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// 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/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// let val = 99.
|
|
||||||
/// let ref_val = 100.
|
|
||||||
/// let xarr = list.repeat(val, 42)
|
|
||||||
/// let yarr = list.repeat(ref_val, 42)
|
|
||||||
/// // We set 'atol' and 'rtol' such that the values are equivalent
|
|
||||||
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
|
||||||
/// let rtol = 0.01
|
|
||||||
/// let atol = 0.10
|
|
||||||
/// predicates.all_close(xarr, yarr, rtol, atol)
|
|
||||||
/// |> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) {
|
|
||||||
/// case zarr {
|
|
||||||
/// Ok(arr) ->
|
|
||||||
/// arr
|
|
||||||
/// |> list.all(fn(a) { a })
|
|
||||||
/// |> Ok
|
|
||||||
/// _ -> Nil |> Error
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// |> should.equal(Ok(True))
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn all_close(
|
|
||||||
xarr: List(Float),
|
|
||||||
yarr: List(Float),
|
|
||||||
rtol: Float,
|
|
||||||
atol: Float,
|
|
||||||
) -> Result(List(Bool), Nil) {
|
|
||||||
let xlen = list.length(xarr)
|
|
||||||
let ylen = list.length(yarr)
|
|
||||||
case xlen == ylen {
|
|
||||||
False -> Error(Nil)
|
|
||||||
True ->
|
|
||||||
list.zip(xarr, yarr)
|
|
||||||
|> list.map(fn(z) { is_close(pair.first(z), pair.second(z), rtol, atol) })
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// Determine if a given value is fractional.
|
|
||||||
///
|
|
||||||
/// `True` is returned if the given value is fractional, otherwise `False` is
|
|
||||||
/// returned.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// predicates.is_fractional(0.3333)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_fractional(1.0)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_fractional(x: Float) -> Bool {
|
|
||||||
do_ceiling(x) -. x >. 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
@external(erlang, "math", "ceil")
|
|
||||||
@external(javascript, "../../maths.mjs", "ceiling")
|
|
||||||
fn do_ceiling(a: Float) -> Float
|
|
||||||
|
|
||||||
/// <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
|
|
||||||
/// power of another integer value \\(y \in \mathbb{Z}\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// // Check if 4 is a power of 2 (it is)
|
|
||||||
/// predicates.is_power(4, 2)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// // Check if 5 is a power of 2 (it is not)
|
|
||||||
/// predicates.is_power(5, 2)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_power(x: Int, y: Int) -> Bool {
|
|
||||||
let assert Ok(value) =
|
|
||||||
elementary.logarithm(int.to_float(x), option.Some(int.to_float(y)))
|
|
||||||
let truncated = piecewise.truncate(value, option.Some(0))
|
|
||||||
let remainder = value -. truncated
|
|
||||||
remainder == 0.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 \\(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>
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
/// - \\(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\\).
|
|
||||||
///
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_perfect(6)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_perfect(28)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_perfect(n: Int) -> Bool {
|
|
||||||
do_sum(arithmetics.proper_divisors(n)) == n
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_sum(arr: List(Int)) -> Int {
|
|
||||||
case arr {
|
|
||||||
[] -> 0
|
|
||||||
_ ->
|
|
||||||
arr
|
|
||||||
|> list.fold(0, fn(acc, a) { a + acc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 even.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_even(-3)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
///
|
|
||||||
/// predicates.is_even(-4)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_even(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 odd.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_odd(-3)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_odd(-4)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
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 = 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 = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 real number \\(x \in \mathbb{R}\\) is strictly
|
|
||||||
/// between two other real numbers, \\(a,b \in \mathbb{R}\\), such that \\(a < x < b\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_between(5.5, 5.0, 6.0)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_between(5.0, 5.0, 6.0)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
///
|
|
||||||
/// predicates.is_between(6.0, 5.0, 6.0)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool {
|
|
||||||
lower <. x && x <. upper
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 \\(n \in \mathbb{Z}\\) is divisible by another
|
|
||||||
/// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Details</summary>
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
/// - \\(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\\).
|
|
||||||
///
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_divisible(10, 2)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_divisible(7, 3)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_divisible(n: Int, d: Int) -> Bool {
|
|
||||||
n % d == 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 \\(m \in \mathbb{Z}\\) is a multiple of another
|
|
||||||
/// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \times q\\), with \\(q \in \mathbb{Z}\\).
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Details</summary>
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
/// - \\(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.
|
|
||||||
///
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example() {
|
|
||||||
/// predicates.is_multiple(15, 5)
|
|
||||||
/// |> should.equal(True)
|
|
||||||
///
|
|
||||||
/// predicates.is_multiple(14, 5)
|
|
||||||
/// |> should.equal(False)
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn is_multiple(m: Int, k: Int) -> Bool {
|
|
||||||
m % k == 0
|
|
||||||
}
|
|
|
@ -1,328 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Sequences: A module containing functions for generating various types of
|
|
||||||
//// sequences, ranges and intervals.
|
|
||||||
////
|
|
||||||
//// * **Ranges and intervals**
|
|
||||||
//// * [`arange`](#arange)
|
|
||||||
//// * [`linear_space`](#linear_space)
|
|
||||||
//// * [`logarithmic_space`](#logarithmic_space)
|
|
||||||
//// * [`geometric_space`](#geometric_space)
|
|
||||||
////
|
|
||||||
|
|
||||||
import gleam/iterator
|
|
||||||
import gleam_community/maths/conversion
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/piecewise
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns an iterator generating evenly spaced values within a given interval.
|
|
||||||
/// based on a start value but excludes the stop value. The spacing between values is determined
|
|
||||||
/// by the step size provided. The function supports both positive and negative step values.
|
|
||||||
///
|
|
||||||
/// <details>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/sequences
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// sequences.arange(1.0, 5.0, 1.0)
|
|
||||||
/// |> iterator.to_list()
|
|
||||||
/// |> should.equal([1.0, 2.0, 3.0, 4.0])
|
|
||||||
///
|
|
||||||
/// // No points returned since
|
|
||||||
/// // start is smaller than stop and the step is positive
|
|
||||||
/// sequences.arange(5.0, 1.0, 1.0)
|
|
||||||
/// |> iterator.to_list()
|
|
||||||
/// |> should.equal([])
|
|
||||||
///
|
|
||||||
/// // Points returned since
|
|
||||||
/// // start smaller than stop but negative step
|
|
||||||
/// sequences.arange(5.0, 1.0, -1.0)
|
|
||||||
/// |> iterator.to_list()
|
|
||||||
/// |> should.equal([5.0, 4.0, 3.0, 2.0])
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn arange(
|
|
||||||
start: Float,
|
|
||||||
stop: Float,
|
|
||||||
step: Float,
|
|
||||||
) -> iterator.Iterator(Float) {
|
|
||||||
case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 {
|
|
||||||
True -> iterator.empty()
|
|
||||||
False -> {
|
|
||||||
let direction = case start <=. stop {
|
|
||||||
True -> {
|
|
||||||
1.0
|
|
||||||
}
|
|
||||||
False -> {
|
|
||||||
-1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let step_abs = piecewise.float_absolute_value(step)
|
|
||||||
let num =
|
|
||||||
piecewise.float_absolute_value(start -. stop) /. step_abs
|
|
||||||
|> conversion.float_to_int()
|
|
||||||
|
|
||||||
iterator.range(0, num - 1)
|
|
||||||
|> iterator.map(fn(i) {
|
|
||||||
start +. conversion.int_to_float(i) *. step_abs *. direction
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns an iterator for generating linearly spaced points over a specified
|
|
||||||
/// 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>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/elementary
|
|
||||||
/// import gleam_community/maths/sequences
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
/// let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
|
|
||||||
/// let assert Ok(result) =
|
|
||||||
/// predicates.all_close(
|
|
||||||
/// linspace |> iterator.to_list(),
|
|
||||||
/// [10.0, 12.5, 15.0, 17.5, 20.0],
|
|
||||||
/// 0.0,
|
|
||||||
/// tol,
|
|
||||||
/// )
|
|
||||||
///
|
|
||||||
/// result
|
|
||||||
/// |> list.all(fn(x) { x == True })
|
|
||||||
/// |> should.be_true()
|
|
||||||
///
|
|
||||||
/// // A negative number of points (-5) does not work
|
|
||||||
/// sequences.linear_space(10.0, 50.0, -5, True)
|
|
||||||
/// |> should.be_error()
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn linear_space(
|
|
||||||
start: Float,
|
|
||||||
stop: Float,
|
|
||||||
num: Int,
|
|
||||||
endpoint: Bool,
|
|
||||||
) -> Result(iterator.Iterator(Float), Nil) {
|
|
||||||
let direction = case start <=. stop {
|
|
||||||
True -> 1.0
|
|
||||||
False -> -1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
start +. conversion.int_to_float(i) *. increment *. direction
|
|
||||||
})
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
False -> Error(Nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns an iterator of logarithmically spaced points over a specified interval.
|
|
||||||
/// 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>
|
|
||||||
/// <summary>Example:</summary>
|
|
||||||
///
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/elementary
|
|
||||||
/// import gleam_community/maths/sequences
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// 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(result) =
|
|
||||||
/// predicates.all_close(
|
|
||||||
/// logspace |> iterator.to_list(),
|
|
||||||
/// [10.0, 100.0, 1000.0],
|
|
||||||
/// 0.0,
|
|
||||||
/// tol,
|
|
||||||
/// )
|
|
||||||
/// result
|
|
||||||
/// |> list.all(fn(x) { x == True })
|
|
||||||
/// |> should.be_true()
|
|
||||||
///
|
|
||||||
/// // A negative number of points (-3) does not work
|
|
||||||
/// sequences.logarithmic_space(1.0, 3.0, -3, False, 10.0)
|
|
||||||
/// |> should.be_error()
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn logarithmic_space(
|
|
||||||
start: Float,
|
|
||||||
stop: Float,
|
|
||||||
num: Int,
|
|
||||||
endpoint: Bool,
|
|
||||||
base: Float,
|
|
||||||
) -> Result(iterator.Iterator(Float), Nil) {
|
|
||||||
case num > 0 {
|
|
||||||
True -> {
|
|
||||||
let assert Ok(linspace) = linear_space(start, stop, num, endpoint)
|
|
||||||
linspace
|
|
||||||
|> iterator.map(fn(i) {
|
|
||||||
let assert Ok(result) = elementary.power(base, i)
|
|
||||||
result
|
|
||||||
})
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
False -> Error(Nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The function returns an iterator 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>
|
|
||||||
///
|
|
||||||
/// import gleam/iterator
|
|
||||||
/// import gleeunit/should
|
|
||||||
/// import gleam_community/maths/elementary
|
|
||||||
/// import gleam_community/maths/sequences
|
|
||||||
/// import gleam_community/maths/predicates
|
|
||||||
///
|
|
||||||
/// pub fn example () {
|
|
||||||
/// 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(result) =
|
|
||||||
/// predicates.all_close(
|
|
||||||
/// logspace |> iterator.to_list(),
|
|
||||||
/// [10.0, 100.0, 1000.0],
|
|
||||||
/// 0.0,
|
|
||||||
/// tol,
|
|
||||||
/// )
|
|
||||||
/// result
|
|
||||||
/// |> list.all(fn(x) { x == True })
|
|
||||||
/// |> should.be_true()
|
|
||||||
///
|
|
||||||
/// // Input (start and stop can't be equal to 0.0)
|
|
||||||
/// sequences.geometric_space(0.0, 1000.0, 3, False)
|
|
||||||
/// |> should.be_error()
|
|
||||||
///
|
|
||||||
/// sequences.geometric_space(-1000.0, 0.0, 3, False)
|
|
||||||
/// |> should.be_error()
|
|
||||||
///
|
|
||||||
/// // A negative number of points (-3) does not work
|
|
||||||
/// sequences.geometric_space(10.0, 1000.0, -3, False)
|
|
||||||
/// |> should.be_error()
|
|
||||||
/// }
|
|
||||||
/// </details>
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn geometric_space(
|
|
||||||
start: Float,
|
|
||||||
stop: Float,
|
|
||||||
num: Int,
|
|
||||||
endpoint: Bool,
|
|
||||||
) -> Result(iterator.Iterator(Float), Nil) {
|
|
||||||
case start == 0.0 || stop == 0.0 {
|
|
||||||
True -> Error(Nil)
|
|
||||||
False ->
|
|
||||||
case num > 0 {
|
|
||||||
True -> {
|
|
||||||
let assert Ok(log_start) = elementary.logarithm_10(start)
|
|
||||||
let assert Ok(log_stop) = elementary.logarithm_10(stop)
|
|
||||||
logarithmic_space(log_start, log_stop, num, endpoint, 10.0)
|
|
||||||
}
|
|
||||||
False -> Error(Nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
////<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.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" 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>
|
|
||||||
//// document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
//// renderMathInElement(document.body, {
|
|
||||||
//// // customised options
|
|
||||||
//// // • auto-render specific keys, e.g.:
|
|
||||||
//// delimiters: [
|
|
||||||
//// {left: '$$', right: '$$', display: false},
|
|
||||||
//// {left: '$', right: '$', display: false},
|
|
||||||
//// {left: '\\(', right: '\\)', display: false},
|
|
||||||
//// {left: '\\[', right: '\\]', display: true}
|
|
||||||
//// ],
|
|
||||||
//// // • rendering keys, e.g.:
|
|
||||||
//// throwOnError : true
|
|
||||||
//// });
|
|
||||||
//// });
|
|
||||||
////</script>
|
|
||||||
////<style>
|
|
||||||
//// .katex { font-size: 1.1em; }
|
|
||||||
////</style>
|
|
||||||
////
|
|
||||||
//// ---
|
|
||||||
////
|
|
||||||
//// Special: A module containing special mathematical functions.
|
|
||||||
////
|
|
||||||
//// * **Special mathematical functions**
|
|
||||||
//// * [`beta`](#beta)
|
|
||||||
//// * [`erf`](#erf)
|
|
||||||
//// * [`gamma`](#gamma)
|
|
||||||
//// * [`incomplete_gamma`](#incomplete_gamma)
|
|
||||||
////
|
|
||||||
|
|
||||||
import gleam/list
|
|
||||||
import gleam_community/maths/conversion
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/piecewise
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The beta function over the real numbers:
|
|
||||||
///
|
|
||||||
/// \\[
|
|
||||||
/// \text{B}(x, y) = \frac{\Gamma(x) \cdot \Gamma(y)}{\Gamma(x + y)}
|
|
||||||
/// \\]
|
|
||||||
///
|
|
||||||
/// The beta function is evaluated through the use of the gamma function.
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn beta(x: Float, y: Float) -> Float {
|
|
||||||
gamma(x) *. gamma(y) /. gamma(x +. y)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The error function.
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn erf(x: Float) -> Float {
|
|
||||||
let assert [a1, a2, a3, a4, a5] = [
|
|
||||||
0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429,
|
|
||||||
]
|
|
||||||
let p = 0.3275911
|
|
||||||
|
|
||||||
let sign = piecewise.float_sign(x)
|
|
||||||
let x = piecewise.float_absolute_value(x)
|
|
||||||
|
|
||||||
// Formula 7.1.26 given in Abramowitz and Stegun.
|
|
||||||
let t = 1.0 /. { 1.0 +. p *. x }
|
|
||||||
let y =
|
|
||||||
1.0
|
|
||||||
-. { { { { a5 *. t +. a4 } *. t +. a3 } *. t +. a2 } *. t +. a1 }
|
|
||||||
*. t
|
|
||||||
*. elementary.exponential(-1.0 *. x *. x)
|
|
||||||
sign *. y
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </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 implemented gamma function is approximated through Lanczos approximation
|
|
||||||
/// using the same coefficients used by the GNU Scientific Library.
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn gamma(x: Float) -> Float {
|
|
||||||
gamma_lanczos(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
const lanczos_g: Float = 7.0
|
|
||||||
|
|
||||||
const lanczos_p: List(Float) = [
|
|
||||||
0.99999999999980993, 676.5203681218851, -1259.1392167224028,
|
|
||||||
771.32342877765313, -176.61502916214059, 12.507343278686905,
|
|
||||||
-0.13857109526572012, 0.0000099843695780195716, 0.00000015056327351493116,
|
|
||||||
]
|
|
||||||
|
|
||||||
fn gamma_lanczos(x: Float) -> Float {
|
|
||||||
case x <. 0.5 {
|
|
||||||
True ->
|
|
||||||
elementary.pi()
|
|
||||||
/. { elementary.sin(elementary.pi() *. x) *. gamma_lanczos(1.0 -. x) }
|
|
||||||
False -> {
|
|
||||||
let z = x -. 1.0
|
|
||||||
let x =
|
|
||||||
list.index_fold(lanczos_p, 0.0, fn(acc, v, index) {
|
|
||||||
case index > 0 {
|
|
||||||
True -> acc +. v /. { z +. conversion.int_to_float(index) }
|
|
||||||
False -> v
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let t = z +. lanczos_g +. 0.5
|
|
||||||
let assert Ok(v1) = elementary.power(2.0 *. elementary.pi(), 0.5)
|
|
||||||
let assert Ok(v2) = elementary.power(t, z +. 0.5)
|
|
||||||
v1 *. v2 *. elementary.exponential(-1.0 *. t) *. x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
|
||||||
/// <small>Spot a typo? Open an issue!</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// The lower incomplete gamma function over the real numbers.
|
|
||||||
///
|
|
||||||
/// The implemented incomplete gamma function is evaluated through a power series
|
|
||||||
/// expansion.
|
|
||||||
///
|
|
||||||
/// <div style="text-align: right;">
|
|
||||||
/// <a href="#">
|
|
||||||
/// <small>Back to top ↑</small>
|
|
||||||
/// </a>
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, Nil) {
|
|
||||||
case a >. 0.0 && x >=. 0.0 {
|
|
||||||
True -> {
|
|
||||||
let assert Ok(v) = elementary.power(x, a)
|
|
||||||
v
|
|
||||||
*. elementary.exponential(-1.0 *. x)
|
|
||||||
*. incomplete_gamma_sum(a, x, 1.0 /. a, 0.0, 1.0)
|
|
||||||
|> Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
False -> Error(Nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn incomplete_gamma_sum(
|
|
||||||
a: Float,
|
|
||||||
x: Float,
|
|
||||||
t: Float,
|
|
||||||
s: Float,
|
|
||||||
n: Float,
|
|
||||||
) -> Float {
|
|
||||||
case t {
|
|
||||||
0.0 -> s
|
|
||||||
_ -> {
|
|
||||||
let ns = s +. t
|
|
||||||
let nt = t *. { x /. { a +. n } }
|
|
||||||
incomplete_gamma_sum(a, x, nt, ns, n +. 1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
203
test/gleam_community/arithmetics_test.gleam
Normal file
203
test/gleam_community/arithmetics_test.gleam
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn int_gcd_test() {
|
||||||
|
maths.gcd(1, 1)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
maths.gcd(100, 10)
|
||||||
|
|> should.equal(10)
|
||||||
|
|
||||||
|
maths.gcd(10, 100)
|
||||||
|
|> should.equal(10)
|
||||||
|
|
||||||
|
maths.gcd(100, -10)
|
||||||
|
|> should.equal(10)
|
||||||
|
|
||||||
|
maths.gcd(-36, -17)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
maths.gcd(-30, -42)
|
||||||
|
|> should.equal(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_euclidean_modulo_test() {
|
||||||
|
// Base Case: Positive x, Positive y
|
||||||
|
// Note that the truncated, floored, and euclidean
|
||||||
|
// definitions should agree for this base case
|
||||||
|
maths.int_euclidean_modulo(15, 4)
|
||||||
|
|> should.equal(3)
|
||||||
|
|
||||||
|
// Case: Positive x, Negative y
|
||||||
|
maths.int_euclidean_modulo(15, -4)
|
||||||
|
|> should.equal(3)
|
||||||
|
|
||||||
|
// Case: Negative x, Positive y
|
||||||
|
maths.int_euclidean_modulo(-15, 4)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
// Case: Negative x, Negative y
|
||||||
|
maths.int_euclidean_modulo(-15, -4)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
// Case: Positive x, Zero y
|
||||||
|
maths.int_euclidean_modulo(5, 0)
|
||||||
|
|> should.equal(0)
|
||||||
|
|
||||||
|
// Case: Zero x, Negative y
|
||||||
|
maths.int_euclidean_modulo(0, 5)
|
||||||
|
|> should.equal(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_lcm_test() {
|
||||||
|
maths.lcm(1, 1)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
maths.lcm(100, 10)
|
||||||
|
|> should.equal(100)
|
||||||
|
|
||||||
|
maths.lcm(10, 100)
|
||||||
|
|> should.equal(100)
|
||||||
|
|
||||||
|
maths.lcm(100, -10)
|
||||||
|
|> should.equal(100)
|
||||||
|
|
||||||
|
maths.lcm(-36, -17)
|
||||||
|
|> should.equal(612)
|
||||||
|
|
||||||
|
maths.lcm(-30, -42)
|
||||||
|
|> should.equal(210)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_proper_divisors_test() {
|
||||||
|
maths.proper_divisors(2)
|
||||||
|
|> should.equal([1])
|
||||||
|
|
||||||
|
maths.proper_divisors(6)
|
||||||
|
|> should.equal([1, 2, 3])
|
||||||
|
|
||||||
|
maths.proper_divisors(13)
|
||||||
|
|> should.equal([1])
|
||||||
|
|
||||||
|
maths.proper_divisors(18)
|
||||||
|
|> should.equal([1, 2, 3, 6, 9])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_divisors_test() {
|
||||||
|
maths.divisors(2)
|
||||||
|
|> should.equal([1, 2])
|
||||||
|
|
||||||
|
maths.divisors(6)
|
||||||
|
|> should.equal([1, 2, 3, 6])
|
||||||
|
|
||||||
|
maths.divisors(13)
|
||||||
|
|> should.equal([1, 13])
|
||||||
|
|
||||||
|
maths.divisors(18)
|
||||||
|
|> should.equal([1, 2, 3, 6, 9, 18])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_cumulative_sum_test() {
|
||||||
|
// An empty lists returns an empty list
|
||||||
|
[]
|
||||||
|
|> maths.float_cumulative_sum()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.float_cumulative_sum()
|
||||||
|
|> should.equal([1.0, 3.0, 6.0])
|
||||||
|
|
||||||
|
[-2.0, 4.0, 6.0]
|
||||||
|
|> maths.float_cumulative_sum()
|
||||||
|
|> should.equal([-2.0, 2.0, 8.0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_cumulative_sum_test() {
|
||||||
|
// An empty lists returns an empty list
|
||||||
|
[]
|
||||||
|
|> maths.int_cumulative_sum()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1, 2, 3]
|
||||||
|
|> maths.int_cumulative_sum()
|
||||||
|
|> should.equal([1, 3, 6])
|
||||||
|
|
||||||
|
[-2, 4, 6]
|
||||||
|
|> maths.int_cumulative_sum()
|
||||||
|
|> should.equal([-2, 2, 8])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_cumulative_product_test() {
|
||||||
|
// An empty lists returns an empty list
|
||||||
|
[]
|
||||||
|
|> maths.float_cumulative_product()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.float_cumulative_product()
|
||||||
|
|> should.equal([1.0, 2.0, 6.0])
|
||||||
|
|
||||||
|
[-2.0, 4.0, 6.0]
|
||||||
|
|> maths.float_cumulative_product()
|
||||||
|
|> should.equal([-2.0, -8.0, -48.0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_cumulative_product_test() {
|
||||||
|
// An empty lists returns an empty list
|
||||||
|
[]
|
||||||
|
|> maths.int_cumulative_product()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1, 2, 3]
|
||||||
|
|> maths.int_cumulative_product()
|
||||||
|
|> should.equal([1, 2, 6])
|
||||||
|
|
||||||
|
[-2, 4, 6]
|
||||||
|
|> maths.int_cumulative_product()
|
||||||
|
|> should.equal([-2, -8, -48])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_weighted_product_test() {
|
||||||
|
[]
|
||||||
|
|> maths.float_weighted_product()
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
[#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
|
||||||
|
|> maths.float_weighted_product()
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
[#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
|
||||||
|
|> maths.float_weighted_product()
|
||||||
|
|> should.equal(Ok(6.0))
|
||||||
|
|
||||||
|
let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
[#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
|
||||||
|
|> maths.float_weighted_product()
|
||||||
|
result
|
||||||
|
|> maths.is_close(30.0, 0.0, tolerance)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_weighted_sum_test() {
|
||||||
|
[]
|
||||||
|
|> maths.float_weighted_sum()
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
[#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
|
||||||
|
|> maths.float_weighted_sum()
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
[#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
|
||||||
|
|> maths.float_weighted_sum()
|
||||||
|
|> should.equal(Ok(6.0))
|
||||||
|
|
||||||
|
[#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
|
||||||
|
|> maths.float_weighted_sum()
|
||||||
|
|> should.equal(Ok(14.5))
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
113
test/gleam_community/conversion_test.gleam
Normal file
113
test/gleam_community/conversion_test.gleam
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_to_degree_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
maths.radians_to_degrees(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.radians_to_degrees(2.0 *. maths.pi())
|
||||||
|
|> maths.is_close(360.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_to_radian_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
maths.degrees_to_radians(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.degrees_to_radians(360.0)
|
||||||
|
|> maths.is_close(2.0 *. maths.pi(), 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_cartesian_to_polar_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Test: Cartesian (1, 0) -> Polar (1, 0)
|
||||||
|
let #(r, theta) = maths.cartesian_to_polar(1.0, 0.0)
|
||||||
|
r
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
theta
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Cartesian (0, 1) -> Polar (1, pi/2)
|
||||||
|
let #(r, theta) = maths.cartesian_to_polar(0.0, 1.0)
|
||||||
|
r
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
theta
|
||||||
|
|> maths.is_close(maths.pi() /. 2.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Cartesian (-1, 0) -> Polar (1, pi)
|
||||||
|
let #(r, theta) = maths.cartesian_to_polar(-1.0, 0.0)
|
||||||
|
r
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
theta
|
||||||
|
|> maths.is_close(maths.pi(), 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Cartesian (0, -1) -> Polar (1, -pi/2)
|
||||||
|
let #(r, theta) = maths.cartesian_to_polar(0.0, -1.0)
|
||||||
|
r
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
theta
|
||||||
|
|> maths.is_close(-1.0 *. maths.pi() /. 2.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_polar_to_cartesian_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Test: Polar (1, 0) -> Cartesian (1, 0)
|
||||||
|
let #(x, y) = maths.polar_to_cartesian(1.0, 0.0)
|
||||||
|
x
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
y
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Polar (1, pi/2) -> Cartesian (0, 1)
|
||||||
|
let #(x, y) = maths.polar_to_cartesian(1.0, maths.pi() /. 2.0)
|
||||||
|
x
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
y
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Polar (1, pi) -> Cartesian (-1, 0)
|
||||||
|
let #(x, y) = maths.polar_to_cartesian(1.0, maths.pi())
|
||||||
|
x
|
||||||
|
|> maths.is_close(-1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
y
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test: Polar (1, -pi/2) -> Cartesian (0, -1)
|
||||||
|
let #(x, y) = maths.polar_to_cartesian(1.0, -1.0 *. maths.pi() /. 2.0)
|
||||||
|
x
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
y
|
||||||
|
|> maths.is_close(-1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
419
test/gleam_community/elementary_test.gleam
Normal file
419
test/gleam_community/elementary_test.gleam
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_acos_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) = maths.acos(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.acos(0.5)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.047197, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.acos(1.1)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.acos(-1.1)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_acosh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) = maths.acosh(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.acosh(0.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_asin_test() {
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.asin(0.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
let assert Ok(result) = maths.asin(0.5)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.523598, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.asin(1.1)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.asin(-1.1)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_asinh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.asinh(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.asinh(0.5)
|
||||||
|
|> maths.is_close(0.481211, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_atan_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.atan(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.atan(0.5)
|
||||||
|
|> maths.is_close(0.463647, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_atan2_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.atan2(0.0, 0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.atan2(0.0, 1.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check atan2(y=1.0, x=0.5)
|
||||||
|
// Should be equal to atan(y / x) for any x > 0 and any y
|
||||||
|
let result = maths.atan(1.0 /. 0.5)
|
||||||
|
maths.atan2(1.0, 0.5)
|
||||||
|
|> maths.is_close(result, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check atan2(y=2.0, x=-1.5)
|
||||||
|
// Should be equal to pi + atan(y / x) for any x < 0 and y >= 0
|
||||||
|
let result = maths.pi() +. maths.atan(2.0 /. -1.5)
|
||||||
|
maths.atan2(2.0, -1.5)
|
||||||
|
|> maths.is_close(result, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check atan2(y=-2.0, x=-1.5)
|
||||||
|
// Should be equal to atan(y / x) - pi for any x < 0 and y < 0
|
||||||
|
let result = maths.atan(-2.0 /. -1.5) -. maths.pi()
|
||||||
|
maths.atan2(-2.0, -1.5)
|
||||||
|
|> maths.is_close(result, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check atan2(y=1.5, x=0.0)
|
||||||
|
// Should be equal to pi/2 for x = 0 and any y > 0
|
||||||
|
let result = maths.pi() /. 2.0
|
||||||
|
maths.atan2(1.5, 0.0)
|
||||||
|
|> maths.is_close(result, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check atan2(y=-1.5, x=0.0)
|
||||||
|
// Should be equal to -pi/2 for x = 0 and any y < 0
|
||||||
|
let result = -1.0 *. maths.pi() /. 2.0
|
||||||
|
maths.atan2(-1.5, 0.0)
|
||||||
|
|> maths.is_close(result, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_atanh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) = maths.atanh(0.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.atanh(0.5)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.549306, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.atanh(1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.atanh(2.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.atanh(1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.atanh(-2.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_cos_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.cos(0.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.cos(maths.pi())
|
||||||
|
|> maths.is_close(-1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.cos(0.5)
|
||||||
|
|> maths.is_close(0.877582, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_cosh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.cosh(0.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.cosh(0.5)
|
||||||
|
|> maths.is_close(1.127625, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
// An (overflow) error might occur when given an input
|
||||||
|
// value that will result in a too large output value
|
||||||
|
// e.g. maths.cosh(1000.0) but this is a property of the
|
||||||
|
// runtime.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_sin_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.sin(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.sin(0.5 *. maths.pi())
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.sin(0.5)
|
||||||
|
|> maths.is_close(0.479425, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_sinh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.sinh(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.sinh(0.5)
|
||||||
|
|> maths.is_close(0.521095, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
// An (overflow) error might occur when given an input
|
||||||
|
// value that will result in a too large output value
|
||||||
|
// e.g. maths.sinh(1000.0) but this is a property of the
|
||||||
|
// runtime.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_tan_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.tan(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.tan(0.5)
|
||||||
|
|> maths.is_close(0.546302, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_tanh_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.tanh(0.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.tanh(25.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.tanh(-25.0)
|
||||||
|
|> maths.is_close(-1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.tanh(0.5)
|
||||||
|
|> maths.is_close(0.462117, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_exponential_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.exponential(0.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.exponential(0.5)
|
||||||
|
|> maths.is_close(1.648721, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
// An (overflow) error might occur when given an input
|
||||||
|
// value that will result in a too large output value
|
||||||
|
// e.g. maths.exponential(1000.0) but this is a property of the
|
||||||
|
// runtime.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_natural_logarithm_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.natural_logarithm(1.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.natural_logarithm(0.5)
|
||||||
|
result
|
||||||
|
|> maths.is_close(-0.693147, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.natural_logarithm(-1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_logarithm_test() {
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.logarithm(10.0, 10.0)
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
maths.logarithm(10.0, 100.0)
|
||||||
|
|> should.equal(Ok(0.5))
|
||||||
|
|
||||||
|
maths.logarithm(1.0, 0.25)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.logarithm(1.0, 1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.logarithm(10.0, 1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.logarithm(-1.0, 1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.logarithm(1.0, 10.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
maths.logarithm(maths.e(), maths.e())
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
maths.logarithm(-1.0, 2.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_logarithm_2_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
maths.logarithm_2(1.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
maths.logarithm_2(2.0)
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.logarithm_2(5.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(2.321928, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.logarithm_2(-1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_logarithm_10_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) = maths.logarithm_10(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.logarithm_10(10.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) = maths.logarithm_10(50.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.69897, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Check that we get an error when the function is evaluated
|
||||||
|
// outside its domain
|
||||||
|
maths.logarithm_10(-1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_nth_root_test() {
|
||||||
|
maths.nth_root(9.0, 2)
|
||||||
|
|> should.equal(Ok(3.0))
|
||||||
|
|
||||||
|
maths.nth_root(27.0, 3)
|
||||||
|
|> should.equal(Ok(3.0))
|
||||||
|
|
||||||
|
maths.nth_root(1.0, 4)
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
maths.nth_root(256.0, 4)
|
||||||
|
|> should.equal(Ok(4.0))
|
||||||
|
|
||||||
|
// An error should be returned as an imaginary number would otherwise
|
||||||
|
// have to be returned
|
||||||
|
maths.nth_root(-1.0, 4)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_constants_test() {
|
||||||
|
let assert Ok(tolerance) = float.power(10.0, -12.0)
|
||||||
|
|
||||||
|
// Test that the constant is approximately equal to 2.7128...
|
||||||
|
maths.e()
|
||||||
|
|> maths.is_close(2.7182818284590452353602, 0.0, tolerance)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test that the constant is approximately equal to 2.7128...
|
||||||
|
maths.pi()
|
||||||
|
|> maths.is_close(3.14159265359, 0.0, tolerance)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test that the constant is approximately equal to 1.6180...
|
||||||
|
maths.golden_ratio()
|
||||||
|
|> maths.is_close(1.618033988749895, 0.0, tolerance)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
|
@ -1,227 +0,0 @@
|
||||||
import gleam/option
|
|
||||||
import gleam_community/maths/arithmetics
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn int_gcd_test() {
|
|
||||||
arithmetics.gcd(1, 1)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
arithmetics.gcd(100, 10)
|
|
||||||
|> should.equal(10)
|
|
||||||
|
|
||||||
arithmetics.gcd(10, 100)
|
|
||||||
|> should.equal(10)
|
|
||||||
|
|
||||||
arithmetics.gcd(100, -10)
|
|
||||||
|> should.equal(10)
|
|
||||||
|
|
||||||
arithmetics.gcd(-36, -17)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
arithmetics.gcd(-30, -42)
|
|
||||||
|> should.equal(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_euclidean_modulo_test() {
|
|
||||||
// Base Case: Positive x, Positive y
|
|
||||||
// Note that the truncated, floored, and euclidean
|
|
||||||
// definitions should agree for this base case
|
|
||||||
arithmetics.int_euclidean_modulo(15, 4)
|
|
||||||
|> should.equal(3)
|
|
||||||
|
|
||||||
// Case: Positive x, Negative y
|
|
||||||
arithmetics.int_euclidean_modulo(15, -4)
|
|
||||||
|> should.equal(3)
|
|
||||||
|
|
||||||
// Case: Negative x, Positive y
|
|
||||||
arithmetics.int_euclidean_modulo(-15, 4)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
// Case: Negative x, Negative y
|
|
||||||
arithmetics.int_euclidean_modulo(-15, -4)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
// Case: Positive x, Zero y
|
|
||||||
arithmetics.int_euclidean_modulo(5, 0)
|
|
||||||
|> should.equal(0)
|
|
||||||
|
|
||||||
// Case: Zero x, Negative y
|
|
||||||
arithmetics.int_euclidean_modulo(0, 5)
|
|
||||||
|> should.equal(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_lcm_test() {
|
|
||||||
arithmetics.lcm(1, 1)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
arithmetics.lcm(100, 10)
|
|
||||||
|> should.equal(100)
|
|
||||||
|
|
||||||
arithmetics.lcm(10, 100)
|
|
||||||
|> should.equal(100)
|
|
||||||
|
|
||||||
arithmetics.lcm(100, -10)
|
|
||||||
|> should.equal(100)
|
|
||||||
|
|
||||||
arithmetics.lcm(-36, -17)
|
|
||||||
|> should.equal(612)
|
|
||||||
|
|
||||||
arithmetics.lcm(-30, -42)
|
|
||||||
|> should.equal(210)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_proper_divisors_test() {
|
|
||||||
arithmetics.proper_divisors(2)
|
|
||||||
|> should.equal([1])
|
|
||||||
|
|
||||||
arithmetics.proper_divisors(6)
|
|
||||||
|> should.equal([1, 2, 3])
|
|
||||||
|
|
||||||
arithmetics.proper_divisors(13)
|
|
||||||
|> should.equal([1])
|
|
||||||
|
|
||||||
arithmetics.proper_divisors(18)
|
|
||||||
|> should.equal([1, 2, 3, 6, 9])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_divisors_test() {
|
|
||||||
arithmetics.divisors(2)
|
|
||||||
|> should.equal([1, 2])
|
|
||||||
|
|
||||||
arithmetics.divisors(6)
|
|
||||||
|> should.equal([1, 2, 3, 6])
|
|
||||||
|
|
||||||
arithmetics.divisors(13)
|
|
||||||
|> should.equal([1, 13])
|
|
||||||
|
|
||||||
arithmetics.divisors(18)
|
|
||||||
|> should.equal([1, 2, 3, 6, 9, 18])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_sum_test() {
|
|
||||||
// An empty list returns 0
|
|
||||||
[]
|
|
||||||
|> arithmetics.float_sum(option.None)
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> arithmetics.float_sum(option.None)
|
|
||||||
|> should.equal(6.0)
|
|
||||||
|
|
||||||
[-2.0, 4.0, 6.0]
|
|
||||||
|> arithmetics.float_sum(option.None)
|
|
||||||
|> should.equal(8.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_sum_test() {
|
|
||||||
// An empty list returns 0
|
|
||||||
[]
|
|
||||||
|> arithmetics.int_sum()
|
|
||||||
|> should.equal(0)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1, 2, 3]
|
|
||||||
|> arithmetics.int_sum()
|
|
||||||
|> should.equal(6)
|
|
||||||
|
|
||||||
[-2, 4, 6]
|
|
||||||
|> arithmetics.int_sum()
|
|
||||||
|> should.equal(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_product_test() {
|
|
||||||
// An empty list returns 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(option.None)
|
|
||||||
|> should.equal(Ok(6.0))
|
|
||||||
|
|
||||||
[-2.0, 4.0, 6.0]
|
|
||||||
|> arithmetics.float_product(option.None)
|
|
||||||
|> should.equal(Ok(-48.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_product_test() {
|
|
||||||
// An empty list returns 0
|
|
||||||
[]
|
|
||||||
|> arithmetics.int_product()
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1, 2, 3]
|
|
||||||
|> arithmetics.int_product()
|
|
||||||
|> should.equal(6)
|
|
||||||
|
|
||||||
[-2, 4, 6]
|
|
||||||
|> arithmetics.int_product()
|
|
||||||
|> should.equal(-48)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_cumulative_sum_test() {
|
|
||||||
// An empty lists returns an empty list
|
|
||||||
[]
|
|
||||||
|> arithmetics.float_cumulative_sum()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> arithmetics.float_cumulative_sum()
|
|
||||||
|> should.equal([1.0, 3.0, 6.0])
|
|
||||||
|
|
||||||
[-2.0, 4.0, 6.0]
|
|
||||||
|> arithmetics.float_cumulative_sum()
|
|
||||||
|> should.equal([-2.0, 2.0, 8.0])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_cumulative_sum_test() {
|
|
||||||
// An empty lists returns an empty list
|
|
||||||
[]
|
|
||||||
|> arithmetics.int_cumulative_sum()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1, 2, 3]
|
|
||||||
|> arithmetics.int_cumulative_sum()
|
|
||||||
|> should.equal([1, 3, 6])
|
|
||||||
|
|
||||||
[-2, 4, 6]
|
|
||||||
|> arithmetics.int_cumulative_sum()
|
|
||||||
|> should.equal([-2, 2, 8])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_cumulative_product_test() {
|
|
||||||
// An empty lists returns an empty list
|
|
||||||
[]
|
|
||||||
|> arithmetics.float_cumulative_product()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> arithmetics.float_cumulative_product()
|
|
||||||
|> should.equal([1.0, 2.0, 6.0])
|
|
||||||
|
|
||||||
[-2.0, 4.0, 6.0]
|
|
||||||
|> arithmetics.float_cumulative_product()
|
|
||||||
|> should.equal([-2.0, -8.0, -48.0])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_cumulative_product_test() {
|
|
||||||
// An empty lists returns an empty list
|
|
||||||
[]
|
|
||||||
|> arithmetics.int_cumulative_product()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1, 2, 3]
|
|
||||||
|> arithmetics.int_cumulative_product()
|
|
||||||
|> should.equal([1, 2, 6])
|
|
||||||
|
|
||||||
[-2, 4, 6]
|
|
||||||
|> arithmetics.int_cumulative_product()
|
|
||||||
|> should.equal([-2, -8, -48])
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
import gleam_community/maths/conversion
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_to_degree_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
conversion.radians_to_degrees(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
conversion.radians_to_degrees(2.0 *. elementary.pi())
|
|
||||||
|> predicates.is_close(360.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_to_radian_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
conversion.degrees_to_radians(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
conversion.degrees_to_radians(360.0)
|
|
||||||
|> predicates.is_close(2.0 *. elementary.pi(), 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_to_int_test() {
|
|
||||||
conversion.float_to_int(12.0654)
|
|
||||||
|> should.equal(12)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_to_float_test() {
|
|
||||||
conversion.int_to_float(-1)
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
conversion.int_to_float(1)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
}
|
|
|
@ -1,486 +0,0 @@
|
||||||
import gleam/option
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_acos_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
let assert Ok(result) = elementary.acos(1.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.acos(0.5)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(1.047197, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.acos(1.1)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.acos(-1.1)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_acosh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
let assert Ok(result) = elementary.acosh(1.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.acosh(0.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_asin_test() {
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.asin(0.0)
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
let assert Ok(result) = elementary.asin(0.5)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.523598, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.asin(1.1)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.asin(-1.1)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_asinh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.asinh(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.asinh(0.5)
|
|
||||||
|> predicates.is_close(0.481211, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_atan_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.atan(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.atan(0.5)
|
|
||||||
|> predicates.is_close(0.463647, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_atan2_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.atan2(0.0, 0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.atan2(0.0, 1.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check atan2(y=1.0, x=0.5)
|
|
||||||
// Should be equal to atan(y / x) for any x > 0 and any y
|
|
||||||
let result = elementary.atan(1.0 /. 0.5)
|
|
||||||
elementary.atan2(1.0, 0.5)
|
|
||||||
|> predicates.is_close(result, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check atan2(y=2.0, x=-1.5)
|
|
||||||
// Should be equal to pi + atan(y / x) for any x < 0 and y >= 0
|
|
||||||
let result = elementary.pi() +. elementary.atan(2.0 /. -1.5)
|
|
||||||
elementary.atan2(2.0, -1.5)
|
|
||||||
|> predicates.is_close(result, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check atan2(y=-2.0, x=-1.5)
|
|
||||||
// Should be equal to atan(y / x) - pi for any x < 0 and y < 0
|
|
||||||
let result = elementary.atan(-2.0 /. -1.5) -. elementary.pi()
|
|
||||||
elementary.atan2(-2.0, -1.5)
|
|
||||||
|> predicates.is_close(result, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check atan2(y=1.5, x=0.0)
|
|
||||||
// Should be equal to pi/2 for x = 0 and any y > 0
|
|
||||||
let result = elementary.pi() /. 2.0
|
|
||||||
elementary.atan2(1.5, 0.0)
|
|
||||||
|> predicates.is_close(result, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check atan2(y=-1.5, x=0.0)
|
|
||||||
// Should be equal to -pi/2 for x = 0 and any y < 0
|
|
||||||
let result = -1.0 *. elementary.pi() /. 2.0
|
|
||||||
elementary.atan2(-1.5, 0.0)
|
|
||||||
|> predicates.is_close(result, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_atanh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
let assert Ok(result) = elementary.atanh(0.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.atanh(0.5)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.549306, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.atanh(1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.atanh(2.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.atanh(1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.atanh(-2.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_cos_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.cos(0.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.cos(elementary.pi())
|
|
||||||
|> predicates.is_close(-1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.cos(0.5)
|
|
||||||
|> predicates.is_close(0.877582, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_cosh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.cosh(0.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.cosh(0.5)
|
|
||||||
|> predicates.is_close(1.127625, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
// An (overflow) error might occur when given an input
|
|
||||||
// value that will result in a too large output value
|
|
||||||
// e.g. elementary.cosh(1000.0) but this is a property of the
|
|
||||||
// runtime.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_sin_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.sin(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.sin(0.5 *. elementary.pi())
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.sin(0.5)
|
|
||||||
|> predicates.is_close(0.479425, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_sinh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.sinh(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.sinh(0.5)
|
|
||||||
|> predicates.is_close(0.521095, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
// An (overflow) error might occur when given an input
|
|
||||||
// value that will result in a too large output value
|
|
||||||
// e.g. elementary.sinh(1000.0) but this is a property of the
|
|
||||||
// runtime.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_tan_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.tan(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.tan(0.5)
|
|
||||||
|> predicates.is_close(0.546302, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_tanh_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.tanh(0.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.tanh(25.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.tanh(-25.0)
|
|
||||||
|> predicates.is_close(-1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.tanh(0.5)
|
|
||||||
|> predicates.is_close(0.462117, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_exponential_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.exponential(0.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.exponential(0.5)
|
|
||||||
|> predicates.is_close(1.648721, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
// An (overflow) error might occur when given an input
|
|
||||||
// value that will result in a too large output value
|
|
||||||
// e.g. elementary.exponential(1000.0) but this is a property of the
|
|
||||||
// runtime.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_natural_logarithm_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.natural_logarithm(1.0)
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.natural_logarithm(0.5)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(-0.693147, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.natural_logarithm(-1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_logarithm_test() {
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.logarithm(10.0, option.Some(10.0))
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.logarithm(10.0, option.Some(100.0))
|
|
||||||
|> should.equal(Ok(0.5))
|
|
||||||
|
|
||||||
elementary.logarithm(1.0, option.Some(0.25))
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.logarithm(1.0, option.Some(1.0))
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.logarithm(10.0, option.Some(1.0))
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.logarithm(-1.0, option.Some(1.0))
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
elementary.logarithm(1.0, option.Some(10.0))
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
elementary.logarithm(elementary.e(), option.Some(elementary.e()))
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.logarithm(-1.0, option.Some(2.0))
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_logarithm_2_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
elementary.logarithm_2(1.0)
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
elementary.logarithm_2(2.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.logarithm_2(5.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(2.321928, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.logarithm_2(-1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_logarithm_10_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
let assert Ok(result) = elementary.logarithm_10(1.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.logarithm_10(10.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(result) = elementary.logarithm_10(50.0)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(1.69897, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Check that we get an error when the function is evaluated
|
|
||||||
// outside its domain
|
|
||||||
elementary.logarithm_10(-1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_power_test() {
|
|
||||||
elementary.power(2.0, 2.0)
|
|
||||||
|> should.equal(Ok(4.0))
|
|
||||||
|
|
||||||
elementary.power(-5.0, 3.0)
|
|
||||||
|> should.equal(Ok(-125.0))
|
|
||||||
|
|
||||||
elementary.power(10.5, 0.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.power(16.0, 0.5)
|
|
||||||
|> should.equal(Ok(4.0))
|
|
||||||
|
|
||||||
elementary.power(2.0, -1.0)
|
|
||||||
|> should.equal(Ok(0.5))
|
|
||||||
|
|
||||||
elementary.power(2.0, -1.0)
|
|
||||||
|> should.equal(Ok(0.5))
|
|
||||||
|
|
||||||
// When y = 0, the result should universally be 1, regardless of the value of x
|
|
||||||
elementary.power(10.0, 0.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
elementary.power(-10.0, 0.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.power(2.0, -1.0)
|
|
||||||
|> should.equal(Ok(0.5))
|
|
||||||
|
|
||||||
// elementary.power(-1.0, 0.5) is equivalent to float.square_root(-1.0)
|
|
||||||
// and should return an error as an imaginary number would otherwise
|
|
||||||
// have to be returned
|
|
||||||
elementary.power(-1.0, 0.5)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Check another case with a negative base and fractional exponent
|
|
||||||
elementary.power(-1.5, 1.5)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// elementary.power(0.0, -1.0) is equivalent to 1. /. 0 and is expected
|
|
||||||
// to be an error
|
|
||||||
elementary.power(0.0, -1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Check that a negative base and exponent is fine as long as the
|
|
||||||
// exponent is not fractional
|
|
||||||
elementary.power(-2.0, -1.0)
|
|
||||||
|> should.equal(Ok(-0.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_square_root_test() {
|
|
||||||
elementary.square_root(1.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.square_root(9.0)
|
|
||||||
|> should.equal(Ok(3.0))
|
|
||||||
|
|
||||||
// An error should be returned as an imaginary number would otherwise
|
|
||||||
// have to be returned
|
|
||||||
elementary.square_root(-1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_cube_root_test() {
|
|
||||||
elementary.cube_root(1.0)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.cube_root(27.0)
|
|
||||||
|> should.equal(Ok(3.0))
|
|
||||||
|
|
||||||
// An error should be returned as an imaginary number would otherwise
|
|
||||||
// have to be returned
|
|
||||||
elementary.cube_root(-1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_nth_root_test() {
|
|
||||||
elementary.nth_root(9.0, 2)
|
|
||||||
|> should.equal(Ok(3.0))
|
|
||||||
|
|
||||||
elementary.nth_root(27.0, 3)
|
|
||||||
|> should.equal(Ok(3.0))
|
|
||||||
|
|
||||||
elementary.nth_root(1.0, 4)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
|
|
||||||
elementary.nth_root(256.0, 4)
|
|
||||||
|> should.equal(Ok(4.0))
|
|
||||||
|
|
||||||
// An error should be returned as an imaginary number would otherwise
|
|
||||||
// have to be returned
|
|
||||||
elementary.nth_root(-1.0, 4)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_constants_test() {
|
|
||||||
elementary.e()
|
|
||||||
|> predicates.is_close(2.71828, 0.0, 0.00001)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
elementary.pi()
|
|
||||||
|> predicates.is_close(3.14159, 0.0, 0.00001)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
|
@ -1,629 +0,0 @@
|
||||||
import gleam/option
|
|
||||||
import gleam/set
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/metrics
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
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, option.None)
|
|
||||||
|> should.equal(Ok(0.0))
|
|
||||||
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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_manhattan_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Empty lists returns an error
|
|
||||||
metrics.manhattan_distance([], [], option.None)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// 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 float_list_minkowski_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.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, option.None)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Test order < 1
|
|
||||||
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, 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, 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, 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, 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, option.None)
|
|
||||||
result
|
|
||||||
|> predicates.is_close(2.23606797749979, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Manhattan distance (p = 1)
|
|
||||||
let assert Ok(result) =
|
|
||||||
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 an error
|
|
||||||
metrics.euclidean_distance([], [], option.None)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// 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 mean_test() {
|
|
||||||
// An empty list returns an error
|
|
||||||
[]
|
|
||||||
|> metrics.mean()
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> metrics.mean()
|
|
||||||
|> should.equal(Ok(2.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn median_test() {
|
|
||||||
// An empty list returns an error
|
|
||||||
[]
|
|
||||||
|> metrics.median()
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> metrics.median()
|
|
||||||
|> should.equal(Ok(2.0))
|
|
||||||
|
|
||||||
[1.0, 2.0, 3.0, 4.0]
|
|
||||||
|> metrics.median()
|
|
||||||
|> should.equal(Ok(2.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variance_test() {
|
|
||||||
// Degrees of freedom
|
|
||||||
let ddof = 1
|
|
||||||
|
|
||||||
// An empty list returns an error
|
|
||||||
[]
|
|
||||||
|> metrics.variance(ddof)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> metrics.variance(ddof)
|
|
||||||
|> should.equal(Ok(1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn standard_deviation_test() {
|
|
||||||
// Degrees of freedom
|
|
||||||
let ddof = 1
|
|
||||||
|
|
||||||
// An empty list returns an error
|
|
||||||
[]
|
|
||||||
|> metrics.standard_deviation(ddof)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 2.0, 3.0]
|
|
||||||
|> 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.from_list([0, 1, 2, 5, 6, 8, 9])
|
|
||||||
let set_b = 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.from_list([0, 1, 2, 3, 4, 5])
|
|
||||||
let set_d = 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.from_list(["cat", "dog", "hippo", "monkey"])
|
|
||||||
let set_f = 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.from_list([0, 1, 2, 5, 6, 8, 9])
|
|
||||||
let set_b = 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.from_list([0, 1, 2, 3, 4, 5])
|
|
||||||
let set_d = 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.from_list(["cat", "dog", "hippo", "monkey"])
|
|
||||||
let set_f = 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.from_list([0, 1, 2, 5, 6, 8, 9])
|
|
||||||
let set_b = 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.from_list([0, 1, 2, 3, 4, 5])
|
|
||||||
let set_d = 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.from_list(["horse", "dog", "hippo", "monkey", "bird"])
|
|
||||||
let set_f = 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 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()
|
|
||||||
}
|
|
|
@ -1,753 +0,0 @@
|
||||||
import gleam/float
|
|
||||||
import gleam/int
|
|
||||||
import gleam/option
|
|
||||||
import gleam_community/maths/piecewise
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_ceiling_test() {
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(3))
|
|
||||||
|> should.equal(12.066)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(2))
|
|
||||||
|> should.equal(12.07)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(1))
|
|
||||||
|> should.equal(12.1)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(0))
|
|
||||||
|> should.equal(13.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(-1))
|
|
||||||
|> should.equal(20.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(-2))
|
|
||||||
|> should.equal(100.0)
|
|
||||||
|
|
||||||
// Round 3. digit BEFORE decimal point
|
|
||||||
piecewise.ceiling(12.0654, option.Some(-3))
|
|
||||||
|> should.equal(1000.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_floor_test() {
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(3))
|
|
||||||
|> should.equal(12.065)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(2))
|
|
||||||
|> should.equal(12.06)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(1))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(0))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(-1))
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(-2))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.floor(12.0654, option.Some(-3))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_truncate_test() {
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(3))
|
|
||||||
|> should.equal(12.065)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(2))
|
|
||||||
|> should.equal(12.06)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(1))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(0))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(-1))
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(-2))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.truncate(12.0654, option.Some(-3))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_to_nearest_test() {
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(2.0)
|
|
||||||
|
|
||||||
piecewise.round(1.75, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(2.0)
|
|
||||||
|
|
||||||
piecewise.round(2.0, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(2.0)
|
|
||||||
|
|
||||||
piecewise.round(3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(4.0)
|
|
||||||
|
|
||||||
piecewise.round(4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(4.0)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(-4.0)
|
|
||||||
|
|
||||||
piecewise.round(-4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(-4.0)
|
|
||||||
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(12.065)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(12.07)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(12.1)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Round 3. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_up_test() {
|
|
||||||
// Note: Rounding mode "RoundUp" is an alias for the ceiling function
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(0.46)
|
|
||||||
|
|
||||||
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(0.51)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.4)
|
|
||||||
|
|
||||||
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
|
|
||||||
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.45)
|
|
||||||
|
|
||||||
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundUp))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_down_test() {
|
|
||||||
// Note: Rounding mode "RoundDown" is an alias for the floor function
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.4)
|
|
||||||
|
|
||||||
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.45)
|
|
||||||
|
|
||||||
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
|
|
||||||
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
|
|
||||||
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-0.46)
|
|
||||||
|
|
||||||
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundDown))
|
|
||||||
|> should.equal(-0.51)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_to_zero_test() {
|
|
||||||
// Note: Rounding mode "RoundToZero" is an alias for the truncate function
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.4)
|
|
||||||
|
|
||||||
piecewise.round(0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.round(0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.45)
|
|
||||||
|
|
||||||
piecewise.round(0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(-0.4)
|
|
||||||
|
|
||||||
piecewise.round(-0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
|
|
||||||
piecewise.round(-0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(-0.45)
|
|
||||||
|
|
||||||
piecewise.round(-0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|
|
||||||
|> should.equal(-0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_ties_away_test() {
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(2.0)
|
|
||||||
|
|
||||||
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(3.0)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(-2.0)
|
|
||||||
|
|
||||||
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(-2.0)
|
|
||||||
|
|
||||||
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(-3.0)
|
|
||||||
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(12.065)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(12.07)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(12.1)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.round(
|
|
||||||
12.0654,
|
|
||||||
option.Some(-1),
|
|
||||||
option.Some(piecewise.RoundTiesAway),
|
|
||||||
)
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.round(
|
|
||||||
12.0654,
|
|
||||||
option.Some(-2),
|
|
||||||
option.Some(piecewise.RoundTiesAway),
|
|
||||||
)
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.round(
|
|
||||||
12.0654,
|
|
||||||
option.Some(-3),
|
|
||||||
option.Some(piecewise.RoundTiesAway),
|
|
||||||
)
|
|
||||||
|> should.equal(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_ties_up_test() {
|
|
||||||
// Try with positive values
|
|
||||||
piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(2.0)
|
|
||||||
|
|
||||||
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(3.0)
|
|
||||||
|
|
||||||
// Try with negative values
|
|
||||||
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
|
|
||||||
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(-2.0)
|
|
||||||
|
|
||||||
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(-2.0)
|
|
||||||
|
|
||||||
// Round 3. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(12.065)
|
|
||||||
|
|
||||||
// Round 2. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(12.07)
|
|
||||||
|
|
||||||
// Round 1. digit AFTER decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(12.1)
|
|
||||||
|
|
||||||
// Round 0. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// Round 1. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
// Round 2. digit BEFORE decimal point
|
|
||||||
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundTiesUp))
|
|
||||||
|> should.equal(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn math_round_edge_cases_test() {
|
|
||||||
// The default number of digits is 0 if None is provided
|
|
||||||
piecewise.round(12.0654, option.None, option.Some(piecewise.RoundNearest))
|
|
||||||
|> should.equal(12.0)
|
|
||||||
|
|
||||||
// The default rounding mode is piecewise.RoundNearest if None is provided
|
|
||||||
piecewise.round(12.0654, option.None, option.None)
|
|
||||||
|> should.equal(12.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_absolute_value_test() {
|
|
||||||
piecewise.float_absolute_value(20.0)
|
|
||||||
|> should.equal(20.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_value(-20.0)
|
|
||||||
|> should.equal(20.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_absolute_value_test() {
|
|
||||||
piecewise.int_absolute_value(20)
|
|
||||||
|> should.equal(20)
|
|
||||||
|
|
||||||
piecewise.int_absolute_value(-20)
|
|
||||||
|> should.equal(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_absolute_difference_test() {
|
|
||||||
piecewise.float_absolute_difference(20.0, 15.0)
|
|
||||||
|> should.equal(5.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(-20.0, -15.0)
|
|
||||||
|> should.equal(5.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(20.0, -15.0)
|
|
||||||
|> should.equal(35.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(-20.0, 15.0)
|
|
||||||
|> should.equal(35.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(0.0, 0.0)
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(1.0, 2.0)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(2.0, 1.0)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(-1.0, 0.0)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(0.0, -1.0)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(10.0, 20.0)
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(-10.0, -20.0)
|
|
||||||
|> should.equal(10.0)
|
|
||||||
|
|
||||||
piecewise.float_absolute_difference(-10.5, 10.5)
|
|
||||||
|> should.equal(21.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_absolute_difference_test() {
|
|
||||||
piecewise.int_absolute_difference(20, 15)
|
|
||||||
|> should.equal(5)
|
|
||||||
|
|
||||||
piecewise.int_absolute_difference(-20, -15)
|
|
||||||
|> should.equal(5)
|
|
||||||
|
|
||||||
piecewise.int_absolute_difference(20, -15)
|
|
||||||
|> should.equal(35)
|
|
||||||
|
|
||||||
piecewise.int_absolute_difference(-20, 15)
|
|
||||||
|> should.equal(35)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_sign_test() {
|
|
||||||
piecewise.float_sign(100.0)
|
|
||||||
|> should.equal(1.0)
|
|
||||||
|
|
||||||
piecewise.float_sign(0.0)
|
|
||||||
|> should.equal(0.0)
|
|
||||||
|
|
||||||
piecewise.float_sign(-100.0)
|
|
||||||
|> should.equal(-1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_flip_sign_test() {
|
|
||||||
piecewise.float_flip_sign(100.0)
|
|
||||||
|> should.equal(-100.0)
|
|
||||||
|
|
||||||
piecewise.float_flip_sign(0.0)
|
|
||||||
|> should.equal(-0.0)
|
|
||||||
|
|
||||||
piecewise.float_flip_sign(-100.0)
|
|
||||||
|> should.equal(100.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_copy_sign_test() {
|
|
||||||
piecewise.float_copy_sign(100.0, 10.0)
|
|
||||||
|> should.equal(100.0)
|
|
||||||
|
|
||||||
piecewise.float_copy_sign(-100.0, 10.0)
|
|
||||||
|> should.equal(100.0)
|
|
||||||
|
|
||||||
piecewise.float_copy_sign(100.0, -10.0)
|
|
||||||
|> should.equal(-100.0)
|
|
||||||
|
|
||||||
piecewise.float_copy_sign(-100.0, -10.0)
|
|
||||||
|> should.equal(-100.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_sign_test() {
|
|
||||||
piecewise.int_sign(100)
|
|
||||||
|> should.equal(1)
|
|
||||||
|
|
||||||
piecewise.int_sign(0)
|
|
||||||
|> should.equal(0)
|
|
||||||
|
|
||||||
piecewise.int_sign(-100)
|
|
||||||
|> should.equal(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_flip_sign_test() {
|
|
||||||
piecewise.int_flip_sign(100)
|
|
||||||
|> should.equal(-100)
|
|
||||||
|
|
||||||
piecewise.int_flip_sign(0)
|
|
||||||
|> should.equal(-0)
|
|
||||||
|
|
||||||
piecewise.int_flip_sign(-100)
|
|
||||||
|> should.equal(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_copy_sign_test() {
|
|
||||||
piecewise.int_copy_sign(100, 10)
|
|
||||||
|> should.equal(100)
|
|
||||||
|
|
||||||
piecewise.int_copy_sign(-100, 10)
|
|
||||||
|> should.equal(100)
|
|
||||||
|
|
||||||
piecewise.int_copy_sign(100, -10)
|
|
||||||
|> should.equal(-100)
|
|
||||||
|
|
||||||
piecewise.int_copy_sign(-100, -10)
|
|
||||||
|> should.equal(-100)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_minimum_test() {
|
|
||||||
piecewise.minimum(0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.minimum(0.5, 0.75, float.compare)
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.minimum(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(-0.75)
|
|
||||||
|
|
||||||
piecewise.minimum(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(-0.75)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_minimum_test() {
|
|
||||||
piecewise.minimum(75, 50, int.compare)
|
|
||||||
|> should.equal(50)
|
|
||||||
|
|
||||||
piecewise.minimum(50, 75, int.compare)
|
|
||||||
|> should.equal(50)
|
|
||||||
|
|
||||||
piecewise.minimum(-75, 50, int.compare)
|
|
||||||
|> should.equal(-75)
|
|
||||||
|
|
||||||
piecewise.minimum(-75, 50, int.compare)
|
|
||||||
|> should.equal(-75)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_maximum_test() {
|
|
||||||
piecewise.maximum(0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(0.75)
|
|
||||||
|
|
||||||
piecewise.maximum(0.5, 0.75, float.compare)
|
|
||||||
|> should.equal(0.75)
|
|
||||||
|
|
||||||
piecewise.maximum(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(0.5)
|
|
||||||
|
|
||||||
piecewise.maximum(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_maximum_test() {
|
|
||||||
piecewise.maximum(75, 50, int.compare)
|
|
||||||
|> should.equal(75)
|
|
||||||
|
|
||||||
piecewise.maximum(50, 75, int.compare)
|
|
||||||
|> should.equal(75)
|
|
||||||
|
|
||||||
piecewise.maximum(-75, 50, int.compare)
|
|
||||||
|> should.equal(50)
|
|
||||||
|
|
||||||
piecewise.maximum(-75, 50, int.compare)
|
|
||||||
|> should.equal(50)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_minmax_test() {
|
|
||||||
piecewise.minmax(0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(#(0.5, 0.75))
|
|
||||||
|
|
||||||
piecewise.minmax(0.5, 0.75, float.compare)
|
|
||||||
|> should.equal(#(0.5, 0.75))
|
|
||||||
|
|
||||||
piecewise.minmax(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(#(-0.75, 0.5))
|
|
||||||
|
|
||||||
piecewise.minmax(-0.75, 0.5, float.compare)
|
|
||||||
|> should.equal(#(-0.75, 0.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_minmax_test() {
|
|
||||||
piecewise.minmax(75, 50, int.compare)
|
|
||||||
|> should.equal(#(50, 75))
|
|
||||||
|
|
||||||
piecewise.minmax(50, 75, int.compare)
|
|
||||||
|> should.equal(#(50, 75))
|
|
||||||
|
|
||||||
piecewise.minmax(-75, 50, int.compare)
|
|
||||||
|> should.equal(#(-75, 50))
|
|
||||||
|
|
||||||
piecewise.minmax(-75, 50, int.compare)
|
|
||||||
|> should.equal(#(-75, 50))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_minimum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.list_minimum(float.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4.5, 4.5, 3.5, 2.5, 1.5]
|
|
||||||
|> piecewise.list_minimum(float.compare)
|
|
||||||
|> should.equal(Ok(1.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_minimum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.list_minimum(int.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4, 4, 3, 2, 1]
|
|
||||||
|> piecewise.list_minimum(int.compare)
|
|
||||||
|> should.equal(Ok(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_maximum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.list_maximum(float.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4.5, 4.5, 3.5, 2.5, 1.5]
|
|
||||||
|> piecewise.list_maximum(float.compare)
|
|
||||||
|> should.equal(Ok(4.5))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_maximum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.list_maximum(int.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4, 4, 3, 2, 1]
|
|
||||||
|> piecewise.list_maximum(int.compare)
|
|
||||||
|> should.equal(Ok(4))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_arg_maximum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.arg_maximum(float.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4.5, 4.5, 3.5, 2.5, 1.5]
|
|
||||||
|> piecewise.arg_maximum(float.compare)
|
|
||||||
|> should.equal(Ok([0, 1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_arg_maximum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.arg_maximum(int.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4, 4, 3, 2, 1]
|
|
||||||
|> piecewise.arg_maximum(int.compare)
|
|
||||||
|> should.equal(Ok([0, 1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_arg_minimum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.arg_minimum(float.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4.5, 4.5, 3.5, 2.5, 1.5]
|
|
||||||
|> piecewise.arg_minimum(float.compare)
|
|
||||||
|> should.equal(Ok([4]))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_arg_minimum_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.arg_minimum(int.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4, 4, 3, 2, 1]
|
|
||||||
|> piecewise.arg_minimum(int.compare)
|
|
||||||
|> should.equal(Ok([4]))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_extrema_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.extrema(float.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
|
||||||
|> piecewise.extrema(float.compare)
|
|
||||||
|> should.equal(Ok(#(1.0, 4.0)))
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1.0, 4.0, 2.0, 5.0, 0.0]
|
|
||||||
|> piecewise.extrema(float.compare)
|
|
||||||
|> should.equal(Ok(#(0.0, 5.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_list_extrema_test() {
|
|
||||||
// An empty lists returns an error
|
|
||||||
[]
|
|
||||||
|> piecewise.extrema(int.compare)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[4, 4, 3, 2, 1]
|
|
||||||
|> piecewise.extrema(int.compare)
|
|
||||||
|> should.equal(Ok(#(1, 4)))
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
[1, 4, 2, 5, 0]
|
|
||||||
|> piecewise.extrema(int.compare)
|
|
||||||
|> should.equal(Ok(#(0, 5)))
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
import gleam/list
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_is_close_test() {
|
|
||||||
let val = 99.0
|
|
||||||
let ref_val = 100.0
|
|
||||||
// We set 'atol' and 'rtol' such that the values are equivalent
|
|
||||||
// if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
|
||||||
let rtol = 0.01
|
|
||||||
let atol = 0.1
|
|
||||||
predicates.is_close(val, ref_val, rtol, atol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_all_close_test() {
|
|
||||||
let val = 99.0
|
|
||||||
let ref_val = 100.0
|
|
||||||
let xarr = list.repeat(val, 42)
|
|
||||||
let yarr = list.repeat(ref_val, 42)
|
|
||||||
// We set 'atol' and 'rtol' such that the values are equivalent
|
|
||||||
// if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
|
||||||
let rtol = 0.01
|
|
||||||
let atol = 0.1
|
|
||||||
predicates.all_close(xarr, yarr, rtol, atol)
|
|
||||||
|> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) {
|
|
||||||
case zarr {
|
|
||||||
Ok(arr) ->
|
|
||||||
arr
|
|
||||||
|> list.all(fn(a) { a })
|
|
||||||
|> Ok
|
|
||||||
_ ->
|
|
||||||
Nil
|
|
||||||
|> Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> should.equal(Ok(True))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_is_fractional_test() {
|
|
||||||
predicates.is_fractional(1.5)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_fractional(0.5)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_fractional(0.3333)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_fractional(0.9999)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_fractional(1.0)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_fractional(999.0)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_is_power_test() {
|
|
||||||
predicates.is_power(10, 10)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_power(11, 10)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_power(4, 2)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_power(5, 2)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_power(27, 3)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_power(28, 3)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_is_even_test() {
|
|
||||||
predicates.is_even(0)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_even(2)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_even(12)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_even(5)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_even(-3)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_even(-4)
|
|
||||||
|> should.equal(True)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_is_odd_test() {
|
|
||||||
predicates.is_odd(0)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_odd(3)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_odd(13)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_odd(4)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_odd(-3)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_odd(-4)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_is_perfect_test() {
|
|
||||||
predicates.is_perfect(6)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_perfect(28)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_perfect(496)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_perfect(1)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_perfect(3)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_between_test() {
|
|
||||||
predicates.is_between(5.5, 5.0, 6.0)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_between(5.0, 5.0, 6.0)
|
|
||||||
|> should.equal(False)
|
|
||||||
|
|
||||||
predicates.is_between(6.0, 5.0, 6.0)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_divisible_test() {
|
|
||||||
predicates.is_divisible(10, 2)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_divisible(7, 3)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_multiple_test() {
|
|
||||||
predicates.is_multiple(15, 5)
|
|
||||||
|> should.equal(True)
|
|
||||||
|
|
||||||
predicates.is_multiple(14, 5)
|
|
||||||
|> should.equal(False)
|
|
||||||
}
|
|
|
@ -1,328 +0,0 @@
|
||||||
import gleam/iterator
|
|
||||||
import gleam/list
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleam_community/maths/sequences
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_list_linear_space_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
// ---> With endpoint included
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, 20.0, 30.0, 40.0, 50.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, 12.5, 15.0, 17.5, 20.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Try with negative stop
|
|
||||||
// ----> Without endpoint included
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, False)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, 18.0, 26.0, 34.0, 42.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, False)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, 12.0, 14.0, 16.0, 18.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Try with negative stop
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, -50.0, 5, False)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, -2.0, -14.0, -26.0, -38.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(10.0, -20.0, 5, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[10.0, 2.5, -5.0, -12.5, -20.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Try with negative start
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(-10.0, 50.0, 5, False)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[-10.0, 2.0, 14.0, 26.0, 38.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
let assert Ok(linspace) = sequences.linear_space(-10.0, 20.0, 5, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
linspace |> iterator.to_list(),
|
|
||||||
[-10.0, -2.5, 5.0, 12.5, 20.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// A negative number of points does not work (-5)
|
|
||||||
sequences.linear_space(10.0, 50.0, -5, True)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_logarithmic_space_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
// ---> With endpoint included
|
|
||||||
// - Positive start, stop, base
|
|
||||||
let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 100.0, 1000.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive start, stop, negative base
|
|
||||||
let assert Ok(logspace) =
|
|
||||||
sequences.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[-10.0, 100.0, -1000.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive start, negative stop, base
|
|
||||||
let assert Ok(logspace) =
|
|
||||||
sequences.logarithmic_space(1.0, -3.0, 3, True, -10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[-10.0, -0.1, -0.001],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive start, base, negative stop
|
|
||||||
let assert Ok(logspace) =
|
|
||||||
sequences.logarithmic_space(1.0, -3.0, 3, True, 10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 0.1, 0.001],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive stop, base, negative start
|
|
||||||
let assert Ok(logspace) =
|
|
||||||
sequences.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[0.1, 10.0, 1000.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// ----> Without endpoint included
|
|
||||||
// - Positive start, stop, base
|
|
||||||
let assert Ok(logspace) =
|
|
||||||
sequences.logarithmic_space(1.0, 3.0, 3, False, 10.0)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 46.41588834, 215.443469],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// A negative number of points does not work (-3)
|
|
||||||
sequences.logarithmic_space(1.0, 3.0, -3, True, 10.0)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_geometric_space_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
// Check that the function agrees, at some arbitrary input
|
|
||||||
// points, with known function values
|
|
||||||
// ---> With endpoint included
|
|
||||||
// - Positive start, stop
|
|
||||||
let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 100.0, 1000.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive start, negative stop
|
|
||||||
let assert Ok(logspace) = sequences.geometric_space(10.0, 0.001, 3, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 0.1, 0.001],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// - Positive stop, negative start
|
|
||||||
let assert Ok(logspace) = sequences.geometric_space(0.1, 1000.0, 3, True)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[0.1, 10.0, 1000.0],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// ----> Without endpoint included
|
|
||||||
// - Positive start, stop
|
|
||||||
let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, False)
|
|
||||||
let assert Ok(result) =
|
|
||||||
predicates.all_close(
|
|
||||||
logspace |> iterator.to_list(),
|
|
||||||
[10.0, 46.41588834, 215.443469],
|
|
||||||
0.0,
|
|
||||||
tol,
|
|
||||||
)
|
|
||||||
result
|
|
||||||
|> list.all(fn(x) { x == True })
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
// Test invalid input (start and stop can't be equal to 0.0)
|
|
||||||
sequences.geometric_space(0.0, 1000.0, 3, False)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
sequences.geometric_space(-1000.0, 0.0, 3, False)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// A negative number of points does not work
|
|
||||||
sequences.geometric_space(-1000.0, 0.0, -3, False)
|
|
||||||
|> should.be_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_list_arange_test() {
|
|
||||||
// Positive start, stop, step
|
|
||||||
sequences.arange(1.0, 5.0, 1.0)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([1.0, 2.0, 3.0, 4.0])
|
|
||||||
|
|
||||||
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])
|
|
||||||
|
|
||||||
sequences.arange(1.0, 2.0, 0.25)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([1.0, 1.25, 1.5, 1.75])
|
|
||||||
|
|
||||||
// Reverse (switch start/stop largest/smallest value)
|
|
||||||
sequences.arange(5.0, 1.0, 1.0)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Reverse negative step
|
|
||||||
sequences.arange(5.0, 1.0, -1.0)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([5.0, 4.0, 3.0, 2.0])
|
|
||||||
|
|
||||||
// Positive start, negative stop, step
|
|
||||||
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])
|
|
||||||
|
|
||||||
// Negative start, stop, step
|
|
||||||
sequences.arange(-5.0, -1.0, -1.0)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([])
|
|
||||||
|
|
||||||
// Negative start, stop, positive step
|
|
||||||
sequences.arange(-5.0, -1.0, 1.0)
|
|
||||||
|> iterator.to_list()
|
|
||||||
|> should.equal([-5.0, -4.0, -3.0, -2.0])
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
import gleam/result
|
|
||||||
import gleam_community/maths/elementary
|
|
||||||
import gleam_community/maths/predicates
|
|
||||||
import gleam_community/maths/special
|
|
||||||
import gleeunit/should
|
|
||||||
|
|
||||||
pub fn float_beta_function_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
special.beta(-0.5, 0.5)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.beta(0.5, 0.5)
|
|
||||||
|> predicates.is_close(3.1415926535897927, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.beta(0.5, -0.5)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.beta(5.0, 5.0)
|
|
||||||
|> predicates.is_close(0.0015873015873015873, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_error_function_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
special.erf(-0.5)
|
|
||||||
|> predicates.is_close(-0.5204998778130465, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.erf(0.5)
|
|
||||||
|> predicates.is_close(0.5204998778130465, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.erf(1.0)
|
|
||||||
|> predicates.is_close(0.8427007929497148, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.erf(2.0)
|
|
||||||
|> predicates.is_close(0.9953222650189527, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.erf(10.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_gamma_function_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
special.gamma(-0.5)
|
|
||||||
|> predicates.is_close(-3.5449077018110318, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.gamma(0.5)
|
|
||||||
|> predicates.is_close(1.7724538509055159, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.gamma(1.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.gamma(2.0)
|
|
||||||
|> predicates.is_close(1.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.gamma(3.0)
|
|
||||||
|> predicates.is_close(2.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.gamma(10.0)
|
|
||||||
|> predicates.is_close(362_880.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_incomplete_gamma_function_test() {
|
|
||||||
let assert Ok(tol) = elementary.power(10.0, -6.0)
|
|
||||||
|
|
||||||
// Invalid input gives an error
|
|
||||||
// 1st arg is invalid
|
|
||||||
special.incomplete_gamma(-1.0, 1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// 2nd arg is invalid
|
|
||||||
special.incomplete_gamma(1.0, -1.0)
|
|
||||||
|> should.be_error()
|
|
||||||
|
|
||||||
// Valid input returns a result
|
|
||||||
special.incomplete_gamma(1.0, 0.0)
|
|
||||||
|> result.unwrap(-999.0)
|
|
||||||
|> predicates.is_close(0.0, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.incomplete_gamma(1.0, 2.0)
|
|
||||||
|> result.unwrap(-999.0)
|
|
||||||
|> predicates.is_close(0.864664716763387308106, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.incomplete_gamma(2.0, 3.0)
|
|
||||||
|> result.unwrap(-999.0)
|
|
||||||
|> predicates.is_close(0.8008517265285442280826, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
|
|
||||||
special.incomplete_gamma(3.0, 4.0)
|
|
||||||
|> result.unwrap(-999.0)
|
|
||||||
|> predicates.is_close(1.523793388892911312363, 0.0, tol)
|
|
||||||
|> should.be_true()
|
|
||||||
}
|
|
549
test/gleam_community/metrics_test.gleam
Normal file
549
test/gleam_community/metrics_test.gleam
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam/set
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_list_norm_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// An empty lists returns 0.0
|
||||||
|
[]
|
||||||
|
|> maths.norm(1.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) =
|
||||||
|
[1.0, 1.0, 1.0]
|
||||||
|
|> maths.norm(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[1.0, 1.0, 1.0]
|
||||||
|
|> maths.norm(-1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.3333333333333333, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[-1.0, -1.0, -1.0]
|
||||||
|
|> maths.norm(-1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.3333333333333333, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[-1.0, -1.0, -1.0]
|
||||||
|
|> maths.norm(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[-1.0, -2.0, -3.0]
|
||||||
|
|> maths.norm(-10.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.9999007044905545, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[-1.0, -2.0, -3.0]
|
||||||
|
|> maths.norm(-100.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[-1.0, -2.0, -3.0]
|
||||||
|
|> maths.norm(2.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.7416573867739413, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_norm_with_weights_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// An empty lists returns 0.0
|
||||||
|
[]
|
||||||
|
|> maths.norm_with_weights(1.0)
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) =
|
||||||
|
[#(1.0, 1.0), #(1.0, 1.0), #(1.0, 1.0)]
|
||||||
|
|> maths.norm_with_weights(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[#(1.0, 0.5), #(1.0, 0.5), #(1.0, 0.5)]
|
||||||
|
|> maths.norm_with_weights(1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.5, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
[#(1.0, 0.5), #(2.0, 0.5), #(3.0, 0.5)]
|
||||||
|
|> maths.norm_with_weights(2.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(2.6457513110645907, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_manhattan_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Try with valid input (same as Minkowski distance with p = 1)
|
||||||
|
let assert Ok(result) = maths.manhattan_distance([#(0.0, 1.0), #(0.0, 2.0)])
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.manhattan_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
|
||||||
|
|> should.equal(Ok(9.0))
|
||||||
|
|
||||||
|
maths.manhattan_distance([#(1.0, 2.0), #(2.0, 3.0)])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
maths.manhattan_distance_with_weights([#(1.0, 2.0, 0.5), #(2.0, 3.0, 1.0)])
|
||||||
|
|> should.equal(Ok(1.5))
|
||||||
|
|
||||||
|
maths.manhattan_distance_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 1.0),
|
||||||
|
#(3.0, 6.0, 1.0),
|
||||||
|
])
|
||||||
|
|> should.equal(Ok(9.0))
|
||||||
|
|
||||||
|
maths.manhattan_distance_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 2.0),
|
||||||
|
#(3.0, 6.0, 3.0),
|
||||||
|
])
|
||||||
|
|> should.equal(Ok(18.0))
|
||||||
|
|
||||||
|
maths.manhattan_distance_with_weights([
|
||||||
|
#(1.0, 4.0, -7.0),
|
||||||
|
#(2.0, 5.0, -8.0),
|
||||||
|
#(3.0, 6.0, -9.0),
|
||||||
|
])
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_minkowski_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Test order < 1
|
||||||
|
maths.minkowski_distance([#(0.0, 0.0), #(0.0, 0.0)], -1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(1.0, 1.0), #(1.0, 1.0)], 1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(0.0, 1.0), #(0.0, 1.0)], 10.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.0717734625362931, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(0.0, 1.0), #(0.0, 1.0)], 100.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.0069555500567189, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Euclidean distance (p = 2)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(0.0, 1.0), #(0.0, 2.0)], 2.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(2.23606797749979, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Manhattan distance (p = 1)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(0.0, 1.0), #(0.0, 2.0)], 1.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try different valid input
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)], 4.0)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.9482220388574776, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance_with_weights(
|
||||||
|
[#(1.0, 4.0, 1.0), #(2.0, 5.0, 1.0), #(3.0, 6.0, 1.0)],
|
||||||
|
4.0,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> maths.is_close(3.9482220388574776, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.minkowski_distance_with_weights(
|
||||||
|
[#(1.0, 4.0, 1.0), #(2.0, 5.0, 2.0), #(3.0, 6.0, 3.0)],
|
||||||
|
4.0,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> maths.is_close(4.6952537402198615, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try invalid input with weights that are negative
|
||||||
|
maths.minkowski_distance_with_weights(
|
||||||
|
[#(1.0, 4.0, -7.0), #(2.0, 5.0, -8.0), #(3.0, 6.0, -9.0)],
|
||||||
|
2.0,
|
||||||
|
)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_euclidean_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Empty lists returns an error
|
||||||
|
maths.euclidean_distance([])
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Try with valid input (same as Minkowski distance with p = 2)
|
||||||
|
let assert Ok(result) = maths.euclidean_distance([#(0.0, 1.0), #(0.0, 2.0)])
|
||||||
|
result
|
||||||
|
|> maths.is_close(2.23606797749979, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try different valid input
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.euclidean_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
|
||||||
|
result
|
||||||
|
|> maths.is_close(5.196152422706632, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.euclidean_distance_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 1.0),
|
||||||
|
#(3.0, 6.0, 1.0),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(5.196152422706632, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.euclidean_distance_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 2.0),
|
||||||
|
#(3.0, 6.0, 3.0),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(7.3484692283495345, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try invalid input with weights that are negative
|
||||||
|
maths.euclidean_distance_with_weights([
|
||||||
|
#(1.0, 4.0, -7.0),
|
||||||
|
#(2.0, 5.0, -8.0),
|
||||||
|
#(3.0, 6.0, -9.0),
|
||||||
|
])
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mean_test() {
|
||||||
|
// An empty list returns an error
|
||||||
|
[]
|
||||||
|
|> maths.mean()
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.mean()
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn median_test() {
|
||||||
|
// An empty list returns an error
|
||||||
|
[]
|
||||||
|
|> maths.median()
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.median()
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
[1.0, 2.0, 3.0, 4.0]
|
||||||
|
|> maths.median()
|
||||||
|
|> should.equal(Ok(2.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variance_test() {
|
||||||
|
// Degrees of freedom
|
||||||
|
let ddof = 1
|
||||||
|
|
||||||
|
// An empty list returns an error
|
||||||
|
[]
|
||||||
|
|> maths.variance(ddof)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.variance(ddof)
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn standard_deviation_test() {
|
||||||
|
// Degrees of freedom
|
||||||
|
let ddof = 1
|
||||||
|
|
||||||
|
// An empty list returns an error
|
||||||
|
[]
|
||||||
|
|> maths.standard_deviation(ddof)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 2.0, 3.0]
|
||||||
|
|> maths.standard_deviation(ddof)
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jaccard_index_test() {
|
||||||
|
maths.jaccard_index(set.from_list([]), set.from_list([]))
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
|
||||||
|
let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
|
||||||
|
maths.jaccard_index(set_a, set_b)
|
||||||
|
|> should.equal(4.0 /. 10.0)
|
||||||
|
|
||||||
|
let set_c = set.from_list([0, 1, 2, 3, 4, 5])
|
||||||
|
let set_d = set.from_list([6, 7, 8, 9, 10])
|
||||||
|
maths.jaccard_index(set_c, set_d)
|
||||||
|
|> should.equal(0.0 /. 11.0)
|
||||||
|
|
||||||
|
let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
|
||||||
|
let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
|
||||||
|
maths.jaccard_index(set_e, set_f)
|
||||||
|
|> should.equal(1.0 /. 7.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sorensen_dice_coefficient_test() {
|
||||||
|
maths.sorensen_dice_coefficient(set.from_list([]), set.from_list([]))
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
|
||||||
|
let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
|
||||||
|
maths.sorensen_dice_coefficient(set_a, set_b)
|
||||||
|
|> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 })
|
||||||
|
|
||||||
|
let set_c = set.from_list([0, 1, 2, 3, 4, 5])
|
||||||
|
let set_d = set.from_list([6, 7, 8, 9, 10])
|
||||||
|
maths.sorensen_dice_coefficient(set_c, set_d)
|
||||||
|
|> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 })
|
||||||
|
|
||||||
|
let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
|
||||||
|
let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
|
||||||
|
maths.sorensen_dice_coefficient(set_e, set_f)
|
||||||
|
|> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn overlap_coefficient_test() {
|
||||||
|
maths.overlap_coefficient(set.from_list([]), set.from_list([]))
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
|
||||||
|
let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
|
||||||
|
maths.overlap_coefficient(set_a, set_b)
|
||||||
|
|> should.equal(4.0 /. 7.0)
|
||||||
|
|
||||||
|
let set_c = set.from_list([0, 1, 2, 3, 4, 5])
|
||||||
|
let set_d = set.from_list([6, 7, 8, 9, 10])
|
||||||
|
maths.overlap_coefficient(set_c, set_d)
|
||||||
|
|> should.equal(0.0 /. 5.0)
|
||||||
|
|
||||||
|
let set_e = set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
|
||||||
|
let set_f = set.from_list(["monkey", "bird", "ostrich", "salmon"])
|
||||||
|
maths.overlap_coefficient(set_e, set_f)
|
||||||
|
|> should.equal(2.0 /. 4.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cosine_similarity_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Two orthogonal vectors (represented by lists)
|
||||||
|
maths.cosine_similarity([#(-1.0, 1.0), #(1.0, 1.0), #(0.0, -1.0)])
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
// Two identical (parallel) vectors (represented by lists)
|
||||||
|
maths.cosine_similarity([#(1.0, 1.0), #(2.0, 2.0), #(3.0, 3.0)])
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
// Two parallel, but oppositely oriented vectors (represented by lists)
|
||||||
|
maths.cosine_similarity([#(-1.0, 1.0), #(-2.0, 2.0), #(-3.0, 3.0)])
|
||||||
|
|> should.equal(Ok(-1.0))
|
||||||
|
|
||||||
|
// Try with arbitrary valid input
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.cosine_similarity([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.9746318461970762, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try valid input with weights
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.cosine_similarity_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 1.0),
|
||||||
|
#(3.0, 6.0, 1.0),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.9746318461970762, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try with different weights
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.cosine_similarity_with_weights([
|
||||||
|
#(1.0, 4.0, 1.0),
|
||||||
|
#(2.0, 5.0, 2.0),
|
||||||
|
#(3.0, 6.0, 3.0),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(0.9855274566525745, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.cosine_similarity_with_weights([
|
||||||
|
#(1.0, 1.0, 2.0),
|
||||||
|
#(2.0, 2.0, 3.0),
|
||||||
|
#(3.0, 3.0, 4.0),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.cosine_similarity_with_weights([
|
||||||
|
#(-1.0, 1.0, 1.0),
|
||||||
|
#(-2.0, 2.0, 0.5),
|
||||||
|
#(-3.0, 3.0, 0.33),
|
||||||
|
])
|
||||||
|
result
|
||||||
|
|> maths.is_close(-1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try invalid input with weights that are negative
|
||||||
|
maths.cosine_similarity_with_weights([
|
||||||
|
#(1.0, 4.0, -7.0),
|
||||||
|
#(2.0, 5.0, -8.0),
|
||||||
|
#(3.0, 6.0, -9.0),
|
||||||
|
])
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chebyshev_distance_test() {
|
||||||
|
// Empty lists returns an error
|
||||||
|
maths.chebyshev_distance([])
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Try different types of valid input
|
||||||
|
maths.chebyshev_distance([#(1.0, 0.0), #(0.0, 2.0)])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
maths.chebyshev_distance([#(1.0, 2.0), #(0.0, 0.0)])
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
maths.chebyshev_distance([#(1.0, -2.0), #(0.0, 0.0)])
|
||||||
|
|> should.equal(Ok(3.0))
|
||||||
|
|
||||||
|
maths.chebyshev_distance([#(-5.0, -1.0), #(-10.0, -12.0), #(-3.0, -3.0)])
|
||||||
|
|> should.equal(Ok(4.0))
|
||||||
|
|
||||||
|
maths.chebyshev_distance([#(1.0, 1.0), #(2.0, 2.0), #(3.0, 3.0)])
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
maths.chebyshev_distance_with_weights([
|
||||||
|
#(-5.0, -1.0, 0.5),
|
||||||
|
#(-10.0, -12.0, 1.0),
|
||||||
|
#(-3.0, -3.0, 1.0),
|
||||||
|
])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn canberra_distance_test() {
|
||||||
|
// Empty lists returns an error
|
||||||
|
maths.canberra_distance([])
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Try different types of valid input
|
||||||
|
maths.canberra_distance([#(0.0, 0.0), #(0.0, 0.0)])
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
|
||||||
|
maths.canberra_distance([#(1.0, -2.0), #(2.0, -1.0)])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
maths.canberra_distance([#(1.0, 0.0), #(0.0, 2.0)])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
maths.canberra_distance([#(1.0, 2.0), #(0.0, 0.0)])
|
||||||
|
|> should.equal(Ok(1.0 /. 3.0))
|
||||||
|
|
||||||
|
maths.canberra_distance_with_weights([#(1.0, 0.0, 1.0), #(0.0, 2.0, 1.0)])
|
||||||
|
|> should.equal(Ok(2.0))
|
||||||
|
|
||||||
|
maths.canberra_distance_with_weights([#(1.0, 0.0, 1.0), #(0.0, 2.0, 0.5)])
|
||||||
|
|> should.equal(Ok(1.5))
|
||||||
|
|
||||||
|
maths.canberra_distance_with_weights([#(1.0, 0.0, 0.5), #(0.0, 2.0, 0.5)])
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
// Try invalid input with weights that are negative
|
||||||
|
maths.canberra_distance_with_weights([
|
||||||
|
#(1.0, 4.0, -7.0),
|
||||||
|
#(2.0, 5.0, -8.0),
|
||||||
|
#(3.0, 6.0, -9.0),
|
||||||
|
])
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn braycurtis_distance_test() {
|
||||||
|
// Empty lists returns an error
|
||||||
|
maths.braycurtis_distance([])
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Try different types of valid input
|
||||||
|
maths.braycurtis_distance([#(0.0, 0.0), #(0.0, 0.0)])
|
||||||
|
|> should.equal(Ok(0.0))
|
||||||
|
maths.braycurtis_distance([#(1.0, -2.0), #(2.0, -1.0)])
|
||||||
|
|> should.equal(Ok(3.0))
|
||||||
|
|
||||||
|
maths.braycurtis_distance([#(1.0, 0.0), #(0.0, 2.0)])
|
||||||
|
|> should.equal(Ok(1.0))
|
||||||
|
|
||||||
|
maths.braycurtis_distance([#(1.0, 3.0), #(2.0, 4.0)])
|
||||||
|
|> should.equal(Ok(0.4))
|
||||||
|
|
||||||
|
maths.braycurtis_distance_with_weights([#(1.0, 3.0, 1.0), #(2.0, 4.0, 1.0)])
|
||||||
|
|> should.equal(Ok(0.4))
|
||||||
|
|
||||||
|
maths.braycurtis_distance_with_weights([#(1.0, 3.0, 0.5), #(2.0, 4.0, 1.0)])
|
||||||
|
|> should.equal(Ok(0.375))
|
||||||
|
|
||||||
|
maths.braycurtis_distance_with_weights([#(1.0, 3.0, 0.25), #(2.0, 4.0, 0.25)])
|
||||||
|
|> should.equal(Ok(0.4))
|
||||||
|
// Try invalid input with weights that are negative
|
||||||
|
maths.braycurtis_distance_with_weights([
|
||||||
|
#(1.0, 4.0, -7.0),
|
||||||
|
#(2.0, 5.0, -8.0),
|
||||||
|
#(3.0, 6.0, -9.0),
|
||||||
|
])
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
656
test/gleam_community/piecewise_test.gleam
Normal file
656
test/gleam_community/piecewise_test.gleam
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam/int
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_ceiling_test() {
|
||||||
|
// Round 3. digit AFTER decimal point
|
||||||
|
maths.round_up(12.0654, 3)
|
||||||
|
|> should.equal(12.066)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_up(12.0654, 2)
|
||||||
|
|> should.equal(12.07)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_up(12.0654, 1)
|
||||||
|
|> should.equal(12.1)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_up(12.0654, 0)
|
||||||
|
|> should.equal(13.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_up(12.0654, -1)
|
||||||
|
|> should.equal(20.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_up(12.0654, -2)
|
||||||
|
|> should.equal(100.0)
|
||||||
|
|
||||||
|
// Round 3. digit BEFORE decimal point
|
||||||
|
maths.round_up(12.0654, -3)
|
||||||
|
|> should.equal(1000.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_floor_test() {
|
||||||
|
// Round 3. digit AFTER decimal point
|
||||||
|
maths.round_down(12.0654, 3)
|
||||||
|
|> should.equal(12.065)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_down(12.0654, 2)
|
||||||
|
|> should.equal(12.06)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_down(12.0654, 1)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_down(12.0654, 0)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_down(12.0654, -1)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_down(12.0654, -2)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_down(12.0654, -3)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_truncate_test() {
|
||||||
|
// Round 3. digit AFTER decimal point
|
||||||
|
maths.round_to_zero(12.0654, 3)
|
||||||
|
|> should.equal(12.065)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_to_zero(12.0654, 2)
|
||||||
|
|> should.equal(12.06)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_to_zero(12.0654, 1)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_to_zero(12.0654, 0)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_to_zero(12.0654, -1)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_to_zero(12.0654, -2)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_to_zero(12.0654, -3)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_to_nearest_test() {
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_to_nearest(1.5, 0)
|
||||||
|
|> should.equal(2.0)
|
||||||
|
|
||||||
|
maths.round_to_nearest(1.75, 0)
|
||||||
|
|> should.equal(2.0)
|
||||||
|
|
||||||
|
maths.round_to_nearest(2.0, 0)
|
||||||
|
|> should.equal(2.0)
|
||||||
|
|
||||||
|
maths.round_to_nearest(3.5, 0)
|
||||||
|
|> should.equal(4.0)
|
||||||
|
|
||||||
|
maths.round_to_nearest(4.5, 0)
|
||||||
|
|> should.equal(4.0)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_to_nearest(-3.5, 0)
|
||||||
|
|> should.equal(-4.0)
|
||||||
|
|
||||||
|
maths.round_to_nearest(-4.5, 0)
|
||||||
|
|> should.equal(-4.0)
|
||||||
|
|
||||||
|
// // Round 3. digit AFTER decimal point
|
||||||
|
maths.round_to_nearest(12.0654, 3)
|
||||||
|
|> should.equal(12.065)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_to_nearest(12.0654, 2)
|
||||||
|
|> should.equal(12.07)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_to_nearest(12.0654, 1)
|
||||||
|
|> should.equal(12.1)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_to_nearest(12.0654, 0)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_to_nearest(12.0654, -1)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_to_nearest(12.0654, -2)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
// Round 3. digit BEFORE decimal point
|
||||||
|
maths.round_to_nearest(12.0654, -3)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_up_test() {
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_up(0.45, 0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.round_up(0.5, 0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.round_up(0.45, 1)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
maths.round_up(0.5, 1)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
maths.round_up(0.455, 2)
|
||||||
|
|> should.equal(0.46)
|
||||||
|
|
||||||
|
maths.round_up(0.505, 2)
|
||||||
|
|> should.equal(0.51)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_up(-0.45, 0)
|
||||||
|
|> should.equal(-0.0)
|
||||||
|
|
||||||
|
maths.round_up(-0.5, 0)
|
||||||
|
|> should.equal(-0.0)
|
||||||
|
|
||||||
|
maths.round_up(-0.45, 1)
|
||||||
|
|> should.equal(-0.4)
|
||||||
|
|
||||||
|
maths.round_up(-0.5, 1)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
|
||||||
|
maths.round_up(-0.455, 2)
|
||||||
|
|> should.equal(-0.45)
|
||||||
|
|
||||||
|
maths.round_up(-0.505, 2)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_down_test() {
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_down(0.45, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_down(0.5, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_down(0.45, 1)
|
||||||
|
|> should.equal(0.4)
|
||||||
|
|
||||||
|
maths.round_down(0.5, 1)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
maths.round_down(0.455, 2)
|
||||||
|
|> should.equal(0.45)
|
||||||
|
|
||||||
|
maths.round_down(0.505, 2)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_down(-0.45, 0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
|
||||||
|
maths.round_down(-0.5, 0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
|
||||||
|
maths.round_down(-0.45, 1)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
|
||||||
|
maths.round_down(-0.5, 1)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
|
||||||
|
maths.round_down(-0.455, 2)
|
||||||
|
|> should.equal(-0.46)
|
||||||
|
|
||||||
|
maths.round_down(-0.505, 2)
|
||||||
|
|> should.equal(-0.51)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_to_zero_test() {
|
||||||
|
// Note: Rounding mode "RoundToZero" is an alias for the truncate function
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_to_zero(0.5, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_to_zero(0.75, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_to_zero(0.45, 1)
|
||||||
|
|> should.equal(0.4)
|
||||||
|
|
||||||
|
maths.round_to_zero(0.57, 1)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
maths.round_to_zero(0.4575, 2)
|
||||||
|
|> should.equal(0.45)
|
||||||
|
|
||||||
|
maths.round_to_zero(0.5075, 2)
|
||||||
|
|> should.equal(0.5)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_to_zero(-0.5, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_to_zero(-0.75, 0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.round_to_zero(-0.45, 1)
|
||||||
|
|> should.equal(-0.4)
|
||||||
|
|
||||||
|
maths.round_to_zero(-0.57, 1)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
|
||||||
|
maths.round_to_zero(-0.4575, 2)
|
||||||
|
|> should.equal(-0.45)
|
||||||
|
|
||||||
|
maths.round_to_zero(-0.5075, 2)
|
||||||
|
|> should.equal(-0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_ties_away_test() {
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_ties_away(1.4, 0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.round_ties_away(1.5, 0)
|
||||||
|
|> should.equal(2.0)
|
||||||
|
|
||||||
|
maths.round_ties_away(2.5, 0)
|
||||||
|
|> should.equal(3.0)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_ties_away(-1.4, 0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
|
||||||
|
maths.round_ties_away(-1.5, 0)
|
||||||
|
|> should.equal(-2.0)
|
||||||
|
|
||||||
|
maths.round_ties_away(-2.0, 0)
|
||||||
|
|> should.equal(-2.0)
|
||||||
|
|
||||||
|
maths.round_ties_away(-2.5, 0)
|
||||||
|
|> should.equal(-3.0)
|
||||||
|
|
||||||
|
// Round 3. digit AFTER decimal point
|
||||||
|
maths.round_ties_away(12.0654, 3)
|
||||||
|
|> should.equal(12.065)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_ties_away(12.0654, 2)
|
||||||
|
|> should.equal(12.07)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_ties_away(12.0654, 1)
|
||||||
|
|> should.equal(12.1)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_ties_away(12.0654, 0)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_ties_away(12.0654, -1)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_ties_away(12.0654, -2)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_ties_away(12.0654, -3)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn math_round_ties_up_test() {
|
||||||
|
// Try with positive values
|
||||||
|
maths.round_ties_up(1.4, 0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.round_ties_up(1.5, 0)
|
||||||
|
|> should.equal(2.0)
|
||||||
|
|
||||||
|
maths.round_ties_up(2.5, 0)
|
||||||
|
|> should.equal(3.0)
|
||||||
|
|
||||||
|
// Try with negative values
|
||||||
|
maths.round_ties_up(-1.4, 0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
|
||||||
|
maths.round_ties_up(-1.5, 0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
|
||||||
|
maths.round_ties_up(-2.0, 0)
|
||||||
|
|> should.equal(-2.0)
|
||||||
|
|
||||||
|
maths.round_ties_up(-2.5, 0)
|
||||||
|
|> should.equal(-2.0)
|
||||||
|
|
||||||
|
// Round 3. digit AFTER decimal point
|
||||||
|
maths.round_ties_up(12.0654, 3)
|
||||||
|
|> should.equal(12.065)
|
||||||
|
|
||||||
|
// Round 2. digit AFTER decimal point
|
||||||
|
maths.round_ties_up(12.0654, 2)
|
||||||
|
|> should.equal(12.07)
|
||||||
|
|
||||||
|
// Round 1. digit AFTER decimal point
|
||||||
|
maths.round_ties_up(12.0654, 1)
|
||||||
|
|> should.equal(12.1)
|
||||||
|
|
||||||
|
// Round 0. digit BEFORE decimal point
|
||||||
|
maths.round_ties_up(12.0654, 0)
|
||||||
|
|> should.equal(12.0)
|
||||||
|
|
||||||
|
// Round 1. digit BEFORE decimal point
|
||||||
|
maths.round_ties_up(12.0654, -1)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_ties_up(12.0654, -2)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
// Round 2. digit BEFORE decimal point
|
||||||
|
maths.round_ties_up(12.0654, -3)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_absolute_difference_test() {
|
||||||
|
maths.float_absolute_difference(20.0, 15.0)
|
||||||
|
|> should.equal(5.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(-20.0, -15.0)
|
||||||
|
|> should.equal(5.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(20.0, -15.0)
|
||||||
|
|> should.equal(35.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(-20.0, 15.0)
|
||||||
|
|> should.equal(35.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(0.0, 0.0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(1.0, 2.0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(2.0, 1.0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(-1.0, 0.0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(0.0, -1.0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(10.0, 20.0)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(-10.0, -20.0)
|
||||||
|
|> should.equal(10.0)
|
||||||
|
|
||||||
|
maths.float_absolute_difference(-10.5, 10.5)
|
||||||
|
|> should.equal(21.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_absolute_difference_test() {
|
||||||
|
maths.int_absolute_difference(20, 15)
|
||||||
|
|> should.equal(5)
|
||||||
|
|
||||||
|
maths.int_absolute_difference(-20, -15)
|
||||||
|
|> should.equal(5)
|
||||||
|
|
||||||
|
maths.int_absolute_difference(20, -15)
|
||||||
|
|> should.equal(35)
|
||||||
|
|
||||||
|
maths.int_absolute_difference(-20, 15)
|
||||||
|
|> should.equal(35)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_sign_test() {
|
||||||
|
maths.float_sign(100.0)
|
||||||
|
|> should.equal(1.0)
|
||||||
|
|
||||||
|
maths.float_sign(0.0)
|
||||||
|
|> should.equal(0.0)
|
||||||
|
|
||||||
|
maths.float_sign(-100.0)
|
||||||
|
|> should.equal(-1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_flip_sign_test() {
|
||||||
|
maths.float_flip_sign(100.0)
|
||||||
|
|> should.equal(-100.0)
|
||||||
|
|
||||||
|
maths.float_flip_sign(0.0)
|
||||||
|
|> should.equal(-0.0)
|
||||||
|
|
||||||
|
maths.float_flip_sign(-100.0)
|
||||||
|
|> should.equal(100.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_copy_sign_test() {
|
||||||
|
maths.float_copy_sign(100.0, 10.0)
|
||||||
|
|> should.equal(100.0)
|
||||||
|
|
||||||
|
maths.float_copy_sign(-100.0, 10.0)
|
||||||
|
|> should.equal(100.0)
|
||||||
|
|
||||||
|
maths.float_copy_sign(100.0, -10.0)
|
||||||
|
|> should.equal(-100.0)
|
||||||
|
|
||||||
|
maths.float_copy_sign(-100.0, -10.0)
|
||||||
|
|> should.equal(-100.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_sign_test() {
|
||||||
|
maths.int_sign(100)
|
||||||
|
|> should.equal(1)
|
||||||
|
|
||||||
|
maths.int_sign(0)
|
||||||
|
|> should.equal(0)
|
||||||
|
|
||||||
|
maths.int_sign(-100)
|
||||||
|
|> should.equal(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_flip_sign_test() {
|
||||||
|
maths.int_flip_sign(100)
|
||||||
|
|> should.equal(-100)
|
||||||
|
|
||||||
|
maths.int_flip_sign(0)
|
||||||
|
|> should.equal(-0)
|
||||||
|
|
||||||
|
maths.int_flip_sign(-100)
|
||||||
|
|> should.equal(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_copy_sign_test() {
|
||||||
|
maths.int_copy_sign(100, 10)
|
||||||
|
|> should.equal(100)
|
||||||
|
|
||||||
|
maths.int_copy_sign(-100, 10)
|
||||||
|
|> should.equal(100)
|
||||||
|
|
||||||
|
maths.int_copy_sign(100, -10)
|
||||||
|
|> should.equal(-100)
|
||||||
|
|
||||||
|
maths.int_copy_sign(-100, -10)
|
||||||
|
|> should.equal(-100)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_minmax_test() {
|
||||||
|
maths.minmax(0.75, 0.5, float.compare)
|
||||||
|
|> should.equal(#(0.5, 0.75))
|
||||||
|
|
||||||
|
maths.minmax(0.5, 0.75, float.compare)
|
||||||
|
|> should.equal(#(0.5, 0.75))
|
||||||
|
|
||||||
|
maths.minmax(-0.75, 0.5, float.compare)
|
||||||
|
|> should.equal(#(-0.75, 0.5))
|
||||||
|
|
||||||
|
maths.minmax(-0.75, 0.5, float.compare)
|
||||||
|
|> should.equal(#(-0.75, 0.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_minmax_test() {
|
||||||
|
maths.minmax(75, 50, int.compare)
|
||||||
|
|> should.equal(#(50, 75))
|
||||||
|
|
||||||
|
maths.minmax(50, 75, int.compare)
|
||||||
|
|> should.equal(#(50, 75))
|
||||||
|
|
||||||
|
maths.minmax(-75, 50, int.compare)
|
||||||
|
|> should.equal(#(-75, 50))
|
||||||
|
|
||||||
|
maths.minmax(-75, 50, int.compare)
|
||||||
|
|> should.equal(#(-75, 50))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_minimum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.list_minimum(float.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4.5, 4.5, 3.5, 2.5, 1.5]
|
||||||
|
|> maths.list_minimum(float.compare)
|
||||||
|
|> should.equal(Ok(1.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_minimum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.list_minimum(int.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4, 4, 3, 2, 1]
|
||||||
|
|> maths.list_minimum(int.compare)
|
||||||
|
|> should.equal(Ok(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_maximum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.list_maximum(float.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4.5, 4.5, 3.5, 2.5, 1.5]
|
||||||
|
|> maths.list_maximum(float.compare)
|
||||||
|
|> should.equal(Ok(4.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_maximum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.list_maximum(int.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4, 4, 3, 2, 1]
|
||||||
|
|> maths.list_maximum(int.compare)
|
||||||
|
|> should.equal(Ok(4))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_arg_maximum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.arg_maximum(float.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4.5, 4.5, 3.5, 2.5, 1.5]
|
||||||
|
|> maths.arg_maximum(float.compare)
|
||||||
|
|> should.equal(Ok([0, 1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_arg_maximum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.arg_maximum(int.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4, 4, 3, 2, 1]
|
||||||
|
|> maths.arg_maximum(int.compare)
|
||||||
|
|> should.equal(Ok([0, 1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_arg_minimum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.arg_minimum(float.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4.5, 4.5, 3.5, 2.5, 1.5]
|
||||||
|
|> maths.arg_minimum(float.compare)
|
||||||
|
|> should.equal(Ok([4]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_arg_minimum_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.arg_minimum(int.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4, 4, 3, 2, 1]
|
||||||
|
|> maths.arg_minimum(int.compare)
|
||||||
|
|> should.equal(Ok([4]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_extrema_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.extrema(float.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||||
|
|> maths.extrema(float.compare)
|
||||||
|
|> should.equal(Ok(#(1.0, 4.0)))
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1.0, 4.0, 2.0, 5.0, 0.0]
|
||||||
|
|> maths.extrema(float.compare)
|
||||||
|
|> should.equal(Ok(#(0.0, 5.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int_list_extrema_test() {
|
||||||
|
// An empty lists returns an error
|
||||||
|
[]
|
||||||
|
|> maths.extrema(int.compare)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[4, 4, 3, 2, 1]
|
||||||
|
|> maths.extrema(int.compare)
|
||||||
|
|> should.equal(Ok(#(1, 4)))
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
[1, 4, 2, 5, 0]
|
||||||
|
|> maths.extrema(int.compare)
|
||||||
|
|> should.equal(Ok(#(0, 5)))
|
||||||
|
}
|
390
test/gleam_community/sequences_test.gleam
Normal file
390
test/gleam_community/sequences_test.gleam
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam/list
|
||||||
|
import gleam/yielder
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_list_linear_space_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
// ---> With endpoint included
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([10.0, 20.0, 30.0, 40.0, 50.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try with negative stop
|
||||||
|
// ----> Without endpoint included
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([10.0, 18.0, 26.0, 34.0, 42.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([10.0, 12.0, 14.0, 16.0, 18.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try with negative stop
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, -50.0, 5, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([10.0, -2.0, -14.0, -26.0, -38.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(linspace) = maths.linear_space(10.0, -20.0, 5, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([10.0, 2.5, -5.0, -12.5, -20.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Try with negative start
|
||||||
|
let assert Ok(linspace) = maths.linear_space(-10.0, 50.0, 5, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([-10.0, 2.0, 14.0, 26.0, 38.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
let assert Ok(linspace) = maths.linear_space(-10.0, 20.0, 5, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
linspace |> yielder.to_list() |> list.zip([-10.0, -2.5, 5.0, 12.5, 20.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// A negative number of points does not work (-5)
|
||||||
|
maths.linear_space(10.0, 50.0, -5, True)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_logarithmic_space_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
// ---> With endpoint included
|
||||||
|
// - Positive start, stop, base
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive start, stop, negative base
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([-10.0, 100.0, -1000.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive start, negative stop, base
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, -10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([-10.0, -0.1, -0.001]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive start, base, negative stop
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([10.0, 0.1, 0.001]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive stop, base, negative start
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// ----> Without endpoint included
|
||||||
|
// - Positive start, stop, base
|
||||||
|
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, False, 10.0)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([10.0, 46.41588834, 215.443469]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// A negative number of points does not work (-3)
|
||||||
|
maths.logarithmic_space(1.0, 3.0, -3, True, 10.0)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_geometric_space_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
// ---> With endpoint included
|
||||||
|
// - Positive start, stop
|
||||||
|
let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive start, negative stop
|
||||||
|
let assert Ok(logspace) = maths.geometric_space(10.0, 0.001, 3, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([10.0, 0.1, 0.001]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// - Positive stop, negative start
|
||||||
|
let assert Ok(logspace) = maths.geometric_space(0.1, 1000.0, 3, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// ----> Without endpoint included
|
||||||
|
// - Positive start, stop
|
||||||
|
let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
logspace
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([10.0, 46.41588834, 215.443469]),
|
||||||
|
0.0,
|
||||||
|
tol,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// Test invalid input (start and stop can't be equal to 0.0)
|
||||||
|
maths.geometric_space(0.0, 1000.0, 3, False)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
maths.geometric_space(-1000.0, 0.0, 3, False)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// A negative number of points does not work
|
||||||
|
maths.geometric_space(-1000.0, 0.0, -3, False)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_arange_test() {
|
||||||
|
// Positive start, stop, step
|
||||||
|
maths.arange(1.0, 5.0, 1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([1.0, 2.0, 3.0, 4.0])
|
||||||
|
|
||||||
|
maths.arange(1.0, 5.0, 0.5)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
|
||||||
|
|
||||||
|
maths.arange(1.0, 2.0, 0.25)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([1.0, 1.25, 1.5, 1.75])
|
||||||
|
|
||||||
|
// Reverse (switch start/stop largest/smallest value)
|
||||||
|
maths.arange(5.0, 1.0, 1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Reverse negative step
|
||||||
|
maths.arange(5.0, 1.0, -1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([5.0, 4.0, 3.0, 2.0])
|
||||||
|
|
||||||
|
// Positive start, negative stop, step
|
||||||
|
maths.arange(5.0, -1.0, -1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
|
||||||
|
|
||||||
|
// Negative start, stop, step
|
||||||
|
maths.arange(-5.0, -1.0, -1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([])
|
||||||
|
|
||||||
|
// Negative start, stop, positive step
|
||||||
|
maths.arange(-5.0, -1.0, 1.0)
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([-5.0, -4.0, -3.0, -2.0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_exponential_space_test() {
|
||||||
|
let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
// ---> With endpoint included
|
||||||
|
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
exp_space |> yielder.to_list() |> list.zip([1.0, 10.0, 100.0, 1000.0]),
|
||||||
|
0.0,
|
||||||
|
tolerance,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// ---> Without endpoint included
|
||||||
|
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, False)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
exp_space
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([
|
||||||
|
1.0, 5.623413251903491, 31.622776601683793, 177.82794100389228,
|
||||||
|
]),
|
||||||
|
0.0,
|
||||||
|
tolerance,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// A negative number of points does not work (-3)
|
||||||
|
maths.exponential_space(1.0, 1000.0, -3, True)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_list_symmetric_space_test() {
|
||||||
|
let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Check that the function agrees, at some arbitrary input
|
||||||
|
// points, with known function values
|
||||||
|
let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
|
||||||
|
sym_space
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
|
||||||
|
|
||||||
|
let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
|
||||||
|
sym_space
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
|
||||||
|
|
||||||
|
// Negative center
|
||||||
|
let assert Ok(sym_space) = maths.symmetric_space(-10.0, 5.0, 5)
|
||||||
|
sym_space
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> should.equal([-15.0, -12.5, -10.0, -7.5, -5.0])
|
||||||
|
|
||||||
|
// Uneven number of points
|
||||||
|
let assert Ok(sym_space) = maths.symmetric_space(0.0, 2.0, 4)
|
||||||
|
let assert Ok(result) =
|
||||||
|
maths.all_close(
|
||||||
|
sym_space
|
||||||
|
|> yielder.to_list()
|
||||||
|
|> list.zip([-2.0, -0.6666666666666667, 0.6666666666666665, 2.0]),
|
||||||
|
0.0,
|
||||||
|
tolerance,
|
||||||
|
)
|
||||||
|
result
|
||||||
|
|> list.all(fn(x) { x == True })
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
// A negative number of points does not work (-5)
|
||||||
|
maths.symmetric_space(0.0, 5.0, -5)
|
||||||
|
|> should.be_error()
|
||||||
|
}
|
113
test/gleam_community/special_test.gleam
Normal file
113
test/gleam_community/special_test.gleam
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import gleam/float
|
||||||
|
import gleam/result
|
||||||
|
import gleam_community/maths
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn float_beta_function_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
maths.beta(-0.5, 0.5)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.beta(0.5, 0.5)
|
||||||
|
|> maths.is_close(3.1415926535897927, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.beta(0.5, -0.5)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.beta(5.0, 5.0)
|
||||||
|
|> maths.is_close(0.0015873015873015873, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_error_function_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
maths.erf(-0.5)
|
||||||
|
|> maths.is_close(-0.5204998778130465, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.erf(0.5)
|
||||||
|
|> maths.is_close(0.5204998778130465, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.erf(1.0)
|
||||||
|
|> maths.is_close(0.8427007929497148, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.erf(2.0)
|
||||||
|
|> maths.is_close(0.9953222650189527, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.erf(10.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_gamma_function_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
maths.gamma(-0.5)
|
||||||
|
|> maths.is_close(-3.5449077018110318, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.gamma(0.5)
|
||||||
|
|> maths.is_close(1.7724538509055159, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.gamma(1.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.gamma(2.0)
|
||||||
|
|> maths.is_close(1.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.gamma(3.0)
|
||||||
|
|> maths.is_close(2.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.gamma(10.0)
|
||||||
|
|> maths.is_close(362_880.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float_incomplete_gamma_function_test() {
|
||||||
|
let assert Ok(tol) = float.power(10.0, -6.0)
|
||||||
|
|
||||||
|
// Invalid input gives an error
|
||||||
|
// 1st arg is invalid
|
||||||
|
maths.incomplete_gamma(-1.0, 1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// 2nd arg is invalid
|
||||||
|
maths.incomplete_gamma(1.0, -1.0)
|
||||||
|
|> should.be_error()
|
||||||
|
|
||||||
|
// Valid input returns a result
|
||||||
|
maths.incomplete_gamma(1.0, 0.0)
|
||||||
|
|> result.unwrap(-999.0)
|
||||||
|
|> maths.is_close(0.0, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.incomplete_gamma(1.0, 2.0)
|
||||||
|
|> result.unwrap(-999.0)
|
||||||
|
|> maths.is_close(0.864664716763387308106, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.incomplete_gamma(2.0, 3.0)
|
||||||
|
|> result.unwrap(-999.0)
|
||||||
|
|> maths.is_close(0.8008517265285442280826, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
|
||||||
|
maths.incomplete_gamma(3.0, 4.0)
|
||||||
|
|> result.unwrap(-999.0)
|
||||||
|
|> maths.is_close(1.523793388892911312363, 0.0, tol)
|
||||||
|
|> should.be_true()
|
||||||
|
}
|
Loading…
Reference in a new issue