From 35863afec33ee974345ee7f27f47e85ee070f4a1 Mon Sep 17 00:00:00 2001 From: NicklasXYZ Date: Sun, 22 Jan 2023 00:13:58 +0100 Subject: [PATCH] Work on all modules --- src/gleam_community/maths/float.gleam | 526 ++++++++++-------- src/gleam_community/maths/float_list.gleam | 61 +- src/gleam_community/maths/int.gleam | 92 +-- src/gleam_community/maths/int_list.gleam | 122 ++-- src/gleam_community/maths/list.gleam | 14 +- temp.gleam | 410 -------------- ...leam_community_maths_float_list_test.gleam | 94 ++++ .../gleam_community_maths_float_test.gleam | 263 ++++++++- .../gleam_community_maths_int_list_test.gleam | 70 +++ .../gleam_community_maths_int_test.gleam | 14 + 10 files changed, 875 insertions(+), 791 deletions(-) delete mode 100644 temp.gleam create mode 100644 test/gleam/gleam_community_maths_float_list_test.gleam create mode 100644 test/gleam/gleam_community_maths_int_list_test.gleam diff --git a/src/gleam_community/maths/float.gleam b/src/gleam_community/maths/float.gleam index 8ffc1bc..479ad82 100644 --- a/src/gleam_community/maths/float.gleam +++ b/src/gleam_community/maths/float.gleam @@ -19,8 +19,7 @@ //// .katex { font-size: 1.1em; } //// //// -//// A module containing several different kinds of mathematical functions and constants -//// that apply to real numbers (floats). +//// A module containing mathematical functions and constants that apply to real numbers (floats). //// //// --- //// @@ -92,35 +91,42 @@ import gleam/option /// /// /// The ceiling function rounds a given input value $$x \in \mathbb{R}$$ towards $$+\infty$$ at a specified number of digits. -/// For example, $$12.0654$$ is rounded to: -/// - $$1000.0$$ at the 3rd digit before the decimal point (`digit = -3`) -/// - $$100.0$$ at the 2nd digit before the decimal point (`digit = -2`) -/// - $$20.0$$ the 1st digit before the decimal point (`digit = -1`) -/// - $$13.0$ at the 0th digit before the decimal point (`digit = 0`) -/// - $$12.1$$ at the 1st digit after the decimal point (`digit = 1`) -/// - $$12.07$$ at the 2nd digit after the decimal point (`digit = 2`) -/// - $$12.066$$ at the 3rd digit after the decimal point (`digit = 3`) /// -/// See also the concrete code example below. -/// -/// Note: The ceiling function is used as an alias for the rounding function `round` with rounding mode `"Up"`. +/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) with rounding mode `"Up"`. /// ///
-/// Example: +/// Details +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// For example, $$12.0654$$ is rounded to: +/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) +/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +///
+/// +///
+/// Example /// /// import gleeunit/should /// import gleam/option /// import gleam_community/maths/float as floatx /// /// pub fn example() { -/// floatx.ceiling(12.0654, option.Some(3)) -/// |> should.equal(12.066) +/// floatx.ceiling(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.1)) /// /// floatx.ceiling(12.0654, option.Some(2)) -/// |> should.equal(12.07) +/// |> should.equal(Ok(12.07)) /// -/// floatx.ceiling(12.0654, option.Some(1)) -/// |> should.equal(12.1) +/// floatx.ceiling(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.066)) /// } ///
/// @@ -141,35 +147,41 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Result(Float, String) { /// /// /// The floor function rounds a given input value $$x \in \mathbb{R}$$ towards $$-\infty$$ at a specified number of digits. -/// For example, $$12.0654$$ is rounded to: -/// - $$0.0$$ at the 3rd digit before the decimal point (`digit = -3`) -/// - $$0.0$$ at the 2nd digit before the decimal point (`digit = -2`) -/// - $$10.0$$ the 1st digit before the decimal point (`digit = -1`) -/// - $$12.0$ at the 0th digit before the decimal point (`digit = 0`) -/// - $$12.0$$ at the 1st digit after the decimal point (`digit = 1`) -/// - $$12.06$$ at the 2nd digit after the decimal point (`digit = 2`) -/// - $$12.065$$ at the 3rd digit after the decimal point (`digit = 3`) /// -/// See also the concrete code example below. -/// -/// Note: The floor function is used as an alias for the rounding function `round` with rounding mode `"Down"`. +/// Note: The floor function is used as an alias for the rounding function [`round`](#round) with rounding mode `"Down"`. /// ///
-/// Example: +/// Details +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +///
+/// +///
+/// Example /// /// import gleeunit/should /// import gleam/option /// import gleam_community/maths/float as floatx /// /// pub fn example() { -/// floatx.ceiling(12.0654, option.Some(3)) -/// |> should.equal(12.065) +/// floatx.floor(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.0)) /// -/// floatx.ceiling(12.0654, option.Some(2)) -/// |> should.equal(12.06) +/// floatx.floor(12.0654, option.Some(2)) +/// |> should.equal(Ok(12.06)) /// -/// floatx.ceiling(12.0654, option.Some(1)) -/// |> should.equal(12.0) +/// floatx.floor(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.065)) /// } ///
/// @@ -189,26 +201,42 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Result(Float, String) { /// /// /// +/// The truncate function rounds a given input value $$x \in \mathbb{R}$$ towards $$0$$ at a specified number of digits. /// +/// Note: The truncate function is used as an alias for the rounding function [`round`](#round) with rounding mode `"ToZero"`. /// ///
-/// Example: +/// Details +/// +/// For example, $$12.0654$$ is rounded to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +///
+/// +///
+/// Example /// /// import gleeunit/should +/// import gleam/option /// import gleam_community/maths/float as floatx /// /// pub fn example() { -/// floatx.round(0.4444, 2) -/// |> should.equal(0.44) +/// floatx.truncate(12.0654, option.Some(1)) +/// |> should.equal(Ok(12.0)) /// -/// floatx.round(0.4445, 2) -/// |> should.equal(0.44) +/// floatx.truncate(12.0654, option.Some(2)) +/// |> should.equal(Ok(12.0)) /// -/// floatx.round(0.4455, 2) -/// |> should.equal(0.45) -/// -/// floatx.round(0.4555, 2) -/// |> should.equal(0.46) +/// floatx.truncate(12.0654, option.Some(3)) +/// |> should.equal(Ok(12.0)) /// } ///
/// @@ -218,8 +246,8 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Result(Float, String) { /// /// /// -pub fn truncate(x: Float, digits: Int) -> Result(Float, String) { - round(x, option.Some(digits), option.Some("ToZero")) +pub fn truncate(x: Float, digits: option.Option(Int)) -> Result(Float, String) { + round(x, digits, option.Some("ToZero")) } ///
@@ -228,49 +256,125 @@ pub fn truncate(x: Float, digits: Int) -> Result(Float, String) { /// ///
/// -/// The function rounds a floating point number to a specific number of digits using a given rounding mode. +/// 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: -/// - `Nearest` (default): If the last digit is equal to 5, then the previous digit is rounded to nearest even integer value. -/// - `TiesAway`: If the last digit is equal to 5, then the previous digit is rounded away from zero (C/C++ rounding behavior). -/// - `TiesUp`: If the last digit is equal to 5, then the previous digit is rounded towards $$+\infty$$ (Java/JavaScript rounding behaviour). -/// - `ToZero`: The last digit is rounded towards $$0$$. -/// - An alias for this rounding mode is [`truncate`](#truncate) -/// - `Down`: If the last digit larger than 0, then the previous digit is rounded towards $$-\infty$$. -/// - An alias for this rounding mode is [`floor`](#floor) -/// - `Up`: If the last digit is larger than 0, then the previous digit is rounded towards $$+\infty$$. -/// - An alias for this rounding mode is [`ceiling`](#ceiling) -/// -/// Valid rounding modes include: -/// - `Nearest` (default): The specified digit is rounded to nearest even integer value if the following digit + 1 is equal to 5. -/// - `TiesAway`: The specified digit is rounded away from zero (C/C++ rounding behavior) if the following digit + 1 is equal to 5. -/// - `TiesUp`: The specified digit is rounded towards $$+\infty$$ (Java/JavaScript rounding behaviour) if the following digit + 1 is equal to 5. -/// - `ToZero`: The input value is truncated at the specified digit. -/// - An alias for this rounding mode is [`truncate`](#truncate) -/// - `Down`: The specified digit is rounded towards $$-\infty$$ if the following digit + 1 is larger than 0. -/// - An alias for this rounding mode is [`floor`](#floor) -/// - `Up`: The specified digit is rounded towards $$+\infty$$ if the following digit + 1 is larger than 0. -/// - An alias for this rounding mode is [`ceiling`](#ceiling) +/// - `"Nearest"` (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. +/// - `"TiesAway"`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++ rounding behavior). +/// - `"TiesUp"`: 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). +/// - `"ToZero"`: The input $$x$$ is rounded to the nearest integer value (at the specified digit) that is less than or equal to the absolute value of the input $$x$$. An alias for this rounding mode is [`truncate`](#truncate). +/// - `"Down"`: 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). +/// - `"Up"`: 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). /// ///
-/// Example: +/// Details +/// +/// The `"Nearest"` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `"TiesAway"` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `"TiesUp"` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `"ToZero"` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `"Down"` rounding mode, rounds $$12.0654$$ to: +/// - $$12.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.0$$ for 1 digits after the decimal point (`digits = 1`) +/// - $$12.06$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.065$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$10.0$$ for 1 digit before the decimal point (`digits = -1`) +/// - $$0.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$0.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +/// The `"Up"` rounding mode, rounds $$12.0654$$ to: +/// - $$13.0$$ for 0 digits after the decimal point (`digits = 0`) +/// - $$12.1$$ for 1 digit after the decimal point (`digits = 1`) +/// - $$12.07$$ for 2 digits after the decimal point (`digits = 2`) +/// - $$12.066$$ for 3 digits after the decimal point (`digits = 3`) +/// +/// It is also possible to specify a negative number of digits. In that case, the negative number refers to the digits before the decimal point. +/// - $$20.0$$ for 1 digit places before the decimal point (`digit = -1`) +/// - $$100.0$$ for 2 digits before the decimal point (`digits = -2`) +/// - $$1000.0$$ for 3 digits before the decimal point (`digits = -3`) +/// +///
+/// +///
+/// Example /// /// import gleeunit/should /// import gleam/option /// import gleam_community/maths/float as floatx /// /// pub fn example() { -/// floatx.round(0.4444, 2) -/// |> should.equal(0.44) +/// // The default number of digits is 0 if None is provided +/// floatx.round(12.0654, option.None, option.Some("Nearest")) +/// |> should.equal(Ok(12.0)) /// -/// floatx.round(0.4445, 2) -/// |> should.equal(0.44) +/// // The default rounding mode is "Nearest" if None is provided +/// floatx.round(12.0654, option.None, option.None) +/// |> should.equal(Ok(12.0)) /// -/// floatx.round(0.4455, 2) -/// |> should.equal(0.45) +/// // We get an error if an invalid rounding mode is provided +/// floatx.round(12.0654, option.None, option.Some("XYZ")) +/// |> should.be_error() /// -/// floatx.round(0.4555, 2) -/// |> should.equal(0.46) +/// // Try different rounding modes +/// floatx.round(12.0654, option.Some(2), option.Some("Nearest")) +/// |> should.equal(Ok(12.07)) +/// +/// floatx.round(12.0654, option.Some(2), option.Some("TiesAway")) +/// |> should.equal(Ok(12.07)) +/// +/// floatx.round(12.0654, option.Some(2), option.Some("TiesUp")) +/// |> should.equal(Ok(12.07) +/// +/// floatx.round(12.0654, option.Some(2), option.Some("ToZero")) +/// |> should.equal(Ok(12.06)) +/// +/// floatx.round(12.0654, option.Some(2), option.Some("Down")) +/// |> should.equal(Ok(12.06)) +/// +/// floatx.round(12.0654, option.Some(2), option.Some("Up")) +/// |> should.equal(Ok(12.07)) /// } ///
/// @@ -288,82 +392,78 @@ pub fn round( case digits { option.Some(a) -> { assert Ok(p) = power(10.0, int.to_float(a)) - case mode { - // Rounding mode choices - option.Some("Nearest") -> - round_nearest(p, x) - |> Ok - option.Some("TiesAway") -> - round_ties_away(p, x) - |> Ok - option.Some("TiesUp") -> - round_ties_up(p, x) - |> Ok - option.Some("ToZero") -> - round_to_zero(p, x) - |> Ok - option.Some("Down") -> - round_down(p, x) - |> Ok - option.Some("Up") -> - round_up(p, x) - |> Ok - // Default rounding mode - option.None -> - round_nearest(p, x) - |> Ok - _ -> - "Invalid Rounding Mode!" - |> Error - } + do_round(p, x, mode) } + option.None -> do_round(1.0, x, mode) + } +} + +fn do_round( + p: Float, + x: Float, + mode: option.Option(String), +) -> Result(Float, String) { + case mode { + // Rounding mode choices + option.Some("Nearest") -> + round_nearest(p, x) + |> Ok + option.Some("TiesAway") -> + round_ties_away(p, x) + |> Ok + option.Some("TiesUp") -> + round_ties_up(p, x) + |> Ok + option.Some("ToZero") -> + round_to_zero(p, x) + |> Ok + option.Some("Down") -> + round_down(p, x) + |> Ok + option.Some("Up") -> + round_up(p, x) + |> Ok + // Default rounding mode. The default is "Nearest" + option.None -> + round_nearest(p, x) + |> Ok _ -> - "Invalid!" + "Invalid rounding mode. Valid input is 'Nearest', 'TiesAway', 'TiesUp', 'ToZero', 'Down', 'Up'." |> Error } } fn round_nearest(p: Float, x: Float) -> Float { - let positive = x >. 0.0 - let xabs = float.absolute_value(x) - let geq_tie = xabs -. truncate_float(xabs) >=. 0.5 - assert Ok(is_even) = int.modulo(to_int(xabs), 2) - io.debug(is_even) - case geq_tie { - True -> + let xabs = float.absolute_value(x) *. p + let rem = xabs -. truncate_float(xabs) + case rem { + _ if rem >. 0.5 -> sign(x) *. truncate_float(xabs +. 1.0) /. p + _ if rem == 0.5 -> { + assert Ok(is_even) = int.modulo(to_int(xabs), 2) case is_even == 0 { - True -> sign(x) *. truncate_float({ xabs +. 0.0 } *. p) /. p - False -> sign(x) *. truncate_float({ xabs +. 1.0 } *. p) /. p + True -> sign(x) *. truncate_float(xabs) /. p + False -> sign(x) *. truncate_float(xabs +. 1.0) /. p } - False -> sign(x) *. truncate_float({ xabs +. 0.0 } *. p) /. p + } + _ -> sign(x) *. truncate_float(xabs) /. p } } fn round_ties_away(p: Float, x: Float) -> Float { - let positive = x >. 0.0 - let xabs = float.absolute_value(x) - let g_tie = xabs -. truncate_float(xabs) >=. 0.5 - case g_tie { - True -> sign(x) *. truncate_float({ xabs +. 1.0 } *. p) /. p - False -> truncate_float(x *. p) /. p + let xabs = float.absolute_value(x) *. p + let rem = xabs -. truncate_float(xabs) + case rem { + _ if rem >=. 0.5 -> sign(x) *. truncate_float(xabs +. 1.0) /. p + _ -> sign(x) *. truncate_float(xabs) /. p } } fn round_ties_up(p: Float, x: Float) -> Float { - let positive = x >. 0.0 - let xabs = float.absolute_value(x) - let geq_tie = xabs -. truncate_float(xabs) >=. 0.5 - case geq_tie { - True -> - case positive { - True -> sign(x) *. truncate_float({ xabs +. 1.0 } *. p) /. p - False -> sign(x) *. truncate_float({ xabs +. 0.0 } *. p) /. p - } - False -> - case positive { - True -> sign(x) *. truncate_float({ xabs +. 0.0 } *. p) /. p - False -> sign(x) *. truncate_float({ xabs +. 0.0 } *. p) /. p - } + let xabs = float.absolute_value(x) *. p + let rem = xabs -. truncate_float(xabs) + case rem { + _ if rem >=. 0.5 && x >=. 0.0 -> sign(x) *. truncate_float(xabs +. 1.0) /. p + _ -> sign(x) *. truncate_float(xabs) /. p } } @@ -434,7 +534,7 @@ if erlang { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -495,7 +595,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -553,7 +653,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -613,7 +713,7 @@ if javascript { /// a numeric value $$y$$ that lies in the range $$\(-\infty, +\infty\)$$ (an angle in radians). /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -660,7 +760,7 @@ if javascript { /// a numeric value $$y$$ that lies in the range $$\[-\frac{\pi}{2}, \frac{\pi}{2}\]$$ (an angle in radians). /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -716,7 +816,7 @@ if javascript { /// returned by $$\text{atan2}(y, x)$$ is in the range $$\[-\pi, \pi\]$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -764,7 +864,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -824,7 +924,7 @@ if javascript { /// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -875,7 +975,7 @@ if javascript { /// If the input value is too large an overflow error might occur. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -923,7 +1023,7 @@ if javascript { /// Note: If the input value $$x$$ is too large an overflow error might occur. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -971,7 +1071,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam/option @@ -1040,7 +1140,7 @@ pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String) /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1102,7 +1202,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1163,7 +1263,7 @@ if javascript { /// If the input value is outside the domain of the function an error is returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1207,61 +1307,6 @@ if javascript { "../floatx.mjs" "log2" } -// pub fn logb(x: Float, b: Float) -> Result(Float, String) { -// case x >. 0.0 { -// True -> -// case b >. 0.0 && b != 1.0 { -// True -> { -// // Apply the change of base formula -// assert Ok(numerator) = log10(x) -// assert Ok(denominator) = log10(b) -// numerator /. denominator -// |> Ok -// } -// False -> -// "Invalid input argument: b <= 0 or b == 1. Valid input is b > 0 and b != 1." -// |> Error -// } -// False -> -// "Invalid input argument: x <= 0. Valid input is x > 0." -// |> Error -// } -// } - -/// -/// -/// The The base-2 logarithm function: -/// -/// \\[ -/// \forall x \in \(0, \infty), \\; \log_{2}{(x)} = y \in \(-\infty, +\infty\) -/// \\] -/// -/// The function takes a number $$x$$ in its domain $$\(0, \infty\)$$ as input and returns -/// a numeric value $$y$$ that lies in the range $$\(-\infty, \infty\)$$. -/// If the input value is outside the domain of the function an error is returned. -/// -///
-/// Example: -/// -/// import gleeunit/should -/// import gleam_community/maths/float as floatx -/// -/// pub fn example () { -/// floatx.logarithm_2(1.0) -/// |> should.equal(Ok(0.0)) -/// -/// floatx.logarithm_2(2.0) -/// |> should.equal(Ok(1.0)) -/// -/// floatx.logarithm_2(-1.0) -/// |> should.be_error() -/// } -///
-/// /// /// pub fn power(x: Float, y: Float) -> Result(Float, String) { - // assert Ok(y_ceiling) = ceiling(y, option.Some(0)) - // io.debug(y_ceiling) - // let fractional: Bool = y_ceiling -. y >. 0.0 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 @@ -1352,7 +1394,7 @@ if javascript { /// as an imaginary number will otherwise have to be returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1404,7 +1446,7 @@ pub fn square_root(x: Float) -> Result(Float, String) { /// as an imaginary number will otherwise have to be returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1456,7 +1498,7 @@ pub fn cube_root(x: Float) -> Result(Float, String) { /// as an imaginary number will otherwise have to be returned. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1517,7 +1559,7 @@ pub fn nth_root(x: Float, n: Int) -> Result(Float, String) { /// Naive (unfused) and corrected (unfused) in [https://arxiv.org/pdf/1904.09481.pdf]("An Improved Algorithm for Hypot(A, B)" by Borges, C. F) /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1556,7 +1598,7 @@ pub fn hypotenuse(x: Float, y: Float) -> Float { /// as input and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1608,7 +1650,7 @@ if javascript { /// occur. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1656,7 +1698,7 @@ if javascript { /// $$\(-\infty, +\infty\)$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1703,7 +1745,7 @@ if javascript { /// and returns a numeric value $$y$$ that lies in the range $$\[-1, 1\]$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1750,7 +1792,7 @@ if javascript { /// That is, $$1 \text{ radians } = \frac{180}{\pi} \text{ degrees }$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1784,7 +1826,7 @@ pub fn to_degree(x: Float) -> Float { /// That is, $$1 \text{ degrees } = \frac{\pi}{180} \text{ radians }$$. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1814,7 +1856,7 @@ pub fn to_radian(x: Float) -> Float { /// The min function. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1850,7 +1892,7 @@ pub fn minimum(x: Float, y: Float) -> Float { /// The min function. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1886,7 +1928,7 @@ pub fn maximum(x: Float, y: Float) -> Float { /// The minmax function. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -1916,8 +1958,7 @@ pub fn minmax(x: Float, y: Float) -> #(Float, Float) { /// /// /// -/// The sign function which returns the sign of the input, indicating -/// whether it is positive, negative, or zero. +/// The sign function which returns the sign of the input, indicating whether it is positive, negative, or zero. /// /// /// -/// The function returns $$x$$ such that it has the same sign as $$y$$. +/// The function takes two arguments $$x$$ and $$y$$ and returns $$x$$ such that it has the same sign as $$y$$. /// /// /// pub fn copy_sign(x: Float, y: Float) -> Float { - // let signx: Float = sign(x) - // let signy: Float = sign(y) - // case signx == signy { - // True -> y - // False -> { - // case signx >. signy { - // True -> - // } - - // } - // } - todo + case sign(x) == sign(y) { + // x and y have the same sign, just return x + True -> x + // x and y have different signs: + // - x is positive and y is negative, then flip sign of x + // - x is negative and y is positive, then flip sign of x + False -> flip_sign(x) + } } /// /// +/// The function flips the sign of a given input value. /// ///
/// @@ -2212,7 +2250,7 @@ pub fn tau() -> Float { /// Note: If the input value $$x$$ is too large an overflow error might occur. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -2250,7 +2288,7 @@ pub fn e() -> Float { /// value which is the the absolute difference of the inputs. /// ///
-/// Example: +/// Example /// /// import gleeunit/should /// import gleam_community/maths/float as floatx @@ -2291,10 +2329,10 @@ pub fn absolute_difference(a: Float, b: Float) -> Float { /// /// `True` is returned if statement holds, otherwise `False` is returned. ///
-/// Example: +/// Example /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/float as floatx /// /// pub fn example () { /// let val: Float = 99. @@ -2303,7 +2341,7 @@ pub fn absolute_difference(a: Float, b: Float) -> Float { /// // if 'val' is within 1 percent of 'ref_val' +/- 0.1 /// let rtol: Float = 0.01 /// let atol: Float = 0.10 -/// stats.is_close(val, ref_val, rtol, atol) +/// floatx.is_close(val, ref_val, rtol, atol) /// |> should.be_true() /// } ///
diff --git a/src/gleam_community/maths/float_list.gleam b/src/gleam_community/maths/float_list.gleam index f835b5e..e8334e5 100644 --- a/src/gleam_community/maths/float_list.gleam +++ b/src/gleam_community/maths/float_list.gleam @@ -40,12 +40,10 @@ //// * [`maximum`](#maximum) //// * [`minimum`](#minimum) //// * [`extrema`](#extrema) -//// * [`argmax`](#arg_maximum) -//// * [`argmin`](#arg_minimum) +//// * [`arg_maximum`](#arg_maximum) +//// * [`arg_minimum`](#arg_minimum) //// * **Tests** //// * [`all_close`](#all_close) -//// * **Misc. functions** -//// * [`trim`](#trim) import gleam/list import gleam/int @@ -173,8 +171,7 @@ pub fn geometric_space( /// \sum_{i=1}^n x_i /// \\] /// -/// In the formula, $$n$$ is the length of the list and -/// $$x_i$$ is the value in the input list indexed by $$i$$. +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. /// ///
/// Example: @@ -222,8 +219,7 @@ pub fn sum(arr: List(Float)) -> Float { /// \prod_{i=1}^n x_i /// \\] /// -/// In the formula, $$n$$ is the length of the list and -/// $$x_i$$ is the value in the input list indexed by $$i$$. +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. /// ///
/// Example: @@ -271,8 +267,7 @@ pub fn product(arr: List(Float)) -> Float { /// \sum_{i=1}^n x_i /// \\] /// -/// In the formula, $$n$$ is the length of the list and -/// $$x_i$$ is the value in the input list indexed by $$i$$. +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. /// ///
/// Example: @@ -314,8 +309,7 @@ pub fn cumulative_sum(arr: List(Float)) -> List(Float) { /// \prod_{i=1}^n x_i /// \\] /// -/// In the formula, $$n$$ is the length of the list and -/// $$x_i$$ is the value in the input list indexed by $$i$$. +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. /// ///
/// Example: @@ -395,9 +389,9 @@ pub fn norm(xarr: List(Float), yarr: List(Float), p: Int) -> List(Float) { /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] +/// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> float_list.maximum() -/// |> should.equal(Ok(4.)) +/// |> should.equal(Ok(4.0)) /// } ///
/// @@ -450,9 +444,9 @@ pub fn maximum(arr: List(Float)) -> Result(Float, String) { /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] +/// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> float_list.minimum() -/// |> should.equal(Ok(1.)) +/// |> should.equal(Ok(1.0)) /// } ///
/// @@ -505,7 +499,7 @@ pub fn minimum(arr: List(Float)) -> Result(Float, String) { /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] +/// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> float_list.arg_maximum() /// |> should.equal(Ok([0, 1])) /// } @@ -565,7 +559,7 @@ pub fn arg_maximum(arr: List(Float)) -> Result(List(Int), String) { /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] +/// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> float_list.arg_minimum() /// |> should.equal(Ok([4])) /// } @@ -577,7 +571,7 @@ pub fn arg_maximum(arr: List(Float)) -> Result(List(Int), String) { ///
///
/// -pub fn arg_mininmum(arr: List(Float)) -> Result(List(Int), String) { +pub fn arg_minimum(arr: List(Float)) -> Result(List(Int), String) { case arr { [] -> "Invalid input argument: The list is empty." @@ -625,9 +619,9 @@ pub fn arg_mininmum(arr: List(Float)) -> Result(List(Int), String) { /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] +/// [4.0, 4.0, 3.0, 2.0, 1.0] /// |> float_list.extrema() -/// |> should.equal(Ok(1.)) +/// |> should.equal(Ok(#(1.0, 4.0))) /// } ///
/// @@ -638,7 +632,30 @@ pub fn arg_mininmum(arr: List(Float)) -> Result(List(Int), String) { /// /// pub fn extrema(arr: List(Float)) -> Result(#(Float, Float), String) { - todo + case arr { + [] -> + "Invalid input argument: The list is empty." + |> Error + _ -> { + assert Ok(val_max) = list.at(arr, 0) + assert Ok(val_min) = list.at(arr, 0) + arr + |> list.fold( + #(val_min, val_max), + fn(acc: #(Float, Float), a: Float) { + let first: Float = pair.first(acc) + let second: Float = pair.second(acc) + case a <. first, a >. second { + True, True -> #(a, a) + True, False -> #(a, second) + False, True -> #(first, a) + False, False -> #(first, second) + } + }, + ) + |> Ok + } + } } ///
diff --git a/src/gleam_community/maths/int.gleam b/src/gleam_community/maths/int.gleam index 438e04b..05c396e 100644 --- a/src/gleam_community/maths/int.gleam +++ b/src/gleam_community/maths/int.gleam @@ -19,8 +19,7 @@ //// .katex { font-size: 1.1em; } //// //// -//// A module containing several different kinds of mathematical functions -//// applying to integer numbers. +//// A module containing mathematical functions applying to integer (int) numbers. //// //// --- //// @@ -77,13 +76,13 @@ import gleam_community/maths/float as floatx /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { -/// math.minimum(2.0, 1.5) +/// intx.minimum(2.0, 1.5) /// |> should.equal(1.5) /// -/// math.minimum(1.5, 2.0) +/// intx.minimum(1.5, 2.0) /// |> should.equal(1.5) /// } ///
@@ -113,13 +112,13 @@ pub fn minimum(x: Int, y: Int) -> Int { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { -/// math.maximum(2.0, 1.5) +/// intx.maximum(2.0, 1.5) /// |> should.equal(1.5) /// -/// math.maximum(1.5, 2.0) +/// intx.maximum(1.5, 2.0) /// |> should.equal(1.5) /// } ///
@@ -149,13 +148,13 @@ pub fn maximum(x: Int, y: Int) -> Int { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { -/// math.minmax(2.0, 1.5) +/// intx.minmax(2.0, 1.5) /// |> should.equal(#(1.5, 2.0)) /// -/// math.minmax(1.5, 2.0) +/// intx.minmax(1.5, 2.0) /// |> should.equal(#(1.5, 2.0)) /// } ///
@@ -204,7 +203,7 @@ if erlang { if javascript { external fn do_sign(Int) -> Int = - "../math.mjs" "sign" + "../intx.mjs" "sign" } ///
@@ -224,6 +223,31 @@ pub fn flip_sign(x: Int) -> Int { -1 * x } +/// +/// +/// The function takes two arguments $$x$$ and $$y$$ and returns $$x$$ such that it has the same sign as $$y$$. +/// +/// +/// +pub fn copy_sign(x: Int, y: Int) -> Int { + case sign(x) == sign(y) { + // x and y have the same sign, just return x + True -> x + // x and y have different signs: + // - x is positive and y is negative, then flip sign of x + // - x is negative and y is positive, then flip sign of x + False -> flip_sign(x) + } +} + ///
@@ -314,23 +338,23 @@ pub fn combination(n: Int, k: Int) -> Result(Int, String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { /// // Invalid input gives an error -/// math.factorial(-1) +/// intx.factorial(-1) /// |> should.be_error() /// /// // Valid input returns a result -/// math.factorial(0) +/// intx.factorial(0) /// |> should.equal(Ok(1)) -/// math.factorial(1) +/// intx.factorial(1) /// |> should.equal(Ok(1)) -/// math.factorial(2) +/// intx.factorial(2) /// |> should.equal(Ok(2)) -/// math.factorial(3) +/// intx.factorial(3) /// |> should.equal(Ok(6)) -/// math.factorial(4) +/// intx.factorial(4) /// |> should.equal(Ok(24)) /// } ///
@@ -379,22 +403,22 @@ pub fn factorial(n) -> Result(Int, String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { /// // Invalid input gives an error /// // Error on: n = -1 < 0 -/// math.permutation(-1, 1) +/// intx.permutation(-1, 1) /// |> should.be_error() /// /// // Valid input returns a result -/// math.permutation(4, 0) +/// intx.permutation(4, 0) /// |> should.equal(Ok(1)) /// -/// math.permutation(4, 4) +/// intx.permutation(4, 4) /// |> should.equal(Ok(1)) /// -/// math.permutation(4, 2) +/// intx.permutation(4, 2) /// |> should.equal(Ok(12)) /// } ///
@@ -450,13 +474,13 @@ pub fn permutation(n: Int, k: Int) -> Result(Int, String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { -/// math.absolute_difference(-10.0, 10.0) +/// intx.absolute_difference(-10.0, 10.0) /// |> should.equal(20.0) /// -/// math.absolute_difference(0.0, -2.0) +/// intx.absolute_difference(0.0, -2.0) /// |> should.equal(2.0) /// } ///
@@ -482,7 +506,7 @@ pub fn absolute_difference(a: Int, b: Int) -> Int { /// Example: /// /// import gleeunit/should -/// import gleam_stats/math +/// import gleam_community/maths/int as intx /// /// pub fn example() { /// @@ -498,7 +522,7 @@ pub fn absolute_difference(a: Int, b: Int) -> Int { pub fn is_power(x: Int, y: Int) -> Bool { assert Ok(value) = floatx.logarithm(int.to_float(x), option.Some(int.to_float(y))) - assert Ok(truncated) = floatx.truncate(value, 0) + assert Ok(truncated) = floatx.truncate(value, option.Some(0)) let rem = value -. truncated rem == 0.0 } diff --git a/src/gleam_community/maths/int_list.gleam b/src/gleam_community/maths/int_list.gleam index 0b928d0..1033f52 100644 --- a/src/gleam_community/maths/int_list.gleam +++ b/src/gleam_community/maths/int_list.gleam @@ -26,11 +26,18 @@ //// //// --- //// -//// * **Miscellaneous functions** -//// * [`amax`](#amax) -//// * [`amin`](#amin) -//// * [`argmax`](#argmax) -//// * [`argmin`](#argmin) +//// * **Distances, sums and products** +//// * [`sum`](#sum) +//// * [`product`](#product) +//// * [`norm`](#norm) +//// * [`cumulative_sum`](#cumulative_sum) +//// * [`cumulative_product`](#cumulative_product) +//// * **Misc. mathematical functions** +//// * [`maximum`](#maximum) +//// * [`minimum`](#minimum) +//// * [`extrema`](#extrema) +//// * [`arg_maximum`](#arg_maximum) +//// * [`arg_minimum`](#arg_minimum) import gleam/list import gleam/int @@ -50,17 +57,17 @@ import gleam_community/maths/int as intx /// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/int_list /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.argmin() +/// |> int_list.arg_minimum() /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] -/// |> stats.argmin() +/// [4, 4, 3, 2, 1] +/// |> stats.arg_minimum() /// |> should.equal(Ok([4])) /// } ///
@@ -71,7 +78,7 @@ import gleam_community/maths/int as intx /// /// /// -pub fn argmin(arr: List(Float)) -> Result(List(Int), String) { +pub fn arg_minimum(arr: List(Int)) -> Result(List(Int), String) { case arr { [] -> "Invalid input argument: The list is empty." @@ -79,11 +86,11 @@ pub fn argmin(arr: List(Float)) -> Result(List(Int), String) { _ -> { assert Ok(min) = arr - |> amin() + |> minimum() arr - |> list.index_map(fn(index: Int, a: Float) -> Int { - case a -. min { - 0. -> index + |> list.index_map(fn(index: Int, a: Int) -> Int { + case a - min { + 0 -> index _ -> -1 } }) @@ -110,17 +117,17 @@ pub fn argmin(arr: List(Float)) -> Result(List(Int), String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/int_list /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.argmax() +/// |> int_list.arg_maximum() /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] -/// |> stats.argmax() +/// [4, 4, 3, 2, 1] +/// |> int_list.arg_maximum() /// |> should.equal(Ok([0, 1])) /// } ///
@@ -131,7 +138,7 @@ pub fn argmin(arr: List(Float)) -> Result(List(Int), String) { /// /// /// -pub fn argmax(arr: List(Float)) -> Result(List(Int), String) { +pub fn arg_maximum(arr: List(Int)) -> Result(List(Int), String) { case arr { [] -> "Invalid input argument: The list is empty." @@ -139,11 +146,11 @@ pub fn argmax(arr: List(Float)) -> Result(List(Int), String) { _ -> { assert Ok(max) = arr - |> amax() + |> maximum() arr - |> list.index_map(fn(index: Int, a: Float) -> Int { - case a -. max { - 0. -> index + |> list.index_map(fn(index: Int, a: Int) -> Int { + case a - max { + 0 -> index _ -> -1 } }) @@ -170,18 +177,18 @@ pub fn argmax(arr: List(Float)) -> Result(List(Int), String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/int_list /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.amax() +/// |> int_list.maximum() /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] -/// |> stats.amax() -/// |> should.equal(Ok(4.)) +/// [4, 4, 3, 2, 1] +/// |> int_list.maximum() +/// |> should.equal(Ok(4)) /// } ///
/// @@ -191,7 +198,7 @@ pub fn argmax(arr: List(Float)) -> Result(List(Int), String) { /// /// /// -pub fn amax(arr: List(Float)) -> Result(Float, String) { +pub fn maximum(arr: List(Int)) -> Result(Int, String) { case arr { [] -> "Invalid input argument: The list is empty." @@ -201,8 +208,8 @@ pub fn amax(arr: List(Float)) -> Result(Float, String) { arr |> list.fold( val0, - fn(acc: Float, a: Float) { - case a >. acc { + fn(acc: Int, a: Int) { + case a > acc { True -> a False -> acc } @@ -225,18 +232,18 @@ pub fn amax(arr: List(Float)) -> Result(Float, String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/int_list /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.amin() +/// |> int_list.minimum() /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] -/// |> stats.amin() -/// |> should.equal(Ok(1.)) +/// [4, 4, 3, 2, 1] +/// |> int_list.minimum() +/// |> should.equal(Ok(1)) /// } ///
/// @@ -246,7 +253,7 @@ pub fn amax(arr: List(Float)) -> Result(Float, String) { /// /// /// -pub fn amin(arr: List(Float)) -> Result(Float, String) { +pub fn minimum(arr: List(Int)) -> Result(Int, String) { case arr { [] -> "Invalid input argument: The list is empty." @@ -256,8 +263,8 @@ pub fn amin(arr: List(Float)) -> Result(Float, String) { arr |> list.fold( val0, - fn(acc: Float, a: Float) { - case a <. acc { + fn(acc: Int, a: Int) { + case a < acc { True -> a False -> acc } @@ -279,18 +286,18 @@ pub fn amin(arr: List(Float)) -> Result(Float, String) { /// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/int_list /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.amin() +/// |> int_list.extrema() /// |> should.be_error() /// /// // Valid input returns a result -/// [4., 4., 3., 2., 1.] -/// |> stats.amin() -/// |> should.equal(Ok(1.)) +/// [4, 4, 3, 2, 1] +/// |> int_list.extrema() +/// |> should.equal(Ok(#(1, 4))) /// } ///
/// @@ -300,6 +307,29 @@ pub fn amin(arr: List(Float)) -> Result(Float, String) { /// /// /// -pub fn extrema(arr: List(Float)) -> Result(#(Float, Float), String) { - todo +pub fn extrema(arr: List(Int)) -> Result(#(Int, Int), String) { + case arr { + [] -> + "Invalid input argument: The list is empty." + |> Error + _ -> { + assert Ok(val_max) = list.at(arr, 0) + assert Ok(val_min) = list.at(arr, 0) + arr + |> list.fold( + #(val_min, val_max), + fn(acc: #(Int, Int), a: Int) { + let first: Int = pair.first(acc) + let second: Int = pair.second(acc) + case a < first, a > second { + True, True -> #(a, a) + True, False -> #(a, second) + False, True -> #(first, a) + False, False -> #(first, second) + } + }, + ) + |> Ok + } + } } diff --git a/src/gleam_community/maths/list.gleam b/src/gleam_community/maths/list.gleam index 634ac83..257fa12 100644 --- a/src/gleam_community/maths/list.gleam +++ b/src/gleam_community/maths/list.gleam @@ -19,11 +19,8 @@ //// .katex { font-size: 1.1em; } //// //// -//// A module containing several different kinds of mathematical functions -//// applying to lists of real numbers. +//// A module containing general utility functions applying to lists. //// -//// Function naming has been adopted from C mathematical function. -//// //// --- //// //// * **Miscellaneous functions** @@ -39,24 +36,23 @@ import gleam/float /// /// /// -/// Trim a list to a certain size given min/max indices. The min/max indices -/// are inclusive. +/// Trim a list to a certain size given min/max indices. The min/max indices are inclusive. /// ///
/// Example: /// /// import gleeunit/should -/// import gleam_stats/stats +/// import gleam_community/maths/list as listx /// /// pub fn example () { /// // An empty lists returns an error /// [] -/// |> stats.trim(0, 0) +/// |> listx.trim(0, 0) /// |> should.be_error() /// /// // Trim the list to only the middle part of list /// [1., 2., 3., 4., 5., 6.] -/// |> stats.trim(1, 4) +/// |> listx.trim(1, 4) /// |> should.equal(Ok([2., 3., 4., 5.])) /// } ///
diff --git a/temp.gleam b/temp.gleam deleted file mode 100644 index aabf936..0000000 --- a/temp.gleam +++ /dev/null @@ -1,410 +0,0 @@ -//// Small examples ALSO used in the docs... - -import gleam/int -import gleam/list -import gleam/pair -import gleam_stats/stats -import gleeunit -import gleeunit/should - -pub fn main() { - gleeunit.main() -} - -pub fn example_sum_test() { - // An empty list returns an error - [] - |> stats.sum() - |> should.equal(0.) - - // Valid input returns a result - [1., 2., 3.] - |> stats.sum() - |> should.equal(6.) -} - -pub fn example_mean_test() { - // An empty list returns an error - [] - |> stats.mean() - |> should.be_error() - - // Valid input returns a result - [1., 2., 3.] - |> stats.mean() - |> should.equal(Ok(2.)) -} - -pub fn example_median_test() { - // An empty list returns an error - [] - |> stats.median() - |> should.be_error() - - // Valid input returns a result - [1., 2., 3.] - |> stats.median() - |> should.equal(Ok(2.)) - - [1., 2., 3., 4.] - |> stats.median() - |> should.equal(Ok(2.5)) -} - -pub fn example_hmean_test() { - // An empty list returns an error - [] - |> stats.hmean() - |> should.be_error() - - // List with negative numbers returns an error - [-1., -3., -6.] - |> stats.hmean() - |> should.be_error() - - // Valid input returns a result - [1., 3., 6.] - |> stats.hmean() - |> should.equal(Ok(2.)) -} - -pub fn example_gmean_test() { - // An empty list returns an error - [] - |> stats.gmean() - |> should.be_error() - // List with negative numbers returns an error - [-1., -3., -6.] - |> stats.gmean() - |> should.be_error() - // Valid input returns a result - [1., 3., 9.] - |> stats.gmean() - |> should.equal(Ok(3.)) -} - -pub fn example_var_test() { - // Degrees of freedom - let ddof: Int = 1 - - // An empty list returns an error - [] - |> stats.var(ddof) - |> should.be_error() - - // Valid input returns a result - [1., 2., 3.] - |> stats.var(ddof) - |> should.equal(Ok(1.)) -} - -pub fn example_std_test() { - // Degrees of freedom - let ddof: Int = 1 - - // An empty list returns an error - [] - |> stats.std(ddof) - |> should.be_error() - - // Valid input returns a result - [1., 2., 3.] - |> stats.std(ddof) - |> should.equal(Ok(1.)) -} - -pub fn example_moment_test() { - // An empty list returns an error - [] - |> stats.moment(0) - |> should.be_error() - - // 0th moment about the mean is 1. per definition - [0., 1., 2., 3., 4.] - |> stats.moment(0) - |> should.equal(Ok(1.)) - - // 1st moment about the mean is 0. per definition - [0., 1., 2., 3., 4.] - |> stats.moment(1) - |> should.equal(Ok(0.)) - - // 2nd moment about the mean - [0., 1., 2., 3., 4.] - |> stats.moment(2) - |> should.equal(Ok(2.)) -} - -pub fn example_skewness_test() { - // An empty list returns an error - [] - |> stats.skewness() - |> should.be_error() - - // No skewness - // -> Zero skewness - [1., 2., 3., 4.] - |> stats.skewness() - |> should.equal(Ok(0.)) - - // Right-skewed distribution - // -> Positive skewness - [1., 1., 1., 2.] - |> stats.skewness() - |> fn(x: Result(Float, String)) -> Bool { - case x { - Ok(x) -> x >. 0. - _ -> False - } - } - |> should.be_true() -} - -pub fn example_kurtosis_test() { - // An empty list returns an error - [] - |> stats.skewness() - |> should.be_error() - - // No tail - // -> Fisher's definition gives kurtosis -3 - [1., 1., 1., 1.] - |> stats.kurtosis() - |> should.equal(Ok(-3.)) - - // Distribution with a tail - // -> Higher kurtosis - [1., 1., 1., 2.] - |> stats.kurtosis() - |> fn(x: Result(Float, String)) -> Bool { - case x { - Ok(x) -> x >. -3. - _ -> False - } - } - |> should.be_true() -} - -pub fn example_zscore_test() { - // An empty list returns an error - [] - // Use degrees of freedom = 1 - |> stats.zscore(1) - |> should.be_error() - - [1., 2., 3.] - // Use degrees of freedom = 1 - |> stats.zscore(1) - |> should.equal(Ok([-1., 0., 1.])) -} - -pub fn example_percentile_test() { - // An empty list returns an error - [] - |> stats.percentile(40) - |> should.be_error() - - // Calculate 40th percentile - [15., 20., 35., 40., 50.] - |> stats.percentile(40) - |> should.equal(Ok(29.)) -} - -pub fn example_iqr_test() { - // An empty list returns an error - [] - |> stats.iqr() - |> should.be_error() - - // Valid input returns a result - [1., 2., 3., 4., 5.] - |> stats.iqr() - |> should.equal(Ok(3.)) -} - -pub fn example_freedman_diaconis_rule_test() { - // An empty list returns an error - [] - |> stats.freedman_diaconis_rule() - |> should.be_error() - - // Calculate histogram bin widths - list.range(0, 1000) - |> list.map(fn(x: Int) -> Float { int.to_float(x) }) - |> stats.freedman_diaconis_rule() - |> should.equal(Ok(10.)) -} - -pub fn example_range_test() { - // Create a range - let range = stats.Range(0., 1.) - // Retrieve min and max values - let stats.Range(min, max) = range - min - |> should.equal(0.) - max - |> should.equal(1.) -} - -pub fn example_bin_test() { - // Create a bin - let bin: stats.Bin = #(stats.Range(0., 1.), 999) - // Retrieve min and max values - let stats.Range(min, max) = pair.first(bin) - min - |> should.equal(0.) - max - |> should.equal(1.) - // Retrieve count - let count = pair.second(bin) - count - |> should.equal(999) -} - -pub fn example_histogram_test() { - // An empty lists returns an error - [] - |> stats.histogram(1.) - |> should.be_error() - // Create the bins of a histogram given a list of values - list.range(0, 100) - |> list.map(fn(x: Int) -> Float { int.to_float(x) }) - // Below 25. is the bin width - // The Freedman-Diaconis’s Rule can be used to determine a decent value - |> stats.histogram(25.) - |> should.equal(Ok([ - #(stats.Range(0., 25.), 25), - #(stats.Range(25., 50.), 25), - #(stats.Range(50., 75.), 25), - #(stats.Range(75., 100.), 25), - ])) -} - -pub fn example_correlation_test() { - // An empty lists returns an error - stats.correlation([], []) - |> should.be_error() - - // Lists with fewer than 2 elements return an error - stats.correlation([1.0], [1.0]) - |> should.be_error() - - // Lists of uneqal length return an error - stats.correlation([1.0, 2.0, 3.0], [1.0, 2.0]) - |> should.be_error() - - // Perfect positive correlation - let xarr0: List(Float) = - list.range(0, 100) - |> list.map(fn(x: Int) -> Float { int.to_float(x) }) - let yarr0: List(Float) = - list.range(0, 100) - |> list.map(fn(x: Int) -> Float { int.to_float(x) }) - stats.correlation(xarr0, yarr0) - |> should.equal(Ok(1.)) - - // Perfect negative correlation - let xarr0: List(Float) = - list.range(0, 100) - |> list.map(fn(x: Int) -> Float { -1. *. int.to_float(x) }) - let yarr0: List(Float) = - list.range(0, 100) - |> list.map(fn(x: Int) -> Float { int.to_float(x) }) - stats.correlation(xarr0, yarr0) - |> should.equal(Ok(-1.)) -} - -pub fn example_trim_test() { - // An empty lists returns an error - [] - |> stats.trim(0, 0) - |> should.be_error() - - // Trim the list to only the middle part of list - [1., 2., 3., 4., 5., 6.] - |> stats.trim(1, 4) - |> should.equal(Ok([2., 3., 4., 5.])) -} - -pub fn example_isclose_test() { - let val: Float = 99. - let ref_val: Float = 100. - // We set 'atol' and 'rtol' such that the values are equivalent - // if 'val' is within 1 percent of 'ref_val' +/- 0.1 - let rtol: Float = 0.01 - let atol: Float = 0.10 - stats.isclose(val, ref_val, rtol, atol) - |> should.be_true() -} - -pub fn example_allclose_test() { - let val: Float = 99. - let ref_val: Float = 100. - let xarr: List(Float) = list.repeat(val, 42) - let yarr: List(Float) = list.repeat(ref_val, 42) - // We set 'atol' and 'rtol' such that the values are equivalent - // if 'val' is within 1 percent of 'ref_val' +/- 0.1 - let rtol: Float = 0.01 - let atol: Float = 0.10 - stats.allclose(xarr, yarr, rtol, atol) - |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { - case zarr { - Ok(arr) -> - arr - |> list.all(fn(a: Bool) -> Bool { a }) - |> Ok - _ -> - Nil - |> Error - } - } - |> should.equal(Ok(True)) -} - -pub fn example_amax_test() { - // An empty lists returns an error - [] - |> stats.amax() - |> should.be_error() - - // Valid input returns a result - [4., 4., 3., 2., 1.] - |> stats.amax() - |> should.equal(Ok(4.)) -} - -pub fn example_amin_test() { - // An empty lists returns an error - [] - |> stats.amin() - |> should.be_error() - - // Valid input returns a result - [4., 4., 3., 2., 1.] - |> stats.amin() - |> should.equal(Ok(1.)) -} - -pub fn example_argmax_test() { - // An empty lists returns an error - [] - |> stats.argmax() - |> should.be_error() - - // Valid input returns a result - [4., 4., 3., 2., 1.] - |> stats.argmax() - |> should.equal(Ok([0, 1])) -} - -pub fn example_argmin_test() { - // An empty lists returns an error - [] - |> stats.argmin() - |> should.be_error() - - // Valid input returns a result - [4., 4., 3., 2., 1.] - |> stats.argmin() - |> should.equal(Ok([4])) -} diff --git a/test/gleam/gleam_community_maths_float_list_test.gleam b/test/gleam/gleam_community_maths_float_list_test.gleam new file mode 100644 index 0000000..aea7158 --- /dev/null +++ b/test/gleam/gleam_community_maths_float_list_test.gleam @@ -0,0 +1,94 @@ +import gleam/int +import gleam/list +import gleam/pair +import gleam_community/maths/float_list +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +pub fn float_list_all_close_test() { + let val: Float = 99.0 + let ref_val: Float = 100.0 + let xarr: List(Float) = list.repeat(val, 42) + let yarr: List(Float) = list.repeat(ref_val, 42) + // We set 'atol' and 'rtol' such that the values are equivalent + // if 'val' is within 1 percent of 'ref_val' +/- 0.1 + let rtol: Float = 0.01 + let atol: Float = 0.10 + float_list.all_close(xarr, yarr, rtol, atol) + |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { + case zarr { + Ok(arr) -> + arr + |> list.all(fn(a: Bool) -> Bool { a }) + |> Ok + _ -> + Nil + |> Error + } + } + |> should.equal(Ok(True)) +} + +pub fn float_list_maximum_test() { + // An empty lists returns an error + [] + |> float_list.maximum() + |> should.be_error() + + // Valid input returns a result + [4.0, 4.0, 3.0, 2.0, 1.0] + |> float_list.maximum() + |> should.equal(Ok(4.0)) +} + +pub fn float_list_minimum_test() { + // An empty lists returns an error + [] + |> float_list.minimum() + |> should.be_error() + + // Valid input returns a result + [4.0, 4.0, 3.0, 2.0, 1.0] + |> float_list.minimum() + |> should.equal(Ok(1.0)) +} + +pub fn float_list_arg_maximum_test() { + // An empty lists returns an error + [] + |> float_list.arg_maximum() + |> should.be_error() + + // Valid input returns a result + [4.0, 4.0, 3.0, 2.0, 1.0] + |> float_list.arg_maximum() + |> should.equal(Ok([0, 1])) +} + +pub fn float_list_arg_minimum_test() { + // An empty lists returns an error + [] + |> float_list.arg_minimum() + |> should.be_error() + + // Valid input returns a result + [4.0, 4.0, 3.0, 2.0, 1.0] + |> float_list.arg_minimum() + |> should.equal(Ok([4])) +} + +pub fn float_list_extrema_test() { + // An empty lists returns an error + [] + |> float_list.extrema() + |> should.be_error() + + // Valid input returns a result + [4.0, 4.0, 3.0, 2.0, 1.0] + |> float_list.extrema() + |> should.equal(Ok(#(1.0, 4.0))) +} diff --git a/test/gleam/gleam_community_maths_float_test.gleam b/test/gleam/gleam_community_maths_float_test.gleam index 07a628e..a144b64 100644 --- a/test/gleam/gleam_community_maths_float_test.gleam +++ b/test/gleam/gleam_community_maths_float_test.gleam @@ -513,65 +513,95 @@ pub fn float_to_radian_test() { } pub fn float_ceiling_test() { - // Round based on 3. digit AFTER decimal point + // Round 3. digit AFTER decimal point floatx.ceiling(12.0654, option.Some(3)) |> should.equal(Ok(12.066)) - // Round based on 2. digit AFTER decimal point + // Round 2. digit AFTER decimal point floatx.ceiling(12.0654, option.Some(2)) |> should.equal(Ok(12.07)) - // Round based on 1. digit AFTER decimal point + // Round 1. digit AFTER decimal point floatx.ceiling(12.0654, option.Some(1)) |> should.equal(Ok(12.1)) - // Round based on 0. digit BEFORE decimal point + // Round 0. digit BEFORE decimal point floatx.ceiling(12.0654, option.Some(0)) |> should.equal(Ok(13.0)) - // Round based on 1. digit BEFORE decimal point + // Round 1. digit BEFORE decimal point floatx.ceiling(12.0654, option.Some(-1)) |> should.equal(Ok(20.0)) - // Round based on 2. digit BEFORE decimal point + // Round 2. digit BEFORE decimal point floatx.ceiling(12.0654, option.Some(-2)) |> should.equal(Ok(100.0)) - // Round based on 3. digit BEFORE decimal point + // Round 3. digit BEFORE decimal point floatx.ceiling(12.0654, option.Some(-3)) |> should.equal(Ok(1000.0)) } pub fn float_floor_test() { - // Round based on 3. digit AFTER decimal point + // Round 3. digit AFTER decimal point floatx.floor(12.0654, option.Some(3)) |> should.equal(Ok(12.065)) - // Round based on 2. digit AFTER decimal point + // Round 2. digit AFTER decimal point floatx.floor(12.0654, option.Some(2)) |> should.equal(Ok(12.06)) - // Round based on 1. digit AFTER decimal point + // Round 1. digit AFTER decimal point floatx.floor(12.0654, option.Some(1)) |> should.equal(Ok(12.0)) - // Round based on 0. digit BEFORE decimal point + // Round 0. digit BEFORE decimal point floatx.floor(12.0654, option.Some(0)) |> should.equal(Ok(12.0)) - // Round based on 1. digit BEFORE decimal point + // Round 1. digit BEFORE decimal point floatx.floor(12.0654, option.Some(-1)) |> should.equal(Ok(10.0)) - // Round based on 2. digit BEFORE decimal point + // Round 2. digit BEFORE decimal point floatx.floor(12.0654, option.Some(-2)) |> should.equal(Ok(0.0)) - // Round based on 2. digit BEFORE decimal point + // Round 2. digit BEFORE decimal point floatx.floor(12.0654, option.Some(-3)) |> should.equal(Ok(0.0)) } +pub fn float_truncate_test() { + // Round 3. digit AFTER decimal point + floatx.truncate(12.0654, option.Some(3)) + |> should.equal(Ok(12.065)) + + // Round 2. digit AFTER decimal point + floatx.truncate(12.0654, option.Some(2)) + |> should.equal(Ok(12.06)) + + // Round 1. digit AFTER decimal point + floatx.truncate(12.0654, option.Some(1)) + |> should.equal(Ok(12.0)) + + // Round 0. digit BEFORE decimal point + floatx.truncate(12.0654, option.Some(0)) + |> should.equal(Ok(12.0)) + + // Round 1. digit BEFORE decimal point + floatx.truncate(12.0654, option.Some(-1)) + |> should.equal(Ok(10.0)) + + // Round 2. digit BEFORE decimal point + floatx.truncate(12.0654, option.Some(-2)) + |> should.equal(Ok(0.0)) + + // Round 2. digit BEFORE decimal point + floatx.truncate(12.0654, option.Some(-3)) + |> should.equal(Ok(0.0)) +} + pub fn float_minimum_test() { floatx.minimum(0.75, 0.5) |> should.equal(0.5) @@ -636,6 +666,20 @@ pub fn float_flip_sign_test() { |> should.equal(100.0) } +pub fn float_copy_sign_test() { + floatx.copy_sign(100.0, 10.0) + |> should.equal(100.0) + + floatx.copy_sign(-100.0, 10.0) + |> should.equal(100.0) + + floatx.copy_sign(100.0, -10.0) + |> should.equal(-100.0) + + floatx.copy_sign(-100.0, -10.0) + |> should.equal(-100.0) +} + pub fn float_beta_function_test() { io.debug("TODO: Implement tests for 'float.beta'.") } @@ -649,6 +693,7 @@ pub fn float_gamma_function_test() { } pub fn math_round_to_nearest_test() { + // Try with positive values floatx.round(1.50, option.Some(0), option.Some("Nearest")) |> should.equal(Ok(2.0)) @@ -664,14 +709,45 @@ pub fn math_round_to_nearest_test() { floatx.round(4.50, option.Some(0), option.Some("Nearest")) |> should.equal(Ok(4.0)) + // Try with negative values floatx.round(-3.50, option.Some(0), option.Some("Nearest")) |> should.equal(Ok(-4.0)) floatx.round(-4.50, option.Some(0), option.Some("Nearest")) |> should.equal(Ok(-4.0)) + + // Round 3. digit AFTER decimal point + floatx.round(12.0654, option.Some(3), option.Some("Nearest")) + |> should.equal(Ok(12.065)) + + // Round 2. digit AFTER decimal point + floatx.round(12.0654, option.Some(2), option.Some("Nearest")) + |> should.equal(Ok(12.07)) + + // Round 1. digit AFTER decimal point + floatx.round(12.0654, option.Some(1), option.Some("Nearest")) + |> should.equal(Ok(12.1)) + + // Round 0. digit BEFORE decimal point + floatx.round(12.0654, option.Some(0), option.Some("Nearest")) + |> should.equal(Ok(12.0)) + + // Round 1. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-1), option.Some("Nearest")) + |> should.equal(Ok(10.0)) + + // Round 2. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-2), option.Some("Nearest")) + |> should.equal(Ok(0.0)) + + // Round 3. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-3), option.Some("Nearest")) + |> should.equal(Ok(0.0)) } pub fn math_round_up_test() { + // Note: Rounding mode "Up" is an alias for the ceiling function + // Try with positive values floatx.round(0.45, option.Some(0), option.Some("Up")) |> should.equal(Ok(1.0)) @@ -689,9 +765,29 @@ pub fn math_round_up_test() { floatx.round(0.505, option.Some(2), option.Some("Up")) |> should.equal(Ok(0.51)) + + // Try with negative values + floatx.round(-0.45, option.Some(0), option.Some("Up")) + |> should.equal(Ok(-0.0)) + + floatx.round(-0.50, option.Some(0), option.Some("Up")) + |> should.equal(Ok(-0.0)) + + floatx.round(-0.45, option.Some(1), option.Some("Up")) + |> should.equal(Ok(-0.4)) + + floatx.round(-0.50, option.Some(1), option.Some("Up")) + |> should.equal(Ok(-0.5)) + + floatx.round(-0.4550, option.Some(2), option.Some("Up")) + |> should.equal(Ok(-0.45)) + + floatx.round(-0.5050, option.Some(2), option.Some("Up")) + |> should.equal(Ok(-0.5)) } pub fn math_round_down_test() { + // Note: Rounding mode "Down" is an alias for the floor function // Try with positive values floatx.round(0.45, option.Some(0), option.Some("Down")) |> should.equal(Ok(0.0)) @@ -732,6 +828,8 @@ pub fn math_round_down_test() { } pub fn math_round_to_zero_test() { + // Note: Rounding mode "ToZero" is an alias for the truncate function + // Try with positive values floatx.round(0.50, option.Some(0), option.Some("ToZero")) |> should.equal(Ok(0.0)) @@ -749,9 +847,39 @@ pub fn math_round_to_zero_test() { floatx.round(0.5075, option.Some(2), option.Some("ToZero")) |> should.equal(Ok(0.50)) + + // Try with negative values + floatx.round(-0.50, option.Some(0), option.Some("ToZero")) + |> should.equal(Ok(0.0)) + + floatx.round(-0.75, option.Some(0), option.Some("ToZero")) + |> should.equal(Ok(0.0)) + + floatx.round(-0.45, option.Some(1), option.Some("ToZero")) + |> should.equal(Ok(-0.4)) + + floatx.round(-0.57, option.Some(1), option.Some("ToZero")) + |> should.equal(Ok(-0.50)) + + floatx.round(-0.4575, option.Some(2), option.Some("ToZero")) + |> should.equal(Ok(-0.45)) + + floatx.round(-0.5075, option.Some(2), option.Some("ToZero")) + |> should.equal(Ok(-0.50)) } pub fn math_round_ties_away_test() { + // Try with positive values + floatx.round(1.40, option.Some(0), option.Some("TiesAway")) + |> should.equal(Ok(1.0)) + + floatx.round(1.50, option.Some(0), option.Some("TiesAway")) + |> should.equal(Ok(2.0)) + + floatx.round(2.50, option.Some(0), option.Some("TiesAway")) + |> should.equal(Ok(3.0)) + + // Try with negative values floatx.round(-1.40, option.Some(0), option.Some("TiesAway")) |> should.equal(Ok(-1.0)) @@ -764,17 +892,47 @@ pub fn math_round_ties_away_test() { floatx.round(-2.50, option.Some(0), option.Some("TiesAway")) |> should.equal(Ok(-3.0)) - floatx.round(1.40, option.Some(0), option.Some("TiesAway")) - |> should.equal(Ok(1.0)) + // Round 3. digit AFTER decimal point + floatx.round(12.0654, option.Some(3), option.Some("TiesAway")) + |> should.equal(Ok(12.065)) - floatx.round(1.50, option.Some(0), option.Some("TiesAway")) - |> should.equal(Ok(2.0)) + // Round 2. digit AFTER decimal point + floatx.round(12.0654, option.Some(2), option.Some("TiesAway")) + |> should.equal(Ok(12.07)) - floatx.round(2.50, option.Some(0), option.Some("TiesAway")) - |> should.equal(Ok(3.0)) + // Round 1. digit AFTER decimal point + floatx.round(12.0654, option.Some(1), option.Some("TiesAway")) + |> should.equal(Ok(12.1)) + + // Round 0. digit BEFORE decimal point + floatx.round(12.0654, option.Some(0), option.Some("TiesAway")) + |> should.equal(Ok(12.0)) + + // Round 1. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-1), option.Some("TiesAway")) + |> should.equal(Ok(10.0)) + + // Round 2. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-2), option.Some("TiesAway")) + |> should.equal(Ok(0.0)) + + // Round 2. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-3), option.Some("TiesAway")) + |> should.equal(Ok(0.0)) } pub fn math_round_ties_up_test() { + // Try with positive values + floatx.round(1.40, option.Some(0), option.Some("TiesUp")) + |> should.equal(Ok(1.0)) + + floatx.round(1.50, option.Some(0), option.Some("TiesUp")) + |> should.equal(Ok(2.0)) + + floatx.round(2.50, option.Some(0), option.Some("TiesUp")) + |> should.equal(Ok(3.0)) + + // Try with negative values floatx.round(-1.40, option.Some(0), option.Some("TiesUp")) |> should.equal(Ok(-1.0)) @@ -787,14 +945,56 @@ pub fn math_round_ties_up_test() { floatx.round(-2.50, option.Some(0), option.Some("TiesUp")) |> should.equal(Ok(-2.0)) - floatx.round(1.40, option.Some(0), option.Some("TiesUp")) - |> should.equal(Ok(1.0)) + // Round 3. digit AFTER decimal point + floatx.round(12.0654, option.Some(3), option.Some("TiesUp")) + |> should.equal(Ok(12.065)) - floatx.round(1.50, option.Some(0), option.Some("TiesUp")) - |> should.equal(Ok(2.0)) + // Round 2. digit AFTER decimal point + floatx.round(12.0654, option.Some(2), option.Some("TiesUp")) + |> should.equal(Ok(12.07)) - floatx.round(2.50, option.Some(0), option.Some("TiesUp")) - |> should.equal(Ok(3.0)) + // Round 1. digit AFTER decimal point + floatx.round(12.0654, option.Some(1), option.Some("TiesUp")) + |> should.equal(Ok(12.1)) + + // Round 0. digit BEFORE decimal point + floatx.round(12.0654, option.Some(0), option.Some("TiesUp")) + |> should.equal(Ok(12.0)) + + // Round 1. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-1), option.Some("TiesUp")) + |> should.equal(Ok(10.0)) + + // Round 2. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-2), option.Some("TiesUp")) + |> should.equal(Ok(0.0)) + + // Round 2. digit BEFORE decimal point + floatx.round(12.0654, option.Some(-3), option.Some("TiesUp")) + |> should.equal(Ok(0.0)) +} + +pub fn math_round_error_test() { + // Test invalid rounding mode + floatx.round(-1.50, option.Some(0), option.Some("XYZ")) + |> should.be_error() +} + +pub fn math_round_edge_cases_test() { + // The default number of digits is 0 if None is provided + floatx.round(12.0654, option.None, option.Some("Nearest")) + |> should.equal(Ok(12.0)) + + // The default rounding mode is "Nearest" if None is provided + floatx.round(12.0654, option.None, option.None) + |> should.equal(Ok(12.0)) + + // Test invalid rounding mode + floatx.round(12.0654, option.None, option.Some("XYZ")) + |> should.be_error() + + floatx.round(-1.50, option.Some(0), option.Some("XYZ")) + |> should.be_error() } pub fn float_incomplete_gamma_function_test() { @@ -865,3 +1065,14 @@ pub fn float_constants_test() { |> floatx.is_close(3.14159, 0.0, 0.00001) |> should.be_true() } + +pub fn float_is_close_test() { + let val: Float = 99.0 + let ref_val: Float = 100.0 + // We set 'atol' and 'rtol' such that the values are equivalent + // if 'val' is within 1 percent of 'ref_val' +/- 0.1 + let rtol: Float = 0.01 + let atol: Float = 0.10 + floatx.is_close(val, ref_val, rtol, atol) + |> should.be_true() +} diff --git a/test/gleam/gleam_community_maths_int_list_test.gleam b/test/gleam/gleam_community_maths_int_list_test.gleam new file mode 100644 index 0000000..8dfcdba --- /dev/null +++ b/test/gleam/gleam_community_maths_int_list_test.gleam @@ -0,0 +1,70 @@ +import gleam/int +import gleam/list +import gleam/pair +import gleam_community/maths/int_list +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +pub fn int_list_maximum_test() { + // An empty lists returns an error + [] + |> int_list.maximum() + |> should.be_error() + + // Valid input returns a result + [4, 4, 3, 2, 1] + |> int_list.maximum() + |> should.equal(Ok(4)) +} + +pub fn int_list_minimum_test() { + // An empty lists returns an error + [] + |> int_list.minimum() + |> should.be_error() + + // Valid input returns a result + [4, 4, 3, 2, 1] + |> int_list.minimum() + |> should.equal(Ok(1)) +} + +pub fn int_list_arg_maximum_test() { + // An empty lists returns an error + [] + |> int_list.arg_maximum() + |> should.be_error() + + // Valid input returns a result + [4, 4, 3, 2, 1] + |> int_list.arg_maximum() + |> should.equal(Ok([0, 1])) +} + +pub fn int_list_arg_minimum_test() { + // An empty lists returns an error + [] + |> int_list.arg_minimum() + |> should.be_error() + + // Valid input returns a result + [4, 4, 3, 2, 1] + |> int_list.arg_minimum() + |> should.equal(Ok([4])) +} + +pub fn int_list_extrema_test() { + // An empty lists returns an error + [] + |> int_list.extrema() + |> should.be_error() + + // Valid input returns a result + [4, 4, 3, 2, 1] + |> int_list.extrema() + |> should.equal(Ok(#(1, 4))) +} diff --git a/test/gleam/gleam_community_maths_int_test.gleam b/test/gleam/gleam_community_maths_int_test.gleam index 2a74ef4..771f4cf 100644 --- a/test/gleam/gleam_community_maths_int_test.gleam +++ b/test/gleam/gleam_community_maths_int_test.gleam @@ -156,6 +156,20 @@ pub fn int_flip_sign_test() { |> should.equal(100) } +pub fn int_copy_sign_test() { + intx.copy_sign(100, 10) + |> should.equal(100) + + intx.copy_sign(-100, 10) + |> should.equal(100) + + intx.copy_sign(100, -10) + |> should.equal(-100) + + intx.copy_sign(-100, -10) + |> should.equal(-100) +} + pub fn int_is_power_test() { intx.is_power(10, 10) |> should.equal(True)