Merge pull request #5 from NicklasXYZ/main

Fix various issues and todo's
This commit is contained in:
Nicklas Sindlev Andersen 2023-09-17 14:18:34 +02:00 committed by GitHub
commit d2241e4240
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 7645 additions and 7404 deletions

View file

@ -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 }}

View file

@ -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

View file

@ -10,27 +10,40 @@ The library supports both targets: Erlang and JavaScript.
## Quickstart
```gleam
import gleam_community/maths/float as floatx
import gleam_community/maths/int as intx
import gleam_community/maths/float_list
import gleam_community/maths/int_list
import gleam_community/maths/elementary
import gleam_community/maths/arithmetics
import gleam_community/maths/piecewise
import gleam_community/maths/tests
import gleam/float
pub fn main() {
// Evaluate the sine function
floatx.sin(floatx.pi())
elementary.sin(floatx.pi())
// Returns Float: 0.0
// Find the greatest common divisor
intx.gcd(54, 24)
// Returns Int: 6
arithmetics.gcd(54, 24)
// Returns Int: 6
// Find the minimum and maximum of a list
float_list.extrema([10.0, 3.0, 50.0, 20.0, 3.0])
piecewise.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
// Returns Tuple: Ok(#(3.0, 50.0))
// Find the list indices of the smallest value
int_list.arg_minimum([10, 3, 50, 20, 3])
// Returns List: Ok([1, 4])
piecewise.arg_minimum([10, 3, 50, 20, 3], float.compare)
// Returns List: Ok([1, 4])
// Determine if a number is fractional
tests.is_fractional(0.3333)
// Returns Bool: True
// Determine if 28 is a power of 3
tests.is_power(28, 3)
// Returns Bool: False
// Generate all k = 1 combinations of [1, 2]
combinatorics.list_combination([1, 2], 1)
// Returns List: Ok([[1], [2]])
}
```

View file

@ -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"

View file

@ -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" }

View 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>
////
//// ---
////
//// 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/int
import gleam/list
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// The 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/arithmetics
///
/// pub fn example() {
/// arithmetics.lcm(1, 1)
/// |> should.equal(1)
///
/// arithmetics.lcm(100, 10)
/// |> should.equal(10)
///
/// arithmetics.gcd(-36, -17)
/// |> should.equal(1)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn gcd(x: Int, y: Int) -> Int {
let absx: 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/arithmetics
///
/// pub fn example() {
/// arithmetics.lcm(1, 1)
/// |> should.equal(1)
///
/// arithmetics.lcm(100, 10)
/// |> should.equal(100)
///
/// arithmetics.lcm(-36, -17)
/// |> should.equal(612)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn lcm(x: Int, y: Int) -> Int {
let absx: 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/arithmetics
///
/// pub fn example() {
/// arithmetics.divisors(4)
/// |> should.equal([1, 2, 4])
///
/// arithmetics.divisors(6)
/// |> should.equal([1, 2, 3, 6])
///
/// arithmetics.proper_divisors(13)
/// |> should.equal([1, 13])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn divisors(n: Int) -> List(Int) {
find_divisors(n)
}
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/arithmetics
///
/// pub fn example() {
/// arithmetics.proper_divisors(4)
/// |> should.equal([1, 2])
///
/// arithmetics.proper_divisors(6)
/// |> should.equal([1, 2, 3])
///
/// arithmetics.proper_divisors(13)
/// |> should.equal([1])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn proper_divisors(n: Int) -> List(Int) {
let divisors: 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 \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> 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)
/// }
/// </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 })
}
}
/// <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 \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns 0
/// []
/// |> arithmetics.int_sum()
/// |> should.equal(0)
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> arithmetics.int_sum()
/// |> should.equal(6)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn int_sum(arr: List(Int)) -> Int {
case arr {
[] -> 0
_ ->
arr
|> list.fold(0, fn(acc: 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>
///
/// 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 \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns 0.0
/// []
/// |> arithmetics.float_product()
/// |> should.equal(0.0)
///
/// // Valid input returns a result
/// [1.0, 2.0, 3.0]
/// |> arithmetics.float_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>
/// </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 \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns 0
/// []
/// |> arithmetics.int_product()
/// |> should.equal(0)
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> arithmetics.int_product()
/// |> should.equal(6)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn int_product(arr: List(Int)) -> Int {
case arr {
[] -> 1
_ ->
arr
|> list.fold(1, fn(acc: 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>
///
/// 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 \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// []
/// |> arithmetics.float_cumulative_sum()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1.0, 2.0, 3.0]
/// |> arithmetics.float_cumulative_sum()
/// |> should.equal([1.0, 3.0, 6.0])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
case arr {
[] -> []
_ ->
arr
|> list.scan(0.0, fn(acc: Float, a: Float) -> Float { a +. acc })
}
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// 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 \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// []
/// |> arithmetics.int_cumulative_sum()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> arithmetics.int_cumulative_sum()
/// |> should.equal([1, 3, 6])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ ->
arr
|> list.scan(0, fn(acc: 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>
///
/// 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 \in \mathbb{R}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the sum of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> arithmetics.float_cumulative_product()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1.0, 2.0, 3.0]
/// |> arithmetics.float_cumulative_product()
/// |> should.equal([1.0, 2.0, 6.0])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn float_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>
/// </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 \in \mathbb{Z}$$ is the value in the input list indexed by $$i$$.
/// The value $$v_j$$ is thus the product of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/arithmetics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> arithmetics.int_cumulative_product()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> arithmetics.int_cumulative_product()
/// |> should.equal([1, 2, 6])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn int_cumulative_product(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ ->
arr
|> list.scan(1, fn(acc: Int, a: Int) -> Int { a * acc })
}
}

View file

@ -0,0 +1,430 @@
////<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/combinatorics
///
/// pub fn example() {
/// // 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))
/// }
/// </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/combinatorics
///
/// pub fn example() {
/// // 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))
/// }
/// </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/combinatorics
///
/// pub fn example() {
/// // 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))
/// }
/// </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/set
/// import gleam_community/maths/combinatorics
///
/// pub fn example () {
/// 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]]))
/// }
/// </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 of a given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam/set
/// import gleam_community/maths/combinatorics
///
/// pub fn example () {
/// [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],
/// ]))
/// }
/// </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/combinatorics
///
/// pub fn example () {
/// []
/// |> combinatorics.cartesian_product([])
/// |> should.equal([])
///
/// [1.0, 10.0]
/// |> combinatorics.cartesian_product([1.0, 2.0])
/// |> should.equal([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)])
/// }
/// </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()
}

View file

@ -0,0 +1,183 @@
////<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 quantities.
////
//// * **Misc. functions**
//// * [`float_to_int`](#float_to_int)
//// * [`int_to_float`](#int_to_float)
//// * [`degrees_to_radians`](#degrees_to_radians)
//// * [`radians_to_degrees`](#radians_to_degrees)
import gleam/int
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// A function that produces a number of type `Float` from an `Int`.
///
/// Note: The function is equivalent to the `int.to_float` function in the Gleam stdlib.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/conversion
///
/// pub fn example() {
/// conversion.int_to_float(-1)
/// |> should.equal(-1.0)
///
/// conversion.int_to_float(1)
/// |> should.equal(1.0)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn int_to_float(x: Int) -> Float {
int.to_float(x)
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// The function returns the integral part of a given floating point value.
/// That is, everything after the decimal point of a given floating point value is discarded and only the integer value before the decimal point is returned.
///
/// <details>
/// <summary>Example</summary>
///
/// import gleeunit/should
/// import gleam/option
/// import gleam_community/maths/conversion
/// import gleam_community/maths/piecewise
///
/// pub fn example() {
/// conversion.float_to_int(12.0654)
/// |> should.equal(12)
///
/// // Note: Making the following function call is equivalent
/// // but instead of returning a value of type 'Int' a value
/// // of type 'Float' is returned.
/// piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundToZero))
/// |> should.equal(Ok(12.0))
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn float_to_int(x: Float) -> Int {
do_to_int(x)
}
@external(erlang, "erlang", "trunc")
@external(javascript, "../../maths.mjs", "truncate")
fn do_to_int(a: Float) -> Int
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// Convert a value in degrees to a value measured in radians.
/// That is, $$1 \text{ degrees } = \frac{\pi}{180} \text{ radians }$$.
///
/// <details>
/// <summary>Example</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/conversion
/// import gleam_community/maths/elementary
///
/// pub fn example() {
/// conversion.degrees_to_radians(360.)
/// |> should.equal(2. *. elementary.pi())
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn degrees_to_radians(x: Float) -> Float {
x *. do_pi() /. 180.0
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// Convert a value in degrees to a value measured in radians.
/// That is, $$1 \text{ radians } = \frac{180}{\pi} \text{ degrees }$$.
///
/// <details>
/// <summary>Example</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/conversion
/// import gleam_community/maths/elementary
///
/// pub fn example() {
/// conversion.radians_to_degrees(0.0)
/// |> should.equal(0.0)
///
/// conversion.radians_to_degrees(2. *. elementary.pi())
/// |> should.equal(360.)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn radians_to_degrees(x: Float) -> Float {
x *. 180.0 /. do_pi()
}
@external(erlang, "math", "pi")
@external(javascript, "../../maths.mjs", "pi")
fn do_pi() -> Float

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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)
}

View file

@ -1,589 +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 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)
import gleam/list
import gleam/int
import gleam/float
import gleam/pair
import gleam_community/maths/int as intx
/// <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 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
}
}
/// <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/int_list
///
/// pub fn example () {
/// // 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)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn 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>
///
/// 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/int_list
///
/// pub fn example () {
/// // An empty list returns 0
/// []
/// |> int_list.product()
/// |> should.equal(0)
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> int_list.product()
/// |> should.equal(6)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn product(arr: List(Int)) -> Int {
case arr {
[] -> 1
_ ->
arr
|> list.fold(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>
///
/// 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/int_list
///
/// pub fn example () {
/// []
/// |> int_list.cumulative_sum()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> int_list.cumulative_sum()
/// |> should.equal([1, 3, 6])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn cumulative_sum(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ ->
arr
|> list.scan(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>
///
/// 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 product of the $$1$$ to $$j$$ first elements in the given list.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/int_list
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> int_list.cumulative_product()
/// |> should.equal([])
///
/// // Valid input returns a result
/// [1, 2, 3]
/// |> int_list.cumulative_product()
/// |> should.equal([1, 2, 6])
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn cumulative_product(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ ->
arr
|> 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
}
}
}

View file

@ -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()
}

View file

@ -0,0 +1,560 @@
////<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 other types of metrics.
////
//// * **Distances**
//// * [`norm`](#norm)
//// * [`manhatten_distance`](#float_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/elementary
/// import gleam_community/maths/metrics
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0)
///
/// [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()
/// }
/// </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/elementary
/// import gleam_community/maths/metrics
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// 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.manhatten_distance([], [1.0])
/// |> should.be_error()
///
/// let assert Ok(result) = metrics.manhatten_distance([0.0, 0.0], [1.0, 2.0])
/// result
/// |> tests.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 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 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/elementary
/// import gleam_community/maths/metrics
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// 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()
///
/// 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()
/// }
/// </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/elementary
/// import gleam_community/maths/metrics
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// 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()
///
/// 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()
/// }
/// </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_community/maths/metrics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> metrics.mean()
/// |> should.be_error()
///
/// // Valid input returns a result
/// [1., 2., 3.]
/// |> metrics.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_community/maths/metrics
///
/// pub fn example () {
/// // An empty list returns an error
/// []
/// |> metrics.median()
/// |> should.be_error()
///
/// // Valid input returns a result
/// [1., 2., 3.]
/// |> metrics.median()
/// |> should.equal(Ok(2.))
///
/// [1., 2., 3., 4.]
/// |> metrics.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_community/maths/metrics
///
/// pub fn example () {
/// // 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., 2., 3.]
/// |> metrics.variance(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_community/maths/metrics
///
/// pub fn example () {
/// // Degrees of freedom
/// let ddof: Int = 1
///
/// // An empty list returns an error
/// []
/// |> metrics.standard_deviationddof)
/// |> should.be_error()
///
/// // Valid input returns a result
/// [1., 2., 3.]
/// |> metrics.standard_deviation(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
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,302 @@
////<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/sequences
///
/// pub fn example () {
/// sequences.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
/// sequences.arange(5.0, 1.0, 1.0)
/// |> should.equal([])
///
/// // Points returned since
/// // start smaller than stop but negative step
/// sequences.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/elementary
/// import gleam_community/maths/sequences
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0)
/// let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True)
/// let assert Ok(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()
///
/// // A negative number of points (-5) does not work
/// sequences.linear_space(10.0, 50.0, -5, True)
/// |> should.be_error()
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn linear_space(
start: Float,
stop: Float,
num: Int,
endpoint: Bool,
) -> Result(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/elementary
/// import gleam_community/maths/sequences
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0)
/// let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
/// let assert Ok(result) =
/// tests.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
/// sequences.logarithmic_space(1.0, 3.0, -3, False, 10.0)
/// |> should.be_error()
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn logarithmic_space(
start: Float,
stop: Float,
num: Int,
endpoint: Bool,
base: Float,
) -> Result(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/elementary
/// import gleam_community/maths/sequences
/// import gleam_community/maths/tests
///
/// pub fn example () {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0)
/// let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
/// let assert Ok(result) =
/// tests.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)
/// sequences.geometric_space(0.0, 1000.0, 3, False)
/// |> should.be_error()
///
/// sequences.geometric_space(-1000.0, 0.0, 3, False)
/// |> should.be_error()
///
/// // A negative number of points (-3) does not work
/// sequences.geometric_space(10.0, 1000.0, -3, False)
/// |> should.be_error()
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn geometric_space(
start: Float,
stop: Float,
num: Int,
endpoint: Bool,
) -> Result(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
}
}
}

View 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)
}
}
}

View file

@ -0,0 +1,363 @@
////<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/tests
///
/// 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/tests
///
/// 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
/// 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))
/// }
/// </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 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/tests
///
/// pub fn example () {
/// tests.is_fractional(0.3333)
/// |> should.equal(True)
///
/// tests.is_fractional(1.0)
/// |> should.equal(False)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn is_fractional(x: Float) -> Bool {
do_ceiling(x) -. x >. 0.0
}
@external(erlang, "math", "ceil")
@external(javascript, "../../maths.mjs", "ceiling")
fn do_ceiling(a: Float) -> Float
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a power of another integer value $$y \in \mathbb{Z}$$.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/tests
///
/// pub fn example() {
/// // Check if 4 is a power of 2 (it is)
/// tests.is_power(4, 2)
/// |> should.equal(True)
///
/// // Check if 5 is a power of 2 (it is not)
/// tests.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/tests
///
/// pub fn example() {
/// tests.is_perfect(6)
/// |> should.equal(True)
///
/// tests.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/tests
///
/// pub fn example() {
/// tests.is_even(-3)
/// |> should.equal(False)
///
/// tests.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/tests
///
/// pub fn example() {
/// tests.is_odd(-3)
/// |> should.equal(True)
///
/// tests.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
}

View 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])
}

View 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]]))
}

View 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)
}

View 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()
}

View file

@ -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

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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],
// ])
}

View file

@ -0,0 +1,219 @@
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.manhatten_distance([], [])
|> should.equal(Ok(0.0))
// Differing lengths returns error
metrics.manhatten_distance([], [1.0])
|> should.be_error()
// Manhatten distance (p = 1)
let assert Ok(result) = metrics.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))
}

View 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)))
}

View 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])
}

View file

@ -0,0 +1,119 @@
import gleam_community/maths/elementary
import gleam_community/maths/special
import gleam_community/maths/tests
import gleeunit
import gleeunit/should
import gleam/result
pub fn main() {
gleeunit.main()
}
pub fn float_beta_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Valid input returns a result
special.beta(-0.5, 0.5)
|> tests.is_close(0.0, 0.0, tol)
|> should.be_true()
special.beta(0.5, 0.5)
|> tests.is_close(3.1415926535897927, 0.0, tol)
|> should.be_true()
special.beta(0.5, -0.5)
|> tests.is_close(0.0, 0.0, tol)
|> should.be_true()
special.beta(5.0, 5.0)
|> tests.is_close(0.0015873015873015873, 0.0, tol)
|> should.be_true()
}
pub fn float_error_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Valid input returns a result
special.erf(-0.5)
|> tests.is_close(-0.5204998778130465, 0.0, tol)
|> should.be_true()
special.erf(0.5)
|> tests.is_close(0.5204998778130465, 0.0, tol)
|> should.be_true()
special.erf(1.0)
|> tests.is_close(0.8427007929497148, 0.0, tol)
|> should.be_true()
special.erf(2.0)
|> tests.is_close(0.9953222650189527, 0.0, tol)
|> should.be_true()
special.erf(10.0)
|> tests.is_close(1.0, 0.0, tol)
|> should.be_true()
}
pub fn float_gamma_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Valid input returns a result
special.gamma(-0.5)
|> tests.is_close(-3.5449077018110318, 0.0, tol)
|> should.be_true()
special.gamma(0.5)
|> tests.is_close(1.7724538509055159, 0.0, tol)
|> should.be_true()
special.gamma(1.0)
|> tests.is_close(1.0, 0.0, tol)
|> should.be_true()
special.gamma(2.0)
|> tests.is_close(1.0, 0.0, tol)
|> should.be_true()
special.gamma(3.0)
|> tests.is_close(2.0, 0.0, tol)
|> should.be_true()
special.gamma(10.0)
|> tests.is_close(362_880.0, 0.0, tol)
|> should.be_true()
}
pub fn float_incomplete_gamma_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
// Invalid input gives an error
// 1st arg is invalid
special.incomplete_gamma(-1.0, 1.0)
|> should.be_error()
// 2nd arg is invalid
special.incomplete_gamma(1.0, -1.0)
|> should.be_error()
// Valid input returns a result
special.incomplete_gamma(1.0, 0.0)
|> result.unwrap(-999.0)
|> tests.is_close(0.0, 0.0, tol)
|> should.be_true()
special.incomplete_gamma(1.0, 2.0)
|> result.unwrap(-999.0)
|> tests.is_close(0.864664716763387308106, 0.0, tol)
|> should.be_true()
special.incomplete_gamma(2.0, 3.0)
|> result.unwrap(-999.0)
|> tests.is_close(0.8008517265285442280826, 0.0, tol)
|> should.be_true()
special.incomplete_gamma(3.0, 4.0)
|> result.unwrap(-999.0)
|> tests.is_close(1.523793388892911312363, 0.0, tol)
|> should.be_true()
}

View 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)
}

View file

@ -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

View file

@ -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();
}
}