diff --git a/src/gleam_community/maths/arithmetics.gleam b/src/gleam_community/maths/arithmetics.gleam index dfa3c8d..d7e0e73 100644 --- a/src/gleam_community/maths/arithmetics.gleam +++ b/src/gleam_community/maths/arithmetics.gleam @@ -20,11 +20,11 @@ //// -//// +//// //// --- -//// +//// //// 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) @@ -40,7 +40,7 @@ //// * [`int_cumulative_sum`](#int_cumulative_sum) //// * [`float_cumulative_product`](#float_cumulative_product) //// * [`int_cumulative_product`](#int_cumulative_product) -//// +//// import gleam/int import gleam/list @@ -57,7 +57,7 @@ import gleam_community/maths/piecewise /// /// /// -/// The function calculates the greatest common divisor of two integers +/// 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\\). /// @@ -70,7 +70,7 @@ import gleam_community/maths/piecewise /// pub fn example() { /// arithmetics.gcd(1, 1) /// |> should.equal(1) -/// +/// /// arithmetics.gcd(100, 10) /// |> should.equal(10) /// @@ -107,24 +107,24 @@ fn do_gcd(x: Int, y: Int) -> Int { /// /// /// -/// +/// /// 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 +/// 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 +/// 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`. /// @@ -138,7 +138,7 @@ fn do_gcd(x: Int, y: Int) -> Int { /// pub fn example() { /// arithmetics.euclidean_modulo(15, 4) /// |> should.equal(3) -/// +/// /// arithmetics.euclidean_modulo(-3, -2) /// |> should.equal(1) /// @@ -168,7 +168,7 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int { /// /// /// -/// The function calculates the least common multiple of two integers +/// 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. /// @@ -181,7 +181,7 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int { /// pub fn example() { /// arithmetics.lcm(1, 1) /// |> should.equal(1) -/// +/// /// arithmetics.lcm(100, 10) /// |> should.equal(100) /// @@ -208,7 +208,7 @@ pub fn lcm(x: Int, y: Int) -> Int { /// /// /// -/// The function returns all the positive divisors of an integer, including the +/// The function returns all the positive divisors of an integer, including the /// number itself. /// ///
@@ -260,7 +260,7 @@ fn find_divisors(n: Int) -> List(Int) { /// /// /// -/// The function returns all the positive divisors of an integer, excluding the +/// The function returns all the positive divisors of an integer, excluding the /// number iteself. /// ///
@@ -362,7 +362,7 @@ pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float /// \sum_{i=1}^n x_i /// \\] /// -/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is +/// 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\\). /// ///
@@ -414,7 +414,7 @@ pub fn int_sum(arr: List(Int)) -> Int { /// 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). -/// +/// ///
/// Example: /// @@ -444,7 +444,7 @@ pub fn int_sum(arr: List(Int)) -> Int { pub fn float_product( arr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case arr, weights { [], _ -> 1.0 @@ -454,22 +454,16 @@ pub fn float_product( |> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc }) |> Ok _, option.Some(warr) -> { - let results = - list.zip(arr, warr) - |> list.map(fn(a: #(Float, Float)) -> Result(Float, String) { - pair.first(a) - |> elementary.power(pair.second(a)) - }) - |> result.all - case results { - Ok(prods) -> - prods - |> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc }) - |> Ok - Error(msg) -> - msg - |> Error - } + 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 }) + }) } } } @@ -486,7 +480,7 @@ pub fn float_product( /// \prod_{i=1}^n x_i /// \\] /// -/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is +/// 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\\). /// ///
@@ -536,7 +530,7 @@ pub fn int_product(arr: List(Int)) -> Int { /// \\] /// /// 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}\\) +/// 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. /// @@ -586,7 +580,7 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) { /// \\] /// /// 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}\\) +/// 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. /// @@ -635,10 +629,10 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) { /// 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 +/// 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. /// ///
@@ -687,9 +681,9 @@ pub fn float_cumulative_product(arr: List(Float)) -> List(Float) { /// 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 +/// 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. /// diff --git a/src/gleam_community/maths/combinatorics.gleam b/src/gleam_community/maths/combinatorics.gleam index b026c4d..f191eef 100644 --- a/src/gleam_community/maths/combinatorics.gleam +++ b/src/gleam_community/maths/combinatorics.gleam @@ -20,12 +20,12 @@ //// -//// +//// //// --- -//// -//// Combinatorics: A module that offers mathematical functions related to counting, arrangements, -//// and permutations/combinations. -//// +//// +//// Combinatorics: A module that offers mathematical functions related to counting, arrangements, +//// and permutations/combinations. +//// //// * **Combinatorial functions** //// * [`combination`](#combination) //// * [`factorial`](#factorial) @@ -33,7 +33,7 @@ //// * [`list_combination`](#list_combination) //// * [`list_permutation`](#list_permutation) //// * [`cartesian_product`](#cartesian_product) -//// +//// import gleam/iterator import gleam/list @@ -70,26 +70,26 @@ pub type CombinatoricsMode { /// Also known as the "stars and bars" problem in combinatorics. /// /// The implementation uses an efficient iterative multiplicative formula for computing the result. -/// +/// ///
/// Details -/// -/// 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 +/// +/// 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 +/// +/// - 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 +/// - 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 +/// - On the contrary, for \\(k\\)-permutations (without repetitions), the order matters, so the /// possible selections are: /// - `["A", "B"], ["B", "A"]` /// - `["A", "C"], ["C", "A"]` @@ -106,15 +106,15 @@ pub type CombinatoricsMode { /// // 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)) @@ -125,17 +125,15 @@ pub type CombinatoricsMode { /// Back to top ↑ /// /// -/// +/// pub fn combination( n: Int, k: Int, mode: option.Option(CombinatoricsMode), -) -> Result(Int, String) { +) -> Result(Int, Nil) { case n, k { - _, _ if n < 0 -> - "Invalid input argument: n < 0. Valid input is n >= 0." |> Error - _, _ if k < 0 -> - "Invalid input argument: k < 0. Valid input is k >= 0." |> Error + _, _ if n < 0 -> Error(Nil) + _, _ if k < 0 -> Error(Nil) _, _ -> { case mode { option.Some(WithRepetitions) -> combination_with_repetitions(n, k) @@ -145,12 +143,11 @@ pub fn combination( } } -fn combination_with_repetitions(n: Int, k: Int) -> Result(Int, String) { - { n + k - 1 } - |> combination_without_repetitions(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, String) { +fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) { case n, k { _, _ if k == 0 || k == n -> { 1 |> Ok @@ -202,17 +199,11 @@ fn combination_without_repetitions(n: Int, k: Int) -> Result(Int, String) { /// /// /// -pub fn factorial(n) -> Result(Int, String) { +pub fn factorial(n) -> Result(Int, Nil) { case n { - _ if n < 0 -> - "Invalid input argument: n < 0. Valid input is n >= 0." - |> Error - 0 -> - 1 - |> Ok - 1 -> - 1 - |> Ok + _ if n < 0 -> Error(Nil) + 0 -> Ok(1) + 1 -> Ok(1) _ -> list.range(1, n) |> list.fold(1, fn(acc: Int, x: Int) -> Int { acc * x }) @@ -227,50 +218,50 @@ pub fn factorial(n) -> Result(Int, String) { /// /// /// 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 -/// +/// /// 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 +/// \\(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 +/// +/// - 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 +/// +/// - 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, +/// - On the contrary, for \\(k\\)-combinations (without repetitions), where order does not matter, /// the possible selections are: /// - `["A", "B"]` /// - `["A", "C"]` /// - `["B", "C"]` ///
-/// +/// ///
/// Example: -/// +/// /// import gleam/option /// import gleeunit/should /// import gleam_community/maths/combinatorics @@ -300,22 +291,16 @@ pub fn permutation( n: Int, k: Int, mode: option.Option(CombinatoricsMode), -) -> Result(Int, String) { - case n, k { - _, _ if n < 0 -> - "Invalid input argument: n < 0. Valid input is n >= 0." |> Error - _, _ if k < 0 -> - "Invalid input argument: k < 0. Valid input is k >= 0." |> Error - _, _ -> { - case mode { - option.Some(WithRepetitions) -> permutation_with_repetitions(n, k) - _ -> permutation_without_repetitions(n, k) - } - } +) -> 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, String) { +fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, Nil) { case n, k { _, _ if k < 0 || k > n -> { 0 |> Ok @@ -330,7 +315,7 @@ fn permutation_without_repetitions(n: Int, k: Int) -> Result(Int, String) { } } -fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, String) { +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... @@ -346,11 +331,11 @@ fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, String) { /// /// /// -/// Generates all possible combinations of \\(k\\) elements selected from a given list of size +/// 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 +/// The function can handle cases with and without repetitions +/// (see more details [here](#combination)). Also, note that repeated elements are treated as /// distinct. /// ///
@@ -370,7 +355,7 @@ fn permutation_with_repetitions(n: Int, k: Int) -> Result(Int, String) { /// 3, /// option.Some(combinatorics.WithoutRepetitions), /// ) -/// +/// /// result /// |> iterator.to_list() /// |> set.from_list() @@ -388,29 +373,20 @@ pub fn list_combination( arr: List(a), k: Int, mode: option.Option(CombinatoricsMode), -) -> Result(iterator.Iterator(List(a)), String) { - case k { - _ if k < 0 -> - "Invalid input argument: k < 0. Valid input is k >= 0." - |> Error - _ -> - case mode { - option.Some(WithRepetitions) -> - list_combination_with_repetitions(arr, k) - _ -> list_combination_without_repetitions(arr, k) - } +) -> 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)), String) { +) -> Result(iterator.Iterator(List(a)), Nil) { case k, list.length(arr) { - _, arr_length if k > arr_length -> { - "Invalid input argument: k > length(arr). Valid input is 0 <= k <= length(arr)." - |> Error - } + _, 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 @@ -446,7 +422,7 @@ fn do_list_combination_without_repetitions( fn list_combination_with_repetitions( arr: List(a), k: Int, -) -> Result(iterator.Iterator(List(a)), String) { +) -> Result(iterator.Iterator(List(a)), Nil) { Ok(do_list_combination_with_repetitions(iterator.from_list(arr), k, [])) } @@ -476,11 +452,11 @@ fn do_list_combination_with_repetitions( /// /// /// -/// Generates all possible permutations of \\(k\\) elements selected from a given list of size +/// 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 +/// The function can handle cases with and without repetitions +/// (see more details [here](#permutation)). Also, note that repeated elements are treated as /// distinct. /// ///
@@ -500,7 +476,7 @@ fn do_list_combination_with_repetitions( /// 3, /// option.Some(combinatorics.WithoutRepetitions), /// ) -/// +/// /// result /// |> iterator.to_list() /// |> set.from_list() @@ -523,22 +499,17 @@ fn do_list_combination_with_repetitions( /// /// /// -/// +/// pub fn list_permutation( arr: List(a), k: Int, mode: option.Option(CombinatoricsMode), -) -> Result(iterator.Iterator(List(a)), String) { - case k { - _ if k < 0 -> - "Invalid input argument: k < 0. Valid input is k >= 0." - |> Error - _ -> - case mode { - option.Some(WithRepetitions) -> - list_permutation_with_repetitions(arr, k) - _ -> list_permutation_without_repetitions(arr, k) - } +) -> 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) } } @@ -558,12 +529,9 @@ fn remove_first_by_index( fn list_permutation_without_repetitions( arr: List(a), k: Int, -) -> Result(iterator.Iterator(List(a)), String) { +) -> Result(iterator.Iterator(List(a)), Nil) { case k, list.length(arr) { - _, arr_length if k > arr_length -> { - "Invalid input argument: k > length(arr). Valid input is 0 <= k <= length(arr)." - |> Error - } + _, 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( @@ -594,9 +562,9 @@ fn do_list_permutation_without_repetitions( fn list_permutation_with_repetitions( arr: List(a), k: Int, -) -> Result(iterator.Iterator(List(a)), String) { +) -> iterator.Iterator(List(a)) { let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) }) - Ok(do_list_permutation_with_repetitions(indexed_arr, k)) + do_list_permutation_with_repetitions(indexed_arr, k) } fn do_list_permutation_with_repetitions( @@ -636,7 +604,7 @@ fn do_list_permutation_with_repetitions( /// 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])) diff --git a/src/gleam_community/maths/elementary.gleam b/src/gleam_community/maths/elementary.gleam index 25e6b47..d5ff8af 100644 --- a/src/gleam_community/maths/elementary.gleam +++ b/src/gleam_community/maths/elementary.gleam @@ -20,11 +20,11 @@ //// -//// +//// //// --- -//// +//// //// Elementary: A module containing a comprehensive set of foundational mathematical functions and constants. -//// +//// //// * **Trigonometric and hyperbolic functions** //// * [`acos`](#acos) //// * [`acosh`](#acosh) @@ -53,7 +53,7 @@ //// * [`pi`](#pi) //// * [`tau`](#tau) //// * [`e`](#e) -//// +//// import gleam/int import gleam/option @@ -98,14 +98,10 @@ import gleam/option /// /// /// -pub fn acos(x: Float) -> Result(Float, String) { +pub fn acos(x: Float) -> Result(Float, Nil) { case x >=. -1.0 && x <=. 1.0 { - True -> - do_acos(x) - |> Ok - False -> - "Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1." - |> Error + True -> Ok(do_acos(x)) + False -> Error(Nil) } } @@ -150,14 +146,10 @@ fn do_acos(a: Float) -> Float /// /// /// -pub fn acosh(x: Float) -> Result(Float, String) { +pub fn acosh(x: Float) -> Result(Float, Nil) { case x >=. 1.0 { - True -> - do_acosh(x) - |> Ok - False -> - "Invalid input argument: x < 1. Valid input is x >= 1." - |> Error + True -> Ok(do_acosh(x)) + False -> Error(Nil) } } @@ -178,7 +170,7 @@ fn do_acosh(a: Float) -> Float /// \\] /// /// 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 +/// 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. /// ///
@@ -205,14 +197,10 @@ fn do_acosh(a: Float) -> Float /// /// /// -pub fn asin(x: Float) -> Result(Float, String) { +pub fn asin(x: Float) -> Result(Float, Nil) { case x >=. -1.0 && x <=. 1.0 { - True -> - do_asin(x) - |> Ok - False -> - "Invalid input argument: x >= -1 or x <= 1. Valid input is -1. <= x <= 1." - |> Error + True -> Ok(do_asin(x)) + False -> Error(Nil) } } @@ -232,8 +220,8 @@ fn do_asin(a: Float) -> Float /// \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 +/// 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). /// ///
@@ -274,7 +262,7 @@ fn do_asinh(a: Float) -> Float /// \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 +/// 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). /// @@ -394,14 +382,10 @@ fn do_atan2(a: Float, b: Float) -> Float /// /// /// -pub fn atanh(x: Float) -> Result(Float, String) { +pub fn atanh(x: Float) -> Result(Float, Nil) { case x >. -1.0 && x <. 1.0 { - True -> - do_atanh(x) - |> Ok - False -> - "Invalid input argument: x > -1 or x < 1. Valid input is -1. < x < 1." - |> Error + True -> Ok(do_atanh(x)) + False -> Error(Nil) } } @@ -421,7 +405,7 @@ fn do_atanh(a: Float) -> Float /// \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 +/// 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\]\\). /// ///
@@ -465,8 +449,8 @@ fn do_cos(a: Float) -> Float /// \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 +/// 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. /// ///
@@ -507,7 +491,7 @@ fn do_cosh(a: Float) -> Float /// \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 +/// 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\]\\). /// ///
@@ -753,14 +737,10 @@ fn do_exponential(a: Float) -> Float /// /// /// -pub fn natural_logarithm(x: Float) -> Result(Float, String) { +pub fn natural_logarithm(x: Float) -> Result(Float, Nil) { case x >. 0.0 { - True -> - do_natural_logarithm(x) - |> Ok - False -> - "Invalid input argument: x <= 0. Valid input is x > 0." - |> Error + True -> Ok(do_natural_logarithm(x)) + False -> Error(Nil) } } @@ -780,7 +760,7 @@ fn do_natural_logarithm(a: Float) -> Float /// \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\\) +/// 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. /// @@ -810,30 +790,19 @@ fn do_natural_logarithm(a: Float) -> Float /// /// /// -pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String) { - case x >. 0.0 { - True -> - case base { - option.Some(a) -> - case a >. 0.0 && a != 1.0 { - True -> { - // Apply the "change of base formula" - let assert Ok(numerator) = logarithm_10(x) - let assert Ok(denominator) = logarithm_10(a) - numerator /. denominator - |> Ok - } - False -> - "Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1." - |> Error - } - _ -> - "Invalid input argument: base <= 0 or base == 1. Valid input is base > 0 and base != 1." - |> Error +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) + } } - _ -> - "Invalid input argument: x <= 0. Valid input is x > 0." - |> Error + _, _ -> Error(Nil) } } @@ -849,7 +818,7 @@ pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String) /// \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 +/// 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. /// @@ -877,14 +846,10 @@ pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String) /// /// /// -pub fn logarithm_2(x: Float) -> Result(Float, String) { +pub fn logarithm_2(x: Float) -> Result(Float, Nil) { case x >. 0.0 { - True -> - do_logarithm_2(x) - |> Ok - False -> - "Invalid input argument: x <= 0. Valid input is x > 0." - |> Error + True -> Ok(do_logarithm_2(x)) + False -> Error(Nil) } } @@ -904,7 +869,7 @@ fn do_logarithm_2(a: Float) -> Float /// \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 +/// 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. /// @@ -932,14 +897,10 @@ fn do_logarithm_2(a: Float) -> Float /// /// /// -pub fn logarithm_10(x: Float) -> Result(Float, String) { +pub fn logarithm_10(x: Float) -> Result(Float, Nil) { case x >. 0.0 { - True -> - do_logarithm_10(x) - |> Ok - False -> - "Invalid input argument: x <= 0. Valid input is x > 0." - |> Error + True -> Ok(do_logarithm_10(x)) + False -> Error(Nil) } } @@ -993,7 +954,7 @@ fn do_logarithm_10(a: Float) -> Float /// /// /// -pub fn power(x: Float, y: Float) -> Result(Float, String) { +pub fn power(x: Float, y: Float) -> Result(Float, Nil) { let fractional: Bool = do_ceiling(y) -. y >. 0.0 // In the following check: // 1. If the base (x) is negative and the exponent (y) is fractional @@ -1002,9 +963,7 @@ pub fn power(x: Float, y: Float) -> Result(Float, String) { // expression is equivalent to the exponent (y) divided by 0 and an // error should be returned case { x <. 0.0 && fractional } || { x == 0.0 && y <. 0.0 } { - True -> - "Invalid input argument: x < 0 and y is fractional or x = 0 and y < 0." - |> Error + True -> Error(Nil) False -> do_power(x, y) |> Ok @@ -1055,19 +1014,13 @@ fn do_ceiling(a: Float) -> Float /// /// /// -pub fn square_root(x: Float) -> Result(Float, String) { +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 + // 1. If x is negative then return an error as it will otherwise be an // imaginary number case x <. 0.0 { - True -> - "Invalid input argument: x < 0." - |> Error - False -> { - let assert Ok(result) = power(x, 1.0 /. 2.0) - result - |> Ok - } + True -> Error(Nil) + False -> power(x, 1.0 /. 2.0) } } @@ -1107,19 +1060,13 @@ pub fn square_root(x: Float) -> Result(Float, String) { /// /// /// -pub fn cube_root(x: Float) -> Result(Float, String) { +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 + // 1. If x is negative then return an error as it will otherwise be an // imaginary number case x <. 0.0 { - True -> - "Invalid input argument: x < 0." - |> Error - False -> { - let assert Ok(result) = power(x, 1.0 /. 3.0) - result - |> Ok - } + True -> Error(Nil) + False -> power(x, 1.0 /. 3.0) } } @@ -1162,25 +1109,13 @@ pub fn cube_root(x: Float) -> Result(Float, String) { /// /// /// -pub fn nth_root(x: Float, n: Int) -> Result(Float, String) { +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 + // 1. If x is negative then return an error as it will otherwise be an // imaginary number - case x <. 0.0 { - True -> - "Invalid input argument: x < 0. Valid input is x > 0" - |> Error - False -> - case n >= 1 { - True -> { - let assert Ok(result) = power(x, 1.0 /. int.to_float(n)) - result - |> Ok - } - False -> - "Invalid input argument: n < 1. Valid input is n >= 2." - |> Error - } + case x >=. 0.0 && n >= 1 { + True -> power(x, 1.0 /. int.to_float(n)) + False -> Error(Nil) } } diff --git a/src/gleam_community/maths/metrics.gleam b/src/gleam_community/maths/metrics.gleam index f4862eb..9047b02 100644 --- a/src/gleam_community/maths/metrics.gleam +++ b/src/gleam_community/maths/metrics.gleam @@ -20,18 +20,18 @@ //// -//// +//// //// --- -//// -//// Metrics: A module offering functions for calculating distances and other +//// +//// 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 +//// 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) @@ -51,7 +51,7 @@ //// * [`median`](#median) //// * [`variance`](#variance) //// * [`standard_deviation`](#standard_deviation) -//// +//// import gleam/bool import gleam/float @@ -59,6 +59,7 @@ 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 @@ -72,21 +73,15 @@ fn validate_lists( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Bool, String) { +) -> Result(Bool, Nil) { case xarr, yarr { - [], _ -> - "Invalid input argument: The list xarr is empty." - |> Error - _, [] -> - "Invalid input argument: The list yarr is empty." - |> Error + [], _ -> Error(Nil) + _, [] -> Error(Nil) _, _ -> { let xarr_length: Int = list.length(xarr) let yarr_length: Int = list.length(yarr) case xarr_length == yarr_length, weights { - False, _ -> - "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." - |> Error + False, _ -> Error(Nil) True, option.None -> { True |> Ok @@ -97,9 +92,7 @@ fn validate_lists( True -> { validate_weights(warr) } - False -> - "Invalid input argument: length(weights) != length(xarr) and length(weights) != length(yarr). Valid input is when length(weights) == length(xarr) == length(yarr)." - |> Error + False -> Error(Nil) } } } @@ -107,16 +100,12 @@ fn validate_lists( } } -fn validate_weights(warr: List(Float)) -> Result(Bool, String) { +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 -> - "Invalid input argument: One or more weights are negative. Valid input is when all weights are >= 0." - |> Error - True -> - True - |> Ok + False -> Error(Nil) + True -> Ok(True) } } @@ -132,7 +121,7 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) { /// \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 +/// 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). /// @@ -147,14 +136,14 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) { /// /// 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) @@ -174,7 +163,7 @@ pub fn norm( arr: List(Float), p: Float, weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case arr, weights { [], _ -> 0.0 @@ -224,10 +213,7 @@ pub fn norm( |> Error } } - False -> { - "Invalid input argument: length(weights) != length(arr). Valid input is when length(weights) == length(arr)." - |> Error - } + False -> Error(Nil) } } } @@ -239,16 +225,16 @@ pub fn norm( /// /// /// -/// Calculate the (weighted) Manhattan distance between two lists (representing +/// 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 +/// 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 \in \mathbb{R}_{+}\\) are corresponding positive weights /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// ///
@@ -266,11 +252,11 @@ pub fn norm( /// // 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 @@ -289,7 +275,7 @@ pub fn manhattan_distance( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { minkowski_distance(xarr, yarr, 1.0, weights) } @@ -306,12 +292,12 @@ pub fn manhattan_distance( /// \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 +/// 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 \\(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 +/// The Minkowski distance is a generalization of both the Euclidean distance /// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)). /// ///
@@ -325,11 +311,11 @@ pub fn manhattan_distance( /// /// 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() @@ -337,7 +323,7 @@ pub fn manhattan_distance( /// // 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 @@ -357,28 +343,18 @@ pub fn minkowski_distance( yarr: List(Float), p: Float, weights: option.Option(List(Float)), -) -> Result(Float, String) { - case validate_lists(xarr, yarr, weights) { - Error(msg) -> - msg - |> Error - Ok(_) -> { - case p <. 1.0 { - True -> - "Invalid input argument: p < 1. Valid input is p >= 1." - |> Error - False -> { - let differences: List(Float) = - list.zip(xarr, yarr) - |> list.map(fn(tuple: #(Float, Float)) -> Float { - pair.first(tuple) -. pair.second(tuple) - }) +) -> 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) + }) - let assert Ok(result) = norm(differences, p, weights) - result - |> Ok - } - } + norm(differences, p, weights) } } } @@ -389,7 +365,7 @@ pub fn minkowski_distance( /// /// /// -/// Calculate the (weighted) Euclidean distance between two lists (representing +/// Calculate the (weighted) Euclidean distance between two lists (representing /// vectors): /// /// \\[ @@ -398,7 +374,7 @@ pub fn minkowski_distance( /// /// 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 \in \mathbb{R}_{+}\\) are corresponding positive weights /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// ///
@@ -412,15 +388,15 @@ pub fn minkowski_distance( /// /// 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 @@ -439,7 +415,7 @@ pub fn euclidean_distance( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { minkowski_distance(xarr, yarr, 2.0, weights) } @@ -455,7 +431,7 @@ pub fn euclidean_distance( /// \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 +/// 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\\). /// ///
@@ -470,11 +446,11 @@ pub fn euclidean_distance( /// // 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)) /// } @@ -489,7 +465,7 @@ pub fn euclidean_distance( pub fn chebyshev_distance( xarr: List(Float), yarr: List(Float), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case validate_lists(xarr, yarr, option.None) { Error(msg) -> msg @@ -545,11 +521,9 @@ pub fn chebyshev_distance( /// /// /// -pub fn mean(arr: List(Float)) -> Result(Float, String) { +pub fn mean(arr: List(Float)) -> Result(Float, Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) _ -> arr |> arithmetics.float_sum(option.None) @@ -632,14 +606,14 @@ fn do_median( /// /// /// 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 +/// 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. /// @@ -671,34 +645,27 @@ fn do_median( /// /// /// -pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) { - case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error - _ -> - case ddof < 0 { - True -> - "Invalid input argument: ddof < 0. Valid input is ddof >= 0." - |> Error - False -> { - let assert Ok(mean) = mean(arr) - arr - |> list.map(fn(a: Float) -> Float { - let assert Ok(result) = elementary.power(a -. mean, 2.0) - result - }) - |> arithmetics.float_sum(option.None) - |> fn(a: Float) -> Float { - a - /. { - conversion.int_to_float(list.length(arr)) - -. conversion.int_to_float(ddof) - } - } - |> Ok +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 + } } } @@ -713,11 +680,11 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) { /// 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 +/// 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 of the sample standard deviation. Setting \\(d = 1\\) gives an unbiased /// estimate. /// ///
@@ -748,25 +715,18 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) { /// /// /// -pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String) { - case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error - _ -> - case ddof < 0 { - True -> - "Invalid input argument: ddof < 0. Valid input is ddof >= 0." - |> Error - False -> { - let assert Ok(variance) = variance(arr, ddof) - // The computed variance will always be positive - // So an error should never be returned - let assert Ok(stdev) = elementary.square_root(variance) - stdev - |> Ok - } - } +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 + } } } @@ -776,24 +736,24 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String) /// /// /// -/// The Jaccard index measures similarity between two sets of elements. +/// 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 +/// +/// 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\\)). -/// +/// ///
/// Example: /// @@ -827,25 +787,25 @@ pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float { /// /// /// -/// The Sørensen-Dice coefficient measures the similarity between two sets of +/// 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 +/// - \\(|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 +/// 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\\)). -/// +/// ///
/// Example: /// @@ -878,31 +838,31 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float { /// Spot a typo? Open an issue! /// /// -/// -/// 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 +/// +/// 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 +/// - \\(|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 +/// 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\\). -/// +/// ///
/// Example: /// @@ -931,7 +891,7 @@ pub fn tversky_index( yset: set.Set(a), alpha: Float, beta: Float, -) -> Result(Float, String) { +) -> Result(Float, Nil) { case alpha >=. 0.0, beta >=. 0.0 { True, True -> { let intersection: Float = @@ -950,18 +910,7 @@ pub fn tversky_index( /. { intersection +. alpha *. difference1 +. beta *. difference2 } |> Ok } - False, True -> { - "Invalid input argument: alpha < 0. Valid input is alpha >= 0." - |> Error - } - True, False -> { - "Invalid input argument: beta < 0. Valid input is beta >= 0." - |> Error - } - _, _ -> { - "Invalid input argument: alpha < 0 and beta < 0. Valid input is alpha >= 0 and beta >= 0." - |> Error - } + _, _ -> Error(Nil) } } @@ -970,10 +919,10 @@ pub fn tversky_index( /// Spot a typo? Open an issue! /// /// -/// +/// /// 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 +/// 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: /// /// \\[ @@ -986,10 +935,10 @@ pub fn tversky_index( /// - \\(|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 +/// 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 +/// of the proportion of overlap is more relevant than the difference in sizes /// between the two sets. /// ///
@@ -1031,27 +980,27 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float { /// Spot a typo? Open an issue! /// /// -/// +/// /// 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}}} +/// \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. -/// +/// \\(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. +/// ///
/// Example: /// @@ -1063,11 +1012,11 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float { /// // 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)) @@ -1084,7 +1033,7 @@ pub fn cosine_similarity( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case validate_lists(xarr, yarr, weights) { Error(msg) -> msg @@ -1135,7 +1084,7 @@ pub fn cosine_similarity( /// Spot a typo? Open an issue! /// /// -/// +/// /// Calculate the (weighted) Canberra distance between two lists: /// /// \\[ @@ -1143,10 +1092,10 @@ pub fn cosine_similarity( /// {\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). +/// 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). /// ///
/// Example: @@ -1159,15 +1108,15 @@ pub fn cosine_similarity( /// // 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])) /// } ///
@@ -1182,7 +1131,7 @@ pub fn canberra_distance( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case validate_lists(xarr, yarr, weights) { Error(msg) -> msg @@ -1223,7 +1172,7 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float { /// Spot a typo? Open an issue! /// /// -/// +/// /// Calculate the (weighted) Bray-Curtis distance between two lists: /// /// \\[ @@ -1231,11 +1180,11 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float { /// {\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 +/// 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. /// @@ -1250,15 +1199,15 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float { /// // 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)) /// } @@ -1275,7 +1224,7 @@ pub fn braycurtis_distance( xarr: List(Float), yarr: List(Float), weights: option.Option(List(Float)), -) -> Result(Float, String) { +) -> Result(Float, Nil) { case validate_lists(xarr, yarr, weights) { Error(msg) -> msg diff --git a/src/gleam_community/maths/piecewise.gleam b/src/gleam_community/maths/piecewise.gleam index 36a18dc..6931f8a 100644 --- a/src/gleam_community/maths/piecewise.gleam +++ b/src/gleam_community/maths/piecewise.gleam @@ -69,7 +69,7 @@ import gleam_community/maths/elementary /// 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) +/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) /// with rounding mode `RoundUp`. /// ///
@@ -124,10 +124,10 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float { /// /// /// -/// The floor function rounds input \\(x \in \mathbb{R}\\) to the nearest integer value (at the +/// 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) +/// Note: The floor function is used as an alias for the rounding function [`round`](#round) /// with rounding mode `RoundDown`. /// ///
@@ -139,7 +139,7 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -182,11 +182,11 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float { /// /// /// -/// 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 +/// 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) +/// Note: The truncate function is used as an alias for the rounding function [`round`](#round) /// with rounding mode `RoundToZero`. /// ///
@@ -198,7 +198,7 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -241,18 +241,18 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// /// /// -/// The function rounds a float to a specific number of digits (after the decimal place or before +/// 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 +/// - `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++ +/// 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\\) +/// - `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 @@ -260,8 +260,8 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// - `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 +/// - `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). /// ///
@@ -273,7 +273,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -285,7 +285,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -309,7 +309,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -321,7 +321,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float { /// - \\(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 +/// 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`) @@ -500,7 +500,7 @@ fn do_ceiling(a: Float) -> Float /// The absolute value: /// /// \\[ -/// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. +/// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. /// \\] /// /// The function takes an input \\(x\\) and returns a positive float value. @@ -529,7 +529,7 @@ pub fn float_absolute_value(x: Float) -> Float { /// The absolute value: /// /// \\[ -/// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. +/// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. /// \\] /// /// The function takes an input \\(x\\) and returns a positive integer value. @@ -709,7 +709,7 @@ fn do_int_sign(a: Int) -> Int /// /// /// -/// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\) +/// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\) /// such that it has the same sign as \\(y\\). /// ///
@@ -735,7 +735,7 @@ pub fn float_copy_sign(x: Float, y: Float) -> Float { /// ///
/// -/// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\) +/// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\) /// such that it has the same sign as \\(y\\). /// ///
@@ -949,11 +949,9 @@ pub fn minmax(x: a, y: a, compare: fn(a, a) -> order.Order) -> #(a, a) { pub fn list_minimum( arr: List(a), compare: fn(a, a) -> order.Order, -) -> Result(a, String) { +) -> Result(a, Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) [x, ..rest] -> Ok( list.fold(rest, x, fn(acc: a, element: a) { @@ -1003,11 +1001,9 @@ pub fn list_minimum( pub fn list_maximum( arr: List(a), compare: fn(a, a) -> order.Order, -) -> Result(a, String) { +) -> Result(a, Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) [x, ..rest] -> Ok( list.fold(rest, x, fn(acc: a, element: a) { @@ -1063,11 +1059,9 @@ pub fn list_maximum( pub fn arg_minimum( arr: List(a), compare: fn(a, a) -> order.Order, -) -> Result(List(Int), String) { +) -> Result(List(Int), Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) _ -> { let assert Ok(min) = arr @@ -1133,11 +1127,9 @@ pub fn arg_minimum( pub fn arg_maximum( arr: List(a), compare: fn(a, a) -> order.Order, -) -> Result(List(Int), String) { +) -> Result(List(Int), Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) _ -> { let assert Ok(max) = arr @@ -1203,11 +1195,9 @@ pub fn arg_maximum( pub fn extrema( arr: List(a), compare: fn(a, a) -> order.Order, -) -> Result(#(a, a), String) { +) -> Result(#(a, a), Nil) { case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error + [] -> Error(Nil) [x, ..rest] -> Ok( list.fold(rest, #(x, x), fn(acc: #(a, a), element: a) { diff --git a/src/gleam_community/maths/predicates.gleam b/src/gleam_community/maths/predicates.gleam index 9cdd74c..37391e9 100644 --- a/src/gleam_community/maths/predicates.gleam +++ b/src/gleam_community/maths/predicates.gleam @@ -20,12 +20,12 @@ //// -//// +//// //// --- -//// -//// Predicates: A module containing functions for testing various mathematical +//// +//// Predicates: A module containing functions for testing various mathematical //// properties of numbers. -//// +//// //// * **Tests** //// * [`is_close`](#is_close) //// * [`list_all_close`](#all_close) @@ -38,7 +38,7 @@ //// * [`is_divisible`](#is_divisible) //// * [`is_multiple`](#is_multiple) //// * [`is_prime`](#is_prime) -//// +//// import gleam/int import gleam/list @@ -54,16 +54,16 @@ import gleam_community/maths/piecewise /// ///
/// -/// Determine if a given value \\(a\\) is close to or equivalent to a reference value +/// 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 +/// 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. +/// `True` is returned if statement holds, otherwise `False` is returned. ///
/// Example /// @@ -135,7 +135,7 @@ fn float_absolute_difference(a: Float, b: Float) -> Float { /// let rtol: Float = 0.01 /// let atol: Float = 0.10 /// predicates.all_close(xarr, yarr, rtol, atol) -/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { +/// |> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) { /// case zarr { /// Ok(arr) -> /// arr @@ -159,13 +159,11 @@ pub fn all_close( yarr: List(Float), rtol: Float, atol: Float, -) -> Result(List(Bool), String) { +) -> Result(List(Bool), Nil) { let xlen: Int = list.length(xarr) let ylen: Int = list.length(yarr) case xlen == ylen { - False -> - "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." - |> Error + False -> Error(Nil) True -> list.zip(xarr, yarr) |> list.map(fn(z: #(Float, Float)) -> Bool { @@ -182,10 +180,10 @@ pub fn all_close( /// /// /// Determine if a given value is fractional. -/// -/// `True` is returned if the given value is fractional, otherwise `False` is -/// returned. -/// +/// +/// `True` is returned if the given value is fractional, otherwise `False` is +/// returned. +/// ///
/// Example /// @@ -195,7 +193,7 @@ pub fn all_close( /// pub fn example () { /// predicates.is_fractional(0.3333) /// |> should.equal(True) -/// +/// /// predicates.is_fractional(1.0) /// |> should.equal(False) /// } @@ -222,7 +220,7 @@ fn do_ceiling(a: Float) -> Float /// /// /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a -/// power of another integer value \\(y \in \mathbb{Z}\\). +/// power of another integer value \\(y \in \mathbb{Z}\\). /// ///
/// Example: @@ -262,9 +260,9 @@ pub fn is_power(x: Int, y: Int) -> Bool { /// /// /// 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 +/// perfect number. A number is perfect if it is equal to the sum of its proper /// positive divisors. -/// +/// ///
/// Details /// @@ -314,7 +312,7 @@ fn do_sum(arr: List(Int)) -> Int { /// /// /// -/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even. +/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even. /// ///
/// Example: @@ -325,7 +323,7 @@ fn do_sum(arr: List(Int)) -> Int { /// pub fn example() { /// predicates.is_even(-3) /// |> should.equal(False) -/// +/// /// predicates.is_even(-4) /// |> should.equal(True) /// } @@ -347,7 +345,7 @@ pub fn is_even(x: Int) -> Bool { /// /// /// -/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd. +/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd. /// ///
/// Example: @@ -358,7 +356,7 @@ pub fn is_even(x: Int) -> Bool { /// pub fn example() { /// predicates.is_odd(-3) /// |> should.equal(True) -/// +/// /// predicates.is_odd(-4) /// |> should.equal(False) /// } @@ -380,24 +378,24 @@ pub fn is_odd(x: Int) -> Bool { /// /// /// -/// 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 +/// 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 +/// +/// 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 +/// iterations (the function uses 64 iterations internally, which is typically /// more than sufficient). The Miller-Rabin test is particularly useful for large /// numbers. -/// +/// ///
/// Details /// /// 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 +/// - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such /// as \\(2\\). /// ///
@@ -414,7 +412,7 @@ pub fn is_odd(x: Int) -> Bool { /// /// predicates.is_prime(4) /// |> should.equal(False) -/// +/// /// // Test the 2nd Carmichael number /// predicates.is_prime(1105) /// |> should.equal(False) @@ -512,9 +510,9 @@ pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool { /// /// /// -/// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another +/// 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 /// @@ -555,9 +553,9 @@ pub fn is_divisible(n: Int, d: Int) -> Bool { /// /// /// -/// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another +/// 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 /// diff --git a/src/gleam_community/maths/sequences.gleam b/src/gleam_community/maths/sequences.gleam index 9ffe6ec..daff369 100644 --- a/src/gleam_community/maths/sequences.gleam +++ b/src/gleam_community/maths/sequences.gleam @@ -20,18 +20,18 @@ //// -//// +//// //// --- -//// -//// Sequences: A module containing functions for generating various types of +//// +//// 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 @@ -47,7 +47,7 @@ import gleam_community/maths/piecewise /// 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. -/// +/// ///
/// Example: /// @@ -59,13 +59,13 @@ import gleam_community/maths/piecewise /// 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) @@ -115,10 +115,10 @@ pub fn arange( /// /// /// -/// 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 +/// 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. -/// +/// ///
/// Example: /// @@ -138,7 +138,7 @@ pub fn arange( /// 0.0, /// tol, /// ) -/// +/// /// result /// |> list.all(fn(x) { x == True }) /// |> should.be_true() @@ -160,7 +160,7 @@ pub fn linear_space( stop: Float, num: Int, endpoint: Bool, -) -> Result(iterator.Iterator(Float), String) { +) -> Result(iterator.Iterator(Float), Nil) { let direction: Float = case start <=. stop { True -> 1.0 False -> -1.0 @@ -184,9 +184,7 @@ pub fn linear_space( }) |> Ok } - False -> - "Invalid input: num < 1. Valid input is num >= 1." - |> Error + False -> Error(Nil) } } @@ -196,10 +194,10 @@ pub fn linear_space( /// /// /// -/// 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, +/// 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. -/// +/// ///
/// Example: /// @@ -241,7 +239,7 @@ pub fn logarithmic_space( num: Int, endpoint: Bool, base: Float, -) -> Result(iterator.Iterator(Float), String) { +) -> Result(iterator.Iterator(Float), Nil) { case num > 0 { True -> { let assert Ok(linspace) = linear_space(start, stop, num, endpoint) @@ -252,9 +250,7 @@ pub fn logarithmic_space( }) |> Ok } - False -> - "Invalid input: num < 1. Valid input is num >= 1." - |> Error + False -> Error(Nil) } } @@ -264,9 +260,9 @@ pub fn logarithmic_space( /// /// /// -/// 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 +/// 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. /// ///
@@ -295,7 +291,7 @@ pub fn logarithmic_space( /// // 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() /// @@ -316,11 +312,9 @@ pub fn geometric_space( stop: Float, num: Int, endpoint: Bool, -) -> Result(iterator.Iterator(Float), String) { +) -> Result(iterator.Iterator(Float), Nil) { case start == 0.0 || stop == 0.0 { - True -> - "Invalid input: Neither 'start' nor 'stop' can be zero, as they must be non-zero for logarithmic calculations." - |> Error + True -> Error(Nil) False -> case num > 0 { True -> { @@ -328,9 +322,7 @@ pub fn geometric_space( let assert Ok(log_stop) = elementary.logarithm_10(stop) logarithmic_space(log_start, log_stop, num, endpoint, 10.0) } - False -> - "Invalid input: num < 1. Valid input is num >= 1." - |> Error + False -> Error(Nil) } } } diff --git a/src/gleam_community/maths/special.gleam b/src/gleam_community/maths/special.gleam index f6e3438..ae5c8f6 100644 --- a/src/gleam_community/maths/special.gleam +++ b/src/gleam_community/maths/special.gleam @@ -20,17 +20,17 @@ //// -//// +//// //// --- -//// +//// //// 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 @@ -100,7 +100,7 @@ pub fn erf(x: Float) -> Float { /// /// /// -/// The gamma function over the real numbers. The function is essentially equal to +/// 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 @@ -163,7 +163,7 @@ fn gamma_lanczos(x: Float) -> Float { /// /// /// -pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, String) { +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) @@ -173,9 +173,7 @@ pub fn incomplete_gamma(a: Float, x: Float) -> Result(Float, String) { |> Ok } - False -> - "Invalid input argument: a <= 0 or x < 0. Valid input is a > 0 and x >= 0." - |> Error + False -> Error(Nil) } } diff --git a/test/gleam_community/maths/predicates_test.gleam b/test/gleam_community/maths/predicates_test.gleam index fcae19c..955e46e 100644 --- a/test/gleam_community/maths/predicates_test.gleam +++ b/test/gleam_community/maths/predicates_test.gleam @@ -23,7 +23,7 @@ pub fn float_list_all_close_test() { let rtol: Float = 0.01 let atol: Float = 0.1 predicates.all_close(xarr, yarr, rtol, atol) - |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { + |> fn(zarr: Result(List(Bool), Nil)) -> Result(Bool, Nil) { case zarr { Ok(arr) -> arr