aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/build/packages/gleam_community_maths/src
diff options
context:
space:
mode:
authorJ.J <thechairman@thechairman.info>2024-05-30 21:50:02 -0400
committerJ.J <thechairman@thechairman.info>2024-05-30 21:50:02 -0400
commit612fd986ab1e00b6d34dc1937136250e08e89325 (patch)
treea3c93952040c6afdf348b5831619a45db7ba0a2e /aoc2023/build/packages/gleam_community_maths/src
parent231c2b688d1e6cf0846d46e883da30e042a9c6cf (diff)
downloadgleam_aoc-612fd986ab1e00b6d34dc1937136250e08e89325.tar.gz
gleam_aoc-612fd986ab1e00b6d34dc1937136250e08e89325.zip
cleanup
Diffstat (limited to 'aoc2023/build/packages/gleam_community_maths/src')
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/arithmetics.gleam618
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/combinatorics.gleam432
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/conversion.gleam183
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/elementary.gleam1256
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/metrics.gleam560
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/piecewise.gleam1228
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/predicates.gleam363
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/sequences.gleam302
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community/maths/special.gleam205
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@arithmetics.erl172
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@combinatorics.erl218
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@conversion.erl23
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@elementary.erl284
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@metrics.erl278
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@piecewise.erl553
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@predicates.erl118
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@sequences.erl199
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community@maths@special.erl157
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/gleam_community_maths.app.src16
-rw-r--r--aoc2023/build/packages/gleam_community_maths/src/maths.mjs95
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)
+}