From 6419c400b9097407c6cc61970768fcf14fff0e77 Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Sat, 25 May 2024 01:42:39 +0100 Subject: [PATCH 1/3] :heavy_plus_sign: Update stdlib dependency to ~> 0.38 --- gleam.toml | 2 +- manifest.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gleam.toml b/gleam.toml index ff28f07..a84ada2 100644 --- a/gleam.toml +++ b/gleam.toml @@ -7,7 +7,7 @@ repository = { type = "github", user = "gleam-community", repo = "maths" } gleam = ">= 0.32.0" [dependencies] -gleam_stdlib = "~> 0.34" +gleam_stdlib = "~> 0.38" [dev-dependencies] gleeunit = "~> 1.0" diff --git a/manifest.toml b/manifest.toml index 6c5a157..c3d0b0d 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,10 +2,10 @@ # You typically do not need to edit this file packages = [ - { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, - { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, ] [requirements] -gleam_stdlib = { version = "~> 0.34" } +gleam_stdlib = { version = "~> 0.38" } gleeunit = { version = "~> 1.0" } From ae959dcafecf6f5c123d444efbc5a1052484e2ab Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Sat, 25 May 2024 01:42:46 +0100 Subject: [PATCH 2/3] :recycle: Remove uses of list.at --- src/gleam_community/maths/metrics.gleam | 119 +++++++++++----------- src/gleam_community/maths/piecewise.gleam | 111 ++++++++++---------- 2 files changed, 110 insertions(+), 120 deletions(-) diff --git a/src/gleam_community/maths/metrics.gleam b/src/gleam_community/maths/metrics.gleam index e35e336..725691b 100644 --- a/src/gleam_community/maths/metrics.gleam +++ b/src/gleam_community/maths/metrics.gleam @@ -20,11 +20,11 @@ //// -//// +//// //// --- -//// +//// //// Metrics: A module offering functions for calculating distances and other types of metrics. -//// +//// //// * **Distances** //// * [`norm`](#norm) //// * [`manhatten_distance`](#float_manhatten_distance) @@ -35,16 +35,15 @@ //// * [`median`](#median) //// * [`variance`](#variance) //// * [`standard_deviation`](#standard_deviation) -//// +//// -import gleam_community/maths/elementary -import gleam_community/maths/piecewise -import gleam_community/maths/arithmetics -import gleam_community/maths/predicates -import gleam_community/maths/conversion +import gleam/bool import gleam/list import gleam/pair -import gleam/float +import gleam_community/maths/arithmetics +import gleam_community/maths/conversion +import gleam_community/maths/elementary +import gleam_community/maths/piecewise ///
/// @@ -130,15 +129,15 @@ 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 /// metrics.float_manhatten_distance([], []) /// |> should.equal(Ok(0.0)) -/// +/// /// // Differing lengths returns error /// metrics.manhatten_distance([], [1.0]) /// |> should.be_error() -/// +/// /// let assert Ok(result) = metrics.manhatten_distance([0.0, 0.0], [1.0, 2.0]) /// result /// |> predicates.is_close(3.0, 0.0, tol) @@ -185,23 +184,23 @@ pub fn manhatten_distance( /// /// pub fn example () { /// let assert Ok(tol) = elementary.power(-10.0, -6.0) -/// +/// /// // Empty lists returns 0.0 /// metrics.minkowski_distance([], [], 1.0) /// |> should.equal(Ok(0.0)) -/// +/// /// // Differing lengths returns error /// metrics.minkowski_distance([], [1.0], 1.0) /// |> should.be_error() -/// +/// /// // Test order < 1 /// metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0) /// |> should.be_error() -/// +/// /// let assert Ok(result) = metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0) /// result /// |> predicates.is_close(3.0, 0.0, tol) -/// |> should.be_true() +/// |> should.be_true() /// } /// /// @@ -262,15 +261,15 @@ pub fn minkowski_distance( /// /// pub fn example () { /// let assert Ok(tol) = elementary.power(-10.0, -6.0) -/// +/// /// // Empty lists returns 0.0 /// metrics.euclidean_distance([], []) /// |> should.equal(Ok(0.0)) -/// +/// /// // Differing lengths returns error /// metrics.euclidean_distance([], [1.0]) /// |> should.be_error() -/// +/// /// let assert Ok(result) = metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0]) /// result /// |> predicates.is_close(2.23606797749979, 0.0, tol) @@ -303,7 +302,7 @@ pub fn euclidean_distance( /// \bar{x} = \frac{1}{n}\sum_{i=1}^n x_i /// \\] /// -/// In the formula, $$n$$ is the sample size (the length of the list) and +/// 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$$. /// ///
@@ -370,7 +369,7 @@ pub fn mean(arr: List(Float)) -> Result(Float, String) { /// [1., 2., 3.] /// |> metrics.median() /// |> should.equal(Ok(2.)) -/// +/// /// [1., 2., 3., 4.] /// |> metrics.median() /// |> should.equal(Ok(2.5)) @@ -383,33 +382,31 @@ pub fn mean(arr: List(Float)) -> Result(Float, String) { /// ///
/// -pub fn median(arr: List(Float)) -> Result(Float, String) { - case arr { - [] -> - "Invalid input argument: The list is empty." - |> Error - _ -> { - let count: Int = list.length(arr) - let mid: Int = list.length(arr) / 2 - let sorted: List(Float) = list.sort(arr, float.compare) - case predicates.is_odd(count) { - // If there is an odd number of elements in the list, then the median - // is just the middle value - True -> { - let assert Ok(val0) = list.at(sorted, mid) - val0 - |> Ok - } - // If there is an even number of elements in the list, then the median - // is the mean of the two middle values - False -> { - let assert Ok(val0) = list.at(sorted, mid - 1) - let assert Ok(val1) = list.at(sorted, mid) - [val0, val1] - |> mean() - } - } - } +pub fn median(arr: List(Float)) -> Result(Float, Nil) { + use <- bool.guard(list.is_empty(arr), Error(Nil)) + let length = list.length(arr) + let mid = length / 2 + + case length % 2 == 0 { + True -> do_median(arr, mid, True, 0) + False -> do_median(arr, mid, False, 0) + } +} + +fn do_median( + xs: List(Float), + mid: Int, + mean: Bool, + index: Int, +) -> Result(Float, Nil) { + use <- bool.guard(index > mid, Error(Nil)) + let mid_less_one = mid - 1 + + case xs { + [x, ..] if !mean && index == mid -> Ok(x) + [x, y, ..] if mean && index == mid_less_one -> Ok({ x +. y } /. 2.0) + [_, ..rest] -> do_median(rest, mid, mean, index + 1) + [] -> Error(Nil) } } @@ -424,9 +421,9 @@ pub fn median(arr: List(Float)) -> Result(Float, String) { /// 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. /// @@ -439,12 +436,12 @@ pub fn median(arr: List(Float)) -> Result(Float, String) { /// pub fn example () { /// // Degrees of freedom /// let ddof: Int = 1 -/// +/// /// // An empty list returns an error /// [] /// |> metrics.variance(ddof) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [1., 2., 3.] /// |> metrics.variance(ddof) @@ -500,9 +497,9 @@ 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. /// @@ -515,12 +512,12 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) { /// pub fn example () { /// // Degrees of freedom /// let ddof: Int = 1 -/// +/// /// // An empty list returns an error /// [] /// |> metrics.standard_deviationddof) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [1., 2., 3.] /// |> metrics.standard_deviation(ddof) @@ -547,7 +544,7 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String) False -> { let assert Ok(variance) = variance(arr, ddof) // The computed variance will always be positive - // So an error should never be returned + // So an error should never be returned let assert Ok(stdev) = elementary.square_root(variance) stdev |> Ok diff --git a/src/gleam_community/maths/piecewise.gleam b/src/gleam_community/maths/piecewise.gleam index c8492da..da48e0f 100644 --- a/src/gleam_community/maths/piecewise.gleam +++ b/src/gleam_community/maths/piecewise.gleam @@ -20,11 +20,11 @@ //// -//// +//// //// --- -//// +//// //// Piecewise: A module containing functions that have different definitions depending on conditions or intervals of their domain. -//// +//// //// * **Rounding functions** //// * [`ceiling`](#ceiling) //// * [`floor`](#floor) @@ -52,11 +52,11 @@ //// * [`arg_maximum`](#arg_maximum) //// -import gleam/option +import gleam/int import gleam/list +import gleam/option import gleam/order import gleam/pair -import gleam/int import gleam_community/maths/conversion import gleam_community/maths/elementary @@ -66,7 +66,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$$. +/// The ceiling function rounds a given input value $$x \in \mathbb{R}$$ to the nearest integer value (at the specified digit) that is larger than or equal to the input $$x$$. /// /// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) with rounding mode `RoundUp`. /// @@ -325,7 +325,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Result(Float, String) { /// piecewise.round(12.0654, option.None, option.Some(piecewise.RoundNearest)) /// |> should.equal(Ok(12.0)) /// -/// // The default rounding mode is "RoundNearest" if None is provided +/// // The default rounding mode is "RoundNearest" if None is provided /// piecewise.round(12.0654, option.None, option.None) /// |> should.equal(Ok(12.0)) /// @@ -490,7 +490,7 @@ fn do_ceiling(a: Float) -> Float /// The absolute value: /// /// \\[ -/// \forall x, y \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. +/// \forall x, y \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. /// \\] /// /// The function takes an input $$x$$ and returns a positive float value. @@ -519,7 +519,7 @@ pub fn float_absolute_value(x: Float) -> Float { /// The absolute value: /// /// \\[ -/// \forall x, y \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. +/// \forall x, y \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. /// \\] /// /// The function takes an input $$x$$ and returns a positive integer value. @@ -548,7 +548,7 @@ pub fn int_absolute_value(x: Int) -> Int { /// The absolute difference: /// /// \\[ -/// \forall x, y \in \mathbb{R}, \\; |x - y| \in \mathbb{R}_{+}. +/// \forall x, y \in \mathbb{R}, \\; |x - y| \in \mathbb{R}_{+}. /// \\] /// /// The function takes two inputs $$x$$ and $$y$$ and returns a positive float @@ -589,7 +589,7 @@ pub fn float_absolute_difference(a: Float, b: Float) -> Float { /// The absolute difference: /// /// \\[ -/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}. +/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}. /// \\] /// /// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer value which is the the absolute difference of the inputs. @@ -627,7 +627,7 @@ pub fn int_absolute_difference(a: Int, b: Int) -> Int { /// /// /// The function takes an input $$x \in \mathbb{R}$$ and returns the sign of -/// the input, indicating whether it is positive (+1.0), negative (-1.0), or +/// the input, indicating whether it is positive (+1.0), negative (-1.0), or /// zero (0.0). /// ///
@@ -871,7 +871,7 @@ pub fn maximum(x: a, y: a, compare: fn(a, a) -> order.Order) -> a { ///
/// /// The minmax function takes two arguments $$x, y$$ along with a function -/// for comparing $$x, y$$. The function returns a tuple with the smallest +/// for comparing $$x, y$$. The function returns a tuple with the smallest /// value first and largest second. /// ///
@@ -906,7 +906,7 @@ pub fn minmax(x: a, y: a, compare: fn(a, a) -> order.Order) -> #(a, a) { /// /// /// -/// Returns the minimum value of a given list. +/// Returns the minimum value of a given list. /// ///
/// Example: @@ -920,7 +920,7 @@ pub fn minmax(x: a, y: a, compare: fn(a, a) -> order.Order) -> #(a, a) { /// [] /// |> piecewise.list_minimum(int.compare) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [4, 4, 3, 2, 1] /// |> piecewise.list_minimum(int.compare) @@ -941,17 +941,15 @@ pub fn list_minimum( [] -> "Invalid input argument: The list is empty." |> Error - _ -> { - let assert Ok(val0) = list.at(arr, 0) - arr - |> list.fold(val0, fn(acc: a, element: a) { - case compare(element, acc) { - order.Lt -> element - _ -> acc - } - }) - |> Ok - } + [x, ..rest] -> + Ok( + list.fold(rest, x, fn(acc: a, element: a) { + case compare(element, acc) { + order.Lt -> element + _ -> acc + } + }), + ) } } @@ -961,7 +959,7 @@ pub fn list_minimum( /// /// /// -/// Returns the maximum value of a given list. +/// Returns the maximum value of a given list. /// ///
/// Example: @@ -997,17 +995,15 @@ pub fn list_maximum( [] -> "Invalid input argument: The list is empty." |> Error - _ -> { - let assert Ok(val0) = list.at(arr, 0) - arr - |> list.fold(val0, fn(acc: a, element: a) { - case compare(acc, element) { - order.Lt -> element - _ -> acc - } - }) - |> Ok - } + [x, ..rest] -> + Ok( + list.fold(rest, x, fn(acc: a, element: a) { + case compare(acc, element) { + order.Lt -> element + _ -> acc + } + }), + ) } } @@ -1023,7 +1019,7 @@ pub fn list_maximum( /// /// /// -/// Returns the indices of the minimum values in a given list. +/// Returns the indices of the minimum values in a given list. /// ///
/// Example: @@ -1037,7 +1033,7 @@ pub fn list_maximum( /// [] /// |> piecewise.arg_minimum(float.compare) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> piecewise.arg_minimum(float.compare) @@ -1107,7 +1103,7 @@ pub fn arg_minimum( /// [] /// |> piecewise.arg_maximum(float.compare) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> piecewise.arg_maximum(float.compare) @@ -1163,7 +1159,7 @@ pub fn arg_maximum( /// /// /// -/// Returns a tuple consisting of the minimum and maximum values of a given list. +/// Returns a tuple consisting of the minimum and maximum values of a given list. /// ///
/// Example: @@ -1177,7 +1173,7 @@ pub fn arg_maximum( /// [] /// |> piecewise.extrema(float.compare) /// |> should.be_error() -/// +/// /// // Valid input returns a result /// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> piecewise.extrema(float.compare) @@ -1199,21 +1195,18 @@ pub fn extrema( [] -> "Invalid input argument: The list is empty." |> Error - _ -> { - let assert Ok(val_max) = list.at(arr, 0) - let assert Ok(val_min) = list.at(arr, 0) - arr - |> list.fold(#(val_min, val_max), fn(acc: #(a, a), element: a) { - let first: a = pair.first(acc) - let second: a = pair.second(acc) - case compare(element, first), compare(second, element) { - order.Lt, order.Lt -> #(element, element) - order.Lt, _ -> #(element, second) - _, order.Lt -> #(first, element) - _, _ -> #(first, second) - } - }) - |> Ok - } + [x, ..rest] -> + Ok( + list.fold(rest, #(x, x), fn(acc: #(a, a), element: a) { + let first: a = pair.first(acc) + let second: a = pair.second(acc) + case compare(element, first), compare(second, element) { + order.Lt, order.Lt -> #(element, element) + order.Lt, _ -> #(element, second) + _, order.Lt -> #(first, element) + _, _ -> #(first, second) + } + }), + ) } } From b0b9e728ea46c1c23331b9790510be0d16defded Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Sat, 25 May 2024 01:43:10 +0100 Subject: [PATCH 3/3] :bookmark: Bump to v1.1.1 --- gleam.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gleam.toml b/gleam.toml index a84ada2..df0dc71 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "gleam_community_maths" -version = "1.1.0" +version = "1.1.1" licences = ["Apache-2.0"] description = "A basic maths library"