diff --git a/src/gleam_community/maths/arithmetics.gleam b/src/gleam_community/maths/arithmetics.gleam index a238abc..dbd3ffe 100644 --- a/src/gleam_community/maths/arithmetics.gleam +++ b/src/gleam_community/maths/arithmetics.gleam @@ -289,7 +289,7 @@ pub fn proper_divisors(n: Int) -> List(Int) { /// /// /// -/// Calculcate the sum of the elements in a list: +/// Calculate the sum of the elements in a list: /// /// \\[ /// \sum_{i=1}^n x_i @@ -337,7 +337,7 @@ pub fn float_sum(arr: List(Float)) -> Float { /// /// /// -/// Calculcate the sum of the elements in a list: +/// Calculate the sum of the elements in a list: /// /// \\[ /// \sum_{i=1}^n x_i @@ -385,7 +385,7 @@ pub fn int_sum(arr: List(Int)) -> Int { /// /// /// -/// Calculcate the product of the elements in a list: +/// Calculate the product of the elements in a list: /// /// \\[ /// \prod_{i=1}^n x_i @@ -433,7 +433,7 @@ pub fn float_product(arr: List(Float)) -> Float { /// /// /// -/// Calculcate the product of the elements in a list: +/// Calculate the product of the elements in a list: /// /// \\[ /// \prod_{i=1}^n x_i @@ -481,7 +481,7 @@ pub fn int_product(arr: List(Int)) -> Int { /// /// /// -/// Calculcate the cumulative sum of the elements in a list: +/// Calculate the cumulative sum of the elements in a list: /// /// \\[ /// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n @@ -530,7 +530,7 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) { /// /// /// -/// Calculcate the cumulative sum of the elements in a list: +/// Calculate the cumulative sum of the elements in a list: /// /// \\[ /// v_j = \sum_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n @@ -579,7 +579,7 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) { /// /// /// -/// Calculcate the cumulative product of the elements in a list: +/// Calculate the cumulative product of the elements in a list: /// /// \\[ /// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n @@ -629,7 +629,7 @@ pub fn float_cumumlative_product(arr: List(Float)) -> List(Float) { /// /// /// -/// Calculcate the cumulative product of the elements in a list: +/// Calculate the cumulative product of the elements in a list: /// /// \\[ /// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n diff --git a/src/gleam_community/maths/metrics.gleam b/src/gleam_community/maths/metrics.gleam index 772b80d..8b204cb 100644 --- a/src/gleam_community/maths/metrics.gleam +++ b/src/gleam_community/maths/metrics.gleam @@ -28,8 +28,9 @@ //// * **Distance measures** //// * [`norm`](#norm) //// * [`manhatten_distance`](#manhatten_distance) -//// * [`minkowski_distance`](#minkowski_distance) //// * [`euclidean_distance`](#euclidean_distance) +//// * [`chebyshev_distance`](#chebyshev_distance) +//// * [`minkowski_distance`](#minkowski_distance) //// * [`cosine_similarity`](#cosine_similarity) //// * **Set & string similarity measures** //// * [`jaccard_index`](#jaccard_index) @@ -60,7 +61,7 @@ import gleam/int /// /// /// -/// Calculcate the $$p$$-norm of a list (representing a vector): +/// Calculate the $$p$$-norm of a list (representing a vector): /// /// \\[ /// \left( \sum_{i=1}^n \left|x_i\right|^{p} \right)^{\frac{1}{p}} @@ -120,13 +121,13 @@ pub fn norm(arr: List(Float), p: Float) -> Float { /// /// /// -/// Calculcate the Manhatten distance between two lists (representing vectors): +/// Calculate the Manhatten distance between two lists (representing vectors): /// /// \\[ /// \sum_{i=1}^n \left|x_i - y_i \right| /// \\] /// -/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$. +/// 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$$. /// ///
/// Example: @@ -139,9 +140,9 @@ pub fn norm(arr: List(Float), p: Float) -> Float { /// pub fn example () { /// let assert Ok(tol) = elementary.power(-10.0, -6.0) /// -/// // Empty lists returns 0.0 +/// // Empty lists returns an error /// metrics.manhatten_distance([], []) -/// |> should.equal(Ok(0.0)) +/// |> should.be_error() /// /// // Differing lengths returns error /// metrics.manhatten_distance([], [1.0]) @@ -173,13 +174,13 @@ pub fn manhatten_distance( /// /// /// -/// Calculcate the Minkowski distance between two lists (representing vectors): +/// Calculate the Minkowski distance between two lists (representing vectors): /// /// \\[ /// \left( \sum_{i=1}^n \left|x_i - y_i \right|^{p} \right)^{\frac{1}{p}} /// \\] /// -/// In the formula, $$p >= 1$$ is the order, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$. +/// 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 both the Euclidean distance ($$p=2$$) and the Manhattan distance ($$p = 1$$). /// @@ -196,7 +197,7 @@ pub fn manhatten_distance( /// /// // Empty lists returns 0.0 /// metrics.minkowski_distance([], [], 1.0) -/// |> should.equal(Ok(0.0)) +/// |> should.be_error() /// /// // Differing lengths returns error /// metrics.minkowski_distance([], [1.0], 1.0) @@ -224,25 +225,35 @@ pub fn minkowski_distance( yarr: List(Float), p: Float, ) -> Result(Float, String) { - let xlen: Int = list.length(xarr) - let ylen: Int = list.length(yarr) - case xlen == ylen { - False -> - "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." + case xarr, yarr { + [], _ -> + "Invalid input argument: The list xarr is empty." |> Error - True -> - case p <. 1.0 { - True -> - "Invalid input argument: p < 1. Valid input is p >= 1." - |> Error + _, [] -> + "Invalid input argument: The list yarr is empty." + |> Error + _, _ -> { + let xlen: Int = list.length(xarr) + let ylen: Int = list.length(yarr) + case xlen == ylen { False -> - list.zip(xarr, yarr) - |> list.map(fn(tuple: #(Float, Float)) -> Float { - pair.first(tuple) -. pair.second(tuple) - }) - |> norm(p) - |> Ok + "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." + |> Error + True -> + case p <. 1.0 { + True -> + "Invalid input argument: p < 1. Valid input is p >= 1." + |> Error + False -> + list.zip(xarr, yarr) + |> list.map(fn(tuple: #(Float, Float)) -> Float { + pair.first(tuple) -. pair.second(tuple) + }) + |> norm(p) + |> Ok + } } + } } } @@ -252,13 +263,13 @@ pub fn minkowski_distance( /// /// /// -/// Calculcate the Euclidean distance between two lists (representing vectors): +/// 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, j$$. +/// 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$$. /// ///
/// Example: @@ -273,7 +284,7 @@ pub fn minkowski_distance( /// /// // Empty lists returns 0.0 /// metrics.euclidean_distance([], []) -/// |> should.equal(Ok(0.0)) +/// |> should.be_error() /// /// // Differing lengths returns error /// metrics.euclidean_distance([], [1.0]) @@ -305,7 +316,82 @@ pub fn euclidean_distance( /// /// /// -/// Calculcate the arithmetic mean of the elements in a list: +/// 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$$. +/// +///
+/// Example: +/// +/// 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)) +/// } +///
+/// +///
+/// +/// Back to top ↑ +/// +///
+/// +pub fn chebyshev_distance( + xarr: List(Float), + yarr: List(Float), +) -> Result(Float, String) { + case xarr, yarr { + [], _ -> + "Invalid input argument: The list xarr is empty." + |> Error + _, [] -> + "Invalid input argument: The list yarr is empty." + |> Error + _, _ -> { + let xlen: Int = list.length(xarr) + let ylen: Int = list.length(yarr) + case xlen == ylen { + False -> + "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." + |> Error + True -> { + let differences = + list.zip(xarr, yarr) + |> list.map(fn(tuple: #(Float, Float)) -> Float { + { pair.first(tuple) -. pair.second(tuple) } + |> piecewise.float_absolute_value() + }) + differences + |> piecewise.list_maximum(float.compare) + } + } + } + } +} + +///
+/// +/// Spot a typo? Open an issue! +/// +///
+/// +/// Calculate the arithmetic mean of the elements in a list: /// /// \\[ /// \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i @@ -360,7 +446,7 @@ pub fn mean(arr: List(Float)) -> Result(Float, String) { /// /// /// -/// Calculcate the median of the elements in a list. +/// Calculate the median of the elements in a list. /// ///
/// Example: @@ -427,7 +513,7 @@ pub fn median(arr: List(Float)) -> Result(Float, String) { /// /// /// -/// Calculcate the sample variance of the elements in a list: +/// Calculate the sample variance of the elements in a list: /// \\[ /// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x}) /// \\] @@ -503,7 +589,7 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) { /// /// /// -/// Calculcate the sample standard deviation of the elements in a list: +/// 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}} /// \\] @@ -858,8 +944,6 @@ pub fn cosine_similarity( xarr: List(Float), yarr: List(Float), ) -> Result(Float, String) { - let xlen: Int = list.length(xarr) - let ylen: Int = list.length(yarr) case xarr, yarr { [], _ -> "Invalid input argument: The list xarr is empty." @@ -868,6 +952,8 @@ pub fn cosine_similarity( "Invalid input argument: The list yarr is empty." |> Error _, _ -> { + 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)." diff --git a/src/gleam_community/maths/predicates.gleam b/src/gleam_community/maths/predicates.gleam index ca81d33..c5d5f01 100644 --- a/src/gleam_community/maths/predicates.gleam +++ b/src/gleam_community/maths/predicates.gleam @@ -33,6 +33,7 @@ //// * [`is_perfect`](#is_perfect) //// * [`is_even`](#is_even) //// * [`is_odd`](#is_odd) +//// * [`is_prime`](#is_prime) import gleam/pair import gleam/int diff --git a/test/gleam_community/maths/metrics_test.gleam b/test/gleam_community/maths/metrics_test.gleam index e2f7307..4ba9cd9 100644 --- a/test/gleam_community/maths/metrics_test.gleam +++ b/test/gleam_community/maths/metrics_test.gleam @@ -53,9 +53,9 @@ pub fn float_list_norm_test() { pub fn float_list_manhatten_test() { let assert Ok(tol) = elementary.power(-10.0, -6.0) - // Empty lists returns 0.0 + // Empty lists returns an error metrics.manhatten_distance([], []) - |> should.equal(Ok(0.0)) + |> should.be_error() // Differing lengths returns error metrics.manhatten_distance([], [1.0]) @@ -85,9 +85,9 @@ pub fn float_list_manhatten_test() { pub fn float_list_minkowski_test() { let assert Ok(tol) = elementary.power(-10.0, -6.0) - // Empty lists returns 0.0 + // Empty lists returns an error metrics.minkowski_distance([], [], 1.0) - |> should.equal(Ok(0.0)) + |> should.be_error() // Differing lengths returns error metrics.minkowski_distance([], [1.0], 1.0) @@ -141,9 +141,9 @@ pub fn float_list_minkowski_test() { pub fn float_list_euclidean_test() { let assert Ok(tol) = elementary.power(-10.0, -6.0) - // Empty lists returns 0.0 + // Empty lists returns an error metrics.euclidean_distance([], []) - |> should.equal(Ok(0.0)) + |> should.be_error() // Differing lengths returns error metrics.euclidean_distance([], [1.0]) @@ -291,7 +291,7 @@ pub fn example_cosine_similarity_test() { metrics.cosine_similarity([], [1.0, 2.0, 3.0]) |> should.be_error() - // Differen sized lists returns an error + // Different sized lists returns an error metrics.cosine_similarity([1.0, 2.0], [1.0, 2.0, 3.0, 4.0]) |> should.be_error() @@ -307,3 +307,34 @@ pub fn example_cosine_similarity_test() { metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0]) |> should.equal(Ok(-1.0)) } + +pub fn example_chebyshev_distance() { + // 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(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)) +} diff --git a/test/gleam_community/maths/predicates_test.gleam b/test/gleam_community/maths/predicates_test.gleam index 4130aab..b2e6d57 100644 --- a/test/gleam_community/maths/predicates_test.gleam +++ b/test/gleam_community/maths/predicates_test.gleam @@ -138,6 +138,10 @@ pub fn int_is_perfect_test() { } 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)