mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-15 07:59:01 +00:00
Refactor
This commit is contained in:
parent
cd1f8e6249
commit
c7e703a3c4
33 changed files with 7294 additions and 7160 deletions
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
|
@ -5,29 +5,24 @@ on:
|
|||
tags: ["v*"]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: erlef/setup-beam@v1.16.0
|
||||
with:
|
||||
otp-version: "25.0"
|
||||
gleam-version: "0.25.3"
|
||||
|
||||
otp-version: "26.0.2"
|
||||
gleam-version: "0.30.5"
|
||||
- run: cargo install tomlq
|
||||
|
||||
- run: |
|
||||
if [ "v$(tomlq version -f gleam.toml)" == "${{ github.ref_name }}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
echo "tag does not match version in gleam.toml, refusing to publish"
|
||||
exit 1
|
||||
- run: gleam format --check
|
||||
|
||||
- run: gleam test
|
||||
|
||||
- run: gleam format --check src test
|
||||
- run: gleam test --target erlang
|
||||
- run: gleam test --target javascript
|
||||
- run: gleam publish -y
|
||||
env:
|
||||
HEXPM_USER: ${{ secrets.HEX_USERNAME }}
|
||||
|
|
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
|
@ -14,14 +14,14 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: erlef/setup-beam@v1
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: erlef/setup-beam@v1.16.0
|
||||
with:
|
||||
otp-version: "25.0"
|
||||
gleam-version: "0.28.3"
|
||||
otp-version: "26.0.2"
|
||||
gleam-version: "0.30.5"
|
||||
- uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: "16.18.1"
|
||||
- run: gleam format --check src test
|
||||
- run: gleam test --target erlang
|
||||
- run: gleam test --target javascript
|
||||
- run: gleam format --check src test
|
||||
- run: gleam test --target javascript
|
|
@ -6,7 +6,7 @@ description = "A basic maths library"
|
|||
repository = { type = "github", user = "gleam-community", repo = "maths" }
|
||||
|
||||
[dependencies]
|
||||
gleam_stdlib = "~> 0.28"
|
||||
gleam_stdlib = "~> 0.30"
|
||||
|
||||
[dev-dependencies]
|
||||
gleeunit = "~> 0.7"
|
||||
gleeunit = "~> 0.10"
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
# You typically do not need to edit this file
|
||||
|
||||
packages = [
|
||||
{ name = "gleam_stdlib", version = "0.28.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "73F0A89FADE5022CBEF6D6C3551F9ADCE7054AFCE0CB1DC4C6D5AB4CA62D0111" },
|
||||
{ name = "gleeunit", version = "0.10.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "ECEA2DE4BE6528D36AFE74F42A21CDF99966EC36D7F25DEB34D47DD0F7977BAF" },
|
||||
{ name = "gleam_stdlib", version = "0.30.2", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "8D8BF3790AA31176B1E1C0B517DD74C86DA8235CF3389EA02043EE4FD82AE3DC" },
|
||||
{ name = "gleeunit", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "1397E5C4AC4108769EE979939AC39BF7870659C5AFB714630DEEEE16B8272AD5" },
|
||||
]
|
||||
|
||||
[requirements]
|
||||
gleam_stdlib = "~> 0.28"
|
||||
gleeunit = "~> 0.7"
|
||||
gleam_stdlib = { version = "~> 0.30" }
|
||||
gleeunit = { version = "~> 0.10" }
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: false},
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
|
@ -18,29 +20,32 @@
|
|||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing mathematical functions applying to one or more lists of integers.
|
||||
////
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Distances, sums and products**
|
||||
//// * [`sum`](#sum)
|
||||
//// * [`product`](#product)
|
||||
//// * [`cumulative_sum`](#cumulative_sum)
|
||||
//// * [`cumulative_product`](#cumulative_product)
|
||||
//// * [`manhatten_distance`](#manhatten_distance)
|
||||
//// * **Misc. mathematical functions**
|
||||
//// * [`maximum`](#maximum)
|
||||
//// * [`minimum`](#minimum)
|
||||
//// * [`extrema`](#extrema)
|
||||
//// * [`arg_maximum`](#arg_maximum)
|
||||
//// * [`arg_minimum`](#arg_minimum)
|
||||
////
|
||||
//// 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)
|
||||
//// * **Sums and products**
|
||||
//// * [`float_sum`](#float_sum)
|
||||
//// * [`int_sum`](#int_sum)
|
||||
//// * [`float_product`](#float_product)
|
||||
//// * [`int_product`](#int_product)
|
||||
//// * [`float_cumulative_sum`](#cumulative_sum)
|
||||
//// * [`int_cumulative_sum`](#cumulative_sum)
|
||||
//// * [`float_cumulative_product`](#float_cumulative_product)
|
||||
//// * [`int_cumulative_product`](#int_cumulative_product)
|
||||
////
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
import gleam/pair
|
||||
import gleam_community/maths/int as intx
|
||||
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">
|
||||
|
@ -48,32 +53,24 @@ import gleam_community/maths/int as intx
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the Manhatten distance between two lists (representing vectors):
|
||||
///
|
||||
/// \\[
|
||||
/// \sum_{i=1}^n \left|x_i - y_i \right|
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$.
|
||||
/// The function calculates the greatest common multiple of two integers $$x, y \in \mathbb{Z}$$.
|
||||
/// The greatest common multiple is the largest positive integer that is divisible by both $$x$$ and $$y$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // Empty lists returns 0
|
||||
/// int_list.manhatten_distance([], [])
|
||||
/// |> should.equal(Ok(0))
|
||||
///
|
||||
/// // Differing lengths returns error
|
||||
/// int_list.manhatten_distance([], [1])
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// let assert Ok(result) = int_list.manhatten_distance([0, 0], [1, 2])
|
||||
/// result
|
||||
/// |> should.equal(3)
|
||||
/// pub fn example() {
|
||||
/// intx.lcm(1, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.lcm(100, 10)
|
||||
/// |> should.equal(10)
|
||||
///
|
||||
/// intx.gcd(-36, -17)
|
||||
/// |> should.equal(1)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
|
@ -83,23 +80,198 @@ import gleam_community/maths/int as intx
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn manhatten_distance(
|
||||
xarr: List(Int),
|
||||
yarr: List(Int),
|
||||
) -> Result(Int, String) {
|
||||
let xlen: Int = list.length(xarr)
|
||||
let ylen: Int = list.length(yarr)
|
||||
case xlen == ylen {
|
||||
False ->
|
||||
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
|
||||
|> Error
|
||||
True ->
|
||||
list.zip(xarr, yarr)
|
||||
|> list.map(fn(tuple: #(Int, Int)) -> Int {
|
||||
int.absolute_value(pair.first(tuple) - pair.second(tuple))
|
||||
})
|
||||
|> sum()
|
||||
|> Ok
|
||||
pub fn gcd(x: Int, y: Int) -> Int {
|
||||
let absx: Int = piecewise.int_absolute_value(x)
|
||||
let absy: Int = piecewise.int_absolute_value(y)
|
||||
do_gcd(absx, absy)
|
||||
}
|
||||
|
||||
pub 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>
|
||||
///
|
||||
/// 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.lcm(1, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.lcm(100, 10)
|
||||
/// |> should.equal(100)
|
||||
///
|
||||
/// intx.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: Int = piecewise.int_absolute_value(x)
|
||||
let absy: Int = 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 iteself.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.divisors(4)
|
||||
/// |> should.equal([1, 2, 4])
|
||||
///
|
||||
/// intx.divisors(6)
|
||||
/// |> should.equal([1, 2, 3, 6])
|
||||
///
|
||||
/// intx.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)
|
||||
}
|
||||
|
||||
pub fn find_divisors(n: Int) -> List(Int) {
|
||||
let nabs: Float = piecewise.float_absolute_value(conversion.int_to_float(n))
|
||||
let assert Ok(sqrt_result) = elementary.square_root(nabs)
|
||||
let max: Int = conversion.float_to_int(sqrt_result) + 1
|
||||
list.range(2, max)
|
||||
|> list.fold(
|
||||
[1, n],
|
||||
fn(acc: List(Int), i: Int) -> List(Int) {
|
||||
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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.proper_divisors(4)
|
||||
/// |> should.equal([1, 2])
|
||||
///
|
||||
/// intx.proper_divisors(6)
|
||||
/// |> should.equal([1, 2, 3])
|
||||
///
|
||||
/// intx.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: List(Int) = 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>
|
||||
///
|
||||
/// Calculcate 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$$ is the value in the input list indexed by $$i$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> float_list.sum()
|
||||
/// |> should.equal(0.0)
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1.0, 2.0, 3.0]
|
||||
/// |> float_list.sum()
|
||||
/// |> 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)) -> Float {
|
||||
case arr {
|
||||
[] -> 0.0
|
||||
_ ->
|
||||
arr
|
||||
|> list.fold(0.0, fn(acc: Float, a: Float) -> Float { a +. acc })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +314,7 @@ pub fn manhatten_distance(
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn sum(arr: List(Int)) -> Int {
|
||||
pub fn int_sum(arr: List(Int)) -> Int {
|
||||
case arr {
|
||||
[] -> 0
|
||||
_ ->
|
||||
|
@ -151,6 +323,54 @@ pub fn sum(arr: List(Int)) -> 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>
|
||||
///
|
||||
/// Calculcate 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$$ is the value in the input list indexed by $$i$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty list returns 0.0
|
||||
/// []
|
||||
/// |> float_list.sum()
|
||||
/// |> should.equal(0.0)
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1.0, 2.0, 3.0]
|
||||
/// |> float_list.product()
|
||||
/// |> 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)) -> Float {
|
||||
case arr {
|
||||
[] -> 1.0
|
||||
_ ->
|
||||
arr
|
||||
|> 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>
|
||||
|
@ -190,7 +410,7 @@ pub fn sum(arr: List(Int)) -> Int {
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn product(arr: List(Int)) -> Int {
|
||||
pub fn int_product(arr: List(Int)) -> Int {
|
||||
case arr {
|
||||
[] -> 1
|
||||
_ ->
|
||||
|
@ -199,6 +419,55 @@ pub fn product(arr: List(Int)) -> 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>
|
||||
///
|
||||
/// Calculcate 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$$ 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/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// []
|
||||
/// |> float_list.cumulative_sum()
|
||||
/// |> should.equal([])
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1.0, 2.0, 3.0]
|
||||
/// |> float_list.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: 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>
|
||||
|
@ -239,7 +508,7 @@ pub fn product(arr: List(Int)) -> Int {
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn cumulative_sum(arr: List(Int)) -> List(Int) {
|
||||
pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
|
||||
case arr {
|
||||
[] -> []
|
||||
_ ->
|
||||
|
@ -248,6 +517,56 @@ pub fn cumulative_sum(arr: List(Int)) -> List(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>
|
||||
///
|
||||
/// Calculcate 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$$ 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/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> float_list.cumulative_product()
|
||||
/// |> should.equal([])
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1.0, 2.0, 3.0]
|
||||
/// |> float_list.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_cumumlative_product(arr: List(Float)) -> List(Float) {
|
||||
case arr {
|
||||
[] -> []
|
||||
_ ->
|
||||
arr
|
||||
|> list.scan(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>
|
||||
|
@ -289,7 +608,7 @@ pub fn cumulative_sum(arr: List(Int)) -> List(Int) {
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn cumulative_product(arr: List(Int)) -> List(Int) {
|
||||
pub fn int_cumulative_product(arr: List(Int)) -> List(Int) {
|
||||
case arr {
|
||||
[] -> []
|
||||
_ ->
|
||||
|
@ -297,293 +616,3 @@ pub fn cumulative_product(arr: List(Int)) -> List(Int) {
|
|||
|> list.scan(1, fn(acc: Int, a: Int) -> Int { 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>
|
||||
///
|
||||
/// Returns the indices of the minimum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> int_list.arg_minimum()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4, 4, 3, 2, 1]
|
||||
/// |> int_list.arg_minimum()
|
||||
/// |> should.equal(Ok([4]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn arg_minimum(arr: List(Int)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let assert Ok(min) =
|
||||
arr
|
||||
|> minimum()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Int) -> Int {
|
||||
case a - min {
|
||||
0 -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> 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>
|
||||
///
|
||||
/// Returns the indices of the maximum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> int_list.arg_maximum()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4, 4, 3, 2, 1]
|
||||
/// |> int_list.arg_maximum()
|
||||
/// |> should.equal(Ok([0, 1]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn arg_maximum(arr: List(Int)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let assert Ok(max) =
|
||||
arr
|
||||
|> maximum()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Int) -> Int {
|
||||
case a - max {
|
||||
0 -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> 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>
|
||||
///
|
||||
/// Returns the maximum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> int_list.maximum()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4, 4, 3, 2, 1]
|
||||
/// |> int_list.maximum()
|
||||
/// |> should.equal(Ok(4))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn maximum(arr: List(Int)) -> Result(Int, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Int, a: Int) {
|
||||
case a > acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> 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>
|
||||
///
|
||||
/// Returns the minimum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> int_list.minimum()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4, 4, 3, 2, 1]
|
||||
/// |> int_list.minimum()
|
||||
/// |> should.equal(Ok(1))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn minimum(arr: List(Int)) -> Result(Int, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Int, a: Int) {
|
||||
case a < acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> 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>
|
||||
///
|
||||
/// Returns a tuple consisting of the minimum and maximum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> int_list.extrema()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4, 4, 3, 2, 1]
|
||||
/// |> int_list.extrema()
|
||||
/// |> should.equal(Ok(#(1, 4)))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn extrema(arr: List(Int)) -> Result(#(Int, Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let assert Ok(val_max) = list.at(arr, 0)
|
||||
let assert Ok(val_min) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
#(val_min, val_max),
|
||||
fn(acc: #(Int, Int), a: Int) {
|
||||
let first: Int = pair.first(acc)
|
||||
let second: Int = pair.second(acc)
|
||||
case a < first, a > second {
|
||||
True, True -> #(a, a)
|
||||
True, False -> #(a, second)
|
||||
False, True -> #(first, a)
|
||||
False, False -> #(first, second)
|
||||
}
|
||||
},
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
411
src/gleam_community/maths/combinatorics.gleam
Normal file
411
src/gleam_community/maths/combinatorics.gleam
Normal file
|
@ -0,0 +1,411 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// Combinatorics: A module that offers mathematical functions related to counting, arrangements, and combinations.
|
||||
////
|
||||
//// * **Combinatorial functions**
|
||||
//// * [`combination`](#combination)
|
||||
//// * [`factorial`](#factorial)
|
||||
//// * [`permutation`](#permutation)
|
||||
//// * [`list_combination`](#list_combination)
|
||||
//// * [`list_permutation`](#list_permutation)
|
||||
//// * [`cartesian_product`](#cartesian_product)
|
||||
////
|
||||
|
||||
import gleam/list
|
||||
import gleam/set
|
||||
|
||||
/// <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 a $$k$$-combinations of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
||||
/// \\]
|
||||
/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
|
||||
///
|
||||
/// The implementation uses the effecient iterative multiplicative formula for the computation.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// intx.combination(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.combination(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.combination(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.combination(4, 2)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn combination(n: Int, k: Int) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == 0 || k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
let min = case k < n - k {
|
||||
True -> k
|
||||
False -> n - k
|
||||
}
|
||||
list.range(1, min)
|
||||
|> list.fold(
|
||||
1,
|
||||
fn(acc: Int, x: Int) -> Int { acc * { n + 1 - x } / x },
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// intx.factorial(-1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.factorial(0)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// intx.factorial(1)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// intx.factorial(2)
|
||||
/// |> should.equal(Ok(2))
|
||||
/// intx.factorial(3)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// intx.factorial(4)
|
||||
/// |> should.equal(Ok(24))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn factorial(n) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case n {
|
||||
0 ->
|
||||
1
|
||||
|> Ok
|
||||
1 ->
|
||||
1
|
||||
|> Ok
|
||||
_ ->
|
||||
list.range(1, n)
|
||||
|> list.fold(1, fn(acc: Int, x: Int) { acc * x })
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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$$-permuations (without repetitions)
|
||||
/// of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// P(n, k) = \frac{n!}{(n - k)!}
|
||||
/// \\]
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// intx.permutation(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.permutation(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.permutation(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.permutation(4, 2)
|
||||
/// |> 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) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
let assert Ok(v1) = factorial(n)
|
||||
let assert Ok(v2) = factorial(n - k)
|
||||
v1 / v2
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 all $$k$$-combinations based on a given list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn list_combination(arr: List(a), k: Int) -> Result(List(List(a)), String) {
|
||||
case k < 0 {
|
||||
True ->
|
||||
"Invalid input argument: k < 0. Valid input is k > 0."
|
||||
|> Error
|
||||
False -> {
|
||||
case k > list.length(arr) {
|
||||
True ->
|
||||
"Invalid input argument: k > length(arr). Valid input is 0 < k <= length(arr)."
|
||||
|> Error
|
||||
False -> {
|
||||
do_list_combination(arr, k, [])
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_list_combination(arr: List(a), k: Int, prefix: List(a)) -> List(List(a)) {
|
||||
case k {
|
||||
0 -> [list.reverse(prefix)]
|
||||
_ ->
|
||||
case arr {
|
||||
[] -> []
|
||||
[x, ..xs] -> {
|
||||
let with_x = do_list_combination(xs, k - 1, [x, ..prefix])
|
||||
let without_x = do_list_combination(xs, k, prefix)
|
||||
list.append(with_x, without_x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 all permutations based on a given list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn list_permutation(arr: List(a)) -> List(List(a)) {
|
||||
case arr {
|
||||
[] -> [[]]
|
||||
_ ->
|
||||
flat_map(
|
||||
arr,
|
||||
fn(x) {
|
||||
let remaining = list.filter(arr, fn(y) { x != y })
|
||||
list.map(list_permutation(remaining), fn(perm) { [x, ..perm] })
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Flat map function
|
||||
fn flat_map(list: List(a), f: fn(a) -> List(b)) -> List(b) {
|
||||
list
|
||||
|> list.map(f)
|
||||
|> concat()
|
||||
}
|
||||
|
||||
/// Concatenate a list of lists
|
||||
fn concat(lists: List(List(a))) -> List(a) {
|
||||
lists
|
||||
|> list.fold([], list.append)
|
||||
}
|
||||
|
||||
/// <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 gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// []
|
||||
/// |> listx.cartesian_product([])
|
||||
/// |> should.equal([])
|
||||
///
|
||||
/// [1.0, 10.0]
|
||||
/// |> listx.cartesian_product([1.0, 2.0])
|
||||
/// |> should.equal([#(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(xarr: List(a), yarr: List(a)) -> List(#(a, a)) {
|
||||
let xset: set.Set(a) =
|
||||
xarr
|
||||
|> set.from_list()
|
||||
let yset: set.Set(a) =
|
||||
yarr
|
||||
|> set.from_list()
|
||||
xset
|
||||
|> set.fold(
|
||||
set.new(),
|
||||
fn(accumulator0: set.Set(#(a, a)), member0: a) -> set.Set(#(a, a)) {
|
||||
set.fold(
|
||||
yset,
|
||||
accumulator0,
|
||||
fn(accumulator1: set.Set(#(a, a)), member1: a) -> set.Set(#(a, a)) {
|
||||
set.insert(accumulator1, #(member0, member1))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|> set.to_list()
|
||||
}
|
180
src/gleam_community/maths/conversion.gleam
Normal file
180
src/gleam_community/maths/conversion.gleam
Normal file
|
@ -0,0 +1,180 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// Conversion: A module containing various functions for converting between types and different 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.to_float(-1)
|
||||
/// |> should.equal(-1.0)
|
||||
///
|
||||
/// intx.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/float as floatx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// floatx.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.
|
||||
/// floatx.round(12.0654, option.Some(0), option.Some(floatx.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/float as floatx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// floatx.to_radian(360.)
|
||||
/// |> should.equal(2. *. floatx.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/float as floatx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// floatx.to_degree(0.0)
|
||||
/// |> should.equal(0.0)
|
||||
///
|
||||
/// floatx.to_degree(2. *. floatx.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
|
1256
src/gleam_community/maths/elementary.gleam
Normal file
1256
src/gleam_community/maths/elementary.gleam
Normal file
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,860 +0,0 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" 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: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing mathematical functions applying to integers.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Sign and absolute value functions**
|
||||
//// * [`absolute_difference`](#absolute_difference)
|
||||
//// * [`sign`](#sign)
|
||||
//// * [`copy_sign`](#copysign)
|
||||
//// * [`flip_sign`](#flipsign)
|
||||
//// * **Misc. mathematical functions**
|
||||
//// * [`minimum`](#min)
|
||||
//// * [`maximum`](#max)
|
||||
//// * [`minmax`](#minmax)
|
||||
//// * **Division functions**
|
||||
//// * [`gcd`](#gcd)
|
||||
//// * [`lcm`](#lcm)
|
||||
//// * [`divisors`](#divisors)
|
||||
//// * [`proper_divisors`](#proper_divisors)
|
||||
//// * **Combinatorial functions**
|
||||
//// * [`combination`](#combination)
|
||||
//// * [`factorial`](#factorial)
|
||||
//// * [`permutation`](#permutation)
|
||||
//// * **Tests**
|
||||
//// * [`is_power`](#is_power)
|
||||
//// * [`is_perfect`](#is_perfect)
|
||||
//// * [`is_even`](#is_even)
|
||||
//// * [`is_odd`](#isodd)
|
||||
//// * **Misc. functions**
|
||||
//// * [`to_float`](#to_float)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
import gleam/io
|
||||
import gleam/option
|
||||
import gleam_community/maths/float as floatx
|
||||
|
||||
/// <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 minimum function that takes two arguments $$x, y \in \mathbb{Z}$$ and returns the smallest of the two.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.minimum(2, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.minimum(1, 2)
|
||||
/// |> should.equal(1)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn minimum(x: Int, y: Int) -> Int {
|
||||
case x < y {
|
||||
True -> x
|
||||
False -> 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 maximum function that takes two arguments $$x, y \in \mathbb{Z}$$ and returns the largest of the two.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.maximum(2, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.maximum(1, 2)
|
||||
/// |> should.equal(1)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn maximum(x: Int, y: Int) -> Int {
|
||||
case x > y {
|
||||
True -> x
|
||||
False -> 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 minmax function that takes two arguments $$x, y \in \mathbb{Z}$$ and returns a tuple with the smallest value first and largest second.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.minmax(2, 1)
|
||||
/// |> should.equal(#(1, 2))
|
||||
///
|
||||
/// intx.minmax(1, 2)
|
||||
/// |> should.equal(#(1, 2))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn minmax(x: Int, y: Int) -> #(Int, Int) {
|
||||
#(minimum(x, y), maximum(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 sign function which returns the sign of the input, indicating
|
||||
/// whether it is positive (+1), negative (-1), or zero (0).
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn sign(x: Int) -> Int {
|
||||
do_sign(x)
|
||||
}
|
||||
|
||||
if erlang {
|
||||
fn do_sign(x: Int) -> Int {
|
||||
case x < 0 {
|
||||
True -> -1
|
||||
False ->
|
||||
case x == 0 {
|
||||
True -> 0
|
||||
False -> 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if javascript {
|
||||
external fn do_sign(Int) -> Int =
|
||||
"../../maths.mjs" "sign"
|
||||
}
|
||||
|
||||
/// <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 flips the sign of a given input value.
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn flip_sign(x: Int) -> Int {
|
||||
-1 * 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 takes two arguments $$x, y \in \mathbb{Z}$$ and returns $$x$$ such that it has the same sign as $$y$$.
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn copy_sign(x: Int, y: Int) -> Int {
|
||||
case sign(x) == sign(y) {
|
||||
// x and y have the same sign, just return x
|
||||
True -> x
|
||||
// x and y have different signs:
|
||||
// - x is positive and y is negative, then flip sign of x
|
||||
// - x is negative and y is positive, then flip sign of x
|
||||
False -> flip_sign(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>
|
||||
///
|
||||
/// A combinatorial function for computing the number of a $$k$$-combinations of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
||||
/// \\]
|
||||
/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
|
||||
///
|
||||
/// The implementation uses the effecient iterative multiplicative formula for the computation.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// intx.combination(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.combination(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.combination(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.combination(4, 2)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn combination(n: Int, k: Int) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == 0 || k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
let min = case k < n - k {
|
||||
True -> k
|
||||
False -> n - k
|
||||
}
|
||||
list.range(1, min)
|
||||
|> list.fold(
|
||||
1,
|
||||
fn(acc: Int, x: Int) -> Int { acc * { n + 1 - x } / x },
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// intx.factorial(-1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.factorial(0)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// intx.factorial(1)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// intx.factorial(2)
|
||||
/// |> should.equal(Ok(2))
|
||||
/// intx.factorial(3)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// intx.factorial(4)
|
||||
/// |> should.equal(Ok(24))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn factorial(n) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case n {
|
||||
0 ->
|
||||
1
|
||||
|> Ok
|
||||
1 ->
|
||||
1
|
||||
|> Ok
|
||||
_ ->
|
||||
list.range(1, n)
|
||||
|> list.fold(1, fn(acc: Int, x: Int) { acc * x })
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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$$-permuations (without repetitions)
|
||||
/// of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// P(n, k) = \frac{n!}{(n - k)!}
|
||||
/// \\]
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// intx.permutation(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// intx.permutation(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.permutation(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// intx.permutation(4, 2)
|
||||
/// |> 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) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
let assert Ok(v1) = factorial(n)
|
||||
let assert Ok(v2) = factorial(n - k)
|
||||
v1 / v2
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 absolute difference:
|
||||
///
|
||||
/// \\[
|
||||
/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}.
|
||||
/// \\]
|
||||
///
|
||||
/// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer value which is the the absolute difference of the inputs.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.absolute_difference(-10, 10)
|
||||
/// |> should.equal(20)
|
||||
///
|
||||
/// intx.absolute_difference(0, -2)
|
||||
/// |> should.equal(2)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn absolute_difference(a: Int, b: Int) -> Int {
|
||||
a - b
|
||||
|> int.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>
|
||||
///
|
||||
/// 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Check if 4 is a power of 2 (it is)
|
||||
/// intx.is_power(4, 2)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// // Check if 5 is a power of 2 (it is not)
|
||||
/// intx.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) =
|
||||
floatx.logarithm(int.to_float(x), option.Some(int.to_float(y)))
|
||||
let assert Ok(truncated) = floatx.truncate(value, option.Some(0))
|
||||
let rem = value -. truncated
|
||||
rem == 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_perfect(6)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// intx.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(proper_divisors(n)) == n
|
||||
}
|
||||
|
||||
fn do_sum(arr: List(Int)) -> Int {
|
||||
case arr {
|
||||
[] -> 0
|
||||
_ ->
|
||||
arr
|
||||
|> list.fold(0, fn(acc: Int, a: Int) -> Int { 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>
|
||||
///
|
||||
/// 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.proper_divisors(4)
|
||||
/// |> should.equal([1, 2])
|
||||
///
|
||||
/// intx.proper_divisors(6)
|
||||
/// |> should.equal([1, 2, 3])
|
||||
///
|
||||
/// intx.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: List(Int) = 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>
|
||||
///
|
||||
/// The function returns all the positive divisors of an integer, including the number iteself.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.divisors(4)
|
||||
/// |> should.equal([1, 2, 4])
|
||||
///
|
||||
/// intx.divisors(6)
|
||||
/// |> should.equal([1, 2, 3, 6])
|
||||
///
|
||||
/// intx.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)
|
||||
}
|
||||
|
||||
pub fn find_divisors(n: Int) -> List(Int) {
|
||||
let nabs: Float = float.absolute_value(to_float(n))
|
||||
let assert Ok(sqrt_result) = floatx.square_root(nabs)
|
||||
let max: Int = floatx.to_int(sqrt_result) + 1
|
||||
list.range(2, max)
|
||||
|> list.fold(
|
||||
[1, n],
|
||||
fn(acc: List(Int), i: Int) -> List(Int) {
|
||||
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 calculates the greatest common multiple of two integers $$x, y \in \mathbb{Z}$$.
|
||||
/// The greatest common multiple is the largest positive integer that is divisible by both $$x$$ and $$y$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.lcm(1, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.lcm(100, 10)
|
||||
/// |> should.equal(10)
|
||||
///
|
||||
/// intx.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: Int = int.absolute_value(x)
|
||||
let absy: Int = int.absolute_value(y)
|
||||
do_gcd(absx, absy)
|
||||
}
|
||||
|
||||
pub 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>
|
||||
///
|
||||
/// 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.lcm(1, 1)
|
||||
/// |> should.equal(1)
|
||||
///
|
||||
/// intx.lcm(100, 10)
|
||||
/// |> should.equal(100)
|
||||
///
|
||||
/// intx.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: Int = int.absolute_value(x)
|
||||
let absy: Int = 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>
|
||||
///
|
||||
/// 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_odd(-3)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// intx.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 even.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_even(-3)
|
||||
/// |> should.equal(False)
|
||||
///
|
||||
/// intx.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 produces a number of type `Float` from an `Int`.
|
||||
///
|
||||
/// Note: The function is equivalent to the similar function in the Gleam stdlib.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.to_float(-1)
|
||||
/// |> should.equal(-1.0)
|
||||
///
|
||||
/// intx.to_float(1)
|
||||
/// |> should.equal(1.0)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn to_float(x: Int) -> Float {
|
||||
int.to_float(x)
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" 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: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing general functions applying to lists.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Combinatorial functions**
|
||||
//// * [`combination`](#combination)
|
||||
//// * [`permutation`](#permutation)
|
||||
//// * [`cartesian_product`](#cartesian_product)
|
||||
//// * **Miscellaneous functions**
|
||||
//// * [`trim`](#trim)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
import gleam/set
|
||||
import gleam/io
|
||||
import gleam/iterator
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Trim a list to a certain size given min/max indices. The min/max indices are inclusive.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> listx.trim(0, 0)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Trim the list to only the middle part of list
|
||||
/// [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
||||
/// |> listx.trim(1, 4)
|
||||
/// |> should.equal(Ok([2.0, 3.0, 4.0, 5.0]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn trim(arr: List(a), min: Int, max: Int) -> Result(List(a), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ ->
|
||||
case min >= 0 && max < list.length(arr) {
|
||||
False ->
|
||||
"Invalid input argument: min < 0 or max < length(arr). Valid input is min > 0 and max < length(arr)."
|
||||
|> Error
|
||||
True ->
|
||||
arr
|
||||
|> list.drop(min)
|
||||
|> list.take(max - min + 1)
|
||||
|> 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>
|
||||
///
|
||||
/// Generate all $$k$$-combinations based on a given list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn combination(arr: List(a), k: Int) -> List(a) {
|
||||
todo
|
||||
}
|
||||
|
||||
/// <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 all permutations based on a given list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn permutation(arr: List(a)) -> List(List(a)) {
|
||||
do_permutation(arr, [], [])
|
||||
}
|
||||
|
||||
fn do_permutation(
|
||||
arr: List(a),
|
||||
pick_acc: List(a),
|
||||
acc: List(a),
|
||||
) -> List(List(a)) {
|
||||
// arr
|
||||
// |> iterator.unfold(fn(xarr: List(a)) -> iterator.Step(List(a), List(a)) {
|
||||
// case xarr {
|
||||
// [head, ..tail] ->
|
||||
// iterator.Next(element: [head, ..tail], accumulator: [head, ..tail])
|
||||
// _ -> iterator.Done
|
||||
// }
|
||||
// })
|
||||
// |> iterator.to_list()
|
||||
todo
|
||||
}
|
||||
|
||||
/// <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 gleeunit/should
|
||||
/// import gleam/list
|
||||
/// import gleam_community/maths/list as listx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// []
|
||||
/// |> listx.cartesian_product([])
|
||||
/// |> should.equal([])
|
||||
///
|
||||
/// [1.0, 10.0]
|
||||
/// |> listx.cartesian_product([1.0, 2.0])
|
||||
/// |> should.equal([#(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(xarr: List(a), yarr: List(a)) -> List(#(a, a)) {
|
||||
let xset: set.Set(a) =
|
||||
xarr
|
||||
|> set.from_list()
|
||||
let yset: set.Set(a) =
|
||||
yarr
|
||||
|> set.from_list()
|
||||
xset
|
||||
|> set.fold(
|
||||
set.new(),
|
||||
fn(accumulator0: set.Set(#(a, a)), member0: a) -> set.Set(#(a, a)) {
|
||||
set.fold(
|
||||
yset,
|
||||
accumulator0,
|
||||
fn(accumulator1: set.Set(#(a, a)), member1: a) -> set.Set(#(a, a)) {
|
||||
set.insert(accumulator1, #(member0, member1))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|> set.to_list()
|
||||
}
|
618
src/gleam_community/maths/metrics.gleam
Normal file
618
src/gleam_community/maths/metrics.gleam
Normal file
|
@ -0,0 +1,618 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// Metrics: A module offering functions for calculating distances and types of metrics.
|
||||
////
|
||||
//// * **Distances**
|
||||
//// * [`norm`](#norm)
|
||||
//// * [`float_manhatten_distance`](#float_manhatten_distance)
|
||||
//// * [`int_manhatten_distance`](#int_manhatten_distance)
|
||||
//// * [`minkowski_distance`](#minkowski_distance)
|
||||
//// * [`euclidean_distance`](#euclidean_distance)
|
||||
//// * **Basic statistical measures**
|
||||
//// * [`mean`](#mean)
|
||||
//// * [`median`](#median)
|
||||
//// * [`variance`](#variance)
|
||||
//// * [`standard_deviation`](#standard_deviation)
|
||||
////
|
||||
|
||||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/piecewise
|
||||
import gleam_community/maths/arithmetics
|
||||
import gleam_community/maths/tests
|
||||
import gleam_community/maths/conversion
|
||||
import gleam/list
|
||||
import gleam/pair
|
||||
import gleam/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>
|
||||
///
|
||||
/// Calculcate the $$p$$-norm of a list (representing a vector):
|
||||
///
|
||||
/// \\[
|
||||
/// \left( \sum_{i=1}^n \left|x_i\right|^{p} \right)^{\frac{1}{p}}
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
///
|
||||
/// [1.0, 1.0, 1.0]
|
||||
/// |> float_list.norm(1.0)
|
||||
/// |> floatx.is_close(3.0, 0.0, tol)
|
||||
/// |> should.be_true()
|
||||
///
|
||||
/// [1.0, 1.0, 1.0]
|
||||
/// |> float_list.norm(-1.0)
|
||||
/// |> floatx.is_close(0.3333333333333333, 0.0, tol)
|
||||
/// |> should.be_true()
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn norm(arr: List(Float), p: Float) -> Float {
|
||||
case arr {
|
||||
[] -> 0.0
|
||||
_ -> {
|
||||
let agg: Float =
|
||||
arr
|
||||
|> list.fold(
|
||||
0.0,
|
||||
fn(acc: Float, a: Float) -> Float {
|
||||
let assert Ok(result) =
|
||||
elementary.power(piecewise.float_absolute_value(a), p)
|
||||
result +. acc
|
||||
},
|
||||
)
|
||||
let assert Ok(result) = elementary.power(agg, 1.0 /. p)
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the Manhatten distance between two lists (representing vectors):
|
||||
///
|
||||
/// \\[
|
||||
/// \sum_{i=1}^n \left|x_i - y_i \right|
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
///
|
||||
/// // Empty lists returns 0.0
|
||||
/// float_list.manhatten_distance([], [])
|
||||
/// |> should.equal(Ok(0.0))
|
||||
///
|
||||
/// // Differing lengths returns error
|
||||
/// float_list.manhatten_distance([], [1.0])
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// let assert Ok(result) = float_list.manhatten_distance([0.0, 0.0], [1.0, 2.0])
|
||||
/// result
|
||||
/// |> floatx.is_close(3.0, 0.0, tol)
|
||||
/// |> should.be_true()
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn float_manhatten_distance(
|
||||
xarr: List(Float),
|
||||
yarr: List(Float),
|
||||
) -> Result(Float, String) {
|
||||
minkowski_distance(xarr, yarr, 1.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>
|
||||
///
|
||||
/// Calculcate the Manhatten distance between two lists (representing vectors):
|
||||
///
|
||||
/// \\[
|
||||
/// \sum_{i=1}^n \left|x_i - y_i \right|
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/int_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // Empty lists returns 0
|
||||
/// int_list.manhatten_distance([], [])
|
||||
/// |> should.equal(Ok(0))
|
||||
///
|
||||
/// // Differing lengths returns error
|
||||
/// int_list.manhatten_distance([], [1])
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// let assert Ok(result) = int_list.manhatten_distance([0, 0], [1, 2])
|
||||
/// result
|
||||
/// |> should.equal(3)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn int_manhatten_distance(
|
||||
xarr: List(Int),
|
||||
yarr: List(Int),
|
||||
) -> Result(Int, String) {
|
||||
let xlen: Int = list.length(xarr)
|
||||
let ylen: Int = list.length(yarr)
|
||||
case xlen == ylen {
|
||||
False ->
|
||||
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
|
||||
|> Error
|
||||
True ->
|
||||
list.zip(xarr, yarr)
|
||||
|> list.map(fn(tuple: #(Int, Int)) -> Int {
|
||||
piecewise.int_absolute_value(pair.first(tuple) - pair.second(tuple))
|
||||
})
|
||||
|> arithmetics.int_sum()
|
||||
|> 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>
|
||||
///
|
||||
/// Calculcate the Minkowski distance between two lists (representing vectors):
|
||||
///
|
||||
/// \\[
|
||||
/// \left( \sum_{i=1}^n \left|x_i - y_i \right|^{p} \right)^{\frac{1}{p}}
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$p >= 1$$ is the order, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$.
|
||||
///
|
||||
/// The Minkowski distance is a generalization of both the Euclidean distance ($$p=2$$) and the Manhattan distance ($$p = 1$$).
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
///
|
||||
/// // Empty lists returns 0.0
|
||||
/// float_list.minkowski_distance([], [], 1.0)
|
||||
/// |> should.equal(Ok(0.0))
|
||||
///
|
||||
/// // Differing lengths returns error
|
||||
/// float_list.minkowski_distance([], [1.0], 1.0)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Test order < 1
|
||||
/// float_list.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// let assert Ok(result) = float_list.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0)
|
||||
/// result
|
||||
/// |> floatx.is_close(3.0, 0.0, tol)
|
||||
/// |> should.be_true()
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn minkowski_distance(
|
||||
xarr: List(Float),
|
||||
yarr: List(Float),
|
||||
p: Float,
|
||||
) -> Result(Float, String) {
|
||||
let xlen: Int = list.length(xarr)
|
||||
let ylen: Int = list.length(yarr)
|
||||
case xlen == ylen {
|
||||
False ->
|
||||
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
|
||||
|> Error
|
||||
True ->
|
||||
case p <. 1.0 {
|
||||
True ->
|
||||
"Invalid input argument: p < 1. Valid input is p >= 1."
|
||||
|> Error
|
||||
False ->
|
||||
list.zip(xarr, yarr)
|
||||
|> list.map(fn(tuple: #(Float, Float)) -> Float {
|
||||
pair.first(tuple) -. pair.second(tuple)
|
||||
})
|
||||
|> norm(p)
|
||||
|> 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>
|
||||
///
|
||||
/// Calculcate the Euclidean distance between two lists (representing vectors):
|
||||
///
|
||||
/// \\[
|
||||
/// \left( \sum_{i=1}^n \left|x_i - y_i \right|^{2} \right)^{\frac{1}{2}}
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
///
|
||||
/// // Empty lists returns 0.0
|
||||
/// float_list.euclidean_distance([], [])
|
||||
/// |> should.equal(Ok(0.0))
|
||||
///
|
||||
/// // Differing lengths returns error
|
||||
/// float_list.euclidean_distance([], [1.0])
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// let assert Ok(result) = float_list.euclidean_distance([0.0, 0.0], [1.0, 2.0])
|
||||
/// result
|
||||
/// |> floatx.is_close(2.23606797749979, 0.0, tol)
|
||||
/// |> should.be_true()
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn euclidean_distance(
|
||||
xarr: List(Float),
|
||||
yarr: List(Float),
|
||||
) -> Result(Float, String) {
|
||||
minkowski_distance(xarr, yarr, 2.0)
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the arithmetic mean of the elements in a list:
|
||||
///
|
||||
/// \\[
|
||||
/// \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the sample size (the length of the list) and
|
||||
/// $$x_i$$ is the sample point in the input list indexed by $$i$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> stats.mean()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1., 2., 3.]
|
||||
/// |> stats.mean()
|
||||
/// |> should.equal(Ok(2.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn mean(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ ->
|
||||
arr
|
||||
|> arithmetics.float_sum()
|
||||
|> fn(a: Float) -> Float {
|
||||
a /. conversion.int_to_float(list.length(arr))
|
||||
}
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the median of the elements in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> stats.median()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1., 2., 3.]
|
||||
/// |> stats.median()
|
||||
/// |> should.equal(Ok(2.))
|
||||
///
|
||||
/// [1., 2., 3., 4.]
|
||||
/// |> stats.median()
|
||||
/// |> should.equal(Ok(2.5))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn median(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
let count: Int = list.length(arr)
|
||||
let mid: Int = list.length(arr) / 2
|
||||
let sorted: List(Float) = list.sort(arr, float.compare)
|
||||
case tests.is_odd(count) {
|
||||
// If there is an odd number of elements in the list, then the median
|
||||
// is just the middle value
|
||||
True -> {
|
||||
let assert Ok(val0) = list.at(sorted, mid)
|
||||
val0
|
||||
|> Ok
|
||||
}
|
||||
// If there is an even number of elements in the list, then the median
|
||||
// is the mean of the two middle values
|
||||
False -> {
|
||||
let assert Ok(val0) = list.at(sorted, mid - 1)
|
||||
let assert Ok(val1) = list.at(sorted, mid)
|
||||
[val0, val1]
|
||||
|> mean()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the sample variance of the elements in a list:
|
||||
/// \\[
|
||||
/// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the sample size (the length of the list) and
|
||||
/// $$x_i$$ is the sample point in the input list indexed by $$i$$.
|
||||
/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta
|
||||
/// Degrees of Freedom", and is by default set to $$d = 0$$, which gives a biased
|
||||
/// estimate of the sample variance. Setting $$d = 1$$ gives an unbiased estimate.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // Degrees of freedom
|
||||
/// let ddof: Int = 1
|
||||
///
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> stats.var(ddof)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1., 2., 3.]
|
||||
/// |> stats.var(ddof)
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ ->
|
||||
case ddof < 0 {
|
||||
True ->
|
||||
"Invalid input argument: ddof < 0. Valid input is ddof >= 0."
|
||||
|> Error
|
||||
False -> {
|
||||
let assert Ok(mean) = mean(arr)
|
||||
arr
|
||||
|> list.map(fn(a: Float) -> Float {
|
||||
let assert Ok(result) = elementary.power(a -. mean, 2.0)
|
||||
result
|
||||
})
|
||||
|> arithmetics.float_sum()
|
||||
|> fn(a: Float) -> Float {
|
||||
a /. {
|
||||
conversion.int_to_float(list.length(arr)) -. conversion.int_to_float(
|
||||
ddof,
|
||||
)
|
||||
}
|
||||
}
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Calculcate the sample standard deviation of the elements in a list:
|
||||
/// \\[
|
||||
/// s = \left(\frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x}))\right)^{\frac{1}{2}}
|
||||
/// \\]
|
||||
///
|
||||
/// In the formula, $$n$$ is the sample size (the length of the list) and
|
||||
/// $$x_i$$ is the sample point in the input list indexed by $$i$$.
|
||||
/// Furthermore, $$\bar{x}$$ is the sample mean and $$d$$ is the "Delta
|
||||
/// Degrees of Freedom", and is by default set to $$d = 0$$, which gives a biased
|
||||
/// estimate of the sample standard deviation. Setting $$d = 1$$ gives an unbiased estimate.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // Degrees of freedom
|
||||
/// let ddof: Int = 1
|
||||
///
|
||||
/// // An empty list returns an error
|
||||
/// []
|
||||
/// |> stats.std(ddof)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [1., 2., 3.]
|
||||
/// |> stats.std(ddof)
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ ->
|
||||
case ddof < 0 {
|
||||
True ->
|
||||
"Invalid input argument: ddof < 0. Valid input is ddof >= 0."
|
||||
|> Error
|
||||
False -> {
|
||||
let assert Ok(variance) = variance(arr, ddof)
|
||||
// The computed variance will always be positive
|
||||
// So an error should never be returned
|
||||
let assert Ok(stdev) = elementary.square_root(variance)
|
||||
stdev
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1168
src/gleam_community/maths/piecewise.gleam
Normal file
1168
src/gleam_community/maths/piecewise.gleam
Normal file
File diff suppressed because it is too large
Load diff
299
src/gleam_community/maths/sequences.gleam
Normal file
299
src/gleam_community/maths/sequences.gleam
Normal file
|
@ -0,0 +1,299 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</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_community/maths/elementary
|
||||
import gleam_community/maths/piecewise
|
||||
import gleam_community/maths/conversion
|
||||
import gleam/list
|
||||
|
||||
/// <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 a list with evenly spaced values within a given interval based on a start, stop value and a given increment (step-length) between consecutive values.
|
||||
/// The list returned includes the given start value but excludes the stop value.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// float_list.arange(1.0, 5.0, 1.0)
|
||||
/// |> should.equal([1.0, 2.0, 3.0, 4.0])
|
||||
///
|
||||
/// // No points returned since
|
||||
/// // start smaller than stop and positive step
|
||||
/// float_list.arange(5.0, 1.0, 1.0)
|
||||
/// |> should.equal([])
|
||||
///
|
||||
/// // Points returned since
|
||||
/// // start smaller than stop but negative step
|
||||
/// float_list.arange(5.0, 1.0, -1.0)
|
||||
/// |> 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) -> List(Float) {
|
||||
case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 {
|
||||
True -> []
|
||||
False -> {
|
||||
let direction: Float = case start <=. stop {
|
||||
True -> 1.0
|
||||
False -> -1.0
|
||||
}
|
||||
let step_abs: Float = piecewise.float_absolute_value(step)
|
||||
let num: Float = piecewise.float_absolute_value(start -. stop) /. step_abs
|
||||
|
||||
list.range(0, conversion.float_to_int(num) - 1)
|
||||
|> list.map(fn(i: Int) -> Float {
|
||||
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>
|
||||
///
|
||||
/// Generate a linearly spaced list of points over a specified interval. The endpoint of the interval can optionally be included/excluded.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
/// let assert Ok(linspace) = float_list.linear_space(10.0, 50.0, 5, True)
|
||||
/// let assert Ok(result) =
|
||||
/// float_list.all_close(linspace, [10.0, 20.0, 30.0, 40.0, 50.0], 0.0, tol)
|
||||
/// result
|
||||
/// |> list.all(fn(x) { x == True })
|
||||
/// |> should.be_true()
|
||||
///
|
||||
/// // A negative number of points (-5) does not work
|
||||
/// float_list.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(List(Float), String) {
|
||||
let direction: Float = case start <=. stop {
|
||||
True -> 1.0
|
||||
False -> -1.0
|
||||
}
|
||||
case num > 0 {
|
||||
True ->
|
||||
case endpoint {
|
||||
True -> {
|
||||
let increment: Float =
|
||||
piecewise.float_absolute_value(start -. stop) /. conversion.int_to_float(
|
||||
num - 1,
|
||||
)
|
||||
list.range(0, num - 1)
|
||||
|> list.map(fn(i: Int) -> Float {
|
||||
start +. conversion.int_to_float(i) *. increment *. direction
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
False -> {
|
||||
let increment: Float =
|
||||
piecewise.float_absolute_value(start -. stop) /. conversion.int_to_float(
|
||||
num,
|
||||
)
|
||||
list.range(0, num - 1)
|
||||
|> list.map(fn(i: Int) -> Float {
|
||||
start +. conversion.int_to_float(i) *. increment *. direction
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
|
||||
False ->
|
||||
"Invalid input: num < 0. Valid input is num > 0."
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 logarithmically spaced list of points over a specified interval. The endpoint of the interval can optionally be included/excluded.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
/// let assert Ok(logspace) = float_list.logarithmic_space(1.0, 3.0, 3, True, 10.0)
|
||||
/// let assert Ok(result) =
|
||||
/// float_list.all_close(logspace, [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
|
||||
/// float_list.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(List(Float), String) {
|
||||
case num > 0 {
|
||||
True -> {
|
||||
let assert Ok(linspace) = linear_space(start, stop, num, endpoint)
|
||||
linspace
|
||||
|> list.map(fn(i: Float) -> Float {
|
||||
let assert Ok(result) = elementary.power(base, i)
|
||||
result
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
False ->
|
||||
"Invalid input: num < 0. Valid input is num > 0."
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 a list of numbers spaced evenly on a log scale (a geometric progression). Each point in the list is a constant multiple of the previous.
|
||||
/// The function is similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints specified directly.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths/float as floatx
|
||||
/// import gleam_community/maths/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
/// let assert Ok(logspace) = float_list.geometric_space(10.0, 1000.0, 3, True)
|
||||
/// let assert Ok(result) =
|
||||
/// float_list.all_close(logspace, [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)
|
||||
/// float_list.geometric_space(0.0, 1000.0, 3, False)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// float_list.geometric_space(-1000.0, 0.0, 3, False)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // A negative number of points (-3) does not work
|
||||
/// float_list.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(List(Float), String) {
|
||||
case start == 0.0 || stop == 0.0 {
|
||||
True ->
|
||||
""
|
||||
|> Error
|
||||
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 ->
|
||||
"Invalid input: num < 0. Valid input is num > 0."
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
}
|
205
src/gleam_community/maths/special.gleam
Normal file
205
src/gleam_community/maths/special.gleam
Normal file
|
@ -0,0 +1,205 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</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_community/maths/conversion
|
||||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/piecewise
|
||||
import gleam/list
|
||||
|
||||
/// <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 [a1, a2, a3, a4, a5]: List(Float) = [
|
||||
0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429,
|
||||
]
|
||||
let p: Float = 0.3275911
|
||||
|
||||
let sign: Float = piecewise.float_sign(x)
|
||||
let x: Float = piecewise.float_absolute_value(x)
|
||||
|
||||
// Formula 7.1.26 given in Abramowitz and Stegun.
|
||||
let t: Float = 1.0 /. { 1.0 +. p *. x }
|
||||
let y: Float =
|
||||
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: Float =
|
||||
list.index_fold(
|
||||
lanczos_p,
|
||||
0.0,
|
||||
fn(acc: Float, v: Float, index: Int) {
|
||||
case index > 0 {
|
||||
True -> acc +. v /. { z +. conversion.int_to_float(index) }
|
||||
False -> v
|
||||
}
|
||||
},
|
||||
)
|
||||
let t: Float = 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, String) {
|
||||
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 ->
|
||||
"Invlaid input argument: a <= 0 or x < 0. Valid input is a > 0 and x >= 0."
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|
||||
fn incomplete_gamma_sum(
|
||||
a: Float,
|
||||
x: Float,
|
||||
t: Float,
|
||||
s: Float,
|
||||
n: Float,
|
||||
) -> Float {
|
||||
case t {
|
||||
0.0 -> s
|
||||
_ -> {
|
||||
let ns: Float = s +. t
|
||||
let nt: Float = t *. { x /. { a +. n } }
|
||||
incomplete_gamma_sum(a, x, nt, ns, n +. 1.0)
|
||||
}
|
||||
}
|
||||
}
|
358
src/gleam_community/maths/tests.gleam
Normal file
358
src/gleam_community/maths/tests.gleam
Normal file
|
@ -0,0 +1,358 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: true},
|
||||
//// {left: '$', right: '$', display: false},
|
||||
//// {left: '\\(', right: '\\)', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// Tests: 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_power`](#is_power)
|
||||
//// * [`is_perfect`](#is_perfect)
|
||||
//// * [`is_even`](#is_even)
|
||||
//// * [`is_odd`](#isodd)
|
||||
|
||||
import gleam/pair
|
||||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/option
|
||||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/piecewise
|
||||
import gleam_community/maths/arithmetics
|
||||
|
||||
/// <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/float as floatx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let val: Float = 99.
|
||||
/// let ref_val: Float = 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: Float = 0.01
|
||||
/// let atol: Float = 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 = float_absolute_difference(a, b)
|
||||
let y: Float = 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/float_list
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let val: Float = 99.
|
||||
/// let ref_val: Float = 100.
|
||||
/// let xarr: List(Float) = list.repeat(val, 42)
|
||||
/// let yarr: List(Float) = 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: Float = 0.01
|
||||
/// let atol: Float = 0.10
|
||||
/// float_list.all_close(xarr, yarr, rtol, atol)
|
||||
/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
|
||||
/// case zarr {
|
||||
/// Ok(arr) ->
|
||||
/// arr
|
||||
/// |> list.all(fn(a: Bool) -> Bool { 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), String) {
|
||||
let xlen: Int = list.length(xarr)
|
||||
let ylen: Int = list.length(yarr)
|
||||
case xlen == ylen {
|
||||
False ->
|
||||
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
|
||||
|> Error
|
||||
True ->
|
||||
list.zip(xarr, yarr)
|
||||
|> list.map(fn(z: #(Float, Float)) -> Bool {
|
||||
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 $$a$$ 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/float as floatx
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// }
|
||||
/// </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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Check if 4 is a power of 2 (it is)
|
||||
/// intx.is_power(4, 2)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// // Check if 5 is a power of 2 (it is not)
|
||||
/// intx.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 assert Ok(truncated) = piecewise.truncate(value, option.Some(0))
|
||||
let rem = value -. truncated
|
||||
rem == 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_perfect(6)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// intx.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: Int, a: Int) -> Int { 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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_even(-3)
|
||||
/// |> should.equal(False)
|
||||
///
|
||||
/// intx.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/int as intx
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// intx.is_odd(-3)
|
||||
/// |> should.equal(True)
|
||||
///
|
||||
/// intx.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
|
||||
}
|
203
test/gleam/gleam_community_maths_arithmetics.gleam
Normal file
203
test/gleam/gleam_community_maths_arithmetics.gleam
Normal file
|
@ -0,0 +1,203 @@
|
|||
import gleam_community/maths/arithmetics
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
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_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()
|
||||
|> should.equal(0.0)
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> arithmetics.float_sum()
|
||||
|> should.equal(6.0)
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> arithmetics.float_sum()
|
||||
|> 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()
|
||||
|> should.equal(1.0)
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> arithmetics.float_product()
|
||||
|> should.equal(6.0)
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> arithmetics.float_product()
|
||||
|> should.equal(-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_cumumlative_product()
|
||||
|> should.equal([])
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> arithmetics.float_cumumlative_product()
|
||||
|> should.equal([1.0, 2.0, 6.0])
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> arithmetics.float_cumumlative_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])
|
||||
}
|
167
test/gleam/gleam_community_maths_combinatorics.gleam
Normal file
167
test/gleam/gleam_community_maths_combinatorics.gleam
Normal file
|
@ -0,0 +1,167 @@
|
|||
import gleam_community/maths/combinatorics
|
||||
import gleam/set
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn int_factorial_test() {
|
||||
// Invalid input gives an error
|
||||
combinatorics.factorial(-1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
combinatorics.factorial(0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.factorial(1)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.factorial(2)
|
||||
|> should.equal(Ok(2))
|
||||
|
||||
combinatorics.factorial(3)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
combinatorics.factorial(4)
|
||||
|> should.equal(Ok(24))
|
||||
}
|
||||
|
||||
pub fn int_combination_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
combinatorics.combination(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
combinatorics.combination(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.combination(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.combination(4, 2)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
combinatorics.combination(7, 5)
|
||||
|> should.equal(Ok(21))
|
||||
// NOTE: Tests with the 'combination' function that produce values that
|
||||
// exceed precision of the JavaScript 'Number' primitive will result in
|
||||
// errors
|
||||
}
|
||||
|
||||
pub fn math_permutation_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
combinatorics.permutation(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
combinatorics.permutation(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.permutation(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
combinatorics.permutation(4, 2)
|
||||
|> should.equal(Ok(12))
|
||||
}
|
||||
|
||||
pub fn list_cartesian_product_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> combinatorics.cartesian_product([])
|
||||
|> should.equal([])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2, 3]
|
||||
|> combinatorics.cartesian_product([1, 2, 3])
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([
|
||||
#(1, 1),
|
||||
#(1, 2),
|
||||
#(1, 3),
|
||||
#(2, 1),
|
||||
#(2, 2),
|
||||
#(2, 3),
|
||||
#(3, 1),
|
||||
#(3, 2),
|
||||
#(3, 3),
|
||||
]))
|
||||
|
||||
[1.0, 10.0]
|
||||
|> combinatorics.cartesian_product([1.0, 2.0])
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([
|
||||
#(1.0, 1.0),
|
||||
#(1.0, 2.0),
|
||||
#(10.0, 1.0),
|
||||
#(10.0, 2.0),
|
||||
]))
|
||||
}
|
||||
|
||||
pub fn list_permutation_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([[]])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2]
|
||||
|> combinatorics.list_permutation()
|
||||
|> should.equal([[1, 2], [2, 1]])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2, 3]
|
||||
|> combinatorics.list_permutation()
|
||||
|> 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],
|
||||
]))
|
||||
}
|
||||
|
||||
pub fn list_combination_test() {
|
||||
// A negative number returns an error
|
||||
[]
|
||||
|> combinatorics.list_combination(-1)
|
||||
|> should.be_error()
|
||||
|
||||
// k is larger than given input list returns an error
|
||||
[1, 2]
|
||||
|> combinatorics.list_combination(3)
|
||||
|> should.be_error()
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> combinatorics.list_combination(0)
|
||||
|> should.equal(Ok([[]]))
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2]
|
||||
|> combinatorics.list_combination(1)
|
||||
|> should.equal(Ok([[1], [2]]))
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2]
|
||||
|> combinatorics.list_combination(2)
|
||||
|> should.equal(Ok([[1, 2]]))
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 2)
|
||||
result
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([[1,
|
||||
2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]))
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
let assert Ok(result) = combinatorics.list_combination([1, 2, 3, 4], 3)
|
||||
result
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
|
||||
}
|
44
test/gleam/gleam_community_maths_conversion.gleam
Normal file
44
test/gleam/gleam_community_maths_conversion.gleam
Normal file
|
@ -0,0 +1,44 @@
|
|||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/tests
|
||||
import gleam_community/maths/conversion
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_to_degree_test() {
|
||||
let assert Ok(tol) = elementary.power(-10.0, -6.0)
|
||||
conversion.radians_to_degrees(0.0)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
conversion.radians_to_degrees(2.0 *. elementary.pi())
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
conversion.degrees_to_radians(360.0)
|
||||
|> tests.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)
|
||||
}
|
482
test/gleam/gleam_community_maths_elementary.gleam
Normal file
482
test/gleam/gleam_community_maths_elementary.gleam
Normal file
|
@ -0,0 +1,482 @@
|
|||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/tests
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/option
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
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
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) = elementary.acos(0.5)
|
||||
result
|
||||
|> tests.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
|
||||
|> tests.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
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.asinh(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.atan(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.atan2(0.0, 1.0)
|
||||
|> tests.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)
|
||||
|> tests.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)
|
||||
|> tests.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)
|
||||
|> tests.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)
|
||||
|> tests.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)
|
||||
|> tests.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
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) = elementary.atanh(0.5)
|
||||
result
|
||||
|> tests.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)
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.cos(elementary.pi())
|
||||
|> tests.is_close(-1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.cos(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.cosh(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.sin(0.5 *. elementary.pi())
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.sin(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.sinh(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.tan(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.tanh(25.0)
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.tanh(-25.0)
|
||||
|> tests.is_close(-1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.tanh(0.5)
|
||||
|> tests.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)
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.exponential(0.5)
|
||||
|> tests.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
|
||||
|> tests.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
|
||||
|> tests.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
|
||||
|> tests.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) = elementary.logarithm_10(10.0)
|
||||
result
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) = elementary.logarithm_10(50.0)
|
||||
result
|
||||
|> tests.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))
|
||||
|
||||
// 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()
|
||||
|> tests.is_close(2.71828, 0.0, 0.00001)
|
||||
|> should.be_true()
|
||||
|
||||
elementary.pi()
|
||||
|> tests.is_close(3.14159, 0.0, 0.00001)
|
||||
|> should.be_true()
|
||||
}
|
|
@ -1,519 +0,0 @@
|
|||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/pair
|
||||
import gleam_community/maths/float_list
|
||||
import gleam_community/maths/float as floatx
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/io
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_list_all_close_test() {
|
||||
let val: Float = 99.0
|
||||
let ref_val: Float = 100.0
|
||||
let xarr: List(Float) = list.repeat(val, 42)
|
||||
let yarr: List(Float) = 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: Float = 0.01
|
||||
let atol: Float = 0.1
|
||||
float_list.all_close(xarr, yarr, rtol, atol)
|
||||
|> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
|
||||
case zarr {
|
||||
Ok(arr) ->
|
||||
arr
|
||||
|> list.all(fn(a: Bool) -> Bool { a })
|
||||
|> Ok
|
||||
_ ->
|
||||
Nil
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|> should.equal(Ok(True))
|
||||
}
|
||||
|
||||
pub fn float_list_norm_test() {
|
||||
let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
|
||||
// An empty lists returns 0.0
|
||||
[]
|
||||
|> float_list.norm(1.0)
|
||||
|> should.equal(0.0)
|
||||
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
[1.0, 1.0, 1.0]
|
||||
|> float_list.norm(1.0)
|
||||
|> floatx.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[1.0, 1.0, 1.0]
|
||||
|> float_list.norm(-1.0)
|
||||
|> floatx.is_close(0.3333333333333333, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -1.0, -1.0]
|
||||
|> float_list.norm(-1.0)
|
||||
|> floatx.is_close(0.3333333333333333, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -1.0, -1.0]
|
||||
|> float_list.norm(1.0)
|
||||
|> floatx.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> float_list.norm(-10.0)
|
||||
|> floatx.is_close(0.9999007044905545, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> float_list.norm(-100.0)
|
||||
|> floatx.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> float_list.norm(2.0)
|
||||
|> floatx.is_close(3.7416573867739413, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_minkowski_test() {
|
||||
let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
float_list.minkowski_distance([], [], 1.0)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
float_list.minkowski_distance([], [1.0], 1.0)
|
||||
|> should.be_error()
|
||||
|
||||
// Test order < 1
|
||||
float_list.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) =
|
||||
float_list.minkowski_distance([1.0, 1.0], [1.0, 1.0], 1.0)
|
||||
result
|
||||
|> floatx.is_close(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) =
|
||||
float_list.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0)
|
||||
result
|
||||
|> floatx.is_close(1.0717734625362931, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) =
|
||||
float_list.minkowski_distance([0.0, 0.0], [1.0, 1.0], 100.0)
|
||||
result
|
||||
|> floatx.is_close(1.0069555500567189, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(result) =
|
||||
float_list.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0)
|
||||
result
|
||||
|> floatx.is_close(1.0717734625362931, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Euclidean distance (p = 2)
|
||||
let assert Ok(result) =
|
||||
float_list.minkowski_distance([0.0, 0.0], [1.0, 2.0], 2.0)
|
||||
result
|
||||
|> floatx.is_close(2.23606797749979, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Manhatten distance (p = 1)
|
||||
let assert Ok(result) =
|
||||
float_list.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0)
|
||||
result
|
||||
|> floatx.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_euclidean_test() {
|
||||
let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
float_list.euclidean_distance([], [])
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
float_list.euclidean_distance([], [1.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Euclidean distance (p = 2)
|
||||
let assert Ok(result) = float_list.euclidean_distance([0.0, 0.0], [1.0, 2.0])
|
||||
result
|
||||
|> floatx.is_close(2.23606797749979, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_manhatten_test() {
|
||||
let assert Ok(tol) = floatx.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
float_list.manhatten_distance([], [])
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
float_list.manhatten_distance([], [1.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Manhatten distance (p = 1)
|
||||
let assert Ok(result) = float_list.manhatten_distance([0.0, 0.0], [1.0, 2.0])
|
||||
result
|
||||
|> floatx.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_linear_space_test() {
|
||||
let assert Ok(tol) = floatx.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) = float_list.linear_space(10.0, 50.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(10.0, 20.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(10.0, 50.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(10.0, 20.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(10.0, -50.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(10.0, -20.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [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) = float_list.linear_space(-10.0, 50.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [-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) = float_list.linear_space(-10.0, 20.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(linspace, [-10.0, -2.5, 5.0, 12.5, 20.0], 0.0, tol)
|
||||
|
||||
// A negative number of points does not work (-5)
|
||||
float_list.linear_space(10.0, 50.0, -5, True)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_list_logarithmic_space_test() {
|
||||
let assert Ok(tol) = floatx.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) =
|
||||
float_list.logarithmic_space(1.0, 3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) =
|
||||
float_list.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [-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) =
|
||||
float_list.logarithmic_space(1.0, -3.0, 3, True, -10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [-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) =
|
||||
float_list.logarithmic_space(1.0, -3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) =
|
||||
float_list.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) =
|
||||
float_list.logarithmic_space(1.0, 3.0, 3, False, 10.0)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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)
|
||||
float_list.logarithmic_space(1.0, 3.0, -3, True, 10.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_list_geometric_space_test() {
|
||||
let assert Ok(tol) = floatx.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) = float_list.geometric_space(10.0, 1000.0, 3, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) = float_list.geometric_space(10.0, 0.001, 3, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) = float_list.geometric_space(0.1, 1000.0, 3, True)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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) = float_list.geometric_space(10.0, 1000.0, 3, False)
|
||||
let assert Ok(result) =
|
||||
float_list.all_close(logspace, [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)
|
||||
float_list.geometric_space(0.0, 1000.0, 3, False)
|
||||
|> should.be_error()
|
||||
|
||||
float_list.geometric_space(-1000.0, 0.0, 3, False)
|
||||
|> should.be_error()
|
||||
|
||||
// A negative number of points does not work
|
||||
float_list.geometric_space(-1000.0, 0.0, -3, False)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_list_arange_test() {
|
||||
// Positive start, stop, step
|
||||
float_list.arange(1.0, 5.0, 1.0)
|
||||
|> should.equal([1.0, 2.0, 3.0, 4.0])
|
||||
|
||||
float_list.arange(1.0, 5.0, 0.5)
|
||||
|> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
|
||||
|
||||
float_list.arange(1.0, 2.0, 0.25)
|
||||
|> should.equal([1.0, 1.25, 1.5, 1.75])
|
||||
|
||||
// Reverse (switch start/stop largest/smallest value)
|
||||
float_list.arange(5.0, 1.0, 1.0)
|
||||
|> should.equal([])
|
||||
|
||||
// Reverse negative step
|
||||
float_list.arange(5.0, 1.0, -1.0)
|
||||
|> should.equal([5.0, 4.0, 3.0, 2.0])
|
||||
|
||||
// Positive start, negative stop, step
|
||||
float_list.arange(5.0, -1.0, -1.0)
|
||||
|> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
|
||||
|
||||
// Negative start, stop, step
|
||||
float_list.arange(-5.0, -1.0, -1.0)
|
||||
|> should.equal([])
|
||||
|
||||
// Negative start, stop, positive step
|
||||
float_list.arange(-5.0, -1.0, 1.0)
|
||||
|> should.equal([-5.0, -4.0, -3.0, -2.0])
|
||||
}
|
||||
|
||||
pub fn float_list_maximum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> float_list.maximum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||
|> float_list.maximum()
|
||||
|> should.equal(Ok(4.0))
|
||||
}
|
||||
|
||||
pub fn float_list_minimum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> float_list.minimum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||
|> float_list.minimum()
|
||||
|> should.equal(Ok(1.0))
|
||||
}
|
||||
|
||||
pub fn float_list_arg_maximum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> float_list.arg_maximum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||
|> float_list.arg_maximum()
|
||||
|> should.equal(Ok([0, 1]))
|
||||
}
|
||||
|
||||
pub fn float_list_arg_minimum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> float_list.arg_minimum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||
|> float_list.arg_minimum()
|
||||
|> should.equal(Ok([4]))
|
||||
}
|
||||
|
||||
pub fn float_list_extrema_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> float_list.extrema()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4.0, 4.0, 3.0, 2.0, 1.0]
|
||||
|> float_list.extrema()
|
||||
|> should.equal(Ok(#(1.0, 4.0)))
|
||||
}
|
||||
|
||||
pub fn float_list_sum_test() {
|
||||
// An empty list returns 0
|
||||
[]
|
||||
|> float_list.sum()
|
||||
|> should.equal(0.0)
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> float_list.sum()
|
||||
|> should.equal(6.0)
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> float_list.sum()
|
||||
|> should.equal(8.0)
|
||||
}
|
||||
|
||||
pub fn float_list_cumulative_sum_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> float_list.cumulative_sum()
|
||||
|> should.equal([])
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> float_list.cumulative_sum()
|
||||
|> should.equal([1.0, 3.0, 6.0])
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> float_list.cumulative_sum()
|
||||
|> should.equal([-2.0, 2.0, 8.0])
|
||||
}
|
||||
|
||||
pub fn float_list_product_test() {
|
||||
// An empty list returns 0
|
||||
[]
|
||||
|> float_list.product()
|
||||
|> should.equal(1.0)
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> float_list.product()
|
||||
|> should.equal(6.0)
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> float_list.product()
|
||||
|> should.equal(-48.0)
|
||||
}
|
||||
|
||||
pub fn float_list_cumulative_product_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> float_list.cumumlative_product()
|
||||
|> should.equal([])
|
||||
|
||||
// Valid input returns a result
|
||||
[1.0, 2.0, 3.0]
|
||||
|> float_list.cumumlative_product()
|
||||
|> should.equal([1.0, 2.0, 6.0])
|
||||
|
||||
[-2.0, 4.0, 6.0]
|
||||
|> float_list.cumumlative_product()
|
||||
|> should.equal([-2.0, -8.0, -48.0])
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,148 +0,0 @@
|
|||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/pair
|
||||
import gleam_community/maths/int_list
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn int_list_maximum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> int_list.maximum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4, 4, 3, 2, 1]
|
||||
|> int_list.maximum()
|
||||
|> should.equal(Ok(4))
|
||||
}
|
||||
|
||||
pub fn int_list_minimum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> int_list.minimum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4, 4, 3, 2, 1]
|
||||
|> int_list.minimum()
|
||||
|> should.equal(Ok(1))
|
||||
}
|
||||
|
||||
pub fn int_list_arg_maximum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> int_list.arg_maximum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4, 4, 3, 2, 1]
|
||||
|> int_list.arg_maximum()
|
||||
|> should.equal(Ok([0, 1]))
|
||||
}
|
||||
|
||||
pub fn int_list_arg_minimum_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> int_list.arg_minimum()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4, 4, 3, 2, 1]
|
||||
|> int_list.arg_minimum()
|
||||
|> should.equal(Ok([4]))
|
||||
}
|
||||
|
||||
pub fn int_list_extrema_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> int_list.extrema()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4, 4, 3, 2, 1]
|
||||
|> int_list.extrema()
|
||||
|> should.equal(Ok(#(1, 4)))
|
||||
}
|
||||
|
||||
pub fn int_list_sum_test() {
|
||||
// An empty list returns 0
|
||||
[]
|
||||
|> int_list.sum()
|
||||
|> should.equal(0)
|
||||
|
||||
// Valid input returns a result
|
||||
[1, 2, 3]
|
||||
|> int_list.sum()
|
||||
|> should.equal(6)
|
||||
|
||||
[-2, 4, 6]
|
||||
|> int_list.sum()
|
||||
|> should.equal(8)
|
||||
}
|
||||
|
||||
pub fn int_list_cumulative_sum_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> int_list.cumulative_sum()
|
||||
|> should.equal([])
|
||||
|
||||
// Valid input returns a result
|
||||
[1, 2, 3]
|
||||
|> int_list.cumulative_sum()
|
||||
|> should.equal([1, 3, 6])
|
||||
|
||||
[-2, 4, 6]
|
||||
|> int_list.cumulative_sum()
|
||||
|> should.equal([-2, 2, 8])
|
||||
}
|
||||
|
||||
pub fn int_list_product_test() {
|
||||
// An empty list returns 0
|
||||
[]
|
||||
|> int_list.product()
|
||||
|> should.equal(1)
|
||||
|
||||
// Valid input returns a result
|
||||
[1, 2, 3]
|
||||
|> int_list.product()
|
||||
|> should.equal(6)
|
||||
|
||||
[-2, 4, 6]
|
||||
|> int_list.product()
|
||||
|> should.equal(-48)
|
||||
}
|
||||
|
||||
pub fn int_list_cumulative_product_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> int_list.cumulative_product()
|
||||
|> should.equal([])
|
||||
|
||||
// Valid input returns a result
|
||||
[1, 2, 3]
|
||||
|> int_list.cumulative_product()
|
||||
|> should.equal([1, 2, 6])
|
||||
|
||||
[-2, 4, 6]
|
||||
|> int_list.cumulative_product()
|
||||
|> should.equal([-2, -8, -48])
|
||||
}
|
||||
|
||||
pub fn int_list_manhatten_test() {
|
||||
// Empty lists returns 0
|
||||
int_list.manhatten_distance([], [])
|
||||
|> should.equal(Ok(0))
|
||||
|
||||
// Differing lengths returns error
|
||||
int_list.manhatten_distance([], [1])
|
||||
|> should.be_error()
|
||||
|
||||
let assert Ok(result) = int_list.manhatten_distance([0, 0], [1, 2])
|
||||
result
|
||||
|> should.equal(3)
|
||||
}
|
|
@ -1,327 +0,0 @@
|
|||
import gleam_community/maths/int as intx
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/result
|
||||
import gleam/io
|
||||
|
||||
pub fn int_absolute_difference_test() {
|
||||
intx.absolute_difference(0, 0)
|
||||
|> should.equal(0)
|
||||
|
||||
intx.absolute_difference(1, 2)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absolute_difference(2, 1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absolute_difference(-1, 0)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absolute_difference(0, -1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absolute_difference(10, 20)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.absolute_difference(-10, -20)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.absolute_difference(-10, 10)
|
||||
|> should.equal(20)
|
||||
}
|
||||
|
||||
pub fn int_factorial_test() {
|
||||
// Invalid input gives an error
|
||||
intx.factorial(-1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.factorial(0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.factorial(1)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.factorial(2)
|
||||
|> should.equal(Ok(2))
|
||||
|
||||
intx.factorial(3)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
intx.factorial(4)
|
||||
|> should.equal(Ok(24))
|
||||
}
|
||||
|
||||
pub fn int_combination_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
intx.combination(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.combination(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.combination(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.combination(4, 2)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
intx.combination(7, 5)
|
||||
|> should.equal(Ok(21))
|
||||
// NOTE: Tests with the 'combination' function that produce values that
|
||||
// exceed precision of the JavaScript 'Number' primitive will result in
|
||||
// errors
|
||||
}
|
||||
|
||||
pub fn math_permutation_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
intx.permutation(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.permutation(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.permutation(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.permutation(4, 2)
|
||||
|> should.equal(Ok(12))
|
||||
}
|
||||
|
||||
pub fn float_minimum_test() {
|
||||
intx.minimum(75, 50)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.minimum(50, 75)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.minimum(-75, 50)
|
||||
|> should.equal(-75)
|
||||
|
||||
intx.minimum(-75, 50)
|
||||
|> should.equal(-75)
|
||||
}
|
||||
|
||||
pub fn float_maximum_test() {
|
||||
intx.maximum(75, 50)
|
||||
|> should.equal(75)
|
||||
|
||||
intx.maximum(50, 75)
|
||||
|> should.equal(75)
|
||||
|
||||
intx.maximum(-75, 50)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.maximum(-75, 50)
|
||||
|> should.equal(50)
|
||||
}
|
||||
|
||||
pub fn int_minmax_test() {
|
||||
intx.minmax(75, 50)
|
||||
|> should.equal(#(50, 75))
|
||||
|
||||
intx.minmax(50, 75)
|
||||
|> should.equal(#(50, 75))
|
||||
|
||||
intx.minmax(-75, 50)
|
||||
|> should.equal(#(-75, 50))
|
||||
|
||||
intx.minmax(-75, 50)
|
||||
|> should.equal(#(-75, 50))
|
||||
}
|
||||
|
||||
pub fn int_sign_test() {
|
||||
intx.sign(100)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.sign(0)
|
||||
|> should.equal(0)
|
||||
|
||||
intx.sign(-100)
|
||||
|> should.equal(-1)
|
||||
}
|
||||
|
||||
pub fn int_flip_sign_test() {
|
||||
intx.flip_sign(100)
|
||||
|> should.equal(-100)
|
||||
|
||||
intx.flip_sign(0)
|
||||
|> should.equal(-0)
|
||||
|
||||
intx.flip_sign(-100)
|
||||
|> should.equal(100)
|
||||
}
|
||||
|
||||
pub fn int_copy_sign_test() {
|
||||
intx.copy_sign(100, 10)
|
||||
|> should.equal(100)
|
||||
|
||||
intx.copy_sign(-100, 10)
|
||||
|> should.equal(100)
|
||||
|
||||
intx.copy_sign(100, -10)
|
||||
|> should.equal(-100)
|
||||
|
||||
intx.copy_sign(-100, -10)
|
||||
|> should.equal(-100)
|
||||
}
|
||||
|
||||
pub fn int_is_power_test() {
|
||||
intx.is_power(10, 10)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_power(11, 10)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_power(4, 2)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_power(5, 2)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_power(27, 3)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_power(28, 3)
|
||||
|> should.equal(False)
|
||||
}
|
||||
|
||||
pub fn int_is_even_test() {
|
||||
intx.is_even(0)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_even(2)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_even(12)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_even(5)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_even(-3)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_even(-4)
|
||||
|> should.equal(True)
|
||||
}
|
||||
|
||||
pub fn int_is_odd_test() {
|
||||
intx.is_odd(0)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_odd(3)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_odd(13)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_odd(4)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_odd(-3)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_odd(-4)
|
||||
|> should.equal(False)
|
||||
}
|
||||
|
||||
pub fn int_gcd_test() {
|
||||
intx.gcd(1, 1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.gcd(100, 10)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.gcd(10, 100)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.gcd(100, -10)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.gcd(-36, -17)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.gcd(-30, -42)
|
||||
|> should.equal(6)
|
||||
}
|
||||
|
||||
pub fn int_lcm_test() {
|
||||
intx.lcm(1, 1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.lcm(100, 10)
|
||||
|> should.equal(100)
|
||||
|
||||
intx.lcm(10, 100)
|
||||
|> should.equal(100)
|
||||
|
||||
intx.lcm(100, -10)
|
||||
|> should.equal(100)
|
||||
|
||||
intx.lcm(-36, -17)
|
||||
|> should.equal(612)
|
||||
|
||||
intx.lcm(-30, -42)
|
||||
|> should.equal(210)
|
||||
}
|
||||
|
||||
pub fn int_to_float_test() {
|
||||
intx.to_float(-1)
|
||||
|> should.equal(-1.0)
|
||||
|
||||
intx.to_float(1)
|
||||
|> should.equal(1.0)
|
||||
}
|
||||
|
||||
pub fn int_proper_divisors_test() {
|
||||
intx.proper_divisors(2)
|
||||
|> should.equal([1])
|
||||
|
||||
intx.proper_divisors(6)
|
||||
|> io.debug()
|
||||
|> should.equal([1, 2, 3])
|
||||
intx.proper_divisors(13)
|
||||
|> should.equal([1])
|
||||
|
||||
intx.proper_divisors(18)
|
||||
|> should.equal([1, 2, 3, 6, 9])
|
||||
}
|
||||
|
||||
pub fn int_divisors_test() {
|
||||
intx.divisors(2)
|
||||
|> should.equal([1, 2])
|
||||
|
||||
intx.divisors(6)
|
||||
|> should.equal([1, 2, 3, 6])
|
||||
|
||||
intx.divisors(13)
|
||||
|> should.equal([1, 13])
|
||||
|
||||
intx.divisors(18)
|
||||
|> should.equal([1, 2, 3, 6, 9, 18])
|
||||
}
|
||||
|
||||
pub fn int_is_perfect_test() {
|
||||
intx.is_perfect(6)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_perfect(28)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_perfect(496)
|
||||
|> should.equal(True)
|
||||
|
||||
intx.is_perfect(1)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_perfect(3)
|
||||
|> should.equal(False)
|
||||
|
||||
intx.is_perfect(13)
|
||||
|> should.equal(False)
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/pair
|
||||
import gleam_community/maths/list as listx
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/io
|
||||
import gleam/set
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn list_trim_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> listx.trim(0, 0)
|
||||
|> should.be_error()
|
||||
|
||||
// Trim the list to only the middle part of list
|
||||
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
||||
|> listx.trim(1, 4)
|
||||
|> should.equal(Ok([2.0, 3.0, 4.0, 5.0]))
|
||||
}
|
||||
|
||||
pub fn list_cartesian_product_test() {
|
||||
// An empty lists returns an empty list
|
||||
[]
|
||||
|> listx.cartesian_product([])
|
||||
|> should.equal([])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
[1, 2, 3]
|
||||
|> listx.cartesian_product([1, 2, 3])
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([
|
||||
#(1, 1),
|
||||
#(1, 2),
|
||||
#(1, 3),
|
||||
#(2, 1),
|
||||
#(2, 2),
|
||||
#(2, 3),
|
||||
#(3, 1),
|
||||
#(3, 2),
|
||||
#(3, 3),
|
||||
]))
|
||||
|
||||
[1.0, 10.0]
|
||||
|> listx.cartesian_product([1.0, 2.0])
|
||||
|> set.from_list()
|
||||
|> should.equal(set.from_list([
|
||||
#(1.0, 1.0),
|
||||
#(1.0, 2.0),
|
||||
#(10.0, 1.0),
|
||||
#(10.0, 2.0),
|
||||
]))
|
||||
}
|
||||
|
||||
pub fn list_permutation_test() {
|
||||
io.debug("TODO: Implement tests for 'list.permutation'.")
|
||||
// // An empty lists returns an empty list
|
||||
// []
|
||||
// |> listx.permutation([])
|
||||
// |> should.equal([[]])
|
||||
|
||||
// Test with some arbitrary inputs
|
||||
// [1, 2]
|
||||
// |> listx.permutation()
|
||||
|
||||
// should.be_error(Ok(1))
|
||||
// |> should.equal([[1, 2], [2, 1]])
|
||||
// // Test with some arbitrary inputs
|
||||
// [1, 2, 3]
|
||||
// |> listx.permutation()
|
||||
// |> should.equal([
|
||||
// [1, 2, 3],
|
||||
// [2, 1, 3],
|
||||
// [3, 1, 2],
|
||||
// [1, 3, 2],
|
||||
// [2, 3, 1],
|
||||
// [3, 2, 1],
|
||||
// ])
|
||||
}
|
220
test/gleam/gleam_community_maths_metrics.gleam
Normal file
220
test/gleam/gleam_community_maths_metrics.gleam
Normal file
|
@ -0,0 +1,220 @@
|
|||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/metrics
|
||||
import gleam_community/maths/tests
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_list_norm_test() {
|
||||
let assert Ok(tol) = elementary.power(-10.0, -6.0)
|
||||
|
||||
// An empty lists returns 0.0
|
||||
[]
|
||||
|> metrics.norm(1.0)
|
||||
|> should.equal(0.0)
|
||||
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
[1.0, 1.0, 1.0]
|
||||
|> metrics.norm(1.0)
|
||||
|> tests.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[1.0, 1.0, 1.0]
|
||||
|> metrics.norm(-1.0)
|
||||
|> tests.is_close(0.3333333333333333, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -1.0, -1.0]
|
||||
|> metrics.norm(-1.0)
|
||||
|> tests.is_close(0.3333333333333333, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -1.0, -1.0]
|
||||
|> metrics.norm(1.0)
|
||||
|> tests.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> metrics.norm(-10.0)
|
||||
|> tests.is_close(0.9999007044905545, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> metrics.norm(-100.0)
|
||||
|> tests.is_close(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
[-1.0, -2.0, -3.0]
|
||||
|> metrics.norm(2.0)
|
||||
|> tests.is_close(3.7416573867739413, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_manhatten_test() {
|
||||
let assert Ok(tol) = elementary.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
metrics.float_manhatten_distance([], [])
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
metrics.float_manhatten_distance([], [1.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Manhatten distance (p = 1)
|
||||
let assert Ok(result) =
|
||||
metrics.float_manhatten_distance([0.0, 0.0], [1.0, 2.0])
|
||||
result
|
||||
|> tests.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn int_list_manhatten_test() {
|
||||
// Empty lists returns 0
|
||||
metrics.int_manhatten_distance([], [])
|
||||
|> should.equal(Ok(0))
|
||||
|
||||
// Differing lengths returns error
|
||||
metrics.int_manhatten_distance([], [1])
|
||||
|> should.be_error()
|
||||
|
||||
let assert Ok(result) = metrics.int_manhatten_distance([0, 0], [1, 2])
|
||||
result
|
||||
|> should.equal(3)
|
||||
}
|
||||
|
||||
pub fn float_list_minkowski_test() {
|
||||
let assert Ok(tol) = elementary.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
metrics.minkowski_distance([], [], 1.0)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
metrics.minkowski_distance([], [1.0], 1.0)
|
||||
|> should.be_error()
|
||||
|
||||
// Test order < 1
|
||||
metrics.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) =
|
||||
metrics.minkowski_distance([1.0, 1.0], [1.0, 1.0], 1.0)
|
||||
result
|
||||
|> tests.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)
|
||||
result
|
||||
|> tests.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)
|
||||
result
|
||||
|> tests.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)
|
||||
result
|
||||
|> tests.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)
|
||||
result
|
||||
|> tests.is_close(2.23606797749979, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Manhatten distance (p = 1)
|
||||
let assert Ok(result) =
|
||||
metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0)
|
||||
result
|
||||
|> tests.is_close(3.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_euclidean_test() {
|
||||
let assert Ok(tol) = elementary.power(-10.0, -6.0)
|
||||
|
||||
// Empty lists returns 0.0
|
||||
metrics.euclidean_distance([], [])
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Differing lengths returns error
|
||||
metrics.euclidean_distance([], [1.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Euclidean distance (p = 2)
|
||||
let assert Ok(result) = metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0])
|
||||
result
|
||||
|> tests.is_close(2.23606797749979, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn example_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 example_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 example_variance_test() {
|
||||
// Degrees of freedom
|
||||
let ddof: Int = 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 example_standard_deviation_test() {
|
||||
// Degrees of freedom
|
||||
let ddof: Int = 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))
|
||||
}
|
758
test/gleam/gleam_community_maths_piecewise.gleam
Normal file
758
test/gleam/gleam_community_maths_piecewise.gleam
Normal file
|
@ -0,0 +1,758 @@
|
|||
import gleam_community/maths/piecewise
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/option
|
||||
import gleam/float
|
||||
import gleam/int
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_ceiling_test() {
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(3))
|
||||
|> should.equal(Ok(12.066))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(2))
|
||||
|> should.equal(Ok(12.07))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(1))
|
||||
|> should.equal(Ok(12.1))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(0))
|
||||
|> should.equal(Ok(13.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(-1))
|
||||
|> should.equal(Ok(20.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(-2))
|
||||
|> should.equal(Ok(100.0))
|
||||
|
||||
// Round 3. digit BEFORE decimal point
|
||||
piecewise.ceiling(12.0654, option.Some(-3))
|
||||
|> should.equal(Ok(1000.0))
|
||||
}
|
||||
|
||||
pub fn float_floor_test() {
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.floor(12.0654, option.Some(3))
|
||||
|> should.equal(Ok(12.065))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.floor(12.0654, option.Some(2))
|
||||
|> should.equal(Ok(12.06))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.floor(12.0654, option.Some(1))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.floor(12.0654, option.Some(0))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.floor(12.0654, option.Some(-1))
|
||||
|> should.equal(Ok(10.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.floor(12.0654, option.Some(-2))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.floor(12.0654, option.Some(-3))
|
||||
|> should.equal(Ok(0.0))
|
||||
}
|
||||
|
||||
pub fn float_truncate_test() {
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.truncate(12.0654, option.Some(3))
|
||||
|> should.equal(Ok(12.065))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.truncate(12.0654, option.Some(2))
|
||||
|> should.equal(Ok(12.06))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.truncate(12.0654, option.Some(1))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.truncate(12.0654, option.Some(0))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.truncate(12.0654, option.Some(-1))
|
||||
|> should.equal(Ok(10.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.truncate(12.0654, option.Some(-2))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.truncate(12.0654, option.Some(-3))
|
||||
|> should.equal(Ok(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(Ok(2.0))
|
||||
|
||||
piecewise.round(1.75, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
piecewise.round(2.0, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
piecewise.round(3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
piecewise.round(4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-3.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(-4.0))
|
||||
|
||||
piecewise.round(-4.5, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(-4.0))
|
||||
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(12.065))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(12.07))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(12.1))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(10.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Round 3. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundNearest))
|
||||
|> should.equal(Ok(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(Ok(1.0))
|
||||
|
||||
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(0.46))
|
||||
|
||||
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(0.51))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-0.0))
|
||||
|
||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-0.0))
|
||||
|
||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-0.4))
|
||||
|
||||
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-0.5))
|
||||
|
||||
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-0.45))
|
||||
|
||||
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundUp))
|
||||
|> should.equal(Ok(-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(Ok(0.0))
|
||||
|
||||
piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(0.4))
|
||||
|
||||
piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(0.45))
|
||||
|
||||
piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-0.5))
|
||||
|
||||
piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-0.5))
|
||||
|
||||
piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-0.46))
|
||||
|
||||
piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundDown))
|
||||
|> should.equal(Ok(-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(Ok(0.0))
|
||||
|
||||
piecewise.round(0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.4))
|
||||
|
||||
piecewise.round(0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
piecewise.round(0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.45))
|
||||
|
||||
piecewise.round(0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
piecewise.round(-0.75, option.Some(0), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(-0.4))
|
||||
|
||||
piecewise.round(-0.57, option.Some(1), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(-0.5))
|
||||
|
||||
piecewise.round(-0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(-0.45))
|
||||
|
||||
piecewise.round(-0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
|
||||
|> should.equal(Ok(-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(Ok(1.0))
|
||||
|
||||
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(3.0))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(-3.0))
|
||||
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(12.065))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(12.07))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(12.1))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesAway))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.round(
|
||||
12.0654,
|
||||
option.Some(-1),
|
||||
option.Some(piecewise.RoundTiesAway),
|
||||
)
|
||||
|> should.equal(Ok(10.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.round(
|
||||
12.0654,
|
||||
option.Some(-2),
|
||||
option.Some(piecewise.RoundTiesAway),
|
||||
)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.round(
|
||||
12.0654,
|
||||
option.Some(-3),
|
||||
option.Some(piecewise.RoundTiesAway),
|
||||
)
|
||||
|> should.equal(Ok(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(Ok(1.0))
|
||||
|
||||
piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(3.0))
|
||||
|
||||
// Try with negative values
|
||||
piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
// Round 3. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(12.065))
|
||||
|
||||
// Round 2. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(12.07))
|
||||
|
||||
// Round 1. digit AFTER decimal point
|
||||
piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(12.1))
|
||||
|
||||
// Round 0. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(12.0))
|
||||
|
||||
// Round 1. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(10.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Round 2. digit BEFORE decimal point
|
||||
piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundTiesUp))
|
||||
|> should.equal(Ok(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(Ok(12.0))
|
||||
|
||||
// The default rounding mode is piecewise.RoundNearest if None is provided
|
||||
piecewise.round(12.0654, option.None, option.None)
|
||||
|> should.equal(Ok(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)))
|
||||
}
|
234
test/gleam/gleam_community_maths_sequences.gleam
Normal file
234
test/gleam/gleam_community_maths_sequences.gleam
Normal file
|
@ -0,0 +1,234 @@
|
|||
import gleam_community/maths/elementary
|
||||
import gleam_community/maths/sequences
|
||||
import gleam_community/maths/tests
|
||||
import gleam/list
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [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) =
|
||||
tests.all_close(linspace, [-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) =
|
||||
tests.all_close(linspace, [-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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [-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) =
|
||||
tests.all_close(logspace, [-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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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) =
|
||||
tests.all_close(logspace, [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)
|
||||
|> should.equal([1.0, 2.0, 3.0, 4.0])
|
||||
|
||||
sequences.arange(1.0, 5.0, 0.5)
|
||||
|> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
|
||||
|
||||
sequences.arange(1.0, 2.0, 0.25)
|
||||
|> 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)
|
||||
|> should.equal([])
|
||||
|
||||
// Reverse negative step
|
||||
sequences.arange(5.0, 1.0, -1.0)
|
||||
|> should.equal([5.0, 4.0, 3.0, 2.0])
|
||||
|
||||
// Positive start, negative stop, step
|
||||
sequences.arange(5.0, -1.0, -1.0)
|
||||
|> 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)
|
||||
|> should.equal([])
|
||||
|
||||
// Negative start, stop, positive step
|
||||
sequences.arange(-5.0, -1.0, 1.0)
|
||||
|> should.equal([-5.0, -4.0, -3.0, -2.0])
|
||||
}
|
57
test/gleam/gleam_community_maths_special.gleam
Normal file
57
test/gleam/gleam_community_maths_special.gleam
Normal file
|
@ -0,0 +1,57 @@
|
|||
import gleam_community/maths/special
|
||||
import gleam_community/maths/tests
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/result
|
||||
import gleam/io
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_beta_function_test() {
|
||||
io.debug("")
|
||||
io.debug("TODO: Implement tests for 'float.beta'.")
|
||||
}
|
||||
|
||||
pub fn float_error_function_test() {
|
||||
io.debug("")
|
||||
io.debug("TODO: Implement tests for 'float.erf'.")
|
||||
}
|
||||
|
||||
pub fn float_gamma_function_test() {
|
||||
io.debug("")
|
||||
io.debug("TODO: Implement tests for 'float.gamma'.")
|
||||
}
|
||||
|
||||
pub fn float_incomplete_gamma_function_test() {
|
||||
// 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)
|
||||
|> tests.is_close(0.0, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
special.incomplete_gamma(1.0, 2.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> tests.is_close(0.864664716763387308106, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
special.incomplete_gamma(2.0, 3.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> tests.is_close(0.8008517265285442280826, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
special.incomplete_gamma(3.0, 4.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> tests.is_close(1.523793388892911312363, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
}
|
143
test/gleam/gleam_community_maths_tests.gleam
Normal file
143
test/gleam/gleam_community_maths_tests.gleam
Normal file
|
@ -0,0 +1,143 @@
|
|||
import gleam_community/maths/tests
|
||||
import gleam/list
|
||||
import gleeunit/should
|
||||
import gleeunit
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_is_close_test() {
|
||||
let val: Float = 99.0
|
||||
let ref_val: Float = 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: Float = 0.01
|
||||
let atol: Float = 0.1
|
||||
tests.is_close(val, ref_val, rtol, atol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_list_all_close_test() {
|
||||
let val: Float = 99.0
|
||||
let ref_val: Float = 100.0
|
||||
let xarr: List(Float) = list.repeat(val, 42)
|
||||
let yarr: List(Float) = 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: Float = 0.01
|
||||
let atol: Float = 0.1
|
||||
tests.all_close(xarr, yarr, rtol, atol)
|
||||
|> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
|
||||
case zarr {
|
||||
Ok(arr) ->
|
||||
arr
|
||||
|> list.all(fn(a: Bool) -> Bool { a })
|
||||
|> Ok
|
||||
_ ->
|
||||
Nil
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|> should.equal(Ok(True))
|
||||
}
|
||||
|
||||
pub fn float_is_fractional_test() {
|
||||
tests.is_fractional(1.5)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_fractional(0.5)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_fractional(0.3333)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_fractional(0.9999)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_fractional(1.0)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_fractional(999.0)
|
||||
|> should.equal(False)
|
||||
}
|
||||
|
||||
pub fn int_is_power_test() {
|
||||
tests.is_power(10, 10)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_power(11, 10)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_power(4, 2)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_power(5, 2)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_power(27, 3)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_power(28, 3)
|
||||
|> should.equal(False)
|
||||
}
|
||||
|
||||
pub fn int_is_even_test() {
|
||||
tests.is_even(0)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_even(2)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_even(12)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_even(5)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_even(-3)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_even(-4)
|
||||
|> should.equal(True)
|
||||
}
|
||||
|
||||
pub fn int_is_odd_test() {
|
||||
tests.is_odd(0)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_odd(3)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_odd(13)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_odd(4)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_odd(-3)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_odd(-4)
|
||||
|> should.equal(False)
|
||||
}
|
||||
|
||||
pub fn int_is_perfect_test() {
|
||||
tests.is_perfect(6)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_perfect(28)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_perfect(496)
|
||||
|> should.equal(True)
|
||||
|
||||
tests.is_perfect(1)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_perfect(3)
|
||||
|> should.equal(False)
|
||||
|
||||
tests.is_perfect(13)
|
||||
|> should.equal(False)
|
||||
}
|
|
@ -1,9 +1,3 @@
|
|||
if erlang {
|
||||
pub external fn main() -> Nil =
|
||||
"gleam_community_maths_test_ffi" "main"
|
||||
}
|
||||
|
||||
if javascript {
|
||||
pub external fn main() -> Nil =
|
||||
"./gleam_community_maths_test_ffi.mjs" "main"
|
||||
}
|
||||
@external(erlang, "gleam_community_maths_test_ffi", "main")
|
||||
@external(javascript, "./gleam_community_maths_test_ffi.mjs", "main")
|
||||
pub fn main() -> Nil
|
||||
|
|
|
@ -1,35 +1,103 @@
|
|||
import { opendir } from "fs/promises";
|
||||
// This file is a verbatim copy of gleeunit 0.10.0's <https://github.com/lpil/gleeunit/blob/main/src/gleeunit_ffi.mjs>
|
||||
|
||||
const dir = "build/dev/javascript/gleam_community_maths/gleam/";
|
||||
async function* gleamFiles(directory) {
|
||||
for (let entry of await read_dir(directory)) {
|
||||
let path = join_path(directory, entry);
|
||||
if (path.endsWith(".gleam")) {
|
||||
yield path;
|
||||
} else {
|
||||
try {
|
||||
yield* gleamFiles(path);
|
||||
} catch (error) {
|
||||
// Could not read directory, assume it's a file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readRootPackageName() {
|
||||
let toml = await read_file("gleam.toml", "utf-8");
|
||||
for (let line of toml.split("\n")) {
|
||||
let matches = line.match(/\s*name\s*=\s*"([a-z][a-z0-9_]*)"/); // Match regexp in compiler-cli/src/new.rs in validate_name()
|
||||
if (matches) return matches[1];
|
||||
}
|
||||
throw new Error("Could not determine package name from gleam.toml");
|
||||
}
|
||||
|
||||
export async function main() {
|
||||
console.log("Running tests...");
|
||||
|
||||
let passes = 0;
|
||||
let failures = 0;
|
||||
|
||||
for await (let entry of await opendir(dir)) {
|
||||
if (!entry.name.endsWith("_test.mjs")) continue;
|
||||
let module = await import("./gleam/" + entry.name);
|
||||
let packageName = await readRootPackageName();
|
||||
let dist = `../${packageName}/`;
|
||||
|
||||
for await (let path of await gleamFiles("test")) {
|
||||
let js_path = path.slice("test/".length).replace(".gleam", ".mjs");
|
||||
let module = await import(join_path(dist, js_path));
|
||||
for (let fnName of Object.keys(module)) {
|
||||
if (!fnName.endsWith("_test")) continue;
|
||||
try {
|
||||
module[fnName]();
|
||||
process.stdout.write(`\u001b[32m.\u001b[0m`);
|
||||
await module[fnName]();
|
||||
write(`\u001b[32m.\u001b[0m`);
|
||||
passes++;
|
||||
} catch (error) {
|
||||
let moduleName = "\ngleam/" + entry.name.slice(0, -3);
|
||||
process.stdout.write(`\n❌ ${moduleName}.${fnName}: ${error}\n`);
|
||||
let moduleName = "\n" + js_path.slice(0, -4);
|
||||
let line = error.line ? `:${error.line}` : "";
|
||||
write(`\n❌ ${moduleName}.${fnName}${line}: ${error}\n`);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`
|
||||
|
||||
${passes + failures} tests
|
||||
${passes} passes
|
||||
${failures} failures`);
|
||||
process.exit(failures ? 1 : 0);
|
||||
${passes + failures} tests, ${failures} failures`);
|
||||
exit(failures ? 1 : 0);
|
||||
}
|
||||
|
||||
export function crash(message) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function write(message) {
|
||||
if (globalThis.Deno) {
|
||||
Deno.stdout.writeSync(new TextEncoder().encode(message));
|
||||
} else {
|
||||
process.stdout.write(message);
|
||||
}
|
||||
}
|
||||
|
||||
function exit(code) {
|
||||
if (globalThis.Deno) {
|
||||
Deno.exit(code);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
async function read_dir(path) {
|
||||
if (globalThis.Deno) {
|
||||
let items = [];
|
||||
for await (let item of Deno.readDir(path, { withFileTypes: true })) {
|
||||
items.push(item.name);
|
||||
}
|
||||
return items;
|
||||
} else {
|
||||
let { readdir } = await import("fs/promises");
|
||||
return readdir(path);
|
||||
}
|
||||
}
|
||||
|
||||
function join_path(a, b) {
|
||||
if (a.endsWith("/")) return a + b;
|
||||
return a + "/" + b;
|
||||
}
|
||||
|
||||
async function read_file(path) {
|
||||
if (globalThis.Deno) {
|
||||
return Deno.readTextFile(path);
|
||||
} else {
|
||||
let { readFile } = await import("fs/promises");
|
||||
let contents = await readFile(path);
|
||||
return contents.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue