Merge pull request #23 from NicklasXYZ/main

Fix tests
This commit is contained in:
Nicklas Sindlev Andersen 2024-08-12 22:40:25 +02:00 committed by GitHub
commit 42c4ae114d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 79 additions and 223 deletions

View file

@ -30,6 +30,7 @@
//// * [`int_to_float`](#int_to_float)
//// * [`degrees_to_radians`](#degrees_to_radians)
//// * [`radians_to_degrees`](#radians_to_degrees)
////
import gleam/int

View file

@ -1000,7 +1000,7 @@ pub fn power(x: Float, y: Float) -> Result(Float, String) {
// 2. If the base (x) is 0 and the exponent (y) is negative then the
// expression is equivalent to the exponent (y) divided by 0 and an
// error should be returned
case x <. 0.0 && fractional || x == 0.0 && y <. 0.0 {
case { x <. 0.0 && fractional } || { x == 0.0 && y <. 0.0 } {
True ->
"Invalid input argument: x < 0 and y is fractional or x = 0 and y < 0."
|> Error

View file

@ -26,6 +26,12 @@
//// Metrics: A module offering functions for calculating distances and other
//// types of metrics.
////
//// Disclaimer: In this module, the terms "distance" and "metric" are used in
//// a broad and practical sense. That is, they are used to denote any difference
//// or discrepancy between two inputs. Consequently, they may not align with their
//// precise mathematical definitions (in particular, some "distance" functions in
//// this module do not satisfy the triangle inequality).
////
//// * **Distance measures**
//// * [`norm`](#norm)
//// * [`manhattan_distance`](#manhattan_distance)
@ -40,7 +46,6 @@
//// * [`sorensen_dice_coefficient`](#sorensen_dice_coefficient)
//// * [`tversky_index`](#tversky_index)
//// * [`overlap_coefficient`](#overlap_coefficient)
//// * [`levenshtein_distance`](#levenshtein_distance)
//// * **Basic statistical measures**
//// * [`mean`](#mean)
//// * [`median`](#median)
@ -49,17 +54,16 @@
////
import gleam/bool
import gleam/float
import gleam/int
import gleam/list
import gleam/option
import gleam/pair
import gleam/set
import gleam_community/maths/arithmetics
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
import gleam/set
import gleam/float
import gleam/int
import gleam/string
import gleam/option
/// Utility function that checks all lists have the expected length and contents
/// The function is primarily used by all distance measures taking 'List(Float)'
@ -1126,125 +1130,6 @@ pub fn cosine_similarity(
}
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
/// </a>
/// </div>
///
/// Calculate the Levenshtein distance between two strings, i.e., measure the
/// difference between two strings (essentially sequences). It is defined as
/// the minimum number of single-character edits required to change one string
/// into the other, using operations:
/// - insertions
/// - deletions
/// - substitutions
///
/// Note: The implementation is primarily based on the Elixir implementation
/// [levenshtein](https://hex.pm/packages/levenshtein).
///
/// <details>
/// <summary>Example:</summary>
///
/// import gleeunit/should
/// import gleam_community/maths/metrics
///
/// pub fn example () {
/// metrics.levenshtein_distance("hello", "hello")
/// |> should.equal(0)
///
/// metrics.levenshtein_distance("cat", "cut")
/// |> should.equal(1)
///
/// metrics.levenshtein_distance("kitten", "sitting")
/// |> should.equal(3)
/// }
/// </details>
///
/// <div style="text-align: right;">
/// <a href="#">
/// <small>Back to top </small>
/// </a>
/// </div>
///
///
pub fn levenshtein_distance(xstring: String, ystring: String) -> Int {
case xstring, ystring {
xstring, ystring if xstring == ystring -> {
0
}
xstring, ystring if xstring == "" -> {
string.length(ystring)
}
xstring, ystring if ystring == "" -> {
string.length(xstring)
}
_, _ -> {
let xstring_graphemes = string.to_graphemes(xstring)
let ystring_graphemes = string.to_graphemes(ystring)
let ystring_length = list.length(ystring_graphemes)
let distance_list = list.range(0, ystring_length)
do_edit_distance(xstring_graphemes, ystring_graphemes, distance_list, 1)
}
}
}
fn do_edit_distance(
xstring: List(String),
ystring: List(String),
distance_list: List(Int),
step: Int,
) -> Int {
case xstring {
// Safe as 'distance_list' is never empty
[] -> {
let assert Ok(last) = list.last(distance_list)
last
}
[xstring_head, ..xstring_tail] -> {
let new_distance_list =
distance_list_helper(ystring, distance_list, xstring_head, [step], step)
do_edit_distance(xstring_tail, ystring, new_distance_list, step + 1)
}
}
}
fn distance_list_helper(
ystring: List(String),
distance_list: List(Int),
grapheme: String,
new_distance_list: List(Int),
last_distance: Int,
) -> List(Int) {
case ystring {
[] -> list.reverse(new_distance_list)
[ystring_head, ..ystring_tail] -> {
let assert [distance_list_head, ..distance_list_tail] = distance_list
let difference = case ystring_head == grapheme {
True -> {
0
}
False -> {
1
}
}
let assert [first, ..] = distance_list_tail
let min =
last_distance + 1
|> piecewise.minimum(first + 1, int.compare)
|> piecewise.minimum(distance_list_head + difference, int.compare)
distance_list_helper(
ystring_tail,
distance_list_tail,
grapheme,
[min, ..new_distance_list],
min,
)
}
}
}
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">
/// <small>Spot a typo? Open an issue!</small>
@ -1293,7 +1178,6 @@ fn distance_list_helper(
/// </a>
/// </div>
///
///
pub fn canberra_distance(
xarr: List(Float),
yarr: List(Float),

View file

@ -35,14 +35,15 @@
//// * [`is_even`](#is_even)
//// * [`is_odd`](#is_odd)
//// * [`is_prime`](#is_prime)
////
import gleam/pair
import gleam/int
import gleam/list
import gleam/option
import gleam/pair
import gleam_community/maths/arithmetics
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
import gleam_community/maths/arithmetics
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">

View file

@ -31,11 +31,12 @@
//// * [`linear_space`](#linear_space)
//// * [`logarithmic_space`](#logarithmic_space)
//// * [`geometric_space`](#geometric_space)
////
import gleam/list
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
import gleam_community/maths/conversion
import gleam/list
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">

View file

@ -32,10 +32,10 @@
//// * [`incomplete_gamma`](#incomplete_gamma)
////
import gleam/list
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/piecewise
import gleam/list
/// <div style="text-align: right;">
/// <a href="https://github.com/gleam-community/maths/issues">

View file

@ -1,6 +1,6 @@
import gleam/option
import gleam_community/maths/arithmetics
import gleeunit/should
import gleam/option
pub fn int_gcd_test() {
arithmetics.gcd(1, 1)

View file

@ -1,6 +1,6 @@
import gleam_community/maths/combinatorics
import gleam/set
import gleam/list
import gleam/set
import gleam_community/maths/combinatorics
import gleeunit/should
pub fn int_factorial_test() {

View file

@ -1,10 +1,10 @@
import gleam_community/maths/conversion
import gleam_community/maths/elementary
import gleam_community/maths/predicates
import gleam_community/maths/conversion
import gleeunit/should
pub fn float_to_degree_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
conversion.radians_to_degrees(0.0)
|> predicates.is_close(0.0, 0.0, tol)
|> should.be_true()
@ -15,7 +15,7 @@ pub fn float_to_degree_test() {
}
pub fn float_to_radian_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
conversion.degrees_to_radians(0.0)
|> predicates.is_close(0.0, 0.0, tol)
|> should.be_true()

View file

@ -1,10 +1,10 @@
import gleam/option
import gleam_community/maths/elementary
import gleam_community/maths/predicates
import gleeunit/should
import gleam/option
pub fn float_acos_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
let assert Ok(result) = elementary.acos(1.0)
@ -27,7 +27,7 @@ pub fn float_acos_test() {
}
pub fn float_acosh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
let assert Ok(result) = elementary.acosh(1.0)
@ -47,7 +47,7 @@ pub fn float_asin_test() {
elementary.asin(0.0)
|> should.equal(Ok(0.0))
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
let assert Ok(result) = elementary.asin(0.5)
result
|> predicates.is_close(0.523598, 0.0, tol)
@ -63,7 +63,7 @@ pub fn float_asin_test() {
}
pub fn float_asinh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.asinh(0.0)
@ -76,7 +76,7 @@ pub fn float_asinh_test() {
}
pub fn float_atan_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.atan(0.0)
@ -89,7 +89,7 @@ pub fn float_atan_test() {
}
pub fn math_atan2_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.atan2(0.0, 0.0)
@ -137,7 +137,7 @@ pub fn math_atan2_test() {
}
pub fn float_atanh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
let assert Ok(result) = elementary.atanh(0.0)
@ -166,7 +166,7 @@ pub fn float_atanh_test() {
}
pub fn float_cos_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.cos(0.0)
@ -183,7 +183,7 @@ pub fn float_cos_test() {
}
pub fn float_cosh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.cosh(0.0)
@ -200,7 +200,7 @@ pub fn float_cosh_test() {
}
pub fn float_sin_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.sin(0.0)
@ -217,7 +217,7 @@ pub fn float_sin_test() {
}
pub fn float_sinh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.sinh(0.0)
@ -234,7 +234,7 @@ pub fn float_sinh_test() {
}
pub fn math_tan_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.tan(0.0)
@ -247,7 +247,7 @@ pub fn math_tan_test() {
}
pub fn math_tanh_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.tanh(0.0)
@ -268,7 +268,7 @@ pub fn math_tanh_test() {
}
pub fn float_exponential_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.exponential(0.0)
@ -285,7 +285,7 @@ pub fn float_exponential_test() {
}
pub fn float_natural_logarithm_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.natural_logarithm(1.0)
@ -336,7 +336,7 @@ pub fn float_logarithm_test() {
}
pub fn float_logarithm_2_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
elementary.logarithm_2(1.0)
@ -357,7 +357,7 @@ pub fn float_logarithm_2_test() {
}
pub fn float_logarithm_10_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
let assert Ok(result) = elementary.logarithm_10(1.0)
@ -400,6 +400,15 @@ pub fn float_power_test() {
elementary.power(2.0, -1.0)
|> should.equal(Ok(0.5))
// When y = 0, the result should universally be 1, regardless of the value of x
elementary.power(10.0, 0.0)
|> should.equal(Ok(1.0))
elementary.power(-10.0, 0.0)
|> should.equal(Ok(1.0))
elementary.power(2.0, -1.0)
|> should.equal(Ok(0.5))
// elementary.power(-1.0, 0.5) is equivalent to float.square_root(-1.0)
// and should return an error as an imaginary number would otherwise
// have to be returned

View file

@ -1,12 +1,12 @@
import gleam/option
import gleam/set
import gleam_community/maths/elementary
import gleam_community/maths/metrics
import gleam_community/maths/predicates
import gleeunit/should
import gleam/set
import gleam/option
pub fn float_list_norm_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// An empty lists returns 0.0
[]
@ -66,7 +66,7 @@ pub fn float_list_norm_test() {
}
pub fn float_list_manhattan_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Empty lists returns an error
metrics.manhattan_distance([], [], option.None)
@ -118,7 +118,7 @@ pub fn float_list_manhattan_test() {
}
pub fn float_list_minkowski_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Empty lists returns an error
metrics.minkowski_distance([], [], 1.0, option.None)
@ -226,7 +226,7 @@ pub fn float_list_minkowski_test() {
}
pub fn float_list_euclidean_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Empty lists returns an error
metrics.euclidean_distance([], [], option.None)
@ -410,7 +410,7 @@ pub fn overlap_coefficient_test() {
}
pub fn cosine_similarity_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Empty lists returns an error
metrics.cosine_similarity([], [], option.None)
@ -520,46 +520,6 @@ pub fn chebyshev_distance_test() {
|> should.equal(Ok(0.0))
}
pub fn levenshtein_distance_test() {
// Try different types of valid input...
// Requires 5 insertions to transform the empty string into "hello"
metrics.levenshtein_distance("", "hello")
|> should.equal(5)
// Requires 5 deletions to remove all characters from "hello" to match the empty string
metrics.levenshtein_distance("hello", "")
|> should.equal(5)
// Requires 2 deletions to remove two 'b's and 1 substitution to change 'b' to 'a'
metrics.levenshtein_distance("bbb", "a")
|> should.equal(3)
// Requires 2 insertions to add two 'b's and 1 substitution to change 'a' to 'b'
metrics.levenshtein_distance("a", "bbb")
|> should.equal(3)
// No changes needed, since the strings are identical
metrics.levenshtein_distance("hello", "hello")
|> should.equal(0)
// Requires 1 substitution to change 'a' to 'u'
metrics.levenshtein_distance("cat", "cut")
|> should.equal(1)
// Requires 2 substitutions (k -> s, e -> i) and 1 insertion (g at the end)
metrics.levenshtein_distance("kitten", "sitting")
|> should.equal(3)
// Some more complex cases, involving multiple insertions, deletions, and substitutions
metrics.levenshtein_distance("gggtatccat", "cctaggtccct")
|> should.equal(6)
metrics.levenshtein_distance(
"This is a longer string",
"This is also a much longer string",
)
|> should.equal(10)
}
pub fn canberra_distance_test() {
// Empty lists returns an error
metrics.canberra_distance([], [], option.None)

View file

@ -1,8 +1,8 @@
import gleam_community/maths/piecewise
import gleeunit/should
import gleam/option
import gleam/float
import gleam/int
import gleam/option
import gleam_community/maths/piecewise
import gleeunit/should
pub fn float_ceiling_test() {
// Round 3. digit AFTER decimal point

View file

@ -1,5 +1,5 @@
import gleam_community/maths/predicates
import gleam/list
import gleam_community/maths/predicates
import gleeunit/should
pub fn float_is_close_test() {

View file

@ -1,11 +1,11 @@
import gleam_community/maths/elementary
import gleam_community/maths/sequences
import gleam_community/maths/predicates
import gleam/list
import gleam_community/maths/elementary
import gleam_community/maths/predicates
import gleam_community/maths/sequences
import gleeunit/should
pub fn float_list_linear_space_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
@ -83,7 +83,7 @@ pub fn float_list_linear_space_test() {
}
pub fn float_list_logarithmic_space_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included
@ -147,7 +147,7 @@ pub fn float_list_logarithmic_space_test() {
}
pub fn float_list_geometric_space_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included

View file

@ -1,11 +1,11 @@
import gleam_community/maths/elementary
import gleam_community/maths/special
import gleam_community/maths/predicates
import gleeunit/should
import gleam/result
import gleam_community/maths/elementary
import gleam_community/maths/predicates
import gleam_community/maths/special
import gleeunit/should
pub fn float_beta_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Valid input returns a result
special.beta(-0.5, 0.5)
@ -26,7 +26,7 @@ pub fn float_beta_function_test() {
}
pub fn float_error_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Valid input returns a result
special.erf(-0.5)
@ -51,7 +51,7 @@ pub fn float_error_function_test() {
}
pub fn float_gamma_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Valid input returns a result
special.gamma(-0.5)
@ -80,7 +80,7 @@ pub fn float_gamma_function_test() {
}
pub fn float_incomplete_gamma_function_test() {
let assert Ok(tol) = elementary.power(-10.0, -6.0)
let assert Ok(tol) = elementary.power(10.0, -6.0)
// Invalid input gives an error
// 1st arg is invalid