Merge pull request from NicklasXYZ/main

Code improvements
This commit is contained in:
Nicklas Sindlev Andersen 2024-12-09 00:18:42 +01:00 committed by GitHub
commit 46d2309268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 348 additions and 391 deletions

View file

@ -110,13 +110,13 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// import gleam_community/maths
///
/// pub fn example() {
/// maths.int_euclidean_modulo(15, 4)
/// maths.euclidean_modulo(15, 4)
/// |> should.equal(3)
///
/// maths.int_euclidean_modulo(-3, -2)
/// maths.euclidean_modulo(-3, -2)
/// |> should.equal(1)
///
/// maths.int_euclidean_modulo(5, 0)
/// maths.euclidean_modulo(5, 0)
/// |> should.equal(0)
/// }
/// </details>
@ -127,7 +127,7 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// </a>
/// </div>
///
pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
pub fn euclidean_modulo(x: Int, y: Int) -> Int {
case x % y, x, y {
_, 0, _ -> 0
_, _, 0 -> 0
@ -302,15 +302,15 @@ pub fn proper_divisors(n: Int) -> List(Int) {
///
/// pub fn example () {
/// []
/// |> maths.float_weighted_sum()
/// |> maths.weighted_sum()
/// |> should.equal(Ok(0.0))
///
/// [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
/// |> maths.float_weighted_sum()
/// |> maths.weighted_sum()
/// |> should.equal(Ok(6.0))
///
/// [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
/// |> maths.float_weighted_sum()
/// |> maths.weighted_sum()
/// |> should.equal(Ok(14.5))
/// }
/// </details>
@ -321,12 +321,11 @@ pub fn proper_divisors(n: Int) -> List(Int) {
/// </a>
/// </div>
///
pub fn float_weighted_sum(arr: List(#(Float, Float))) -> Result(Float, Nil) {
pub fn weighted_sum(arr: List(#(Float, Float))) -> Result(Float, Nil) {
case arr {
[] -> Ok(0.0)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.1 <. 0.0 })
case weight_is_negative {
True -> Error(Nil)
False -> {
@ -364,17 +363,17 @@ pub fn float_weighted_sum(arr: List(#(Float, Float))) -> Result(Float, Nil) {
///
/// pub fn example () {
/// []
/// |> maths.float_weighted_product()
/// |> maths.weighted_product()
/// |> should.equal(Ok(1.0))
///
/// [#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
/// |> maths.float_weighted_product()
/// |> maths.weighted_product()
/// |> should.equal(Ok(6.0))
///
/// let assert Ok(tolerance) = float.power(10.0, -6.0)
/// let assert Ok(result) =
/// [#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
/// |> maths.float_weighted_product()
/// |> maths.weighted_product()
/// result
/// |> maths.is_close(30.0, 0.0, tolerance)
/// |> should.be_true()
@ -387,22 +386,18 @@ pub fn float_weighted_sum(arr: List(#(Float, Float))) -> Result(Float, Nil) {
/// </a>
/// </div>
///
pub fn float_weighted_product(arr: List(#(Float, Float))) -> Result(Float, Nil) {
pub fn weighted_product(arr: List(#(Float, Float))) -> Result(Float, Nil) {
case arr {
[] -> Ok(1.0)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.1 <. 0.0 })
case weight_is_negative {
True -> Error(Nil)
False -> {
list.map(arr, fn(a: #(Float, Float)) -> Result(Float, Nil) {
float.power(a.0, a.1)
})
|> result.all
|> result.map(fn(prods) {
prods
|> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc })
list.map(arr, fn(tuple) { float.power(tuple.0, tuple.1) })
|> result.all()
|> result.map(fn(products) {
list.fold(products, 1.0, fn(acc, element) { element *. acc })
})
}
}
@ -435,11 +430,11 @@ pub fn float_weighted_product(arr: List(#(Float, Float))) -> Result(Float, Nil)
///
/// pub fn example () {
/// []
/// |> maths.float_cumulative_sum()
/// |> maths.cumulative_sum()
/// |> should.equal([])
///
/// [1.0, 2.0, 3.0]
/// |> maths.float_cumulative_sum()
/// |> maths.cumulative_sum()
/// |> should.equal([1.0, 3.0, 6.0])
/// }
/// </details>
@ -450,10 +445,10 @@ pub fn float_weighted_product(arr: List(#(Float, Float))) -> Result(Float, Nil)
/// </a>
/// </div>
///
pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
pub fn cumulative_sum(arr: List(Float)) -> List(Float) {
case arr {
[] -> []
_ -> list.scan(arr, 0.0, fn(acc, a) { a +. acc })
_ -> list.scan(arr, 0.0, fn(acc, element) { element +. acc })
}
}
@ -500,7 +495,7 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ -> list.scan(arr, 0, fn(acc, a) { a + acc })
_ -> list.scan(arr, 0, fn(acc, element) { element + acc })
}
}
@ -530,11 +525,11 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
///
/// pub fn example () {
/// []
/// |> maths.float_cumulative_product()
/// |> maths.cumulative_product()
/// |> should.equal([])
///
/// [1.0, 2.0, 3.0]
/// |> maths.float_cumulative_product()
/// |> maths.cumulative_product()
/// |> should.equal([1.0, 2.0, 6.0])
/// }
/// </details>
@ -545,10 +540,10 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
/// </a>
/// </div>
///
pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
pub fn cumulative_product(arr: List(Float)) -> List(Float) {
case arr {
[] -> []
_ -> list.scan(arr, 1.0, fn(acc, a) { a *. acc })
_ -> list.scan(arr, 1.0, fn(acc, element) { element *. acc })
}
}
@ -596,7 +591,7 @@ pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
pub fn int_cumulative_product(arr: List(Int)) -> List(Int) {
case arr {
[] -> []
_ -> list.scan(arr, 1, fn(acc, a) { a * acc })
_ -> list.scan(arr, 1, fn(acc, element) { element * acc })
}
}
@ -1792,15 +1787,15 @@ pub fn round_to_nearest(x: Float, p: Int) -> Float {
let xabs_truncated = truncate_float(xabs)
let remainder = xabs -. xabs_truncated
case remainder {
_ if remainder >. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p
_ if remainder >. 0.5 -> sign(x) *. truncate_float(xabs +. 1.0) /. p
_ if remainder == 0.5 -> {
let assert Ok(is_even) = int.modulo(float.truncate(xabs), 2)
case is_even == 0 {
True -> float_sign(x) *. xabs_truncated /. p
False -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p
True -> sign(x) *. xabs_truncated /. p
False -> sign(x) *. truncate_float(xabs +. 1.0) /. p
}
}
_ -> float_sign(x) *. xabs_truncated /. p
_ -> sign(x) *. xabs_truncated /. p
}
}
@ -1855,8 +1850,8 @@ pub fn round_ties_away(x: Float, p: Int) -> Float {
let xabs = float.absolute_value(x) *. p
let remainder = xabs -. truncate_float(xabs)
case remainder {
_ if remainder >=. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p
_ -> float_sign(x) *. truncate_float(xabs) /. p
_ if remainder >=. 0.5 -> sign(x) *. truncate_float(xabs +. 1.0) /. p
_ -> sign(x) *. truncate_float(xabs) /. p
}
}
@ -1913,8 +1908,8 @@ pub fn round_ties_up(x: Float, p: Int) -> Float {
let remainder = xabs -. xabs_truncated
case remainder {
_ if remainder >=. 0.5 && x >=. 0.0 ->
float_sign(x) *. truncate_float(xabs +. 1.0) /. p
_ -> float_sign(x) *. xabs_truncated /. p
sign(x) *. truncate_float(xabs +. 1.0) /. p
_ -> sign(x) *. xabs_truncated /. p
}
}
@ -2109,10 +2104,10 @@ fn do_ceiling(a: Float) -> Float
/// import gleam_community/maths
///
/// pub fn example() {
/// maths.float_absolute_difference(-10.0, 10.0)
/// maths.absolute_difference(-10.0, 10.0)
/// |> should.equal(20.0)
///
/// maths.float_absolute_difference(0.0, -2.0)
/// maths.absolute_difference(0.0, -2.0)
/// |> should.equal(2.0)
/// }
/// </details>
@ -2123,9 +2118,8 @@ fn do_ceiling(a: Float) -> Float
/// </a>
/// </div>
///
pub fn float_absolute_difference(a: Float, b: Float) -> Float {
a -. b
|> float.absolute_value()
pub fn absolute_difference(a: Float, b: Float) -> Float {
float.absolute_value(a -. b)
}
/// <div style="text-align: right;">
@ -2165,8 +2159,7 @@ pub fn float_absolute_difference(a: Float, b: Float) -> Float {
/// </div>
///
pub fn int_absolute_difference(a: Int, b: Int) -> Int {
a - b
|> int.absolute_value()
int.absolute_value(a - b)
}
/// <div style="text-align: right;">
@ -2185,12 +2178,12 @@ pub fn int_absolute_difference(a: Int, b: Int) -> Int {
/// </a>
/// </div>
///
pub fn float_sign(x: Float) -> Float {
do_float_sign(x)
pub fn sign(x: Float) -> Float {
do_sign(x)
}
@target(erlang)
fn do_float_sign(x: Float) -> Float {
@external(javascript, "../maths.mjs", "sign")
fn do_sign(x: Float) -> Float {
case x {
_ if x <. 0.0 -> -1.0
_ if x >. 0.0 -> 1.0
@ -2198,10 +2191,6 @@ fn do_float_sign(x: Float) -> Float {
}
}
@target(javascript)
@external(javascript, "../maths.mjs", "sign")
fn do_float_sign(a: Float) -> Float
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
@ -2222,7 +2211,7 @@ pub fn int_sign(x: Int) -> Int {
do_int_sign(x)
}
@target(erlang)
@external(javascript, "../maths.mjs", "sign")
fn do_int_sign(x: Int) -> Int {
case x {
_ if x < 0 -> -1
@ -2231,10 +2220,6 @@ fn do_int_sign(x: Int) -> Int {
}
}
@target(javascript)
@external(javascript, "../maths.mjs", "sign")
fn do_int_sign(a: Int) -> Int
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
@ -2250,14 +2235,14 @@ fn do_int_sign(a: Int) -> Int
/// </a>
/// </div>
///
pub fn float_copy_sign(x: Float, y: Float) -> Float {
case float_sign(x) == float_sign(y) {
pub fn copy_sign(x: Float, y: Float) -> Float {
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 -> float_flip_sign(x)
False -> flip_sign(x)
}
}
@ -2301,7 +2286,7 @@ pub fn int_copy_sign(x: Int, y: Int) -> Int {
/// </a>
/// </div>
///
pub fn float_flip_sign(x: Float) -> Float {
pub fn flip_sign(x: Float) -> Float {
-1.0 *. x
}
@ -2772,7 +2757,9 @@ pub fn combination(n: Int, k: Int) -> Result(Int, Nil) {
False -> n - k
}
Ok(
list.fold(list.range(1, min), 1, fn(acc, x) { acc * { n + 1 - x } / x }),
list.fold(list.range(1, min), 1, fn(acc, element) {
acc * { n + 1 - element } / element
}),
)
}
}
@ -3101,8 +3088,8 @@ fn remove_first_by_index(
arr: Yielder(#(Int, a)),
index_to_remove: Int,
) -> Yielder(#(Int, a)) {
yielder.flat_map(arr, fn(arg) {
let #(index, element) = arg
yielder.flat_map(arr, fn(tuple) {
let #(index, element) = tuple
case index == index_to_remove {
True -> yielder.empty()
False -> yielder.single(#(index, element))
@ -3149,7 +3136,8 @@ pub fn list_permutation(arr: List(a), k: Int) -> Result(Yielder(List(a)), Nil) {
_, _ if k < 0 -> Error(Nil)
_, arr_length if k > arr_length -> Error(Nil)
_, _ -> {
let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
let indexed_arr =
list.index_map(arr, fn(element, index) { #(index, element) })
Ok(do_list_permutation_without_repetitions(
yielder.from_list(indexed_arr),
k,
@ -3165,8 +3153,8 @@ fn do_list_permutation_without_repetitions(
case k {
0 -> yielder.single([])
_ ->
yielder.flat_map(arr, fn(arg) {
let #(index, element) = arg
yielder.flat_map(arr, fn(tuple) {
let #(index, element) = tuple
let remaining = remove_first_by_index(arr, index)
let permutations =
do_list_permutation_without_repetitions(remaining, k - 1)
@ -3217,21 +3205,22 @@ pub fn list_permutation_with_repetitions(
case k {
_ if k < 0 -> Error(Nil)
_ -> {
let indexed_arr = list.index_map(arr, fn(x, i) { #(i, x) })
Ok(do_list_permutation_with_repetitions(indexed_arr, k))
let indexed_arr =
list.index_map(arr, fn(element, index) { #(index, element) })
Ok(do_list_permutation_with_repetitions(yielder.from_list(indexed_arr), k))
}
}
}
fn do_list_permutation_with_repetitions(
arr: List(#(Int, a)),
arr: Yielder(#(Int, a)),
k: Int,
) -> Yielder(List(a)) {
case k {
0 -> yielder.single([])
_ ->
yielder.flat_map(arr |> yielder.from_list, fn(arg) {
let #(_, element) = arg
yielder.flat_map(arr, fn(tuple) {
let #(_, element) = tuple
// Allow the same element (by index) to be reused in future recursive calls
let permutations = do_list_permutation_with_repetitions(arr, k - 1)
// Prepend the current element to each generated permutation
@ -3275,9 +3264,9 @@ fn do_list_permutation_with_repetitions(
/// </div>
///
pub fn cartesian_product(xset: set.Set(a), yset: set.Set(b)) -> set.Set(#(a, b)) {
set.fold(xset, set.new(), fn(accumulator0: set.Set(#(a, b)), member0: a) {
set.fold(yset, accumulator0, fn(accumulator1: set.Set(#(a, b)), member1: b) {
set.insert(accumulator1, #(member0, member1))
set.fold(xset, set.new(), fn(acc0, element0) {
set.fold(yset, acc0, fn(acc1, element1) {
set.insert(acc1, #(element0, element1))
})
})
}
@ -3329,9 +3318,9 @@ pub fn norm(arr: List(Float), p: Float) -> Result(Float, Nil) {
[] -> Ok(0.0)
_ -> {
let aggregate =
list.fold(arr, 0.0, fn(accumulator, element) {
list.fold(arr, 0.0, fn(acc, element) {
let assert Ok(result) = float.power(float.absolute_value(element), p)
result +. accumulator
result +. acc
})
float.power(aggregate, 1.0 /. p)
}
@ -3388,15 +3377,14 @@ pub fn norm_with_weights(
case arr {
[] -> Ok(0.0)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float)) { tuple.1 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.1 <. 0.0 })
case weight_is_negative {
False -> {
let aggregate =
list.fold(arr, 0.0, fn(accumulator, tuple) {
list.fold(arr, 0.0, fn(acc, tuple) {
let assert Ok(result) =
float.power(float.absolute_value(tuple.0), p)
tuple.1 *. result +. accumulator
tuple.1 *. result +. acc
})
float.power(aggregate, 1.0 /. p)
}
@ -3537,10 +3525,7 @@ pub fn minkowski_distance(
case p <. 1.0 {
True -> Error(Nil)
False -> {
let differences =
list.map(arr, fn(tuple: #(Float, Float)) -> Float {
tuple.0 -. tuple.1
})
let differences = list.map(arr, fn(tuple) { tuple.0 -. tuple.1 })
norm(differences, p)
}
}
@ -3601,15 +3586,12 @@ pub fn minkowski_distance_with_weights(
case arr {
[] -> Error(Nil)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.2 <. 0.0 })
case p <. 1.0, weight_is_negative {
False, False -> {
let differences =
list.map(arr, fn(tuple: #(Float, Float, Float)) -> #(Float, Float) {
#(tuple.0 -. tuple.1, tuple.2)
})
list.map(arr, fn(tuple) { #(tuple.0 -. tuple.1, tuple.2) })
norm_with_weights(differences, p)
}
_, _ -> Error(Nil)
@ -3793,8 +3775,7 @@ pub fn chebyshev_distance_with_weights(
case arr {
[] -> Error(Nil)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.2 <. 0.0 })
case weight_is_negative {
True -> Error(Nil)
@ -3959,7 +3940,7 @@ fn do_median(
/// <small>Back to top </small>
/// </a>
/// </div>
///
///
pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, Nil) {
case arr, ddof {
[], _ -> Error(Nil)
@ -3967,13 +3948,13 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, Nil) {
_, _ -> {
let assert Ok(mean) = mean(arr)
Ok(
list.map(arr, fn(a: Float) -> Float {
let assert Ok(result) = float.power(a -. mean, 2.0)
list.map(arr, fn(element) {
let assert Ok(result) = float.power(element -. mean, 2.0)
result
})
|> float.sum()
|> fn(a: Float) -> Float {
a /. { int.to_float(list.length(arr)) -. int.to_float(ddof) }
|> fn(element) {
element /. { int.to_float(list.length(arr)) -. int.to_float(ddof) }
},
)
}
@ -4328,13 +4309,10 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
///
pub fn cosine_similarity(arr: List(#(Float, Float))) -> Result(Float, Nil) {
let numerator =
arr
|> list.fold(0.0, fn(accumulator, tuple) {
accumulator +. tuple.0 *. tuple.1
})
list.fold(arr, 0.0, fn(acc, tuple) { acc +. tuple.0 *. tuple.1 })
let xarr = arr |> list.map(fn(tuple: #(Float, Float)) { tuple.0 })
let yarr = arr |> list.map(fn(tuple: #(Float, Float)) { tuple.1 })
let xarr = list.map(arr, fn(tuple) { tuple.0 })
let yarr = list.map(arr, fn(tuple) { tuple.1 })
let assert Ok(xarr_norm) = norm(xarr, 2.0)
let assert Ok(yarr_norm) = norm(yarr, 2.0)
@ -4411,26 +4389,21 @@ pub fn cosine_similarity(arr: List(#(Float, Float))) -> Result(Float, Nil) {
pub fn cosine_similarity_with_weights(
arr: List(#(Float, Float, Float)),
) -> Result(Float, Nil) {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.2 <. 0.0 })
case weight_is_negative {
False -> {
let numerator =
arr
|> list.fold(0.0, fn(accumulator, tuple) {
accumulator +. tuple.0 *. tuple.1 *. tuple.2
list.fold(arr, 0.0, fn(acc, tuple) {
acc +. tuple.0 *. tuple.1 *. tuple.2
})
let xarr =
arr
|> list.map(fn(tuple: #(Float, Float, Float)) { #(tuple.0, tuple.2) })
let yarr =
arr
|> list.map(fn(tuple: #(Float, Float, Float)) { #(tuple.1, tuple.2) })
let xarr = list.map(arr, fn(tuple) { #(tuple.0, tuple.2) })
let yarr = list.map(arr, fn(tuple) { #(tuple.1, tuple.2) })
let assert Ok(xarr_norm) = norm_with_weights(xarr, 2.0)
let assert Ok(yarr_norm) = norm_with_weights(yarr, 2.0)
let denominator = {
xarr_norm *. yarr_norm
}
@ -4482,12 +4455,12 @@ pub fn canberra_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
[] -> Error(Nil)
_ -> {
Ok(
list.fold(arr, 0.0, fn(accumulator, tuple) {
list.fold(arr, 0.0, fn(acc, tuple) {
let numerator = float.absolute_value({ tuple.0 -. tuple.1 })
let denominator = {
float.absolute_value(tuple.0) +. float.absolute_value(tuple.1)
}
accumulator +. numerator /. denominator
acc +. numerator /. denominator
}),
)
}
@ -4538,19 +4511,18 @@ pub fn canberra_distance_with_weights(
case arr {
[] -> Error(Nil)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.2 <. 0.0 })
case weight_is_negative {
True -> Error(Nil)
False -> {
Ok(
list.fold(arr, 0.0, fn(accumulator, tuple) {
list.fold(arr, 0.0, fn(acc, tuple) {
let numerator = float.absolute_value({ tuple.0 -. tuple.1 })
let denominator = {
float.absolute_value(tuple.0) +. float.absolute_value(tuple.1)
}
accumulator +. tuple.2 *. numerator /. denominator
acc +. tuple.2 *. numerator /. denominator
}),
)
}
@ -4605,13 +4577,13 @@ pub fn braycurtis_distance(arr: List(#(Float, Float))) -> Result(Float, Nil) {
[] -> Error(Nil)
_ -> {
let numerator =
list.fold(arr, 0.0, fn(accumulator, tuple) {
accumulator +. float.absolute_value({ tuple.0 -. tuple.1 })
list.fold(arr, 0.0, fn(acc, tuple) {
acc +. float.absolute_value({ tuple.0 -. tuple.1 })
})
let denominator =
list.fold(arr, 0.0, fn(accumulator, tuple) {
accumulator +. float.absolute_value({ tuple.0 +. tuple.1 })
list.fold(arr, 0.0, fn(acc, tuple) {
acc +. float.absolute_value({ tuple.0 +. tuple.1 })
})
Ok({ numerator /. denominator })
@ -4666,24 +4638,19 @@ pub fn braycurtis_distance_with_weights(
case arr {
[] -> Error(Nil)
_ -> {
let weight_is_negative =
list.any(arr, fn(tuple: #(Float, Float, Float)) { tuple.2 <. 0.0 })
let weight_is_negative = list.any(arr, fn(tuple) { tuple.2 <. 0.0 })
case weight_is_negative {
True -> Error(Nil)
False -> {
let numerator =
list.fold(arr, 0.0, fn(accumulator, tuple) {
accumulator
+. tuple.2
*. float.absolute_value({ tuple.0 -. tuple.1 })
list.fold(arr, 0.0, fn(acc, tuple) {
acc +. tuple.2 *. float.absolute_value({ tuple.0 -. tuple.1 })
})
let denominator =
list.fold(arr, 0.0, fn(accumulator, tuple) {
accumulator
+. tuple.2
*. float.absolute_value({ tuple.0 +. tuple.1 })
list.fold(arr, 0.0, fn(acc, tuple) {
acc +. tuple.2 *. float.absolute_value({ tuple.0 +. tuple.1 })
})
Ok({ numerator /. denominator })
@ -4734,12 +4701,9 @@ pub fn braycurtis_distance_with_weights(
/// </div>
///
pub fn is_close(x: Float, y: Float, rtol: Float, atol: Float) -> Bool {
let x = float_absolute_difference(x, y)
let x = absolute_difference(x, y)
let y = atol +. rtol *. float.absolute_value(y)
case x <=. y {
True -> True
False -> False
}
x <=. y
}
/// <div style="text-align: right;">
@ -5196,7 +5160,7 @@ pub fn erf(x: Float) -> Float {
]
let p = 0.3275911
let sign = float_sign(x)
let sign = sign(x)
let x = float.absolute_value(x)
// Formula 7.1.26 given in Abramowitz and Stegun.
@ -5368,8 +5332,8 @@ pub fn arange(start: Float, stop: Float, increment: Float) -> Yielder(Float) {
let distance = float.absolute_value(start -. stop)
let num = float.round(distance /. increment_abs)
yielder.map(yielder.range(0, num - 1), fn(i) {
start +. int.to_float(i) *. increment_abs *. direction
yielder.map(yielder.range(0, num - 1), fn(index) {
start +. int.to_float(index) *. increment_abs *. direction
})
}
}
@ -5436,8 +5400,8 @@ pub fn linear_space(
case steps > 0 {
True -> {
Ok(
yielder.map(yielder.range(0, steps - 1), fn(i) {
start +. int.to_float(i) *. increment *. direction
yielder.map(yielder.range(0, steps - 1), fn(index) {
start +. int.to_float(index) *. increment *. direction
}),
)
}
@ -5455,6 +5419,11 @@ pub fn linear_space(
/// interval. The endpoint of the interval can optionally be included/excluded. The number of
/// points, base, and whether the endpoint is included determine the spacing between values.
///
/// The values in the sequence are computed as powers of the given base, where the exponents are
/// evenly spaced between `start` and `stop`. The `base` parameter must be positive, as negative
/// bases lead to undefined behavior when computing fractional exponents. Similarly, the number of
/// points (`steps`) must be positive; specifying zero or a negative value will result in an error.
///
/// <details>
/// <summary>Example:</summary>
///
@ -5482,7 +5451,7 @@ pub fn linear_space(
/// <small>Back to top </small>
/// </a>
/// </div>
///
///
pub fn logarithmic_space(
start: Float,
stop: Float,
@ -5490,13 +5459,13 @@ pub fn logarithmic_space(
endpoint: Bool,
base: Float,
) -> Result(Yielder(Float), Nil) {
case steps > 0 {
case steps > 0 && base >=. 0.0 {
True -> {
let assert Ok(linspace) = linear_space(start, stop, steps, endpoint)
Ok(
yielder.map(linspace, fn(i) {
let assert Ok(result) = float.power(base, i)
yielder.map(linspace, fn(value) {
let assert Ok(result) = float.power(base, value)
result
}),
)
@ -5511,11 +5480,20 @@ pub fn logarithmic_space(
/// </a>
/// </div>
///
/// The function returns an iterator of numbers spaced evenly on a log scale (a geometric
/// progression). Each point in the list is a constant multiple of the previous. The function is
/// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
/// specified directly.
/// The function returns an iterator for generating a geometric progression between two specified
/// values, where each value is a constant multiple of the previous one. Unlike
/// [`logarithmic_space`](#logarithmic_space), this function allows specifying the starting
/// and ending values (`start` and `stop`) directly, without requiring them to be transformed
/// into exponents.
///
/// Internally, the function computes the logarithms of `start` and `stop` and generates evenly
/// spaced points in the logarithmic domain (using base 10). These points are then transformed back
/// into their original scale to create a sequence of values that grow multiplicatively.
///
/// The `start` and `stop` values must be positive, as logarithms are undefined for non-positive
/// values. The number of points (`steps`) must also be positive; specifying zero or a negative
/// value will result in an error.
///
/// <details>
/// <summary>Example:</summary>
///
@ -5532,7 +5510,7 @@ pub fn logarithmic_space(
/// |> list.all(fn(x) { x == True })
/// |> should.be_true()
///
/// // Input (start and stop can't be equal to 0.0)
/// // Input (start and stop can't be less than or equal to 0.0)
/// maths.geometric_space(0.0, 1000.0, 3, False)
/// |> should.be_error()
///
@ -5557,75 +5535,13 @@ pub fn geometric_space(
steps: Int,
endpoint: Bool,
) -> Result(Yielder(Float), Nil) {
case start == 0.0 || stop == 0.0 {
case start <=. 0.0 || stop <=. 0.0 || steps < 0 {
True -> Error(Nil)
False ->
case steps > 0 {
True -> {
let assert Ok(log_start) = logarithm_10(start)
let assert Ok(log_stop) = logarithm_10(stop)
logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
}
False -> Error(Nil)
}
}
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// The function returns an iterator of exponentially spaced points over a specified interval. The
/// endpoint of the interval can optionally be included/excluded. The number of points and whether
/// the endpoint is included determine the spacing between values. The sequence is generated by
/// computing intermediate values in a logarithmic domain and transforming them into the exponential
/// domain.
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleam/yielder
/// import gleeunit/should
/// import gleam_community/maths
///
/// pub fn example() {
/// let assert Ok(tolerance) = float.power(10.0, -6.0)
/// let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
/// let expected = [1.0, 10.0, 100.0, 1000.0]
/// let pairs = exp_space |> yielder.to_list() |> list.zip(expected)
/// let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
/// result |> list.all(fn(x) { x == True }) |> should.be_true()
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
pub fn exponential_space(
start: Float,
stop: Float,
steps: Int,
endpoint: Bool,
) -> Result(Yielder(Float), Nil) {
case steps > 0 {
True -> {
False -> {
let assert Ok(log_start) = logarithm_10(start)
let assert Ok(log_stop) = logarithm_10(stop)
let assert Ok(log_space) =
linear_space(log_start, log_stop, steps, endpoint)
Ok(
yielder.map(log_space, fn(log_value) {
let assert Ok(exp_value) = float.power(10.0, log_value)
exp_value
}),
)
logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
}
False -> Error(Nil)
}
}
@ -5635,8 +5551,8 @@ pub fn exponential_space(
/// </a>
/// </div>
///
/// Generates evenly spaced points around a center value. The total span is determined by
/// the radius argument of the function.
/// Generates evenly spaced points around a center value. The total span (around the center value)
/// is determined by the `radius` argument of the function.
///
/// <details>
/// <summary>Example:</summary>
@ -5650,6 +5566,12 @@ pub fn exponential_space(
/// sym_space
/// |> yielder.to_list()
/// |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
///
/// // A negative radius reverses the order of the values
/// let assert Ok(sym_space) = maths.symmetric_space(0.0, -5.0, 5)
/// sym_space
/// |> yielder.to_list()
/// |> should.equal([5.0, 2.5, 0.0, -2.5, -5.0])
/// }
/// </details>
///

View file

@ -22,35 +22,35 @@ pub fn int_gcd_test() {
|> should.equal(6)
}
pub fn int_euclidean_modulo_test() {
pub fn euclidean_modulo_test() {
// Base Case: Positive x, Positive y
// Note that the truncated, floored, and euclidean
// definitions should agree for this base case
maths.int_euclidean_modulo(15, 4)
maths.euclidean_modulo(15, 4)
|> should.equal(3)
// Case: Positive x, Negative y
maths.int_euclidean_modulo(15, -4)
maths.euclidean_modulo(15, -4)
|> should.equal(3)
// Case: Negative x, Positive y
maths.int_euclidean_modulo(-15, 4)
maths.euclidean_modulo(-15, 4)
|> should.equal(1)
// Case: Negative x, Negative y
maths.int_euclidean_modulo(-15, -4)
maths.euclidean_modulo(-15, -4)
|> should.equal(1)
// Case: Positive x, Zero y
maths.int_euclidean_modulo(5, 0)
maths.euclidean_modulo(5, 0)
|> should.equal(0)
// Case: Zero x, Negative y
maths.int_euclidean_modulo(0, 5)
maths.euclidean_modulo(0, 5)
|> should.equal(0)
}
pub fn int_lcm_test() {
pub fn lcm_test() {
maths.lcm(1, 1)
|> should.equal(1)
@ -70,7 +70,7 @@ pub fn int_lcm_test() {
|> should.equal(210)
}
pub fn int_proper_divisors_test() {
pub fn proper_divisors_test() {
maths.proper_divisors(2)
|> should.equal([1])
@ -84,7 +84,7 @@ pub fn int_proper_divisors_test() {
|> should.equal([1, 2, 3, 6, 9])
}
pub fn int_divisors_test() {
pub fn divisors_test() {
maths.divisors(2)
|> should.equal([1, 2])
@ -98,19 +98,19 @@ pub fn int_divisors_test() {
|> should.equal([1, 2, 3, 6, 9, 18])
}
pub fn float_list_cumulative_sum_test() {
pub fn list_cumulative_sum_test() {
// An empty lists returns an empty list
[]
|> maths.float_cumulative_sum()
|> maths.cumulative_sum()
|> should.equal([])
// Valid input returns a result
[1.0, 2.0, 3.0]
|> maths.float_cumulative_sum()
|> maths.cumulative_sum()
|> should.equal([1.0, 3.0, 6.0])
[-2.0, 4.0, 6.0]
|> maths.float_cumulative_sum()
|> maths.cumulative_sum()
|> should.equal([-2.0, 2.0, 8.0])
}
@ -130,19 +130,19 @@ pub fn int_list_cumulative_sum_test() {
|> should.equal([-2, 2, 8])
}
pub fn float_list_cumulative_product_test() {
pub fn list_cumulative_product_test() {
// An empty lists returns an empty list
[]
|> maths.float_cumulative_product()
|> maths.cumulative_product()
|> should.equal([])
// Valid input returns a result
[1.0, 2.0, 3.0]
|> maths.float_cumulative_product()
|> maths.cumulative_product()
|> should.equal([1.0, 2.0, 6.0])
[-2.0, 4.0, 6.0]
|> maths.float_cumulative_product()
|> maths.cumulative_product()
|> should.equal([-2.0, -8.0, -48.0])
}
@ -162,42 +162,42 @@ pub fn int_list_cumulative_product_test() {
|> should.equal([-2, -8, -48])
}
pub fn float_weighted_product_test() {
pub fn weighted_product_test() {
[]
|> maths.float_weighted_product()
|> maths.weighted_product()
|> should.equal(Ok(1.0))
[#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
|> maths.float_weighted_product()
|> maths.weighted_product()
|> should.equal(Ok(1.0))
[#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
|> maths.float_weighted_product()
|> maths.weighted_product()
|> should.equal(Ok(6.0))
let assert Ok(tolerance) = float.power(10.0, -6.0)
let assert Ok(result) =
[#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
|> maths.float_weighted_product()
|> maths.weighted_product()
result
|> maths.is_close(30.0, 0.0, tolerance)
|> should.be_true()
}
pub fn float_weighted_sum_test() {
pub fn weighted_sum_test() {
[]
|> maths.float_weighted_sum()
|> maths.weighted_sum()
|> should.equal(Ok(0.0))
[#(1.0, 0.0), #(2.0, 0.0), #(3.0, 0.0)]
|> maths.float_weighted_sum()
|> maths.weighted_sum()
|> should.equal(Ok(0.0))
[#(1.0, 1.0), #(2.0, 1.0), #(3.0, 1.0)]
|> maths.float_weighted_sum()
|> maths.weighted_sum()
|> should.equal(Ok(6.0))
[#(9.0, 0.5), #(10.0, 0.5), #(10.0, 0.5)]
|> maths.float_weighted_sum()
|> maths.weighted_sum()
|> should.equal(Ok(14.5))
}

View file

@ -2,7 +2,7 @@ import gleam/float
import gleam_community/maths
import gleeunit/should
pub fn float_to_degree_test() {
pub fn to_degree_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
maths.radians_to_degrees(0.0)
|> maths.is_close(0.0, 0.0, tol)
@ -13,7 +13,7 @@ pub fn float_to_degree_test() {
|> should.be_true()
}
pub fn float_to_radian_test() {
pub fn to_radian_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
maths.degrees_to_radians(0.0)
|> maths.is_close(0.0, 0.0, tol)
@ -24,7 +24,7 @@ pub fn float_to_radian_test() {
|> should.be_true()
}
pub fn float_cartesian_to_polar_test() {
pub fn cartesian_to_polar_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Test: Cartesian (1, 0) -> Polar (1, 0)
@ -68,7 +68,7 @@ pub fn float_cartesian_to_polar_test() {
|> should.be_true()
}
pub fn float_polar_to_cartesian_test() {
pub fn polar_to_cartesian_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Test: Polar (1, 0) -> Cartesian (1, 0)

View file

@ -2,7 +2,7 @@ import gleam/float
import gleam_community/maths
import gleeunit/should
pub fn float_acos_test() {
pub fn acos_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -25,7 +25,7 @@ pub fn float_acos_test() {
|> should.be_error()
}
pub fn float_acosh_test() {
pub fn acosh_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -40,7 +40,7 @@ pub fn float_acosh_test() {
|> should.be_error()
}
pub fn float_asin_test() {
pub fn asin_test() {
// Check that the function agrees, at some arbitrary input
// points, with known function values
maths.asin(0.0)
@ -61,7 +61,7 @@ pub fn float_asin_test() {
|> should.be_error()
}
pub fn float_asinh_test() {
pub fn asinh_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -74,7 +74,7 @@ pub fn float_asinh_test() {
|> should.be_true()
}
pub fn float_atan_test() {
pub fn atan_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -135,7 +135,7 @@ pub fn math_atan2_test() {
|> should.be_true()
}
pub fn float_atanh_test() {
pub fn atanh_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -164,7 +164,7 @@ pub fn float_atanh_test() {
|> should.be_error()
}
pub fn float_cos_test() {
pub fn cos_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -181,7 +181,7 @@ pub fn float_cos_test() {
|> should.be_true()
}
pub fn float_cosh_test() {
pub fn cosh_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -198,7 +198,7 @@ pub fn float_cosh_test() {
// runtime.
}
pub fn float_sin_test() {
pub fn sin_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -215,7 +215,7 @@ pub fn float_sin_test() {
|> should.be_true()
}
pub fn float_sinh_test() {
pub fn sinh_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -266,7 +266,7 @@ pub fn math_tanh_test() {
|> should.be_true()
}
pub fn float_exponential_test() {
pub fn exponential_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -283,7 +283,7 @@ pub fn float_exponential_test() {
// runtime.
}
pub fn float_natural_logarithm_test() {
pub fn natural_logarithm_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -301,7 +301,7 @@ pub fn float_natural_logarithm_test() {
|> should.be_error()
}
pub fn float_logarithm_test() {
pub fn logarithm_test() {
// Check that the function agrees, at some arbitrary input
// points, with known function values
maths.logarithm(10.0, 10.0)
@ -334,7 +334,7 @@ pub fn float_logarithm_test() {
|> should.be_error()
}
pub fn float_logarithm_2_test() {
pub fn logarithm_2_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -355,7 +355,7 @@ pub fn float_logarithm_2_test() {
|> should.be_error()
}
pub fn float_logarithm_10_test() {
pub fn logarithm_10_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -380,7 +380,7 @@ pub fn float_logarithm_10_test() {
|> should.be_error()
}
pub fn float_nth_root_test() {
pub fn nth_root_test() {
maths.nth_root(9.0, 2)
|> should.equal(Ok(3.0))
@ -399,7 +399,7 @@ pub fn float_nth_root_test() {
|> should.be_error()
}
pub fn float_constants_test() {
pub fn constants_test() {
let assert Ok(tolerance) = float.power(10.0, -12.0)
// Test that the constant is approximately equal to 2.7128...

View file

@ -3,7 +3,7 @@ import gleam/set
import gleam_community/maths
import gleeunit/should
pub fn float_list_norm_test() {
pub fn list_norm_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// An empty lists returns 0.0
@ -63,7 +63,7 @@ pub fn float_list_norm_test() {
|> should.be_true()
}
pub fn float_list_norm_with_weights_test() {
pub fn list_norm_with_weights_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// An empty lists returns 0.0
@ -95,7 +95,7 @@ pub fn float_list_norm_with_weights_test() {
|> should.be_true()
}
pub fn float_list_manhattan_test() {
pub fn list_manhattan_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Try with valid input (same as Minkowski distance with p = 1)
@ -135,7 +135,7 @@ pub fn float_list_manhattan_test() {
|> should.be_error()
}
pub fn float_list_minkowski_test() {
pub fn list_minkowski_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Test order < 1
@ -209,7 +209,7 @@ pub fn float_list_minkowski_test() {
|> should.be_error()
}
pub fn float_list_euclidean_test() {
pub fn list_euclidean_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Empty lists returns an error
@ -268,6 +268,10 @@ pub fn mean_test() {
[1.0, 2.0, 3.0]
|> maths.mean()
|> should.equal(Ok(2.0))
[-1.0, -2.0, -3.0]
|> maths.mean()
|> should.equal(Ok(-2.0))
}
pub fn median_test() {

View file

@ -3,7 +3,7 @@ import gleam/int
import gleam_community/maths
import gleeunit/should
pub fn float_ceiling_test() {
pub fn ceiling_test() {
// Round 3. digit AFTER decimal point
maths.round_up(12.0654, 3)
|> should.equal(12.066)
@ -33,7 +33,7 @@ pub fn float_ceiling_test() {
|> should.equal(1000.0)
}
pub fn float_floor_test() {
pub fn floor_test() {
// Round 3. digit AFTER decimal point
maths.round_down(12.0654, 3)
|> should.equal(12.065)
@ -63,7 +63,7 @@ pub fn float_floor_test() {
|> should.equal(0.0)
}
pub fn float_truncate_test() {
pub fn truncate_test() {
// Round 3. digit AFTER decimal point
maths.round_to_zero(12.0654, 3)
|> should.equal(12.065)
@ -373,41 +373,41 @@ pub fn math_round_ties_up_test() {
|> should.equal(0.0)
}
pub fn float_absolute_difference_test() {
maths.float_absolute_difference(20.0, 15.0)
pub fn absolute_difference_test() {
maths.absolute_difference(20.0, 15.0)
|> should.equal(5.0)
maths.float_absolute_difference(-20.0, -15.0)
maths.absolute_difference(-20.0, -15.0)
|> should.equal(5.0)
maths.float_absolute_difference(20.0, -15.0)
maths.absolute_difference(20.0, -15.0)
|> should.equal(35.0)
maths.float_absolute_difference(-20.0, 15.0)
maths.absolute_difference(-20.0, 15.0)
|> should.equal(35.0)
maths.float_absolute_difference(0.0, 0.0)
maths.absolute_difference(0.0, 0.0)
|> should.equal(0.0)
maths.float_absolute_difference(1.0, 2.0)
maths.absolute_difference(1.0, 2.0)
|> should.equal(1.0)
maths.float_absolute_difference(2.0, 1.0)
maths.absolute_difference(2.0, 1.0)
|> should.equal(1.0)
maths.float_absolute_difference(-1.0, 0.0)
maths.absolute_difference(-1.0, 0.0)
|> should.equal(1.0)
maths.float_absolute_difference(0.0, -1.0)
maths.absolute_difference(0.0, -1.0)
|> should.equal(1.0)
maths.float_absolute_difference(10.0, 20.0)
maths.absolute_difference(10.0, 20.0)
|> should.equal(10.0)
maths.float_absolute_difference(-10.0, -20.0)
maths.absolute_difference(-10.0, -20.0)
|> should.equal(10.0)
maths.float_absolute_difference(-10.5, 10.5)
maths.absolute_difference(-10.5, 10.5)
|> should.equal(21.0)
}
@ -425,39 +425,39 @@ pub fn int_absolute_difference_test() {
|> should.equal(35)
}
pub fn float_sign_test() {
maths.float_sign(100.0)
pub fn sign_test() {
maths.sign(100.0)
|> should.equal(1.0)
maths.float_sign(0.0)
maths.sign(0.0)
|> should.equal(0.0)
maths.float_sign(-100.0)
maths.sign(-100.0)
|> should.equal(-1.0)
}
pub fn float_flip_sign_test() {
maths.float_flip_sign(100.0)
pub fn flip_sign_test() {
maths.flip_sign(100.0)
|> should.equal(-100.0)
maths.float_flip_sign(0.0)
maths.flip_sign(0.0)
|> should.equal(-0.0)
maths.float_flip_sign(-100.0)
maths.flip_sign(-100.0)
|> should.equal(100.0)
}
pub fn float_copy_sign_test() {
maths.float_copy_sign(100.0, 10.0)
pub fn copy_sign_test() {
maths.copy_sign(100.0, 10.0)
|> should.equal(100.0)
maths.float_copy_sign(-100.0, 10.0)
maths.copy_sign(-100.0, 10.0)
|> should.equal(100.0)
maths.float_copy_sign(100.0, -10.0)
maths.copy_sign(100.0, -10.0)
|> should.equal(-100.0)
maths.float_copy_sign(-100.0, -10.0)
maths.copy_sign(-100.0, -10.0)
|> should.equal(-100.0)
}
@ -497,7 +497,7 @@ pub fn int_copy_sign_test() {
|> should.equal(-100)
}
pub fn float_minmax_test() {
pub fn minmax_test() {
maths.minmax(0.75, 0.5, float.compare)
|> should.equal(#(0.5, 0.75))
@ -525,7 +525,7 @@ pub fn int_minmax_test() {
|> should.equal(#(-75, 50))
}
pub fn float_list_minimum_test() {
pub fn list_minimum_test() {
// An empty lists returns an error
[]
|> maths.list_minimum(float.compare)
@ -549,7 +549,7 @@ pub fn int_list_minimum_test() {
|> should.equal(Ok(1))
}
pub fn float_list_maximum_test() {
pub fn list_maximum_test() {
// An empty lists returns an error
[]
|> maths.list_maximum(float.compare)
@ -573,7 +573,7 @@ pub fn int_list_maximum_test() {
|> should.equal(Ok(4))
}
pub fn float_list_arg_maximum_test() {
pub fn list_arg_maximum_test() {
// An empty lists returns an error
[]
|> maths.arg_maximum(float.compare)
@ -597,7 +597,7 @@ pub fn int_list_arg_maximum_test() {
|> should.equal(Ok([0, 1]))
}
pub fn float_list_arg_minimum_test() {
pub fn list_arg_minimum_test() {
// An empty lists returns an error
[]
|> maths.arg_minimum(float.compare)
@ -621,7 +621,7 @@ pub fn int_list_arg_minimum_test() {
|> should.equal(Ok([4]))
}
pub fn float_list_extrema_test() {
pub fn list_extrema_test() {
// An empty lists returns an error
[]
|> maths.extrema(float.compare)

View file

@ -4,7 +4,7 @@ import gleam/yielder
import gleam_community/maths
import gleeunit/should
pub fn float_list_linear_space_test() {
pub fn list_linear_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
@ -28,7 +28,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -42,7 +41,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -54,7 +52,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -69,7 +66,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -83,7 +79,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -96,7 +91,6 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -108,7 +102,31 @@ pub fn float_list_linear_space_test() {
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// Check that when start == stop and steps > 0, then
// the value (start/stop) is just repeated, since the
// step increment will be 0
let assert Ok(linspace) = maths.linear_space(10.0, 10.0, 5, True)
let assert Ok(result) =
maths.all_close(
linspace |> yielder.to_list() |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
let assert Ok(linspace) = maths.linear_space(10.0, 10.0, 5, False)
let assert Ok(result) =
maths.all_close(
linspace |> yielder.to_list() |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
@ -118,12 +136,12 @@ pub fn float_list_linear_space_test() {
|> should.be_error()
}
pub fn float_list_logarithmic_space_test() {
pub fn list_logarithmic_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included
// - Positive start, stop, base
// - Positive start, stop
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
let assert Ok(result) =
maths.all_close(
@ -135,31 +153,7 @@ pub fn float_list_logarithmic_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
// - Positive start, stop, negative base
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([-10.0, 100.0, -1000.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// - Positive start, negative stop, base
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, -10.0)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([-10.0, -0.1, -0.001]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// - Positive start, base, negative stop
// - Positive start, negative stop
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
let assert Ok(result) =
maths.all_close(
@ -171,7 +165,7 @@ pub fn float_list_logarithmic_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
// - Positive stop, base, negative start
// - Positive stop, negative start
let assert Ok(logspace) = maths.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
let assert Ok(result) =
maths.all_close(
@ -184,7 +178,7 @@ pub fn float_list_logarithmic_space_test() {
|> should.be_true()
// ----> Without endpoint included
// - Positive start, stop, base
// - Positive start, stop
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, False, 10.0)
let assert Ok(result) =
maths.all_close(
@ -198,12 +192,44 @@ pub fn float_list_logarithmic_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
// Check that when start == stop and steps > 0, then
// the value (start/stop) is just repeated, since the
// step increment will be 0
let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, True, 5.0)
let assert Ok(result) =
maths.all_close(
logspace
|> yielder.to_list()
|> list.zip([3125.0, 3125.0, 3125.0, 3125.0, 3125.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, False, 5.0)
let assert Ok(result) =
maths.all_close(
logspace
|> yielder.to_list()
|> list.zip([3125.0, 3125.0, 3125.0, 3125.0, 3125.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// A negative number of points does not work (-3)
maths.logarithmic_space(1.0, 3.0, -3, True, 10.0)
|> should.be_error()
// A negative base does not work (-10)
maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|> should.be_error()
}
pub fn float_list_geometric_space_test() {
pub fn list_geometric_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -259,7 +285,36 @@ pub fn float_list_geometric_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
// Test invalid input (start and stop can't be equal to 0.0)
// Check that when start == stop and steps > 0, then
// the value (start/stop) is just repeated, since the
// step increment will be 0
let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, True)
let assert Ok(result) =
maths.all_close(
logspace
|> yielder.to_list()
|> list.zip([5.0, 5.0, 5.0, 5.0, 5.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, False)
let assert Ok(result) =
maths.all_close(
logspace
|> yielder.to_list()
|> list.zip([5.0, 5.0, 5.0, 5.0, 5.0]),
0.0,
tol,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// Test invalid input (start and stop can't be less than or equal to 0.0)
maths.geometric_space(0.0, 1000.0, 3, False)
|> should.be_error()
@ -271,7 +326,7 @@ pub fn float_list_geometric_space_test() {
|> should.be_error()
}
pub fn float_list_arange_test() {
pub fn list_arange_test() {
// Positive start, stop, step
maths.arange(1.0, 5.0, 1.0)
|> yielder.to_list()
@ -311,45 +366,7 @@ pub fn float_list_arange_test() {
|> should.equal([-5.0, -4.0, -3.0, -2.0])
}
pub fn float_list_exponential_space_test() {
let assert Ok(tolerance) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
let assert Ok(result) =
maths.all_close(
exp_space |> yielder.to_list() |> list.zip([1.0, 10.0, 100.0, 1000.0]),
0.0,
tolerance,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// ---> Without endpoint included
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, False)
let assert Ok(result) =
maths.all_close(
exp_space
|> yielder.to_list()
|> list.zip([
1.0, 5.623413251903491, 31.622776601683793, 177.82794100389228,
]),
0.0,
tolerance,
)
result
|> list.all(fn(x) { x == True })
|> should.be_true()
// A negative number of points does not work (-3)
maths.exponential_space(1.0, 1000.0, -3, True)
|> should.be_error()
}
pub fn float_list_symmetric_space_test() {
pub fn list_symmetric_space_test() {
let assert Ok(tolerance) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
@ -370,6 +387,12 @@ pub fn float_list_symmetric_space_test() {
|> yielder.to_list()
|> should.equal([-15.0, -12.5, -10.0, -7.5, -5.0])
// Negative Radius (simply reverses the order of the values)
let assert Ok(sym_space) = maths.symmetric_space(0.0, -5.0, 5)
sym_space
|> yielder.to_list()
|> should.equal([5.0, 2.5, 0.0, -2.5, -5.0])
// Uneven number of points
let assert Ok(sym_space) = maths.symmetric_space(0.0, 2.0, 4)
let assert Ok(result) =
@ -384,6 +407,14 @@ pub fn float_list_symmetric_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
// Check that when radius == 0 and steps > 0, then
// the value center value is just repeated, since the
// step increment will be 0
let assert Ok(sym_space) = maths.symmetric_space(10.0, 0.0, 4)
sym_space
|> yielder.to_list()
|> should.equal([10.0, 10.0, 10.0, 10.0])
// A negative number of points does not work (-5)
maths.symmetric_space(0.0, 5.0, -5)
|> should.be_error()