diff options
author | J.J <thechairman@thechairman.info> | 2024-05-30 21:50:02 -0400 |
---|---|---|
committer | J.J <thechairman@thechairman.info> | 2024-05-30 21:50:02 -0400 |
commit | 612fd986ab1e00b6d34dc1937136250e08e89325 (patch) | |
tree | a3c93952040c6afdf348b5831619a45db7ba0a2e /aoc2023/build/packages/gleam_community_maths/src | |
parent | 231c2b688d1e6cf0846d46e883da30e042a9c6cf (diff) | |
download | gleam_aoc-612fd986ab1e00b6d34dc1937136250e08e89325.tar.gz gleam_aoc-612fd986ab1e00b6d34dc1937136250e08e89325.zip |
cleanup
Diffstat (limited to 'aoc2023/build/packages/gleam_community_maths/src')
20 files changed, 7260 insertions, 0 deletions
diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/arithmetics.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/arithmetics.gleam new file mode 100644 index 0000000..3e0f63a --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/arithmetics.gleam @@ -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: false}, +//// // {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`](#float_cumulative_sum) +//// * [`int_cumulative_sum`](#int_cumulative_sum) +//// * [`float_cumulative_product`](#float_cumulative_product) +//// * [`int_cumulative_product`](#int_cumulative_product) +//// + +import gleam/int +import gleam/list +import gleam_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) +} + +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) +} + +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 }) + } +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/combinatorics.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/combinatorics.gleam new file mode 100644 index 0000000..ee771a1 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/combinatorics.gleam @@ -0,0 +1,432 @@ +////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> +////<script> +//// document.addEventListener("DOMContentLoaded", function() { +//// renderMathInElement(document.body, { +//// // customised options +//// // • auto-render specific keys, e.g.: +//// delimiters: [ +//// {left: '$$', right: '$$', display: false}, +//// // {left: '$', right: '$', display: 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. +/// +/// Repeated elements are treated as distinct for the +/// purpose of permutations, so two identical elements +/// for example will appear "both ways round". This +/// means lists with repeated elements return the same +/// number of permutations as ones without. +/// +/// N.B. The output of this function is a list of size +/// factorial in the size of the input list. Caution is +/// advised on input lists longer than ~11 elements, which +/// may cause the VM to use unholy amounts of memory for +/// the output. +/// +/// <details> +/// <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], +/// ])) +/// +/// [1.0, 1.0] +/// |> combinatorics.list_permutation() +/// |> should.equal([[1.0, 1.0], [1.0, 1.0]]) +/// } +/// </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 { + [] -> [[]] + _ -> { + use x <- list.flat_map(arr) + // `x` is drawn from the list `arr` above, + // so Ok(...) can be safely asserted as the result of `list.pop` below + let assert Ok(#(_, remaining)) = list.pop(arr, fn(y) { x == y }) + list.map(list_permutation(remaining), fn(perm) { [x, ..perm] }) + } + } +} + +/// <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() +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/conversion.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/conversion.gleam new file mode 100644 index 0000000..017aabd --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/conversion.gleam @@ -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: false}, +//// // {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 diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/elementary.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/elementary.gleam new file mode 100644 index 0000000..1b518a4 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/elementary.gleam @@ -0,0 +1,1256 @@ +////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> +////<script> +//// document.addEventListener("DOMContentLoaded", function() { +//// renderMathInElement(document.body, { +//// // customised options +//// // • auto-render specific keys, e.g.: +//// delimiters: [ +//// {left: '$$', right: '$$', display: false}, +//// // {left: '$', right: '$', display: false}, +//// // {left: '\\(', right: '\\)', display: false}, +//// {left: '\\[', right: '\\]', display: true} +//// ], +//// // • rendering keys, e.g.: +//// throwOnError : false +//// }); +//// }); +////</script> +////<style> +//// .katex { font-size: 1.1em; } +////</style> +//// +//// --- +//// +//// Elementary: A module containing a comprehensive set of foundational mathematical functions and constants. +//// +//// * **Trigonometric and hyperbolic functions** +//// * [`acos`](#acos) +//// * [`acosh`](#acosh) +//// * [`asin`](#asin) +//// * [`asinh`](#asinh) +//// * [`atan`](#atan) +//// * [`atan2`](#atan2) +//// * [`atanh`](#atanh) +//// * [`cos`](#cos) +//// * [`cosh`](#cosh) +//// * [`sin`](#sin) +//// * [`sinh`](#sinh) +//// * [`tan`](#tan) +//// * [`tanh`](#tanh) +//// * **Powers, logs and roots** +//// * [`exponential`](#exponential) +//// * [`natural_logarithm`](#natural_logarithm) +//// * [`logarithm`](#logarithm) +//// * [`logarithm_2`](#logarithm_2) +//// * [`logarithm_10`](#logarithm_10) +//// * [`power`](#power) +//// * [`square_root`](#square_root) +//// * [`cube_root`](#cube_root) +//// * [`nth_root`](#nth_root) +//// * **Mathematical constants** +//// * [`pi`](#pi) +//// * [`tau`](#tau) +//// * [`e`](#e) +//// + +import gleam/int +import gleam/option + +/// <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 inverse cosine function: +/// +/// \\[ +/// \forall x \in \[-1, 1\], \\; \cos^{-1}{(x)} = y \in \[0, \pi \] +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\[-1, 1\]$$ as input and returns a +/// numeric value $$y$$ that lies in the range $$\[0, \pi \]$$ (an angle in radians). +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.acos(1.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.acos(1.1) +/// |> should.be_error() +/// +/// elementary.acos(-1.1) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn acos(x: Float) -> Result(Float, String) { + case x >=. -1.0 && x <=. 1.0 { + True -> + do_acos(x) + |> Ok + False -> + "Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1." + |> Error + } +} + +@external(erlang, "math", "acos") +@external(javascript, "../../maths.mjs", "acos") +fn do_acos(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> +/// +/// The inverse hyperbolic cosine function: +/// +/// \\[ +/// \forall x \in [1, +\infty\), \\; \cosh^{-1}{(x)} = y \in \[0, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\[1, +\infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\[0, +\infty\)$$ (an angle in radians). +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.acosh(1.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.acosh(0.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn acosh(x: Float) -> Result(Float, String) { + case x >=. 1.0 { + True -> + do_acosh(x) + |> Ok + False -> + "Invalid input argument: x < 1. Valid input is x >= 1." + |> Error + } +} + +@external(erlang, "math", "acosh") +@external(javascript, "../../maths.mjs", "acosh") +fn do_acosh(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> +/// +/// The inverse sine function: +/// +/// \\[ +/// \forall x \in \[-1, 1\], \\; \sin^{-1}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\[-1, 1\]$$ as input and returns a numeric +/// value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians). +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.asin(0.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.asin(1.1) +/// |> should.be_error() +/// +/// elementary.asin(-1.1) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn asin(x: Float) -> Result(Float, String) { + case x >=. -1.0 && x <=. 1.0 { + True -> + do_asin(x) + |> Ok + False -> + "Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1." + |> Error + } +} + +@external(erlang, "math", "asin") +@external(javascript, "../../maths.mjs", "asin") +fn do_asin(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> +/// +/// The inverse hyperbolic sine function: +/// +/// \\[ +/// \forall x \in \(-\infty, \infty\), \\; \sinh^{-1}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, +\infty\)$$ (an angle in radians). +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.asinh(0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn asinh(x: Float) -> Float { + do_asinh(x) +} + +@external(erlang, "math", "asinh") +@external(javascript, "../../maths.mjs", "asinh") +fn do_asinh(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> +/// +/// The inverse tangent function: +/// +/// \\[ +/// \forall x \in \(-\infty, \infty\), \\; \tan^{-1}{(x)} = y \in \[-\frac{\pi}{2}, \frac{\pi}{2}\] +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians). +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.atan(0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn atan(x: Float) -> Float { + do_atan(x) +} + +@external(erlang, "math", "atan") +@external(javascript, "../../maths.mjs", "atan") +fn do_atan(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> +/// +/// The inverse 2-argument tangent function: +/// +/// \\[ +/// \text{atan2}(y, x) = +/// \begin{cases} +/// \tan^{-1}(\frac y x) &\text{if } x > 0, \\\\ +/// \tan^{-1}(\frac y x) + \pi &\text{if } x < 0 \text{ and } y \ge 0, \\\\ +/// \tan^{-1}(\frac y x) - \pi &\text{if } x < 0 \text{ and } y < 0, \\\\ +/// +\frac{\pi}{2} &\text{if } x = 0 \text{ and } y > 0, \\\\ +/// -\frac{\pi}{2} &\text{if } x = 0 \text{ and } y < 0, \\\\ +/// \text{undefined} &\text{if } x = 0 \text{ and } y = 0. +/// \end{cases} +/// \\] +/// +/// The function returns the angle in radians from the x-axis to the line containing the +/// origin $$\(0, 0\)$$ and a point given as input with coordinates $$\(x, y\)$$. The numeric value +/// returned by $$\text{atan2}(y, x)$$ is in the range $$\[-\pi, \pi\]$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.atan2(0.0, 0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn atan2(y: Float, x: Float) -> Float { + do_atan2(y, x) +} + +@external(erlang, "math", "atan2") +@external(javascript, "../../maths.mjs", "atan2") +fn do_atan2(a: Float, b: 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> +/// +/// The inverse hyperbolic tangent function: +/// +/// \\[ +/// \forall x \in \(-1, 1\), \\; \tanh^{-1}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-1, 1\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$ (an angle in radians). +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.atanh(0.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.atanh(1.0) +/// |> should.be_error() +/// +/// elementary.atanh(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn atanh(x: Float) -> Result(Float, String) { + case x >. -1.0 && x <. 1.0 { + True -> + do_atanh(x) + |> Ok + False -> + "Invalid input argument: x > -1 or x < 1. Valid input is -1. < x < 1." + |> Error + } +} + +@external(erlang, "math", "atanh") +@external(javascript, "../../maths.mjs", "atanh") +fn do_atanh(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> +/// +/// The cosine function: +/// +/// \\[ +/// \forall x \in \(-\infty, +\infty\), \\; \cos{(x)} = y \in \[-1, 1\] +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ (an angle in radians) +/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.cos(0.0) +/// |> should.equal(1.0) +/// +/// elementary.cos(elementary.pi()) +/// |> should.equal(-1.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn cos(x: Float) -> Float { + do_cos(x) +} + +@external(erlang, "math", "cos") +@external(javascript, "../../maths.mjs", "cos") +fn do_cos(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> +/// +/// The hyperbolic cosine function: +/// +/// \\[ +/// \forall x \in \(-\infty, \infty\), \\; \cosh{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ as input (an angle in radians) +/// and returns a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. +/// If the input value is too large an overflow error might occur. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.cosh(0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn cosh(x: Float) -> Float { + do_cosh(x) +} + +@external(erlang, "math", "cosh") +@external(javascript, "../../maths.mjs", "cosh") +fn do_cosh(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> +/// +/// The sine function: +/// +/// \\[ +/// \forall x \in \(-\infty, +\infty\), \\; \sin{(x)} = y \in \[-1, 1\] +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ (an angle in radians) +/// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.sin(0.0) +/// |> should.equal(0.0) +/// +/// elementary.sin(0.5 *. elementary.pi()) +/// |> should.equal(1.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn sin(x: Float) -> Float { + do_sin(x) +} + +@external(erlang, "math", "sin") +@external(javascript, "../../maths.mjs", "sin") +fn do_sin(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> +/// +/// The hyperbolic sine function: +/// +/// \\[ +/// \forall x \in \(-\infty, +\infty\), \\; \sinh{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input +/// (an angle in radians) and returns a numeric value $$y$$ that lies in the range +/// $$\(-\infty, +\infty\)$$. If the input value is too large an overflow error might +/// occur. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.sinh(0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn sinh(x: Float) -> Float { + do_sinh(x) +} + +@external(erlang, "math", "sinh") +@external(javascript, "../../maths.mjs", "sinh") +fn do_sinh(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> +/// +/// The tangent function: +/// +/// \\[ +/// \forall x \in \(-\infty, +\infty\), \\; \tan{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, +\infty\)$$ as input +/// (an angle in radians) and returns a numeric value $$y$$ that lies in the range +/// $$\(-\infty, +\infty\)$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.tan(0.0) +/// |> should.equal(0.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn tan(x: Float) -> Float { + do_tan(x) +} + +@external(erlang, "math", "tan") +@external(javascript, "../../maths.mjs", "tan") +fn do_tan(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> +/// +/// The hyperbolic tangent function: +/// +/// \\[ +/// \forall x \in \(-\infty, \infty\), \\; \tanh{(x)} = y \in \[-1, 1\] +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(-\infty, \infty\)$$ as input (an angle in radians) +/// and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example () { +/// elementary.tanh(0.0) +/// |> should.equal(0.0) +/// +/// elementary.tanh(25.0) +/// |> should.equal(1.0) +/// +/// elementary.tanh(-25.0) +/// |> should.equal(-1.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn tanh(x: Float) -> Float { + do_tanh(x) +} + +@external(erlang, "math", "tanh") +@external(javascript, "../../maths.mjs", "tanh") +fn do_tanh(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> +/// +/// The exponential function: +/// +/// \\[ +/// \forall x \in \(-\infty, \infty\), \\; e^{(x)} = y \in \(0, +\infty\) +/// \\] +/// +/// $$e \approx 2.71828\dots$$ is Eulers' number. +/// +/// Note: If the input value $$x$$ is too large an overflow error might occur. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.exponential(0.0) +/// |> should.equal(1.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn exponential(x: Float) -> Float { + do_exponential(x) +} + +@external(erlang, "math", "exp") +@external(javascript, "../../maths.mjs", "exponential") +fn do_exponential(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> +/// +/// The natural logarithm function: +/// +/// \\[ +/// \forall x \in \(0, \infty\), \\; \log_{e}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example () { +/// elementary.natural_logarithm(1.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.natural_logarithm(elementary.e()) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.natural_logarithm(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn natural_logarithm(x: Float) -> Result(Float, String) { + case x >. 0.0 { + True -> + do_natural_logarithm(x) + |> Ok + False -> + "Invalid input argument: x <= 0. Valid input is x > 0." + |> Error + } +} + +@external(erlang, "math", "log") +@external(javascript, "../../maths.mjs", "logarithm") +fn do_natural_logarithm(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> +/// +/// The base $$b$$ logarithm function (computed through the "change of base" formula): +/// +/// \\[ +/// \forall x \in \(0, \infty\) \textnormal{ and } b > 1, \\; \log_{b}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ and a base $$b > 1$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/option +/// import gleam_community/maths/elementary +/// +/// pub fn example () { +/// 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() +/// } +/// </details> +/// +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String) { + case x >. 0.0 { + True -> + case base { + option.Some(a) -> + case a >. 0.0 && a != 1.0 { + True -> { + // Apply the "change of base formula" + let assert Ok(numerator) = logarithm_10(x) + let assert Ok(denominator) = logarithm_10(a) + numerator /. denominator + |> Ok + } + False -> + "Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1." + |> Error + } + _ -> + "Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1." + |> Error + } + _ -> + "Invalid input argument: x <= 0. Valid input is x > 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 The base-2 logarithm function: +/// +/// \\[ +/// \forall x \in \(0, \infty), \\; \log_{2}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example () { +/// elementary.logarithm_2(1.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.logarithm_2(2.0) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.logarithm_2(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn logarithm_2(x: Float) -> Result(Float, String) { + case x >. 0.0 { + True -> + do_logarithm_2(x) + |> Ok + False -> + "Invalid input argument: x <= 0. Valid input is x > 0." + |> Error + } +} + +@external(erlang, "math", "log2") +@external(javascript, "../../maths.mjs", "logarithm_2") +fn do_logarithm_2(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> +/// +/// The base-10 logarithm function: +/// +/// \\[ +/// \forall x \in \(0, \infty), \\; \log_{10}{(x)} = y \in \(-\infty, +\infty\) +/// \\] +/// +/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns +/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. +/// If the input value is outside the domain of the function an error is returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example () { +/// elementary.logarithm_10(1.0) +/// |> should.equal(Ok(0.0)) +/// +/// elementary.logarithm_10(10.0) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.logarithm_10(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn logarithm_10(x: Float) -> Result(Float, String) { + case x >. 0.0 { + True -> + do_logarithm_10(x) + |> Ok + False -> + "Invalid input argument: x <= 0. Valid input is x > 0." + |> Error + } +} + +@external(erlang, "math", "log10") +@external(javascript, "../../maths.mjs", "logarithm_10") +fn do_logarithm_10(a: Float) -> Float + +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +/// <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 exponentiation function: $$y = x^{a}$$. +/// +/// Note that the function is not defined if: +/// 1. The base is negative ($$x < 0$$) and the exponent is fractional +/// ($$a = \frac{n}{m}$$ is an irrreducible fraction). An error will be returned +/// as an imaginary number will otherwise have to be returned. +/// 2. The base is zero ($$x = 0$$) and the exponent is negative ($$a < 0$$) then the +/// expression is equivalent to the exponent $$y$$ divided by $$0$$ and an +/// error will have to be returned as the expression is otherwise undefined. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.power(2., -1.) +/// |> should.equal(Ok(0.5)) +/// +/// elementary.power(2., 2.) +/// |> should.equal(Ok(4.0)) +/// +/// elementary.power(-1., 0.5) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn power(x: Float, y: Float) -> Result(Float, String) { + let fractional: Bool = do_ceiling(y) -. y >. 0.0 + // In the following check: + // 1. If the base (x) is negative and the exponent (y) is fractional + // then return an error as it will otherwise be an imaginary number + // 2. If the base (x) is 0 and the exponent (y) is negative then the + // expression is equivalent to the exponent (y) divided by 0 and an + // error should be returned + case x <. 0.0 && fractional || x == 0.0 && y <. 0.0 { + True -> + "Invalid input argument: x < 0 and y is fractional or x = 0 and y < 0." + |> Error + False -> + do_power(x, y) + |> Ok + } +} + +@external(erlang, "math", "pow") +@external(javascript, "../../maths.mjs", "power") +fn do_power(a: Float, b: Float) -> Float + +@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> +/// +/// The square root function: $$y = \sqrt[2]{x} = x^{\frac{1}{2}}$$. +/// +/// Note that the function is not defined if: +/// 1. The input is negative ($$x < 0$$). An error will be returned +/// as an imaginary number will otherwise have to be returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.square_root(1.0) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.square_root(4.0) +/// |> should.equal(Ok(2.0)) +/// +/// elementary.square_root(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn square_root(x: Float) -> Result(Float, String) { + // In the following check: + // 1. If x is negative then return an error as it will otherwise be an + // imaginary number + case x <. 0.0 { + True -> + "Invalid input argument: x < 0." + |> Error + False -> { + let assert Ok(result) = power(x, 1.0 /. 2.0) + result + |> 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 cube root function: $$y = \sqrt[3]{x} = x^{\frac{1}{3}}$$. +/// +/// Note that the function is not defined if: +/// 1. The input is negative ($$x < 0$$). An error will be returned +/// as an imaginary number will otherwise have to be returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.cube_root(1.0) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.cube_root(27.0) +/// |> should.equal(Ok(3.0)) +/// +/// elementary.cube_root(-1.0) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn cube_root(x: Float) -> Result(Float, String) { + // In the following check: + // 1. If x is negative then return an error as it will otherwise be an + // imaginary number + case x <. 0.0 { + True -> + "Invalid input argument: x < 0." + |> Error + False -> { + let assert Ok(result) = power(x, 1.0 /. 3.0) + result + |> 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 $$n$$'th root function: $$y = \sqrt[n]{x} = x^{\frac{1}{n}}$$. +/// +/// Note that the function is not defined if: +/// 1. The input is negative ($$x < 0$$). An error will be returned +/// as an imaginary number will otherwise have to be returned. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// elementary.nth_root(1.0, 2) +/// |> should.equal(Ok(1.0)) +/// +/// elementary.nth_root(27.0, 3) +/// |> should.equal(Ok(3.0)) +/// +/// elementary.nth_root(256.0, 4) +/// |> should.equal(Ok(4.0)) +/// +/// elementary.nth_root(-1.0, 2) +/// |> should.be_error() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn nth_root(x: Float, n: Int) -> Result(Float, String) { + // In the following check: + // 1. If x is negative then return an error as it will otherwise be an + // imaginary number + case x <. 0.0 { + True -> + "Invalid input argument: x < 0. Valid input is x > 0" + |> Error + False -> + case n >= 1 { + True -> { + let assert Ok(result) = power(x, 1.0 /. int.to_float(n)) + result + |> Ok + } + False -> + "Invalid input argument: n < 1. Valid input is n >= 2." + |> 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 mathematical constant pi: $$\pi \approx 3.1415\dots$$ +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn pi() -> Float { + do_pi() +} + +@external(erlang, "math", "pi") +@external(javascript, "../../maths.mjs", "pi") +fn do_pi() -> 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> +/// +/// The mathematical constant tau: $$\tau = 2 \cdot \pi \approx 6.283\dots$$ +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn tau() -> Float { + 2.0 *. pi() +} + +/// <div style="text-align: right;"> +/// <a href="https://github.com/gleam-community/maths/issues"> +/// <small>Spot a typo? Open an issue!</small> +/// </a> +/// </div> +/// +/// Euler's number $$e \approx 2.71828\dots$$. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/elementary +/// +/// pub fn example() { +/// // Test that the constant is approximately equal to 2.7128... +/// elementary.e() +/// |> elementary.is_close(2.7128, 0.0, 0.000001) +/// |> should.be_true() +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn e() -> Float { + exponential(1.0) +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/metrics.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/metrics.gleam new file mode 100644 index 0000000..1dab2b4 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/metrics.gleam @@ -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: false}, +//// // {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/predicates +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/predicates +/// +/// pub fn example () { +/// let assert Ok(tol) = elementary.power(-10.0, -6.0) +/// +/// [1.0, 1.0, 1.0] +/// |> metrics.norm(1.0) +/// |> predicates.is_close(3.0, 0.0, tol) +/// |> should.be_true() +/// +/// [1.0, 1.0, 1.0] +/// |> metrics.norm(-1.0) +/// |> predicates.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/predicates +/// +/// 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 +/// |> predicates.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/predicates +/// +/// 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 +/// |> predicates.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/predicates +/// +/// 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 +/// |> predicates.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 predicates.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 + } + } + } +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/piecewise.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/piecewise.gleam new file mode 100644 index 0000000..3b40a18 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/piecewise.gleam @@ -0,0 +1,1228 @@ +////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous"> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script> +////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> +////<script> +//// document.addEventListener("DOMContentLoaded", function() { +//// renderMathInElement(document.body, { +//// // customised options +//// // • auto-render specific keys, e.g.: +//// delimiters: [ +//// {left: '$$', right: '$$', display: false}, +//// // {left: '$', right: '$', display: false}, +//// // {left: '\\(', right: '\\)', display: false}, +//// {left: '\\[', right: '\\]', display: true} +//// ], +//// // • rendering keys, e.g.: +//// throwOnError : false +//// }); +//// }); +////</script> +////<style> +//// .katex { font-size: 1.1em; } +////</style> +//// +//// --- +//// +//// Piecewise: A module containing functions that have different definitions depending on conditions or intervals of their domain. +//// +//// * **Rounding functions** +//// * [`ceiling`](#ceiling) +//// * [`floor`](#floor) +//// * [`truncate`](#truncate) +//// * [`round`](#round) +//// * **Sign and absolute value functions** +//// * [`float_absolute_value`](#float_absolute_value) +//// * [`int_absolute_value`](#int_absolute_value) +//// * [`float_absolute_difference`](#float_absolute_difference) +//// * [`int_absolute_difference`](#int_absolute_difference) +//// * [`float_sign`](#float_sign) +//// * [`int_sign`](#int_sign) +//// * [`float_copy_sign`](#float_copy_sign) +//// * [`int_copy_sign`](#float_copy_sign) +//// * [`float_flip_sign`](#float_flip_sign) +//// * [`int_flip_sign`](#int_flip_sign) +//// * **Misc. mathematical functions** +//// * [`minimum`](#minimum) +//// * [`maximum`](#maximum) +//// * [`minmax`](#minmax) +//// * [`list_minimum`](#list_minimum) +//// * [`list_maximum`](#list_maximum) +//// * [`extrema`](#extrema) +//// * [`arg_minimum`](#arg_minimum) +//// * [`arg_maximum`](#arg_maximum) +//// + +import gleam/option +import gleam/list +import gleam/order +import gleam/pair +import gleam/int +import gleam_community/maths/conversion +import gleam_community/maths/elementary + +/// <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 ceiling function rounds a given input value $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is larger than or equal to the input $$x$$. +/// +/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundUp`. +/// +/// <details> +/// <summary>Details</summary> +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// For example, $$12.0654$$ is rounded to: +/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) +/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// </details> +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/option +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.ceiling(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.1)) +/// +/// piecewise.ceiling(12.0654, option.Some(2)) +/// |> should.equal(Ok(12.07)) +/// +/// piecewise.ceiling(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.066)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn ceiling(x: Float, digits: option.Option(Int)) -> Result(Float, String) { + round(x, digits, option.Some(RoundUp)) +} + +/// <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 floor function rounds input $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is less than or equal to the input $$x$$. +/// +/// Note: The floor function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundDown`. +/// +/// <details> +/// <summary>Details</summary> +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// </details> +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/option +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.floor(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.0)) +/// +/// piecewise.floor(12.0654, option.Some(2)) +/// |> should.equal(Ok(12.06)) +/// +/// piecewise.floor(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.065)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn floor(x: Float, digits: option.Option(Int)) -> Result(Float, String) { + round(x, digits, option.Some(RoundDown)) +} + +/// <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 truncate function rounds a given input $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is less than or equal to the absolute value of the input $$x$$. +/// +/// Note: The truncate function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundToZero`. +/// +/// <details> +/// <summary>Details</summary> +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// </details> +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/option +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.truncate(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.0)) +/// +/// piecewise.truncate(12.0654, option.Some(2)) +/// |> should.equal(Ok(12.0)) +/// +/// piecewise.truncate(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.0)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn truncate(x: Float, digits: option.Option(Int)) -> Result(Float, String) { + round(x, digits, option.Some(RoundToZero)) +} + +/// <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 rounds a float to a specific number of digits (after the decimal place or before if negative) using a specified rounding mode. +/// +/// Valid rounding modes include: +/// - `RoundNearest` (default): The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded to the nearest even integer. +/// - `RoundTiesAway`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++ rounding behavior). +/// - `RoundTiesUp`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded towards $$+\infty$$ (Java/JavaScript rounding behaviour). +/// - `RoundToZero`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is less than or equal to the absolute value of the input $$x$$. An alias for this rounding mode is [`truncate`](#truncate). +/// - `RoundDown`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is less than or equal to the input $$x$$. An alias for this rounding mode is [`floor`](#floor). +/// - `RoundUp`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is larger than or equal to the input $$x$$. An alias for this rounding mode is [`ceiling`](#ceiling). +/// +/// <details> +/// <summary>Details</summary> +/// +/// The `RoundNearest` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `RoundTiesAway` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `RoundTiesUp` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `RoundToZero` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `RoundDown` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `RoundUp` rounding mode, rounds $$12.0654$$ to: +/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) +/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// </details> +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/option +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// // 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 "RoundNearest" if None is provided +/// piecewise.round(12.0654, option.None, option.None) +/// |> should.equal(Ok(12.0)) +/// +/// // Try different rounding modes +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest)) +/// |> should.equal(Ok(12.07)) +/// +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway)) +/// |> should.equal(Ok(12.07)) +/// +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp)) +/// |> should.equal(Ok(12.07)) +/// +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundToZero)) +/// |> should.equal(Ok(12.06)) +/// +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundDown)) +/// |> should.equal(Ok(12.06)) +/// +/// piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundUp)) +/// |> should.equal(Ok(12.07)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn round( + x: Float, + digits: option.Option(Int), + mode: option.Option(RoundingMode), +) -> Result(Float, String) { + case digits { + option.Some(a) -> { + let assert Ok(p) = elementary.power(10.0, conversion.int_to_float(a)) + // Round the given input x using at the specified digit + do_round(p, x, mode) + } + // Round the given input x using at the default digit + option.None -> do_round(1.0, x, mode) + } +} + +pub type RoundingMode { + RoundNearest + RoundTiesAway + RoundTiesUp + RoundToZero + RoundDown + RoundUp +} + +fn do_round( + p: Float, + x: Float, + mode: option.Option(RoundingMode), +) -> Result(Float, String) { + case mode { + // Determine the rounding mode + option.Some(RoundNearest) -> + round_to_nearest(p, x) + |> Ok + option.Some(RoundTiesAway) -> + round_ties_away(p, x) + |> Ok + option.Some(RoundTiesUp) -> + round_ties_up(p, x) + |> Ok + option.Some(RoundToZero) -> + round_to_zero(p, x) + |> Ok + option.Some(RoundDown) -> + round_down(p, x) + |> Ok + option.Some(RoundUp) -> + round_up(p, x) + |> Ok + // Otherwise, use the default rounding mode + option.None -> + round_to_nearest(p, x) + |> Ok + } +} + +fn round_to_nearest(p: Float, x: Float) -> Float { + let xabs: Float = float_absolute_value(x) *. p + let xabs_truncated: Float = truncate_float(xabs) + let remainder: Float = xabs -. xabs_truncated + case remainder { + _ if remainder >. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p + _ if remainder == 0.5 -> { + let assert Ok(is_even) = int.modulo(conversion.float_to_int(xabs), 2) + case is_even == 0 { + True -> float_sign(x) *. xabs_truncated /. p + False -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p + } + } + _ -> float_sign(x) *. xabs_truncated /. p + } +} + +fn round_ties_away(p: Float, x: Float) -> Float { + let xabs: Float = float_absolute_value(x) *. p + let remainder: Float = xabs -. truncate_float(xabs) + case remainder { + _ if remainder >=. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p + _ -> float_sign(x) *. truncate_float(xabs) /. p + } +} + +fn round_ties_up(p: Float, x: Float) -> Float { + let xabs: Float = float_absolute_value(x) *. p + let xabs_truncated: Float = truncate_float(xabs) + let remainder: Float = xabs -. xabs_truncated + case remainder { + _ if remainder >=. 0.5 && x >=. 0.0 -> + float_sign(x) *. truncate_float(xabs +. 1.0) /. p + _ -> float_sign(x) *. xabs_truncated /. p + } +} + +// Rounding mode: ToZero / Truncate +fn round_to_zero(p: Float, x: Float) -> Float { + truncate_float(x *. p) /. p +} + +fn truncate_float(x: Float) -> Float { + do_truncate_float(x) +} + +@external(erlang, "erlang", "trunc") +@external(javascript, "../../maths.mjs", "truncate") +fn do_truncate_float(a: Float) -> Float + +// Rounding mode: Down / Floor +fn round_down(p: Float, x: Float) -> Float { + do_floor(x *. p) /. p +} + +@external(erlang, "math", "floor") +@external(javascript, "../../maths.mjs", "floor") +fn do_floor(a: Float) -> Float + +// Rounding mode: Up / Ceiling +fn round_up(p: Float, x: Float) -> Float { + do_ceiling(x *. p) /. p +} + +@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> +/// +/// The absolute value: +/// +/// \\[ +/// \forall x, y \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. +/// \\] +/// +/// The function takes an input $$x$$ and returns a positive float value. +/// +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn float_absolute_value(x: Float) -> Float { + case x >. 0.0 { + True -> x + False -> -1.0 *. 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 absolute value: +/// +/// \\[ +/// \forall x, y \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. +/// \\] +/// +/// The function takes an input $$x$$ and returns a positive integer value. +/// +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn int_absolute_value(x: Int) -> Int { + case x > 0 { + True -> x + False -> -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 absolute difference: +/// +/// \\[ +/// \forall x, y \in \mathbb{R}, \\; |x - y| \in \mathbb{R}_{+}. +/// \\] +/// +/// The function takes two inputs $$x$$ and $$y$$ and returns a positive float +/// value which is the the absolute difference of the inputs. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.float_absolute_difference(-10.0, 10.0) +/// |> should.equal(20.0) +/// +/// piecewise.float_absolute_difference(0.0, -2.0) +/// |> should.equal(2.0) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub 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> +/// +/// 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/piecewise +/// +/// pub fn example() { +/// piecewise.absolute_difference(-10, 10) +/// |> should.equal(20) +/// +/// piecewise.absolute_difference(0, -2) +/// |> should.equal(2) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn int_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> +/// +/// The function takes an input $$x \in \mathbb{R}$$ and returns the sign of +/// the input, indicating whether it is positive (+1.0), negative (-1.0), or +/// zero (0.0). +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn float_sign(x: Float) -> Float { + do_float_sign(x) +} + +@target(erlang) +fn do_float_sign(x: Float) -> Float { + case x <. 0.0 { + True -> -1.0 + False -> + case x == 0.0 { + True -> 0.0 + False -> 1.0 + } + } +} + +@target(javascript) +@external(javascript, "../../maths.mjs", "sign") +fn do_float_sign(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> +/// +/// The function takes an input $$x \in \mathbb{Z}$$ and returns the sign of +/// the input, indicating whether it is positive (+1), negative (-1), or zero +/// (0). +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn int_sign(x: Int) -> Int { + do_int_sign(x) +} + +@target(erlang) +fn do_int_sign(x: Int) -> Int { + case x < 0 { + True -> -1 + False -> + case x == 0 { + True -> 0 + False -> 1 + } + } +} + +@target(javascript) +@external(javascript, "../../maths.mjs", "sign") +fn do_int_sign(a: Int) -> Int + +/// <div style="text-align: right;"> +/// <a href="https://github.com/gleam-community/maths/issues"> +/// <small>Spot a typo? Open an issue!</small> +/// </a> +/// </div> +/// +/// The function takes two arguments $$x, y \in \mathbb{R}$$ and returns $$x$$ such that it has the same sign as $$y$$. +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn float_copy_sign(x: Float, y: Float) -> Float { + case float_sign(x) == float_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 -> float_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> +/// +/// 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 int_copy_sign(x: Int, y: Int) -> Int { + case int_sign(x) == int_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 -> int_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> +/// +/// The function flips the sign of a given input value $$x \in \mathbb{R}$$. +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn float_flip_sign(x: Float) -> Float { + -1.0 *. 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 flips the sign of a given input value $$x \in \mathbb{Z}$$. +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn int_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 minimum function takes two arguments $$x, y$$ along with a function +/// for comparing $$x, y$$. The function returns the smallest of the two given +/// values. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.minimum(2.0, 1.5, float.compare) +/// |> should.equal(1.5) +/// +/// piecewise.minimum(1.5, 2.0, float.compare) +/// |> should.equal(1.5) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn minimum(x: a, y: a, compare: fn(a, a) -> order.Order) -> a { + case compare(x, y) { + order.Lt -> x + order.Eq -> x + order.Gt -> y + } +} + +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +/// <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 takes two arguments $$x, y$$ along with a function +/// for comparing $$x, y$$. The function returns the largest of the two given +/// values. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.maximum(2.0, 1.5, float.compare) +/// |> should.equal(1.5) +/// +/// piecewise.maximum(1.5, 2.0, float.compare) +/// |> should.equal(1.5) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn maximum(x: a, y: a, compare: fn(a, a) -> order.Order) -> a { + case compare(x, y) { + order.Lt -> y + order.Eq -> y + order.Gt -> 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 minmax function takes two arguments $$x, y$$ along with a function +/// for comparing $$x, y$$. The function returns a tuple with the smallest +/// value first and largest second. +/// +/// <details> +/// <summary>Example</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example() { +/// piecewise.minmax(2.0, 1.5, float.compare) +/// |> should.equal(#(1.5, 2.0)) +/// +/// piecewise.minmax(1.5, 2.0, float.compare) +/// |> should.equal(#(1.5, 2.0)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn minmax(x: a, y: a, compare: fn(a, a) -> order.Order) -> #(a, a) { + #(minimum(x, y, compare), maximum(x, y, 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> +/// +/// Returns the minimum value of a given list. +/// +/// <details> +/// <summary>Example:</summary> +/// +/// import gleeunit/should +/// import gleam/int +/// import gleam_community/maths/piecewise +/// +/// pub fn example () { +/// // 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)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +pub fn list_minimum( + arr: List(a), + compare: fn(a, a) -> order.Order, +) -> Result(a, 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: a, element: a) { + case compare(element, acc) { + order.Lt -> element + _ -> 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 maximum value of a given list. +/// +/// <details> +/// <summary>Example:</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example () { +/// // An empty lists returns an error +/// [] +/// |> piecewise.list_maximum(float.compare) +/// |> should.be_error() +/// +/// // Valid input returns a result +/// [4.0, 4.0, 3.0, 2.0, 1.0] +/// |> piecewise.list_maximum(float.compare) +/// |> should.equal(Ok(4.0)) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn list_maximum( + arr: List(a), + compare: fn(a, a) -> order.Order, +) -> Result(a, 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: a, element: a) { + case compare(acc, element) { + order.Lt -> element + _ -> acc + } + }, + ) + |> Ok + } + } +} + +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +/// <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 given list. +/// +/// <details> +/// <summary>Example:</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example () { +/// // An empty lists returns an error +/// [] +/// |> piecewise.arg_minimum(float.compare) +/// |> should.be_error() +/// +/// // Valid input returns a result +/// [4.0, 4.0, 3.0, 2.0, 1.0] +/// |> piecewise.arg_minimum(float.compare) +/// |> 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(a), + compare: fn(a, a) -> order.Order, +) -> Result(List(Int), String) { + case arr { + [] -> + "Invalid input argument: The list is empty." + |> Error + _ -> { + let assert Ok(min) = + arr + |> list_minimum(compare) + arr + |> list.index_map(fn(index: Int, element: a) -> Int { + case compare(element, min) { + order.Eq -> index + _ -> -1 + } + }) + |> list.filter(fn(index: Int) -> Bool { + case index { + -1 -> False + _ -> True + } + }) + |> Ok + } + } +} + +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +/// <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 given list. +/// +/// <details> +/// <summary>Example:</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example () { +/// // An empty lists returns an error +/// [] +/// |> piecewise.arg_maximum(float.compare) +/// |> should.be_error() +/// +/// // Valid input returns a result +/// [4.0, 4.0, 3.0, 2.0, 1.0] +/// |> piecewise.arg_maximum(float.compare) +/// |> 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(a), + compare: fn(a, a) -> order.Order, +) -> Result(List(Int), String) { + case arr { + [] -> + "Invalid input argument: The list is empty." + |> Error + _ -> { + let assert Ok(max) = + arr + |> list_maximum(compare) + arr + |> list.index_map(fn(index: Int, element: a) -> Int { + case compare(element, max) { + order.Eq -> index + _ -> -1 + } + }) + |> list.filter(fn(index: Int) -> Bool { + case index { + -1 -> False + _ -> True + } + }) + |> Ok + } + } +} + +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +/// <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 values of a given list. +/// +/// <details> +/// <summary>Example:</summary> +/// +/// import gleeunit/should +/// import gleam/float +/// import gleam_community/maths/piecewise +/// +/// pub fn example () { +/// // 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))) +/// } +/// </details> +/// +/// <div style="text-align: right;"> +/// <a href="#"> +/// <small>Back to top ↑</small> +/// </a> +/// </div> +/// +pub fn extrema( + arr: List(a), + compare: fn(a, a) -> order.Order, +) -> Result(#(a, a), 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: #(a, a), element: a) { + let first: a = pair.first(acc) + let second: a = pair.second(acc) + case compare(element, first), compare(second, element) { + order.Lt, order.Lt -> #(element, element) + order.Lt, _ -> #(element, second) + _, order.Lt -> #(first, element) + _, _ -> #(first, second) + } + }, + ) + |> Ok + } + } +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/predicates.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/predicates.gleam new file mode 100644 index 0000000..f8d357c --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/predicates.gleam @@ -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: false}, +//// // {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> +//// +//// --- +//// +//// Predicates: A module containing functions for testing various mathematical properties of numbers. +//// +//// * **Tests** +//// * [`is_close`](#is_close) +//// * [`list_all_close`](#all_close) +//// * [`is_fractional`](#is_fractional) +//// * [`is_power`](#is_power) +//// * [`is_perfect`](#is_perfect) +//// * [`is_even`](#is_even) +//// * [`is_odd`](#is_odd) + +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 +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/sequences.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/sequences.gleam new file mode 100644 index 0000000..e7c0388 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/sequences.gleam @@ -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: false}, +//// // {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/predicates +/// +/// 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) = +/// predicates.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/predicates +/// +/// pub fn example () { +/// let assert Ok(tol) = elementary.power(-10.0, -6.0) +/// let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0) +/// let assert Ok(result) = +/// predicates.all_close(logspace, [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/predicates +/// +/// pub fn example () { +/// let assert Ok(tol) = elementary.power(-10.0, -6.0) +/// let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True) +/// let assert Ok(result) = +/// predicates.all_close(logspace, [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 + } + } +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/special.gleam b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/special.gleam new file mode 100644 index 0000000..dfd9cbb --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/special.gleam @@ -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: false}, +//// // {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 assert [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) + } + } +} diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@arithmetics.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@arithmetics.erl new file mode 100644 index 0000000..ca266c8 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@arithmetics.erl @@ -0,0 +1,172 @@ +-module(gleam_community@maths@arithmetics). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([gcd/2, lcm/2, divisors/1, proper_divisors/1, float_sum/1, int_sum/1, float_product/1, int_product/1, float_cumulative_sum/1, int_cumulative_sum/1, float_cumumlative_product/1, int_cumulative_product/1]). + +-spec do_gcd(integer(), integer()) -> integer(). +do_gcd(X, Y) -> + case X =:= 0 of + true -> + Y; + + false -> + _assert_subject = gleam@int:modulo(Y, X), + {ok, Z} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/arithmetics"/utf8>>, + function => <<"do_gcd"/utf8>>, + line => 93}) + end, + do_gcd(Z, X) + end. + +-spec gcd(integer(), integer()) -> integer(). +gcd(X, Y) -> + Absx = gleam_community@maths@piecewise:int_absolute_value(X), + Absy = gleam_community@maths@piecewise:int_absolute_value(Y), + do_gcd(Absx, Absy). + +-spec lcm(integer(), integer()) -> integer(). +lcm(X, Y) -> + Absx = gleam_community@maths@piecewise:int_absolute_value(X), + Absy = gleam_community@maths@piecewise:int_absolute_value(Y), + case do_gcd(Absx, Absy) of + 0 -> 0; + Gleam@denominator -> (Absx * Absy) div Gleam@denominator + end. + +-spec find_divisors(integer()) -> list(integer()). +find_divisors(N) -> + Nabs = gleam_community@maths@piecewise:float_absolute_value( + gleam_community@maths@conversion:int_to_float(N) + ), + _assert_subject = gleam_community@maths@elementary:square_root(Nabs), + {ok, Sqrt_result} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/arithmetics"/utf8>>, + function => <<"find_divisors"/utf8>>, + line => 176}) + end, + Max = gleam_community@maths@conversion:float_to_int(Sqrt_result) + 1, + _pipe = gleam@list:range(2, Max), + _pipe@1 = gleam@list:fold(_pipe, [1, N], fun(Acc, I) -> case (case I of + 0 -> 0; + Gleam@denominator -> N rem Gleam@denominator + end) =:= 0 of + true -> + [I, case I of + 0 -> 0; + Gleam@denominator@1 -> N div Gleam@denominator@1 + end | Acc]; + + false -> + Acc + end end), + _pipe@2 = gleam@list:unique(_pipe@1), + gleam@list:sort(_pipe@2, fun gleam@int:compare/2). + +-spec divisors(integer()) -> list(integer()). +divisors(N) -> + find_divisors(N). + +-spec proper_divisors(integer()) -> list(integer()). +proper_divisors(N) -> + Divisors = find_divisors(N), + _pipe = Divisors, + gleam@list:take(_pipe, gleam@list:length(Divisors) - 1). + +-spec float_sum(list(float())) -> float(). +float_sum(Arr) -> + case Arr of + [] -> + +0.0; + + _ -> + _pipe = Arr, + gleam@list:fold(_pipe, +0.0, fun(Acc, A) -> A + Acc end) + end. + +-spec int_sum(list(integer())) -> integer(). +int_sum(Arr) -> + case Arr of + [] -> + 0; + + _ -> + _pipe = Arr, + gleam@list:fold(_pipe, 0, fun(Acc, A) -> A + Acc end) + end. + +-spec float_product(list(float())) -> float(). +float_product(Arr) -> + case Arr of + [] -> + 1.0; + + _ -> + _pipe = Arr, + gleam@list:fold(_pipe, 1.0, fun(Acc, A) -> A * Acc end) + end. + +-spec int_product(list(integer())) -> integer(). +int_product(Arr) -> + case Arr of + [] -> + 1; + + _ -> + _pipe = Arr, + gleam@list:fold(_pipe, 1, fun(Acc, A) -> A * Acc end) + end. + +-spec float_cumulative_sum(list(float())) -> list(float()). +float_cumulative_sum(Arr) -> + case Arr of + [] -> + []; + + _ -> + _pipe = Arr, + gleam@list:scan(_pipe, +0.0, fun(Acc, A) -> A + Acc end) + end. + +-spec int_cumulative_sum(list(integer())) -> list(integer()). +int_cumulative_sum(Arr) -> + case Arr of + [] -> + []; + + _ -> + _pipe = Arr, + gleam@list:scan(_pipe, 0, fun(Acc, A) -> A + Acc end) + end. + +-spec float_cumumlative_product(list(float())) -> list(float()). +float_cumumlative_product(Arr) -> + case Arr of + [] -> + []; + + _ -> + _pipe = Arr, + gleam@list:scan(_pipe, 1.0, fun(Acc, A) -> A * Acc end) + end. + +-spec int_cumulative_product(list(integer())) -> list(integer()). +int_cumulative_product(Arr) -> + case Arr of + [] -> + []; + + _ -> + _pipe = Arr, + gleam@list:scan(_pipe, 1, fun(Acc, A) -> A * Acc end) + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@combinatorics.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@combinatorics.erl new file mode 100644 index 0000000..17644c4 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@combinatorics.erl @@ -0,0 +1,218 @@ +-module(gleam_community@maths@combinatorics). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([combination/2, factorial/1, permutation/2, list_combination/2, list_permutation/1, cartesian_product/2]). + +-spec combination(integer(), integer()) -> {ok, integer()} | {error, binary()}. +combination(N, K) -> + case N < 0 of + true -> + _pipe = <<"Invalid input argument: n < 0. Valid input is n > 0."/utf8>>, + {error, _pipe}; + + false -> + case (K < 0) orelse (K > N) of + true -> + _pipe@1 = 0, + {ok, _pipe@1}; + + false -> + case (K =:= 0) orelse (K =:= N) of + true -> + _pipe@2 = 1, + {ok, _pipe@2}; + + false -> + Min = case K < (N - K) of + true -> + K; + + false -> + N - K + end, + _pipe@3 = gleam@list:range(1, Min), + _pipe@4 = gleam@list:fold( + _pipe@3, + 1, + fun(Acc, X) -> case X of + 0 -> 0; + Gleam@denominator -> (Acc * ((N + 1) - X)) + div Gleam@denominator + end end + ), + {ok, _pipe@4} + end + end + end. + +-spec factorial(integer()) -> {ok, integer()} | {error, binary()}. +factorial(N) -> + case N < 0 of + true -> + _pipe = <<"Invalid input argument: n < 0. Valid input is n > 0."/utf8>>, + {error, _pipe}; + + false -> + case N of + 0 -> + _pipe@1 = 1, + {ok, _pipe@1}; + + 1 -> + _pipe@2 = 1, + {ok, _pipe@2}; + + _ -> + _pipe@3 = gleam@list:range(1, N), + _pipe@4 = gleam@list:fold( + _pipe@3, + 1, + fun(Acc, X) -> Acc * X end + ), + {ok, _pipe@4} + end + end. + +-spec permutation(integer(), integer()) -> {ok, integer()} | {error, binary()}. +permutation(N, K) -> + case N < 0 of + true -> + _pipe = <<"Invalid input argument: n < 0. Valid input is n > 0."/utf8>>, + {error, _pipe}; + + false -> + case (K < 0) orelse (K > N) of + true -> + _pipe@1 = 0, + {ok, _pipe@1}; + + false -> + case K =:= N of + true -> + _pipe@2 = 1, + {ok, _pipe@2}; + + false -> + _assert_subject = factorial(N), + {ok, V1} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/combinatorics"/utf8>>, + function => <<"permutation"/utf8>>, + line => 241}) + end, + _assert_subject@1 = factorial(N - K), + {ok, V2} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/combinatorics"/utf8>>, + function => <<"permutation"/utf8>>, + line => 242}) + end, + _pipe@3 = case V2 of + 0 -> 0; + Gleam@denominator -> V1 div Gleam@denominator + end, + {ok, _pipe@3} + end + end + end. + +-spec do_list_combination(list(GIO), integer(), list(GIO)) -> list(list(GIO)). +do_list_combination(Arr, K, Prefix) -> + case K of + 0 -> + [gleam@list:reverse(Prefix)]; + + _ -> + case Arr of + [] -> + []; + + [X | Xs] -> + With_x = do_list_combination(Xs, K - 1, [X | Prefix]), + Without_x = do_list_combination(Xs, K, Prefix), + gleam@list:append(With_x, Without_x) + end + end. + +-spec list_combination(list(GII), integer()) -> {ok, list(list(GII))} | + {error, binary()}. +list_combination(Arr, K) -> + case K < 0 of + true -> + _pipe = <<"Invalid input argument: k < 0. Valid input is k > 0."/utf8>>, + {error, _pipe}; + + false -> + case K > gleam@list:length(Arr) of + true -> + _pipe@1 = <<"Invalid input argument: k > length(arr). Valid input is 0 < k <= length(arr)."/utf8>>, + {error, _pipe@1}; + + false -> + _pipe@2 = do_list_combination(Arr, K, []), + {ok, _pipe@2} + end + end. + +-spec list_permutation(list(GIT)) -> list(list(GIT)). +list_permutation(Arr) -> + case Arr of + [] -> + [[]]; + + _ -> + gleam@list:flat_map( + Arr, + fun(X) -> + _assert_subject = gleam@list:pop(Arr, fun(Y) -> X =:= Y end), + {ok, {_, Remaining}} = case _assert_subject of + {ok, {_, _}} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/combinatorics"/utf8>>, + function => <<"list_permutation"/utf8>>, + line => 373}) + end, + gleam@list:map( + list_permutation(Remaining), + fun(Perm) -> [X | Perm] end + ) + end + ) + end. + +-spec cartesian_product(list(GIX), list(GIX)) -> list({GIX, GIX}). +cartesian_product(Xarr, Yarr) -> + Xset = begin + _pipe = Xarr, + gleam@set:from_list(_pipe) + end, + Yset = begin + _pipe@1 = Yarr, + gleam@set:from_list(_pipe@1) + end, + _pipe@2 = Xset, + _pipe@3 = gleam@set:fold( + _pipe@2, + gleam@set:new(), + fun(Accumulator0, Member0) -> + gleam@set:fold( + Yset, + Accumulator0, + fun(Accumulator1, Member1) -> + gleam@set:insert(Accumulator1, {Member0, Member1}) + end + ) + end + ), + gleam@set:to_list(_pipe@3). diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@conversion.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@conversion.erl new file mode 100644 index 0000000..0f6decb --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@conversion.erl @@ -0,0 +1,23 @@ +-module(gleam_community@maths@conversion). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([int_to_float/1, float_to_int/1, degrees_to_radians/1, radians_to_degrees/1]). + +-spec int_to_float(integer()) -> float(). +int_to_float(X) -> + gleam@int:to_float(X). + +-spec float_to_int(float()) -> integer(). +float_to_int(X) -> + erlang:trunc(X). + +-spec degrees_to_radians(float()) -> float(). +degrees_to_radians(X) -> + (X * math:pi()) / 180.0. + +-spec radians_to_degrees(float()) -> float(). +radians_to_degrees(X) -> + case math:pi() of + 0.0 -> 0.0; + Gleam@denominator -> (X * 180.0) / Gleam@denominator + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@elementary.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@elementary.erl new file mode 100644 index 0000000..dab5d68 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@elementary.erl @@ -0,0 +1,284 @@ +-module(gleam_community@maths@elementary). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([acos/1, acosh/1, asin/1, asinh/1, atan/1, atan2/2, atanh/1, cos/1, cosh/1, sin/1, sinh/1, tan/1, tanh/1, exponential/1, natural_logarithm/1, logarithm_2/1, logarithm_10/1, logarithm/2, power/2, square_root/1, cube_root/1, nth_root/2, pi/0, tau/0, e/0]). + +-spec acos(float()) -> {ok, float()} | {error, binary()}. +acos(X) -> + case (X >= -1.0) andalso (X =< 1.0) of + true -> + _pipe = math:acos(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1."/utf8>>, + {error, _pipe@1} + end. + +-spec acosh(float()) -> {ok, float()} | {error, binary()}. +acosh(X) -> + case X >= 1.0 of + true -> + _pipe = math:acosh(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x < 1. Valid input is x >= 1."/utf8>>, + {error, _pipe@1} + end. + +-spec asin(float()) -> {ok, float()} | {error, binary()}. +asin(X) -> + case (X >= -1.0) andalso (X =< 1.0) of + true -> + _pipe = math:asin(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1."/utf8>>, + {error, _pipe@1} + end. + +-spec asinh(float()) -> float(). +asinh(X) -> + math:asinh(X). + +-spec atan(float()) -> float(). +atan(X) -> + math:atan(X). + +-spec atan2(float(), float()) -> float(). +atan2(Y, X) -> + math:atan2(Y, X). + +-spec atanh(float()) -> {ok, float()} | {error, binary()}. +atanh(X) -> + case (X > -1.0) andalso (X < 1.0) of + true -> + _pipe = math:atanh(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x > -1 or x < 1. Valid input is -1. < x < 1."/utf8>>, + {error, _pipe@1} + end. + +-spec cos(float()) -> float(). +cos(X) -> + math:cos(X). + +-spec cosh(float()) -> float(). +cosh(X) -> + math:cosh(X). + +-spec sin(float()) -> float(). +sin(X) -> + math:sin(X). + +-spec sinh(float()) -> float(). +sinh(X) -> + math:sinh(X). + +-spec tan(float()) -> float(). +tan(X) -> + math:tan(X). + +-spec tanh(float()) -> float(). +tanh(X) -> + math:tanh(X). + +-spec exponential(float()) -> float(). +exponential(X) -> + math:exp(X). + +-spec natural_logarithm(float()) -> {ok, float()} | {error, binary()}. +natural_logarithm(X) -> + case X > +0.0 of + true -> + _pipe = math:log(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x <= 0. Valid input is x > 0."/utf8>>, + {error, _pipe@1} + end. + +-spec logarithm_2(float()) -> {ok, float()} | {error, binary()}. +logarithm_2(X) -> + case X > +0.0 of + true -> + _pipe = math:log2(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x <= 0. Valid input is x > 0."/utf8>>, + {error, _pipe@1} + end. + +-spec logarithm_10(float()) -> {ok, float()} | {error, binary()}. +logarithm_10(X) -> + case X > +0.0 of + true -> + _pipe = math:log10(X), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: x <= 0. Valid input is x > 0."/utf8>>, + {error, _pipe@1} + end. + +-spec logarithm(float(), gleam@option:option(float())) -> {ok, float()} | + {error, binary()}. +logarithm(X, Base) -> + case X > +0.0 of + true -> + case Base of + {some, A} -> + case (A > +0.0) andalso (A /= 1.0) of + true -> + _assert_subject = logarithm_10(X), + {ok, Numerator} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/elementary"/utf8>>, + function => <<"logarithm"/utf8>>, + line => 820}) + end, + _assert_subject@1 = logarithm_10(A), + {ok, Denominator} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/elementary"/utf8>>, + function => <<"logarithm"/utf8>>, + line => 821}) + end, + _pipe = case Denominator of + 0.0 -> 0.0; + Gleam@denominator -> Numerator / Gleam@denominator + end, + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1."/utf8>>, + {error, _pipe@1} + end; + + _ -> + _pipe@2 = <<"Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1."/utf8>>, + {error, _pipe@2} + end; + + _ -> + _pipe@3 = <<"Invalid input argument: x <= 0. Valid input is x > 0."/utf8>>, + {error, _pipe@3} + end. + +-spec power(float(), float()) -> {ok, float()} | {error, binary()}. +power(X, Y) -> + Fractional = (math:ceil(Y) - Y) > +0.0, + case ((X < +0.0) andalso Fractional) orelse ((X =:= +0.0) andalso (Y < +0.0)) of + true -> + _pipe = <<"Invalid input argument: x < 0 and y is fractional or x = 0 and y < 0."/utf8>>, + {error, _pipe}; + + false -> + _pipe@1 = math:pow(X, Y), + {ok, _pipe@1} + end. + +-spec square_root(float()) -> {ok, float()} | {error, binary()}. +square_root(X) -> + case X < +0.0 of + true -> + _pipe = <<"Invalid input argument: x < 0."/utf8>>, + {error, _pipe}; + + false -> + _assert_subject = power(X, 1.0 / 2.0), + {ok, Result} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/elementary"/utf8>>, + function => <<"square_root"/utf8>>, + line => 1066}) + end, + _pipe@1 = Result, + {ok, _pipe@1} + end. + +-spec cube_root(float()) -> {ok, float()} | {error, binary()}. +cube_root(X) -> + case X < +0.0 of + true -> + _pipe = <<"Invalid input argument: x < 0."/utf8>>, + {error, _pipe}; + + false -> + _assert_subject = power(X, 1.0 / 3.0), + {ok, Result} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/elementary"/utf8>>, + function => <<"cube_root"/utf8>>, + line => 1118}) + end, + _pipe@1 = Result, + {ok, _pipe@1} + end. + +-spec nth_root(float(), integer()) -> {ok, float()} | {error, binary()}. +nth_root(X, N) -> + case X < +0.0 of + true -> + _pipe = <<"Invalid input argument: x < 0. Valid input is x > 0"/utf8>>, + {error, _pipe}; + + false -> + case N >= 1 of + true -> + _assert_subject = power(X, case gleam@int:to_float(N) of + 0.0 -> 0.0; + Gleam@denominator -> 1.0 / Gleam@denominator + end), + {ok, Result} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/elementary"/utf8>>, + function => <<"nth_root"/utf8>>, + line => 1175}) + end, + _pipe@1 = Result, + {ok, _pipe@1}; + + false -> + _pipe@2 = <<"Invalid input argument: n < 1. Valid input is n >= 2."/utf8>>, + {error, _pipe@2} + end + end. + +-spec pi() -> float(). +pi() -> + math:pi(). + +-spec tau() -> float(). +tau() -> + 2.0 * pi(). + +-spec e() -> float(). +e() -> + exponential(1.0). diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@metrics.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@metrics.erl new file mode 100644 index 0000000..2a58da6 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@metrics.erl @@ -0,0 +1,278 @@ +-module(gleam_community@maths@metrics). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([norm/2, minkowski_distance/3, manhatten_distance/2, euclidean_distance/2, mean/1, median/1, variance/2, standard_deviation/2]). + +-spec norm(list(float()), float()) -> float(). +norm(Arr, P) -> + case Arr of + [] -> + +0.0; + + _ -> + Agg = begin + _pipe = Arr, + gleam@list:fold( + _pipe, + +0.0, + fun(Acc, A) -> + _assert_subject = gleam_community@maths@elementary:power( + gleam_community@maths@piecewise:float_absolute_value( + A + ), + P + ), + {ok, Result} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"norm"/utf8>>, + line => 101}) + end, + Result + Acc + end + ) + end, + _assert_subject@1 = gleam_community@maths@elementary:power( + Agg, + case P of + 0.0 -> 0.0; + Gleam@denominator -> 1.0 / Gleam@denominator + end + ), + {ok, Result@1} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"norm"/utf8>>, + line => 106}) + end, + Result@1 + end. + +-spec minkowski_distance(list(float()), list(float()), float()) -> {ok, float()} | + {error, binary()}. +minkowski_distance(Xarr, Yarr, P) -> + Xlen = gleam@list:length(Xarr), + Ylen = gleam@list:length(Yarr), + case Xlen =:= Ylen of + false -> + _pipe = <<"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."/utf8>>, + {error, _pipe}; + + true -> + case P < 1.0 of + true -> + _pipe@1 = <<"Invalid input argument: p < 1. Valid input is p >= 1."/utf8>>, + {error, _pipe@1}; + + false -> + _pipe@2 = gleam@list:zip(Xarr, Yarr), + _pipe@3 = gleam@list:map( + _pipe@2, + fun(Tuple) -> + gleam@pair:first(Tuple) - gleam@pair:second(Tuple) + end + ), + _pipe@4 = norm(_pipe@3, P), + {ok, _pipe@4} + end + end. + +-spec manhatten_distance(list(float()), list(float())) -> {ok, float()} | + {error, binary()}. +manhatten_distance(Xarr, Yarr) -> + minkowski_distance(Xarr, Yarr, 1.0). + +-spec euclidean_distance(list(float()), list(float())) -> {ok, float()} | + {error, binary()}. +euclidean_distance(Xarr, Yarr) -> + minkowski_distance(Xarr, Yarr, 2.0). + +-spec mean(list(float())) -> {ok, float()} | {error, binary()}. +mean(Arr) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _pipe@1 = Arr, + _pipe@2 = gleam_community@maths@arithmetics:float_sum(_pipe@1), + _pipe@3 = (fun(A) -> + case gleam_community@maths@conversion:int_to_float( + gleam@list:length(Arr) + ) of + 0.0 -> 0.0; + Gleam@denominator -> A / Gleam@denominator + end + end)(_pipe@2), + {ok, _pipe@3} + end. + +-spec median(list(float())) -> {ok, float()} | {error, binary()}. +median(Arr) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + Count = gleam@list:length(Arr), + Mid = gleam@list:length(Arr) div 2, + Sorted = gleam@list:sort(Arr, fun gleam@float:compare/2), + case gleam_community@maths@predicates:is_odd(Count) of + true -> + _assert_subject = gleam@list:at(Sorted, Mid), + {ok, Val0} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"median"/utf8>>, + line => 402}) + end, + _pipe@1 = Val0, + {ok, _pipe@1}; + + false -> + _assert_subject@1 = gleam@list:at(Sorted, Mid - 1), + {ok, Val0@1} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"median"/utf8>>, + line => 409}) + end, + _assert_subject@2 = gleam@list:at(Sorted, Mid), + {ok, Val1} = case _assert_subject@2 of + {ok, _} -> _assert_subject@2; + _assert_fail@2 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@2, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"median"/utf8>>, + line => 410}) + end, + _pipe@2 = [Val0@1, Val1], + mean(_pipe@2) + end + end. + +-spec variance(list(float()), integer()) -> {ok, float()} | {error, binary()}. +variance(Arr, Ddof) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + case Ddof < 0 of + true -> + _pipe@1 = <<"Invalid input argument: ddof < 0. Valid input is ddof >= 0."/utf8>>, + {error, _pipe@1}; + + false -> + _assert_subject = mean(Arr), + {ok, Mean} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"variance"/utf8>>, + line => 475}) + end, + _pipe@2 = Arr, + _pipe@3 = gleam@list:map( + _pipe@2, + fun(A) -> + _assert_subject@1 = gleam_community@maths@elementary:power( + A - Mean, + 2.0 + ), + {ok, Result} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"variance"/utf8>>, + line => 478}) + end, + Result + end + ), + _pipe@4 = gleam_community@maths@arithmetics:float_sum( + _pipe@3 + ), + _pipe@5 = (fun(A@1) -> + case (gleam_community@maths@conversion:int_to_float( + gleam@list:length(Arr) + ) + - gleam_community@maths@conversion:int_to_float(Ddof)) of + 0.0 -> 0.0; + Gleam@denominator -> A@1 / Gleam@denominator + end + end)(_pipe@4), + {ok, _pipe@5} + end + end. + +-spec standard_deviation(list(float()), integer()) -> {ok, float()} | + {error, binary()}. +standard_deviation(Arr, Ddof) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + case Ddof < 0 of + true -> + _pipe@1 = <<"Invalid input argument: ddof < 0. Valid input is ddof >= 0."/utf8>>, + {error, _pipe@1}; + + false -> + _assert_subject = variance(Arr, Ddof), + {ok, Variance} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"standard_deviation"/utf8>>, + line => 551}) + end, + _assert_subject@1 = gleam_community@maths@elementary:square_root( + Variance + ), + {ok, Stdev} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/metrics"/utf8>>, + function => <<"standard_deviation"/utf8>>, + line => 554}) + end, + _pipe@2 = Stdev, + {ok, _pipe@2} + end + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@piecewise.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@piecewise.erl new file mode 100644 index 0000000..258d879 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@piecewise.erl @@ -0,0 +1,553 @@ +-module(gleam_community@maths@piecewise). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([float_absolute_value/1, int_absolute_value/1, float_absolute_difference/2, int_absolute_difference/2, float_sign/1, round/3, ceiling/2, floor/2, truncate/2, int_sign/1, float_flip_sign/1, float_copy_sign/2, int_flip_sign/1, int_copy_sign/2, minimum/3, maximum/3, minmax/3, list_minimum/2, list_maximum/2, arg_minimum/2, arg_maximum/2, extrema/2]). +-export_type([rounding_mode/0]). + +-type rounding_mode() :: round_nearest | + round_ties_away | + round_ties_up | + round_to_zero | + round_down | + round_up. + +-spec truncate_float(float()) -> float(). +truncate_float(X) -> + erlang:trunc(X). + +-spec round_to_zero(float(), float()) -> float(). +round_to_zero(P, X) -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> truncate_float(X * P) / Gleam@denominator + end. + +-spec round_down(float(), float()) -> float(). +round_down(P, X) -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> math:floor(X * P) / Gleam@denominator + end. + +-spec round_up(float(), float()) -> float(). +round_up(P, X) -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> math:ceil(X * P) / Gleam@denominator + end. + +-spec float_absolute_value(float()) -> float(). +float_absolute_value(X) -> + case X > +0.0 of + true -> + X; + + false -> + -1.0 * X + end. + +-spec int_absolute_value(integer()) -> integer(). +int_absolute_value(X) -> + case X > 0 of + true -> + X; + + false -> + -1 * X + end. + +-spec float_absolute_difference(float(), float()) -> float(). +float_absolute_difference(A, B) -> + _pipe = A - B, + float_absolute_value(_pipe). + +-spec int_absolute_difference(integer(), integer()) -> integer(). +int_absolute_difference(A, B) -> + _pipe = A - B, + int_absolute_value(_pipe). + +-spec do_float_sign(float()) -> float(). +do_float_sign(X) -> + case X < +0.0 of + true -> + -1.0; + + false -> + case X =:= +0.0 of + true -> + +0.0; + + false -> + 1.0 + end + end. + +-spec float_sign(float()) -> float(). +float_sign(X) -> + do_float_sign(X). + +-spec round_to_nearest(float(), float()) -> float(). +round_to_nearest(P, X) -> + Xabs = float_absolute_value(X) * P, + Xabs_truncated = truncate_float(Xabs), + Remainder = Xabs - Xabs_truncated, + case Remainder of + _ when Remainder > 0.5 -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> (float_sign(X) * truncate_float(Xabs + 1.0)) + / Gleam@denominator + end; + + _ when Remainder =:= 0.5 -> + _assert_subject = gleam@int:modulo( + gleam_community@maths@conversion:float_to_int(Xabs), + 2 + ), + {ok, Is_even} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"round_to_nearest"/utf8>>, + line => 423}) + end, + case Is_even =:= 0 of + true -> + case P of + 0.0 -> 0.0; + Gleam@denominator@1 -> (float_sign(X) * Xabs_truncated) + / Gleam@denominator@1 + end; + + false -> + case P of + 0.0 -> 0.0; + Gleam@denominator@2 -> (float_sign(X) * truncate_float( + Xabs + 1.0 + )) + / Gleam@denominator@2 + end + end; + + _ -> + case P of + 0.0 -> 0.0; + Gleam@denominator@3 -> (float_sign(X) * Xabs_truncated) / Gleam@denominator@3 + end + end. + +-spec round_ties_away(float(), float()) -> float(). +round_ties_away(P, X) -> + Xabs = float_absolute_value(X) * P, + Remainder = Xabs - truncate_float(Xabs), + case Remainder of + _ when Remainder >= 0.5 -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> (float_sign(X) * truncate_float(Xabs + 1.0)) + / Gleam@denominator + end; + + _ -> + case P of + 0.0 -> 0.0; + Gleam@denominator@1 -> (float_sign(X) * truncate_float(Xabs)) / Gleam@denominator@1 + end + end. + +-spec round_ties_up(float(), float()) -> float(). +round_ties_up(P, X) -> + Xabs = float_absolute_value(X) * P, + Xabs_truncated = truncate_float(Xabs), + Remainder = Xabs - Xabs_truncated, + case Remainder of + _ when (Remainder >= 0.5) andalso (X >= +0.0) -> + case P of + 0.0 -> 0.0; + Gleam@denominator -> (float_sign(X) * truncate_float(Xabs + 1.0)) + / Gleam@denominator + end; + + _ -> + case P of + 0.0 -> 0.0; + Gleam@denominator@1 -> (float_sign(X) * Xabs_truncated) / Gleam@denominator@1 + end + end. + +-spec do_round(float(), float(), gleam@option:option(rounding_mode())) -> {ok, + float()} | + {error, binary()}. +do_round(P, X, Mode) -> + case Mode of + {some, round_nearest} -> + _pipe = round_to_nearest(P, X), + {ok, _pipe}; + + {some, round_ties_away} -> + _pipe@1 = round_ties_away(P, X), + {ok, _pipe@1}; + + {some, round_ties_up} -> + _pipe@2 = round_ties_up(P, X), + {ok, _pipe@2}; + + {some, round_to_zero} -> + _pipe@3 = round_to_zero(P, X), + {ok, _pipe@3}; + + {some, round_down} -> + _pipe@4 = round_down(P, X), + {ok, _pipe@4}; + + {some, round_up} -> + _pipe@5 = round_up(P, X), + {ok, _pipe@5}; + + none -> + _pipe@6 = round_to_nearest(P, X), + {ok, _pipe@6} + end. + +-spec round( + float(), + gleam@option:option(integer()), + gleam@option:option(rounding_mode()) +) -> {ok, float()} | {error, binary()}. +round(X, Digits, Mode) -> + case Digits of + {some, A} -> + _assert_subject = gleam_community@maths@elementary:power( + 10.0, + gleam_community@maths@conversion:int_to_float(A) + ), + {ok, P} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"round"/utf8>>, + line => 366}) + end, + do_round(P, X, Mode); + + none -> + do_round(1.0, X, Mode) + end. + +-spec ceiling(float(), gleam@option:option(integer())) -> {ok, float()} | + {error, binary()}. +ceiling(X, Digits) -> + round(X, Digits, {some, round_up}). + +-spec floor(float(), gleam@option:option(integer())) -> {ok, float()} | + {error, binary()}. +floor(X, Digits) -> + round(X, Digits, {some, round_down}). + +-spec truncate(float(), gleam@option:option(integer())) -> {ok, float()} | + {error, binary()}. +truncate(X, Digits) -> + round(X, Digits, {some, round_to_zero}). + +-spec do_int_sign(integer()) -> integer(). +do_int_sign(X) -> + case X < 0 of + true -> + -1; + + false -> + case X =:= 0 of + true -> + 0; + + false -> + 1 + end + end. + +-spec int_sign(integer()) -> integer(). +int_sign(X) -> + do_int_sign(X). + +-spec float_flip_sign(float()) -> float(). +float_flip_sign(X) -> + -1.0 * X. + +-spec float_copy_sign(float(), float()) -> float(). +float_copy_sign(X, Y) -> + case float_sign(X) =:= float_sign(Y) of + true -> + X; + + false -> + float_flip_sign(X) + end. + +-spec int_flip_sign(integer()) -> integer(). +int_flip_sign(X) -> + -1 * X. + +-spec int_copy_sign(integer(), integer()) -> integer(). +int_copy_sign(X, Y) -> + case int_sign(X) =:= int_sign(Y) of + true -> + X; + + false -> + int_flip_sign(X) + end. + +-spec minimum(FQL, FQL, fun((FQL, FQL) -> gleam@order:order())) -> FQL. +minimum(X, Y, Compare) -> + case Compare(X, Y) of + lt -> + X; + + eq -> + X; + + gt -> + Y + end. + +-spec maximum(FQM, FQM, fun((FQM, FQM) -> gleam@order:order())) -> FQM. +maximum(X, Y, Compare) -> + case Compare(X, Y) of + lt -> + Y; + + eq -> + Y; + + gt -> + X + end. + +-spec minmax(FQN, FQN, fun((FQN, FQN) -> gleam@order:order())) -> {FQN, FQN}. +minmax(X, Y, Compare) -> + {minimum(X, Y, Compare), maximum(X, Y, Compare)}. + +-spec list_minimum(list(FQO), fun((FQO, FQO) -> gleam@order:order())) -> {ok, + FQO} | + {error, binary()}. +list_minimum(Arr, Compare) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _assert_subject = gleam@list:at(Arr, 0), + {ok, Val0} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"list_minimum"/utf8>>, + line => 945}) + end, + _pipe@1 = Arr, + _pipe@2 = gleam@list:fold( + _pipe@1, + Val0, + fun(Acc, Element) -> case Compare(Element, Acc) of + lt -> + Element; + + _ -> + Acc + end end + ), + {ok, _pipe@2} + end. + +-spec list_maximum(list(FQS), fun((FQS, FQS) -> gleam@order:order())) -> {ok, + FQS} | + {error, binary()}. +list_maximum(Arr, Compare) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _assert_subject = gleam@list:at(Arr, 0), + {ok, Val0} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"list_maximum"/utf8>>, + line => 1004}) + end, + _pipe@1 = Arr, + _pipe@2 = gleam@list:fold( + _pipe@1, + Val0, + fun(Acc, Element) -> case Compare(Acc, Element) of + lt -> + Element; + + _ -> + Acc + end end + ), + {ok, _pipe@2} + end. + +-spec arg_minimum(list(FQW), fun((FQW, FQW) -> gleam@order:order())) -> {ok, + list(integer())} | + {error, binary()}. +arg_minimum(Arr, Compare) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _assert_subject = begin + _pipe@1 = Arr, + list_minimum(_pipe@1, Compare) + end, + {ok, Min} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"arg_minimum"/utf8>>, + line => 1069}) + end, + _pipe@2 = Arr, + _pipe@3 = gleam@list:index_map( + _pipe@2, + fun(Index, Element) -> case Compare(Element, Min) of + eq -> + Index; + + _ -> + -1 + end end + ), + _pipe@4 = gleam@list:filter(_pipe@3, fun(Index@1) -> case Index@1 of + -1 -> + false; + + _ -> + true + end end), + {ok, _pipe@4} + end. + +-spec arg_maximum(list(FRB), fun((FRB, FRB) -> gleam@order:order())) -> {ok, + list(integer())} | + {error, binary()}. +arg_maximum(Arr, Compare) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _assert_subject = begin + _pipe@1 = Arr, + list_maximum(_pipe@1, Compare) + end, + {ok, Max} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"arg_maximum"/utf8>>, + line => 1139}) + end, + _pipe@2 = Arr, + _pipe@3 = gleam@list:index_map( + _pipe@2, + fun(Index, Element) -> case Compare(Element, Max) of + eq -> + Index; + + _ -> + -1 + end end + ), + _pipe@4 = gleam@list:filter(_pipe@3, fun(Index@1) -> case Index@1 of + -1 -> + false; + + _ -> + true + end end), + {ok, _pipe@4} + end. + +-spec extrema(list(FRG), fun((FRG, FRG) -> gleam@order:order())) -> {ok, + {FRG, FRG}} | + {error, binary()}. +extrema(Arr, Compare) -> + case Arr of + [] -> + _pipe = <<"Invalid input argument: The list is empty."/utf8>>, + {error, _pipe}; + + _ -> + _assert_subject = gleam@list:at(Arr, 0), + {ok, Val_max} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"extrema"/utf8>>, + line => 1209}) + end, + _assert_subject@1 = gleam@list:at(Arr, 0), + {ok, Val_min} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/piecewise"/utf8>>, + function => <<"extrema"/utf8>>, + line => 1210}) + end, + _pipe@1 = Arr, + _pipe@2 = gleam@list:fold( + _pipe@1, + {Val_min, Val_max}, + fun(Acc, Element) -> + First = gleam@pair:first(Acc), + Second = gleam@pair:second(Acc), + case {Compare(Element, First), Compare(Second, Element)} of + {lt, lt} -> + {Element, Element}; + + {lt, _} -> + {Element, Second}; + + {_, lt} -> + {First, Element}; + + {_, _} -> + {First, Second} + end + end + ), + {ok, _pipe@2} + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@predicates.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@predicates.erl new file mode 100644 index 0000000..d991d89 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@predicates.erl @@ -0,0 +1,118 @@ +-module(gleam_community@maths@predicates). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([is_close/4, all_close/4, is_fractional/1, is_power/2, is_perfect/1, is_even/1, is_odd/1]). + +-spec float_absolute_value(float()) -> float(). +float_absolute_value(X) -> + case X > +0.0 of + true -> + X; + + false -> + -1.0 * X + end. + +-spec float_absolute_difference(float(), float()) -> float(). +float_absolute_difference(A, B) -> + _pipe = A - B, + float_absolute_value(_pipe). + +-spec is_close(float(), float(), float(), float()) -> boolean(). +is_close(A, B, Rtol, Atol) -> + X = float_absolute_difference(A, B), + Y = Atol + (Rtol * float_absolute_value(B)), + case X =< Y of + true -> + true; + + false -> + false + end. + +-spec all_close(list(float()), list(float()), float(), float()) -> {ok, + list(boolean())} | + {error, binary()}. +all_close(Xarr, Yarr, Rtol, Atol) -> + Xlen = gleam@list:length(Xarr), + Ylen = gleam@list:length(Yarr), + case Xlen =:= Ylen of + false -> + _pipe = <<"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."/utf8>>, + {error, _pipe}; + + true -> + _pipe@1 = gleam@list:zip(Xarr, Yarr), + _pipe@2 = gleam@list:map( + _pipe@1, + fun(Z) -> + is_close( + gleam@pair:first(Z), + gleam@pair:second(Z), + Rtol, + Atol + ) + end + ), + {ok, _pipe@2} + end. + +-spec is_fractional(float()) -> boolean(). +is_fractional(X) -> + (math:ceil(X) - X) > +0.0. + +-spec is_power(integer(), integer()) -> boolean(). +is_power(X, Y) -> + _assert_subject = gleam_community@maths@elementary:logarithm( + gleam@int:to_float(X), + {some, gleam@int:to_float(Y)} + ), + {ok, Value} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/predicates"/utf8>>, + function => <<"is_power"/utf8>>, + line => 241}) + end, + _assert_subject@1 = gleam_community@maths@piecewise:truncate( + Value, + {some, 0} + ), + {ok, Truncated} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/predicates"/utf8>>, + function => <<"is_power"/utf8>>, + line => 243}) + end, + Rem = Value - Truncated, + Rem =:= +0.0. + +-spec do_sum(list(integer())) -> integer(). +do_sum(Arr) -> + case Arr of + [] -> + 0; + + _ -> + _pipe = Arr, + gleam@list:fold(_pipe, 0, fun(Acc, A) -> A + Acc end) + end. + +-spec is_perfect(integer()) -> boolean(). +is_perfect(N) -> + do_sum(gleam_community@maths@arithmetics:proper_divisors(N)) =:= N. + +-spec is_even(integer()) -> boolean(). +is_even(X) -> + (X rem 2) =:= 0. + +-spec is_odd(integer()) -> boolean(). +is_odd(X) -> + (X rem 2) /= 0. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@sequences.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@sequences.erl new file mode 100644 index 0000000..74dcff4 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@sequences.erl @@ -0,0 +1,199 @@ +-module(gleam_community@maths@sequences). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([arange/3, linear_space/4, logarithmic_space/5, geometric_space/4]). + +-spec arange(float(), float(), float()) -> list(float()). +arange(Start, Stop, Step) -> + case ((Start >= Stop) andalso (Step > +0.0)) orelse ((Start =< Stop) andalso (Step + < +0.0)) of + true -> + []; + + false -> + Direction = case Start =< Stop of + true -> + 1.0; + + false -> + -1.0 + end, + Step_abs = gleam_community@maths@piecewise:float_absolute_value( + Step + ), + Num = case Step_abs of + 0.0 -> 0.0; + Gleam@denominator -> gleam_community@maths@piecewise:float_absolute_value( + Start - Stop + ) + / Gleam@denominator + end, + _pipe = gleam@list:range( + 0, + gleam_community@maths@conversion:float_to_int(Num) - 1 + ), + gleam@list:map( + _pipe, + fun(I) -> + Start + ((gleam_community@maths@conversion:int_to_float(I) * Step_abs) + * Direction) + end + ) + end. + +-spec linear_space(float(), float(), integer(), boolean()) -> {ok, + list(float())} | + {error, binary()}. +linear_space(Start, Stop, Num, Endpoint) -> + Direction = case Start =< Stop of + true -> + 1.0; + + false -> + -1.0 + end, + case Num > 0 of + true -> + case Endpoint of + true -> + Increment = case gleam_community@maths@conversion:int_to_float( + Num - 1 + ) of + 0.0 -> 0.0; + Gleam@denominator -> gleam_community@maths@piecewise:float_absolute_value( + Start - Stop + ) + / Gleam@denominator + end, + _pipe = gleam@list:range(0, Num - 1), + _pipe@1 = gleam@list:map( + _pipe, + fun(I) -> + Start + ((gleam_community@maths@conversion:int_to_float( + I + ) + * Increment) + * Direction) + end + ), + {ok, _pipe@1}; + + false -> + Increment@1 = case gleam_community@maths@conversion:int_to_float( + Num + ) of + 0.0 -> 0.0; + Gleam@denominator@1 -> gleam_community@maths@piecewise:float_absolute_value( + Start - Stop + ) + / Gleam@denominator@1 + end, + _pipe@2 = gleam@list:range(0, Num - 1), + _pipe@3 = gleam@list:map( + _pipe@2, + fun(I@1) -> + Start + ((gleam_community@maths@conversion:int_to_float( + I@1 + ) + * Increment@1) + * Direction) + end + ), + {ok, _pipe@3} + end; + + false -> + _pipe@4 = <<"Invalid input: num < 0. Valid input is num > 0."/utf8>>, + {error, _pipe@4} + end. + +-spec logarithmic_space(float(), float(), integer(), boolean(), float()) -> {ok, + list(float())} | + {error, binary()}. +logarithmic_space(Start, Stop, Num, Endpoint, Base) -> + case Num > 0 of + true -> + _assert_subject = linear_space(Start, Stop, Num, Endpoint), + {ok, Linspace} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/sequences"/utf8>>, + function => <<"logarithmic_space"/utf8>>, + line => 221}) + end, + _pipe = Linspace, + _pipe@1 = gleam@list:map( + _pipe, + fun(I) -> + _assert_subject@1 = gleam_community@maths@elementary:power( + Base, + I + ), + {ok, Result} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/sequences"/utf8>>, + function => <<"logarithmic_space"/utf8>>, + line => 224}) + end, + Result + end + ), + {ok, _pipe@1}; + + false -> + _pipe@2 = <<"Invalid input: num < 0. Valid input is num > 0."/utf8>>, + {error, _pipe@2} + end. + +-spec geometric_space(float(), float(), integer(), boolean()) -> {ok, + list(float())} | + {error, binary()}. +geometric_space(Start, Stop, Num, Endpoint) -> + case (Start =:= +0.0) orelse (Stop =:= +0.0) of + true -> + _pipe = <<""/utf8>>, + {error, _pipe}; + + false -> + case Num > 0 of + true -> + _assert_subject = gleam_community@maths@elementary:logarithm_10( + Start + ), + {ok, Log_start} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/sequences"/utf8>>, + function => <<"geometric_space"/utf8>>, + line => 293}) + end, + _assert_subject@1 = gleam_community@maths@elementary:logarithm_10( + Stop + ), + {ok, Log_stop} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/sequences"/utf8>>, + function => <<"geometric_space"/utf8>>, + line => 294}) + end, + logarithmic_space(Log_start, Log_stop, Num, Endpoint, 10.0); + + false -> + _pipe@1 = <<"Invalid input: num < 0. Valid input is num > 0."/utf8>>, + {error, _pipe@1} + end + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@special.erl b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@special.erl new file mode 100644 index 0000000..925f4bb --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@special.erl @@ -0,0 +1,157 @@ +-module(gleam_community@maths@special). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([erf/1, gamma/1, beta/2, incomplete_gamma/2]). + +-spec erf(float()) -> float(). +erf(X) -> + _assert_subject = [0.254829592, + -0.284496736, + 1.421413741, + -1.453152027, + 1.061405429], + [A1, A2, A3, A4, A5] = case _assert_subject of + [_, _, _, _, _] -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/special"/utf8>>, + function => <<"erf"/utf8>>, + line => 79}) + end, + P = 0.3275911, + Sign = gleam_community@maths@piecewise:float_sign(X), + X@1 = gleam_community@maths@piecewise:float_absolute_value(X), + T = case (1.0 + (P * X@1)) of + 0.0 -> 0.0; + Gleam@denominator -> 1.0 / Gleam@denominator + end, + Y = 1.0 - ((((((((((A5 * T) + A4) * T) + A3) * T) + A2) * T) + A1) * T) * gleam_community@maths@elementary:exponential( + (-1.0 * X@1) * X@1 + )), + Sign * Y. + +-spec gamma_lanczos(float()) -> float(). +gamma_lanczos(X) -> + case X < 0.5 of + true -> + case (gleam_community@maths@elementary:sin( + gleam_community@maths@elementary:pi() * X + ) + * gamma_lanczos(1.0 - X)) of + 0.0 -> 0.0; + Gleam@denominator -> gleam_community@maths@elementary:pi() / Gleam@denominator + end; + + false -> + Z = X - 1.0, + X@1 = gleam@list:index_fold( + [0.99999999999980993, + 676.5203681218851, + -1259.1392167224028, + 771.32342877765313, + -176.61502916214059, + 12.507343278686905, + -0.13857109526572012, + 0.0000099843695780195716, + 0.00000015056327351493116], + +0.0, + fun(Acc, V, Index) -> case Index > 0 of + true -> + Acc + (case (Z + gleam_community@maths@conversion:int_to_float( + Index + )) of + 0.0 -> 0.0; + Gleam@denominator@1 -> V / Gleam@denominator@1 + end); + + false -> + V + end end + ), + T = (Z + 7.0) + 0.5, + _assert_subject = gleam_community@maths@elementary:power( + 2.0 * gleam_community@maths@elementary:pi(), + 0.5 + ), + {ok, V1} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/special"/utf8>>, + function => <<"gamma_lanczos"/utf8>>, + line => 146}) + end, + _assert_subject@1 = gleam_community@maths@elementary:power( + T, + Z + 0.5 + ), + {ok, V2} = case _assert_subject@1 of + {ok, _} -> _assert_subject@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail@1, + module => <<"gleam_community/maths/special"/utf8>>, + function => <<"gamma_lanczos"/utf8>>, + line => 147}) + end, + ((V1 * V2) * gleam_community@maths@elementary:exponential(-1.0 * T)) + * X@1 + end. + +-spec gamma(float()) -> float(). +gamma(X) -> + gamma_lanczos(X). + +-spec beta(float(), float()) -> float(). +beta(X, Y) -> + case gamma(X + Y) of + 0.0 -> 0.0; + Gleam@denominator -> (gamma(X) * gamma(Y)) / Gleam@denominator + end. + +-spec incomplete_gamma_sum(float(), float(), float(), float(), float()) -> float(). +incomplete_gamma_sum(A, X, T, S, N) -> + case T of + +0.0 -> + S; + + _ -> + Ns = S + T, + Nt = T * (case (A + N) of + 0.0 -> 0.0; + Gleam@denominator -> X / Gleam@denominator + end), + incomplete_gamma_sum(A, X, Nt, Ns, N + 1.0) + end. + +-spec incomplete_gamma(float(), float()) -> {ok, float()} | {error, binary()}. +incomplete_gamma(A, X) -> + case (A > +0.0) andalso (X >= +0.0) of + true -> + _assert_subject = gleam_community@maths@elementary:power(X, A), + {ok, V} = case _assert_subject of + {ok, _} -> _assert_subject; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Assertion pattern match failed"/utf8>>, + value => _assert_fail, + module => <<"gleam_community/maths/special"/utf8>>, + function => <<"incomplete_gamma"/utf8>>, + line => 173}) + end, + _pipe = (V * gleam_community@maths@elementary:exponential(-1.0 * X)) + * incomplete_gamma_sum(A, X, case A of + 0.0 -> 0.0; + Gleam@denominator -> 1.0 / Gleam@denominator + end, +0.0, 1.0), + {ok, _pipe}; + + false -> + _pipe@1 = <<"Invlaid input argument: a <= 0 or x < 0. Valid input is a > 0 and x >= 0."/utf8>>, + {error, _pipe@1} + end. diff --git a/aoc2023/build/packages/gleam_community_maths/src/gleam_community_maths.app.src b/aoc2023/build/packages/gleam_community_maths/src/gleam_community_maths.app.src new file mode 100644 index 0000000..091e679 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/gleam_community_maths.app.src @@ -0,0 +1,16 @@ +{application, gleam_community_maths, [ + {vsn, "1.0.1"}, + {applications, [gleam_stdlib, + gleeunit]}, + {description, "A basic maths library"}, + {modules, [gleam_community@maths@arithmetics, + gleam_community@maths@combinatorics, + gleam_community@maths@conversion, + gleam_community@maths@elementary, + gleam_community@maths@metrics, + gleam_community@maths@piecewise, + gleam_community@maths@predicates, + gleam_community@maths@sequences, + gleam_community@maths@special]}, + {registered, []} +]}. diff --git a/aoc2023/build/packages/gleam_community_maths/src/maths.mjs b/aoc2023/build/packages/gleam_community_maths/src/maths.mjs new file mode 100644 index 0000000..5c5ab31 --- /dev/null +++ b/aoc2023/build/packages/gleam_community_maths/src/maths.mjs @@ -0,0 +1,95 @@ +export function sin(float) { + return Math.sin(float) +} + +export function pi() { + return Math.PI +} + +export function acos(float) { + return Math.acos(float) +} + +export function acosh(float) { + return Math.acosh(float) +} + +export function asin(float) { + return Math.asin(float) +} + +export function asinh(float) { + return Math.asinh(float) +} + +export function atan(float) { + return Math.atan(float) +} + +export function tan(float) { + return Math.tan(float) +} + +export function atan2(floaty, floatx) { + return Math.atan2(floaty, floatx) +} + +export function atanh(float) { + return Math.atanh(float) +} + +export function cos(float) { + return Math.cos(float) +} + +export function cosh(float) { + return Math.cosh(float) +} + +export function exponential(float) { + return Math.exp(float) +} + +export function ceiling(float) { + return Math.ceil(float) +} + +export function floor(float) { + return Math.floor(float) +} + +export function power(base, exponent) { + return Math.pow(base, exponent) +} + +export function logarithm(float) { + return Math.log(float) +} + +export function logarithm_10(float) { + return Math.log10(float) +} + +export function logarithm_2(float) { + return Math.log2(float) +} + +export function sinh(float) { + return Math.sinh(float) +} + +export function tanh(float) { + return Math.tanh(float) +} + +export function sign(float) { + return Math.sign(float) +} + +export function truncate(float) { + return Math.trunc(float) +} + +export function to_int(float) { + return Math.trunc(float) +} |