diff --git a/README.md b/README.md
index ba95f0d..153dd9d 100644
--- a/README.md
+++ b/README.md
@@ -11,46 +11,44 @@ The library supports both targets: Erlang and JavaScript.
 
 ```gleam
 import gleam/float
-import gleam/iterator
-import gleam/option.{Some}
-import gleam_community/maths/arithmetics
-import gleam_community/maths/combinatorics.{WithoutRepetitions}
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-import gleam_community/maths/predicates
+import gleam/yielder
+import gleam_community/maths
 import gleeunit/should
 
 pub fn example() {
   // Evaluate the sine function
-  let result = elementary.sin(elementary.pi())
+  let result = maths.sin(maths.pi())
 
   // Set the relative and absolute tolerance
-  let assert Ok(absolute_tol) = elementary.power(10.0, -6.0)
+  let assert Ok(absolute_tol) = float.power(10.0, -6.0)
   let relative_tol = 0.0
 
   // Check that the value is very close to 0.0
   // That is, if 'result' is within +/- 10^(-6)
-  predicates.is_close(result, 0.0, relative_tol, absolute_tol)
+  maths.is_close(result, 0.0, relative_tol, absolute_tol)
   |> should.be_true()
 
   // Find the greatest common divisor
-  arithmetics.gcd(54, 24)
+  maths.gcd(54, 24)
   |> should.equal(6)
 
   // Find the minimum and maximum of a list
-  piecewise.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
+  maths.extrema([10.0, 3.0, 50.0, 20.0, 3.0], float.compare)
   |> should.equal(Ok(#(3.0, 50.0)))
 
   // Determine if a number is fractional
-  predicates.is_fractional(0.3333)
+  maths.is_fractional(0.3333)
   |> should.equal(True)
 
   // Generate all k = 2 combinations of [1, 2, 3]
-  let assert Ok(combinations) =
-    combinatorics.list_combination([1, 2, 3], 2, Some(WithoutRepetitions))
+  let assert Ok(combinations) = maths.list_combination([1, 2, 3], 2)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1, 2], [1, 3], [2, 3]])
+
+  // Compute the Cosine Similarity between two (orthogonal) vectors
+  maths.cosine_similarity([#(-1.0, 1.0), #(1.0, 1.0), #(0.0, -1.0)])
+  |> should.equal(Ok(0.0))
 }
 
 ```
diff --git a/gleam.toml b/gleam.toml
index df0dc71..0a6ade1 100644
--- a/gleam.toml
+++ b/gleam.toml
@@ -8,6 +8,7 @@ gleam = ">= 0.32.0"
 
 [dependencies]
 gleam_stdlib = "~> 0.38"
+gleam_yielder = ">= 1.1.0 and < 2.0.0"
 
 [dev-dependencies]
 gleeunit = "~> 1.0"
diff --git a/manifest.toml b/manifest.toml
index adad39a..ea3a357 100644
--- a/manifest.toml
+++ b/manifest.toml
@@ -2,10 +2,12 @@
 # You typically do not need to edit this file
 
 packages = [
-  { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
+  { name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" },
+  { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
   { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
 ]
 
 [requirements]
 gleam_stdlib = { version = "~> 0.38" }
+gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" }
 gleeunit = { version = "~> 1.0" }
diff --git a/src/gleam_community/maths.gleam b/src/gleam_community/maths.gleam
new file mode 100644
index 0000000..b5a76a4
--- /dev/null
+++ b/src/gleam_community/maths.gleam
@@ -0,0 +1,5675 @@
+////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
+////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
+////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
+////<script>
+////    document.addEventListener("DOMContentLoaded", function() {
+////        renderMathInElement(document.body, {
+////          // customised options
+////          // • auto-render specific keys, e.g.:
+////          delimiters: [
+////              {left: '$$', right: '$$', display: false},
+////              {left: '$', right: '$', display: false},
+////              {left: '\\(', right: '\\)', display: false},
+////              {left: '\\[', right: '\\]', display: true}
+////          ],
+////          // • rendering keys, e.g.:
+////          throwOnError : true
+////        });
+////    });
+////</script>
+////<style>
+////    .katex { font-size: 1.10em; }
+////</style>
+////
+//// ---
+//// 
+
+import gleam/bool
+import gleam/float
+import gleam/int
+import gleam/list
+import gleam/order
+import gleam/result
+import gleam/set
+import gleam/yielder.{type Yielder, Done, Next}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function calculates the greatest common divisor of two integers
+/// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive
+/// integer that is divisible by both \\(x\\) and \\(y\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.gcd(1, 1)
+///       |> should.equal(1)
+///       
+///       maths.gcd(100, 10)
+///       |> should.equal(10)
+///     
+///       maths.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.absolute_value(x)
+  let absy = int.absolute_value(y)
+  do_gcd(absx, absy)
+}
+
+fn do_gcd(x: Int, y: Int) -> Int {
+  case x == 0 {
+    True -> y
+    False -> {
+      let assert Ok(z) = int.modulo(y, x)
+      do_gcd(z, x)
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+///
+/// Given two integers, \\(x\\) (dividend) and \\(y\\) (divisor), the Euclidean modulo
+/// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the
+/// division of \\(x\\) by \\(y\\), such that:
+///
+/// \\[
+/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|,
+/// \\]
+///
+/// where \\(q\\) is an integer that represents the quotient of the division.
+///
+/// Note that like the Gleam division operator `/` this will return `0` if one of
+/// the arguments is `0`.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.int_euclidean_modulo(15, 4)
+///       |> should.equal(3)
+///
+///       maths.int_euclidean_modulo(-3, -2)
+///       |> should.equal(1)
+///
+///       maths.int_euclidean_modulo(5, 0)
+///       |> should.equal(0)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
+  case x % y, x, y {
+    _, 0, _ -> 0
+    _, _, 0 -> 0
+    md, _, _ if md < 0 -> md + int.absolute_value(y)
+    md, _, _ -> md
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function calculates the least common multiple of two integers
+/// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive
+/// integer that has both \\(x\\) and \\(y\\) as factors.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.lcm(1, 1)
+///       |> should.equal(1)
+///
+///       maths.lcm(100, 10)
+///       |> should.equal(100)
+///
+///       maths.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.absolute_value(x)
+  let absy = int.absolute_value(y)
+  absx * absy / do_gcd(absx, absy)
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns all the positive divisors of an integer, including the
+/// number itself.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.divisors(4)
+///       |> should.equal([1, 2, 4])
+///
+///       maths.divisors(6)
+///       |> should.equal([1, 2, 3, 6])
+///
+///       maths.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) {
+  let nabs = float.absolute_value(int.to_float(n))
+  let assert Ok(sqrt_result) = float.square_root(nabs)
+  let max = float.round(sqrt_result) + 1
+  find_divisors(n, max, [1, n], 2)
+  |> list.unique()
+  |> list.sort(int.compare)
+}
+
+fn find_divisors(n: Int, max: Int, acc: List(Int), i: Int) -> List(Int) {
+  case i <= max {
+    True -> {
+      let acc = case n % i == 0 {
+        True -> [i, n / i, ..acc]
+        False -> acc
+      }
+      find_divisors(n, max, acc, i + 1)
+    }
+    False -> acc
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns all the positive divisors of an integer, excluding the
+/// number iteself.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.proper_divisors(4)
+///       |> should.equal([1, 2])
+///
+///       maths.proper_divisors(6)
+///       |> should.equal([1, 2, 3])
+///
+///       maths.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 nabs = float.absolute_value(int.to_float(n))
+  let assert Ok(sqrt_result) = float.square_root(nabs)
+  let max = float.round(sqrt_result) + 1
+  let divisors =
+    find_divisors(n, max, [1, n], 2)
+    |> list.unique()
+    |> list.sort(int.compare)
+
+  divisors
+  |> list.take(list.length(divisors) - 1)
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted sum of the elements in a list:
+///
+/// \\[
+/// \sum_{i=1}^n w_i x_i
+/// \\]
+///
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
+/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
+/// are corresponding positive weights.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.float_weighted_sum()
+///       |> should.equal(Ok(0.0))
+///   
+///       [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
+///       |> maths.float_weighted_sum()
+///       |> should.equal(Ok(6.0))
+///   
+///       [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
+///       |> maths.float_weighted_sum()
+///       |> should.equal(Ok(14.5))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn float_weighted_sum(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  case arr {
+    [] -> Ok(0.0)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
+      case weight_is_negative {
+        True -> Error(Nil)
+        False -> {
+          let weighted_sum =
+            list.fold(arr, 0.0, fn(acc, a) { a.0 *. a.1 +. acc })
+          Ok(weighted_sum)
+        }
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted product of the elements in a list:
+///
+/// \\[
+/// \prod_{i=1}^n x_i^{w_i}
+/// \\]
+///
+/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
+/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
+/// are corresponding positive weights.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///     
+///     import gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.float_weighted_product()
+///       |> should.equal(Ok(1.0))
+///   
+///       [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
+///       |> maths.float_weighted_product()
+///       |> should.equal(Ok(6.0))
+///       
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(result) =
+///         [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
+///         |> maths.float_weighted_product()
+///       result
+///       |> maths.is_close(30.0, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn float_weighted_product(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  case arr {
+    [] -> Ok(1.0)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
+      case weight_is_negative {
+        True -> Error(Nil)
+        False -> {
+          list.map(arr, fn(a: #(Float, Float)) -> Result(Float, Nil) {
+            float.power(a.0, a.1)
+          })
+          |> result.all
+          |> result.map(fn(prods) {
+            prods
+            |> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
+          })
+        }
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the 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
+///
+///     pub fn example () {
+///       []
+///       |> maths.float_cumulative_sum()
+///       |> should.equal([])
+///
+///       [1.0, 2.0, 3.0]
+///       |> maths.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 {
+    [] -> []
+    _ -> list.scan(arr, 0.0, fn(acc, a) { a +. acc })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the cumulative sum of the elements in a list:
+///
+/// \\[
+/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
+/// \\]
+///
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
+/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\)
+/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
+/// sum of the \\(1\\) to \\(j\\) first elements in the given list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.int_cumulative_sum()
+///       |> should.equal([])
+///
+///       [1, 2, 3]
+///       |> maths.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 {
+    [] -> []
+    _ -> list.scan(arr, 0, fn(acc, a) { a + acc })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the cumulative product of the elements in a list:
+///
+/// \\[
+/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
+/// \\]
+///
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
+/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
+/// \\(x_i \in \mathbb{R}\\) is the value in the input list indexed by \\(i\\). The
+/// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the
+/// given list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.float_cumulative_product()
+///       |> should.equal([])
+///
+///       [1.0, 2.0, 3.0]
+///       |> maths.float_cumulative_product()
+///       |> should.equal([1.0, 2.0, 6.0])
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
+  case arr {
+    [] -> []
+    _ -> list.scan(arr, 1.0, fn(acc, a) { a *. acc })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the cumulative product of the elements in a list:
+///
+/// \\[
+/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
+/// \\]
+///
+/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
+/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
+/// \\(x_i \in \mathbb{Z}\\) is the value in the input list indexed by \\(i\\). The
+/// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the
+/// given list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.int_cumulative_product()
+///       |> should.equal([])
+///
+///       [1, 2, 3]
+///       |> maths.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 {
+    [] -> []
+    _ -> list.scan(arr, 1, fn(acc, a) { a * acc })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// 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
+///
+///     pub fn example() {
+///       maths.degrees_to_radians(360.)
+///       |> should.equal(2. *. maths.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
+///
+///     pub fn example() {
+///       maths.radians_to_degrees(0.0)
+///       |> should.equal(0.0)
+///
+///       maths.radians_to_degrees(2. *. maths.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()
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Converts polar coordinates \\((r, \theta)\\) to Cartesian coordinates \\((x, y)\\).
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.polar_to_cartesian(1.0, 0.0)
+///       |> should.equal(#(1.0, 0.0))
+///
+///       maths.polar_to_cartesian(1.0, float.pi() /. 2.0)
+///       |> should.equal(#(0.0, 1.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn polar_to_cartesian(r: Float, theta: Float) -> #(Float, Float) {
+  // Calculate x and y
+  let x = r *. cos(theta)
+  let y = r *. sin(theta)
+  #(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>
+///
+/// Converts Cartesian coordinates \\((x, y)\\) to polar coordinates \\((r, \theta)\\).
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.cartesian_to_polar(1.0, 0.0)
+///       |> should.equal((1.0, 0.0))
+///
+///       maths.cartesian_to_polar(0.0, 1.0)
+///       |> should.equal((1.0, float.pi() /. 2.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn cartesian_to_polar(x: Float, y: Float) -> #(Float, Float) {
+  // Calculate r and theta
+  let assert Ok(r) = float.power(x *. x +. y *. y, 1.0 /. 2.0)
+  let theta = atan2(y, x)
+  #(r, theta)
+}
+
+/// <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
+///
+///     pub fn example() {
+///       maths.acos(1.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.acos(1.1)
+///       |> should.be_error()
+///
+///       maths.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, Nil) {
+  case x >=. -1.0 && x <=. 1.0 {
+    True -> Ok(do_acos(x))
+    False -> Error(Nil)
+  }
+}
+
+@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
+///
+///     pub fn example() {
+///       maths.acosh(1.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.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, Nil) {
+  case x >=. 1.0 {
+    True -> Ok(do_acosh(x))
+    False -> Error(Nil)
+  }
+}
+
+@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
+///
+///     pub fn example() {
+///       maths.asin(0.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.asin(1.1)
+///       |> should.be_error()
+///
+///       maths.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, Nil) {
+  case x >=. -1.0 && x <=. 1.0 {
+    True -> Ok(do_asin(x))
+    False -> Error(Nil)
+  }
+}
+
+@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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example() {
+///       maths.atanh(0.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.atanh(1.0)
+///       |> should.be_error()
+///
+///       maths.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, Nil) {
+  case x >. -1.0 && x <. 1.0 {
+    True -> Ok(do_atanh(x))
+    False -> Error(Nil)
+  }
+}
+
+@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
+///
+///     pub fn example() {
+///       maths.cos(0.0)
+///       |> should.equal(1.0)
+///
+///       maths.cos(maths.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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example() {
+///       maths.sin(0.0)
+///       |> should.equal(0.0)
+///
+///       maths.sin(0.5 *. maths.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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example () {
+///       maths.tanh(0.0)
+///       |> should.equal(0.0)
+///
+///       maths.tanh(25.0)
+///       |> should.equal(1.0)
+///
+///       maths.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\)
+/// \\]
+///
+/// where \\(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
+///
+///     pub fn example() {
+///       maths.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
+///
+///     pub fn example () {
+///       maths.natural_logarithm(1.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.natural_logarithm(maths.e())
+///       |> should.equal(Ok(1.0))
+///
+///       maths.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, Nil) {
+  case x >. 0.0 {
+    True -> Ok(do_natural_logarithm(x))
+    False -> Error(Nil)
+  }
+}
+
+@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_community/maths
+///
+///     pub fn example () {
+///       maths.logarithm(1.0, 10.0)
+///       |> should.equal(Ok(0.0))
+///     
+///       maths.logarithm(maths.e(), maths.e())
+///       |> should.equal(Ok(1.0))
+///     
+///       maths.logarithm(-1.0, 2.0)
+///       |> should.be_error()
+///     }
+/// </details>
+///
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn logarithm(x: Float, base: Float) -> Result(Float, Nil) {
+  case x >. 0.0 {
+    True ->
+      case base >. 0.0 && base != 1.0 {
+        False -> Error(Nil)
+        True -> {
+          // Apply the "change of base formula"
+          let assert Ok(numerator) = logarithm_10(x)
+          let assert Ok(denominator) = logarithm_10(base)
+          Ok(numerator /. denominator)
+        }
+      }
+    _ -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The 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
+///
+///     pub fn example () {
+///       maths.logarithm_2(1.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.logarithm_2(2.0)
+///       |> should.equal(Ok(1.0))
+///
+///       maths.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, Nil) {
+  case x >. 0.0 {
+    True -> Ok(do_logarithm_2(x))
+    False -> Error(Nil)
+  }
+}
+
+@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
+///
+///     pub fn example () {
+///       maths.logarithm_10(1.0)
+///       |> should.equal(Ok(0.0))
+///
+///       maths.logarithm_10(10.0)
+///       |> should.equal(Ok(1.0))
+///
+///       maths.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, Nil) {
+  case x >. 0.0 {
+    True -> Ok(do_logarithm_10(x))
+    False -> Error(Nil)
+  }
+}
+
+@external(erlang, "math", "log10")
+@external(javascript, "../maths.mjs", "logarithm_10")
+fn do_logarithm_10(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 \\(n\\)'th root function: \\(y = \sqrt[n]{x} = x^{\frac{1}{n}}\\).
+///
+/// Note that the function is not defined if 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
+///
+///     pub fn example() {
+///       maths.nth_root(-1.0, 2)
+///       |> should.be_error()
+/// 
+///       maths.nth_root(1.0, 2)
+///       |> should.equal(Ok(1.0))
+///
+///       maths.nth_root(27.0, 3)
+///       |> should.equal(Ok(3.0))
+///
+///       maths.nth_root(256.0, 4)
+///       |> should.equal(Ok(4.0))
+///     }
+/// </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, Nil) {
+  // 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 && n >= 1 {
+    True -> float.power(x, 1.0 /. int.to_float(n))
+    False -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The 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 (circle) 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>
+///
+/// The golden ratio: \\(\phi = \frac{1 + \sqrt{5}}{2}\\).
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.golden_ratio()
+///       |> should.equal(1.618033988749895)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn golden_ratio() -> Float {
+  // Calculate the golden ratio: (1 + sqrt(5)) / 2
+  let assert Ok(sqrt5) = float.power(5.0, 1.0 /. 2.0)
+  { 1.0 +. sqrt5 } /. 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>
+///
+/// Euler's number \\(e \approx 2.71828\dots\\).
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       // Test that the constant is approximately equal to 2.7128...
+///       maths.e()
+///       |> maths.is_close(2.7182818284590452353602, 0.0, tolerance)
+///       |> 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)
+}
+
+/// <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). In particular, 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.
+///
+/// <details>
+/// <summary>Details</summary>
+///
+///   The 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`)
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.round_to_nearest(12.0654, 2)
+///       |> should.equal(12.07)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_to_nearest(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  let xabs = float.absolute_value(x) *. p
+  let xabs_truncated = truncate_float(xabs)
+  let remainder = 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(float.truncate(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
+  }
+}
+
+/// <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). In particular, 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 behaviour).
+///
+/// <details>
+/// <summary>Details</summary>
+///
+///   The 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`)
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.round_ties_away(12.0654, 2)
+///       |> should.equal(12.07)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_ties_away(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  let xabs = float.absolute_value(x) *. p
+  let remainder = 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
+  }
+}
+
+/// <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). In particular, 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).
+///
+/// <details>
+/// <summary>Details</summary>
+///
+///   The 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`)
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.round_ties_up(12.0654, 2)
+///       |> should.equal(12.07)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_ties_up(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  let xabs = float.absolute_value(x) *. p
+  let xabs_truncated = truncate_float(xabs)
+  let remainder = 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
+  }
+}
+
+/// <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). In particular, 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\\). This 
+/// rounding behaviour is similar to behaviour of the Gleam stdlib `truncate` function.
+///
+/// <details>
+/// <summary>Details</summary>
+///
+///   The 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`)
+///
+/// </details>
+/// 
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.round_to_zero(12.0654, 2)
+///       |> should.equal(12.06)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_to_zero(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  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
+
+/// <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). In particular, the input \\(x\\) is rounded to the nearest integer value (at the 
+/// specified digit) that is less than or equal to the input \\(x\\). This rounding behaviour is 
+/// similar to behaviour of the Gleam stdlib `floor` function.
+/// 
+/// <details>
+/// <summary>Details</summary>
+/// 
+///   The 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`)
+///
+/// </details>
+/// 
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.round_down(12.0654, 2)
+///       |> should.equal(12.06)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_down(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  do_floor(x *. p) /. p
+}
+
+@external(erlang, "math", "floor")
+@external(javascript, "../maths.mjs", "floor")
+fn do_floor(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 rounds a float to a specific number of digits (after the decimal place or before
+/// if negative). In particular, the input \\(x\\) is rounded to the nearest integer value (at the 
+/// specified digit) that is larger than or equal to the input \\(x\\). This rounding behaviour is 
+/// similar to behaviour of the Gleam stdlib `ceiling` function.
+/// 
+/// <details>
+/// <summary>Details</summary>
+/// 
+///   The 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_community/maths
+///
+///     pub fn example() {
+///       maths.round_up(12.0654, 2)
+///       |> should.equal(12.07)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn round_up(x: Float, p: Int) -> Float {
+  let assert Ok(p) = float.power(10.0, int.to_float(p))
+  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 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 absolute difference of the inputs.
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.float_absolute_difference(-10.0, 10.0)
+///       |> should.equal(20.0)
+///
+///       maths.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
+///
+///     pub fn example() {
+///       maths.absolute_difference(-10, 10)
+///       |> should.equal(20)
+///
+///       maths.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 {
+    _ if x <. 0.0 -> -1.0
+    _ if x >. 0.0 -> 1.0
+    _ -> 0.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 {
+    _ if x < 0 -> -1
+    _ if x > 0 -> 1
+    _ -> 0
+  }
+}
+
+@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 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 gleam/float
+///     import gleam/int
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.minmax(2.0, 1.5, float.compare)
+///       |> should.equal(#(1.5, 2.0))
+///
+///       maths.minmax(1, 2, int.compare)
+///       |> should.equal(#(1, 2))
+///     }
+/// </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) {
+  case compare(x, y) {
+    order.Lt -> #(x, y)
+    order.Eq -> #(x, y)
+    order.Gt -> #(y, 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>
+///
+/// Returns the minimum value of a given list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/int
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.list_minimum(int.compare)
+///       |> should.be_error()
+///
+///       [4, 4, 3, 2, 1]
+///       |> maths.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, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    [x, ..rest] ->
+      Ok(
+        list.fold(rest, x, fn(acc, element) {
+          case compare(element, acc) {
+            order.Lt -> element
+            _ -> acc
+          }
+        }),
+      )
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Returns the maximum value of a given list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.list_maximum(float.compare)
+///       |> should.be_error()
+///
+///       [4.0, 4.0, 3.0, 2.0, 1.0]
+///       |> maths.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, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    [x, ..rest] ->
+      Ok(
+        list.fold(rest, x, fn(acc, element) {
+          case compare(acc, element) {
+            order.Lt -> element
+            _ -> acc
+          }
+        }),
+      )
+  }
+}
+
+/// <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 gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.arg_minimum(float.compare)
+///       |> should.be_error()
+///
+///       [4.0, 4.0, 3.0, 2.0, 1.0]
+///       |> maths.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), Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let assert Ok(min) = list_minimum(arr, compare)
+      Ok(
+        list.index_map(arr, fn(element, index) {
+          case compare(element, min) {
+            order.Eq -> index
+            _ -> -1
+          }
+        })
+        |> list.filter(fn(index) {
+          case index {
+            -1 -> False
+            _ -> True
+          }
+        }),
+      )
+    }
+  }
+}
+
+/// <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 gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.arg_maximum(float.compare)
+///       |> should.be_error()
+///
+///       [4.0, 4.0, 3.0, 2.0, 1.0]
+///       |> maths.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), Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let assert Ok(max) = list_maximum(arr, compare)
+      Ok(
+        list.index_map(arr, fn(element, index) {
+          case compare(element, max) {
+            order.Eq -> index
+            _ -> -1
+          }
+        })
+        |> list.filter(fn(index) {
+          case index {
+            -1 -> False
+            _ -> True
+          }
+        }),
+      )
+    }
+  }
+}
+
+/// <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 gleam/float
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.extrema(float.compare)
+///       |> should.be_error()
+///
+///       [4.0, 4.0, 3.0, 2.0, 1.0]
+///       |> maths.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), Nil) {
+  case arr {
+    [] -> Error(Nil)
+    [x, ..rest] ->
+      Ok(
+        list.fold(rest, #(x, x), fn(acc, element) {
+          let first = acc.0
+          let second = acc.1
+          case compare(element, first), compare(second, element) {
+            order.Lt, order.Lt -> #(element, element)
+            order.Lt, _ -> #(element, second)
+            _, order.Lt -> #(first, element)
+            _, _ -> #(first, second)
+          }
+        }),
+      )
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A combinatorial function for computing the number of \\(k\\)-combinations of \\(n\\) elements 
+/// with repetitions:
+///
+/// \\[
+/// C^*(n, k) = \binom{n + k - 1}{k} = \frac{(n + k - 1)!}{k! (n - 1)!}
+/// \\]
+/// 
+/// Also known as the "stars and bars" problem in maths. Furthermore, the implementation uses an 
+/// efficient iterative multiplicative formula for computing the result.
+///
+/// <details>
+/// <summary>Details</summary>
+///
+/// A \\(k\\)-combination with repetitions is a sequence of \\(k\\) elements selected from 
+/// \\(n\\) elements where the order of selection does not matter and elements are allowed to 
+/// repeat. For example, consider selecting 2 elements from a list of 3 elements: `["A", "B", "C"]`.
+/// In this case, possible selections are:
+///   - `["A", "A"], ["A", "B"], ["A", "C"]`
+///   - `["B", "B"], ["B", "C"], ["C", "C"]`
+///
+/// </details>
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.combination_with_repetitions(-1, 1)
+///       |> should.be_error()
+///
+///       maths.combination_with_repetitions(2, 3)
+///       |> should.equal(Ok(4))
+///     
+///       maths.combination_with_repetitions(13, 5)
+///       |> should.equal(Ok(6188))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
+  combination(n + k - 1, k)
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A combinatorial function for computing the number of \\(k\\)-combinations of \\(n\\) elements 
+/// without repetitions:
+///
+/// \\[
+/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
+/// \\]
+/// 
+/// Also known as "\\(n\\) choose \\(k\\)" or the binomial coefficient.
+///
+///
+/// <details>
+/// <summary>Details</summary>
+///
+/// A \\(k\\)-combination without repetition is a sequence of \\(k\\) elements selected from 
+/// \\(n\\) elements where the order of selection does not matter and elements are not allowed to 
+/// repeat. For example, consider selecting  2 elements from a list of 3 elements: 
+/// `["A", "B", "C"]`. In this case, possible selections are:
+///   - `["A", "B"]`
+///   - `["A", "C"]`
+///   - `["B", "C"]`
+/// 
+/// </details>
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.combination(-1, 1)
+///       |> should.be_error()
+///     
+///       maths.combination(4, 0)
+///       |> should.equal(Ok(1))
+///     
+///       maths.combination(4, 4)
+///       |> should.equal(Ok(1))
+///     
+///       maths.combination(13, 5)
+///       |> should.equal(Ok(1287))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn combination(n: Int, k: Int) -> Result(Int, Nil) {
+  case n, k {
+    _, _ if n < 0 -> Error(Nil)
+    _, _ if k < 0 -> Error(Nil)
+    _, _ if k == 0 || k == n -> Ok(1)
+    _, _ -> {
+      let min = case k < n - k {
+        True -> k
+        False -> n - k
+      }
+      Ok(
+        list.fold(list.range(1, min), 1, fn(acc, x) { acc * { n + 1 - x } / x }),
+      )
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A combinatorial function for computing the total number of combinations of \\(n\\)
+/// elements, that is \\(n!\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.factorial(-1)
+///       |> should.be_error()
+///     
+///       maths.factorial(0)
+///       |> should.equal(Ok(1))
+///     
+///       maths.factorial(3)
+///       |> should.equal(Ok(6))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn factorial(n: Int) -> Result(Int, Nil) {
+  case n {
+    _ if n < 0 -> Error(Nil)
+    _ -> Ok(do_factorial(n, 1))
+  }
+}
+
+fn do_factorial(n: Int, acc: Int) -> Int {
+  case n {
+    0 -> acc
+    1 -> acc
+    _ -> do_factorial(n - 1, acc * n)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A combinatorial function for computing the number of \\(k\\)-permutations without 
+/// repetitions:
+///
+/// \\[
+/// P(n, k) = \binom{n}{k} \cdot k! = \frac{n!}{(n - k)!}
+/// \\]
+///
+/// The implementation uses an efficient iterative multiplicative formula for computing the result.
+///
+/// <details>
+/// <summary>Details</summary>
+///
+/// A \\(k\\)-permutation without repetitions is a sequence of \\(k\\) elements selected from \
+/// \\(n\\) elements where the order of selection matters and elements are not allowed to repeat. 
+/// For example, consider selecting 2 elements from a list of 3 elements: `["A", "B", "C"]`. In 
+/// this case, possible selections are:
+///   - `["A", "B"], ["B", "A"]`
+///   - `["A", "C"], ["C", "A"]`
+///   - `["B", "C"], ["C", "B"]`
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.permutation(-1, 1)
+///       |> should.be_error()
+///     
+///       maths.permutation(4, 0)
+///       |> should.equal(Ok(1))
+///     
+///       maths.permutation(4, 2)
+///       |> should.equal(Ok(12))
+///     
+///       maths.permutation(13, 5)
+///       |> should.equal(Ok(154_440))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn permutation(n: Int, k: Int) -> Result(Int, Nil) {
+  case n, k {
+    _, _ if n < 0 -> Error(Nil)
+    _, _ if k < 0 -> Error(Nil)
+    _, _ if k > n -> Ok(0)
+    _, _ if k == 0 -> Ok(1)
+    _, _ -> Ok(do_permutation(n, k, 1))
+  }
+}
+
+fn do_permutation(n: Int, k: Int, acc: Int) -> Int {
+  case k {
+    0 -> acc
+    _ -> do_permutation(n - 1, k - 1, acc * n)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A combinatorial function for computing the number of \\(k\\)-permutations with repetitions:
+///
+/// \\[
+/// P^*(n, k) = n^k
+/// \\]
+///
+/// <details>
+/// <summary>Details</summary>
+///
+/// A \\(k\\)-permutation with repetitions is a sequence of \\(k\\) elements selected from \\(n\\) 
+/// elements where the order of selection matters and elements are allowed to repeat. For example, 
+/// consider selecting 2 elements from a list of 3 elements: `["A", "B", "C"]`. In this case, 
+/// possible selections are:
+///   - `["A", "A"], ["A", "B"], ["A", "C"]`
+///   - `["B", "A"], ["B", "B"], ["B", "C"]`
+///   - `["C", "A"], ["C", "B"], ["C", "C"]`
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.permutation_with_repetitions(1, -1)
+///       |> should.be_error()
+///     
+///       maths.permutation_with_repetitions(2, 3)
+///       |> should.equal(Ok(8))
+///     
+///       maths.permutation_with_repetitions(4, 4)
+///       |> should.equal(Ok(256))
+///     
+///       maths.permutation_with_repetitions(6, 3)
+///       |> should.equal(Ok(216))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
+  case n, k {
+    _, _ if n < 0 -> Error(Nil)
+    _, _ if k < 0 -> Error(Nil)
+    _, _ -> {
+      let n_float = int.to_float(n)
+      let k_float = int.to_float(k)
+      // 'n' and 'k' are positive integers, so no errors here...
+      let assert Ok(result) = float.power(n_float, k_float)
+      Ok(float.round(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>
+///
+/// Generates all possible combinations of \\(k\\) elements selected from a given list of size
+/// \\(n\\). The function handles the case without repetitions, that is, repeated elements 
+/// are not treated as distinct.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // All 2-combinations of [1, 2, 3] without repetition
+///       let assert Ok(combinations) = maths.list_combination([1, 2, 3], 2)
+///     
+///       combinations
+///       |> yielder.to_list()
+///       |> should.equal([[1, 2], [1, 3], [2, 3]])
+///     }
+/// </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(Yielder(List(a)), Nil) {
+  case k, list.length(arr) {
+    _, _ if k < 0 -> Error(Nil)
+    _, arr_length if k > arr_length -> Error(Nil)
+    // Special case: When k = n, then the entire list is the only valid combination
+    _, arr_length if k == arr_length -> {
+      Ok(yielder.single(arr))
+    }
+    _, _ -> {
+      Ok(do_list_combination_without_repetitions(yielder.from_list(arr), k, []))
+    }
+  }
+}
+
+fn do_list_combination_without_repetitions(
+  arr: Yielder(a),
+  k: Int,
+  prefix: List(a),
+) -> Yielder(List(a)) {
+  case k {
+    0 -> yielder.single(list.reverse(prefix))
+    _ ->
+      case yielder.step(arr) {
+        yielder.Done -> yielder.empty()
+        yielder.Next(x, xs) -> {
+          let with_x =
+            do_list_combination_without_repetitions(xs, k - 1, [x, ..prefix])
+          let without_x = do_list_combination_without_repetitions(xs, k, prefix)
+          yielder.concat([with_x, without_x])
+        }
+      }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Generates all possible combinations of \\(k\\) elements selected from a given list of size
+/// \\(n\\). The function handles the case when the repetition of elements is allowed, that is, 
+/// repeated elements are treated as distinct.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // All 2-combinations of [1, 2, 3] with repetition
+///       let assert Ok(combinations) =
+///         maths.list_combination_with_repetitions([1, 2, 3], 2)
+///     
+///       combinations
+///       |> yielder.to_list()
+///       |> should.equal([[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]])
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn list_combination_with_repetitions(
+  arr: List(a),
+  k: Int,
+) -> Result(Yielder(List(a)), Nil) {
+  case k {
+    _ if k < 0 -> Error(Nil)
+    _ -> {
+      Ok(do_list_combination_with_repetitions(yielder.from_list(arr), k, []))
+    }
+  }
+}
+
+fn do_list_combination_with_repetitions(
+  arr: Yielder(a),
+  k: Int,
+  prefix: List(a),
+) -> Yielder(List(a)) {
+  case k {
+    0 -> yielder.single(list.reverse(prefix))
+    _ ->
+      case yielder.step(arr) {
+        yielder.Done -> yielder.empty()
+        yielder.Next(x, xs) -> {
+          let with_x =
+            do_list_combination_with_repetitions(arr, k - 1, [x, ..prefix])
+          let without_x = do_list_combination_with_repetitions(xs, k, prefix)
+          yielder.concat([with_x, without_x])
+        }
+      }
+  }
+}
+
+fn remove_first_by_index(
+  arr: Yielder(#(Int, a)),
+  index_to_remove: Int,
+) -> Yielder(#(Int, a)) {
+  yielder.flat_map(arr, fn(arg) {
+    let #(index, element) = arg
+    case index == index_to_remove {
+      True -> yielder.empty()
+      False -> yielder.single(#(index, element))
+    }
+  })
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Generates all possible permutations of \\(k\\) elements selected from a given list of size
+/// \\(n\\). The function handles the case without repetitions, that is, repeated elements are 
+/// not treated as distinct.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // All 2-permutations of [1, 2] without repetition
+///       let assert Ok(permutations) =
+///         [1, 2]
+///         |> maths.list_permutation(2)
+///       permutations
+///       |> yielder.to_list()
+///       |> should.equal([[1, 2], [2, 1]])
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn list_permutation(arr: List(a), k: Int) -> Result(Yielder(List(a)), Nil) {
+  case k, list.length(arr) {
+    _, _ if k < 0 -> Error(Nil)
+    _, arr_length if k > arr_length -> Error(Nil)
+    _, _ -> {
+      let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
+      Ok(do_list_permutation_without_repetitions(
+        yielder.from_list(indexed_arr),
+        k,
+      ))
+    }
+  }
+}
+
+fn do_list_permutation_without_repetitions(
+  arr: Yielder(#(Int, a)),
+  k: Int,
+) -> Yielder(List(a)) {
+  case k {
+    0 -> yielder.single([])
+    _ ->
+      yielder.flat_map(arr, fn(arg) {
+        let #(index, element) = arg
+        let remaining = remove_first_by_index(arr, index)
+        let permutations =
+          do_list_permutation_without_repetitions(remaining, k - 1)
+        yielder.map(permutations, fn(permutation) { [element, ..permutation] })
+      })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Generates all possible permutations of \\(k\\) elements selected from a given list of size
+/// \\(n\\). The function handles the case when the repetition of elements is allowed, that is, 
+/// repeated elements are treated as distinct.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // All 2-permutations of [1, 2] with repetition
+///       let assert Ok(permutations) =
+///         [1, 2]
+///         |> maths.list_permutation_with_repetitions(2)
+///       permutations
+///       |> yielder.to_list()
+///       |> set.from_list()
+///       |> should.equal(set.from_list([[1, 1], [1, 2], [2, 2], [2, 1]]))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn list_permutation_with_repetitions(
+  arr: List(a),
+  k: Int,
+) -> Result(Yielder(List(a)), Nil) {
+  case k {
+    _ if k < 0 -> Error(Nil)
+    _ -> {
+      let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
+      Ok(do_list_permutation_with_repetitions(indexed_arr, k))
+    }
+  }
+}
+
+fn do_list_permutation_with_repetitions(
+  arr: List(#(Int, a)),
+  k: Int,
+) -> Yielder(List(a)) {
+  case k {
+    0 -> yielder.single([])
+    _ ->
+      yielder.flat_map(arr |> yielder.from_list, fn(arg) {
+        let #(_, element) = arg
+        // Allow the same element (by index) to be reused in future recursive calls
+        let permutations = do_list_permutation_with_repetitions(arr, k - 1)
+        // Prepend the current element to each generated permutation
+        yielder.map(permutations, fn(permutation) { [element, ..permutation] })
+      })
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Generate a list containing all combinations of pairs of elements coming from two given sets.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/set
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       set.from_list([])
+///       |> maths.cartesian_product(set.from_list([]))
+///       |> should.equal(set.from_list([]))
+///
+///       set.from_list([1.0, 10.0])
+///       |> maths.cartesian_product(set.from_list([1.0, 2.0]))
+///       |> should.equal(
+///         set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
+///       )
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn cartesian_product(xset: set.Set(a), yset: set.Set(b)) -> set.Set(#(a, b)) {
+  set.fold(xset, set.new(), fn(accumulator0: set.Set(#(a, b)), member0: a) {
+    set.fold(yset, accumulator0, fn(accumulator1: set.Set(#(a, b)), member1: b) {
+      set.insert(accumulator1, #(member0, member1))
+    })
+  })
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the \\(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
+///
+///     pub fn example() {
+///       [1.0, 1.0, 1.0]
+///       |> maths.norm(1.0)
+///       |> should.equal(Ok(3.0))
+///     
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(result) =
+///         [1.0, 2.0, 3.0]
+///         |> maths.norm(2.0)
+///       result
+///       |> maths.is_close(3.7416573867739413, 0.0, tolerance)
+///       |> 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) -> Result(Float, Nil) {
+  case arr {
+    [] -> Ok(0.0)
+    _ -> {
+      let aggregate =
+        list.fold(arr, 0.0, fn(accumulator, element) {
+          let assert Ok(result) = float.power(float.absolute_value(element), p)
+          result +. accumulator
+        })
+      float.power(aggregate, 1.0 /. p)
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted \\(p\\)-norm of a list (representing a vector):
+///
+/// \\[
+/// \left( \sum_{i=1}^n w_{i} \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\\), while \\(w_i \in \mathbb{R}_{+}\\) is
+/// a corresponding positive weight.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       [#(1.0, 0.5), #(1.0, 0.5), #(1.0, 0.5)]
+///       |> maths.norm_with_weights(1.0)
+///       |> should.equal(Ok(1.5))
+///     
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(result) =
+///         [#(1.0, 0.5), #(2.0, 0.5), #(3.0, 0.5)]
+///         |> maths.norm_with_weights(2.0)
+///       result
+///       |> maths.is_close(2.6457513110645907, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn norm_with_weights(
+  arr: List(#(Float, Float)),
+  p: Float,
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Ok(0.0)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
+      case weight_is_negative {
+        False -> {
+          let aggregate =
+            list.fold(arr, 0.0, fn(accumulator, tuple) {
+              let assert Ok(result) =
+                float.power(float.absolute_value(tuple.0), p)
+              tuple.1 *. result +. accumulator
+            })
+          float.power(aggregate, 1.0 /. p)
+        }
+        True -> Error(Nil)
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the Manhattan 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\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.manhattan_distance([#(1.0, 2.0), #(2.0, 3.0)])
+///       |> should.equal(Ok(2.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn manhattan_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  minkowski_distance(arr, 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>
+///
+/// Calculate the weighted Manhattan distance between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \sum_{i=1}^n w_{i} \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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.manhattan_distance_with_weights([#(1.0, 2.0, 0.5), #(2.0, 3.0, 1.0)])
+///       |> should.equal(Ok(1.5))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn manhattan_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  minkowski_distance_with_weights(arr, 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>
+///
+/// Calculate the Minkowski distance between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \left( \sum_{i=1}^n w_{i} \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\\).
+///
+/// The Minkowski distance is a generalization of the Euclidean distance
+/// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       let assert Ok(result) =
+///         maths.minkowski_distance([#(1.0, 2.0), #(3.0, 4.0), #(5.0, 6.0)], 4.0)
+///       result
+///       |> maths.is_close(1.3160740129524924, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn minkowski_distance(
+  arr: List(#(Float, Float)),
+  p: Float,
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      case p <. 1.0 {
+        True -> Error(Nil)
+        False -> {
+          let differences =
+            list.map(arr, fn(tuple: #(Float, Float)) -> Float {
+              tuple.0 -. tuple.1
+            })
+          norm(differences, p)
+        }
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted Minkowski distance between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \left( \sum_{i=1}^n w_{i} \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\\).
+/// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// The Minkowski distance is a generalization of the Euclidean distance
+/// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       let assert Ok(result) =
+///         maths.minkowski_distance_with_weights(
+///           [#(1.0, 2.0, 0.5), #(3.0, 4.0, 1.0), #(5.0, 6.0, 1.0)],
+///           4.0,
+///         )
+///       result
+///       |> maths.is_close(1.2574334296829355, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn minkowski_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+  p: Float,
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
+
+      case p <. 1.0, weight_is_negative {
+        False, False -> {
+          let differences =
+            list.map(arr, fn(tuple: #(Float, Float, Float)) -> #(Float, Float) {
+              #(tuple.0 -. tuple.1, tuple.2)
+            })
+          norm_with_weights(differences, p)
+        }
+        _, _ -> Error(Nil)
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate 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\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       let assert Ok(result) = maths.euclidean_distance([#(1.0, 2.0), #(3.0, 4.0)])
+///       result
+///       |> maths.is_close(1.4142135623730951, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn euclidean_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  minkowski_distance(arr, 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>
+///
+/// Calculate the weighted Euclidean distance between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \left( \sum_{i=1}^n w_{i} \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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       let assert Ok(result) =
+///         maths.euclidean_distance_with_weights([#(1.0, 2.0, 0.5), #(3.0, 4.0, 1.0)])
+///       result
+///       |> maths.is_close(1.224744871391589, 0.0, tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn euclidean_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  minkowski_distance_with_weights(arr, 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>
+///
+/// Calculate the Chebyshev distance between two lists (representing vectors):
+///
+/// \\[
+/// \text{max}_{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\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.chebyshev_distance([#(-5.0, -1.0), #(-10.0, -12.0), #(-3.0, -3.0)])
+///       |> should.equal(Ok(4.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn chebyshev_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      list.map(arr, fn(tuple) { float.absolute_value({ tuple.0 -. tuple.1 }) })
+      |> list_maximum(float.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>
+///
+/// Calculate the weighted Chebyshev distance between two lists (representing vectors):
+///
+/// \\[
+/// \text{max}_{i=1}^n w_i \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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.chebyshev_distance_with_weights([
+///         #(-5.0, -1.0, 0.5),
+///         #(-10.0, -12.0, 1.0),
+///         #(-3.0, -3.0, 1.0),
+///       ])
+///       |> should.equal(Ok(2.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn chebyshev_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
+
+      case weight_is_negative {
+        True -> Error(Nil)
+        False -> {
+          list.map(arr, fn(tuple) {
+            float.absolute_value({ tuple.0 -. tuple.1 }) *. tuple.2
+          })
+          |> list_maximum(float.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>
+///
+/// Calculate 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
+///
+///     pub fn example () {
+///       []
+///       |> maths.mean()
+///       |> should.be_error()
+///     
+///       [1.0, 2.0, 3.0]
+///       |> maths.mean()
+///       |> should.equal(Ok(2.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn mean(arr: List(Float)) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> Ok(float.sum(arr) /. int.to_float(list.length(arr)))
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the median of the elements in a list.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       []
+///       |> maths.median()
+///       |> should.be_error()
+///
+///       [1.0, 2.0, 3.0]
+///       |> maths.median()
+///       |> should.equal(Ok(2.0))
+///
+///       [1.0, 2.0, 3.0, 4.0]
+///       |> maths.median()
+///       |> should.equal(Ok(2.5))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn median(arr: List(Float)) -> Result(Float, Nil) {
+  use <- bool.guard(list.is_empty(arr), Error(Nil))
+  let length = list.length(arr)
+  let mid = length / 2
+
+  case length % 2 == 0 {
+    True -> do_median(arr, mid, True, 0)
+    False -> do_median(arr, mid, False, 0)
+  }
+}
+
+fn do_median(
+  xs: List(Float),
+  mid: Int,
+  mean: Bool,
+  index: Int,
+) -> Result(Float, Nil) {
+  use <- bool.guard(index > mid, Error(Nil))
+  let mid_less_one = mid - 1
+
+  case xs {
+    [x, ..] if !mean && index == mid -> Ok(x)
+    [x, y, ..] if mean && index == mid_less_one -> Ok({ x +. y } /. 2.0)
+    [_, ..rest] -> do_median(rest, mid, mean, index + 1)
+    [] -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate 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". It is typically set to \\(d = 1\\), which gives an unbiased estimate.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // Degrees of freedom
+///       let ddof = 1
+///
+///       []
+///       |> maths.variance(ddof)
+///       |> should.be_error()
+///
+///       [1.0, 2.0, 3.0]
+///       |> maths.variance(ddof)
+///       |> should.equal(Ok(1.0))
+///     }
+/// </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, Nil) {
+  case arr, ddof {
+    [], _ -> Error(Nil)
+    _, _ if ddof < 0 -> Error(Nil)
+    _, _ -> {
+      let assert Ok(mean) = mean(arr)
+      Ok(
+        list.map(arr, fn(a: Float) -> Float {
+          let assert Ok(result) = float.power(a -. mean, 2.0)
+          result
+        })
+        |> float.sum()
+        |> fn(a: Float) -> Float {
+          a /. { int.to_float(list.length(arr)) -. int.to_float(ddof) }
+        },
+      )
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the 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 typically set to \\(d = 1\\), which gives an unbiased estimate.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       // Degrees of freedom
+///       let ddof = 1
+///     
+///       []
+///       |> maths.standard_deviation(ddof)
+///       |> should.be_error()
+///     
+///       [1.0, 2.0, 3.0]
+///       |> maths.standard_deviation(ddof)
+///       |> should.equal(Ok(1.0))
+///     }
+/// </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, Nil) {
+  case arr, ddof {
+    [], _ -> Error(Nil)
+    _, _ if ddof < 0 -> Error(Nil)
+    _, _ -> {
+      let assert Ok(variance) = variance(arr, ddof)
+      float.square_root(variance)
+    }
+  }
+}
+
+/// <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 Jaccard index measures similarity between two sets of elements. Mathematically, the 
+/// Jaccard index is defined as:
+///
+/// \\[
+/// \frac{|X \cap Y|}{|X \cup Y|} \\; \in \\; \left[0, 1\right]
+/// \\]
+///
+/// where:
+///
+/// - \\(X\\) and \\(Y\\) are two sets being compared
+/// - \\(|X \cap Y|\\) represents the size of the intersection of the two sets
+/// - \\(|X \cup Y|\\) denotes the size of the union of the two sets
+///
+/// The value of the Jaccard index ranges from 0 to 1, where 0 indicates that the
+/// two sets share no elements and 1 indicates that the sets are identical. The
+/// Jaccard index is a special case of the  [Tversky index](#tversky_index) (with
+/// \\(\alpha=\beta=1\\)).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/set
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
+///       let yset = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
+///       maths.jaccard_index(xset, yset)
+///       |> should.equal(1.0 /. 7.0)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
+  let assert Ok(result) = tversky_index(xset, yset, 1.0, 1.0)
+  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>
+///
+/// The Sørensen-Dice coefficient measures the similarity between two sets of elements. 
+/// Mathematically, the coefficient is defined as:
+///
+/// \\[
+/// \frac{2 |X \cap Y|}{|X| + |Y|} \\; \in \\; \left[0, 1\right]
+/// \\]
+///
+/// where:
+/// 
+/// - \\(X\\) and \\(Y\\) are two sets being compared
+/// - \\(|X \cap Y|\\) is the size of the intersection of the two sets (i.e., the
+/// number of elements common to both sets)
+/// - \\(|X|\\) and \\(|Y|\\) are the sizes of the sets \\(X\\) and \\(Y\\), respectively
+///
+/// The coefficient ranges from 0 to 1, where 0 indicates no similarity (the sets
+/// share no elements) and 1 indicates perfect similarity (the sets are identical).
+/// The higher the coefficient, the greater the similarity between the two sets.
+/// The Sørensen-Dice coefficient is a special case of the
+/// [Tversky index](#tversky_index) (with \\(\alpha=\beta=0.5\\)).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/set
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
+///       let yset = set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
+///       maths.sorensen_dice_coefficient(xset, yset)
+///       |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
+  let assert Ok(result) = tversky_index(xset, yset, 0.5, 0.5)
+  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>
+///
+/// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice
+/// coefficient, which adds flexibility in measuring similarity between two sets using two 
+/// parameters, \\(\alpha\\) and \\(\beta\\). These parameters allow for asymmetric 
+/// similarity measures between sets. The Tversky index is defined as:
+///
+/// \\[
+/// \frac{|X \cap Y|}{|X \cap Y| + \alpha|X - Y| + \beta|Y - X|}
+/// \\]
+///
+/// where:
+///
+/// - \\(X\\) and \\(Y\\) are the sets being compared
+/// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of
+/// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively,
+/// - \\(\alpha\\) and \\(\beta\\) are parameters that weight the relative importance
+/// of the elements unique to \\(X\\) and \\(Y\\)
+///
+/// The Tversky index reduces to the Jaccard index when \\(\alpha = \beta = 1\\) and
+/// to the Sørensen-Dice coefficient when \\(\alpha = \beta = 0.5\\). In general, the
+/// Tversky index can take on any non-negative value, including 0. The index equals
+/// 0 when there is no intersection between the two sets, indicating no similarity.
+/// The Tversky index does not have a strict upper limit of 1 when \\(\alpha \neq \beta\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/set
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let yset = set.from_list(["cat", "dog", "hippo", "monkey"])
+///       let xset = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
+///       // Test Jaccard index (alpha = beta = 1)
+///       maths.tversky_index(xset, yset, 1.0, 1.0)
+///       |> should.equal(Ok(1.0 /. 7.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn tversky_index(
+  xset: set.Set(a),
+  yset: set.Set(a),
+  alpha: Float,
+  beta: Float,
+) -> Result(Float, Nil) {
+  case alpha >=. 0.0, beta >=. 0.0 {
+    True, True -> {
+      let intersection =
+        set.intersection(xset, yset)
+        |> set.size()
+        |> int.to_float()
+      let difference1 =
+        set.difference(xset, yset)
+        |> set.size()
+        |> int.to_float()
+      let difference2 =
+        set.difference(yset, xset)
+        |> set.size()
+        |> int.to_float()
+      Ok(
+        intersection
+        /. { intersection +. alpha *. difference1 +. beta *. difference2 },
+      )
+    }
+    _, _ -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The Overlap coefficient, also known as the Szymkiewicz–Simpson coefficient, is
+/// a measure of similarity between two sets that focuses on the size of the
+/// intersection relative to the smaller of the two sets. It is defined
+/// mathematically as:
+///
+/// \\[
+/// \frac{|X \cap Y|}{\min(|X|, |Y|)} \\; \in \\; \left[0, 1\right]
+/// \\]
+///
+/// where:
+///
+/// - \\(X\\) and \\(Y\\) are the sets being compared
+/// - \\(|X \cap Y|\\) is the size of the intersection of the sets
+/// - \\(\min(|X|, |Y|)\\) is the size of the smaller set among \\(X\\) and \\(Y\\)
+///
+/// The coefficient ranges from 0 to 1, where 0 indicates no overlap and 1
+/// indicates that the smaller set is a suyset of the larger set. This
+/// measure is especially useful in situations where the similarity in terms
+/// of the proportion of overlap is more relevant than the difference in sizes
+/// between the two sets.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/set
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let set_a = set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
+///       let set_b = set.from_list(["monkey", "bird", "ostrich", "salmon"])
+///       maths.overlap_coefficient(set_a, set_b)
+///       |> should.equal(2.0 /. 4.0)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
+  let intersection =
+    set.intersection(xset, yset)
+    |> set.size()
+    |> int.to_float()
+  let minsize =
+    int.min(set.size(xset), set.size(yset))
+    |> int.to_float()
+  intersection /. minsize
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the cosine similarity between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \frac{\sum_{i=1}^n  x_i \cdot y_i}
+/// {\left(\sum_{i=1}^n x_i^2\right)^{\frac{1}{2}}
+/// \cdot
+/// \left(\sum_{i=1}^n y_i^2\right)^{\frac{1}{2}}}
+/// \\; \in \\; \left[-1, 1\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\\).
+///
+/// The cosine similarity provides a value between -1 and 1, where 1 means the
+/// vectors are in the same direction, -1 means they are in exactly opposite
+/// directions, and 0 indicates orthogonality.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/option
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       // Two orthogonal vectors
+///       maths.cosine_similarity([#(-1.0, 1.0), #(1.0, 1.0), #(0.0, -1.0)])
+///       |> should.equal(Ok(0.0))
+///     
+///       // Two identical (parallel) vectors
+///       maths.cosine_similarity([#(1.0, 1.0), #(2.0, 2.0), #(3.0, 3.0)])
+///       |> should.equal(Ok(1.0))
+///     
+///       // Two parallel, but oppositely oriented vectors
+///       maths.cosine_similarity([#(-1.0, 1.0), #(-2.0, 2.0), #(-3.0, 3.0)])
+///       |> should.equal(Ok(-1.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn cosine_similarity(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  let numerator =
+    arr
+    |> list.fold(0.0, fn(accumulator, tuple) {
+      accumulator +. tuple.0 *. tuple.1
+    })
+
+  let xarr = arr |> list.map(fn(tuple: #(Float, Float)) { tuple.0 })
+  let yarr = arr |> list.map(fn(tuple: #(Float, Float)) { tuple.1 })
+
+  let assert Ok(xarr_norm) = norm(xarr, 2.0)
+  let assert Ok(yarr_norm) = norm(yarr, 2.0)
+
+  let denominator = {
+    xarr_norm *. yarr_norm
+  }
+  Ok(numerator /. denominator)
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted cosine similarity between two lists (representing
+/// vectors):
+///
+/// \\[
+/// \frac{\sum_{i=1}^n w_{i} \cdot x_i \cdot y_i}
+/// {\left(\sum_{i=1}^n w_{i} \cdot x_i^2\right)^{\frac{1}{2}}
+/// \cdot
+/// \left(\sum_{i=1}^n w_{i} \cdot y_i^2\right)^{\frac{1}{2}}}
+/// \\; \in \\; \left[-1, 1\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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// The cosine similarity provides a value between -1 and 1, where 1 means the
+/// vectors are in the same direction, -1 means they are in exactly opposite
+/// directions, and 0 indicates orthogonality.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/option
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///     
+///       let assert Ok(result) =
+///         maths.cosine_similarity_with_weights([
+///           #(1.0, 1.0, 2.0),
+///           #(2.0, 2.0, 3.0),
+///           #(3.0, 3.0, 4.0),
+///         ])
+///       result
+///       |> maths.is_close(1.0, 0.0, tolerance)
+///       |> should.be_true()
+///     
+///       let assert Ok(result) =
+///         maths.cosine_similarity_with_weights([
+///           #(-1.0, 1.0, 1.0),
+///           #(-2.0, 2.0, 0.5),
+///           #(-3.0, 3.0, 0.33),
+///         ])
+///       result
+///       |> maths.is_close(-1.0, 0.0, tolerance)
+///       |> should.be_true()  
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn cosine_similarity_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  let weight_is_negative =
+    list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
+
+  case weight_is_negative {
+    False -> {
+      let numerator =
+        arr
+        |> list.fold(0.0, fn(accumulator, tuple) {
+          accumulator +. tuple.0 *. tuple.1 *. tuple.2
+        })
+
+      let xarr =
+        arr
+        |> list.map(fn(tuple: #(Float, Float, Float)) { #(tuple.0, tuple.2) })
+      let yarr =
+        arr
+        |> list.map(fn(tuple: #(Float, Float, Float)) { #(tuple.1, tuple.2) })
+
+      let assert Ok(xarr_norm) = norm_with_weights(xarr, 2.0)
+      let assert Ok(yarr_norm) = norm_with_weights(yarr, 2.0)
+      let denominator = {
+        xarr_norm *. yarr_norm
+      }
+      Ok(numerator /. denominator)
+    }
+    True -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the Canberra distance between two lists:
+///
+/// \\[
+/// \sum_{i=1}^n \frac{\left| x_i - y_i \right|}
+/// {\left| x_i \right| + \left| 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\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.canberra_distance([])
+///       |> should.be_error()
+///     
+///       maths.canberra_distance([#(1.0, -2.0), #(2.0, -1.0)])
+///       |> should.equal(Ok(2.0))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn canberra_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      Ok(
+        list.fold(arr, 0.0, fn(accumulator, tuple) {
+          let numerator = float.absolute_value({ tuple.0 -. tuple.1 })
+          let denominator = {
+            float.absolute_value(tuple.0) +. float.absolute_value(tuple.1)
+          }
+          accumulator +. numerator /. denominator
+        }),
+      )
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted Canberra distance between two lists:
+///
+/// \\[
+/// \sum_{i=1}^n w_{i}\frac{\left| x_i - y_i \right|}
+/// {\left| x_i \right| + \left| 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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.canberra_distance([])
+///       |> should.be_error()
+///     
+///       maths.canberra_distance_with_weights([#(1.0, -2.0, 0.5), #(2.0, -1.0, 1.0)])
+///       |> should.equal(Ok(1.5))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn canberra_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
+
+      case weight_is_negative {
+        True -> Error(Nil)
+        False -> {
+          Ok(
+            list.fold(arr, 0.0, fn(accumulator, tuple) {
+              let numerator = float.absolute_value({ tuple.0 -. tuple.1 })
+              let denominator = {
+                float.absolute_value(tuple.0) +. float.absolute_value(tuple.1)
+              }
+              accumulator +. tuple.2 *. numerator /. denominator
+            }),
+          )
+        }
+      }
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the Bray-Curtis distance between two lists:
+///
+/// \\[
+/// \frac{\sum_{i=1}^n  \left| x_i - y_i \right|}
+/// {\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\\).
+///
+/// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are
+/// positive.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.canberra_distance([])
+///       |> should.be_error()
+///     
+///       maths.canberra_distance_with_weights([#(1.0, -2.0, 0.5), #(2.0, -1.0, 1.0)])
+///       |> should.equal(Ok(1.5))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+///
+pub fn braycurtis_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let numerator =
+        list.fold(arr, 0.0, fn(accumulator, tuple) {
+          accumulator +. float.absolute_value({ tuple.0 -. tuple.1 })
+        })
+
+      let denominator =
+        list.fold(arr, 0.0, fn(accumulator, tuple) {
+          accumulator +. float.absolute_value({ tuple.0 +. tuple.1 })
+        })
+
+      Ok({ numerator /. denominator })
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Calculate the weighted Bray-Curtis distance between two lists:
+///
+/// \\[
+/// \frac{\sum_{i=1}^n w_{i} \left| x_i - y_i \right|}
+/// {\sum_{i=1}^n w_{i}\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\\), while the
+/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights.
+///
+/// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are
+/// positive and \\(w_i = 1.0\\;\forall i=1...n\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.braycurtis_distance_with_weights([])
+///       |> should.be_error()
+///     
+///       maths.braycurtis_distance_with_weights([#(1.0, 3.0, 0.5), #(2.0, 4.0, 1.0)])
+///       |> should.equal(Ok(0.375))
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn braycurtis_distance_with_weights(
+  arr: List(#(Float, Float, Float)),
+) -> Result(Float, Nil) {
+  case arr {
+    [] -> Error(Nil)
+    _ -> {
+      let weight_is_negative =
+        list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
+
+      case weight_is_negative {
+        True -> Error(Nil)
+        False -> {
+          let numerator =
+            list.fold(arr, 0.0, fn(accumulator, tuple) {
+              accumulator
+              +. tuple.2
+              *. float.absolute_value({ tuple.0 -. tuple.1 })
+            })
+
+          let denominator =
+            list.fold(arr, 0.0, fn(accumulator, tuple) {
+              accumulator
+              +. tuple.2
+              *. float.absolute_value({ tuple.0 +. tuple.1 })
+            })
+
+          Ok({ numerator /. denominator })
+        }
+      }
+    }
+  }
+}
+
+/// <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 \\(x\\) is close to or equivalent to a reference value
+/// \\(y\\) 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:
+///
+/// \\[
+///     \|x - y\| \leq (a_{tol} + r_{tol} \cdot \|y\|)
+/// \\]
+///
+/// `True` is returned if the statement holds, otherwise `False` is returned.
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let value = 99.
+///       let reference_value = 100.
+///       // We set 'absolute_tolerance' and 'relative_tolerance' such that the values are 
+///       // equivalent if 'value' is within 1 percent of 'reference_value' +/- 0.1
+///       let relative_tolerance = 0.01
+///       let absolute_tolerance = 0.10
+///       maths.is_close(value, reference_value, relative_tolerance, absolute_tolerance)
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn is_close(x: Float, y: Float, rtol: Float, atol: Float) -> Bool {
+  let x = float_absolute_difference(x, y)
+  let y = atol +. rtol *. float.absolute_value(y)
+  case x <=. y {
+    True -> True
+    False -> False
+  }
+}
+
+/// <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 each value \\(x_i\\) is close to or equivalent to its corresponding reference value 
+/// \\(y_i\\), in a list of value pairs \\((x_i, y_i)\\), based on supplied relative \\(r_{tol}\\) 
+/// and absolute  \\(a_{tol}\\) tolerance values. The equivalence of each pair \\((x_i, y_i)\\) is 
+/// determined by the equation:
+///
+/// \\[
+///     \|x_i - y_i\| \leq (a_{tol} + r_{tol} \cdot \|y_i\|), \\; \forall i=1,...,n.
+/// \\]
+///
+/// A list of `Bool` values is returned, where each entry indicates if the corresponding pair
+/// satisfies the condition. If all conditions are satisfied, the list will contain only `True` 
+/// values.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/list
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let value = 99.0
+///       let reference_value = 100.0
+///       let xarr = list.repeat(value, 42)
+///       let yarr = list.repeat(reference_value, 42)
+///       let arr = list.zip(xarr, yarr)
+///       // We set 'absolute_tolerance' and 'relative_tolerance' such that
+///       // the values are equivalent if 'value' is within 1 percent of 
+///       // 'reference_value' +/- 0.1
+///       let relative_tolerance = 0.01
+///       let absolute_tolerance = 0.1
+///       let assert Ok(result) =
+///         maths.all_close(arr, relative_tolerance, absolute_tolerance)
+///       result
+///       |> list.all(fn(x) { x == True })
+///       |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn all_close(
+  arr: List(#(Float, Float)),
+  rtol: Float,
+  atol: Float,
+) -> Result(List(Bool), Nil) {
+  Ok(list.map(arr, fn(tuple) { is_close(tuple.0, tuple.1, rtol, atol) }))
+}
+
+/// <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 \\(x\\) is fractional, i.e., if it contains a fractional part:
+/// 
+/// \\[
+///     x - \lfloor x \rfloor > 0
+/// \\]
+/// 
+/// `True` is returned if the given value is fractional (i.e., it has a non-zero decimal part),
+/// otherwise `False` is returned.
+///
+/// <details>
+///     <summary>Example</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       maths.is_fractional(0.3333)
+///       |> should.equal(True)
+///
+///       maths.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
+}
+
+/// <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 determines if a given integer value \\(x \in \mathbb{Z}\\) is a power of 
+/// another integer value \\(y \in \mathbb{Z}\\), i.e., the function evaluates whether \\(x\\) can 
+/// be expressed as \\(y^n\\) for some integer \\(n \geq 0\\), by computing the base-\\(y\\) 
+/// logarithm of \\(x\\):
+/// 
+/// \\[
+///     n = \log_y(x)
+/// \\]
+/// 
+/// If \\(n\\) is an integer (i.e., it has no fractional part), then \\(x\\) is a power of \\(y\\) 
+/// and `True` is returned. Otherwise `False` is returned.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       // Check if 4 is a power of 2 (it is)
+///       maths.is_power(4, 2)
+///       |> should.equal(True)
+///
+///       // Check if 5 is a power of 2 (it is not)
+///       maths.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) = logarithm(int.to_float(x), int.to_float(y))
+  let truncated = round_to_zero(value, 0)
+  let remainder = value -. truncated
+  remainder == 0.0
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A function that tests whether a given integer value \\(n \in \mathbb{Z}\\) is a
+/// perfect number. A number is perfect if it is equal to the sum of its proper
+/// positive divisors.
+///
+/// <details>
+///     <summary>Details</summary>
+///
+///   For example:
+///   - \\(6\\) is a perfect number since the divisors of 6 are \\(1 + 2 + 3 = 6\\).
+///   - \\(28\\) is a perfect number since the divisors of 28 are \\(1 + 2 + 4 + 7 + 14 = 28\\).
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.is_perfect(6)
+///       |> should.equal(True)
+///
+///       maths.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 {
+  int.sum(proper_divisors(n)) == n
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
+/// prime number. A prime number is a natural number greater than 1 that has no
+/// positive divisors other than 1 and itself.
+///
+/// The function uses the Miller-Rabin primality test to assess if \\(x\\) is prime.
+/// It is a probabilistic test, so it can mistakenly identify a composite number
+/// as prime. However, the probability of such errors decreases with more testing
+/// iterations (the function uses 64 iterations internally, which is typically
+/// more than sufficient). The Miller-Rabin test is particularly useful for large
+/// numbers.
+///
+/// <details>
+///     <summary>Details</summary>
+///
+///   Examples of prime numbers:
+///   - \\(2\\) is a prime number since it has only two divisors: \\(1\\) and \\(2\\).
+///   - \\(7\\) is a prime number since it has only two divisors: \\(1\\) and \\(7\\).
+///   - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such
+///     as \\(2\\).
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.is_prime(2)
+///       |> should.equal(True)
+///
+///       maths.is_prime(4)
+///       |> should.equal(False)
+///
+///       // Test the 2nd Carmichael number
+///       maths.is_prime(1105)
+///       |> should.equal(False)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn is_prime(x: Int) -> Bool {
+  case x {
+    x if x < 2 -> {
+      False
+    }
+    x if x == 2 -> {
+      True
+    }
+    _ -> {
+      miller_rabin_test(x, 64)
+    }
+  }
+}
+
+fn miller_rabin_test(n: Int, k: Int) -> Bool {
+  case n, k {
+    _, 0 -> True
+    _, _ -> {
+      // Generate a random int in the range [2, n]
+      let random_candidate = 2 + int.random(n - 2)
+      case powmod_with_check(random_candidate, n - 1, n) == 1 {
+        True -> miller_rabin_test(n, k - 1)
+        False -> False
+      }
+    }
+  }
+}
+
+fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
+  case exponent, { exponent % 2 } == 0 {
+    0, _ -> 1
+    _, True -> {
+      let x = powmod_with_check(base, exponent / 2, modulus)
+      case { x * x } % modulus, x != 1 && x != { modulus - 1 } {
+        1, True -> 0
+        _, _ -> { x * x } % modulus
+      }
+    }
+    _, _ -> { base * powmod_with_check(base, exponent - 1, modulus) } % modulus
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A function that tests whether a given real number \\(x \in \mathbb{R}\\) is strictly
+/// between two other real numbers, \\(a,b \in \mathbb{R}\\), such that \\(a < x < b\\).
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.is_between(5.5, 5.0, 6.0)
+///       |> should.equal(True)
+///
+///       maths.is_between(5.0, 5.0, 6.0)
+///       |> should.equal(False)
+///
+///       maths.is_between(6.0, 5.0, 6.0)
+///       |> should.equal(False)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool {
+  lower <. x && x <. upper
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another
+/// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\).
+///
+/// <details>
+///     <summary>Details</summary>
+///
+///   For example:
+///   - \\(n = 10\\) is divisible by \\(d = 2\\) because \\(10 \mod 2 = 0\\).
+///   - \\(n = 7\\) is not divisible by \\(d = 3\\) because \\(7 \mod 3 \neq 0\\).
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.is_divisible(10, 2)
+///       |> should.equal(True)
+///
+///       maths.is_divisible(7, 3)
+///       |> should.equal(False)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn is_divisible(n: Int, d: Int) -> Bool {
+  n % d == 0
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another
+/// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \cdot q\\), with \\(q \in \mathbb{Z}\\).
+///
+/// <details>
+///     <summary>Details</summary>
+///
+///   For example:
+///   - \\(m = 15\\) is a multiple of \\(k = 5\\) because \\(15 = 5 \cdot 3\\).
+///   - \\(m = 14\\) is not a multiple of \\(k = 5\\) because \\(\frac{14}{5}\\) does not yield an
+///     integer quotient.
+///
+/// </details>
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       maths.is_multiple(15, 5)
+///       |> should.equal(True)
+///
+///       maths.is_multiple(14, 5)
+///       |> should.equal(False)
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn is_multiple(m: Int, k: Int) -> Bool {
+  m % k == 0
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The beta function over the real numbers:
+///
+/// \\[
+/// \text{B}(x, y) = \frac{\Gamma(x) \cdot \Gamma(y)}{\Gamma(x + y)}
+/// \\]
+///
+/// The beta function is evaluated through the use of the gamma function.
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn beta(x: Float, y: Float) -> Float {
+  gamma(x) *. gamma(y) /. gamma(x +. y)
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The error function.
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn erf(x: Float) -> Float {
+  let assert [a1, a2, a3, a4, a5] = [
+    0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429,
+  ]
+  let p = 0.3275911
+
+  let sign = float_sign(x)
+  let x = float.absolute_value(x)
+
+  // Formula 7.1.26 given in Abramowitz and Stegun.
+  let t = 1.0 /. { 1.0 +. p *. x }
+  let y =
+    1.0
+    -. { { { { a5 *. t +. a4 } *. t +. a3 } *. t +. a2 } *. t +. a1 }
+    *. t
+    *. 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 -> pi() /. { sin(pi() *. x) *. gamma_lanczos(1.0 -. x) }
+    False -> {
+      let z = x -. 1.0
+      let x =
+        list.index_fold(lanczos_p, 0.0, fn(acc, v, index) {
+          case index > 0 {
+            True -> acc +. v /. { z +. int.to_float(index) }
+            False -> v
+          }
+        })
+      let t = z +. lanczos_g +. 0.5
+      let assert Ok(v1) = float.power(2.0 *. pi(), 0.5)
+      let assert Ok(v2) = float.power(t, z +. 0.5)
+      v1 *. v2 *. exponential(-1.0 *. t) *. x
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The lower incomplete gamma function over the real numbers.
+///
+/// The implemented incomplete gamma function is evaluated through a power series
+/// expansion.
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, Nil) {
+  case a >. 0.0 && x >=. 0.0 {
+    True -> {
+      let assert Ok(v) = float.power(x, a)
+      Ok(
+        v
+        *. exponential(-1.0 *. x)
+        *. incomplete_gamma_sum(a, x, 1.0 /. a, 0.0, 1.0),
+      )
+    }
+
+    False -> Error(Nil)
+  }
+}
+
+fn incomplete_gamma_sum(
+  a: Float,
+  x: Float,
+  t: Float,
+  s: Float,
+  n: Float,
+) -> Float {
+  case t {
+    0.0 -> s
+    _ -> {
+      let ns = s +. t
+      let nt = t *. { x /. { a +. n } }
+      incomplete_gamma_sum(a, x, nt, ns, n +. 1.0)
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns an iterator generating evenly spaced values within a specified interval 
+/// `[start, stop)` based on a given increment size. 
+///  
+/// Note that if `increment > 0`, the sequence progresses from `start`  towards `stop`, while if 
+/// `increment < 0`, the sequence progresses from `start` towards `stop` in reverse.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       maths.arange(1.0, 5.0, 1.0)
+///       |> yielder.to_list()
+///       |> should.equal([1.0, 2.0, 3.0, 4.0])
+///
+///       // No points returned since
+///       // start is smaller than stop and the step is positive
+///       maths.arange(5.0, 1.0, 1.0)
+///       |> yielder.to_list()
+///       |> should.equal([])
+///
+///       // Points returned since
+///       // start smaller than stop but negative step
+///       maths.arange(5.0, 1.0, -1.0)
+///       |> yielder.to_list()
+///       |> should.equal([5.0, 4.0, 3.0, 2.0])
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn arange(start: Float, stop: Float, increment: Float) -> Yielder(Float) {
+  // Check if the range would be empty due to direction and increment
+  case
+    { start >=. stop && increment >. 0.0 }
+    || { start <=. stop && increment <. 0.0 }
+  {
+    True -> yielder.empty()
+    False -> {
+      let direction = case start <=. stop {
+        True -> 1.0
+        False -> -1.0
+      }
+      let increment_abs = float.absolute_value(increment)
+      let distance = float.absolute_value(start -. stop)
+      let num = float.round(distance /. increment_abs)
+
+      yielder.map(yielder.range(0, num - 1), fn(i) {
+        start +. int.to_float(i) *. increment_abs *. direction
+      })
+    }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns an iterator for generating linearly spaced points over a specified
+/// interval. The endpoint of the interval can optionally be included/excluded. The number of
+/// points and whether the endpoint is included determine the spacing between values.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, True)
+///       let pairs =
+///         linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0])
+///       let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
+///       result
+///       |> list.all(fn(x) { x == True })
+///       |> should.be_true()
+///     
+///       // A negative number of points (-5) does not work
+///       maths.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,
+  steps: Int,
+  endpoint: Bool,
+) -> Result(Yielder(Float), Nil) {
+  let direction = case start <=. stop {
+    True -> 1.0
+    False -> -1.0
+  }
+
+  let increment = case endpoint {
+    True -> {
+      float.absolute_value(start -. stop) /. int.to_float(steps - 1)
+    }
+    False -> {
+      float.absolute_value(start -. stop) /. int.to_float(steps)
+    }
+  }
+  case steps > 0 {
+    True -> {
+      Ok(
+        yielder.map(yielder.range(0, steps - 1), fn(i) {
+          start +. int.to_float(i) *. increment *. direction
+        }),
+      )
+    }
+    False -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns an iterator for generating logarithmically spaced points over a specified 
+/// interval. The endpoint of the interval can optionally be included/excluded. The number of 
+/// points, base, and whether the endpoint is included determine the spacing between values.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
+///       let pairs = logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0])
+///       let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
+///       result
+///       |> list.all(fn(x) { x == True })
+///       |> should.be_true()
+///     
+///       // A negative number of points (-3) does not work
+///       maths.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,
+  steps: Int,
+  endpoint: Bool,
+  base: Float,
+) -> Result(Yielder(Float), Nil) {
+  case steps > 0 {
+    True -> {
+      let assert Ok(linspace) = linear_space(start, stop, steps, endpoint)
+
+      Ok(
+        yielder.map(linspace, fn(i) {
+          let assert Ok(result) = float.power(base, i)
+          result
+        }),
+      )
+    }
+    False -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns an iterator of numbers spaced evenly on a log scale (a geometric
+/// progression). Each point in the list is a constant multiple of the previous. The function is
+/// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
+/// specified directly.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example () {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
+///       let pairs = logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0])
+///       let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
+///       result
+///       |> list.all(fn(x) { x == True })
+///       |> should.be_true()
+///     
+///       // Input (start and stop can't be equal to 0.0)
+///       maths.geometric_space(0.0, 1000.0, 3, False)
+///       |> should.be_error()
+///     
+///       maths.geometric_space(-1000.0, 0.0, 3, False)
+///       |> should.be_error()
+///     
+///       // A negative number of points (-3) does not work
+///       maths.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,
+  steps: Int,
+  endpoint: Bool,
+) -> Result(Yielder(Float), Nil) {
+  case start == 0.0 || stop == 0.0 {
+    True -> Error(Nil)
+    False ->
+      case steps > 0 {
+        True -> {
+          let assert Ok(log_start) = logarithm_10(start)
+          let assert Ok(log_stop) = logarithm_10(stop)
+          logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
+        }
+        False -> Error(Nil)
+      }
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// The function returns an iterator of exponentially spaced points over a specified interval. The 
+/// endpoint of the interval can optionally be included/excluded. The number of points and whether 
+/// the endpoint is included determine the spacing between values. The sequence is generated by 
+/// computing intermediate values in a logarithmic domain and transforming them into the exponential
+/// domain.
+/// 
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(tolerance) = float.power(10.0, -6.0)
+///       let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
+///       let expected = [1.0, 10.0, 100.0, 1000.0]
+///       let pairs = exp_space |> yielder.to_list() |> list.zip(expected)
+///       let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
+///       result |> list.all(fn(x) { x == True }) |> should.be_true()
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn exponential_space(
+  start: Float,
+  stop: Float,
+  steps: Int,
+  endpoint: Bool,
+) -> Result(Yielder(Float), Nil) {
+  case steps > 0 {
+    True -> {
+      let assert Ok(log_start) = logarithm_10(start)
+      let assert Ok(log_stop) = logarithm_10(stop)
+      let assert Ok(log_space) =
+        linear_space(log_start, log_stop, steps, endpoint)
+      Ok(
+        yielder.map(log_space, fn(log_value) {
+          let assert Ok(exp_value) = float.power(10.0, log_value)
+          exp_value
+        }),
+      )
+    }
+    False -> Error(Nil)
+  }
+}
+
+/// <div style="text-align: right;">
+///     <a href="https://github.com/gleam-community/maths/issues">
+///         <small>Spot a typo? Open an issue!</small>
+///     </a>
+/// </div>
+///
+/// Generates evenly spaced points around a center value. The total span is determined by
+/// the radius argument of the function.
+///
+/// <details>
+///     <summary>Example:</summary>
+///
+///     import gleam/yielder
+///     import gleeunit/should
+///     import gleam_community/maths
+///
+///     pub fn example() {
+///       let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
+///       sym_space
+///       |> yielder.to_list()
+///       |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
+///     }
+/// </details>
+///
+/// <div style="text-align: right;">
+///     <a href="#">
+///         <small>Back to top ↑</small>
+///     </a>
+/// </div>
+///
+pub fn symmetric_space(
+  center: Float,
+  radius: Float,
+  steps: Int,
+) -> Result(Yielder(Float), Nil) {
+  case steps > 0 {
+    False -> Error(Nil)
+    True -> {
+      let start = center -. radius
+      let stop = center +. radius
+      linear_space(start, stop, steps, True)
+    }
+  }
+}
diff --git a/src/gleam_community/maths/arithmetics.gleam b/src/gleam_community/maths/arithmetics.gleam
deleted file mode 100644
index b44f80a..0000000
--- a/src/gleam_community/maths/arithmetics.gleam
+++ /dev/null
@@ -1,720 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Arithmetics: A module containing a collection of fundamental mathematical functions relating to simple arithmetics (addition, subtraction, multiplication, etc.), but also number theory.
-////
-//// * **Division functions**
-////   * [`gcd`](#gcd)
-////   * [`lcm`](#lcm)
-////   * [`divisors`](#divisors)
-////   * [`proper_divisors`](#proper_divisors)
-////   * [`int_euclidean_modulo`](#int_euclidean_modulo)
-//// * **Sums and products**
-////   * [`float_sum`](#float_sum)
-////   * [`int_sum`](#int_sum)
-////   * [`float_product`](#float_product)
-////   * [`int_product`](#int_product)
-////   * [`float_cumulative_sum`](#float_cumulative_sum)
-////   * [`int_cumulative_sum`](#int_cumulative_sum)
-////   * [`float_cumulative_product`](#float_cumulative_product)
-////   * [`int_cumulative_product`](#int_cumulative_product)
-////
-
-import gleam/int
-import gleam/list
-import gleam/option
-import gleam/pair
-import gleam/result
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function calculates the greatest common divisor of two integers
-/// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive
-/// integer that is divisible by both \\(x\\) and \\(y\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example() {
-///       arithmetics.gcd(1, 1)
-///       |> should.equal(1)
-///
-///       arithmetics.gcd(100, 10)
-///       |> should.equal(10)
-///
-///       arithmetics.gcd(-36, -17)
-///       |> should.equal(1)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn gcd(x: Int, y: Int) -> Int {
-  let absx = piecewise.int_absolute_value(x)
-  let absy = piecewise.int_absolute_value(y)
-  do_gcd(absx, absy)
-}
-
-fn do_gcd(x: Int, y: Int) -> Int {
-  case x == 0 {
-    True -> y
-    False -> {
-      let assert Ok(z) = int.modulo(y, x)
-      do_gcd(z, x)
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-///
-/// Given two integers, \\(x\\) (dividend) and \\(y\\) (divisor), the Euclidean modulo
-/// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the
-/// division of \\(x\\) by \\(y\\), such that:
-///
-/// \\[
-/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|,
-/// \\]
-///
-/// where \\(q\\) is an integer that represents the quotient of the division.
-///
-/// The Euclidean modulo function of two numbers, is the remainder operation most
-/// commonly utilized in mathematics. This differs from the standard truncating
-/// modulo operation frequently employed in programming via the `%` operator.
-/// Unlike the `%` operator, which may return negative results depending on the
-/// divisor's sign, the Euclidean modulo function is designed to always yield a
-/// positive outcome, ensuring consistency with mathematical conventions.
-///
-/// Note that like the Gleam division operator `/` this will return `0` if one of
-/// the arguments is `0`.
-///
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example() {
-///       arithmetics.euclidean_modulo(15, 4)
-///       |> should.equal(3)
-///
-///       arithmetics.euclidean_modulo(-3, -2)
-///       |> should.equal(1)
-///
-///       arithmetics.euclidean_modulo(5, 0)
-///       |> should.equal(0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
-  case x % y, x, y {
-    _, 0, _ -> 0
-    _, _, 0 -> 0
-    md, _, _ if md < 0 -> md + int.absolute_value(y)
-    md, _, _ -> md
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function calculates the least common multiple of two integers
-/// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive
-/// integer that has both \\(x\\) and \\(y\\) as factors.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example() {
-///       arithmetics.lcm(1, 1)
-///       |> should.equal(1)
-///
-///       arithmetics.lcm(100, 10)
-///       |> should.equal(100)
-///
-///       arithmetics.lcm(-36, -17)
-///       |> should.equal(612)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn lcm(x: Int, y: Int) -> Int {
-  let absx = piecewise.int_absolute_value(x)
-  let absy = piecewise.int_absolute_value(y)
-  absx * absy / do_gcd(absx, absy)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns all the positive divisors of an integer, including the
-/// number itself.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example() {
-///       arithmetics.divisors(4)
-///       |> should.equal([1, 2, 4])
-///
-///       arithmetics.divisors(6)
-///       |> should.equal([1, 2, 3, 6])
-///
-///       arithmetics.proper_divisors(13)
-///       |> should.equal([1, 13])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn divisors(n: Int) -> List(Int) {
-  find_divisors(n)
-}
-
-fn find_divisors(n: Int) -> List(Int) {
-  let nabs = piecewise.float_absolute_value(conversion.int_to_float(n))
-  let assert Ok(sqrt_result) = elementary.square_root(nabs)
-  let max = conversion.float_to_int(sqrt_result) + 1
-  list.range(2, max)
-  |> list.fold([1, n], fn(acc, i) {
-    case n % i == 0 {
-      True -> [i, n / i, ..acc]
-      False -> acc
-    }
-  })
-  |> list.unique()
-  |> list.sort(int.compare)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns all the positive divisors of an integer, excluding the
-/// number iteself.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example() {
-///       arithmetics.proper_divisors(4)
-///       |> should.equal([1, 2])
-///
-///       arithmetics.proper_divisors(6)
-///       |> should.equal([1, 2, 3])
-///
-///       arithmetics.proper_divisors(13)
-///       |> should.equal([1])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn proper_divisors(n: Int) -> List(Int) {
-  let divisors = find_divisors(n)
-  divisors
-  |> list.take(list.length(divisors) - 1)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) sum of the elements in a list:
-///
-/// \\[
-/// \sum_{i=1}^n w_i x_i
-/// \\]
-///
-/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
-/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
-/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns an error
-///       []
-///       |> arithmetics.float_sum(option.None)
-///       |> should.equal(0.0)
-///
-///       // Valid input returns a result
-///       [1.0, 2.0, 3.0]
-///       |> arithmetics.float_sum(option.None)
-///       |> should.equal(6.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float {
-  case arr, weights {
-    [], _ -> 0.0
-    _, option.None ->
-      arr
-      |> list.fold(0.0, fn(acc, a) { a +. acc })
-    _, option.Some(warr) -> {
-      list.zip(arr, warr)
-      |> list.fold(0.0, fn(acc, a) { pair.first(a) *. pair.second(a) +. acc })
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the sum of the elements in a list:
-///
-/// \\[
-/// \sum_{i=1}^n x_i
-/// \\]
-///
-/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
-/// the value in the input list indexed by \\(i\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns 0
-///       []
-///       |> arithmetics.int_sum()
-///       |> should.equal(0)
-///
-///       // Valid input returns a result
-///       [1, 2, 3]
-///       |> arithmetics.int_sum()
-///       |> should.equal(6)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_sum(arr: List(Int)) -> Int {
-  case arr {
-    [] -> 0
-    _ ->
-      arr
-      |> list.fold(0, fn(acc, a) { a + acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) product of the elements in a list:
-///
-/// \\[
-/// \prod_{i=1}^n x_i^{w_i}
-/// \\]
-///
-/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
-/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
-/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns 1.0
-///       []
-///       |> arithmetics.float_product(option.None)
-///       |> should.equal(1.0)
-///
-///       // Valid input returns a result
-///       [1.0, 2.0, 3.0]
-///       |> arithmetics.float_product(option.None)
-///       |> should.equal(6.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn float_product(
-  arr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  case arr, weights {
-    [], _ ->
-      1.0
-      |> Ok
-    _, option.None ->
-      arr
-      |> list.fold(1.0, fn(acc, a) { a *. acc })
-      |> Ok
-    _, option.Some(warr) -> {
-      list.zip(arr, warr)
-      |> list.map(fn(a: #(Float, Float)) -> Result(Float, Nil) {
-        pair.first(a)
-        |> elementary.power(pair.second(a))
-      })
-      |> result.all
-      |> result.map(fn(prods) {
-        prods
-        |> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
-      })
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the product of the elements in a list:
-///
-/// \\[
-/// \prod_{i=1}^n x_i
-/// \\]
-///
-/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
-/// the value in the input list indexed by \\(i\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns 1
-///       []
-///       |> arithmetics.int_product()
-///       |> should.equal(1)
-///
-///       // Valid input returns a result
-///       [1, 2, 3]
-///       |> arithmetics.int_product()
-///       |> should.equal(6)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_product(arr: List(Int)) -> Int {
-  case arr {
-    [] -> 1
-    _ ->
-      arr
-      |> list.fold(1, fn(acc, a) { a * acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the cumulative sum of the elements in a list:
-///
-/// \\[
-/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
-/// \\]
-///
-/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
-/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\)
-/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
-/// sum of the \\(1\\) to \\(j\\) first elements in the given list.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       []
-///       |> arithmetics.float_cumulative_sum()
-///       |> should.equal([])
-///
-///       // Valid input returns a result
-///       [1.0, 2.0, 3.0]
-///       |> arithmetics.float_cumulative_sum()
-///       |> should.equal([1.0, 3.0, 6.0])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
-  case arr {
-    [] -> []
-    _ ->
-      arr
-      |> list.scan(0.0, fn(acc, a) { a +. acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the cumulative sum of the elements in a list:
-///
-/// \\[
-/// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
-/// \\]
-///
-/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
-/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\)
-/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
-/// sum of the \\(1\\) to \\(j\\) first elements in the given list.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       []
-///       |> arithmetics.int_cumulative_sum()
-///       |> should.equal([])
-///
-///       // Valid input returns a result
-///       [1, 2, 3]
-///       |> arithmetics.int_cumulative_sum()
-///       |> should.equal([1, 3, 6])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
-  case arr {
-    [] -> []
-    _ ->
-      arr
-      |> list.scan(0, fn(acc, a) { a + acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the cumulative product of the elements in a list:
-///
-/// \\[
-/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
-/// \\]
-///
-/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
-/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
-/// \\(x_i \in \mathbb{R}\\) is the value in the input list indexed by \\(i\\). The
-/// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the
-/// given list.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns an error
-///       []
-///       |> arithmetics.float_cumulative_product()
-///       |> should.equal([])
-///
-///       // Valid input returns a result
-///       [1.0, 2.0, 3.0]
-///       |> arithmetics.float_cumulative_product()
-///       |> should.equal([1.0, 2.0, 6.0])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
-  case arr {
-    [] -> []
-    _ ->
-      arr
-      |> list.scan(1.0, fn(acc, a) { a *. acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the cumulative product of the elements in a list:
-///
-/// \\[
-/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
-/// \\]
-///
-/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
-/// \\(n\\) elements. That is, \\(n\\) is the length of the list and
-/// \\(x_i \in \mathbb{Z}\\) is the value in the input list indexed by \\(i\\). The
-/// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the
-/// given list.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/arithmetics
-///
-///     pub fn example () {
-///       // An empty list returns an error
-///       []
-///       |> arithmetics.int_cumulative_product()
-///       |> should.equal([])
-///
-///       // Valid input returns a result
-///       [1, 2, 3]
-///       |> arithmetics.int_cumulative_product()
-///       |> should.equal([1, 2, 6])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_cumulative_product(arr: List(Int)) -> List(Int) {
-  case arr {
-    [] -> []
-    _ ->
-      arr
-      |> list.scan(1, fn(acc, a) { a * acc })
-  }
-}
diff --git a/src/gleam_community/maths/combinatorics.gleam b/src/gleam_community/maths/combinatorics.gleam
deleted file mode 100644
index a3a4e7e..0000000
--- a/src/gleam_community/maths/combinatorics.gleam
+++ /dev/null
@@ -1,630 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Combinatorics: A module that offers mathematical functions related to counting, arrangements,
-//// and permutations/combinations.
-////
-//// * **Combinatorial functions**
-////   * [`combination`](#combination)
-////   * [`factorial`](#factorial)
-////   * [`permutation`](#permutation)
-////   * [`list_combination`](#list_combination)
-////   * [`list_permutation`](#list_permutation)
-////   * [`cartesian_product`](#cartesian_product)
-////
-
-import gleam/iterator
-import gleam/list
-import gleam/option
-import gleam/set
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-
-pub type CombinatoricsMode {
-  WithRepetitions
-  WithoutRepetitions
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A combinatorial function for computing the number of \\(k\\)-combinations of \\(n\\) elements.
-///
-/// **Without Repetitions:**
-///
-/// \\[
-/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
-/// \\]
-/// Also known as "\\(n\\) choose \\(k\\)" or the binomial coefficient.
-///
-/// **With Repetitions:**
-///
-/// \\[
-/// C^*(n, k) = \binom{n + k - 1}{k} = \frac{(n + k - 1)!}{k! (n - 1)!}
-/// \\]
-/// Also known as the "stars and bars" problem in combinatorics.
-///
-/// The implementation uses an efficient iterative multiplicative formula for computing the result.
-///
-/// <details>
-/// <summary>Details</summary>
-///
-/// A \\(k\\)-combination is a sequence of \\(k\\) elements selected from \\(n\\) elements where
-/// the order of selection does not matter. For example, consider selecting 2 elements from a list
-/// of 3 elements: `["A", "B", "C"]`:
-///
-/// - For \\(k\\)-combinations (without repetitions), where order does not matter, the possible
-///   selections are:
-///   - `["A", "B"]`
-///   - `["A", "C"]`
-///   - `["B", "C"]`
-///
-/// - For \\(k\\)-combinations (with repetitions), where order does not matter but elements can
-///   repeat, the possible selections are:
-///   - `["A", "A"], ["A", "B"], ["A", "C"]`
-///   - `["B", "B"], ["B", "C"], ["C", "C"]`
-///
-/// - On the contrary, for \\(k\\)-permutations (without repetitions), the order matters, so the
-///   possible selections are:
-///   - `["A", "B"], ["B", "A"]`
-///   - `["A", "C"], ["C", "A"]`
-///   - `["B", "C"], ["C", "B"]`
-/// </details>
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/option
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example() {
-///       // Invalid input gives an error
-///       combinatorics.combination(-1, 1, option.None)
-///       |> should.be_error()
-///
-///       // Valid input: n = 4 and k = 0
-///       combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
-///       |> should.equal(Ok(1))
-///
-///       // Valid input: k = n (n = 4, k = 4)
-///       combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
-///       |> should.equal(Ok(1))
-///
-///       // Valid input: combinations with repetition (n = 2, k = 3)
-///       combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
-///       |> should.equal(Ok(4))
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn combination(
-  n: Int,
-  k: Int,
-  mode: option.Option(CombinatoricsMode),
-) -> Result(Int, Nil) {
-  case n, k {
-    _, _ if n < 0 -> Error(Nil)
-    _, _ if k < 0 -> Error(Nil)
-    _, _ -> {
-      case mode {
-        option.Some(WithRepetitions) -> combination_with_repetitions(n, k)
-        _ -> combination_without_repetitions(n, k)
-      }
-    }
-  }
-}
-
-fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
-  combination_without_repetitions(n + k - 1, k)
-}
-
-fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
-  case n, k {
-    _, _ if k == 0 || k == n -> {
-      1 |> Ok
-    }
-    _, _ -> {
-      let min = case k < n - k {
-        True -> k
-        False -> n - k
-      }
-      list.range(1, min)
-      |> list.fold(1, fn(acc, x) { acc * { n + 1 - x } / x })
-      |> Ok
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A combinatorial function for computing the total number of combinations of \\(n\\)
-/// elements, that is \\(n!\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example() {
-///       // Invalid input gives an error
-///       combinatorics.factorial(-1)
-///       |> should.be_error()
-///
-///       // Valid input returns a result (n = 0)
-///       combinatorics.factorial(0)
-///       |> should.equal(Ok(1))
-///
-///       combinatorics.factorial(3)
-///       |> should.equal(Ok(6))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn factorial(n) -> Result(Int, Nil) {
-  case n {
-    _ if n < 0 -> Error(Nil)
-    0 -> Ok(1)
-    1 -> Ok(1)
-    _ ->
-      list.range(1, n)
-      |> list.fold(1, fn(acc, x) { acc * x })
-      |> Ok
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A combinatorial function for computing the number of \\(k\\)-permutations.
-///
-/// **Without** repetitions:
-///
-/// \\[
-/// P(n, k) = \binom{n}{k} \cdot k! = \frac{n!}{(n - k)!}
-/// \\]
-///
-/// **With** repetitions:
-///
-/// \\[
-/// P^*(n, k) = n^k
-/// \\]
-///
-/// The implementation uses an efficient iterative multiplicative formula for computing the result.
-///
-/// <details>
-/// <summary>Details</summary>
-///
-/// A \\(k\\)-permutation (without repetitions) is a sequence of \\(k\\) elements selected from \
-/// \\(n\\) elements where the order of selection matters. For example, consider selecting 2
-/// elements from a list of 3 elements: `["A", "B", "C"]`:
-///
-/// - For \\(k\\)-permutations (without repetitions), the order matters, so the possible selections
-/// are:
-///   - `["A", "B"], ["B", "A"]`
-///   - `["A", "C"], ["C", "A"]`
-///   - `["B", "C"], ["C", "B"]`
-///
-/// - For \\(k\\)-permutations (with repetitions), the order also matters, but we have repeated
-///   selections:
-///   - `["A", "A"], ["A", "B"], ["A", "C"]`
-///   - `["B", "A"], ["B", "B"], ["B", "C"]`
-///   - `["C", "A"], ["C", "B"], ["C", "C"]`
-///
-/// - On the contrary, for \\(k\\)-combinations (without repetitions), where order does not matter,
-///   the possible selections are:
-///   - `["A", "B"]`
-///   - `["A", "C"]`
-///   - `["B", "C"]`
-/// </details>
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/option
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example() {
-///       // Invalid input gives an error
-///       combinatorics.permutation(-1, 1, option.None)
-///       |> should.be_error()
-///
-///       // Valid input returns a result (n = 4, k = 0)
-///       combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
-///       |> should.equal(Ok(1))
-///
-///       // Valid input returns the correct number of permutations (n = 4, k = 2)
-///       combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
-///       |> should.equal(Ok(12))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn permutation(
-  n: Int,
-  k: Int,
-  mode: option.Option(CombinatoricsMode),
-) -> Result(Int, Nil) {
-  case n, k, mode {
-    _, _, _ if n < 0 -> Error(Nil)
-    _, _, _ if k < 0 -> Error(Nil)
-    _, _, option.Some(WithRepetitions) -> permutation_with_repetitions(n, k)
-    _, _, _ -> permutation_without_repetitions(n, k)
-  }
-}
-
-fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
-  case n, k {
-    _, _ if k < 0 || k > n -> {
-      0 |> Ok
-    }
-    _, _ if k == 0 -> {
-      1 |> Ok
-    }
-    _, _ ->
-      list.range(0, k - 1)
-      |> list.fold(1, fn(acc, x) { acc * { n - x } })
-      |> Ok
-  }
-}
-
-fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, Nil) {
-  let n_float = conversion.int_to_float(n)
-  let k_float = conversion.int_to_float(k)
-  // 'n' ank 'k' are positive integers, so no errors here...
-  let assert Ok(result) = elementary.power(n_float, k_float)
-  result
-  |> conversion.float_to_int()
-  |> Ok
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Generates all possible combinations of \\(k\\) elements selected from a given list of size
-/// \\(n\\).
-///
-/// The function can handle cases with and without repetitions
-/// (see more details [here](#combination)). Also, note that repeated elements are treated as
-/// distinct.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/set
-///     import gleam/option
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example () {
-///       // Generate all 3-combinations without repetition
-///       let assert Ok(result) =
-///         combinatorics.list_combination(
-///           [1, 2, 3, 4],
-///           3,
-///           option.Some(combinatorics.WithoutRepetitions),
-///         )
-///
-///       result
-///       |> iterator.to_list()
-///       |> set.from_list()
-///       |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn list_combination(
-  arr: List(a),
-  k: Int,
-  mode: option.Option(CombinatoricsMode),
-) -> Result(iterator.Iterator(List(a)), Nil) {
-  case k, mode {
-    _, _ if k < 0 -> Error(Nil)
-    _, option.Some(WithRepetitions) -> list_combination_with_repetitions(arr, k)
-    _, _ -> list_combination_without_repetitions(arr, k)
-  }
-}
-
-fn list_combination_without_repetitions(
-  arr: List(a),
-  k: Int,
-) -> Result(iterator.Iterator(List(a)), Nil) {
-  case k, list.length(arr) {
-    _, arr_length if k > arr_length -> Error(Nil)
-    // Special case: When k = n, then the entire list is the only valid combination
-    _, arr_length if k == arr_length -> {
-      iterator.single(arr) |> Ok
-    }
-    _, _ -> {
-      Ok(
-        do_list_combination_without_repetitions(iterator.from_list(arr), k, []),
-      )
-    }
-  }
-}
-
-fn do_list_combination_without_repetitions(
-  arr: iterator.Iterator(a),
-  k: Int,
-  prefix: List(a),
-) -> iterator.Iterator(List(a)) {
-  case k {
-    0 -> iterator.single(list.reverse(prefix))
-    _ ->
-      case arr |> iterator.step {
-        iterator.Done -> iterator.empty()
-        iterator.Next(x, xs) -> {
-          let with_x =
-            do_list_combination_without_repetitions(xs, k - 1, [x, ..prefix])
-          let without_x = do_list_combination_without_repetitions(xs, k, prefix)
-          iterator.concat([with_x, without_x])
-        }
-      }
-  }
-}
-
-fn list_combination_with_repetitions(
-  arr: List(a),
-  k: Int,
-) -> Result(iterator.Iterator(List(a)), Nil) {
-  Ok(do_list_combination_with_repetitions(iterator.from_list(arr), k, []))
-}
-
-fn do_list_combination_with_repetitions(
-  arr: iterator.Iterator(a),
-  k: Int,
-  prefix: List(a),
-) -> iterator.Iterator(List(a)) {
-  case k {
-    0 -> iterator.single(list.reverse(prefix))
-    _ ->
-      case arr |> iterator.step {
-        iterator.Done -> iterator.empty()
-        iterator.Next(x, xs) -> {
-          let with_x =
-            do_list_combination_with_repetitions(arr, k - 1, [x, ..prefix])
-          let without_x = do_list_combination_with_repetitions(xs, k, prefix)
-          iterator.concat([with_x, without_x])
-        }
-      }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Generates all possible permutations of \\(k\\) elements selected from a given list of size
-/// \\(n\\).
-///
-/// The function can handle cases with and without repetitions
-/// (see more details [here](#permutation)). Also, note that repeated elements are treated as
-/// distinct.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/set
-///     import gleam/option
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example () {
-///       // Generate all 3-permutations without repetition
-///       let assert Ok(result) =
-///         combinatorics.list_permutation(
-///           [1, 2, 3],
-///           3,
-///           option.Some(combinatorics.WithoutRepetitions),
-///         )
-///
-///       result
-///       |> iterator.to_list()
-///       |> set.from_list()
-///       |> should.equal(
-///         set.from_list([
-///           [1, 2, 3],
-///           [2, 1, 3],
-///           [3, 1, 2],
-///           [1, 3, 2],
-///           [2, 3, 1],
-///           [3, 2, 1],
-///         ]),
-///       )
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-///
-pub fn list_permutation(
-  arr: List(a),
-  k: Int,
-  mode: option.Option(CombinatoricsMode),
-) -> Result(iterator.Iterator(List(a)), Nil) {
-  case k, mode {
-    _, _ if k < 0 -> Error(Nil)
-    _, option.Some(WithRepetitions) ->
-      Ok(list_permutation_with_repetitions(arr, k))
-    _, _ -> list_permutation_without_repetitions(arr, k)
-  }
-}
-
-fn remove_first_by_index(
-  arr: iterator.Iterator(#(Int, a)),
-  index_to_remove: Int,
-) -> iterator.Iterator(#(Int, a)) {
-  iterator.flat_map(arr, fn(arg) {
-    let #(index, element) = arg
-    case index == index_to_remove {
-      True -> iterator.empty()
-      False -> iterator.single(#(index, element))
-    }
-  })
-}
-
-fn list_permutation_without_repetitions(
-  arr: List(a),
-  k: Int,
-) -> Result(iterator.Iterator(List(a)), Nil) {
-  case k, list.length(arr) {
-    _, arr_length if k > arr_length -> Error(Nil)
-    _, _ -> {
-      let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
-      Ok(do_list_permutation_without_repetitions(
-        iterator.from_list(indexed_arr),
-        k,
-      ))
-    }
-  }
-}
-
-fn do_list_permutation_without_repetitions(
-  arr: iterator.Iterator(#(Int, a)),
-  k: Int,
-) -> iterator.Iterator(List(a)) {
-  case k {
-    0 -> iterator.single([])
-    _ ->
-      iterator.flat_map(arr, fn(arg) {
-        let #(index, element) = arg
-        let remaining = remove_first_by_index(arr, index)
-        let permutations =
-          do_list_permutation_without_repetitions(remaining, k - 1)
-        iterator.map(permutations, fn(permutation) { [element, ..permutation] })
-      })
-  }
-}
-
-fn list_permutation_with_repetitions(
-  arr: List(a),
-  k: Int,
-) -> iterator.Iterator(List(a)) {
-  let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
-  do_list_permutation_with_repetitions(indexed_arr, k)
-}
-
-fn do_list_permutation_with_repetitions(
-  arr: List(#(Int, a)),
-  k: Int,
-) -> iterator.Iterator(List(a)) {
-  case k {
-    0 -> iterator.single([])
-    _ ->
-      iterator.flat_map(arr |> iterator.from_list, fn(arg) {
-        let #(_, element) = arg
-        // Allow the same element (by index) to be reused in future recursive calls
-        let permutations = do_list_permutation_with_repetitions(arr, k - 1)
-        // Prepend the current element to each generated permutation
-        iterator.map(permutations, fn(permutation) { [element, ..permutation] })
-      })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Generate a list containing all combinations of pairs of elements coming from two given lists.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/set
-///     import gleeunit/should
-///     import gleam_community/maths/combinatorics
-///
-///     pub fn example () {
-///       // Cartesian product of two empty sets
-///       set.from_list([])
-///       |> combinatorics.cartesian_product(set.from_list([]))
-///       |> should.equal(set.from_list([]))
-///
-///       // Cartesian product of two sets with numeric values
-///       set.from_list([1.0, 10.0])
-///       |> combinatorics.cartesian_product(set.from_list([1.0, 2.0]))
-///       |> should.equal(
-///         set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
-///       )
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn cartesian_product(xset: set.Set(a), yset: set.Set(b)) -> set.Set(#(a, b)) {
-  xset
-  |> set.fold(set.new(), fn(accumulator0: set.Set(#(a, b)), member0: a) {
-    set.fold(yset, accumulator0, fn(accumulator1: set.Set(#(a, b)), member1: b) {
-      set.insert(accumulator1, #(member0, member1))
-    })
-  })
-}
diff --git a/src/gleam_community/maths/conversion.gleam b/src/gleam_community/maths/conversion.gleam
deleted file mode 100644
index 1355938..0000000
--- a/src/gleam_community/maths/conversion.gleam
+++ /dev/null
@@ -1,185 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-//// 
-//// ---
-//// 
-//// Conversion: A module containing various functions for converting between types and quantities.
-//// 
-//// * **Misc. functions**
-////   * [`float_to_int`](#float_to_int)
-////   * [`int_to_float`](#int_to_float)
-////   * [`degrees_to_radians`](#degrees_to_radians)
-////   * [`radians_to_degrees`](#radians_to_degrees)
-////
-
-import gleam/int
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that produces a number of type `Float` from an `Int`.
-/// 
-/// Note: The function is equivalent to the `int.to_float` function in the Gleam stdlib.
-/// 
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/conversion
-///
-///     pub fn example() {
-///       conversion.int_to_float(-1)
-///       |> should.equal(-1.0)
-///     
-///       conversion.int_to_float(1)
-///       |> should.equal(1.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn int_to_float(x: Int) -> Float {
-  int.to_float(x)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns the integral part of a given floating point value.
-/// That is, everything after the decimal point of a given floating point value is discarded
-/// and only the integer value before the decimal point is returned. 
-///
-/// <details>
-///     <summary>Example</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/conversion
-///     import gleam_community/maths/piecewise
-///
-///     pub fn example() {
-///       conversion.float_to_int(12.0654)
-///       |> should.equal(12)
-///       
-///       // Note: Making the following function call is equivalent
-///       // but instead of returning a value of type 'Int' a value
-///       // of type 'Float' is returned.
-///       piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundToZero))
-///       |> should.equal(Ok(12.0))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn float_to_int(x: Float) -> Int {
-  do_to_int(x)
-}
-
-@external(erlang, "erlang", "trunc")
-@external(javascript, "../../maths.mjs", "truncate")
-fn do_to_int(a: Float) -> Int
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Convert a value in degrees to a value measured in radians.
-/// That is, \\(1 \text{ degrees } = \frac{\pi}{180} \text{ radians }\\).
-///
-/// <details>
-///     <summary>Example</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/conversion
-///     import gleam_community/maths/elementary
-///
-///     pub fn example() {
-///       conversion.degrees_to_radians(360.)
-///       |> should.equal(2. *. elementary.pi())
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn degrees_to_radians(x: Float) -> Float {
-  x *. do_pi() /. 180.0
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Convert a value in degrees to a value measured in radians.
-/// That is, \\(1 \text{ radians } = \frac{180}{\pi} \text{ degrees }\\).
-///
-/// <details>
-///     <summary>Example</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/conversion
-///     import gleam_community/maths/elementary
-///
-///     pub fn example() {
-///       conversion.radians_to_degrees(0.0)
-///       |> should.equal(0.0)
-///
-///       conversion.radians_to_degrees(2. *. elementary.pi())
-///       |> should.equal(360.)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn radians_to_degrees(x: Float) -> Float {
-  x *. 180.0 /. do_pi()
-}
-
-@external(erlang, "math", "pi")
-@external(javascript, "../../maths.mjs", "pi")
-fn do_pi() -> Float
diff --git a/src/gleam_community/maths/elementary.gleam b/src/gleam_community/maths/elementary.gleam
deleted file mode 100644
index 491cab7..0000000
--- a/src/gleam_community/maths/elementary.gleam
+++ /dev/null
@@ -1,1192 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// 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, Nil) {
-  case x >=. -1.0 && x <=. 1.0 {
-    True -> Ok(do_acos(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >=. 1.0 {
-    True -> Ok(do_acosh(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >=. -1.0 && x <=. 1.0 {
-    True -> Ok(do_asin(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >. -1.0 && x <. 1.0 {
-    True -> Ok(do_atanh(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >. 0.0 {
-    True -> Ok(do_natural_logarithm(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >. 0.0, base {
-    True, option.Some(a) ->
-      case a >. 0.0 && a != 1.0 {
-        False -> Error(Nil)
-        True -> {
-          // Apply the "change of base formula"
-          let assert Ok(numerator) = logarithm_10(x)
-          let assert Ok(denominator) = logarithm_10(a)
-          Ok(numerator /. denominator)
-        }
-      }
-    _, _ -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The 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, Nil) {
-  case x >. 0.0 {
-    True -> Ok(do_logarithm_2(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  case x >. 0.0 {
-    True -> Ok(do_logarithm_10(x))
-    False -> Error(Nil)
-  }
-}
-
-@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, Nil) {
-  let fractional = 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 -> Error(Nil)
-    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, Nil) {
-  // 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 -> Error(Nil)
-    False -> power(x, 1.0 /. 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>
-///
-/// 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, Nil) {
-  // 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 -> Error(Nil)
-    False -> power(x, 1.0 /. 3.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>
-///
-/// 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, Nil) {
-  // 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 && n >= 1 {
-    True -> power(x, 1.0 /. int.to_float(n))
-    False -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The 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/src/gleam_community/maths/metrics.gleam b/src/gleam_community/maths/metrics.gleam
deleted file mode 100644
index 6c8959c..0000000
--- a/src/gleam_community/maths/metrics.gleam
+++ /dev/null
@@ -1,1265 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Metrics: A module offering functions for calculating distances and other
-//// types of metrics.
-////
-//// Disclaimer: In this module, the terms "distance" and "metric" are used in
-//// a broad and practical sense. That is, they are used to denote any difference
-//// or discrepancy between two inputs. Consequently, they may not align with their
-//// precise mathematical definitions (in particular, some "distance" functions in
-//// this module do not satisfy the triangle inequality).
-////
-//// * **Distance measures**
-////   * [`norm`](#norm)
-////   * [`manhattan_distance`](#manhattan_distance)
-////   * [`euclidean_distance`](#euclidean_distance)
-////   * [`chebyshev_distance`](#chebyshev_distance)
-////   * [`minkowski_distance`](#minkowski_distance)
-////   * [`cosine_similarity`](#cosine_similarity)
-////   * [`canberra_distance`](#canberra_distance)
-////   * [`braycurtis_distance`](#braycurtis_distance)
-//// * **Set & string similarity measures**
-////   * [`jaccard_index`](#jaccard_index)
-////   * [`sorensen_dice_coefficient`](#sorensen_dice_coefficient)
-////   * [`tversky_index`](#tversky_index)
-////   * [`overlap_coefficient`](#overlap_coefficient)
-//// * **Basic statistical measures**
-////   * [`mean`](#mean)
-////   * [`median`](#median)
-////   * [`variance`](#variance)
-////   * [`standard_deviation`](#standard_deviation)
-////
-
-import gleam/bool
-import gleam/float
-import gleam/int
-import gleam/list
-import gleam/option
-import gleam/pair
-import gleam/result
-import gleam/set
-import gleam_community/maths/arithmetics
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-
-/// Utility function that checks all lists have the expected length and contents
-/// The function is primarily used by all distance measures taking 'List(Float)'
-/// as input
-fn validate_lists(
-  xarr: List(Float),
-  yarr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Bool, Nil) {
-  case xarr, yarr {
-    [], _ -> Error(Nil)
-    _, [] -> Error(Nil)
-    _, _ -> {
-      let xarr_length = list.length(xarr)
-      let yarr_length = list.length(yarr)
-      case xarr_length == yarr_length, weights {
-        False, _ -> Error(Nil)
-        True, option.None -> {
-          True
-          |> Ok
-        }
-        True, option.Some(warr) -> {
-          let warr_length = list.length(warr)
-          case xarr_length == warr_length {
-            True -> {
-              validate_weights(warr)
-            }
-            False -> Error(Nil)
-          }
-        }
-      }
-    }
-  }
-}
-
-fn validate_weights(warr: List(Float)) -> Result(Bool, Nil) {
-  // Check that all the given weights are positive
-  let assert Ok(minimum) = piecewise.list_minimum(warr, float.compare)
-  case minimum >=. 0.0 {
-    False -> Error(Nil)
-    True -> Ok(True)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) \\(p\\)-norm of a list (representing a vector):
-///
-/// \\[
-/// \left( \sum_{i=1}^n w_{i} \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\\), while \\(w_i \in \mathbb{R}_{+}\\) is
-/// a corresponding positive weight (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     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)
-///
-///       let assert Ok(result) =
-///         [1.0, 1.0, 1.0]
-///         |> metrics.norm(1.0, option.None)
-///       result
-///       |> predicates.is_close(3.0, 0.0, tol)
-///       |> should.be_true()
-///
-///       let assert Ok(result) =
-///         [1.0, 1.0, 1.0]
-///         |> metrics.norm(-1.0, option.None)
-///       result
-///       |> predicates.is_close(0.3333333333333333, 0.0, tol)
-///       |> should.be_true()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn norm(
-  arr: List(Float),
-  p: Float,
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  case arr, weights {
-    [], _ ->
-      0.0
-      |> Ok
-    _, option.None -> {
-      let aggregate =
-        arr
-        |> list.fold(0.0, fn(accumulator, element) {
-          let assert Ok(result) =
-            piecewise.float_absolute_value(element)
-            |> elementary.power(p)
-          result +. accumulator
-        })
-      let assert Ok(result) = elementary.power(aggregate, 1.0 /. p)
-      result
-      |> Ok
-    }
-    _, option.Some(warr) -> {
-      let arr_length = list.length(arr)
-      let warr_length = list.length(warr)
-      case arr_length == warr_length {
-        True -> {
-          case validate_weights(warr) {
-            Ok(_) -> {
-              let tuples = list.zip(arr, warr)
-              let aggregate =
-                tuples
-                |> list.fold(0.0, fn(accumulator, tuple) {
-                  let first_element = pair.first(tuple)
-                  let second_element = pair.second(tuple)
-                  let assert Ok(result) =
-                    elementary.power(
-                      piecewise.float_absolute_value(first_element),
-                      p,
-                    )
-                  second_element *. result +. accumulator
-                })
-              let assert Ok(result) = elementary.power(aggregate, 1.0 /. p)
-              result
-              |> Ok
-            }
-            Error(msg) ->
-              msg
-              |> Error
-          }
-        }
-        False -> Error(Nil)
-      }
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) Manhattan distance between two lists (representing
-/// vectors):
-///
-/// \\[
-/// \sum_{i=1}^n w_{i} \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\\), while the
-/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/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 an error
-///       metrics.manhattan_distance([], [], option.None)
-///       |> should.be_error()
-///
-///       // Differing lengths returns error
-///       metrics.manhattan_distance([], [1.0], option.None)
-///       |> should.be_error()
-///
-///       let assert Ok(result) =
-///         metrics.manhattan_distance([0.0, 0.0], [1.0, 2.0], option.None)
-///       result
-///       |> predicates.is_close(3.0, 0.0, tol)
-///       |> should.be_true()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn manhattan_distance(
-  xarr: List(Float),
-  yarr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  minkowski_distance(xarr, yarr, 1.0, weights)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) Minkowski distance between two lists (representing
-/// vectors):
-///
-/// \\[
-/// \left( \sum_{i=1}^n w_{i} \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\\).
-/// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// 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/option
-///     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 an error
-///       metrics.minkowski_distance([], [], 1.0, option.None)
-///       |> should.be_error()
-///
-///       // Differing lengths returns error
-///       metrics.minkowski_distance([], [1.0], 1.0, option.None)
-///       |> should.be_error()
-///
-///       // Test order < 1
-///       metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0, option.None)
-///       |> should.be_error()
-///
-///       let assert Ok(result) =
-///         metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0, option.None)
-///       result
-///       |> predicates.is_close(3.0, 0.0, tol)
-///       |> should.be_true()
-///     }
-/// </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,
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  use _ <- result.try(validate_lists(xarr, yarr, weights))
-  case p <. 1.0 {
-    True -> Error(Nil)
-    False -> {
-      let differences: List(Float) =
-        list.zip(xarr, yarr)
-        |> list.map(fn(tuple: #(Float, Float)) -> Float {
-          pair.first(tuple) -. pair.second(tuple)
-        })
-      norm(differences, p, weights)
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) Euclidean distance between two lists (representing
-/// vectors):
-///
-/// \\[
-/// \left( \sum_{i=1}^n w_{i} \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\\), while the
-/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/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 an error
-///       metrics.euclidean_distance([], [], option.None)
-///       |> should.be_error()
-///
-///       // Differing lengths returns an error
-///       metrics.euclidean_distance([], [1.0], option.None)
-///       |> should.be_error()
-///
-///       let assert Ok(result) =
-///         metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0], option.None)
-///       result
-///       |> predicates.is_close(2.23606797749979, 0.0, tol)
-///       |> should.be_true()
-///     }
-/// </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),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  minkowski_distance(xarr, yarr, 2.0, weights)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the Chebyshev distance between two lists (representing vectors):
-///
-/// \\[
-/// \text{max}_{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\\).
-///
-/// <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() {
-///       // Empty lists returns an error
-///       metrics.chebyshev_distance([], [])
-///       |> should.be_error()
-///
-///       // Differing lengths returns error
-///       metrics.chebyshev_distance([], [1.0])
-///       |> should.be_error()
-///
-///       metrics.chebyshev_distance([-5.0, -10.0, -3.0], [-1.0, -12.0, -3.0])
-///       |> should.equal(Ok(4.0))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn chebyshev_distance(
-  xarr: List(Float),
-  yarr: List(Float),
-) -> Result(Float, Nil) {
-  case validate_lists(xarr, yarr, option.None) {
-    Error(msg) ->
-      msg
-      |> Error
-    Ok(_) -> {
-      list.zip(xarr, yarr)
-      |> list.map(fn(tuple) {
-        { pair.first(tuple) -. pair.second(tuple) }
-        |> piecewise.float_absolute_value()
-      })
-      |> piecewise.list_maximum(float.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>
-///
-/// Calculate 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, Nil) {
-  case arr {
-    [] -> Error(Nil)
-    _ ->
-      arr
-      |> arithmetics.float_sum(option.None)
-      |> fn(a) { a /. conversion.int_to_float(list.length(arr)) }
-      |> 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>
-///
-/// Calculate 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, Nil) {
-  use <- bool.guard(list.is_empty(arr), Error(Nil))
-  let length = list.length(arr)
-  let mid = length / 2
-
-  case length % 2 == 0 {
-    True -> do_median(arr, mid, True, 0)
-    False -> do_median(arr, mid, False, 0)
-  }
-}
-
-fn do_median(
-  xs: List(Float),
-  mid: Int,
-  mean: Bool,
-  index: Int,
-) -> Result(Float, Nil) {
-  use <- bool.guard(index > mid, Error(Nil))
-  let mid_less_one = mid - 1
-
-  case xs {
-    [x, ..] if !mean && index == mid -> Ok(x)
-    [x, y, ..] if mean && index == mid_less_one -> Ok({ x +. y } /. 2.0)
-    [_, ..rest] -> do_median(rest, mid, mean, index + 1)
-    [] -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate 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 = 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, Nil) {
-  case arr, ddof {
-    [], _ -> Error(Nil)
-    _, _ if ddof < 0 -> Error(Nil)
-    _, _ -> {
-      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(option.None)
-      |> 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/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate 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 = 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, Nil) {
-  case arr, ddof {
-    [], _ -> Error(Nil)
-    _, _ if ddof < 0 -> Error(Nil)
-    _, _ -> {
-      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
-    }
-  }
-}
-
-/// <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 Jaccard index measures similarity between two sets of elements.
-/// Mathematically, the Jaccard index is defined as:
-///
-/// \\[
-/// \frac{|X \cap Y|}{|X \cup Y|} \\; \in \\; \left[0, 1\right]
-/// \\]
-///
-/// where:
-///
-/// - \\(X\\) and \\(Y\\) are two sets being compared,
-/// - \\(|X \cap Y|\\) represents the size of the intersection of the two sets
-/// - \\(|X \cup Y|\\) denotes the size of the union of the two sets
-///
-/// The value of the Jaccard index ranges from 0 to 1, where 0 indicates that the
-/// two sets share no elements and 1 indicates that the sets are identical. The
-/// Jaccard index is a special case of the  [Tversky index](#tversky_index) (with
-/// \\(\alpha=\beta=1\\)).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/metrics
-///     import gleam/set
-///
-///     pub fn example () {
-///       let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
-///       let yset =
-///         set.from_list(["monkey", "rhino", "ostrich", "salmon"])
-///       metrics.jaccard_index(xset, yset)
-///       |> should.equal(1.0 /. 7.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
-  let assert Ok(result) = tversky_index(xset, yset, 1.0, 1.0)
-  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>
-///
-/// The Sørensen-Dice coefficient measures the similarity between two sets of
-/// elements. Mathematically, the coefficient is defined as:
-///
-/// \\[
-/// \frac{2 |X \cap Y|}{|X| + |Y|} \\; \in \\; \left[0, 1\right]
-/// \\]
-///
-/// where:
-/// - \\(X\\) and \\(Y\\) are two sets being compared
-/// - \\(|X \cap Y|\\) is the size of the intersection of the two sets (i.e., the
-/// number of elements common to both sets)
-/// - \\(|X|\\) and \\(|Y|\\) are the sizes of the sets \\(X\\) and \\(Y\\), respectively
-///
-/// The coefficient ranges from 0 to 1, where 0 indicates no similarity (the sets
-/// share no elements) and 1 indicates perfect similarity (the sets are identical).
-/// The higher the coefficient, the greater the similarity between the two sets.
-/// The Sørensen-Dice coefficient is a special case of the
-/// [Tversky index](#tversky_index) (with \\(\alpha=\beta=0.5\\)).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/metrics
-///     import gleam/set
-///
-///     pub fn example () {
-///       let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
-///       let yset =
-///         set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
-///       metrics.sorensen_dice_coefficient(xset, yset)
-///       |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
-  let assert Ok(result) = tversky_index(xset, yset, 0.5, 0.5)
-  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>
-///
-/// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice
-/// coefficient, which adds flexibility through two parameters, \\(\alpha\\) and
-/// \\(\beta\\), allowing for asymmetric similarity measures between sets. The
-/// Tversky index is defined as:
-///
-/// \\[
-/// \frac{|X \cap Y|}{|X \cap Y| + \alpha|X - Y| + \beta|Y - X|}
-/// \\]
-///
-/// where:
-///
-/// - \\(X\\) and \\(Y\\) are the sets being compared
-/// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of
-/// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively,
-/// - \\(\alpha\\) and \\(\beta\\) are parameters that weigh the relative importance
-/// of the elements unique to \\(X\\) and \\(Y\\)
-///
-/// The Tversky index reduces to the Jaccard index when \\(\alpha = \beta = 1\\) and
-/// to the Sørensen-Dice coefficient when \\(\alpha = \beta = 0.5\\). In general, the
-/// Tversky index can take on any non-negative value, including 0. The index equals
-/// 0 when there is no intersection between the two sets, indicating no similarity.
-/// However, unlike similarity measures bounded strictly between 0 and 1, the
-/// Tversky index does not have a strict upper limit of 1 when \\(\alpha \neq \beta\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/metrics
-///     import gleam/set
-///
-///     pub fn example () {
-///       let yset = set.from_list(["cat", "dog", "hippo", "monkey"])
-///       let xset =
-///         set.from_list(["monkey", "rhino", "ostrich", "salmon"])
-///       // Test Jaccard index (alpha = beta = 1)
-///       metrics.tversky_index(xset, yset, 1.0, 1.0)
-///       |> should.equal(1.0 /. 7.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn tversky_index(
-  xset: set.Set(a),
-  yset: set.Set(a),
-  alpha: Float,
-  beta: Float,
-) -> Result(Float, Nil) {
-  case alpha >=. 0.0, beta >=. 0.0 {
-    True, True -> {
-      let intersection =
-        set.intersection(xset, yset)
-        |> set.size()
-        |> conversion.int_to_float()
-      let difference1 =
-        set.difference(xset, yset)
-        |> set.size()
-        |> conversion.int_to_float()
-      let difference2 =
-        set.difference(yset, xset)
-        |> set.size()
-        |> conversion.int_to_float()
-      intersection
-      /. { intersection +. alpha *. difference1 +. beta *. difference2 }
-      |> Ok
-    }
-    _, _ -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The Overlap coefficient, also known as the Szymkiewicz–Simpson coefficient, is
-/// a measure of similarity between two sets that focuses on the size of the
-/// intersection relative to the smaller of the two sets. It is defined
-/// mathematically as:
-///
-/// \\[
-/// \frac{|X \cap Y|}{\min(|X|, |Y|)} \\; \in \\; \left[0, 1\right]
-/// \\]
-///
-/// where:
-///
-/// - \\(X\\) and \\(Y\\) are the sets being compared
-/// - \\(|X \cap Y|\\) is the size of the intersection of the sets
-/// - \\(\min(|X|, |Y|)\\) is the size of the smaller set among \\(X\\) and \\(Y\\)
-///
-/// The coefficient ranges from 0 to 1, where 0 indicates no overlap and 1
-/// indicates that the smaller set is a suyset of the larger set. This
-/// measure is especially useful in situations where the similarity in terms
-/// of the proportion of overlap is more relevant than the difference in sizes
-/// between the two sets.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/metrics
-///     import gleam/set
-///
-///     pub fn example () {
-///       let set_a =
-///         set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
-///       let set_b =
-///         set.from_list(["monkey", "bird", "ostrich", "salmon"])
-///       metrics.overlap_coefficient(set_a, set_b)
-///       |> should.equal(2.0 /. 4.0)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
-  let intersection =
-    set.intersection(xset, yset)
-    |> set.size()
-    |> conversion.int_to_float()
-  let minsize =
-    piecewise.minimum(set.size(xset), set.size(yset), int.compare)
-    |> conversion.int_to_float()
-  intersection /. minsize
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) cosine similarity between two lists (representing
-/// vectors):
-///
-/// \\[
-/// \frac{\sum_{i=1}^n w_{i} \cdot x_i \cdot y_i}
-/// {\left(\sum_{i=1}^n w_{i} \cdot x_i^2\right)^{\frac{1}{2}}
-/// \cdot
-/// \left(\sum_{i=1}^n w_{i} \cdot y_i^2\right)^{\frac{1}{2}}}
-/// \\; \in \\; \left[-1, 1\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\\), while the
-/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// The cosine similarity provides a value between -1 and 1, where 1 means the
-/// vectors are in the same direction, -1 means they are in exactly opposite
-/// directions, and 0 indicates orthogonality.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/metrics
-///
-///     pub fn example() {
-///       // Two orthogonal vectors
-///       metrics.cosine_similarity([-1.0, 1.0, 0.0], [1.0, 1.0, -1.0], option.None)
-///       |> should.equal(Ok(0.0))
-///
-///       // Two identical (parallel) vectors
-///       metrics.cosine_similarity([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], option.None)
-///       |> should.equal(Ok(1.0))
-///
-///       // Two parallel, but oppositely oriented vectors
-///       metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None)
-///       |> should.equal(Ok(-1.0))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn cosine_similarity(
-  xarr: List(Float),
-  yarr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  case validate_lists(xarr, yarr, weights) {
-    Error(msg) ->
-      msg
-      |> Error
-    Ok(_) -> {
-      let zipped_arr = list.zip(xarr, yarr)
-
-      let numerator_elements =
-        zipped_arr
-        |> list.map(fn(tuple) { pair.first(tuple) *. pair.second(tuple) })
-
-      case weights {
-        option.None -> {
-          let numerator =
-            numerator_elements
-            |> arithmetics.float_sum(option.None)
-
-          let assert Ok(xarr_norm) = norm(xarr, 2.0, option.None)
-          let assert Ok(yarr_norm) = norm(yarr, 2.0, option.None)
-          let denominator = {
-            xarr_norm *. yarr_norm
-          }
-          numerator /. denominator
-          |> Ok
-        }
-        _ -> {
-          let numerator =
-            numerator_elements
-            |> arithmetics.float_sum(weights)
-
-          let assert Ok(xarr_norm) = norm(xarr, 2.0, weights)
-          let assert Ok(yarr_norm) = norm(yarr, 2.0, weights)
-          let denominator = {
-            xarr_norm *. yarr_norm
-          }
-          numerator /. denominator
-          |> 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>
-///
-/// Calculate the (weighted) Canberra distance between two lists:
-///
-/// \\[
-/// \sum_{i=1}^n w_{i}\frac{\left| x_i - y_i \right|}
-/// {\left| x_i \right| + \left| 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\\), while the
-/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/metrics
-///
-///     pub fn example() {
-///       // Empty lists returns an error
-///       metrics.canberra_distance([], [], option.None)
-///       |> should.be_error()
-///
-///       // Different sized lists returns an error
-///       metrics.canberra_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
-///       |> should.be_error()
-///
-///       // Valid inputs
-///       metrics.canberra_distance([1.0, 2.0], [-2.0, -1.0], option.None)
-///       |> should.equal(Ok(2.0))
-///
-///       metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 0.5]))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn canberra_distance(
-  xarr: List(Float),
-  yarr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  case validate_lists(xarr, yarr, weights) {
-    Error(msg) ->
-      msg
-      |> Error
-    Ok(_) -> {
-      let arr =
-        list.zip(xarr, yarr)
-        |> list.map(canberra_distance_helper)
-
-      case weights {
-        option.None -> {
-          arr
-          |> arithmetics.float_sum(option.None)
-          |> Ok
-        }
-        _ -> {
-          arr
-          |> arithmetics.float_sum(weights)
-          |> Ok
-        }
-      }
-    }
-  }
-}
-
-fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
-  let numerator =
-    piecewise.float_absolute_value({ pair.first(tuple) -. pair.second(tuple) })
-  let denominator = {
-    piecewise.float_absolute_value(pair.first(tuple))
-    +. piecewise.float_absolute_value(pair.second(tuple))
-  }
-  numerator /. denominator
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Calculate the (weighted) Bray-Curtis distance between two lists:
-///
-/// \\[
-/// \frac{\sum_{i=1}^n w_{i} \left| x_i - y_i \right|}
-/// {\sum_{i=1}^n w_{i}\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\\), while the
-/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
-/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
-///
-/// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are
-/// positive.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/option
-///     import gleam_community/maths/metrics
-///
-///     pub fn example() {
-///       // Empty lists returns an error
-///       metrics.braycurtis_distance([], [], option.None)
-///       |> should.be_error()
-///
-///       // Different sized lists returns an error
-///       metrics.braycurtis_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
-///       |> should.be_error()
-///
-///       // Valid inputs
-///       metrics.braycurtis_distance([1.0, 0.0], [0.0, 2.0], option.None)
-///       |> should.equal(Ok(1.0))
-///
-///       metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.5, 1.0]))
-///       |> should.equal(Ok(0.375))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-///
-pub fn braycurtis_distance(
-  xarr: List(Float),
-  yarr: List(Float),
-  weights: option.Option(List(Float)),
-) -> Result(Float, Nil) {
-  case validate_lists(xarr, yarr, weights) {
-    Error(msg) ->
-      msg
-      |> Error
-    Ok(_) -> {
-      let zipped_arr = list.zip(xarr, yarr)
-      let numerator_elements =
-        zipped_arr
-        |> list.map(fn(tuple) {
-          piecewise.float_absolute_value({
-            pair.first(tuple) -. pair.second(tuple)
-          })
-        })
-      let denominator_elements =
-        zipped_arr
-        |> list.map(fn(tuple) {
-          piecewise.float_absolute_value({
-            pair.first(tuple) +. pair.second(tuple)
-          })
-        })
-
-      case weights {
-        option.None -> {
-          let numerator =
-            numerator_elements
-            |> arithmetics.float_sum(option.None)
-          let denominator =
-            denominator_elements
-            |> arithmetics.float_sum(option.None)
-          { numerator /. denominator }
-          |> Ok
-        }
-        _ -> {
-          let numerator =
-            numerator_elements
-            |> arithmetics.float_sum(weights)
-          let denominator =
-            denominator_elements
-            |> arithmetics.float_sum(weights)
-          { numerator /. denominator }
-          |> Ok
-        }
-      }
-    }
-  }
-}
diff --git a/src/gleam_community/maths/piecewise.gleam b/src/gleam_community/maths/piecewise.gleam
deleted file mode 100644
index cebdc38..0000000
--- a/src/gleam_community/maths/piecewise.gleam
+++ /dev/null
@@ -1,1215 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// 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/int
-import gleam/list
-import gleam/option
-import gleam/order
-import gleam/pair
-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(12.1)
-///
-///       piecewise.ceiling(12.0654, option.Some(2))
-///       |> should.equal(12.07)
-///
-///       piecewise.ceiling(12.0654, option.Some(3))
-///       |> should.equal(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)) -> Float {
-  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(12.0)
-///
-///       piecewise.floor(12.0654, option.Some(2))
-///       |> should.equal(12.06)
-///
-///       piecewise.floor(12.0654, option.Some(3))
-///       |> should.equal(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)) -> Float {
-  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(12.0)
-///
-///       piecewise.truncate(12.0654, option.Some(2))
-///       |> should.equal(12.06)
-///
-///       piecewise.truncate(12.0654, option.Some(3))
-///       |> should.equal(12.065)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
-  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(12.0)
-///
-///       // The default rounding mode is "RoundNearest" if None is provided
-///       piecewise.round(12.0654, option.None, option.None)
-///       |> should.equal(12.0)
-///
-///       // Try different rounding modes
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest))
-///       |> should.equal(12.07)
-///
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway))
-///       |> should.equal(12.07)
-///
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp))
-///       |> should.equal(12.07)
-///
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundToZero))
-///       |> should.equal(12.06)
-///
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundDown))
-///       |> should.equal(12.06)
-///
-///       piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundUp))
-///       |> should.equal(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),
-) -> Float {
-  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)) -> Float {
-  case mode {
-    // Determine the rounding mode
-    option.Some(RoundNearest) -> round_to_nearest(p, x)
-    option.Some(RoundTiesAway) -> round_ties_away(p, x)
-    option.Some(RoundTiesUp) -> round_ties_up(p, x)
-    option.Some(RoundToZero) -> round_to_zero(p, x)
-    option.Some(RoundDown) -> round_down(p, x)
-    option.Some(RoundUp) -> round_up(p, x)
-    // Otherwise, use the default rounding mode
-    option.None -> round_to_nearest(p, x)
-  }
-}
-
-fn round_to_nearest(p: Float, x: Float) -> Float {
-  let xabs = float_absolute_value(x) *. p
-  let xabs_truncated = truncate_float(xabs)
-  let remainder = 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_absolute_value(x) *. p
-  let remainder = 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_absolute_value(x) *. p
-  let xabs_truncated = truncate_float(xabs)
-  let remainder = 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 \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 \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) {
-  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) {
-  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) {
-  #(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, Nil) {
-  case arr {
-    [] -> Error(Nil)
-    [x, ..rest] ->
-      Ok(
-        list.fold(rest, x, fn(acc, element) {
-          case compare(element, acc) {
-            order.Lt -> element
-            _ -> acc
-          }
-        }),
-      )
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Returns the 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, Nil) {
-  case arr {
-    [] -> Error(Nil)
-    [x, ..rest] ->
-      Ok(
-        list.fold(rest, x, fn(acc, element) {
-          case compare(acc, element) {
-            order.Lt -> element
-            _ -> acc
-          }
-        }),
-      )
-  }
-}
-
-/// <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), Nil) {
-  case arr {
-    [] -> Error(Nil)
-    _ -> {
-      let assert Ok(min) =
-        arr
-        |> list_minimum(compare)
-      arr
-      |> list.index_map(fn(element, index) {
-        case compare(element, min) {
-          order.Eq -> index
-          _ -> -1
-        }
-      })
-      |> list.filter(fn(index) {
-        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), Nil) {
-  case arr {
-    [] -> Error(Nil)
-    _ -> {
-      let assert Ok(max) =
-        arr
-        |> list_maximum(compare)
-      arr
-      |> list.index_map(fn(element, index) {
-        case compare(element, max) {
-          order.Eq -> index
-          _ -> -1
-        }
-      })
-      |> list.filter(fn(index) {
-        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), Nil) {
-  case arr {
-    [] -> Error(Nil)
-    [x, ..rest] ->
-      Ok(
-        list.fold(rest, #(x, x), fn(acc, element) {
-          let first = pair.first(acc)
-          let second = 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)
-          }
-        }),
-      )
-  }
-}
diff --git a/src/gleam_community/maths/predicates.gleam b/src/gleam_community/maths/predicates.gleam
deleted file mode 100644
index 73e5743..0000000
--- a/src/gleam_community/maths/predicates.gleam
+++ /dev/null
@@ -1,590 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Predicates: A module containing functions for testing various mathematical
-//// properties of numbers.
-////
-//// * **Tests**
-////   * [`is_close`](#is_close)
-////   * [`list_all_close`](#all_close)
-////   * [`is_fractional`](#is_fractional)
-////   * [`is_between`](#is_between)
-////   * [`is_power`](#is_power)
-////   * [`is_perfect`](#is_perfect)
-////   * [`is_even`](#is_even)
-////   * [`is_odd`](#is_odd)
-////   * [`is_divisible`](#is_divisible)
-////   * [`is_multiple`](#is_multiple)
-////   * [`is_prime`](#is_prime)
-////
-
-import gleam/int
-import gleam/list
-import gleam/option
-import gleam/pair
-import gleam_community/maths/arithmetics
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Determine if a given value \\(a\\) is close to or equivalent to a reference value
-/// \\(b\\) based on supplied relative \\(r_{tol}\\) and absolute \\(a_{tol}\\) tolerance
-/// values. The equivalance of the two given values are then determined based on
-/// the equation:
-///
-/// \\[
-///     \|a - b\| \leq (a_{tol} + r_{tol} \cdot \|b\|)
-/// \\]
-///
-/// `True` is returned if statement holds, otherwise `False` is returned.
-/// <details>
-///     <summary>Example</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       let val = 99.
-///       let ref_val = 100.
-///       // We set 'atol' and 'rtol' such that the values are equivalent
-///       // if 'val' is within 1 percent of 'ref_val' +/- 0.1
-///       let rtol = 0.01
-///       let atol = 0.10
-///       floatx.is_close(val, ref_val, rtol, atol)
-///       |> should.be_true()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_close(a: Float, b: Float, rtol: Float, atol: Float) -> Bool {
-  let x = float_absolute_difference(a, b)
-  let y = atol +. rtol *. float_absolute_value(b)
-  case x <=. y {
-    True -> True
-    False -> False
-  }
-}
-
-fn float_absolute_value(x: Float) -> Float {
-  case x >. 0.0 {
-    True -> x
-    False -> -1.0 *. x
-  }
-}
-
-fn float_absolute_difference(a: Float, b: Float) -> Float {
-  a -. b
-  |> float_absolute_value()
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Determine if a list of values are close to or equivalent to a another list of
-/// reference values.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam/list
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       let val = 99.
-///       let ref_val = 100.
-///       let xarr = list.repeat(val, 42)
-///       let yarr = list.repeat(ref_val, 42)
-///       // We set 'atol' and 'rtol' such that the values are equivalent
-///       // if 'val' is within 1 percent of 'ref_val' +/- 0.1
-///       let rtol = 0.01
-///       let atol = 0.10
-///       predicates.all_close(xarr, yarr, rtol, atol)
-///       |> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) {
-///         case zarr {
-///           Ok(arr) ->
-///             arr
-///             |> list.all(fn(a) { a })
-///             |> Ok
-///           _ -> Nil |> Error
-///         }
-///       }
-///       |> should.equal(Ok(True))
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn all_close(
-  xarr: List(Float),
-  yarr: List(Float),
-  rtol: Float,
-  atol: Float,
-) -> Result(List(Bool), Nil) {
-  let xlen = list.length(xarr)
-  let ylen = list.length(yarr)
-  case xlen == ylen {
-    False -> Error(Nil)
-    True ->
-      list.zip(xarr, yarr)
-      |> list.map(fn(z) { is_close(pair.first(z), pair.second(z), rtol, atol) })
-      |> Ok
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// Determine if a given value is fractional.
-///
-/// `True` is returned if the given value is fractional, otherwise `False` is
-/// returned.
-///
-/// <details>
-///     <summary>Example</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       predicates.is_fractional(0.3333)
-///       |> should.equal(True)
-///
-///       predicates.is_fractional(1.0)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_fractional(x: Float) -> Bool {
-  do_ceiling(x) -. x >. 0.0
-}
-
-@external(erlang, "math", "ceil")
-@external(javascript, "../../maths.mjs", "ceiling")
-fn do_ceiling(a: Float) -> Float
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
-/// power of another integer value \\(y \in \mathbb{Z}\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       // Check if 4 is a power of 2 (it is)
-///       predicates.is_power(4, 2)
-///       |> should.equal(True)
-///
-///       // Check if 5 is a power of 2 (it is not)
-///       predicates.is_power(5, 2)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_power(x: Int, y: Int) -> Bool {
-  let assert Ok(value) =
-    elementary.logarithm(int.to_float(x), option.Some(int.to_float(y)))
-  let truncated = piecewise.truncate(value, option.Some(0))
-  let remainder = value -. truncated
-  remainder == 0.0
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer value \\(n \in \mathbb{Z}\\) is a
-/// perfect number. A number is perfect if it is equal to the sum of its proper
-/// positive divisors.
-///
-/// <details>
-///     <summary>Details</summary>
-///
-///   For example:
-///   - \\(6\\) is a perfect number since the divisors of 6 are \\(1 + 2 + 3 = 6\\).
-///   - \\(28\\) is a perfect number since the divisors of 28 are \\(1 + 2 + 4 + 7 + 14 = 28\\).
-///
-/// </details>
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_perfect(6)
-///       |> should.equal(True)
-///
-///       predicates.is_perfect(28)
-///       |> should.equal(True)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_perfect(n: Int) -> Bool {
-  do_sum(arithmetics.proper_divisors(n)) == n
-}
-
-fn do_sum(arr: List(Int)) -> Int {
-  case arr {
-    [] -> 0
-    _ ->
-      arr
-      |> list.fold(0, fn(acc, a) { a + acc })
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_even(-3)
-///       |> should.equal(False)
-///
-///       predicates.is_even(-4)
-///       |> should.equal(True)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_even(x: Int) -> Bool {
-  x % 2 == 0
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_odd(-3)
-///       |> should.equal(True)
-///
-///       predicates.is_odd(-4)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_odd(x: Int) -> Bool {
-  x % 2 != 0
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
-/// prime number. A prime number is a natural number greater than 1 that has no
-/// positive divisors other than 1 and itself.
-///
-/// The function uses the Miller-Rabin primality test to assess if \\(x\\) is prime.
-/// It is a probabilistic test, so it can mistakenly identify a composite number
-/// as prime. However, the probability of such errors decreases with more testing
-/// iterations (the function uses 64 iterations internally, which is typically
-/// more than sufficient). The Miller-Rabin test is particularly useful for large
-/// numbers.
-///
-/// <details>
-///     <summary>Details</summary>
-///
-///   Examples of prime numbers:
-///   - \\(2\\) is a prime number since it has only two divisors: \\(1\\) and \\(2\\).
-///   - \\(7\\) is a prime number since it has only two divisors: \\(1\\) and \\(7\\).
-///   - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such
-///     as \\(2\\).
-///
-/// </details>
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_prime(2)
-///       |> should.equal(True)
-///
-///       predicates.is_prime(4)
-///       |> should.equal(False)
-///
-///       // Test the 2nd Carmichael number
-///       predicates.is_prime(1105)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_prime(x: Int) -> Bool {
-  case x {
-    x if x < 2 -> {
-      False
-    }
-    x if x == 2 -> {
-      True
-    }
-    _ -> {
-      miller_rabin_test(x, 64)
-    }
-  }
-}
-
-fn miller_rabin_test(n: Int, k: Int) -> Bool {
-  case n, k {
-    _, 0 -> True
-    _, _ -> {
-      // Generate a random int in the range [2, n]
-      let random_candidate = 2 + int.random(n - 2)
-      case powmod_with_check(random_candidate, n - 1, n) == 1 {
-        True -> miller_rabin_test(n, k - 1)
-        False -> False
-      }
-    }
-  }
-}
-
-fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
-  case exponent, { exponent % 2 } == 0 {
-    0, _ -> 1
-    _, True -> {
-      let x = powmod_with_check(base, exponent / 2, modulus)
-      case { x * x } % modulus, x != 1 && x != { modulus - 1 } {
-        1, True -> 0
-        _, _ -> { x * x } % modulus
-      }
-    }
-    _, _ -> { base * powmod_with_check(base, exponent - 1, modulus) } % modulus
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given real number \\(x \in \mathbb{R}\\) is strictly
-/// between two other real numbers, \\(a,b \in \mathbb{R}\\), such that \\(a < x < b\\).
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_between(5.5, 5.0, 6.0)
-///       |> should.equal(True)
-///
-///       predicates.is_between(5.0, 5.0, 6.0)
-///       |> should.equal(False)
-///
-///       predicates.is_between(6.0, 5.0, 6.0)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool {
-  lower <. x && x <. upper
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another
-/// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\).
-///
-/// <details>
-///     <summary>Details</summary>
-///
-///   For example:
-///   - \\(n = 10\\) is divisible by \\(d = 2\\) because \\(10 \mod 2 = 0\\).
-///   - \\(n = 7\\) is not divisible by \\(d = 3\\) because \\(7 \mod 3 \neq 0\\).
-///
-/// </details>
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_divisible(10, 2)
-///       |> should.equal(True)
-///
-///       predicates.is_divisible(7, 3)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_divisible(n: Int, d: Int) -> Bool {
-  n % d == 0
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another
-/// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \times q\\), with \\(q \in \mathbb{Z}\\).
-///
-/// <details>
-///     <summary>Details</summary>
-///
-///   For example:
-///   - \\(m = 15\\) is a multiple of \\(k = 5\\) because \\(15 = 5 \times 3\\).
-///   - \\(m = 14\\) is not a multiple of \\(k = 5\\) because \\(14 \div 5\\) does not yield an
-///     integer quotient.
-///
-/// </details>
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleeunit/should
-///     import gleam_community/maths/predicates
-///
-///     pub fn example() {
-///       predicates.is_multiple(15, 5)
-///       |> should.equal(True)
-///
-///       predicates.is_multiple(14, 5)
-///       |> should.equal(False)
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn is_multiple(m: Int, k: Int) -> Bool {
-  m % k == 0
-}
diff --git a/src/gleam_community/maths/sequences.gleam b/src/gleam_community/maths/sequences.gleam
deleted file mode 100644
index c801184..0000000
--- a/src/gleam_community/maths/sequences.gleam
+++ /dev/null
@@ -1,328 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Sequences: A module containing functions for generating various types of
-//// sequences, ranges and intervals.
-////
-//// * **Ranges and intervals**
-////   * [`arange`](#arange)
-////   * [`linear_space`](#linear_space)
-////   * [`logarithmic_space`](#logarithmic_space)
-////   * [`geometric_space`](#geometric_space)
-////
-
-import gleam/iterator
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns an iterator generating evenly spaced values within a given interval.
-/// based on a start value but excludes the stop value. The spacing between values is determined
-/// by the step size provided. The function supports both positive and negative step values.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/sequences
-///
-///     pub fn example () {
-///       sequences.arange(1.0, 5.0, 1.0)
-///       |> iterator.to_list()
-///       |> should.equal([1.0, 2.0, 3.0, 4.0])
-///
-///       // No points returned since
-///       // start is smaller than stop and the step is positive
-///       sequences.arange(5.0, 1.0, 1.0)
-///       |> iterator.to_list()
-///       |> should.equal([])
-///
-///       // Points returned since
-///       // start smaller than stop but negative step
-///       sequences.arange(5.0, 1.0, -1.0)
-///       |> iterator.to_list()
-///       |> should.equal([5.0, 4.0, 3.0, 2.0])
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn arange(
-  start: Float,
-  stop: Float,
-  step: Float,
-) -> iterator.Iterator(Float) {
-  case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 {
-    True -> iterator.empty()
-    False -> {
-      let direction = case start <=. stop {
-        True -> {
-          1.0
-        }
-        False -> {
-          -1.0
-        }
-      }
-      let step_abs = piecewise.float_absolute_value(step)
-      let num =
-        piecewise.float_absolute_value(start -. stop) /. step_abs
-        |> conversion.float_to_int()
-
-      iterator.range(0, num - 1)
-      |> iterator.map(fn(i) {
-        start +. conversion.int_to_float(i) *. step_abs *. direction
-      })
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns an iterator for generating linearly spaced points over a specified
-/// interval. The endpoint of the interval can optionally be included/excluded. The number of
-/// points and whether the endpoint is included determine the spacing between values.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/elementary
-///     import gleam_community/maths/sequences
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       let assert Ok(tol) = elementary.power(10.0, -6.0)
-///       let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
-///       let assert Ok(result) =
-///         predicates.all_close(
-///           linspace |> iterator.to_list(),
-///           [10.0, 12.5, 15.0, 17.5, 20.0],
-///           0.0,
-///           tol,
-///         )
-///
-///       result
-///       |> list.all(fn(x) { x == True })
-///       |> should.be_true()
-///
-///       // A negative number of points (-5) does not work
-///       sequences.linear_space(10.0, 50.0, -5, True)
-///       |> should.be_error()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn linear_space(
-  start: Float,
-  stop: Float,
-  num: Int,
-  endpoint: Bool,
-) -> Result(iterator.Iterator(Float), Nil) {
-  let direction = case start <=. stop {
-    True -> 1.0
-    False -> -1.0
-  }
-
-  let increment = case endpoint {
-    True -> {
-      piecewise.float_absolute_value(start -. stop)
-      /. conversion.int_to_float(num - 1)
-    }
-    False -> {
-      piecewise.float_absolute_value(start -. stop)
-      /. conversion.int_to_float(num)
-    }
-  }
-  case num > 0 {
-    True -> {
-      iterator.range(0, num - 1)
-      |> iterator.map(fn(i) {
-        start +. conversion.int_to_float(i) *. increment *. direction
-      })
-      |> Ok
-    }
-    False -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns an iterator of logarithmically spaced points over a specified interval.
-/// The endpoint of the interval can optionally be included/excluded. The number of points, base,
-/// and whether the endpoint is included determine the spacing between values.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/elementary
-///     import gleam_community/maths/sequences
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       let assert Ok(tol) = elementary.power(10.0, -6.0)
-///       let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
-///       let assert Ok(result) =
-///         predicates.all_close(
-///           logspace |> iterator.to_list(),
-///           [10.0, 100.0, 1000.0],
-///           0.0,
-///           tol,
-///         )
-///       result
-///       |> list.all(fn(x) { x == True })
-///       |> should.be_true()
-///
-///       // A negative number of points (-3) does not work
-///       sequences.logarithmic_space(1.0, 3.0, -3, False, 10.0)
-///       |> should.be_error()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn logarithmic_space(
-  start: Float,
-  stop: Float,
-  num: Int,
-  endpoint: Bool,
-  base: Float,
-) -> Result(iterator.Iterator(Float), Nil) {
-  case num > 0 {
-    True -> {
-      let assert Ok(linspace) = linear_space(start, stop, num, endpoint)
-      linspace
-      |> iterator.map(fn(i) {
-        let assert Ok(result) = elementary.power(base, i)
-        result
-      })
-      |> Ok
-    }
-    False -> Error(Nil)
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The function returns an iterator of numbers spaced evenly on a log scale (a geometric
-/// progression). Each point in the list is a constant multiple of the previous. The function is
-/// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
-/// specified directly.
-///
-/// <details>
-///     <summary>Example:</summary>
-///
-///     import gleam/iterator
-///     import gleeunit/should
-///     import gleam_community/maths/elementary
-///     import gleam_community/maths/sequences
-///     import gleam_community/maths/predicates
-///
-///     pub fn example () {
-///       let assert Ok(tol) = elementary.power(10.0, -6.0)
-///       let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
-///       let assert Ok(result) =
-///         predicates.all_close(
-///           logspace |> iterator.to_list(),
-///           [10.0, 100.0, 1000.0],
-///           0.0,
-///           tol,
-///         )
-///       result
-///       |> list.all(fn(x) { x == True })
-///       |> should.be_true()
-///
-///       // Input (start and stop can't be equal to 0.0)
-///       sequences.geometric_space(0.0, 1000.0, 3, False)
-///       |> should.be_error()
-///
-///       sequences.geometric_space(-1000.0, 0.0, 3, False)
-///       |> should.be_error()
-///
-///       // A negative number of points (-3) does not work
-///       sequences.geometric_space(10.0, 1000.0, -3, False)
-///       |> should.be_error()
-///     }
-/// </details>
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn geometric_space(
-  start: Float,
-  stop: Float,
-  num: Int,
-  endpoint: Bool,
-) -> Result(iterator.Iterator(Float), Nil) {
-  case start == 0.0 || stop == 0.0 {
-    True -> Error(Nil)
-    False ->
-      case num > 0 {
-        True -> {
-          let assert Ok(log_start) = elementary.logarithm_10(start)
-          let assert Ok(log_stop) = elementary.logarithm_10(stop)
-          logarithmic_space(log_start, log_stop, num, endpoint, 10.0)
-        }
-        False -> Error(Nil)
-      }
-  }
-}
diff --git a/src/gleam_community/maths/special.gleam b/src/gleam_community/maths/special.gleam
deleted file mode 100644
index 6422709..0000000
--- a/src/gleam_community/maths/special.gleam
+++ /dev/null
@@ -1,195 +0,0 @@
-////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
-////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
-////<script>
-////    document.addEventListener("DOMContentLoaded", function() {
-////        renderMathInElement(document.body, {
-////          // customised options
-////          // • auto-render specific keys, e.g.:
-////          delimiters: [
-////              {left: '$$', right: '$$', display: false},
-////              {left: '$', right: '$', display: false},
-////              {left: '\\(', right: '\\)', display: false},
-////              {left: '\\[', right: '\\]', display: true}
-////          ],
-////          // • rendering keys, e.g.:
-////          throwOnError : true
-////        });
-////    });
-////</script>
-////<style>
-////    .katex { font-size: 1.1em; }
-////</style>
-////
-//// ---
-////
-//// Special: A module containing special mathematical functions.
-////
-//// * **Special mathematical functions**
-////   * [`beta`](#beta)
-////   * [`erf`](#erf)
-////   * [`gamma`](#gamma)
-////   * [`incomplete_gamma`](#incomplete_gamma)
-////
-
-import gleam/list
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-import gleam_community/maths/piecewise
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The beta function over the real numbers:
-///
-/// \\[
-/// \text{B}(x, y) = \frac{\Gamma(x) \cdot \Gamma(y)}{\Gamma(x + y)}
-/// \\]
-///
-/// The beta function is evaluated through the use of the gamma function.
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn beta(x: Float, y: Float) -> Float {
-  gamma(x) *. gamma(y) /. gamma(x +. y)
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The error function.
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn erf(x: Float) -> Float {
-  let assert [a1, a2, a3, a4, a5] = [
-    0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429,
-  ]
-  let p = 0.3275911
-
-  let sign = piecewise.float_sign(x)
-  let x = piecewise.float_absolute_value(x)
-
-  // Formula 7.1.26 given in Abramowitz and Stegun.
-  let t = 1.0 /. { 1.0 +. p *. x }
-  let y =
-    1.0
-    -. { { { { a5 *. t +. a4 } *. t +. a3 } *. t +. a2 } *. t +. a1 }
-    *. t
-    *. elementary.exponential(-1.0 *. x *. x)
-  sign *. y
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The gamma function over the real numbers. The function is essentially equal to
-/// the factorial for any positive integer argument: \\(\Gamma(n) = (n - 1)!\\)
-///
-/// The implemented gamma function is approximated through Lanczos approximation
-/// using the same coefficients used by the GNU Scientific Library.
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn gamma(x: Float) -> Float {
-  gamma_lanczos(x)
-}
-
-const lanczos_g: Float = 7.0
-
-const lanczos_p: List(Float) = [
-  0.99999999999980993, 676.5203681218851, -1259.1392167224028,
-  771.32342877765313, -176.61502916214059, 12.507343278686905,
-  -0.13857109526572012, 0.0000099843695780195716, 0.00000015056327351493116,
-]
-
-fn gamma_lanczos(x: Float) -> Float {
-  case x <. 0.5 {
-    True ->
-      elementary.pi()
-      /. { elementary.sin(elementary.pi() *. x) *. gamma_lanczos(1.0 -. x) }
-    False -> {
-      let z = x -. 1.0
-      let x =
-        list.index_fold(lanczos_p, 0.0, fn(acc, v, index) {
-          case index > 0 {
-            True -> acc +. v /. { z +. conversion.int_to_float(index) }
-            False -> v
-          }
-        })
-      let t = z +. lanczos_g +. 0.5
-      let assert Ok(v1) = elementary.power(2.0 *. elementary.pi(), 0.5)
-      let assert Ok(v2) = elementary.power(t, z +. 0.5)
-      v1 *. v2 *. elementary.exponential(-1.0 *. t) *. x
-    }
-  }
-}
-
-/// <div style="text-align: right;">
-///     <a href="https://github.com/gleam-community/maths/issues">
-///         <small>Spot a typo? Open an issue!</small>
-///     </a>
-/// </div>
-///
-/// The lower incomplete gamma function over the real numbers.
-///
-/// The implemented incomplete gamma function is evaluated through a power series
-/// expansion.
-///
-/// <div style="text-align: right;">
-///     <a href="#">
-///         <small>Back to top ↑</small>
-///     </a>
-/// </div>
-///
-pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, Nil) {
-  case a >. 0.0 && x >=. 0.0 {
-    True -> {
-      let assert Ok(v) = elementary.power(x, a)
-      v
-      *. elementary.exponential(-1.0 *. x)
-      *. incomplete_gamma_sum(a, x, 1.0 /. a, 0.0, 1.0)
-      |> Ok
-    }
-
-    False -> Error(Nil)
-  }
-}
-
-fn incomplete_gamma_sum(
-  a: Float,
-  x: Float,
-  t: Float,
-  s: Float,
-  n: Float,
-) -> Float {
-  case t {
-    0.0 -> s
-    _ -> {
-      let ns = s +. t
-      let nt = t *. { x /. { a +. n } }
-      incomplete_gamma_sum(a, x, nt, ns, n +. 1.0)
-    }
-  }
-}
diff --git a/test/gleam_community/arithmetics_test.gleam b/test/gleam_community/arithmetics_test.gleam
new file mode 100644
index 0000000..6cbc9ec
--- /dev/null
+++ b/test/gleam_community/arithmetics_test.gleam
@@ -0,0 +1,203 @@
+import gleam/float
+import gleam_community/maths
+import gleeunit/should
+
+pub fn int_gcd_test() {
+  maths.gcd(1, 1)
+  |> should.equal(1)
+
+  maths.gcd(100, 10)
+  |> should.equal(10)
+
+  maths.gcd(10, 100)
+  |> should.equal(10)
+
+  maths.gcd(100, -10)
+  |> should.equal(10)
+
+  maths.gcd(-36, -17)
+  |> should.equal(1)
+
+  maths.gcd(-30, -42)
+  |> should.equal(6)
+}
+
+pub fn int_euclidean_modulo_test() {
+  // Base Case: Positive x, Positive y
+  // Note that the truncated, floored, and euclidean 
+  // definitions should agree for this base case
+  maths.int_euclidean_modulo(15, 4)
+  |> should.equal(3)
+
+  // Case: Positive x, Negative y
+  maths.int_euclidean_modulo(15, -4)
+  |> should.equal(3)
+
+  // Case: Negative x, Positive y
+  maths.int_euclidean_modulo(-15, 4)
+  |> should.equal(1)
+
+  // Case: Negative x, Negative y
+  maths.int_euclidean_modulo(-15, -4)
+  |> should.equal(1)
+
+  // Case: Positive x, Zero y
+  maths.int_euclidean_modulo(5, 0)
+  |> should.equal(0)
+
+  // Case: Zero x, Negative y
+  maths.int_euclidean_modulo(0, 5)
+  |> should.equal(0)
+}
+
+pub fn int_lcm_test() {
+  maths.lcm(1, 1)
+  |> should.equal(1)
+
+  maths.lcm(100, 10)
+  |> should.equal(100)
+
+  maths.lcm(10, 100)
+  |> should.equal(100)
+
+  maths.lcm(100, -10)
+  |> should.equal(100)
+
+  maths.lcm(-36, -17)
+  |> should.equal(612)
+
+  maths.lcm(-30, -42)
+  |> should.equal(210)
+}
+
+pub fn int_proper_divisors_test() {
+  maths.proper_divisors(2)
+  |> should.equal([1])
+
+  maths.proper_divisors(6)
+  |> should.equal([1, 2, 3])
+
+  maths.proper_divisors(13)
+  |> should.equal([1])
+
+  maths.proper_divisors(18)
+  |> should.equal([1, 2, 3, 6, 9])
+}
+
+pub fn int_divisors_test() {
+  maths.divisors(2)
+  |> should.equal([1, 2])
+
+  maths.divisors(6)
+  |> should.equal([1, 2, 3, 6])
+
+  maths.divisors(13)
+  |> should.equal([1, 13])
+
+  maths.divisors(18)
+  |> should.equal([1, 2, 3, 6, 9, 18])
+}
+
+pub fn float_list_cumulative_sum_test() {
+  // An empty lists returns an empty list
+  []
+  |> maths.float_cumulative_sum()
+  |> should.equal([])
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.float_cumulative_sum()
+  |> should.equal([1.0, 3.0, 6.0])
+
+  [-2.0, 4.0, 6.0]
+  |> maths.float_cumulative_sum()
+  |> should.equal([-2.0, 2.0, 8.0])
+}
+
+pub fn int_list_cumulative_sum_test() {
+  // An empty lists returns an empty list
+  []
+  |> maths.int_cumulative_sum()
+  |> should.equal([])
+
+  // Valid input returns a result
+  [1, 2, 3]
+  |> maths.int_cumulative_sum()
+  |> should.equal([1, 3, 6])
+
+  [-2, 4, 6]
+  |> maths.int_cumulative_sum()
+  |> should.equal([-2, 2, 8])
+}
+
+pub fn float_list_cumulative_product_test() {
+  // An empty lists returns an empty list
+  []
+  |> maths.float_cumulative_product()
+  |> should.equal([])
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.float_cumulative_product()
+  |> should.equal([1.0, 2.0, 6.0])
+
+  [-2.0, 4.0, 6.0]
+  |> maths.float_cumulative_product()
+  |> should.equal([-2.0, -8.0, -48.0])
+}
+
+pub fn int_list_cumulative_product_test() {
+  // An empty lists returns an empty list
+  []
+  |> maths.int_cumulative_product()
+  |> should.equal([])
+
+  // Valid input returns a result
+  [1, 2, 3]
+  |> maths.int_cumulative_product()
+  |> should.equal([1, 2, 6])
+
+  [-2, 4, 6]
+  |> maths.int_cumulative_product()
+  |> should.equal([-2, -8, -48])
+}
+
+pub fn float_weighted_product_test() {
+  []
+  |> maths.float_weighted_product()
+  |> should.equal(Ok(1.0))
+
+  [#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
+  |> maths.float_weighted_product()
+  |> should.equal(Ok(1.0))
+
+  [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
+  |> maths.float_weighted_product()
+  |> should.equal(Ok(6.0))
+
+  let assert Ok(tolerance) = float.power(10.0, -6.0)
+  let assert Ok(result) =
+    [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
+    |> maths.float_weighted_product()
+  result
+  |> maths.is_close(30.0, 0.0, tolerance)
+  |> should.be_true()
+}
+
+pub fn float_weighted_sum_test() {
+  []
+  |> maths.float_weighted_sum()
+  |> should.equal(Ok(0.0))
+
+  [#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
+  |> maths.float_weighted_sum()
+  |> should.equal(Ok(0.0))
+
+  [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
+  |> maths.float_weighted_sum()
+  |> should.equal(Ok(6.0))
+
+  [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
+  |> maths.float_weighted_sum()
+  |> should.equal(Ok(14.5))
+}
diff --git a/test/gleam_community/maths/combinatorics_test.gleam b/test/gleam_community/combinatorics_test.gleam
similarity index 51%
rename from test/gleam_community/maths/combinatorics_test.gleam
rename to test/gleam_community/combinatorics_test.gleam
index 729f450..dc9122a 100644
--- a/test/gleam_community/maths/combinatorics_test.gleam
+++ b/test/gleam_community/combinatorics_test.gleam
@@ -1,132 +1,139 @@
-import gleam/iterator
 import gleam/list
-import gleam/option
 import gleam/set
-import gleam_community/maths/combinatorics
+import gleam/yielder
+import gleam_community/maths
 import gleeunit/should
 
 pub fn int_factorial_test() {
   // Invalid input gives an error (factorial of negative number)
-  combinatorics.factorial(-1)
+  maths.factorial(-1)
   |> should.be_error()
 
   // Valid inputs for factorial of small numbers
-  combinatorics.factorial(0)
+  maths.factorial(0)
   |> should.equal(Ok(1))
 
-  combinatorics.factorial(1)
+  maths.factorial(1)
   |> should.equal(Ok(1))
 
-  combinatorics.factorial(2)
+  maths.factorial(2)
   |> should.equal(Ok(2))
 
-  combinatorics.factorial(3)
+  maths.factorial(3)
   |> should.equal(Ok(6))
 
-  combinatorics.factorial(4)
+  maths.factorial(4)
   |> should.equal(Ok(24))
 }
 
-pub fn int_combination_test() {
+pub fn int_combination_with_repetitions_test() {
   // Invalid input: k < 0 should return an error
-  combinatorics.combination(1, -1, option.None)
+  maths.combination_with_repetitions(1, -1)
   |> should.be_error()
 
   // Invalid input: n < 0 should return an error
-  combinatorics.combination(-1, 1, option.None)
+  maths.combination_with_repetitions(-1, 1)
   |> should.be_error()
 
-  // Valid input: k > n without repetition gives 0 combinations
-  combinatorics.combination(2, 3, option.Some(combinatorics.WithoutRepetitions))
-  |> should.equal(Ok(0))
-
   // Valid input: k > n with repetition allowed
-  combinatorics.combination(2, 3, option.Some(combinatorics.WithRepetitions))
+  maths.combination_with_repetitions(2, 3)
   |> should.equal(Ok(4))
 
-  // Valid input: zero combinations (k=0) should always yield 1 combination
-  combinatorics.combination(4, 0, option.Some(combinatorics.WithoutRepetitions))
-  |> should.equal(Ok(1))
-
-  combinatorics.combination(4, 0, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(1))
-
-  // Valid input: k = n without repetition gives 1 combination
-  combinatorics.combination(4, 4, option.Some(combinatorics.WithoutRepetitions))
+  maths.combination_with_repetitions(4, 0)
   |> should.equal(Ok(1))
 
   // Valid input: k = n with repetition allows more combinations
-  combinatorics.combination(4, 4, option.Some(combinatorics.WithRepetitions))
+  maths.combination_with_repetitions(4, 4)
   |> should.equal(Ok(35))
 
-  // Valid input: k < n without and with repetition
-  combinatorics.combination(4, 2, option.Some(combinatorics.WithoutRepetitions))
-  |> should.equal(Ok(6))
-
-  combinatorics.combination(4, 2, option.Some(combinatorics.WithRepetitions))
+  maths.combination_with_repetitions(4, 2)
   |> should.equal(Ok(10))
 
-  // Valid input with larger values of n and k
-  combinatorics.combination(7, 5, option.Some(combinatorics.WithoutRepetitions))
-  |> should.equal(Ok(21))
-
-  combinatorics.combination(7, 5, option.Some(combinatorics.WithRepetitions))
+  maths.combination_with_repetitions(7, 5)
   |> should.equal(Ok(462))
   // NOTE: Tests with the 'combination' function that produce values that exceed
   // precision of the JavaScript 'Number' primitive will result in errors
 }
 
-pub fn math_permutation_test() {
+pub fn int_combination_without_repetitions_test() {
+  // Valid input: k > n without repetition gives 0 combinations
+  maths.combination(2, 3)
+  |> should.equal(Ok(0))
+
+  // Valid input: k = n without repetition gives 1 combination
+  maths.combination(4, 4)
+  |> should.equal(Ok(1))
+
+  // Valid input: zero combinations (k=0) should always yield 1 combination
+  maths.combination(4, 0)
+  |> should.equal(Ok(1))
+
+  // Valid input: k < n without and with repetition
+  maths.combination(4, 2)
+  |> should.equal(Ok(6))
+
+  // Valid input with larger values of n and k
+  maths.combination(7, 5)
+  |> should.equal(Ok(21))
+}
+
+pub fn math_permutation_with_repetitions_test() {
   // Invalid input: k < 0 should return an error
-  combinatorics.permutation(1, -1, option.None)
+  maths.permutation_with_repetitions(1, -1)
+  |> should.be_error()
+
+  // Valid input: k > n with repetition allowed gives non-zero permutations
+  maths.permutation_with_repetitions(2, 3)
+  |> should.equal(Ok(8))
+
+  maths.permutation_with_repetitions(4, 0)
+  |> should.equal(Ok(1))
+
+  // Valid input: k = n permutations with repetition
+  maths.permutation_with_repetitions(4, 4)
+  |> should.equal(Ok(256))
+
+  maths.permutation_with_repetitions(4, 2)
+  |> should.equal(Ok(16))
+
+  maths.permutation_with_repetitions(6, 2)
+  |> should.equal(Ok(36))
+
+  maths.permutation_with_repetitions(6, 3)
+  |> should.equal(Ok(216))
+}
+
+pub fn math_permutation_without_repetitions_test() {
+  // Invalid input: k < 0 should return an error
+  maths.permutation(1, -1)
   |> should.be_error()
 
   // Invalid input: n < 0 should return an error
-  combinatorics.permutation(-1, 1, option.None)
+  maths.permutation(-1, 1)
   |> should.be_error()
 
   // Valid input: k > n without repetition gives 0 permutations
-  combinatorics.permutation(2, 3, option.Some(combinatorics.WithoutRepetitions))
+  maths.permutation(2, 3)
   |> should.equal(Ok(0))
 
-  // Valid input: k > n with repetition allowed gives non-zero permutations
-  combinatorics.permutation(2, 3, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(8))
-
   // Valid input: k = 0 should always yield 1 permutation
-  combinatorics.permutation(4, 0, option.Some(combinatorics.WithoutRepetitions))
-  |> should.equal(Ok(1))
-
-  combinatorics.permutation(4, 0, option.Some(combinatorics.WithRepetitions))
+  maths.permutation(4, 0)
   |> should.equal(Ok(1))
 
   // Valid input: k = n permutations without repetition
-  combinatorics.permutation(4, 4, option.Some(combinatorics.WithoutRepetitions))
+  maths.permutation(4, 4)
   |> should.equal(Ok(24))
 
-  // Valid input: k = n permutations with repetition
-  combinatorics.permutation(4, 4, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(256))
-
   // Valid input: k < n permutations without and with repetition
-  combinatorics.permutation(4, 2, option.Some(combinatorics.WithoutRepetitions))
+  maths.permutation(4, 2)
   |> should.equal(Ok(12))
 
-  combinatorics.permutation(4, 2, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(16))
-
   // Valid input with larger values of n and k
-  combinatorics.permutation(6, 2, option.Some(combinatorics.WithoutRepetitions))
+  maths.permutation(6, 2)
   |> should.equal(Ok(30))
 
-  combinatorics.permutation(6, 2, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(36))
-
-  combinatorics.permutation(6, 3, option.Some(combinatorics.WithoutRepetitions))
+  maths.permutation(6, 3)
   |> should.equal(Ok(120))
-
-  combinatorics.permutation(6, 3, option.Some(combinatorics.WithRepetitions))
-  |> should.equal(Ok(216))
 }
 
 pub fn list_cartesian_product_test() {
@@ -135,7 +142,7 @@ pub fn list_cartesian_product_test() {
   let yset = set.from_list([])
   let expected_result = set.from_list([])
   xset
-  |> combinatorics.cartesian_product(yset)
+  |> maths.cartesian_product(yset)
   |> should.equal(expected_result)
 
   // Cartesian product of two sets with the same elements
@@ -154,7 +161,7 @@ pub fn list_cartesian_product_test() {
       #(3, 3),
     ])
   xset
-  |> combinatorics.cartesian_product(yset)
+  |> maths.cartesian_product(yset)
   |> should.equal(expected_result)
 
   // Cartesian product with floating-point numbers
@@ -163,7 +170,7 @@ pub fn list_cartesian_product_test() {
   let expected_result =
     set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)])
   xset
-  |> combinatorics.cartesian_product(yset)
+  |> maths.cartesian_product(yset)
   |> should.equal(expected_result)
 
   // Cartesian product of sets with different sizes
@@ -179,7 +186,7 @@ pub fn list_cartesian_product_test() {
       #(100.0, 2.0),
     ])
   xset
-  |> combinatorics.cartesian_product(yset)
+  |> maths.cartesian_product(yset)
   |> should.equal(expected_result)
 
   // Cartesian product with different types (strings)
@@ -195,195 +202,216 @@ pub fn list_cartesian_product_test() {
       #("z", "x"),
     ])
   xset
-  |> combinatorics.cartesian_product(yset)
+  |> maths.cartesian_product(yset)
   |> should.equal(expected_result)
 }
 
-pub fn list_permutation_test() {
+pub fn cartesian_product_mixed_types_test() {
+  // Cartesian product of two empty sets
+  set.from_list([])
+  |> maths.cartesian_product(set.from_list([]))
+  |> should.equal(set.from_list([]))
+
+  // Cartesian product of two sets with numeric values
+  set.from_list([1.0, 10.0])
+  |> maths.cartesian_product(set.from_list([1.0, 2.0]))
+  |> should.equal(
+    set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
+  )
+
+  // Cartesian product of two sets with different types
+  set.from_list(["1", "10"])
+  |> maths.cartesian_product(set.from_list([1.0, 2.0]))
+  |> should.equal(
+    set.from_list([#("1", 1.0), #("1", 2.0), #("10", 1.0), #("10", 2.0)]),
+  )
+}
+
+pub fn list_permutation_with_repetitions_test() {
   // Invalid input: k < 0 should return an error for an empty list
   []
-  |> combinatorics.list_permutation(-1, option.None)
-  |> should.be_error()
-
-  // Invalid input: k > n should return an error without repetition
-  [1, 2]
-  |> combinatorics.list_permutation(
-    3,
-    option.Some(combinatorics.WithoutRepetitions),
-  )
+  |> maths.list_permutation_with_repetitions(-1)
   |> should.be_error()
 
   // Valid input: An empty list returns a single empty permutation
   let assert Ok(permutations) =
     []
-    |> combinatorics.list_permutation(0, option.None)
+    |> maths.list_permutation_with_repetitions(0)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[]])
 
-  // Singleton list returns a single permutation regardless of repetition settings
   let assert Ok(permutations) =
     ["a"]
-    |> combinatorics.list_permutation(
-      1,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(1)
   permutations
-  |> iterator.to_list()
-  |> should.equal([["a"]])
-
-  let assert Ok(permutations) =
-    ["a"]
-    |> combinatorics.list_permutation(
-      1,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([["a"]])
 
   // 4-permutations of a single element repeats it 4 times
   let assert Ok(permutations) =
     ["a"]
-    |> combinatorics.list_permutation(
-      4,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(4)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([["a", "a", "a", "a"]])
 
-  // 2-permutations of [1, 2] without repetition
-  let assert Ok(permutations) =
-    [1, 2]
-    |> combinatorics.list_permutation(
-      2,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
-  permutations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(set.from_list([[1, 2], [2, 1]]))
-
   // 2-permutations of [1, 2] with repetition
   let assert Ok(permutations) =
     [1, 2]
-    |> combinatorics.list_permutation(
-      2,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(2)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> set.from_list()
   |> should.equal(set.from_list([[1, 1], [1, 2], [2, 2], [2, 1]]))
 
-  // 3-permutations of [1, 2, 3] without repetition
-  let assert Ok(permutations) =
-    [1, 2, 3]
-    |> combinatorics.list_permutation(
-      3,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
-  permutations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(
-    set.from_list([
-      [1, 2, 3],
-      [2, 1, 3],
-      [3, 1, 2],
-      [1, 3, 2],
-      [2, 3, 1],
-      [3, 2, 1],
-    ]),
-  )
-
   // 3-permutations of [1, 2, 3] with repetition
   let assert Ok(permutations) =
     [1, 2, 3]
-    |> combinatorics.list_permutation(
-      3,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(3)
   permutations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(
-    set.from_list([
-      [1, 1, 1],
-      [1, 1, 2],
-      [1, 1, 3],
-      [1, 2, 1],
-      [1, 2, 2],
-      [1, 2, 3],
-      [1, 3, 1],
-      [1, 3, 2],
-      [1, 3, 3],
-      [2, 1, 1],
-      [2, 1, 2],
-      [2, 1, 3],
-      [2, 2, 1],
-      [2, 2, 2],
-      [2, 2, 3],
-      [2, 3, 1],
-      [2, 3, 2],
-      [2, 3, 3],
-      [3, 1, 1],
-      [3, 1, 2],
-      [3, 1, 3],
-      [3, 2, 1],
-      [3, 2, 2],
-      [3, 2, 3],
-      [3, 3, 1],
-      [3, 3, 2],
-      [3, 3, 3],
-    ]),
-  )
-
-  // Repeated elements are treated as distinct in permutations without repetition
-  let assert Ok(permutations) =
-    [1.0, 1.0]
-    |> combinatorics.list_permutation(
-      2,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
-  permutations
-  |> iterator.to_list()
-  |> should.equal([[1.0, 1.0], [1.0, 1.0]])
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 1, 1],
+    [1, 1, 2],
+    [1, 1, 3],
+    [1, 2, 1],
+    [1, 2, 2],
+    [1, 2, 3],
+    [1, 3, 1],
+    [1, 3, 2],
+    [1, 3, 3],
+    [2, 1, 1],
+    [2, 1, 2],
+    [2, 1, 3],
+    [2, 2, 1],
+    [2, 2, 2],
+    [2, 2, 3],
+    [2, 3, 1],
+    [2, 3, 2],
+    [2, 3, 3],
+    [3, 1, 1],
+    [3, 1, 2],
+    [3, 1, 3],
+    [3, 2, 1],
+    [3, 2, 2],
+    [3, 2, 3],
+    [3, 3, 1],
+    [3, 3, 2],
+    [3, 3, 3],
+  ])
 
   // Repeated elements allow more possibilities when repetition is allowed
   let assert Ok(permutations) =
     [1.0, 1.0]
-    |> combinatorics.list_permutation(
-      2,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(2)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]])
 
+  let assert Ok(permutations) =
+    ["a", "b", "c", "d", "e"]
+    |> maths.list_permutation_with_repetitions(5)
+  permutations
+  |> yielder.to_list()
+  |> list.length()
+  |> should.equal(3125)
+}
+
+pub fn list_permutation_without_repetitions_test() {
+  // Invalid input: k > n should return an error without repetition
+  [1, 2]
+  |> maths.list_permutation(3)
+  |> should.be_error()
+
+  // Singleton list returns a single permutation regardless of repetition settings
+  let assert Ok(permutations) =
+    ["a"]
+    |> maths.list_permutation(1)
+  permutations
+  |> yielder.to_list()
+  |> should.equal([["a"]])
+
+  // 2-permutations of [1, 2] without repetition
+  let assert Ok(permutations) =
+    [1, 2]
+    |> maths.list_permutation(2)
+  permutations
+  |> yielder.to_list()
+  |> should.equal([[1, 2], [2, 1]])
+
+  // 2-permutations without repetition
+  let assert Ok(result) =
+    [1, 2, 3]
+    |> maths.list_permutation(2)
+  result
+  |> yielder.to_list()
+  |> should.equal([[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]])
+
+  // 3-permutations of [1, 2, 3] without repetition
+  let assert Ok(permutations) =
+    [1, 2, 3]
+    |> maths.list_permutation(3)
+  permutations
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 2, 3],
+    [1, 3, 2],
+    [2, 1, 3],
+    [2, 3, 1],
+    [3, 1, 2],
+    [3, 2, 1],
+  ])
+
+  // 3-permutations of [1, 2, 3, 4] without repetition
+  let assert Ok(permutations) =
+    [1, 2, 3, 4]
+    |> maths.list_permutation(3)
+  permutations
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 2, 3],
+    [1, 2, 4],
+    [1, 3, 2],
+    [1, 3, 4],
+    [1, 4, 2],
+    [1, 4, 3],
+    [2, 1, 3],
+    [2, 1, 4],
+    [2, 3, 1],
+    [2, 3, 4],
+    [2, 4, 1],
+    [2, 4, 3],
+    [3, 1, 2],
+    [3, 1, 4],
+    [3, 2, 1],
+    [3, 2, 4],
+    [3, 4, 1],
+    [3, 4, 2],
+    [4, 1, 2],
+    [4, 1, 3],
+    [4, 2, 1],
+    [4, 2, 3],
+    [4, 3, 1],
+    [4, 3, 2],
+  ])
+
+  // Repeated elements are treated as distinct in permutations without repetition
+  let assert Ok(permutations) =
+    [1.0, 1.0]
+    |> maths.list_permutation(2)
+  permutations
+  |> yielder.to_list()
+  |> should.equal([[1.0, 1.0], [1.0, 1.0]])
+
   // Large inputs: Ensure the correct number of permutations is generated
   let assert Ok(permutations) =
     ["a", "b", "c", "d", "e"]
-    |> combinatorics.list_permutation(
-      5,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_permutation(5)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(120)
-
-  let assert Ok(permutations) =
-    ["a", "b", "c", "d", "e"]
-    |> combinatorics.list_permutation(
-      5,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  permutations
-  |> iterator.to_list()
-  |> list.length()
-  |> should.equal(3125)
 }
 
 pub fn permutation_alignment_test() {
@@ -392,20 +420,12 @@ pub fn permutation_alignment_test() {
   let arr = ["a", "b", "c", "d", "e", "f"]
   let length = list.length(arr)
 
-  let assert Ok(number_of_permutations) =
-    combinatorics.permutation(
-      length,
-      length,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+  let assert Ok(number_of_permutations) = maths.permutation(length, length)
   let assert Ok(permutations) =
     arr
-    |> combinatorics.list_permutation(
-      length,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_permutation(length)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(number_of_permutations)
 
@@ -414,230 +434,196 @@ pub fn permutation_alignment_test() {
   let length = list.length(arr)
 
   let assert Ok(number_of_permutations) =
-    combinatorics.permutation(
-      length,
-      length,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    maths.permutation_with_repetitions(length, length)
   let assert Ok(permutations) =
     arr
-    |> combinatorics.list_permutation(
-      length,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_permutation_with_repetitions(length)
   permutations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(number_of_permutations)
 }
 
-pub fn list_combination_test() {
+pub fn list_combination_with_repetitions_test() {
   // Invalid input: k < 0 should return an error for an empty list
   []
-  |> combinatorics.list_combination(-1, option.None)
-  |> should.be_error()
-
-  // Invalid input: k > n should return an error without repetition
-  [1, 2]
-  |> combinatorics.list_combination(
-    3,
-    option.Some(combinatorics.WithoutRepetitions),
-  )
+  |> maths.list_combination_with_repetitions(-1)
   |> should.be_error()
 
   // Valid input: k > n with repetition allowed
   let assert Ok(combinations) =
     [1, 2]
-    |> combinatorics.list_combination(
-      3,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_combination_with_repetitions(3)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]])
 
+  let assert Ok(combinations) =
+    []
+    |> maths.list_combination_with_repetitions(0)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([[]])
+
+  let assert Ok(combinations) =
+    [1, 2]
+    |> maths.list_combination_with_repetitions(1)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([[1], [2]])
+
+  let assert Ok(combinations) =
+    [1, 2]
+    |> maths.list_combination_with_repetitions(2)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([[1, 1], [1, 2], [2, 2]])
+
+  let assert Ok(combinations) =
+    [1, 2, 3, 4]
+    |> maths.list_combination_with_repetitions(2)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 1],
+    [1, 2],
+    [1, 3],
+    [1, 4],
+    [2, 2],
+    [2, 3],
+    [2, 4],
+    [3, 3],
+    [3, 4],
+    [4, 4],
+  ])
+
+  // 3-combination of [1, 2, 3] with repetition
+  let assert Ok(combinations) =
+    [1, 2, 3]
+    |> maths.list_combination_with_repetitions(3)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 1, 1],
+    [1, 1, 2],
+    [1, 1, 3],
+    [1, 2, 2],
+    [1, 2, 3],
+    [1, 3, 3],
+    [2, 2, 2],
+    [2, 2, 3],
+    [2, 3, 3],
+    [3, 3, 3],
+  ])
+
+  // 3-permutations of [1, 2, 3, 4] with repetition
+  let assert Ok(permutations) =
+    maths.list_combination_with_repetitions([1, 2, 3, 4], 3)
+
+  permutations
+  |> yielder.to_list()
+  |> should.equal([
+    [1, 1, 1],
+    [1, 1, 2],
+    [1, 1, 3],
+    [1, 1, 4],
+    [1, 2, 2],
+    [1, 2, 3],
+    [1, 2, 4],
+    [1, 3, 3],
+    [1, 3, 4],
+    [1, 4, 4],
+    [2, 2, 2],
+    [2, 2, 3],
+    [2, 2, 4],
+    [2, 3, 3],
+    [2, 3, 4],
+    [2, 4, 4],
+    [3, 3, 3],
+    [3, 3, 4],
+    [3, 4, 4],
+    [4, 4, 4],
+  ])
+
+  // Repetition creates more possibilities even with identical elements
+  let assert Ok(combinations) =
+    [1.0, 1.0]
+    |> maths.list_combination_with_repetitions(2)
+  combinations
+  |> yielder.to_list()
+  |> should.equal([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]])
+
+  let assert Ok(combinations) =
+    ["a", "b", "c", "d", "e"]
+    |> maths.list_combination_with_repetitions(5)
+  combinations
+  |> yielder.to_list()
+  |> list.length()
+  |> should.equal(126)
+}
+
+pub fn list_combination_without_repetitions_test() {
+  // Invalid input: k > n should return an error without repetition
+  [1, 2]
+  |> maths.list_combination(3)
+  |> should.be_error()
+
   // Valid input: Empty list should return a single empty combination
   let assert Ok(combinations) =
     []
-    |> combinatorics.list_combination(
-      0,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(0)
   combinations
-  |> iterator.to_list()
-  |> should.equal([[]])
-
-  let assert Ok(combinations) =
-    []
-    |> combinatorics.list_combination(
-      0,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[]])
 
   // 1-combination of [1, 2] without and with repetition
   let assert Ok(combinations) =
     [1, 2]
-    |> combinatorics.list_combination(
-      1,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(1)
   combinations
-  |> iterator.to_list()
-  |> should.equal([[1], [2]])
-
-  let assert Ok(combinations) =
-    [1, 2]
-    |> combinatorics.list_combination(
-      1,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1], [2]])
 
   // 2-combination of [1, 2] without and with repetition
   let assert Ok(combinations) =
     [1, 2]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(2)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1, 2]])
 
-  let assert Ok(combinations) =
-    [1, 2]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
-  |> should.equal([[1, 1], [1, 2], [2, 2]])
-
   // 2-combination of [1, 2, 3, 4] without and with repetition
   let assert Ok(combinations) =
     [1, 2, 3, 4]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(2)
   combinations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(
-    set.from_list([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]),
-  )
-
-  let assert Ok(combinations) =
-    [1, 2, 3, 4]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(
-    set.from_list([
-      [1, 1],
-      [1, 2],
-      [1, 3],
-      [1, 4],
-      [2, 2],
-      [2, 3],
-      [2, 4],
-      [3, 3],
-      [3, 4],
-      [4, 4],
-    ]),
-  )
+  |> yielder.to_list()
+  |> should.equal([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]])
 
   // 3-combination of [1, 2, 3, 4] without repetition
   let assert Ok(combinations) =
     [1, 2, 3, 4]
-    |> combinatorics.list_combination(
-      3,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(3)
   combinations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(set.from_list([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]))
-
-  // 3-combination of [1, 2, 3] with repetition
-  let assert Ok(combinations) =
-    [1, 2, 3]
-    |> combinatorics.list_combination(
-      3,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
-  |> set.from_list()
-  |> should.equal(
-    set.from_list([
-      [1, 1, 1],
-      [1, 1, 2],
-      [1, 1, 3],
-      [1, 2, 2],
-      [1, 2, 3],
-      [1, 3, 3],
-      [2, 2, 2],
-      [2, 2, 3],
-      [2, 3, 3],
-      [3, 3, 3],
-    ]),
-  )
+  |> yielder.to_list()
+  |> should.equal([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]])
 
   // Combinations treat repeated elements as distinct in certain scenarios
   let assert Ok(combinations) =
     [1.0, 1.0]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(2)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> should.equal([[1.0, 1.0]])
 
-  // Repetition creates more possibilities even with identical elements
-  let assert Ok(combinations) =
-    [1.0, 1.0]
-    |> combinatorics.list_combination(
-      2,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
-  |> should.equal([[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]])
-
   // Large input: Ensure correct number of combinations is generated
   let assert Ok(combinations) =
     ["a", "b", "c", "d", "e"]
-    |> combinatorics.list_combination(
-      5,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(5)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(1)
-
-  let assert Ok(combinations) =
-    ["a", "b", "c", "d", "e"]
-    |> combinatorics.list_combination(
-      5,
-      option.Some(combinatorics.WithRepetitions),
-    )
-  combinations
-  |> iterator.to_list()
-  |> list.length()
-  |> should.equal(126)
 }
 
 pub fn combination_alignment_test() {
@@ -646,20 +632,12 @@ pub fn combination_alignment_test() {
   let arr = ["a", "b", "c", "d", "e", "f"]
   let length = list.length(arr)
 
-  let assert Ok(number_of_combinations) =
-    combinatorics.combination(
-      length,
-      length,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+  let assert Ok(number_of_combinations) = maths.combination(length, length)
   let assert Ok(combinations) =
     arr
-    |> combinatorics.list_combination(
-      length,
-      option.Some(combinatorics.WithoutRepetitions),
-    )
+    |> maths.list_combination(length)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(number_of_combinations)
 
@@ -668,40 +646,12 @@ pub fn combination_alignment_test() {
   let length = list.length(arr)
 
   let assert Ok(number_of_combinations) =
-    combinatorics.combination(
-      length,
-      length,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    maths.combination_with_repetitions(length, length)
   let assert Ok(combinations) =
     arr
-    |> combinatorics.list_combination(
-      length,
-      option.Some(combinatorics.WithRepetitions),
-    )
+    |> maths.list_combination_with_repetitions(length)
   combinations
-  |> iterator.to_list()
+  |> yielder.to_list()
   |> list.length()
   |> should.equal(number_of_combinations)
 }
-
-pub fn example_test() {
-  // Cartesian product of two empty sets
-  set.from_list([])
-  |> combinatorics.cartesian_product(set.from_list([]))
-  |> should.equal(set.from_list([]))
-
-  // Cartesian product of two sets with numeric values
-  set.from_list([1.0, 10.0])
-  |> combinatorics.cartesian_product(set.from_list([1.0, 2.0]))
-  |> should.equal(
-    set.from_list([#(1.0, 1.0), #(1.0, 2.0), #(10.0, 1.0), #(10.0, 2.0)]),
-  )
-
-  // Cartesian product of two sets with different types
-  set.from_list(["1", "10"])
-  |> combinatorics.cartesian_product(set.from_list([1.0, 2.0]))
-  |> should.equal(
-    set.from_list([#("1", 1.0), #("1", 2.0), #("10", 1.0), #("10", 2.0)]),
-  )
-}
diff --git a/test/gleam_community/conversion_test.gleam b/test/gleam_community/conversion_test.gleam
new file mode 100644
index 0000000..081089c
--- /dev/null
+++ b/test/gleam_community/conversion_test.gleam
@@ -0,0 +1,113 @@
+import gleam/float
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_to_degree_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  maths.radians_to_degrees(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.radians_to_degrees(2.0 *. maths.pi())
+  |> maths.is_close(360.0, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_to_radian_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  maths.degrees_to_radians(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.degrees_to_radians(360.0)
+  |> maths.is_close(2.0 *. maths.pi(), 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_cartesian_to_polar_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Test: Cartesian (1, 0) -> Polar (1, 0)
+  let #(r, theta) = maths.cartesian_to_polar(1.0, 0.0)
+  r
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  theta
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  // Test: Cartesian (0, 1) -> Polar (1, pi/2)
+  let #(r, theta) = maths.cartesian_to_polar(0.0, 1.0)
+  r
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  theta
+  |> maths.is_close(maths.pi() /. 2.0, 0.0, tol)
+  |> should.be_true()
+
+  // Test: Cartesian (-1, 0) -> Polar (1, pi)
+  let #(r, theta) = maths.cartesian_to_polar(-1.0, 0.0)
+  r
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  theta
+  |> maths.is_close(maths.pi(), 0.0, tol)
+  |> should.be_true()
+
+  // Test: Cartesian (0, -1) -> Polar (1, -pi/2)
+  let #(r, theta) = maths.cartesian_to_polar(0.0, -1.0)
+  r
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  theta
+  |> maths.is_close(-1.0 *. maths.pi() /. 2.0, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_polar_to_cartesian_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Test: Polar (1, 0) -> Cartesian (1, 0)
+  let #(x, y) = maths.polar_to_cartesian(1.0, 0.0)
+  x
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  y
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  // Test: Polar (1, pi/2) -> Cartesian (0, 1)
+  let #(x, y) = maths.polar_to_cartesian(1.0, maths.pi() /. 2.0)
+  x
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  y
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  // Test: Polar (1, pi) -> Cartesian (-1, 0)
+  let #(x, y) = maths.polar_to_cartesian(1.0, maths.pi())
+  x
+  |> maths.is_close(-1.0, 0.0, tol)
+  |> should.be_true()
+
+  y
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  // Test: Polar (1, -pi/2) -> Cartesian (0, -1)
+  let #(x, y) = maths.polar_to_cartesian(1.0, -1.0 *. maths.pi() /. 2.0)
+  x
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  y
+  |> maths.is_close(-1.0, 0.0, tol)
+  |> should.be_true()
+}
diff --git a/test/gleam_community/elementary_test.gleam b/test/gleam_community/elementary_test.gleam
new file mode 100644
index 0000000..ed833f2
--- /dev/null
+++ b/test/gleam_community/elementary_test.gleam
@@ -0,0 +1,419 @@
+import gleam/float
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_acos_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) = maths.acos(1.0)
+  result
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) = maths.acos(0.5)
+  result
+  |> maths.is_close(1.047197, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.acos(1.1)
+  |> should.be_error()
+
+  maths.acos(-1.1)
+  |> should.be_error()
+}
+
+pub fn float_acosh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) = maths.acosh(1.0)
+  result
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.acosh(0.0)
+  |> should.be_error()
+}
+
+pub fn float_asin_test() {
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.asin(0.0)
+  |> should.equal(Ok(0.0))
+
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  let assert Ok(result) = maths.asin(0.5)
+  result
+  |> maths.is_close(0.523598, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.asin(1.1)
+  |> should.be_error()
+
+  maths.asin(-1.1)
+  |> should.be_error()
+}
+
+pub fn float_asinh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.asinh(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.asinh(0.5)
+  |> maths.is_close(0.481211, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_atan_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.atan(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.atan(0.5)
+  |> maths.is_close(0.463647, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn math_atan2_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.atan2(0.0, 0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.atan2(0.0, 1.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  // Check atan2(y=1.0, x=0.5)
+  // Should be equal to atan(y / x) for any x > 0 and any y
+  let result = maths.atan(1.0 /. 0.5)
+  maths.atan2(1.0, 0.5)
+  |> maths.is_close(result, 0.0, tol)
+  |> should.be_true()
+
+  // Check atan2(y=2.0, x=-1.5)
+  // Should be equal to pi + atan(y / x) for any x < 0 and y >= 0
+  let result = maths.pi() +. maths.atan(2.0 /. -1.5)
+  maths.atan2(2.0, -1.5)
+  |> maths.is_close(result, 0.0, tol)
+  |> should.be_true()
+
+  // Check atan2(y=-2.0, x=-1.5)
+  // Should be equal to atan(y / x) - pi for any x < 0 and y < 0
+  let result = maths.atan(-2.0 /. -1.5) -. maths.pi()
+  maths.atan2(-2.0, -1.5)
+  |> maths.is_close(result, 0.0, tol)
+  |> should.be_true()
+
+  // Check atan2(y=1.5, x=0.0)
+  // Should be equal to pi/2 for x = 0 and any y > 0
+  let result = maths.pi() /. 2.0
+  maths.atan2(1.5, 0.0)
+  |> maths.is_close(result, 0.0, tol)
+  |> should.be_true()
+
+  // Check atan2(y=-1.5, x=0.0)
+  // Should be equal to -pi/2 for x = 0 and any y < 0
+  let result = -1.0 *. maths.pi() /. 2.0
+  maths.atan2(-1.5, 0.0)
+  |> maths.is_close(result, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_atanh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) = maths.atanh(0.0)
+  result
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) = maths.atanh(0.5)
+  result
+  |> maths.is_close(0.549306, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.atanh(1.0)
+  |> should.be_error()
+
+  maths.atanh(2.0)
+  |> should.be_error()
+
+  maths.atanh(1.0)
+  |> should.be_error()
+
+  maths.atanh(-2.0)
+  |> should.be_error()
+}
+
+pub fn float_cos_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.cos(0.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.cos(maths.pi())
+  |> maths.is_close(-1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.cos(0.5)
+  |> maths.is_close(0.877582, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_cosh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.cosh(0.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.cosh(0.5)
+  |> maths.is_close(1.127625, 0.0, tol)
+  |> should.be_true()
+  // An (overflow) error might occur when given an input
+  // value that will result in a too large output value
+  // e.g. maths.cosh(1000.0) but this is a property of the
+  // runtime.
+}
+
+pub fn float_sin_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.sin(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.sin(0.5 *. maths.pi())
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.sin(0.5)
+  |> maths.is_close(0.479425, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_sinh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.sinh(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.sinh(0.5)
+  |> maths.is_close(0.521095, 0.0, tol)
+  |> should.be_true()
+  // An (overflow) error might occur when given an input
+  // value that will result in a too large output value
+  // e.g. maths.sinh(1000.0) but this is a property of the
+  // runtime.
+}
+
+pub fn math_tan_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.tan(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.tan(0.5)
+  |> maths.is_close(0.546302, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn math_tanh_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.tanh(0.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.tanh(25.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.tanh(-25.0)
+  |> maths.is_close(-1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.tanh(0.5)
+  |> maths.is_close(0.462117, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_exponential_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.exponential(0.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.exponential(0.5)
+  |> maths.is_close(1.648721, 0.0, tol)
+  |> should.be_true()
+  // An (overflow) error might occur when given an input
+  // value that will result in a too large output value
+  // e.g. maths.exponential(1000.0) but this is a property of the
+  // runtime.
+}
+
+pub fn float_natural_logarithm_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.natural_logarithm(1.0)
+  |> should.equal(Ok(0.0))
+
+  let assert Ok(result) = maths.natural_logarithm(0.5)
+  result
+  |> maths.is_close(-0.693147, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.natural_logarithm(-1.0)
+  |> should.be_error()
+}
+
+pub fn float_logarithm_test() {
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.logarithm(10.0, 10.0)
+  |> should.equal(Ok(1.0))
+
+  maths.logarithm(10.0, 100.0)
+  |> should.equal(Ok(0.5))
+
+  maths.logarithm(1.0, 0.25)
+  |> should.equal(Ok(0.0))
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.logarithm(1.0, 1.0)
+  |> should.be_error()
+
+  maths.logarithm(10.0, 1.0)
+  |> should.be_error()
+
+  maths.logarithm(-1.0, 1.0)
+  |> should.be_error()
+
+  maths.logarithm(1.0, 10.0)
+  |> should.equal(Ok(0.0))
+
+  maths.logarithm(maths.e(), maths.e())
+  |> should.equal(Ok(1.0))
+
+  maths.logarithm(-1.0, 2.0)
+  |> should.be_error()
+}
+
+pub fn float_logarithm_2_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  maths.logarithm_2(1.0)
+  |> should.equal(Ok(0.0))
+
+  maths.logarithm_2(2.0)
+  |> should.equal(Ok(1.0))
+
+  let assert Ok(result) = maths.logarithm_2(5.0)
+  result
+  |> maths.is_close(2.321928, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.logarithm_2(-1.0)
+  |> should.be_error()
+}
+
+pub fn float_logarithm_10_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) = maths.logarithm_10(1.0)
+  result
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) = maths.logarithm_10(10.0)
+  result
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) = maths.logarithm_10(50.0)
+  result
+  |> maths.is_close(1.69897, 0.0, tol)
+  |> should.be_true()
+
+  // Check that we get an error when the function is evaluated
+  // outside its domain
+  maths.logarithm_10(-1.0)
+  |> should.be_error()
+}
+
+pub fn float_nth_root_test() {
+  maths.nth_root(9.0, 2)
+  |> should.equal(Ok(3.0))
+
+  maths.nth_root(27.0, 3)
+  |> should.equal(Ok(3.0))
+
+  maths.nth_root(1.0, 4)
+  |> should.equal(Ok(1.0))
+
+  maths.nth_root(256.0, 4)
+  |> should.equal(Ok(4.0))
+
+  // An error should be returned as an imaginary number would otherwise
+  // have to be returned
+  maths.nth_root(-1.0, 4)
+  |> should.be_error()
+}
+
+pub fn float_constants_test() {
+  let assert Ok(tolerance) = float.power(10.0, -12.0)
+
+  // Test that the constant is approximately equal to 2.7128...
+  maths.e()
+  |> maths.is_close(2.7182818284590452353602, 0.0, tolerance)
+  |> should.be_true()
+
+  // Test that the constant is approximately equal to 2.7128...
+  maths.pi()
+  |> maths.is_close(3.14159265359, 0.0, tolerance)
+  |> should.be_true()
+
+  // Test that the constant is approximately equal to 1.6180...
+  maths.golden_ratio()
+  |> maths.is_close(1.618033988749895, 0.0, tolerance)
+  |> should.be_true()
+}
diff --git a/test/gleam_community/maths/arithmetics_test.gleam b/test/gleam_community/maths/arithmetics_test.gleam
deleted file mode 100644
index 3643561..0000000
--- a/test/gleam_community/maths/arithmetics_test.gleam
+++ /dev/null
@@ -1,227 +0,0 @@
-import gleam/option
-import gleam_community/maths/arithmetics
-import gleeunit/should
-
-pub fn int_gcd_test() {
-  arithmetics.gcd(1, 1)
-  |> should.equal(1)
-
-  arithmetics.gcd(100, 10)
-  |> should.equal(10)
-
-  arithmetics.gcd(10, 100)
-  |> should.equal(10)
-
-  arithmetics.gcd(100, -10)
-  |> should.equal(10)
-
-  arithmetics.gcd(-36, -17)
-  |> should.equal(1)
-
-  arithmetics.gcd(-30, -42)
-  |> should.equal(6)
-}
-
-pub fn int_euclidean_modulo_test() {
-  // Base Case: Positive x, Positive y
-  // Note that the truncated, floored, and euclidean 
-  // definitions should agree for this base case
-  arithmetics.int_euclidean_modulo(15, 4)
-  |> should.equal(3)
-
-  // Case: Positive x, Negative y
-  arithmetics.int_euclidean_modulo(15, -4)
-  |> should.equal(3)
-
-  // Case: Negative x, Positive y
-  arithmetics.int_euclidean_modulo(-15, 4)
-  |> should.equal(1)
-
-  // Case: Negative x, Negative y
-  arithmetics.int_euclidean_modulo(-15, -4)
-  |> should.equal(1)
-
-  // Case: Positive x, Zero y
-  arithmetics.int_euclidean_modulo(5, 0)
-  |> should.equal(0)
-
-  // Case: Zero x, Negative y
-  arithmetics.int_euclidean_modulo(0, 5)
-  |> should.equal(0)
-}
-
-pub fn int_lcm_test() {
-  arithmetics.lcm(1, 1)
-  |> should.equal(1)
-
-  arithmetics.lcm(100, 10)
-  |> should.equal(100)
-
-  arithmetics.lcm(10, 100)
-  |> should.equal(100)
-
-  arithmetics.lcm(100, -10)
-  |> should.equal(100)
-
-  arithmetics.lcm(-36, -17)
-  |> should.equal(612)
-
-  arithmetics.lcm(-30, -42)
-  |> should.equal(210)
-}
-
-pub fn int_proper_divisors_test() {
-  arithmetics.proper_divisors(2)
-  |> should.equal([1])
-
-  arithmetics.proper_divisors(6)
-  |> should.equal([1, 2, 3])
-
-  arithmetics.proper_divisors(13)
-  |> should.equal([1])
-
-  arithmetics.proper_divisors(18)
-  |> should.equal([1, 2, 3, 6, 9])
-}
-
-pub fn int_divisors_test() {
-  arithmetics.divisors(2)
-  |> should.equal([1, 2])
-
-  arithmetics.divisors(6)
-  |> should.equal([1, 2, 3, 6])
-
-  arithmetics.divisors(13)
-  |> should.equal([1, 13])
-
-  arithmetics.divisors(18)
-  |> should.equal([1, 2, 3, 6, 9, 18])
-}
-
-pub fn float_list_sum_test() {
-  // An empty list returns 0
-  []
-  |> arithmetics.float_sum(option.None)
-  |> should.equal(0.0)
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> arithmetics.float_sum(option.None)
-  |> should.equal(6.0)
-
-  [-2.0, 4.0, 6.0]
-  |> arithmetics.float_sum(option.None)
-  |> should.equal(8.0)
-}
-
-pub fn int_list_sum_test() {
-  // An empty list returns 0
-  []
-  |> arithmetics.int_sum()
-  |> should.equal(0)
-
-  // Valid input returns a result
-  [1, 2, 3]
-  |> arithmetics.int_sum()
-  |> should.equal(6)
-
-  [-2, 4, 6]
-  |> arithmetics.int_sum()
-  |> should.equal(8)
-}
-
-pub fn float_list_product_test() {
-  // An empty list returns 0
-  []
-  |> arithmetics.float_product(option.None)
-  |> should.equal(Ok(1.0))
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> arithmetics.float_product(option.None)
-  |> should.equal(Ok(6.0))
-
-  [-2.0, 4.0, 6.0]
-  |> arithmetics.float_product(option.None)
-  |> should.equal(Ok(-48.0))
-}
-
-pub fn int_list_product_test() {
-  // An empty list returns 0
-  []
-  |> arithmetics.int_product()
-  |> should.equal(1)
-
-  // Valid input returns a result
-  [1, 2, 3]
-  |> arithmetics.int_product()
-  |> should.equal(6)
-
-  [-2, 4, 6]
-  |> arithmetics.int_product()
-  |> should.equal(-48)
-}
-
-pub fn float_list_cumulative_sum_test() {
-  // An empty lists returns an empty list
-  []
-  |> arithmetics.float_cumulative_sum()
-  |> should.equal([])
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> arithmetics.float_cumulative_sum()
-  |> should.equal([1.0, 3.0, 6.0])
-
-  [-2.0, 4.0, 6.0]
-  |> arithmetics.float_cumulative_sum()
-  |> should.equal([-2.0, 2.0, 8.0])
-}
-
-pub fn int_list_cumulative_sum_test() {
-  // An empty lists returns an empty list
-  []
-  |> arithmetics.int_cumulative_sum()
-  |> should.equal([])
-
-  // Valid input returns a result
-  [1, 2, 3]
-  |> arithmetics.int_cumulative_sum()
-  |> should.equal([1, 3, 6])
-
-  [-2, 4, 6]
-  |> arithmetics.int_cumulative_sum()
-  |> should.equal([-2, 2, 8])
-}
-
-pub fn float_list_cumulative_product_test() {
-  // An empty lists returns an empty list
-  []
-  |> arithmetics.float_cumulative_product()
-  |> should.equal([])
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> arithmetics.float_cumulative_product()
-  |> should.equal([1.0, 2.0, 6.0])
-
-  [-2.0, 4.0, 6.0]
-  |> arithmetics.float_cumulative_product()
-  |> should.equal([-2.0, -8.0, -48.0])
-}
-
-pub fn int_list_cumulative_product_test() {
-  // An empty lists returns an empty list
-  []
-  |> arithmetics.int_cumulative_product()
-  |> should.equal([])
-
-  // Valid input returns a result
-  [1, 2, 3]
-  |> arithmetics.int_cumulative_product()
-  |> should.equal([1, 2, 6])
-
-  [-2, 4, 6]
-  |> arithmetics.int_cumulative_product()
-  |> should.equal([-2, -8, -48])
-}
diff --git a/test/gleam_community/maths/conversion_test.gleam b/test/gleam_community/maths/conversion_test.gleam
deleted file mode 100644
index 0a55a10..0000000
--- a/test/gleam_community/maths/conversion_test.gleam
+++ /dev/null
@@ -1,39 +0,0 @@
-import gleam_community/maths/conversion
-import gleam_community/maths/elementary
-import gleam_community/maths/predicates
-import gleeunit/should
-
-pub fn float_to_degree_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  conversion.radians_to_degrees(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  conversion.radians_to_degrees(2.0 *. elementary.pi())
-  |> predicates.is_close(360.0, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_to_radian_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  conversion.degrees_to_radians(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  conversion.degrees_to_radians(360.0)
-  |> predicates.is_close(2.0 *. elementary.pi(), 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_to_int_test() {
-  conversion.float_to_int(12.0654)
-  |> should.equal(12)
-}
-
-pub fn int_to_float_test() {
-  conversion.int_to_float(-1)
-  |> should.equal(-1.0)
-
-  conversion.int_to_float(1)
-  |> should.equal(1.0)
-}
diff --git a/test/gleam_community/maths/elementary_test.gleam b/test/gleam_community/maths/elementary_test.gleam
deleted file mode 100644
index a547f87..0000000
--- a/test/gleam_community/maths/elementary_test.gleam
+++ /dev/null
@@ -1,486 +0,0 @@
-import gleam/option
-import gleam_community/maths/elementary
-import gleam_community/maths/predicates
-import gleeunit/should
-
-pub fn float_acos_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) = elementary.acos(1.0)
-  result
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) = elementary.acos(0.5)
-  result
-  |> predicates.is_close(1.047197, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.acos(1.1)
-  |> should.be_error()
-
-  elementary.acos(-1.1)
-  |> should.be_error()
-}
-
-pub fn float_acosh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) = elementary.acosh(1.0)
-  result
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.acosh(0.0)
-  |> should.be_error()
-}
-
-pub fn float_asin_test() {
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.asin(0.0)
-  |> should.equal(Ok(0.0))
-
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  let assert Ok(result) = elementary.asin(0.5)
-  result
-  |> predicates.is_close(0.523598, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.asin(1.1)
-  |> should.be_error()
-
-  elementary.asin(-1.1)
-  |> should.be_error()
-}
-
-pub fn float_asinh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.asinh(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.asinh(0.5)
-  |> predicates.is_close(0.481211, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_atan_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.atan(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.atan(0.5)
-  |> predicates.is_close(0.463647, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn math_atan2_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.atan2(0.0, 0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.atan2(0.0, 1.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  // Check atan2(y=1.0, x=0.5)
-  // Should be equal to atan(y / x) for any x > 0 and any y
-  let result = elementary.atan(1.0 /. 0.5)
-  elementary.atan2(1.0, 0.5)
-  |> predicates.is_close(result, 0.0, tol)
-  |> should.be_true()
-
-  // Check atan2(y=2.0, x=-1.5)
-  // Should be equal to pi + atan(y / x) for any x < 0 and y >= 0
-  let result = elementary.pi() +. elementary.atan(2.0 /. -1.5)
-  elementary.atan2(2.0, -1.5)
-  |> predicates.is_close(result, 0.0, tol)
-  |> should.be_true()
-
-  // Check atan2(y=-2.0, x=-1.5)
-  // Should be equal to atan(y / x) - pi for any x < 0 and y < 0
-  let result = elementary.atan(-2.0 /. -1.5) -. elementary.pi()
-  elementary.atan2(-2.0, -1.5)
-  |> predicates.is_close(result, 0.0, tol)
-  |> should.be_true()
-
-  // Check atan2(y=1.5, x=0.0)
-  // Should be equal to pi/2 for x = 0 and any y > 0
-  let result = elementary.pi() /. 2.0
-  elementary.atan2(1.5, 0.0)
-  |> predicates.is_close(result, 0.0, tol)
-  |> should.be_true()
-
-  // Check atan2(y=-1.5, x=0.0)
-  // Should be equal to -pi/2 for x = 0 and any y < 0
-  let result = -1.0 *. elementary.pi() /. 2.0
-  elementary.atan2(-1.5, 0.0)
-  |> predicates.is_close(result, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_atanh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) = elementary.atanh(0.0)
-  result
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) = elementary.atanh(0.5)
-  result
-  |> predicates.is_close(0.549306, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.atanh(1.0)
-  |> should.be_error()
-
-  elementary.atanh(2.0)
-  |> should.be_error()
-
-  elementary.atanh(1.0)
-  |> should.be_error()
-
-  elementary.atanh(-2.0)
-  |> should.be_error()
-}
-
-pub fn float_cos_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.cos(0.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.cos(elementary.pi())
-  |> predicates.is_close(-1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.cos(0.5)
-  |> predicates.is_close(0.877582, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_cosh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.cosh(0.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.cosh(0.5)
-  |> predicates.is_close(1.127625, 0.0, tol)
-  |> should.be_true()
-  // An (overflow) error might occur when given an input
-  // value that will result in a too large output value
-  // e.g. elementary.cosh(1000.0) but this is a property of the
-  // runtime.
-}
-
-pub fn float_sin_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.sin(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.sin(0.5 *. elementary.pi())
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.sin(0.5)
-  |> predicates.is_close(0.479425, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_sinh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.sinh(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.sinh(0.5)
-  |> predicates.is_close(0.521095, 0.0, tol)
-  |> should.be_true()
-  // An (overflow) error might occur when given an input
-  // value that will result in a too large output value
-  // e.g. elementary.sinh(1000.0) but this is a property of the
-  // runtime.
-}
-
-pub fn math_tan_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.tan(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.tan(0.5)
-  |> predicates.is_close(0.546302, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn math_tanh_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.tanh(0.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.tanh(25.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.tanh(-25.0)
-  |> predicates.is_close(-1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.tanh(0.5)
-  |> predicates.is_close(0.462117, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_exponential_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.exponential(0.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  elementary.exponential(0.5)
-  |> predicates.is_close(1.648721, 0.0, tol)
-  |> should.be_true()
-  // An (overflow) error might occur when given an input
-  // value that will result in a too large output value
-  // e.g. elementary.exponential(1000.0) but this is a property of the
-  // runtime.
-}
-
-pub fn float_natural_logarithm_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.natural_logarithm(1.0)
-  |> should.equal(Ok(0.0))
-
-  let assert Ok(result) = elementary.natural_logarithm(0.5)
-  result
-  |> predicates.is_close(-0.693147, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.natural_logarithm(-1.0)
-  |> should.be_error()
-}
-
-pub fn float_logarithm_test() {
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.logarithm(10.0, option.Some(10.0))
-  |> should.equal(Ok(1.0))
-
-  elementary.logarithm(10.0, option.Some(100.0))
-  |> should.equal(Ok(0.5))
-
-  elementary.logarithm(1.0, option.Some(0.25))
-  |> should.equal(Ok(0.0))
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.logarithm(1.0, option.Some(1.0))
-  |> should.be_error()
-
-  elementary.logarithm(10.0, option.Some(1.0))
-  |> should.be_error()
-
-  elementary.logarithm(-1.0, option.Some(1.0))
-  |> should.be_error()
-
-  elementary.logarithm(1.0, option.Some(10.0))
-  |> should.equal(Ok(0.0))
-
-  elementary.logarithm(elementary.e(), option.Some(elementary.e()))
-  |> should.equal(Ok(1.0))
-
-  elementary.logarithm(-1.0, option.Some(2.0))
-  |> should.be_error()
-}
-
-pub fn float_logarithm_2_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  elementary.logarithm_2(1.0)
-  |> should.equal(Ok(0.0))
-
-  elementary.logarithm_2(2.0)
-  |> should.equal(Ok(1.0))
-
-  let assert Ok(result) = elementary.logarithm_2(5.0)
-  result
-  |> predicates.is_close(2.321928, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.logarithm_2(-1.0)
-  |> should.be_error()
-}
-
-pub fn float_logarithm_10_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) = elementary.logarithm_10(1.0)
-  result
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) = elementary.logarithm_10(10.0)
-  result
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) = elementary.logarithm_10(50.0)
-  result
-  |> predicates.is_close(1.69897, 0.0, tol)
-  |> should.be_true()
-
-  // Check that we get an error when the function is evaluated
-  // outside its domain
-  elementary.logarithm_10(-1.0)
-  |> should.be_error()
-}
-
-pub fn float_power_test() {
-  elementary.power(2.0, 2.0)
-  |> should.equal(Ok(4.0))
-
-  elementary.power(-5.0, 3.0)
-  |> should.equal(Ok(-125.0))
-
-  elementary.power(10.5, 0.0)
-  |> should.equal(Ok(1.0))
-
-  elementary.power(16.0, 0.5)
-  |> should.equal(Ok(4.0))
-
-  elementary.power(2.0, -1.0)
-  |> should.equal(Ok(0.5))
-
-  elementary.power(2.0, -1.0)
-  |> should.equal(Ok(0.5))
-
-  // When y = 0, the result should universally be 1, regardless of the value of x 
-  elementary.power(10.0, 0.0)
-  |> should.equal(Ok(1.0))
-  elementary.power(-10.0, 0.0)
-  |> should.equal(Ok(1.0))
-
-  elementary.power(2.0, -1.0)
-  |> should.equal(Ok(0.5))
-
-  // elementary.power(-1.0, 0.5) is equivalent to float.square_root(-1.0)
-  // and should return an error as an imaginary number would otherwise
-  // have to be returned
-  elementary.power(-1.0, 0.5)
-  |> should.be_error()
-
-  // Check another case with a negative base and fractional exponent
-  elementary.power(-1.5, 1.5)
-  |> should.be_error()
-
-  // elementary.power(0.0, -1.0) is equivalent to 1. /. 0 and is expected
-  // to be an error
-  elementary.power(0.0, -1.0)
-  |> should.be_error()
-
-  // Check that a negative base and exponent is fine as long as the
-  // exponent is not fractional
-  elementary.power(-2.0, -1.0)
-  |> should.equal(Ok(-0.5))
-}
-
-pub fn float_square_root_test() {
-  elementary.square_root(1.0)
-  |> should.equal(Ok(1.0))
-
-  elementary.square_root(9.0)
-  |> should.equal(Ok(3.0))
-
-  // An error should be returned as an imaginary number would otherwise
-  // have to be returned
-  elementary.square_root(-1.0)
-  |> should.be_error()
-}
-
-pub fn float_cube_root_test() {
-  elementary.cube_root(1.0)
-  |> should.equal(Ok(1.0))
-
-  elementary.cube_root(27.0)
-  |> should.equal(Ok(3.0))
-
-  // An error should be returned as an imaginary number would otherwise
-  // have to be returned
-  elementary.cube_root(-1.0)
-  |> should.be_error()
-}
-
-pub fn float_nth_root_test() {
-  elementary.nth_root(9.0, 2)
-  |> should.equal(Ok(3.0))
-
-  elementary.nth_root(27.0, 3)
-  |> should.equal(Ok(3.0))
-
-  elementary.nth_root(1.0, 4)
-  |> should.equal(Ok(1.0))
-
-  elementary.nth_root(256.0, 4)
-  |> should.equal(Ok(4.0))
-
-  // An error should be returned as an imaginary number would otherwise
-  // have to be returned
-  elementary.nth_root(-1.0, 4)
-  |> should.be_error()
-}
-
-pub fn float_constants_test() {
-  elementary.e()
-  |> predicates.is_close(2.71828, 0.0, 0.00001)
-  |> should.be_true()
-
-  elementary.pi()
-  |> predicates.is_close(3.14159, 0.0, 0.00001)
-  |> should.be_true()
-}
diff --git a/test/gleam_community/maths/metrics_test.gleam b/test/gleam_community/maths/metrics_test.gleam
deleted file mode 100644
index 4debea5..0000000
--- a/test/gleam_community/maths/metrics_test.gleam
+++ /dev/null
@@ -1,629 +0,0 @@
-import gleam/option
-import gleam/set
-import gleam_community/maths/elementary
-import gleam_community/maths/metrics
-import gleam_community/maths/predicates
-import gleeunit/should
-
-pub fn float_list_norm_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // An empty lists returns 0.0
-  []
-  |> metrics.norm(1.0, option.None)
-  |> should.equal(Ok(0.0))
-
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) =
-    [1.0, 1.0, 1.0]
-    |> metrics.norm(1.0, option.None)
-  result
-  |> predicates.is_close(3.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [1.0, 1.0, 1.0]
-    |> metrics.norm(-1.0, option.None)
-  result
-  |> predicates.is_close(0.3333333333333333, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [-1.0, -1.0, -1.0]
-    |> metrics.norm(-1.0, option.None)
-  result
-  |> predicates.is_close(0.3333333333333333, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [-1.0, -1.0, -1.0]
-    |> metrics.norm(1.0, option.None)
-  result
-  |> predicates.is_close(3.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [-1.0, -2.0, -3.0]
-    |> metrics.norm(-10.0, option.None)
-  result
-  |> predicates.is_close(0.9999007044905545, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [-1.0, -2.0, -3.0]
-    |> metrics.norm(-100.0, option.None)
-  result
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    [-1.0, -2.0, -3.0]
-    |> metrics.norm(2.0, option.None)
-  result
-  |> predicates.is_close(3.7416573867739413, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_list_manhattan_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Empty lists returns an error
-  metrics.manhattan_distance([], [], option.None)
-  |> should.be_error()
-
-  // Differing lengths returns error
-  metrics.manhattan_distance([], [1.0], option.None)
-  |> should.be_error()
-
-  // Try with valid input (same as Minkowski distance with p = 1)
-  let assert Ok(result) =
-    metrics.manhattan_distance([0.0, 0.0], [1.0, 2.0], option.None)
-  result
-  |> predicates.is_close(3.0, 0.0, tol)
-  |> should.be_true()
-
-  metrics.manhattan_distance([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
-  |> should.equal(Ok(9.0))
-
-  metrics.manhattan_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([1.0, 1.0, 1.0]),
-  )
-  |> should.equal(Ok(9.0))
-
-  metrics.manhattan_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([1.0, 2.0, 3.0]),
-  )
-  |> should.equal(Ok(18.0))
-
-  // Try invalid input with weights (different sized lists returns an error)
-  metrics.manhattan_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([7.0, 8.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.manhattan_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
-
-pub fn float_list_minkowski_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Empty lists returns an error
-  metrics.minkowski_distance([], [], 1.0, option.None)
-  |> should.be_error()
-
-  // Differing lengths returns error
-  metrics.minkowski_distance([], [1.0], 1.0, option.None)
-  |> should.be_error()
-
-  // Test order < 1
-  metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0, option.None)
-  |> should.be_error()
-
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  let assert Ok(result) =
-    metrics.minkowski_distance([1.0, 1.0], [1.0, 1.0], 1.0, option.None)
-  result
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0, option.None)
-  result
-  |> predicates.is_close(1.0717734625362931, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 100.0, option.None)
-  result
-  |> predicates.is_close(1.0069555500567189, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.minkowski_distance([0.0, 0.0], [1.0, 1.0], 10.0, option.None)
-  result
-  |> predicates.is_close(1.0717734625362931, 0.0, tol)
-  |> should.be_true()
-
-  // Euclidean distance (p = 2)
-  let assert Ok(result) =
-    metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 2.0, option.None)
-  result
-  |> predicates.is_close(2.23606797749979, 0.0, tol)
-  |> should.be_true()
-
-  // Manhattan distance (p = 1)
-  let assert Ok(result) =
-    metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0, option.None)
-  result
-  |> predicates.is_close(3.0, 0.0, tol)
-  |> should.be_true()
-
-  // Try different valid input
-  let assert Ok(result) =
-    metrics.minkowski_distance(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      4.0,
-      option.None,
-    )
-  result
-  |> predicates.is_close(3.9482220388574776, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.minkowski_distance(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      4.0,
-      option.Some([1.0, 1.0, 1.0]),
-    )
-  result
-  |> predicates.is_close(3.9482220388574776, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.minkowski_distance(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      4.0,
-      option.Some([1.0, 2.0, 3.0]),
-    )
-  result
-  |> predicates.is_close(4.6952537402198615, 0.0, tol)
-  |> should.be_true()
-
-  // Try invalid input with weights (different sized lists returns an error)
-  metrics.minkowski_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    2.0,
-    option.Some([7.0, 8.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.minkowski_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    2.0,
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
-
-pub fn float_list_euclidean_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Empty lists returns an error
-  metrics.euclidean_distance([], [], option.None)
-  |> should.be_error()
-
-  // Differing lengths returns error
-  metrics.euclidean_distance([], [1.0], option.None)
-  |> should.be_error()
-
-  // Try with valid input (same as Minkowski distance with p = 2)
-  let assert Ok(result) =
-    metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0], option.None)
-  result
-  |> predicates.is_close(2.23606797749979, 0.0, tol)
-  |> should.be_true()
-
-  // Try different valid input
-  let assert Ok(result) =
-    metrics.euclidean_distance([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
-  result
-  |> predicates.is_close(5.196152422706632, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.euclidean_distance(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      option.Some([1.0, 1.0, 1.0]),
-    )
-  result
-  |> predicates.is_close(5.196152422706632, 0.0, tol)
-  |> should.be_true()
-
-  let assert Ok(result) =
-    metrics.euclidean_distance(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      option.Some([1.0, 2.0, 3.0]),
-    )
-  result
-  |> predicates.is_close(7.3484692283495345, 0.0, tol)
-  |> should.be_true()
-
-  // Try invalid input with weights (different sized lists returns an error)
-  metrics.euclidean_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([7.0, 8.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.euclidean_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
-
-pub fn mean_test() {
-  // An empty list returns an error
-  []
-  |> metrics.mean()
-  |> should.be_error()
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> metrics.mean()
-  |> should.equal(Ok(2.0))
-}
-
-pub fn median_test() {
-  // An empty list returns an error
-  []
-  |> metrics.median()
-  |> should.be_error()
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> metrics.median()
-  |> should.equal(Ok(2.0))
-
-  [1.0, 2.0, 3.0, 4.0]
-  |> metrics.median()
-  |> should.equal(Ok(2.5))
-}
-
-pub fn variance_test() {
-  // Degrees of freedom
-  let ddof = 1
-
-  // An empty list returns an error
-  []
-  |> metrics.variance(ddof)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> metrics.variance(ddof)
-  |> should.equal(Ok(1.0))
-}
-
-pub fn standard_deviation_test() {
-  // Degrees of freedom
-  let ddof = 1
-
-  // An empty list returns an error
-  []
-  |> metrics.standard_deviation(ddof)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [1.0, 2.0, 3.0]
-  |> metrics.standard_deviation(ddof)
-  |> should.equal(Ok(1.0))
-}
-
-pub fn jaccard_index_test() {
-  metrics.jaccard_index(set.from_list([]), set.from_list([]))
-  |> should.equal(0.0)
-
-  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
-  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
-  metrics.jaccard_index(set_a, set_b)
-  |> should.equal(4.0 /. 10.0)
-
-  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
-  let set_d = set.from_list([6, 7, 8, 9, 10])
-  metrics.jaccard_index(set_c, set_d)
-  |> should.equal(0.0 /. 11.0)
-
-  let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
-  let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
-  metrics.jaccard_index(set_e, set_f)
-  |> should.equal(1.0 /. 7.0)
-}
-
-pub fn sorensen_dice_coefficient_test() {
-  metrics.sorensen_dice_coefficient(set.from_list([]), set.from_list([]))
-  |> should.equal(0.0)
-
-  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
-  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
-  metrics.sorensen_dice_coefficient(set_a, set_b)
-  |> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 })
-
-  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
-  let set_d = set.from_list([6, 7, 8, 9, 10])
-  metrics.sorensen_dice_coefficient(set_c, set_d)
-  |> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 })
-
-  let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
-  let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
-  metrics.sorensen_dice_coefficient(set_e, set_f)
-  |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
-}
-
-pub fn overlap_coefficient_test() {
-  metrics.overlap_coefficient(set.from_list([]), set.from_list([]))
-  |> should.equal(0.0)
-
-  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
-  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
-  metrics.overlap_coefficient(set_a, set_b)
-  |> should.equal(4.0 /. 7.0)
-
-  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
-  let set_d = set.from_list([6, 7, 8, 9, 10])
-  metrics.overlap_coefficient(set_c, set_d)
-  |> should.equal(0.0 /. 5.0)
-
-  let set_e = set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
-  let set_f = set.from_list(["monkey", "bird", "ostrich", "salmon"])
-  metrics.overlap_coefficient(set_e, set_f)
-  |> should.equal(2.0 /. 4.0)
-}
-
-pub fn cosine_similarity_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Empty lists returns an error
-  metrics.cosine_similarity([], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.cosine_similarity([1.0, 2.0, 3.0], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.cosine_similarity([], [1.0, 2.0, 3.0], option.None)
-  |> should.be_error()
-
-  // Different sized lists returns an error
-  metrics.cosine_similarity([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
-  |> should.be_error()
-
-  // Two orthogonal vectors (represented by lists)
-  metrics.cosine_similarity([-1.0, 1.0, 0.0], [1.0, 1.0, -1.0], option.None)
-  |> should.equal(Ok(0.0))
-
-  // Two identical (parallel) vectors (represented by lists)
-  metrics.cosine_similarity([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], option.None)
-  |> should.equal(Ok(1.0))
-
-  // Two parallel, but oppositely oriented vectors (represented by lists)
-  metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None)
-  |> should.equal(Ok(-1.0))
-
-  // Try with arbitrary valid input
-  let assert Ok(result) =
-    metrics.cosine_similarity([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
-  result
-  |> predicates.is_close(0.9746318461970762, 0.0, tol)
-  |> should.be_true()
-
-  // Try valid input with weights
-  let assert Ok(result) =
-    metrics.cosine_similarity(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      option.Some([1.0, 1.0, 1.0]),
-    )
-  result
-  |> predicates.is_close(0.9746318461970762, 0.0, tol)
-  |> should.be_true()
-
-  // Try with different weights
-  let assert Ok(result) =
-    metrics.cosine_similarity(
-      [1.0, 2.0, 3.0],
-      [4.0, 5.0, 6.0],
-      option.Some([1.0, 2.0, 3.0]),
-    )
-  result
-  |> predicates.is_close(0.9855274566525745, 0.0, tol)
-  |> should.be_true()
-
-  // Try invalid input with weights (different sized lists returns an error)
-  metrics.cosine_similarity(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([7.0, 8.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.cosine_similarity(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
-
-pub fn chebyshev_distance_test() {
-  // Empty lists returns an error
-  metrics.chebyshev_distance([], [])
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.chebyshev_distance([1.0, 2.0, 3.0], [])
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.chebyshev_distance([], [1.0, 2.0, 3.0])
-  |> should.be_error()
-
-  // Different sized lists returns an error
-  metrics.chebyshev_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0])
-  |> should.be_error()
-
-  // Try different types of valid input
-  metrics.chebyshev_distance([1.0, 0.0], [0.0, 2.0])
-  |> should.equal(Ok(2.0))
-
-  metrics.chebyshev_distance([1.0, 0.0], [2.0, 0.0])
-  |> should.equal(Ok(1.0))
-
-  metrics.chebyshev_distance([1.0, 0.0], [-2.0, 0.0])
-  |> should.equal(Ok(3.0))
-
-  metrics.chebyshev_distance([-5.0, -10.0, -3.0], [-1.0, -12.0, -3.0])
-  |> should.equal(Ok(4.0))
-
-  metrics.chebyshev_distance([1.0, 2.0, 3.0], [1.0, 2.0, 3.0])
-  |> should.equal(Ok(0.0))
-}
-
-pub fn canberra_distance_test() {
-  // Empty lists returns an error
-  metrics.canberra_distance([], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.canberra_distance([1.0, 2.0, 3.0], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.canberra_distance([], [1.0, 2.0, 3.0], option.None)
-  |> should.be_error()
-
-  // Different sized lists returns an error
-  metrics.canberra_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
-  |> should.be_error()
-
-  // Try different types of valid input
-  metrics.canberra_distance([0.0, 0.0], [0.0, 0.0], option.None)
-  |> should.equal(Ok(0.0))
-
-  metrics.canberra_distance([1.0, 2.0], [-2.0, -1.0], option.None)
-  |> should.equal(Ok(2.0))
-
-  metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.None)
-  |> should.equal(Ok(2.0))
-
-  metrics.canberra_distance([1.0, 0.0], [2.0, 0.0], option.None)
-  |> should.equal(Ok(1.0 /. 3.0))
-
-  metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 1.0]))
-  |> should.equal(Ok(2.0))
-
-  metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 0.5]))
-  |> should.equal(Ok(1.5))
-
-  metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([0.5, 0.5]))
-  |> should.equal(Ok(1.0))
-
-  // Different sized lists (weights) returns an error
-  metrics.canberra_distance(
-    [1.0, 2.0, 3.0],
-    [1.0, 2.0, 3.0],
-    option.Some([1.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.canberra_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
-
-pub fn braycurtis_distance_test() {
-  // Empty lists returns an error
-  metrics.braycurtis_distance([], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.braycurtis_distance([1.0, 2.0, 3.0], [], option.None)
-  |> should.be_error()
-
-  // One empty list returns an error
-  metrics.braycurtis_distance([], [1.0, 2.0, 3.0], option.None)
-  |> should.be_error()
-
-  // Different sized lists returns an error
-  metrics.braycurtis_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
-  |> should.be_error()
-
-  // Try different types of valid input
-  metrics.braycurtis_distance([0.0, 0.0], [0.0, 0.0], option.None)
-  |> should.equal(Ok(0.0))
-
-  metrics.braycurtis_distance([1.0, 2.0], [-2.0, -1.0], option.None)
-  |> should.equal(Ok(3.0))
-
-  metrics.braycurtis_distance([1.0, 0.0], [0.0, 2.0], option.None)
-  |> should.equal(Ok(1.0))
-
-  metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.None)
-  |> should.equal(Ok(0.4))
-
-  metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([1.0, 1.0]))
-  |> should.equal(Ok(0.4))
-
-  metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.5, 1.0]))
-  |> should.equal(Ok(0.375))
-
-  metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.25, 0.25]))
-  |> should.equal(Ok(0.4))
-
-  // Different sized lists (weights) returns an error
-  metrics.braycurtis_distance(
-    [1.0, 2.0, 3.0],
-    [1.0, 2.0, 3.0],
-    option.Some([1.0]),
-  )
-  |> should.be_error()
-
-  // Try invalid input with weights that are negative
-  metrics.braycurtis_distance(
-    [1.0, 2.0, 3.0],
-    [4.0, 5.0, 6.0],
-    option.Some([-7.0, -8.0, -9.0]),
-  )
-  |> should.be_error()
-}
diff --git a/test/gleam_community/maths/piecewise_test.gleam b/test/gleam_community/maths/piecewise_test.gleam
deleted file mode 100644
index 81ab38e..0000000
--- a/test/gleam_community/maths/piecewise_test.gleam
+++ /dev/null
@@ -1,753 +0,0 @@
-import gleam/float
-import gleam/int
-import gleam/option
-import gleam_community/maths/piecewise
-import gleeunit/should
-
-pub fn float_ceiling_test() {
-  // Round 3. digit AFTER decimal point 
-  piecewise.ceiling(12.0654, option.Some(3))
-  |> should.equal(12.066)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.ceiling(12.0654, option.Some(2))
-  |> should.equal(12.07)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.ceiling(12.0654, option.Some(1))
-  |> should.equal(12.1)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.ceiling(12.0654, option.Some(0))
-  |> should.equal(13.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.ceiling(12.0654, option.Some(-1))
-  |> should.equal(20.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.ceiling(12.0654, option.Some(-2))
-  |> should.equal(100.0)
-
-  // Round 3. digit BEFORE decimal point 
-  piecewise.ceiling(12.0654, option.Some(-3))
-  |> should.equal(1000.0)
-}
-
-pub fn float_floor_test() {
-  // Round 3. digit AFTER decimal point 
-  piecewise.floor(12.0654, option.Some(3))
-  |> should.equal(12.065)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.floor(12.0654, option.Some(2))
-  |> should.equal(12.06)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.floor(12.0654, option.Some(1))
-  |> should.equal(12.0)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.floor(12.0654, option.Some(0))
-  |> should.equal(12.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.floor(12.0654, option.Some(-1))
-  |> should.equal(10.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.floor(12.0654, option.Some(-2))
-  |> should.equal(0.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.floor(12.0654, option.Some(-3))
-  |> should.equal(0.0)
-}
-
-pub fn float_truncate_test() {
-  // Round 3. digit AFTER decimal point 
-  piecewise.truncate(12.0654, option.Some(3))
-  |> should.equal(12.065)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.truncate(12.0654, option.Some(2))
-  |> should.equal(12.06)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.truncate(12.0654, option.Some(1))
-  |> should.equal(12.0)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.truncate(12.0654, option.Some(0))
-  |> should.equal(12.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.truncate(12.0654, option.Some(-1))
-  |> should.equal(10.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.truncate(12.0654, option.Some(-2))
-  |> should.equal(0.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.truncate(12.0654, option.Some(-3))
-  |> should.equal(0.0)
-}
-
-pub fn math_round_to_nearest_test() {
-  // Try with positive values
-  piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(2.0)
-
-  piecewise.round(1.75, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(2.0)
-
-  piecewise.round(2.0, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(2.0)
-
-  piecewise.round(3.5, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(4.0)
-
-  piecewise.round(4.5, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(4.0)
-
-  // Try with negative values
-  piecewise.round(-3.5, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(-4.0)
-
-  piecewise.round(-4.5, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(-4.0)
-
-  // Round 3. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundNearest))
-  |> should.equal(12.065)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundNearest))
-  |> should.equal(12.07)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundNearest))
-  |> should.equal(12.1)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundNearest))
-  |> should.equal(12.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundNearest))
-  |> should.equal(10.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundNearest))
-  |> should.equal(0.0)
-
-  // Round 3. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundNearest))
-  |> should.equal(0.0)
-}
-
-pub fn math_round_up_test() {
-  // Note: Rounding mode "RoundUp" is an alias for the ceiling function
-  // Try with positive values
-  piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundUp))
-  |> should.equal(1.0)
-
-  piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundUp))
-  |> should.equal(1.0)
-
-  piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundUp))
-  |> should.equal(0.5)
-
-  piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundUp))
-  |> should.equal(0.5)
-
-  piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundUp))
-  |> should.equal(0.46)
-
-  piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundUp))
-  |> should.equal(0.51)
-
-  // Try with negative values
-  piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.0)
-
-  piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.0)
-
-  piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.4)
-
-  piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.5)
-
-  piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.45)
-
-  piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundUp))
-  |> should.equal(-0.5)
-}
-
-pub fn math_round_down_test() {
-  // Note: Rounding mode "RoundDown" is an alias for the floor function
-  // Try with positive values
-  piecewise.round(0.45, option.Some(0), option.Some(piecewise.RoundDown))
-  |> should.equal(0.0)
-
-  piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundDown))
-  |> should.equal(0.0)
-
-  piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundDown))
-  |> should.equal(0.4)
-
-  piecewise.round(0.5, option.Some(1), option.Some(piecewise.RoundDown))
-  |> should.equal(0.5)
-
-  piecewise.round(0.455, option.Some(2), option.Some(piecewise.RoundDown))
-  |> should.equal(0.45)
-
-  piecewise.round(0.505, option.Some(2), option.Some(piecewise.RoundDown))
-  |> should.equal(0.5)
-
-  // Try with negative values
-  piecewise.round(-0.45, option.Some(0), option.Some(piecewise.RoundDown))
-  |> should.equal(-1.0)
-
-  piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundDown))
-  |> should.equal(-1.0)
-
-  piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundDown))
-  |> should.equal(-0.5)
-
-  piecewise.round(-0.5, option.Some(1), option.Some(piecewise.RoundDown))
-  |> should.equal(-0.5)
-
-  piecewise.round(-0.455, option.Some(2), option.Some(piecewise.RoundDown))
-  |> should.equal(-0.46)
-
-  piecewise.round(-0.505, option.Some(2), option.Some(piecewise.RoundDown))
-  |> should.equal(-0.51)
-}
-
-pub fn math_round_to_zero_test() {
-  // Note: Rounding mode "RoundToZero" is an alias for the truncate function
-  // Try with positive values
-  piecewise.round(0.5, option.Some(0), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.0)
-
-  piecewise.round(0.75, option.Some(0), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.0)
-
-  piecewise.round(0.45, option.Some(1), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.4)
-
-  piecewise.round(0.57, option.Some(1), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.5)
-
-  piecewise.round(0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.45)
-
-  piecewise.round(0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.5)
-
-  // Try with negative values
-  piecewise.round(-0.5, option.Some(0), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.0)
-
-  piecewise.round(-0.75, option.Some(0), option.Some(piecewise.RoundToZero))
-  |> should.equal(0.0)
-
-  piecewise.round(-0.45, option.Some(1), option.Some(piecewise.RoundToZero))
-  |> should.equal(-0.4)
-
-  piecewise.round(-0.57, option.Some(1), option.Some(piecewise.RoundToZero))
-  |> should.equal(-0.5)
-
-  piecewise.round(-0.4575, option.Some(2), option.Some(piecewise.RoundToZero))
-  |> should.equal(-0.45)
-
-  piecewise.round(-0.5075, option.Some(2), option.Some(piecewise.RoundToZero))
-  |> should.equal(-0.5)
-}
-
-pub fn math_round_ties_away_test() {
-  // Try with positive values
-  piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(1.0)
-
-  piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(2.0)
-
-  piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(3.0)
-
-  // Try with negative values
-  piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(-1.0)
-
-  piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(-2.0)
-
-  piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(-2.0)
-
-  piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(-3.0)
-
-  // Round 3. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(12.065)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(12.07)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(12.1)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesAway))
-  |> should.equal(12.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.round(
-    12.0654,
-    option.Some(-1),
-    option.Some(piecewise.RoundTiesAway),
-  )
-  |> should.equal(10.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.round(
-    12.0654,
-    option.Some(-2),
-    option.Some(piecewise.RoundTiesAway),
-  )
-  |> should.equal(0.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.round(
-    12.0654,
-    option.Some(-3),
-    option.Some(piecewise.RoundTiesAway),
-  )
-  |> should.equal(0.0)
-}
-
-pub fn math_round_ties_up_test() {
-  // Try with positive values
-  piecewise.round(1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(1.0)
-
-  piecewise.round(1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(2.0)
-
-  piecewise.round(2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(3.0)
-
-  // Try with negative values
-  piecewise.round(-1.4, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(-1.0)
-
-  piecewise.round(-1.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(-1.0)
-
-  piecewise.round(-2.0, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(-2.0)
-
-  piecewise.round(-2.5, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(-2.0)
-
-  // Round 3. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(3), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(12.065)
-
-  // Round 2. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(2), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(12.07)
-
-  // Round 1. digit AFTER decimal point 
-  piecewise.round(12.0654, option.Some(1), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(12.1)
-
-  // Round 0. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(0), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(12.0)
-
-  // Round 1. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-1), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(10.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-2), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(0.0)
-
-  // Round 2. digit BEFORE decimal point 
-  piecewise.round(12.0654, option.Some(-3), option.Some(piecewise.RoundTiesUp))
-  |> should.equal(0.0)
-}
-
-pub fn math_round_edge_cases_test() {
-  // The default number of digits is 0 if None is provided
-  piecewise.round(12.0654, option.None, option.Some(piecewise.RoundNearest))
-  |> should.equal(12.0)
-
-  // The default rounding mode is piecewise.RoundNearest if None is provided 
-  piecewise.round(12.0654, option.None, option.None)
-  |> should.equal(12.0)
-}
-
-pub fn float_absolute_value_test() {
-  piecewise.float_absolute_value(20.0)
-  |> should.equal(20.0)
-
-  piecewise.float_absolute_value(-20.0)
-  |> should.equal(20.0)
-}
-
-pub fn int_absolute_value_test() {
-  piecewise.int_absolute_value(20)
-  |> should.equal(20)
-
-  piecewise.int_absolute_value(-20)
-  |> should.equal(20)
-}
-
-pub fn float_absolute_difference_test() {
-  piecewise.float_absolute_difference(20.0, 15.0)
-  |> should.equal(5.0)
-
-  piecewise.float_absolute_difference(-20.0, -15.0)
-  |> should.equal(5.0)
-
-  piecewise.float_absolute_difference(20.0, -15.0)
-  |> should.equal(35.0)
-
-  piecewise.float_absolute_difference(-20.0, 15.0)
-  |> should.equal(35.0)
-
-  piecewise.float_absolute_difference(0.0, 0.0)
-  |> should.equal(0.0)
-
-  piecewise.float_absolute_difference(1.0, 2.0)
-  |> should.equal(1.0)
-
-  piecewise.float_absolute_difference(2.0, 1.0)
-  |> should.equal(1.0)
-
-  piecewise.float_absolute_difference(-1.0, 0.0)
-  |> should.equal(1.0)
-
-  piecewise.float_absolute_difference(0.0, -1.0)
-  |> should.equal(1.0)
-
-  piecewise.float_absolute_difference(10.0, 20.0)
-  |> should.equal(10.0)
-
-  piecewise.float_absolute_difference(-10.0, -20.0)
-  |> should.equal(10.0)
-
-  piecewise.float_absolute_difference(-10.5, 10.5)
-  |> should.equal(21.0)
-}
-
-pub fn int_absolute_difference_test() {
-  piecewise.int_absolute_difference(20, 15)
-  |> should.equal(5)
-
-  piecewise.int_absolute_difference(-20, -15)
-  |> should.equal(5)
-
-  piecewise.int_absolute_difference(20, -15)
-  |> should.equal(35)
-
-  piecewise.int_absolute_difference(-20, 15)
-  |> should.equal(35)
-}
-
-pub fn float_sign_test() {
-  piecewise.float_sign(100.0)
-  |> should.equal(1.0)
-
-  piecewise.float_sign(0.0)
-  |> should.equal(0.0)
-
-  piecewise.float_sign(-100.0)
-  |> should.equal(-1.0)
-}
-
-pub fn float_flip_sign_test() {
-  piecewise.float_flip_sign(100.0)
-  |> should.equal(-100.0)
-
-  piecewise.float_flip_sign(0.0)
-  |> should.equal(-0.0)
-
-  piecewise.float_flip_sign(-100.0)
-  |> should.equal(100.0)
-}
-
-pub fn float_copy_sign_test() {
-  piecewise.float_copy_sign(100.0, 10.0)
-  |> should.equal(100.0)
-
-  piecewise.float_copy_sign(-100.0, 10.0)
-  |> should.equal(100.0)
-
-  piecewise.float_copy_sign(100.0, -10.0)
-  |> should.equal(-100.0)
-
-  piecewise.float_copy_sign(-100.0, -10.0)
-  |> should.equal(-100.0)
-}
-
-pub fn int_sign_test() {
-  piecewise.int_sign(100)
-  |> should.equal(1)
-
-  piecewise.int_sign(0)
-  |> should.equal(0)
-
-  piecewise.int_sign(-100)
-  |> should.equal(-1)
-}
-
-pub fn int_flip_sign_test() {
-  piecewise.int_flip_sign(100)
-  |> should.equal(-100)
-
-  piecewise.int_flip_sign(0)
-  |> should.equal(-0)
-
-  piecewise.int_flip_sign(-100)
-  |> should.equal(100)
-}
-
-pub fn int_copy_sign_test() {
-  piecewise.int_copy_sign(100, 10)
-  |> should.equal(100)
-
-  piecewise.int_copy_sign(-100, 10)
-  |> should.equal(100)
-
-  piecewise.int_copy_sign(100, -10)
-  |> should.equal(-100)
-
-  piecewise.int_copy_sign(-100, -10)
-  |> should.equal(-100)
-}
-
-pub fn float_minimum_test() {
-  piecewise.minimum(0.75, 0.5, float.compare)
-  |> should.equal(0.5)
-
-  piecewise.minimum(0.5, 0.75, float.compare)
-  |> should.equal(0.5)
-
-  piecewise.minimum(-0.75, 0.5, float.compare)
-  |> should.equal(-0.75)
-
-  piecewise.minimum(-0.75, 0.5, float.compare)
-  |> should.equal(-0.75)
-}
-
-pub fn int_minimum_test() {
-  piecewise.minimum(75, 50, int.compare)
-  |> should.equal(50)
-
-  piecewise.minimum(50, 75, int.compare)
-  |> should.equal(50)
-
-  piecewise.minimum(-75, 50, int.compare)
-  |> should.equal(-75)
-
-  piecewise.minimum(-75, 50, int.compare)
-  |> should.equal(-75)
-}
-
-pub fn float_maximum_test() {
-  piecewise.maximum(0.75, 0.5, float.compare)
-  |> should.equal(0.75)
-
-  piecewise.maximum(0.5, 0.75, float.compare)
-  |> should.equal(0.75)
-
-  piecewise.maximum(-0.75, 0.5, float.compare)
-  |> should.equal(0.5)
-
-  piecewise.maximum(-0.75, 0.5, float.compare)
-  |> should.equal(0.5)
-}
-
-pub fn int_maximum_test() {
-  piecewise.maximum(75, 50, int.compare)
-  |> should.equal(75)
-
-  piecewise.maximum(50, 75, int.compare)
-  |> should.equal(75)
-
-  piecewise.maximum(-75, 50, int.compare)
-  |> should.equal(50)
-
-  piecewise.maximum(-75, 50, int.compare)
-  |> should.equal(50)
-}
-
-pub fn float_minmax_test() {
-  piecewise.minmax(0.75, 0.5, float.compare)
-  |> should.equal(#(0.5, 0.75))
-
-  piecewise.minmax(0.5, 0.75, float.compare)
-  |> should.equal(#(0.5, 0.75))
-
-  piecewise.minmax(-0.75, 0.5, float.compare)
-  |> should.equal(#(-0.75, 0.5))
-
-  piecewise.minmax(-0.75, 0.5, float.compare)
-  |> should.equal(#(-0.75, 0.5))
-}
-
-pub fn int_minmax_test() {
-  piecewise.minmax(75, 50, int.compare)
-  |> should.equal(#(50, 75))
-
-  piecewise.minmax(50, 75, int.compare)
-  |> should.equal(#(50, 75))
-
-  piecewise.minmax(-75, 50, int.compare)
-  |> should.equal(#(-75, 50))
-
-  piecewise.minmax(-75, 50, int.compare)
-  |> should.equal(#(-75, 50))
-}
-
-pub fn float_list_minimum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.list_minimum(float.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4.5, 4.5, 3.5, 2.5, 1.5]
-  |> piecewise.list_minimum(float.compare)
-  |> should.equal(Ok(1.5))
-}
-
-pub fn int_list_minimum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.list_minimum(int.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4, 4, 3, 2, 1]
-  |> piecewise.list_minimum(int.compare)
-  |> should.equal(Ok(1))
-}
-
-pub fn float_list_maximum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.list_maximum(float.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4.5, 4.5, 3.5, 2.5, 1.5]
-  |> piecewise.list_maximum(float.compare)
-  |> should.equal(Ok(4.5))
-}
-
-pub fn int_list_maximum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.list_maximum(int.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4, 4, 3, 2, 1]
-  |> piecewise.list_maximum(int.compare)
-  |> should.equal(Ok(4))
-}
-
-pub fn float_list_arg_maximum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.arg_maximum(float.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4.5, 4.5, 3.5, 2.5, 1.5]
-  |> piecewise.arg_maximum(float.compare)
-  |> should.equal(Ok([0, 1]))
-}
-
-pub fn int_list_arg_maximum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.arg_maximum(int.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4, 4, 3, 2, 1]
-  |> piecewise.arg_maximum(int.compare)
-  |> should.equal(Ok([0, 1]))
-}
-
-pub fn float_list_arg_minimum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.arg_minimum(float.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4.5, 4.5, 3.5, 2.5, 1.5]
-  |> piecewise.arg_minimum(float.compare)
-  |> should.equal(Ok([4]))
-}
-
-pub fn int_list_arg_minimum_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.arg_minimum(int.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4, 4, 3, 2, 1]
-  |> piecewise.arg_minimum(int.compare)
-  |> should.equal(Ok([4]))
-}
-
-pub fn float_list_extrema_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.extrema(float.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4.0, 4.0, 3.0, 2.0, 1.0]
-  |> piecewise.extrema(float.compare)
-  |> should.equal(Ok(#(1.0, 4.0)))
-
-  // Valid input returns a result
-  [1.0, 4.0, 2.0, 5.0, 0.0]
-  |> piecewise.extrema(float.compare)
-  |> should.equal(Ok(#(0.0, 5.0)))
-}
-
-pub fn int_list_extrema_test() {
-  // An empty lists returns an error
-  []
-  |> piecewise.extrema(int.compare)
-  |> should.be_error()
-
-  // Valid input returns a result
-  [4, 4, 3, 2, 1]
-  |> piecewise.extrema(int.compare)
-  |> should.equal(Ok(#(1, 4)))
-
-  // Valid input returns a result
-  [1, 4, 2, 5, 0]
-  |> piecewise.extrema(int.compare)
-  |> should.equal(Ok(#(0, 5)))
-}
diff --git a/test/gleam_community/maths/predicates_test.gleam b/test/gleam_community/maths/predicates_test.gleam
deleted file mode 100644
index e544eb5..0000000
--- a/test/gleam_community/maths/predicates_test.gleam
+++ /dev/null
@@ -1,203 +0,0 @@
-import gleam/list
-import gleam_community/maths/predicates
-import gleeunit/should
-
-pub fn float_is_close_test() {
-  let val = 99.0
-  let ref_val = 100.0
-  // We set 'atol' and 'rtol' such that the values are equivalent
-  // if 'val' is within 1 percent of 'ref_val' +/- 0.1
-  let rtol = 0.01
-  let atol = 0.1
-  predicates.is_close(val, ref_val, rtol, atol)
-  |> should.be_true()
-}
-
-pub fn float_list_all_close_test() {
-  let val = 99.0
-  let ref_val = 100.0
-  let xarr = list.repeat(val, 42)
-  let yarr = list.repeat(ref_val, 42)
-  // We set 'atol' and 'rtol' such that the values are equivalent
-  // if 'val' is within 1 percent of 'ref_val' +/- 0.1
-  let rtol = 0.01
-  let atol = 0.1
-  predicates.all_close(xarr, yarr, rtol, atol)
-  |> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) {
-    case zarr {
-      Ok(arr) ->
-        arr
-        |> list.all(fn(a) { a })
-        |> Ok
-      _ ->
-        Nil
-        |> Error
-    }
-  }
-  |> should.equal(Ok(True))
-}
-
-pub fn float_is_fractional_test() {
-  predicates.is_fractional(1.5)
-  |> should.equal(True)
-
-  predicates.is_fractional(0.5)
-  |> should.equal(True)
-
-  predicates.is_fractional(0.3333)
-  |> should.equal(True)
-
-  predicates.is_fractional(0.9999)
-  |> should.equal(True)
-
-  predicates.is_fractional(1.0)
-  |> should.equal(False)
-
-  predicates.is_fractional(999.0)
-  |> should.equal(False)
-}
-
-pub fn int_is_power_test() {
-  predicates.is_power(10, 10)
-  |> should.equal(True)
-
-  predicates.is_power(11, 10)
-  |> should.equal(False)
-
-  predicates.is_power(4, 2)
-  |> should.equal(True)
-
-  predicates.is_power(5, 2)
-  |> should.equal(False)
-
-  predicates.is_power(27, 3)
-  |> should.equal(True)
-
-  predicates.is_power(28, 3)
-  |> should.equal(False)
-}
-
-pub fn int_is_even_test() {
-  predicates.is_even(0)
-  |> should.equal(True)
-
-  predicates.is_even(2)
-  |> should.equal(True)
-
-  predicates.is_even(12)
-  |> should.equal(True)
-
-  predicates.is_even(5)
-  |> should.equal(False)
-
-  predicates.is_even(-3)
-  |> should.equal(False)
-
-  predicates.is_even(-4)
-  |> should.equal(True)
-}
-
-pub fn int_is_odd_test() {
-  predicates.is_odd(0)
-  |> should.equal(False)
-
-  predicates.is_odd(3)
-  |> should.equal(True)
-
-  predicates.is_odd(13)
-  |> should.equal(True)
-
-  predicates.is_odd(4)
-  |> should.equal(False)
-
-  predicates.is_odd(-3)
-  |> should.equal(True)
-
-  predicates.is_odd(-4)
-  |> should.equal(False)
-}
-
-pub fn int_is_perfect_test() {
-  predicates.is_perfect(6)
-  |> should.equal(True)
-
-  predicates.is_perfect(28)
-  |> should.equal(True)
-
-  predicates.is_perfect(496)
-  |> should.equal(True)
-
-  predicates.is_perfect(1)
-  |> should.equal(False)
-
-  predicates.is_perfect(3)
-  |> should.equal(False)
-
-  predicates.is_perfect(13)
-  |> should.equal(False)
-}
-
-pub fn int_is_prime_test() {
-  // Test a negative integer, i.e., not a natural number
-  predicates.is_prime(-7)
-  |> should.equal(False)
-
-  predicates.is_prime(1)
-  |> should.equal(False)
-
-  predicates.is_prime(2)
-  |> should.equal(True)
-
-  predicates.is_prime(3)
-  |> should.equal(True)
-
-  predicates.is_prime(5)
-  |> should.equal(True)
-
-  predicates.is_prime(7)
-  |> should.equal(True)
-
-  predicates.is_prime(11)
-  |> should.equal(True)
-
-  predicates.is_prime(42)
-  |> should.equal(False)
-
-  predicates.is_prime(7919)
-  |> should.equal(True)
-
-  // Test 1st Carmichael number
-  predicates.is_prime(561)
-  |> should.equal(False)
-
-  // Test 2nd Carmichael number
-  predicates.is_prime(1105)
-  |> should.equal(False)
-}
-
-pub fn is_between_test() {
-  predicates.is_between(5.5, 5.0, 6.0)
-  |> should.equal(True)
-
-  predicates.is_between(5.0, 5.0, 6.0)
-  |> should.equal(False)
-
-  predicates.is_between(6.0, 5.0, 6.0)
-  |> should.equal(False)
-}
-
-pub fn is_divisible_test() {
-  predicates.is_divisible(10, 2)
-  |> should.equal(True)
-
-  predicates.is_divisible(7, 3)
-  |> should.equal(False)
-}
-
-pub fn is_multiple_test() {
-  predicates.is_multiple(15, 5)
-  |> should.equal(True)
-
-  predicates.is_multiple(14, 5)
-  |> should.equal(False)
-}
diff --git a/test/gleam_community/maths/sequences_test.gleam b/test/gleam_community/maths/sequences_test.gleam
deleted file mode 100644
index 1571d4b..0000000
--- a/test/gleam_community/maths/sequences_test.gleam
+++ /dev/null
@@ -1,328 +0,0 @@
-import gleam/iterator
-import gleam/list
-import gleam_community/maths/elementary
-import gleam_community/maths/predicates
-import gleam_community/maths/sequences
-import gleeunit/should
-
-pub fn float_list_linear_space_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  // ---> With endpoint included
-  let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, 20.0, 30.0, 40.0, 50.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, 12.5, 15.0, 17.5, 20.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // Try with negative stop
-  // ----> Without endpoint included
-  let assert Ok(linspace) = sequences.linear_space(10.0, 50.0, 5, False)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, 18.0, 26.0, 34.0, 42.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  let assert Ok(linspace) = sequences.linear_space(10.0, 20.0, 5, False)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, 12.0, 14.0, 16.0, 18.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // Try with negative stop
-  let assert Ok(linspace) = sequences.linear_space(10.0, -50.0, 5, False)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, -2.0, -14.0, -26.0, -38.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  let assert Ok(linspace) = sequences.linear_space(10.0, -20.0, 5, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [10.0, 2.5, -5.0, -12.5, -20.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // Try with negative start
-  let assert Ok(linspace) = sequences.linear_space(-10.0, 50.0, 5, False)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [-10.0, 2.0, 14.0, 26.0, 38.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  let assert Ok(linspace) = sequences.linear_space(-10.0, 20.0, 5, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      linspace |> iterator.to_list(),
-      [-10.0, -2.5, 5.0, 12.5, 20.0],
-      0.0,
-      tol,
-    )
-
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // A negative number of points does not work (-5)
-  sequences.linear_space(10.0, 50.0, -5, True)
-  |> should.be_error()
-}
-
-pub fn float_list_logarithmic_space_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  // ---> With endpoint included
-  // - Positive start, stop, base
-  let assert Ok(logspace) = sequences.logarithmic_space(1.0, 3.0, 3, True, 10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 100.0, 1000.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive start, stop, negative base
-  let assert Ok(logspace) =
-    sequences.logarithmic_space(1.0, 3.0, 3, True, -10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [-10.0, 100.0, -1000.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive start, negative stop, base 
-  let assert Ok(logspace) =
-    sequences.logarithmic_space(1.0, -3.0, 3, True, -10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [-10.0, -0.1, -0.001],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive start, base, negative stop  
-  let assert Ok(logspace) =
-    sequences.logarithmic_space(1.0, -3.0, 3, True, 10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 0.1, 0.001],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive stop, base, negative start
-  let assert Ok(logspace) =
-    sequences.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [0.1, 10.0, 1000.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // ----> Without endpoint included
-  // - Positive start, stop, base
-  let assert Ok(logspace) =
-    sequences.logarithmic_space(1.0, 3.0, 3, False, 10.0)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 46.41588834, 215.443469],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // A negative number of points does not work (-3)
-  sequences.logarithmic_space(1.0, 3.0, -3, True, 10.0)
-  |> should.be_error()
-}
-
-pub fn float_list_geometric_space_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-  // Check that the function agrees, at some arbitrary input
-  // points, with known function values
-  // ---> With endpoint included
-  // - Positive start, stop
-  let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 100.0, 1000.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive start, negative stop  
-  let assert Ok(logspace) = sequences.geometric_space(10.0, 0.001, 3, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 0.1, 0.001],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // - Positive stop, negative start
-  let assert Ok(logspace) = sequences.geometric_space(0.1, 1000.0, 3, True)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [0.1, 10.0, 1000.0],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // ----> Without endpoint included
-  // - Positive start, stop
-  let assert Ok(logspace) = sequences.geometric_space(10.0, 1000.0, 3, False)
-  let assert Ok(result) =
-    predicates.all_close(
-      logspace |> iterator.to_list(),
-      [10.0, 46.41588834, 215.443469],
-      0.0,
-      tol,
-    )
-  result
-  |> list.all(fn(x) { x == True })
-  |> should.be_true()
-
-  // Test invalid input (start and stop can't be equal to 0.0)
-  sequences.geometric_space(0.0, 1000.0, 3, False)
-  |> should.be_error()
-
-  sequences.geometric_space(-1000.0, 0.0, 3, False)
-  |> should.be_error()
-
-  // A negative number of points does not work
-  sequences.geometric_space(-1000.0, 0.0, -3, False)
-  |> should.be_error()
-}
-
-pub fn float_list_arange_test() {
-  // Positive start, stop, step
-  sequences.arange(1.0, 5.0, 1.0)
-  |> iterator.to_list()
-  |> should.equal([1.0, 2.0, 3.0, 4.0])
-
-  sequences.arange(1.0, 5.0, 0.5)
-  |> iterator.to_list()
-  |> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
-
-  sequences.arange(1.0, 2.0, 0.25)
-  |> iterator.to_list()
-  |> should.equal([1.0, 1.25, 1.5, 1.75])
-
-  // Reverse (switch start/stop largest/smallest value)
-  sequences.arange(5.0, 1.0, 1.0)
-  |> iterator.to_list()
-  |> should.equal([])
-
-  // Reverse negative step
-  sequences.arange(5.0, 1.0, -1.0)
-  |> iterator.to_list()
-  |> should.equal([5.0, 4.0, 3.0, 2.0])
-
-  // Positive start, negative stop, step
-  sequences.arange(5.0, -1.0, -1.0)
-  |> iterator.to_list()
-  |> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
-
-  // Negative start, stop, step
-  sequences.arange(-5.0, -1.0, -1.0)
-  |> iterator.to_list()
-  |> should.equal([])
-
-  // Negative start, stop, positive step
-  sequences.arange(-5.0, -1.0, 1.0)
-  |> iterator.to_list()
-  |> should.equal([-5.0, -4.0, -3.0, -2.0])
-}
diff --git a/test/gleam_community/maths/special_test.gleam b/test/gleam_community/maths/special_test.gleam
deleted file mode 100644
index 158c8f0..0000000
--- a/test/gleam_community/maths/special_test.gleam
+++ /dev/null
@@ -1,114 +0,0 @@
-import gleam/result
-import gleam_community/maths/elementary
-import gleam_community/maths/predicates
-import gleam_community/maths/special
-import gleeunit/should
-
-pub fn float_beta_function_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Valid input returns a result
-  special.beta(-0.5, 0.5)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  special.beta(0.5, 0.5)
-  |> predicates.is_close(3.1415926535897927, 0.0, tol)
-  |> should.be_true()
-
-  special.beta(0.5, -0.5)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  special.beta(5.0, 5.0)
-  |> predicates.is_close(0.0015873015873015873, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_error_function_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Valid input returns a result
-  special.erf(-0.5)
-  |> predicates.is_close(-0.5204998778130465, 0.0, tol)
-  |> should.be_true()
-
-  special.erf(0.5)
-  |> predicates.is_close(0.5204998778130465, 0.0, tol)
-  |> should.be_true()
-
-  special.erf(1.0)
-  |> predicates.is_close(0.8427007929497148, 0.0, tol)
-  |> should.be_true()
-
-  special.erf(2.0)
-  |> predicates.is_close(0.9953222650189527, 0.0, tol)
-  |> should.be_true()
-
-  special.erf(10.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_gamma_function_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Valid input returns a result
-  special.gamma(-0.5)
-  |> predicates.is_close(-3.5449077018110318, 0.0, tol)
-  |> should.be_true()
-
-  special.gamma(0.5)
-  |> predicates.is_close(1.7724538509055159, 0.0, tol)
-  |> should.be_true()
-
-  special.gamma(1.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  special.gamma(2.0)
-  |> predicates.is_close(1.0, 0.0, tol)
-  |> should.be_true()
-
-  special.gamma(3.0)
-  |> predicates.is_close(2.0, 0.0, tol)
-  |> should.be_true()
-
-  special.gamma(10.0)
-  |> predicates.is_close(362_880.0, 0.0, tol)
-  |> should.be_true()
-}
-
-pub fn float_incomplete_gamma_function_test() {
-  let assert Ok(tol) = elementary.power(10.0, -6.0)
-
-  // Invalid input gives an error
-  // 1st arg is invalid
-  special.incomplete_gamma(-1.0, 1.0)
-  |> should.be_error()
-
-  // 2nd arg is invalid
-  special.incomplete_gamma(1.0, -1.0)
-  |> should.be_error()
-
-  // Valid input returns a result
-  special.incomplete_gamma(1.0, 0.0)
-  |> result.unwrap(-999.0)
-  |> predicates.is_close(0.0, 0.0, tol)
-  |> should.be_true()
-
-  special.incomplete_gamma(1.0, 2.0)
-  |> result.unwrap(-999.0)
-  |> predicates.is_close(0.864664716763387308106, 0.0, tol)
-  |> should.be_true()
-
-  special.incomplete_gamma(2.0, 3.0)
-  |> result.unwrap(-999.0)
-  |> predicates.is_close(0.8008517265285442280826, 0.0, tol)
-  |> should.be_true()
-
-  special.incomplete_gamma(3.0, 4.0)
-  |> result.unwrap(-999.0)
-  |> predicates.is_close(1.523793388892911312363, 0.0, tol)
-  |> should.be_true()
-}
diff --git a/test/gleam_community/metrics_test.gleam b/test/gleam_community/metrics_test.gleam
new file mode 100644
index 0000000..f799692
--- /dev/null
+++ b/test/gleam_community/metrics_test.gleam
@@ -0,0 +1,549 @@
+import gleam/float
+import gleam/set
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_list_norm_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // An empty lists returns 0.0
+  []
+  |> maths.norm(1.0)
+  |> should.equal(Ok(0.0))
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) =
+    [1.0, 1.0, 1.0]
+    |> maths.norm(1.0)
+  result
+  |> maths.is_close(3.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [1.0, 1.0, 1.0]
+    |> maths.norm(-1.0)
+  result
+  |> maths.is_close(0.3333333333333333, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [-1.0, -1.0, -1.0]
+    |> maths.norm(-1.0)
+  result
+  |> maths.is_close(0.3333333333333333, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [-1.0, -1.0, -1.0]
+    |> maths.norm(1.0)
+  result
+  |> maths.is_close(3.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [-1.0, -2.0, -3.0]
+    |> maths.norm(-10.0)
+  result
+  |> maths.is_close(0.9999007044905545, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [-1.0, -2.0, -3.0]
+    |> maths.norm(-100.0)
+  result
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [-1.0, -2.0, -3.0]
+    |> maths.norm(2.0)
+  result
+  |> maths.is_close(3.7416573867739413, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_list_norm_with_weights_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // An empty lists returns 0.0
+  []
+  |> maths.norm_with_weights(1.0)
+  |> should.equal(Ok(0.0))
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) =
+    [#(1.0, 1.0), #(1.0, 1.0), #(1.0, 1.0)]
+    |> maths.norm_with_weights(1.0)
+  result
+  |> maths.is_close(3.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [#(1.0, 0.5), #(1.0, 0.5), #(1.0, 0.5)]
+    |> maths.norm_with_weights(1.0)
+  result
+  |> maths.is_close(1.5, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    [#(1.0, 0.5), #(2.0, 0.5), #(3.0, 0.5)]
+    |> maths.norm_with_weights(2.0)
+  result
+  |> maths.is_close(2.6457513110645907, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_list_manhattan_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Try with valid input (same as Minkowski distance with p = 1)
+  let assert Ok(result) = maths.manhattan_distance([#(0.0, 1.0), #(0.0, 2.0)])
+  result
+  |> maths.is_close(3.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.manhattan_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
+  |> should.equal(Ok(9.0))
+
+  maths.manhattan_distance([#(1.0, 2.0), #(2.0, 3.0)])
+  |> should.equal(Ok(2.0))
+
+  maths.manhattan_distance_with_weights([#(1.0, 2.0, 0.5), #(2.0, 3.0, 1.0)])
+  |> should.equal(Ok(1.5))
+
+  maths.manhattan_distance_with_weights([
+    #(1.0, 4.0, 1.0),
+    #(2.0, 5.0, 1.0),
+    #(3.0, 6.0, 1.0),
+  ])
+  |> should.equal(Ok(9.0))
+
+  maths.manhattan_distance_with_weights([
+    #(1.0, 4.0, 1.0),
+    #(2.0, 5.0, 2.0),
+    #(3.0, 6.0, 3.0),
+  ])
+  |> should.equal(Ok(18.0))
+
+  maths.manhattan_distance_with_weights([
+    #(1.0, 4.0, -7.0),
+    #(2.0, 5.0, -8.0),
+    #(3.0, 6.0, -9.0),
+  ])
+  |> should.be_error()
+}
+
+pub fn float_list_minkowski_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Test order < 1
+  maths.minkowski_distance([#(0.0, 0.0), #(0.0, 0.0)], -1.0)
+  |> should.be_error()
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(result) =
+    maths.minkowski_distance([#(1.0, 1.0), #(1.0, 1.0)], 1.0)
+  result
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.minkowski_distance([#(0.0, 1.0), #(0.0, 1.0)], 10.0)
+  result
+  |> maths.is_close(1.0717734625362931, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.minkowski_distance([#(0.0, 1.0), #(0.0, 1.0)], 100.0)
+  result
+  |> maths.is_close(1.0069555500567189, 0.0, tol)
+  |> should.be_true()
+
+  // Euclidean distance (p = 2)
+  let assert Ok(result) =
+    maths.minkowski_distance([#(0.0, 1.0), #(0.0, 2.0)], 2.0)
+  result
+  |> maths.is_close(2.23606797749979, 0.0, tol)
+  |> should.be_true()
+
+  // Manhattan distance (p = 1)
+  let assert Ok(result) =
+    maths.minkowski_distance([#(0.0, 1.0), #(0.0, 2.0)], 1.0)
+  result
+  |> maths.is_close(3.0, 0.0, tol)
+  |> should.be_true()
+
+  // Try different valid input
+  let assert Ok(result) =
+    maths.minkowski_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)], 4.0)
+  result
+  |> maths.is_close(3.9482220388574776, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.minkowski_distance_with_weights(
+      [#(1.0, 4.0, 1.0), #(2.0, 5.0, 1.0), #(3.0, 6.0, 1.0)],
+      4.0,
+    )
+  result
+  |> maths.is_close(3.9482220388574776, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.minkowski_distance_with_weights(
+      [#(1.0, 4.0, 1.0), #(2.0, 5.0, 2.0), #(3.0, 6.0, 3.0)],
+      4.0,
+    )
+  result
+  |> maths.is_close(4.6952537402198615, 0.0, tol)
+  |> should.be_true()
+
+  // Try invalid input with weights that are negative
+  maths.minkowski_distance_with_weights(
+    [#(1.0, 4.0, -7.0), #(2.0, 5.0, -8.0), #(3.0, 6.0, -9.0)],
+    2.0,
+  )
+  |> should.be_error()
+}
+
+pub fn float_list_euclidean_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Empty lists returns an error
+  maths.euclidean_distance([])
+  |> should.be_error()
+
+  // Try with valid input (same as Minkowski distance with p = 2)
+  let assert Ok(result) = maths.euclidean_distance([#(0.0, 1.0), #(0.0, 2.0)])
+  result
+  |> maths.is_close(2.23606797749979, 0.0, tol)
+  |> should.be_true()
+
+  // Try different valid input
+  let assert Ok(result) =
+    maths.euclidean_distance([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
+  result
+  |> maths.is_close(5.196152422706632, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.euclidean_distance_with_weights([
+      #(1.0, 4.0, 1.0),
+      #(2.0, 5.0, 1.0),
+      #(3.0, 6.0, 1.0),
+    ])
+  result
+  |> maths.is_close(5.196152422706632, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.euclidean_distance_with_weights([
+      #(1.0, 4.0, 1.0),
+      #(2.0, 5.0, 2.0),
+      #(3.0, 6.0, 3.0),
+    ])
+  result
+  |> maths.is_close(7.3484692283495345, 0.0, tol)
+  |> should.be_true()
+
+  // Try invalid input with weights that are negative
+  maths.euclidean_distance_with_weights([
+    #(1.0, 4.0, -7.0),
+    #(2.0, 5.0, -8.0),
+    #(3.0, 6.0, -9.0),
+  ])
+  |> should.be_error()
+}
+
+pub fn mean_test() {
+  // An empty list returns an error
+  []
+  |> maths.mean()
+  |> should.be_error()
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.mean()
+  |> should.equal(Ok(2.0))
+}
+
+pub fn median_test() {
+  // An empty list returns an error
+  []
+  |> maths.median()
+  |> should.be_error()
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.median()
+  |> should.equal(Ok(2.0))
+
+  [1.0, 2.0, 3.0, 4.0]
+  |> maths.median()
+  |> should.equal(Ok(2.5))
+}
+
+pub fn variance_test() {
+  // Degrees of freedom
+  let ddof = 1
+
+  // An empty list returns an error
+  []
+  |> maths.variance(ddof)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.variance(ddof)
+  |> should.equal(Ok(1.0))
+}
+
+pub fn standard_deviation_test() {
+  // Degrees of freedom
+  let ddof = 1
+
+  // An empty list returns an error
+  []
+  |> maths.standard_deviation(ddof)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [1.0, 2.0, 3.0]
+  |> maths.standard_deviation(ddof)
+  |> should.equal(Ok(1.0))
+}
+
+pub fn jaccard_index_test() {
+  maths.jaccard_index(set.from_list([]), set.from_list([]))
+  |> should.equal(0.0)
+
+  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
+  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
+  maths.jaccard_index(set_a, set_b)
+  |> should.equal(4.0 /. 10.0)
+
+  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
+  let set_d = set.from_list([6, 7, 8, 9, 10])
+  maths.jaccard_index(set_c, set_d)
+  |> should.equal(0.0 /. 11.0)
+
+  let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
+  let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
+  maths.jaccard_index(set_e, set_f)
+  |> should.equal(1.0 /. 7.0)
+}
+
+pub fn sorensen_dice_coefficient_test() {
+  maths.sorensen_dice_coefficient(set.from_list([]), set.from_list([]))
+  |> should.equal(0.0)
+
+  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
+  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
+  maths.sorensen_dice_coefficient(set_a, set_b)
+  |> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 })
+
+  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
+  let set_d = set.from_list([6, 7, 8, 9, 10])
+  maths.sorensen_dice_coefficient(set_c, set_d)
+  |> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 })
+
+  let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
+  let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
+  maths.sorensen_dice_coefficient(set_e, set_f)
+  |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
+}
+
+pub fn overlap_coefficient_test() {
+  maths.overlap_coefficient(set.from_list([]), set.from_list([]))
+  |> should.equal(0.0)
+
+  let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
+  let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
+  maths.overlap_coefficient(set_a, set_b)
+  |> should.equal(4.0 /. 7.0)
+
+  let set_c = set.from_list([0, 1, 2, 3, 4, 5])
+  let set_d = set.from_list([6, 7, 8, 9, 10])
+  maths.overlap_coefficient(set_c, set_d)
+  |> should.equal(0.0 /. 5.0)
+
+  let set_e = set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
+  let set_f = set.from_list(["monkey", "bird", "ostrich", "salmon"])
+  maths.overlap_coefficient(set_e, set_f)
+  |> should.equal(2.0 /. 4.0)
+}
+
+pub fn cosine_similarity_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Two orthogonal vectors (represented by lists)
+  maths.cosine_similarity([#(-1.0, 1.0), #(1.0, 1.0), #(0.0, -1.0)])
+  |> should.equal(Ok(0.0))
+
+  // Two identical (parallel) vectors (represented by lists)
+  maths.cosine_similarity([#(1.0, 1.0), #(2.0, 2.0), #(3.0, 3.0)])
+  |> should.equal(Ok(1.0))
+
+  // Two parallel, but oppositely oriented vectors (represented by lists)
+  maths.cosine_similarity([#(-1.0, 1.0), #(-2.0, 2.0), #(-3.0, 3.0)])
+  |> should.equal(Ok(-1.0))
+
+  // Try with arbitrary valid input
+  let assert Ok(result) =
+    maths.cosine_similarity([#(1.0, 4.0), #(2.0, 5.0), #(3.0, 6.0)])
+  result
+  |> maths.is_close(0.9746318461970762, 0.0, tol)
+  |> should.be_true()
+
+  // Try valid input with weights
+  let assert Ok(result) =
+    maths.cosine_similarity_with_weights([
+      #(1.0, 4.0, 1.0),
+      #(2.0, 5.0, 1.0),
+      #(3.0, 6.0, 1.0),
+    ])
+  result
+  |> maths.is_close(0.9746318461970762, 0.0, tol)
+  |> should.be_true()
+
+  // Try with different weights
+  let assert Ok(result) =
+    maths.cosine_similarity_with_weights([
+      #(1.0, 4.0, 1.0),
+      #(2.0, 5.0, 2.0),
+      #(3.0, 6.0, 3.0),
+    ])
+  result
+  |> maths.is_close(0.9855274566525745, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.cosine_similarity_with_weights([
+      #(1.0, 1.0, 2.0),
+      #(2.0, 2.0, 3.0),
+      #(3.0, 3.0, 4.0),
+    ])
+  result
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  let assert Ok(result) =
+    maths.cosine_similarity_with_weights([
+      #(-1.0, 1.0, 1.0),
+      #(-2.0, 2.0, 0.5),
+      #(-3.0, 3.0, 0.33),
+    ])
+  result
+  |> maths.is_close(-1.0, 0.0, tol)
+  |> should.be_true()
+
+  // Try invalid input with weights that are negative
+  maths.cosine_similarity_with_weights([
+    #(1.0, 4.0, -7.0),
+    #(2.0, 5.0, -8.0),
+    #(3.0, 6.0, -9.0),
+  ])
+  |> should.be_error()
+}
+
+pub fn chebyshev_distance_test() {
+  // Empty lists returns an error
+  maths.chebyshev_distance([])
+  |> should.be_error()
+
+  // Try different types of valid input
+  maths.chebyshev_distance([#(1.0, 0.0), #(0.0, 2.0)])
+  |> should.equal(Ok(2.0))
+
+  maths.chebyshev_distance([#(1.0, 2.0), #(0.0, 0.0)])
+  |> should.equal(Ok(1.0))
+
+  maths.chebyshev_distance([#(1.0, -2.0), #(0.0, 0.0)])
+  |> should.equal(Ok(3.0))
+
+  maths.chebyshev_distance([#(-5.0, -1.0), #(-10.0, -12.0), #(-3.0, -3.0)])
+  |> should.equal(Ok(4.0))
+
+  maths.chebyshev_distance([#(1.0, 1.0), #(2.0, 2.0), #(3.0, 3.0)])
+  |> should.equal(Ok(0.0))
+
+  maths.chebyshev_distance_with_weights([
+    #(-5.0, -1.0, 0.5),
+    #(-10.0, -12.0, 1.0),
+    #(-3.0, -3.0, 1.0),
+  ])
+  |> should.equal(Ok(2.0))
+}
+
+pub fn canberra_distance_test() {
+  // Empty lists returns an error
+  maths.canberra_distance([])
+  |> should.be_error()
+
+  // Try different types of valid input
+  maths.canberra_distance([#(0.0, 0.0), #(0.0, 0.0)])
+  |> should.equal(Ok(0.0))
+
+  maths.canberra_distance([#(1.0, -2.0), #(2.0, -1.0)])
+  |> should.equal(Ok(2.0))
+
+  maths.canberra_distance([#(1.0, 0.0), #(0.0, 2.0)])
+  |> should.equal(Ok(2.0))
+
+  maths.canberra_distance([#(1.0, 2.0), #(0.0, 0.0)])
+  |> should.equal(Ok(1.0 /. 3.0))
+
+  maths.canberra_distance_with_weights([#(1.0, 0.0, 1.0), #(0.0, 2.0, 1.0)])
+  |> should.equal(Ok(2.0))
+
+  maths.canberra_distance_with_weights([#(1.0, 0.0, 1.0), #(0.0, 2.0, 0.5)])
+  |> should.equal(Ok(1.5))
+
+  maths.canberra_distance_with_weights([#(1.0, 0.0, 0.5), #(0.0, 2.0, 0.5)])
+  |> should.equal(Ok(1.0))
+
+  // Try invalid input with weights that are negative
+  maths.canberra_distance_with_weights([
+    #(1.0, 4.0, -7.0),
+    #(2.0, 5.0, -8.0),
+    #(3.0, 6.0, -9.0),
+  ])
+  |> should.be_error()
+}
+
+pub fn braycurtis_distance_test() {
+  // Empty lists returns an error
+  maths.braycurtis_distance([])
+  |> should.be_error()
+
+  // Try different types of valid input
+  maths.braycurtis_distance([#(0.0, 0.0), #(0.0, 0.0)])
+  |> should.equal(Ok(0.0))
+  maths.braycurtis_distance([#(1.0, -2.0), #(2.0, -1.0)])
+  |> should.equal(Ok(3.0))
+
+  maths.braycurtis_distance([#(1.0, 0.0), #(0.0, 2.0)])
+  |> should.equal(Ok(1.0))
+
+  maths.braycurtis_distance([#(1.0, 3.0), #(2.0, 4.0)])
+  |> should.equal(Ok(0.4))
+
+  maths.braycurtis_distance_with_weights([#(1.0, 3.0, 1.0), #(2.0, 4.0, 1.0)])
+  |> should.equal(Ok(0.4))
+
+  maths.braycurtis_distance_with_weights([#(1.0, 3.0, 0.5), #(2.0, 4.0, 1.0)])
+  |> should.equal(Ok(0.375))
+
+  maths.braycurtis_distance_with_weights([#(1.0, 3.0, 0.25), #(2.0, 4.0, 0.25)])
+  |> should.equal(Ok(0.4))
+  // Try invalid input with weights that are negative
+  maths.braycurtis_distance_with_weights([
+    #(1.0, 4.0, -7.0),
+    #(2.0, 5.0, -8.0),
+    #(3.0, 6.0, -9.0),
+  ])
+  |> should.be_error()
+}
diff --git a/test/gleam_community/piecewise_test.gleam b/test/gleam_community/piecewise_test.gleam
new file mode 100644
index 0000000..f74a84f
--- /dev/null
+++ b/test/gleam_community/piecewise_test.gleam
@@ -0,0 +1,656 @@
+import gleam/float
+import gleam/int
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_ceiling_test() {
+  // Round 3. digit AFTER decimal point 
+  maths.round_up(12.0654, 3)
+  |> should.equal(12.066)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_up(12.0654, 2)
+  |> should.equal(12.07)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_up(12.0654, 1)
+  |> should.equal(12.1)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_up(12.0654, 0)
+  |> should.equal(13.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_up(12.0654, -1)
+  |> should.equal(20.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_up(12.0654, -2)
+  |> should.equal(100.0)
+
+  // Round 3. digit BEFORE decimal point 
+  maths.round_up(12.0654, -3)
+  |> should.equal(1000.0)
+}
+
+pub fn float_floor_test() {
+  // Round 3. digit AFTER decimal point 
+  maths.round_down(12.0654, 3)
+  |> should.equal(12.065)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_down(12.0654, 2)
+  |> should.equal(12.06)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_down(12.0654, 1)
+  |> should.equal(12.0)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_down(12.0654, 0)
+  |> should.equal(12.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_down(12.0654, -1)
+  |> should.equal(10.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_down(12.0654, -2)
+  |> should.equal(0.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_down(12.0654, -3)
+  |> should.equal(0.0)
+}
+
+pub fn float_truncate_test() {
+  // Round 3. digit AFTER decimal point 
+  maths.round_to_zero(12.0654, 3)
+  |> should.equal(12.065)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_to_zero(12.0654, 2)
+  |> should.equal(12.06)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_to_zero(12.0654, 1)
+  |> should.equal(12.0)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_to_zero(12.0654, 0)
+  |> should.equal(12.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_to_zero(12.0654, -1)
+  |> should.equal(10.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_to_zero(12.0654, -2)
+  |> should.equal(0.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_to_zero(12.0654, -3)
+  |> should.equal(0.0)
+}
+
+pub fn math_round_to_nearest_test() {
+  // Try with positive values
+  maths.round_to_nearest(1.5, 0)
+  |> should.equal(2.0)
+
+  maths.round_to_nearest(1.75, 0)
+  |> should.equal(2.0)
+
+  maths.round_to_nearest(2.0, 0)
+  |> should.equal(2.0)
+
+  maths.round_to_nearest(3.5, 0)
+  |> should.equal(4.0)
+
+  maths.round_to_nearest(4.5, 0)
+  |> should.equal(4.0)
+
+  // Try with negative values
+  maths.round_to_nearest(-3.5, 0)
+  |> should.equal(-4.0)
+
+  maths.round_to_nearest(-4.5, 0)
+  |> should.equal(-4.0)
+
+  // // Round 3. digit AFTER decimal point 
+  maths.round_to_nearest(12.0654, 3)
+  |> should.equal(12.065)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_to_nearest(12.0654, 2)
+  |> should.equal(12.07)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_to_nearest(12.0654, 1)
+  |> should.equal(12.1)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_to_nearest(12.0654, 0)
+  |> should.equal(12.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_to_nearest(12.0654, -1)
+  |> should.equal(10.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_to_nearest(12.0654, -2)
+  |> should.equal(0.0)
+
+  // Round 3. digit BEFORE decimal point 
+  maths.round_to_nearest(12.0654, -3)
+  |> should.equal(0.0)
+}
+
+pub fn math_round_up_test() {
+  // Try with positive values
+  maths.round_up(0.45, 0)
+  |> should.equal(1.0)
+
+  maths.round_up(0.5, 0)
+  |> should.equal(1.0)
+
+  maths.round_up(0.45, 1)
+  |> should.equal(0.5)
+
+  maths.round_up(0.5, 1)
+  |> should.equal(0.5)
+
+  maths.round_up(0.455, 2)
+  |> should.equal(0.46)
+
+  maths.round_up(0.505, 2)
+  |> should.equal(0.51)
+
+  // Try with negative values
+  maths.round_up(-0.45, 0)
+  |> should.equal(-0.0)
+
+  maths.round_up(-0.5, 0)
+  |> should.equal(-0.0)
+
+  maths.round_up(-0.45, 1)
+  |> should.equal(-0.4)
+
+  maths.round_up(-0.5, 1)
+  |> should.equal(-0.5)
+
+  maths.round_up(-0.455, 2)
+  |> should.equal(-0.45)
+
+  maths.round_up(-0.505, 2)
+  |> should.equal(-0.5)
+}
+
+pub fn math_round_down_test() {
+  // Try with positive values
+  maths.round_down(0.45, 0)
+  |> should.equal(0.0)
+
+  maths.round_down(0.5, 0)
+  |> should.equal(0.0)
+
+  maths.round_down(0.45, 1)
+  |> should.equal(0.4)
+
+  maths.round_down(0.5, 1)
+  |> should.equal(0.5)
+
+  maths.round_down(0.455, 2)
+  |> should.equal(0.45)
+
+  maths.round_down(0.505, 2)
+  |> should.equal(0.5)
+
+  // Try with negative values
+  maths.round_down(-0.45, 0)
+  |> should.equal(-1.0)
+
+  maths.round_down(-0.5, 0)
+  |> should.equal(-1.0)
+
+  maths.round_down(-0.45, 1)
+  |> should.equal(-0.5)
+
+  maths.round_down(-0.5, 1)
+  |> should.equal(-0.5)
+
+  maths.round_down(-0.455, 2)
+  |> should.equal(-0.46)
+
+  maths.round_down(-0.505, 2)
+  |> should.equal(-0.51)
+}
+
+pub fn math_round_to_zero_test() {
+  // Note: Rounding mode "RoundToZero" is an alias for the truncate function
+  // Try with positive values
+  maths.round_to_zero(0.5, 0)
+  |> should.equal(0.0)
+
+  maths.round_to_zero(0.75, 0)
+  |> should.equal(0.0)
+
+  maths.round_to_zero(0.45, 1)
+  |> should.equal(0.4)
+
+  maths.round_to_zero(0.57, 1)
+  |> should.equal(0.5)
+
+  maths.round_to_zero(0.4575, 2)
+  |> should.equal(0.45)
+
+  maths.round_to_zero(0.5075, 2)
+  |> should.equal(0.5)
+
+  // Try with negative values
+  maths.round_to_zero(-0.5, 0)
+  |> should.equal(0.0)
+
+  maths.round_to_zero(-0.75, 0)
+  |> should.equal(0.0)
+
+  maths.round_to_zero(-0.45, 1)
+  |> should.equal(-0.4)
+
+  maths.round_to_zero(-0.57, 1)
+  |> should.equal(-0.5)
+
+  maths.round_to_zero(-0.4575, 2)
+  |> should.equal(-0.45)
+
+  maths.round_to_zero(-0.5075, 2)
+  |> should.equal(-0.5)
+}
+
+pub fn math_round_ties_away_test() {
+  // Try with positive values
+  maths.round_ties_away(1.4, 0)
+  |> should.equal(1.0)
+
+  maths.round_ties_away(1.5, 0)
+  |> should.equal(2.0)
+
+  maths.round_ties_away(2.5, 0)
+  |> should.equal(3.0)
+
+  // Try with negative values
+  maths.round_ties_away(-1.4, 0)
+  |> should.equal(-1.0)
+
+  maths.round_ties_away(-1.5, 0)
+  |> should.equal(-2.0)
+
+  maths.round_ties_away(-2.0, 0)
+  |> should.equal(-2.0)
+
+  maths.round_ties_away(-2.5, 0)
+  |> should.equal(-3.0)
+
+  // Round 3. digit AFTER decimal point 
+  maths.round_ties_away(12.0654, 3)
+  |> should.equal(12.065)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_ties_away(12.0654, 2)
+  |> should.equal(12.07)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_ties_away(12.0654, 1)
+  |> should.equal(12.1)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_ties_away(12.0654, 0)
+  |> should.equal(12.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_ties_away(12.0654, -1)
+  |> should.equal(10.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_ties_away(12.0654, -2)
+  |> should.equal(0.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_ties_away(12.0654, -3)
+  |> should.equal(0.0)
+}
+
+pub fn math_round_ties_up_test() {
+  // Try with positive values
+  maths.round_ties_up(1.4, 0)
+  |> should.equal(1.0)
+
+  maths.round_ties_up(1.5, 0)
+  |> should.equal(2.0)
+
+  maths.round_ties_up(2.5, 0)
+  |> should.equal(3.0)
+
+  // Try with negative values
+  maths.round_ties_up(-1.4, 0)
+  |> should.equal(-1.0)
+
+  maths.round_ties_up(-1.5, 0)
+  |> should.equal(-1.0)
+
+  maths.round_ties_up(-2.0, 0)
+  |> should.equal(-2.0)
+
+  maths.round_ties_up(-2.5, 0)
+  |> should.equal(-2.0)
+
+  // Round 3. digit AFTER decimal point 
+  maths.round_ties_up(12.0654, 3)
+  |> should.equal(12.065)
+
+  // Round 2. digit AFTER decimal point 
+  maths.round_ties_up(12.0654, 2)
+  |> should.equal(12.07)
+
+  // Round 1. digit AFTER decimal point 
+  maths.round_ties_up(12.0654, 1)
+  |> should.equal(12.1)
+
+  // Round 0. digit BEFORE decimal point 
+  maths.round_ties_up(12.0654, 0)
+  |> should.equal(12.0)
+
+  // Round 1. digit BEFORE decimal point 
+  maths.round_ties_up(12.0654, -1)
+  |> should.equal(10.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_ties_up(12.0654, -2)
+  |> should.equal(0.0)
+
+  // Round 2. digit BEFORE decimal point 
+  maths.round_ties_up(12.0654, -3)
+  |> should.equal(0.0)
+}
+
+pub fn float_absolute_difference_test() {
+  maths.float_absolute_difference(20.0, 15.0)
+  |> should.equal(5.0)
+
+  maths.float_absolute_difference(-20.0, -15.0)
+  |> should.equal(5.0)
+
+  maths.float_absolute_difference(20.0, -15.0)
+  |> should.equal(35.0)
+
+  maths.float_absolute_difference(-20.0, 15.0)
+  |> should.equal(35.0)
+
+  maths.float_absolute_difference(0.0, 0.0)
+  |> should.equal(0.0)
+
+  maths.float_absolute_difference(1.0, 2.0)
+  |> should.equal(1.0)
+
+  maths.float_absolute_difference(2.0, 1.0)
+  |> should.equal(1.0)
+
+  maths.float_absolute_difference(-1.0, 0.0)
+  |> should.equal(1.0)
+
+  maths.float_absolute_difference(0.0, -1.0)
+  |> should.equal(1.0)
+
+  maths.float_absolute_difference(10.0, 20.0)
+  |> should.equal(10.0)
+
+  maths.float_absolute_difference(-10.0, -20.0)
+  |> should.equal(10.0)
+
+  maths.float_absolute_difference(-10.5, 10.5)
+  |> should.equal(21.0)
+}
+
+pub fn int_absolute_difference_test() {
+  maths.int_absolute_difference(20, 15)
+  |> should.equal(5)
+
+  maths.int_absolute_difference(-20, -15)
+  |> should.equal(5)
+
+  maths.int_absolute_difference(20, -15)
+  |> should.equal(35)
+
+  maths.int_absolute_difference(-20, 15)
+  |> should.equal(35)
+}
+
+pub fn float_sign_test() {
+  maths.float_sign(100.0)
+  |> should.equal(1.0)
+
+  maths.float_sign(0.0)
+  |> should.equal(0.0)
+
+  maths.float_sign(-100.0)
+  |> should.equal(-1.0)
+}
+
+pub fn float_flip_sign_test() {
+  maths.float_flip_sign(100.0)
+  |> should.equal(-100.0)
+
+  maths.float_flip_sign(0.0)
+  |> should.equal(-0.0)
+
+  maths.float_flip_sign(-100.0)
+  |> should.equal(100.0)
+}
+
+pub fn float_copy_sign_test() {
+  maths.float_copy_sign(100.0, 10.0)
+  |> should.equal(100.0)
+
+  maths.float_copy_sign(-100.0, 10.0)
+  |> should.equal(100.0)
+
+  maths.float_copy_sign(100.0, -10.0)
+  |> should.equal(-100.0)
+
+  maths.float_copy_sign(-100.0, -10.0)
+  |> should.equal(-100.0)
+}
+
+pub fn int_sign_test() {
+  maths.int_sign(100)
+  |> should.equal(1)
+
+  maths.int_sign(0)
+  |> should.equal(0)
+
+  maths.int_sign(-100)
+  |> should.equal(-1)
+}
+
+pub fn int_flip_sign_test() {
+  maths.int_flip_sign(100)
+  |> should.equal(-100)
+
+  maths.int_flip_sign(0)
+  |> should.equal(-0)
+
+  maths.int_flip_sign(-100)
+  |> should.equal(100)
+}
+
+pub fn int_copy_sign_test() {
+  maths.int_copy_sign(100, 10)
+  |> should.equal(100)
+
+  maths.int_copy_sign(-100, 10)
+  |> should.equal(100)
+
+  maths.int_copy_sign(100, -10)
+  |> should.equal(-100)
+
+  maths.int_copy_sign(-100, -10)
+  |> should.equal(-100)
+}
+
+pub fn float_minmax_test() {
+  maths.minmax(0.75, 0.5, float.compare)
+  |> should.equal(#(0.5, 0.75))
+
+  maths.minmax(0.5, 0.75, float.compare)
+  |> should.equal(#(0.5, 0.75))
+
+  maths.minmax(-0.75, 0.5, float.compare)
+  |> should.equal(#(-0.75, 0.5))
+
+  maths.minmax(-0.75, 0.5, float.compare)
+  |> should.equal(#(-0.75, 0.5))
+}
+
+pub fn int_minmax_test() {
+  maths.minmax(75, 50, int.compare)
+  |> should.equal(#(50, 75))
+
+  maths.minmax(50, 75, int.compare)
+  |> should.equal(#(50, 75))
+
+  maths.minmax(-75, 50, int.compare)
+  |> should.equal(#(-75, 50))
+
+  maths.minmax(-75, 50, int.compare)
+  |> should.equal(#(-75, 50))
+}
+
+pub fn float_list_minimum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.list_minimum(float.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4.5, 4.5, 3.5, 2.5, 1.5]
+  |> maths.list_minimum(float.compare)
+  |> should.equal(Ok(1.5))
+}
+
+pub fn int_list_minimum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.list_minimum(int.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4, 4, 3, 2, 1]
+  |> maths.list_minimum(int.compare)
+  |> should.equal(Ok(1))
+}
+
+pub fn float_list_maximum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.list_maximum(float.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4.5, 4.5, 3.5, 2.5, 1.5]
+  |> maths.list_maximum(float.compare)
+  |> should.equal(Ok(4.5))
+}
+
+pub fn int_list_maximum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.list_maximum(int.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4, 4, 3, 2, 1]
+  |> maths.list_maximum(int.compare)
+  |> should.equal(Ok(4))
+}
+
+pub fn float_list_arg_maximum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.arg_maximum(float.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4.5, 4.5, 3.5, 2.5, 1.5]
+  |> maths.arg_maximum(float.compare)
+  |> should.equal(Ok([0, 1]))
+}
+
+pub fn int_list_arg_maximum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.arg_maximum(int.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4, 4, 3, 2, 1]
+  |> maths.arg_maximum(int.compare)
+  |> should.equal(Ok([0, 1]))
+}
+
+pub fn float_list_arg_minimum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.arg_minimum(float.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4.5, 4.5, 3.5, 2.5, 1.5]
+  |> maths.arg_minimum(float.compare)
+  |> should.equal(Ok([4]))
+}
+
+pub fn int_list_arg_minimum_test() {
+  // An empty lists returns an error
+  []
+  |> maths.arg_minimum(int.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4, 4, 3, 2, 1]
+  |> maths.arg_minimum(int.compare)
+  |> should.equal(Ok([4]))
+}
+
+pub fn float_list_extrema_test() {
+  // An empty lists returns an error
+  []
+  |> maths.extrema(float.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4.0, 4.0, 3.0, 2.0, 1.0]
+  |> maths.extrema(float.compare)
+  |> should.equal(Ok(#(1.0, 4.0)))
+
+  // Valid input returns a result
+  [1.0, 4.0, 2.0, 5.0, 0.0]
+  |> maths.extrema(float.compare)
+  |> should.equal(Ok(#(0.0, 5.0)))
+}
+
+pub fn int_list_extrema_test() {
+  // An empty lists returns an error
+  []
+  |> maths.extrema(int.compare)
+  |> should.be_error()
+
+  // Valid input returns a result
+  [4, 4, 3, 2, 1]
+  |> maths.extrema(int.compare)
+  |> should.equal(Ok(#(1, 4)))
+
+  // Valid input returns a result
+  [1, 4, 2, 5, 0]
+  |> maths.extrema(int.compare)
+  |> should.equal(Ok(#(0, 5)))
+}
diff --git a/test/gleam_community/sequences_test.gleam b/test/gleam_community/sequences_test.gleam
new file mode 100644
index 0000000..ef8b68f
--- /dev/null
+++ b/test/gleam_community/sequences_test.gleam
@@ -0,0 +1,390 @@
+import gleam/float
+import gleam/list
+import gleam/yielder
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_list_linear_space_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  // ---> With endpoint included
+  let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, True)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([10.0, 20.0, 30.0, 40.0, 50.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, True)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // Try with negative stop
+  // ----> Without endpoint included
+  let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, False)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([10.0, 18.0, 26.0, 34.0, 42.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, False)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([10.0, 12.0, 14.0, 16.0, 18.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // Try with negative stop
+  let assert Ok(linspace) = maths.linear_space(10.0, -50.0, 5, False)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace
+        |> yielder.to_list()
+        |> list.zip([10.0, -2.0, -14.0, -26.0, -38.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  let assert Ok(linspace) = maths.linear_space(10.0, -20.0, 5, True)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace
+        |> yielder.to_list()
+        |> list.zip([10.0, 2.5, -5.0, -12.5, -20.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // Try with negative start
+  let assert Ok(linspace) = maths.linear_space(-10.0, 50.0, 5, False)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([-10.0, 2.0, 14.0, 26.0, 38.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  let assert Ok(linspace) = maths.linear_space(-10.0, 20.0, 5, True)
+  let assert Ok(result) =
+    maths.all_close(
+      linspace |> yielder.to_list() |> list.zip([-10.0, -2.5, 5.0, 12.5, 20.0]),
+      0.0,
+      tol,
+    )
+
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // A negative number of points does not work (-5)
+  maths.linear_space(10.0, 50.0, -5, True)
+  |> should.be_error()
+}
+
+pub fn float_list_logarithmic_space_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  // ---> With endpoint included
+  // - Positive start, stop, base
+  let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive start, stop, negative base
+  let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([-10.0, 100.0, -1000.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive start, negative stop, base 
+  let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, -10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([-10.0, -0.1, -0.001]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive start, base, negative stop  
+  let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([10.0, 0.1, 0.001]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive stop, base, negative start
+  let assert Ok(logspace) = maths.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // ----> Without endpoint included
+  // - Positive start, stop, base
+  let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, False, 10.0)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace
+        |> yielder.to_list()
+        |> list.zip([10.0, 46.41588834, 215.443469]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // A negative number of points does not work (-3)
+  maths.logarithmic_space(1.0, 3.0, -3, True, 10.0)
+  |> should.be_error()
+}
+
+pub fn float_list_geometric_space_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  // ---> With endpoint included
+  // - Positive start, stop
+  let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive start, negative stop  
+  let assert Ok(logspace) = maths.geometric_space(10.0, 0.001, 3, True)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([10.0, 0.1, 0.001]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // - Positive stop, negative start
+  let assert Ok(logspace) = maths.geometric_space(0.1, 1000.0, 3, True)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // ----> Without endpoint included
+  // - Positive start, stop
+  let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, False)
+  let assert Ok(result) =
+    maths.all_close(
+      logspace
+        |> yielder.to_list()
+        |> list.zip([10.0, 46.41588834, 215.443469]),
+      0.0,
+      tol,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // Test invalid input (start and stop can't be equal to 0.0)
+  maths.geometric_space(0.0, 1000.0, 3, False)
+  |> should.be_error()
+
+  maths.geometric_space(-1000.0, 0.0, 3, False)
+  |> should.be_error()
+
+  // A negative number of points does not work
+  maths.geometric_space(-1000.0, 0.0, -3, False)
+  |> should.be_error()
+}
+
+pub fn float_list_arange_test() {
+  // Positive start, stop, step
+  maths.arange(1.0, 5.0, 1.0)
+  |> yielder.to_list()
+  |> should.equal([1.0, 2.0, 3.0, 4.0])
+
+  maths.arange(1.0, 5.0, 0.5)
+  |> yielder.to_list()
+  |> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
+
+  maths.arange(1.0, 2.0, 0.25)
+  |> yielder.to_list()
+  |> should.equal([1.0, 1.25, 1.5, 1.75])
+
+  // Reverse (switch start/stop largest/smallest value)
+  maths.arange(5.0, 1.0, 1.0)
+  |> yielder.to_list()
+  |> should.equal([])
+
+  // Reverse negative step
+  maths.arange(5.0, 1.0, -1.0)
+  |> yielder.to_list()
+  |> should.equal([5.0, 4.0, 3.0, 2.0])
+
+  // Positive start, negative stop, step
+  maths.arange(5.0, -1.0, -1.0)
+  |> yielder.to_list()
+  |> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
+
+  // Negative start, stop, step
+  maths.arange(-5.0, -1.0, -1.0)
+  |> yielder.to_list()
+  |> should.equal([])
+
+  // Negative start, stop, positive step
+  maths.arange(-5.0, -1.0, 1.0)
+  |> yielder.to_list()
+  |> should.equal([-5.0, -4.0, -3.0, -2.0])
+}
+
+pub fn float_list_exponential_space_test() {
+  let assert Ok(tolerance) = float.power(10.0, -6.0)
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  // ---> With endpoint included
+  let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
+  let assert Ok(result) =
+    maths.all_close(
+      exp_space |> yielder.to_list() |> list.zip([1.0, 10.0, 100.0, 1000.0]),
+      0.0,
+      tolerance,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // ---> Without endpoint included
+  let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, False)
+  let assert Ok(result) =
+    maths.all_close(
+      exp_space
+        |> yielder.to_list()
+        |> list.zip([
+          1.0, 5.623413251903491, 31.622776601683793, 177.82794100389228,
+        ]),
+      0.0,
+      tolerance,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // A negative number of points does not work (-3)
+  maths.exponential_space(1.0, 1000.0, -3, True)
+  |> should.be_error()
+}
+
+pub fn float_list_symmetric_space_test() {
+  let assert Ok(tolerance) = float.power(10.0, -6.0)
+
+  // Check that the function agrees, at some arbitrary input
+  // points, with known function values
+  let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
+  sym_space
+  |> yielder.to_list()
+  |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
+
+  let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
+  sym_space
+  |> yielder.to_list()
+  |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
+
+  // Negative center
+  let assert Ok(sym_space) = maths.symmetric_space(-10.0, 5.0, 5)
+  sym_space
+  |> yielder.to_list()
+  |> should.equal([-15.0, -12.5, -10.0, -7.5, -5.0])
+
+  // Uneven number of points
+  let assert Ok(sym_space) = maths.symmetric_space(0.0, 2.0, 4)
+  let assert Ok(result) =
+    maths.all_close(
+      sym_space
+        |> yielder.to_list()
+        |> list.zip([-2.0, -0.6666666666666667, 0.6666666666666665, 2.0]),
+      0.0,
+      tolerance,
+    )
+  result
+  |> list.all(fn(x) { x == True })
+  |> should.be_true()
+
+  // A negative number of points does not work (-5)
+  maths.symmetric_space(0.0, 5.0, -5)
+  |> should.be_error()
+}
diff --git a/test/gleam_community/special_test.gleam b/test/gleam_community/special_test.gleam
new file mode 100644
index 0000000..ab07eb9
--- /dev/null
+++ b/test/gleam_community/special_test.gleam
@@ -0,0 +1,113 @@
+import gleam/float
+import gleam/result
+import gleam_community/maths
+import gleeunit/should
+
+pub fn float_beta_function_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Valid input returns a result
+  maths.beta(-0.5, 0.5)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.beta(0.5, 0.5)
+  |> maths.is_close(3.1415926535897927, 0.0, tol)
+  |> should.be_true()
+
+  maths.beta(0.5, -0.5)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.beta(5.0, 5.0)
+  |> maths.is_close(0.0015873015873015873, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_error_function_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Valid input returns a result
+  maths.erf(-0.5)
+  |> maths.is_close(-0.5204998778130465, 0.0, tol)
+  |> should.be_true()
+
+  maths.erf(0.5)
+  |> maths.is_close(0.5204998778130465, 0.0, tol)
+  |> should.be_true()
+
+  maths.erf(1.0)
+  |> maths.is_close(0.8427007929497148, 0.0, tol)
+  |> should.be_true()
+
+  maths.erf(2.0)
+  |> maths.is_close(0.9953222650189527, 0.0, tol)
+  |> should.be_true()
+
+  maths.erf(10.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_gamma_function_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Valid input returns a result
+  maths.gamma(-0.5)
+  |> maths.is_close(-3.5449077018110318, 0.0, tol)
+  |> should.be_true()
+
+  maths.gamma(0.5)
+  |> maths.is_close(1.7724538509055159, 0.0, tol)
+  |> should.be_true()
+
+  maths.gamma(1.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.gamma(2.0)
+  |> maths.is_close(1.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.gamma(3.0)
+  |> maths.is_close(2.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.gamma(10.0)
+  |> maths.is_close(362_880.0, 0.0, tol)
+  |> should.be_true()
+}
+
+pub fn float_incomplete_gamma_function_test() {
+  let assert Ok(tol) = float.power(10.0, -6.0)
+
+  // Invalid input gives an error
+  // 1st arg is invalid
+  maths.incomplete_gamma(-1.0, 1.0)
+  |> should.be_error()
+
+  // 2nd arg is invalid
+  maths.incomplete_gamma(1.0, -1.0)
+  |> should.be_error()
+
+  // Valid input returns a result
+  maths.incomplete_gamma(1.0, 0.0)
+  |> result.unwrap(-999.0)
+  |> maths.is_close(0.0, 0.0, tol)
+  |> should.be_true()
+
+  maths.incomplete_gamma(1.0, 2.0)
+  |> result.unwrap(-999.0)
+  |> maths.is_close(0.864664716763387308106, 0.0, tol)
+  |> should.be_true()
+
+  maths.incomplete_gamma(2.0, 3.0)
+  |> result.unwrap(-999.0)
+  |> maths.is_close(0.8008517265285442280826, 0.0, tol)
+  |> should.be_true()
+
+  maths.incomplete_gamma(3.0, 4.0)
+  |> result.unwrap(-999.0)
+  |> maths.is_close(1.523793388892911312363, 0.0, tol)
+  |> should.be_true()
+}