♻️ Remove type annotations in let bindings.

This commit is contained in:
Hayleigh Thompson 2024-08-17 11:21:44 +01:00
parent 7fd4d74b2c
commit 4806e56baa
9 changed files with 344 additions and 348 deletions

View file

@ -20,11 +20,11 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Arithmetics: A module containing a collection of fundamental mathematical functions relating to simple arithmetics (addition, subtraction, multiplication, etc.), but also number theory. //// Arithmetics: A module containing a collection of fundamental mathematical functions relating to simple arithmetics (addition, subtraction, multiplication, etc.), but also number theory.
//// ////
//// * **Division functions** //// * **Division functions**
//// * [`gcd`](#gcd) //// * [`gcd`](#gcd)
//// * [`lcm`](#lcm) //// * [`lcm`](#lcm)
@ -40,7 +40,7 @@
//// * [`int_cumulative_sum`](#int_cumulative_sum) //// * [`int_cumulative_sum`](#int_cumulative_sum)
//// * [`float_cumulative_product`](#float_cumulative_product) //// * [`float_cumulative_product`](#float_cumulative_product)
//// * [`int_cumulative_product`](#int_cumulative_product) //// * [`int_cumulative_product`](#int_cumulative_product)
//// ////
import gleam/int import gleam/int
import gleam/list import gleam/list
@ -57,7 +57,7 @@ import gleam_community/maths/piecewise
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function calculates the greatest common divisor of two integers /// The function calculates the greatest common divisor of two integers
/// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive /// \\(x, y \in \mathbb{Z}\\). The greatest common divisor is the largest positive
/// integer that is divisible by both \\(x\\) and \\(y\\). /// integer that is divisible by both \\(x\\) and \\(y\\).
/// ///
@ -70,7 +70,7 @@ import gleam_community/maths/piecewise
/// pub fn example() { /// pub fn example() {
/// arithmetics.gcd(1, 1) /// arithmetics.gcd(1, 1)
/// |> should.equal(1) /// |> should.equal(1)
/// ///
/// arithmetics.gcd(100, 10) /// arithmetics.gcd(100, 10)
/// |> should.equal(10) /// |> should.equal(10)
/// ///
@ -86,8 +86,8 @@ import gleam_community/maths/piecewise
/// </div> /// </div>
/// ///
pub fn gcd(x: Int, y: Int) -> Int { pub fn gcd(x: Int, y: Int) -> Int {
let absx: Int = piecewise.int_absolute_value(x) let absx = piecewise.int_absolute_value(x)
let absy: Int = piecewise.int_absolute_value(y) let absy = piecewise.int_absolute_value(y)
do_gcd(absx, absy) do_gcd(absx, absy)
} }
@ -107,24 +107,24 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// ///
/// Given two integers, \\(x\\) (dividend) and \\(y\\) (divisor), the Euclidean modulo /// Given two integers, \\(x\\) (dividend) and \\(y\\) (divisor), the Euclidean modulo
/// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the /// of \\(x\\) by \\(y\\), denoted as \\(x \mod y\\), is the remainder \\(r\\) of the
/// division of \\(x\\) by \\(y\\), such that: /// division of \\(x\\) by \\(y\\), such that:
/// ///
/// \\[ /// \\[
/// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|, /// x = q \cdot y + r \quad \text{and} \quad 0 \leq r < |y|,
/// \\] /// \\]
/// ///
/// where \\(q\\) is an integer that represents the quotient of the division. /// where \\(q\\) is an integer that represents the quotient of the division.
/// ///
/// The Euclidean modulo function of two numbers, is the remainder operation most /// The Euclidean modulo function of two numbers, is the remainder operation most
/// commonly utilized in mathematics. This differs from the standard truncating /// commonly utilized in mathematics. This differs from the standard truncating
/// modulo operation frequently employed in programming via the `%` operator. /// modulo operation frequently employed in programming via the `%` operator.
/// Unlike the `%` operator, which may return negative results depending on the /// Unlike the `%` operator, which may return negative results depending on the
/// divisor's sign, the Euclidean modulo function is designed to always yield a /// divisor's sign, the Euclidean modulo function is designed to always yield a
/// positive outcome, ensuring consistency with mathematical conventions. /// positive outcome, ensuring consistency with mathematical conventions.
/// ///
/// Note that like the Gleam division operator `/` this will return `0` if one of /// Note that like the Gleam division operator `/` this will return `0` if one of
/// the arguments is `0`. /// the arguments is `0`.
/// ///
@ -138,7 +138,7 @@ fn do_gcd(x: Int, y: Int) -> Int {
/// pub fn example() { /// pub fn example() {
/// arithmetics.euclidean_modulo(15, 4) /// arithmetics.euclidean_modulo(15, 4)
/// |> should.equal(3) /// |> should.equal(3)
/// ///
/// arithmetics.euclidean_modulo(-3, -2) /// arithmetics.euclidean_modulo(-3, -2)
/// |> should.equal(1) /// |> should.equal(1)
/// ///
@ -168,7 +168,7 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function calculates the least common multiple of two integers /// The function calculates the least common multiple of two integers
/// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive /// \\(x, y \in \mathbb{Z}\\). The least common multiple is the smallest positive
/// integer that has both \\(x\\) and \\(y\\) as factors. /// integer that has both \\(x\\) and \\(y\\) as factors.
/// ///
@ -181,7 +181,7 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
/// pub fn example() { /// pub fn example() {
/// arithmetics.lcm(1, 1) /// arithmetics.lcm(1, 1)
/// |> should.equal(1) /// |> should.equal(1)
/// ///
/// arithmetics.lcm(100, 10) /// arithmetics.lcm(100, 10)
/// |> should.equal(100) /// |> should.equal(100)
/// ///
@ -197,8 +197,8 @@ pub fn int_euclidean_modulo(x: Int, y: Int) -> Int {
/// </div> /// </div>
/// ///
pub fn lcm(x: Int, y: Int) -> Int { pub fn lcm(x: Int, y: Int) -> Int {
let absx: Int = piecewise.int_absolute_value(x) let absx = piecewise.int_absolute_value(x)
let absy: Int = piecewise.int_absolute_value(y) let absy = piecewise.int_absolute_value(y)
absx * absy / do_gcd(absx, absy) absx * absy / do_gcd(absx, absy)
} }
@ -208,7 +208,7 @@ pub fn lcm(x: Int, y: Int) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns all the positive divisors of an integer, including the /// The function returns all the positive divisors of an integer, including the
/// number itself. /// number itself.
/// ///
/// <details> /// <details>
@ -240,9 +240,9 @@ pub fn divisors(n: Int) -> List(Int) {
} }
fn find_divisors(n: Int) -> List(Int) { fn find_divisors(n: Int) -> List(Int) {
let nabs: Float = piecewise.float_absolute_value(conversion.int_to_float(n)) let nabs = piecewise.float_absolute_value(conversion.int_to_float(n))
let assert Ok(sqrt_result) = elementary.square_root(nabs) let assert Ok(sqrt_result) = elementary.square_root(nabs)
let max: Int = conversion.float_to_int(sqrt_result) + 1 let max = conversion.float_to_int(sqrt_result) + 1
list.range(2, max) list.range(2, max)
|> list.fold([1, n], fn(acc: List(Int), i: Int) -> List(Int) { |> list.fold([1, n], fn(acc: List(Int), i: Int) -> List(Int) {
case n % i == 0 { case n % i == 0 {
@ -260,7 +260,7 @@ fn find_divisors(n: Int) -> List(Int) {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns all the positive divisors of an integer, excluding the /// The function returns all the positive divisors of an integer, excluding the
/// number iteself. /// number iteself.
/// ///
/// <details> /// <details>
@ -288,7 +288,7 @@ fn find_divisors(n: Int) -> List(Int) {
/// </div> /// </div>
/// ///
pub fn proper_divisors(n: Int) -> List(Int) { pub fn proper_divisors(n: Int) -> List(Int) {
let divisors: List(Int) = find_divisors(n) let divisors = find_divisors(n)
divisors divisors
|> list.take(list.length(divisors) - 1) |> list.take(list.length(divisors) - 1)
} }
@ -362,7 +362,7 @@ pub fn float_sum(arr: List(Float), weights: option.Option(List(Float))) -> Float
/// \sum_{i=1}^n x_i /// \sum_{i=1}^n x_i
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is /// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
/// the value in the input list indexed by \\(i\\). /// the value in the input list indexed by \\(i\\).
/// ///
/// <details> /// <details>
@ -414,7 +414,7 @@ pub fn int_sum(arr: List(Int)) -> Int {
/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is /// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) is
/// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\) /// the value in the input list indexed by \\(i\\), while the \\(w_i \in \mathbb{R}\\)
/// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// are corresponding weights (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -486,7 +486,7 @@ pub fn float_product(
/// \prod_{i=1}^n x_i /// \prod_{i=1}^n x_i
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is /// In the formula, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) is
/// the value in the input list indexed by \\(i\\). /// the value in the input list indexed by \\(i\\).
/// ///
/// <details> /// <details>
@ -536,7 +536,7 @@ pub fn int_product(arr: List(Int)) -> Int {
/// \\] /// \\]
/// ///
/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\) /// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\) /// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{R}\\)
/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the /// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
/// sum of the \\(1\\) to \\(j\\) first elements in the given list. /// sum of the \\(1\\) to \\(j\\) first elements in the given list.
/// ///
@ -586,7 +586,7 @@ pub fn float_cumulative_sum(arr: List(Float)) -> List(Float) {
/// \\] /// \\]
/// ///
/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\) /// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative sum of \\(n\\)
/// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\) /// elements. That is, \\(n\\) is the length of the list and \\(x_i \in \mathbb{Z}\\)
/// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the /// is the value in the input list indexed by \\(i\\). The value \\(v_j\\) is thus the
/// sum of the \\(1\\) to \\(j\\) first elements in the given list. /// sum of the \\(1\\) to \\(j\\) first elements in the given list.
/// ///
@ -635,10 +635,10 @@ pub fn int_cumulative_sum(arr: List(Int)) -> List(Int) {
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n /// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\] /// \\]
/// ///
/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of /// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
/// \\(n\\) elements. That is, \\(n\\) is the length of the list and /// \\(n\\) elements. That is, \\(n\\) is the length of the list and
/// \\(x_i \in \mathbb{R}\\) is the value in the input list indexed by \\(i\\). The /// \\(x_i \in \mathbb{R}\\) is the value in the input list indexed by \\(i\\). The
/// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the /// value \\(v_j\\) is thus the sum of the \\(1\\) to \\(j\\) first elements in the
/// given list. /// given list.
/// ///
/// <details> /// <details>
@ -687,9 +687,9 @@ pub fn float_cumulative_product(arr: List(Float)) -> List(Float) {
/// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n /// v_j = \prod_{i=1}^j x_i \\;\\; \forall j = 1,\dots, n
/// \\] /// \\]
/// ///
/// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of /// In the formula, \\(v_j\\) is the \\(j\\)'th element in the cumulative product of
/// \\(n\\) elements. That is, \\(n\\) is the length of the list and /// \\(n\\) elements. That is, \\(n\\) is the length of the list and
/// \\(x_i \in \mathbb{Z}\\) is the value in the input list indexed by \\(i\\). The /// \\(x_i \in \mathbb{Z}\\) is the value in the input list indexed by \\(i\\). The
/// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the /// value \\(v_j\\) is thus the product of the \\(1\\) to \\(j\\) first elements in the
/// given list. /// given list.
/// ///

View file

@ -20,11 +20,11 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Elementary: A module containing a comprehensive set of foundational mathematical functions and constants. //// Elementary: A module containing a comprehensive set of foundational mathematical functions and constants.
//// ////
//// * **Trigonometric and hyperbolic functions** //// * **Trigonometric and hyperbolic functions**
//// * [`acos`](#acos) //// * [`acos`](#acos)
//// * [`acosh`](#acosh) //// * [`acosh`](#acosh)
@ -53,7 +53,7 @@
//// * [`pi`](#pi) //// * [`pi`](#pi)
//// * [`tau`](#tau) //// * [`tau`](#tau)
//// * [`e`](#e) //// * [`e`](#e)
//// ////
import gleam/int import gleam/int
import gleam/option import gleam/option
@ -178,7 +178,7 @@ fn do_acosh(a: Float) -> Float
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\[-1, 1\]\\) as input and returns a numeric /// The function takes a number \\(x\\) in its domain \\(\[-1, 1\]\\) as input and returns a numeric
/// value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\) (an angle in /// value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\) (an angle in
/// radians). If the input value is outside the domain of the function an error is returned. /// radians). If the input value is outside the domain of the function an error is returned.
/// ///
/// <details> /// <details>
@ -232,8 +232,8 @@ fn do_asin(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \sinh^{-1}{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(-\infty, \infty\), \\; \sinh^{-1}{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and /// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
/// returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, +\infty\)\\) (an angle in /// returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, +\infty\)\\) (an angle in
/// radians). /// radians).
/// ///
/// <details> /// <details>
@ -274,7 +274,7 @@ fn do_asinh(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \tan^{-1}{(x)} = y \in \[-\frac{\pi}{2}, \frac{\pi}{2}\] /// \forall x \in \(-\infty, \infty\), \\; \tan^{-1}{(x)} = y \in \[-\frac{\pi}{2}, \frac{\pi}{2}\]
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and /// The function takes a number \\(x\\) in its domain \\(\(-\infty, +\infty\)\\) as input and
/// returns a numeric value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\) /// returns a numeric value \\(y\\) that lies in the range \\(\[-\frac{\pi}{2}, \frac{\pi}{2}\]\\)
/// (an angle in radians). /// (an angle in radians).
/// ///
@ -421,7 +421,7 @@ fn do_atanh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \cos{(x)} = y \in \[-1, 1\] /// \forall x \in \(-\infty, +\infty\), \\; \cos{(x)} = y \in \[-1, 1\]
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
/// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\). /// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
/// ///
/// <details> /// <details>
@ -465,8 +465,8 @@ fn do_cos(a: Float) -> Float
/// \forall x \in \(-\infty, \infty\), \\; \cosh{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(-\infty, \infty\), \\; \cosh{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) as input (an angle
/// in radians) and returns a numeric value \\(y\\) that lies in the range /// in radians) and returns a numeric value \\(y\\) that lies in the range
/// \\(\(-\infty, \infty\)\\). If the input value is too large an overflow error might occur. /// \\(\(-\infty, \infty\)\\). If the input value is too large an overflow error might occur.
/// ///
/// <details> /// <details>
@ -507,7 +507,7 @@ fn do_cosh(a: Float) -> Float
/// \forall x \in \(-\infty, +\infty\), \\; \sin{(x)} = y \in \[-1, 1\] /// \forall x \in \(-\infty, +\infty\), \\; \sin{(x)} = y \in \[-1, 1\]
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in /// The function takes a number \\(x\\) in its domain \\(\(-\infty, \infty\)\\) (an angle in
/// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\). /// radians) as input and returns a numeric value \\(y\\) that lies in the range \\(\[-1, 1\]\\).
/// ///
/// <details> /// <details>
@ -780,7 +780,7 @@ fn do_natural_logarithm(a: Float) -> Float
/// \forall x \in \(0, \infty\) \textnormal{ and } b > 1, \\; \log_{b}{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(0, \infty\) \textnormal{ and } b > 1, \\; \log_{b}{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) and a base \\(b > 1\\) /// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) and a base \\(b > 1\\)
/// as input and returns a numeric value \\(y\\) that lies in the range \\(\(-\infty, \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. /// If the input value is outside the domain of the function an error is returned.
/// ///
@ -849,7 +849,7 @@ pub fn logarithm(x: Float, base: option.Option(Float)) -> Result(Float, String)
/// \forall x \in \(0, \infty), \\; \log_{2}{(x)} = y \in \(-\infty, +\infty\) /// \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 /// 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\)\\). /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
@ -904,7 +904,7 @@ fn do_logarithm_2(a: Float) -> Float
/// \forall x \in \(0, \infty), \\; \log_{10}{(x)} = y \in \(-\infty, +\infty\) /// \forall x \in \(0, \infty), \\; \log_{10}{(x)} = y \in \(-\infty, +\infty\)
/// \\] /// \\]
/// ///
/// The function takes a number \\(x\\) in its domain \\(\(0, \infty\)\\) as input and returns a /// 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\)\\). /// 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. /// If the input value is outside the domain of the function an error is returned.
/// ///
@ -994,7 +994,7 @@ fn do_logarithm_10(a: Float) -> Float
/// </div> /// </div>
/// ///
pub fn power(x: Float, y: Float) -> Result(Float, String) { pub fn power(x: Float, y: Float) -> Result(Float, String) {
let fractional: Bool = do_ceiling(y) -. y >. 0.0 let fractional = do_ceiling(y) -. y >. 0.0
// In the following check: // In the following check:
// 1. If the base (x) is negative and the exponent (y) is fractional // 1. If the base (x) is negative and the exponent (y) is fractional
// then return an error as it will otherwise be an imaginary number // then return an error as it will otherwise be an imaginary number
@ -1057,7 +1057,7 @@ fn do_ceiling(a: Float) -> Float
/// ///
pub fn square_root(x: Float) -> Result(Float, String) { pub fn square_root(x: Float) -> Result(Float, String) {
// In the following check: // In the following check:
// 1. If x is negative then return an error as it will otherwise be an // 1. If x is negative then return an error as it will otherwise be an
// imaginary number // imaginary number
case x <. 0.0 { case x <. 0.0 {
True -> True ->
@ -1109,7 +1109,7 @@ pub fn square_root(x: Float) -> Result(Float, String) {
/// ///
pub fn cube_root(x: Float) -> Result(Float, String) { pub fn cube_root(x: Float) -> Result(Float, String) {
// In the following check: // In the following check:
// 1. If x is negative then return an error as it will otherwise be an // 1. If x is negative then return an error as it will otherwise be an
// imaginary number // imaginary number
case x <. 0.0 { case x <. 0.0 {
True -> True ->
@ -1164,7 +1164,7 @@ pub fn cube_root(x: Float) -> Result(Float, String) {
/// ///
pub fn nth_root(x: Float, n: Int) -> Result(Float, String) { pub fn nth_root(x: Float, n: Int) -> Result(Float, String) {
// In the following check: // In the following check:
// 1. If x is negative then return an error as it will otherwise be an // 1. If x is negative then return an error as it will otherwise be an
// imaginary number // imaginary number
case x <. 0.0 { case x <. 0.0 {
True -> True ->

View file

@ -20,18 +20,18 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Metrics: A module offering functions for calculating distances and other //// Metrics: A module offering functions for calculating distances and other
//// types of metrics. //// types of metrics.
//// ////
//// Disclaimer: In this module, the terms "distance" and "metric" are used in //// 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 //// 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 //// or discrepancy between two inputs. Consequently, they may not align with their
//// precise mathematical definitions (in particular, some "distance" functions in //// precise mathematical definitions (in particular, some "distance" functions in
//// this module do not satisfy the triangle inequality). //// this module do not satisfy the triangle inequality).
//// ////
//// * **Distance measures** //// * **Distance measures**
//// * [`norm`](#norm) //// * [`norm`](#norm)
//// * [`manhattan_distance`](#manhattan_distance) //// * [`manhattan_distance`](#manhattan_distance)
@ -51,7 +51,7 @@
//// * [`median`](#median) //// * [`median`](#median)
//// * [`variance`](#variance) //// * [`variance`](#variance)
//// * [`standard_deviation`](#standard_deviation) //// * [`standard_deviation`](#standard_deviation)
//// ////
import gleam/bool import gleam/bool
import gleam/float import gleam/float
@ -81,8 +81,8 @@ fn validate_lists(
"Invalid input argument: The list yarr is empty." "Invalid input argument: The list yarr is empty."
|> Error |> Error
_, _ -> { _, _ -> {
let xarr_length: Int = list.length(xarr) let xarr_length = list.length(xarr)
let yarr_length: Int = list.length(yarr) let yarr_length = list.length(yarr)
case xarr_length == yarr_length, weights { case xarr_length == yarr_length, weights {
False, _ -> False, _ ->
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
@ -92,7 +92,7 @@ fn validate_lists(
|> Ok |> Ok
} }
True, option.Some(warr) -> { True, option.Some(warr) -> {
let warr_length: Int = list.length(warr) let warr_length = list.length(warr)
case xarr_length == warr_length { case xarr_length == warr_length {
True -> { True -> {
validate_weights(warr) validate_weights(warr)
@ -132,7 +132,7 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) {
/// \left( \sum_{i=1}^n w_{i} \left|x_{i}\right|^{p} \right)^{\frac{1}{p}} /// \left( \sum_{i=1}^n w_{i} \left|x_{i}\right|^{p} \right)^{\frac{1}{p}}
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the list and \\(x_i\\) is the value in /// In the formula, \\(n\\) is the length of the list and \\(x_i\\) is the value in
/// the input list indexed by \\(i\\), while \\(w_i \in \mathbb{R}_{+}\\) is /// the input list indexed by \\(i\\), while \\(w_i \in \mathbb{R}_{+}\\) is
/// a corresponding positive weight (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// a corresponding positive weight (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
@ -147,14 +147,14 @@ fn validate_weights(warr: List(Float)) -> Result(Bool, String) {
/// ///
/// pub fn example() { /// pub fn example() {
/// let assert Ok(tol) = elementary.power(-10.0, -6.0) /// let assert Ok(tol) = elementary.power(-10.0, -6.0)
/// ///
/// let assert Ok(result) = /// let assert Ok(result) =
/// [1.0, 1.0, 1.0] /// [1.0, 1.0, 1.0]
/// |> metrics.norm(1.0, option.None) /// |> metrics.norm(1.0, option.None)
/// result /// result
/// |> predicates.is_close(3.0, 0.0, tol) /// |> predicates.is_close(3.0, 0.0, tol)
/// |> should.be_true() /// |> should.be_true()
/// ///
/// let assert Ok(result) = /// let assert Ok(result) =
/// [1.0, 1.0, 1.0] /// [1.0, 1.0, 1.0]
/// |> metrics.norm(-1.0, option.None) /// |> metrics.norm(-1.0, option.None)
@ -180,7 +180,7 @@ pub fn norm(
0.0 0.0
|> Ok |> Ok
_, option.None -> { _, option.None -> {
let aggregate: Float = let aggregate =
arr arr
|> list.fold(0.0, fn(accumulator: Float, element: Float) -> Float { |> list.fold(0.0, fn(accumulator: Float, element: Float) -> Float {
let assert Ok(result) = let assert Ok(result) =
@ -193,20 +193,20 @@ pub fn norm(
|> Ok |> Ok
} }
_, option.Some(warr) -> { _, option.Some(warr) -> {
let arr_length: Int = list.length(arr) let arr_length = list.length(arr)
let warr_length: Int = list.length(warr) let warr_length = list.length(warr)
case arr_length == warr_length { case arr_length == warr_length {
True -> { True -> {
case validate_weights(warr) { case validate_weights(warr) {
Ok(_) -> { Ok(_) -> {
let tuples: List(#(Float, Float)) = list.zip(arr, warr) let tuples = list.zip(arr, warr)
let aggregate: Float = let aggregate =
tuples tuples
|> list.fold( |> list.fold(
0.0, 0.0,
fn(accumulator: Float, tuple: #(Float, Float)) -> Float { fn(accumulator: Float, tuple: #(Float, Float)) -> Float {
let first_element: Float = pair.first(tuple) let first_element = pair.first(tuple)
let second_element: Float = pair.second(tuple) let second_element = pair.second(tuple)
let assert Ok(result) = let assert Ok(result) =
elementary.power( elementary.power(
piecewise.float_absolute_value(first_element), piecewise.float_absolute_value(first_element),
@ -239,16 +239,16 @@ pub fn norm(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Calculate the (weighted) Manhattan distance between two lists (representing /// Calculate the (weighted) Manhattan distance between two lists (representing
/// vectors): /// vectors):
/// ///
/// \\[ /// \\[
/// \sum_{i=1}^n w_{i} \left|x_i - y_i \right| /// \sum_{i=1}^n w_{i} \left|x_i - y_i \right|
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the /// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
/// values in the respective input lists indexed by \\(i\\), while the /// values in the respective input lists indexed by \\(i\\), while the
/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
@ -266,11 +266,11 @@ pub fn norm(
/// // Empty lists returns an error /// // Empty lists returns an error
/// metrics.manhattan_distance([], [], option.None) /// metrics.manhattan_distance([], [], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Differing lengths returns error /// // Differing lengths returns error
/// metrics.manhattan_distance([], [1.0], option.None) /// metrics.manhattan_distance([], [1.0], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// let assert Ok(result) = /// let assert Ok(result) =
/// metrics.manhattan_distance([0.0, 0.0], [1.0, 2.0], option.None) /// metrics.manhattan_distance([0.0, 0.0], [1.0, 2.0], option.None)
/// result /// result
@ -306,12 +306,12 @@ pub fn manhattan_distance(
/// \left( \sum_{i=1}^n w_{i} \left|x_i - y_i \right|^{p} \right)^{\frac{1}{p}} /// \left( \sum_{i=1}^n w_{i} \left|x_i - y_i \right|^{p} \right)^{\frac{1}{p}}
/// \\] /// \\]
/// ///
/// In the formula, \\(p >= 1\\) is the order, \\(n\\) is the length of the two lists /// In the formula, \\(p >= 1\\) is the order, \\(n\\) is the length of the two lists
/// and \\(x_i, y_i\\) are the values in the respective input lists indexed by \\(i\\). /// and \\(x_i, y_i\\) are the values in the respective input lists indexed by \\(i\\).
/// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// The \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// The Minkowski distance is a generalization of both the Euclidean distance /// The Minkowski distance is a generalization of both the Euclidean distance
/// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)). /// (\\(p=2\\)) and the Manhattan distance (\\(p = 1\\)).
/// ///
/// <details> /// <details>
@ -325,11 +325,11 @@ pub fn manhattan_distance(
/// ///
/// pub fn example() { /// pub fn example() {
/// 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 /// // Empty lists returns an error
/// metrics.minkowski_distance([], [], 1.0, option.None) /// metrics.minkowski_distance([], [], 1.0, option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Differing lengths returns error /// // Differing lengths returns error
/// metrics.minkowski_distance([], [1.0], 1.0, option.None) /// metrics.minkowski_distance([], [1.0], 1.0, option.None)
/// |> should.be_error() /// |> should.be_error()
@ -337,7 +337,7 @@ pub fn manhattan_distance(
/// // Test order < 1 /// // Test order < 1
/// metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0, option.None) /// metrics.minkowski_distance([0.0, 0.0], [0.0, 0.0], -1.0, option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// let assert Ok(result) = /// let assert Ok(result) =
/// metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0, option.None) /// metrics.minkowski_distance([0.0, 0.0], [1.0, 2.0], 1.0, option.None)
/// result /// result
@ -368,7 +368,7 @@ pub fn minkowski_distance(
"Invalid input argument: p < 1. Valid input is p >= 1." "Invalid input argument: p < 1. Valid input is p >= 1."
|> Error |> Error
False -> { False -> {
let differences: List(Float) = let differences =
list.zip(xarr, yarr) list.zip(xarr, yarr)
|> list.map(fn(tuple: #(Float, Float)) -> Float { |> list.map(fn(tuple: #(Float, Float)) -> Float {
pair.first(tuple) -. pair.second(tuple) pair.first(tuple) -. pair.second(tuple)
@ -389,7 +389,7 @@ pub fn minkowski_distance(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Calculate the (weighted) Euclidean distance between two lists (representing /// Calculate the (weighted) Euclidean distance between two lists (representing
/// vectors): /// vectors):
/// ///
/// \\[ /// \\[
@ -398,7 +398,7 @@ pub fn minkowski_distance(
/// ///
/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the /// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
/// values in the respective input lists indexed by \\(i\\), while the /// values in the respective input lists indexed by \\(i\\), while the
/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
@ -412,15 +412,15 @@ pub fn minkowski_distance(
/// ///
/// pub fn example() { /// pub fn example() {
/// 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 /// // Empty lists returns an error
/// metrics.euclidean_distance([], [], option.None) /// metrics.euclidean_distance([], [], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Differing lengths returns an error /// // Differing lengths returns an error
/// metrics.euclidean_distance([], [1.0], option.None) /// metrics.euclidean_distance([], [1.0], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// let assert Ok(result) = /// let assert Ok(result) =
/// metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0], option.None) /// metrics.euclidean_distance([0.0, 0.0], [1.0, 2.0], option.None)
/// result /// result
@ -455,7 +455,7 @@ pub fn euclidean_distance(
/// \text{max}_{i=1}^n \left|x_i - y_i \right| /// \text{max}_{i=1}^n \left|x_i - y_i \right|
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the /// In the formula, \\(n\\) is the length of the two lists and \\(x_i, y_i\\) are the
/// values in the respective input lists indexed by \\(i\\). /// values in the respective input lists indexed by \\(i\\).
/// ///
/// <details> /// <details>
@ -470,11 +470,11 @@ pub fn euclidean_distance(
/// // Empty lists returns an error /// // Empty lists returns an error
/// metrics.chebyshev_distance([], []) /// metrics.chebyshev_distance([], [])
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Differing lengths returns error /// // Differing lengths returns error
/// metrics.chebyshev_distance([], [1.0]) /// metrics.chebyshev_distance([], [1.0])
/// |> should.be_error() /// |> should.be_error()
/// ///
/// metrics.chebyshev_distance([-5.0, -10.0, -3.0], [-1.0, -12.0, -3.0]) /// metrics.chebyshev_distance([-5.0, -10.0, -3.0], [-1.0, -12.0, -3.0])
/// |> should.equal(Ok(4.0)) /// |> should.equal(Ok(4.0))
/// } /// }
@ -632,14 +632,14 @@ fn do_median(
/// </div> /// </div>
/// ///
/// Calculate the sample variance of the elements in a list: /// Calculate the sample variance of the elements in a list:
/// ///
/// \\[ /// \\[
/// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x}) /// s^{2} = \frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\) /// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\)
/// is the sample point in the input list indexed by \\(i\\). /// is the sample point in the input list indexed by \\(i\\).
/// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta /// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta
/// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased /// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased
/// estimate of the sample variance. Setting \\(d = 1\\) gives an unbiased estimate. /// estimate of the sample variance. Setting \\(d = 1\\) gives an unbiased estimate.
/// ///
@ -651,7 +651,7 @@ fn do_median(
/// ///
/// pub fn example () { /// pub fn example () {
/// // Degrees of freedom /// // Degrees of freedom
/// let ddof: Int = 1 /// let ddof = 1
/// ///
/// // An empty list returns an error /// // An empty list returns an error
/// [] /// []
@ -713,11 +713,11 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) {
/// s = \left(\frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})\right)^{\frac{1}{2}} /// s = \left(\frac{1}{n - d} \sum_{i=1}^{n}(x_i - \bar{x})\right)^{\frac{1}{2}}
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\) /// In the formula, \\(n\\) is the sample size (the length of the list) and \\(x_i\\)
/// is the sample point in the input list indexed by \\(i\\). /// is the sample point in the input list indexed by \\(i\\).
/// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta /// Furthermore, \\(\bar{x}\\) is the sample mean and \\(d\\) is the "Delta
/// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased /// Degrees of Freedom", and is by default set to \\(d = 0\\), which gives a biased
/// estimate of the sample standard deviation. Setting \\(d = 1\\) gives an unbiased /// estimate of the sample standard deviation. Setting \\(d = 1\\) gives an unbiased
/// estimate. /// estimate.
/// ///
/// <details> /// <details>
@ -728,7 +728,7 @@ pub fn variance(arr: List(Float), ddof: Int) -> Result(Float, String) {
/// ///
/// pub fn example () { /// pub fn example () {
/// // Degrees of freedom /// // Degrees of freedom
/// let ddof: Int = 1 /// let ddof = 1
/// ///
/// // An empty list returns an error /// // An empty list returns an error
/// [] /// []
@ -776,24 +776,24 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String)
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The Jaccard index measures similarity between two sets of elements. /// The Jaccard index measures similarity between two sets of elements.
/// Mathematically, the Jaccard index is defined as: /// Mathematically, the Jaccard index is defined as:
/// ///
/// \\[ /// \\[
/// \frac{|X \cap Y|}{|X \cup Y|} \\; \in \\; \left[0, 1\right] /// \frac{|X \cap Y|}{|X \cup Y|} \\; \in \\; \left[0, 1\right]
/// \\] /// \\]
/// ///
/// where: /// where:
/// ///
/// - \\(X\\) and \\(Y\\) are two sets being compared, /// - \\(X\\) and \\(Y\\) are two sets being compared,
/// - \\(|X \cap Y|\\) represents the size of the intersection of the two sets /// - \\(|X \cap Y|\\) represents the size of the intersection of the two sets
/// - \\(|X \cup Y|\\) denotes the size of the union of the two sets /// - \\(|X \cup Y|\\) denotes the size of the union of the two sets
/// ///
/// The value of the Jaccard index ranges from 0 to 1, where 0 indicates that the /// The value of the Jaccard index ranges from 0 to 1, where 0 indicates that the
/// two sets share no elements and 1 indicates that the sets are identical. The /// two sets share no elements and 1 indicates that the sets are identical. The
/// Jaccard index is a special case of the [Tversky index](#tversky_index) (with /// Jaccard index is a special case of the [Tversky index](#tversky_index) (with
/// \\(\alpha=\beta=1\\)). /// \\(\alpha=\beta=1\\)).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -802,8 +802,8 @@ pub fn standard_deviation(arr: List(Float), ddof: Int) -> Result(Float, String)
/// import gleam/set /// import gleam/set
/// ///
/// pub fn example () { /// pub fn example () {
/// let xset: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"]) /// let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
/// let yset: set.Set(String) = /// let yset =
/// set.from_list(["monkey", "rhino", "ostrich", "salmon"]) /// set.from_list(["monkey", "rhino", "ostrich", "salmon"])
/// metrics.jaccard_index(xset, yset) /// metrics.jaccard_index(xset, yset)
/// |> should.equal(1.0 /. 7.0) /// |> should.equal(1.0 /. 7.0)
@ -827,25 +827,25 @@ pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The Sørensen-Dice coefficient measures the similarity between two sets of /// The Sørensen-Dice coefficient measures the similarity between two sets of
/// elements. Mathematically, the coefficient is defined as: /// elements. Mathematically, the coefficient is defined as:
/// ///
/// \\[ /// \\[
/// \frac{2 |X \cap Y|}{|X| + |Y|} \\; \in \\; \left[0, 1\right] /// \frac{2 |X \cap Y|}{|X| + |Y|} \\; \in \\; \left[0, 1\right]
/// \\] /// \\]
/// ///
/// where: /// where:
/// - \\(X\\) and \\(Y\\) are two sets being compared /// - \\(X\\) and \\(Y\\) are two sets being compared
/// - \\(|X \cap Y|\\) is the size of the intersection of the two sets (i.e., the /// - \\(|X \cap Y|\\) is the size of the intersection of the two sets (i.e., the
/// number of elements common to both sets) /// number of elements common to both sets)
/// - \\(|X|\\) and \\(|Y|\\) are the sizes of the sets \\(X\\) and \\(Y\\), respectively /// - \\(|X|\\) and \\(|Y|\\) are the sizes of the sets \\(X\\) and \\(Y\\), respectively
/// ///
/// The coefficient ranges from 0 to 1, where 0 indicates no similarity (the sets /// The coefficient ranges from 0 to 1, where 0 indicates no similarity (the sets
/// share no elements) and 1 indicates perfect similarity (the sets are identical). /// share no elements) and 1 indicates perfect similarity (the sets are identical).
/// The higher the coefficient, the greater the similarity between the two sets. /// The higher the coefficient, the greater the similarity between the two sets.
/// The Sørensen-Dice coefficient is a special case of the /// The Sørensen-Dice coefficient is a special case of the
/// [Tversky index](#tversky_index) (with \\(\alpha=\beta=0.5\\)). /// [Tversky index](#tversky_index) (with \\(\alpha=\beta=0.5\\)).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -854,8 +854,8 @@ pub fn jaccard_index(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// import gleam/set /// import gleam/set
/// ///
/// pub fn example () { /// pub fn example () {
/// let xset: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"]) /// let xset = set.from_list(["cat", "dog", "hippo", "monkey"])
/// let yset: set.Set(String) = /// let yset =
/// set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"]) /// set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
/// metrics.sorensen_dice_coefficient(xset, yset) /// metrics.sorensen_dice_coefficient(xset, yset)
/// |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 }) /// |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
@ -878,31 +878,31 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice /// The Tversky index is a generalization of the Jaccard index and Sørensen-Dice
/// coefficient, which adds flexibility through two parameters, \\(\alpha\\) and /// coefficient, which adds flexibility through two parameters, \\(\alpha\\) and
/// \\(\beta\\), allowing for asymmetric similarity measures between sets. The /// \\(\beta\\), allowing for asymmetric similarity measures between sets. The
/// Tversky index is defined as: /// Tversky index is defined as:
/// ///
/// \\[ /// \\[
/// \frac{|X \cap Y|}{|X \cap Y| + \alpha|X - Y| + \beta|Y - X|} /// \frac{|X \cap Y|}{|X \cap Y| + \alpha|X - Y| + \beta|Y - X|}
/// \\] /// \\]
/// ///
/// where: /// where:
/// ///
/// - \\(X\\) and \\(Y\\) are the sets being compared /// - \\(X\\) and \\(Y\\) are the sets being compared
/// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of /// - \\(|X - Y|\\) and \\(|Y - X|\\) are the sizes of the relative complements of
/// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively, /// \\(Y\\) in \\(X\\) and \\(X\\) in \\(Y\\), respectively,
/// - \\(\alpha\\) and \\(\beta\\) are parameters that weigh the relative importance /// - \\(\alpha\\) and \\(\beta\\) are parameters that weigh the relative importance
/// of the elements unique to \\(X\\) and \\(Y\\) /// of the elements unique to \\(X\\) and \\(Y\\)
/// ///
/// The Tversky index reduces to the Jaccard index when \\(\alpha = \beta = 1\\) and /// The Tversky index reduces to the Jaccard index when \\(\alpha = \beta = 1\\) and
/// to the Sørensen-Dice coefficient when \\(\alpha = \beta = 0.5\\). In general, the /// to the Sørensen-Dice coefficient when \\(\alpha = \beta = 0.5\\). In general, the
/// Tversky index can take on any non-negative value, including 0. The index equals /// Tversky index can take on any non-negative value, including 0. The index equals
/// 0 when there is no intersection between the two sets, indicating no similarity. /// 0 when there is no intersection between the two sets, indicating no similarity.
/// However, unlike similarity measures bounded strictly between 0 and 1, the /// However, unlike similarity measures bounded strictly between 0 and 1, the
/// Tversky index does not have a strict upper limit of 1 when \\(\alpha \neq \beta\\). /// Tversky index does not have a strict upper limit of 1 when \\(\alpha \neq \beta\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -911,8 +911,8 @@ pub fn sorensen_dice_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// import gleam/set /// import gleam/set
/// ///
/// pub fn example () { /// pub fn example () {
/// let yset: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"]) /// let yset = set.from_list(["cat", "dog", "hippo", "monkey"])
/// let xset: set.Set(String) = /// let xset =
/// set.from_list(["monkey", "rhino", "ostrich", "salmon"]) /// set.from_list(["monkey", "rhino", "ostrich", "salmon"])
/// // Test Jaccard index (alpha = beta = 1) /// // Test Jaccard index (alpha = beta = 1)
/// metrics.tversky_index(xset, yset, 1.0, 1.0) /// metrics.tversky_index(xset, yset, 1.0, 1.0)
@ -934,15 +934,15 @@ pub fn tversky_index(
) -> Result(Float, String) { ) -> Result(Float, String) {
case alpha >=. 0.0, beta >=. 0.0 { case alpha >=. 0.0, beta >=. 0.0 {
True, True -> { True, True -> {
let intersection: Float = let intersection =
set.intersection(xset, yset) set.intersection(xset, yset)
|> set.size() |> set.size()
|> conversion.int_to_float() |> conversion.int_to_float()
let difference1: Float = let difference1 =
set.difference(xset, yset) set.difference(xset, yset)
|> set.size() |> set.size()
|> conversion.int_to_float() |> conversion.int_to_float()
let difference2: Float = let difference2 =
set.difference(yset, xset) set.difference(yset, xset)
|> set.size() |> set.size()
|> conversion.int_to_float() |> conversion.int_to_float()
@ -970,10 +970,10 @@ pub fn tversky_index(
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The Overlap coefficient, also known as the SzymkiewiczSimpson coefficient, is /// The Overlap coefficient, also known as the SzymkiewiczSimpson coefficient, is
/// a measure of similarity between two sets that focuses on the size of the /// a measure of similarity between two sets that focuses on the size of the
/// intersection relative to the smaller of the two sets. It is defined /// intersection relative to the smaller of the two sets. It is defined
/// mathematically as: /// mathematically as:
/// ///
/// \\[ /// \\[
@ -986,10 +986,10 @@ pub fn tversky_index(
/// - \\(|X \cap Y|\\) is the size of the intersection of the sets /// - \\(|X \cap Y|\\) is the size of the intersection of the sets
/// - \\(\min(|X|, |Y|)\\) is the size of the smaller set among \\(X\\) and \\(Y\\) /// - \\(\min(|X|, |Y|)\\) is the size of the smaller set among \\(X\\) and \\(Y\\)
/// ///
/// The coefficient ranges from 0 to 1, where 0 indicates no overlap and 1 /// The coefficient ranges from 0 to 1, where 0 indicates no overlap and 1
/// indicates that the smaller set is a suyset of the larger set. This /// indicates that the smaller set is a suyset of the larger set. This
/// measure is especially useful in situations where the similarity in terms /// measure is especially useful in situations where the similarity in terms
/// of the proportion of overlap is more relevant than the difference in sizes /// of the proportion of overlap is more relevant than the difference in sizes
/// between the two sets. /// between the two sets.
/// ///
/// <details> /// <details>
@ -1000,9 +1000,9 @@ pub fn tversky_index(
/// import gleam/set /// import gleam/set
/// ///
/// pub fn example () { /// pub fn example () {
/// let set_a: set.Set(String) = /// let set_a =
/// set.from_list(["horse", "dog", "hippo", "monkey", "bird"]) /// set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
/// let set_b: set.Set(String) = /// let set_b =
/// set.from_list(["monkey", "bird", "ostrich", "salmon"]) /// set.from_list(["monkey", "bird", "ostrich", "salmon"])
/// metrics.overlap_coefficient(set_a, set_b) /// metrics.overlap_coefficient(set_a, set_b)
/// |> should.equal(2.0 /. 4.0) /// |> should.equal(2.0 /. 4.0)
@ -1016,11 +1016,11 @@ pub fn tversky_index(
/// </div> /// </div>
/// ///
pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float { pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
let intersection: Float = let intersection =
set.intersection(xset, yset) set.intersection(xset, yset)
|> set.size() |> set.size()
|> conversion.int_to_float() |> conversion.int_to_float()
let minsize: Float = let minsize =
piecewise.minimum(set.size(xset), set.size(yset), int.compare) piecewise.minimum(set.size(xset), set.size(yset), int.compare)
|> conversion.int_to_float() |> conversion.int_to_float()
intersection /. minsize intersection /. minsize
@ -1031,27 +1031,27 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Calculate the (weighted) cosine similarity between two lists (representing /// Calculate the (weighted) cosine similarity between two lists (representing
/// vectors): /// vectors):
/// ///
/// \\[ /// \\[
/// \frac{\sum_{i=1}^n w_{i} \cdot x_i \cdot y_i} /// \frac{\sum_{i=1}^n w_{i} \cdot x_i \cdot y_i}
/// {\left(\sum_{i=1}^n w_{i} \cdot x_i^2\right)^{\frac{1}{2}} /// {\left(\sum_{i=1}^n w_{i} \cdot x_i^2\right)^{\frac{1}{2}}
/// \cdot /// \cdot
/// \left(\sum_{i=1}^n w_{i} \cdot y_i^2\right)^{\frac{1}{2}}} /// \left(\sum_{i=1}^n w_{i} \cdot y_i^2\right)^{\frac{1}{2}}}
/// \\; \in \\; \left[-1, 1\right] /// \\; \in \\; \left[-1, 1\right]
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the two lists and \\(x_i\\), \\(y_i\\) are /// In the formula, \\(n\\) is the length of the two lists and \\(x_i\\), \\(y_i\\) are
/// the values in the respective input lists indexed by \\(i\\), while the /// the values in the respective input lists indexed by \\(i\\), while the
/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// The cosine similarity provides a value between -1 and 1, where 1 means the /// The cosine similarity provides a value between -1 and 1, where 1 means the
/// vectors are in the same direction, -1 means they are in exactly opposite /// vectors are in the same direction, -1 means they are in exactly opposite
/// directions, and 0 indicates orthogonality. /// directions, and 0 indicates orthogonality.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -1063,11 +1063,11 @@ pub fn overlap_coefficient(xset: set.Set(a), yset: set.Set(a)) -> Float {
/// // Two orthogonal vectors /// // Two orthogonal vectors
/// metrics.cosine_similarity([-1.0, 1.0, 0.0], [1.0, 1.0, -1.0], option.None) /// metrics.cosine_similarity([-1.0, 1.0, 0.0], [1.0, 1.0, -1.0], option.None)
/// |> should.equal(Ok(0.0)) /// |> should.equal(Ok(0.0))
/// ///
/// // Two identical (parallel) vectors /// // Two identical (parallel) vectors
/// metrics.cosine_similarity([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], option.None) /// metrics.cosine_similarity([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], option.None)
/// |> should.equal(Ok(1.0)) /// |> should.equal(Ok(1.0))
/// ///
/// // Two parallel, but oppositely oriented vectors /// // Two parallel, but oppositely oriented vectors
/// metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None) /// metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None)
/// |> should.equal(Ok(-1.0)) /// |> should.equal(Ok(-1.0))
@ -1090,9 +1090,9 @@ pub fn cosine_similarity(
msg msg
|> Error |> Error
Ok(_) -> { Ok(_) -> {
let zipped_arr: List(#(Float, Float)) = list.zip(xarr, yarr) let zipped_arr = list.zip(xarr, yarr)
let numerator_elements: List(Float) = let numerator_elements =
zipped_arr zipped_arr
|> list.map(fn(tuple: #(Float, Float)) -> Float { |> list.map(fn(tuple: #(Float, Float)) -> Float {
pair.first(tuple) *. pair.second(tuple) pair.first(tuple) *. pair.second(tuple)
@ -1100,26 +1100,26 @@ pub fn cosine_similarity(
case weights { case weights {
option.None -> { option.None -> {
let numerator: Float = let numerator =
numerator_elements numerator_elements
|> arithmetics.float_sum(option.None) |> arithmetics.float_sum(option.None)
let assert Ok(xarr_norm) = norm(xarr, 2.0, option.None) let assert Ok(xarr_norm) = norm(xarr, 2.0, option.None)
let assert Ok(yarr_norm) = norm(yarr, 2.0, option.None) let assert Ok(yarr_norm) = norm(yarr, 2.0, option.None)
let denominator: Float = { let denominator = {
xarr_norm *. yarr_norm xarr_norm *. yarr_norm
} }
numerator /. denominator numerator /. denominator
|> Ok |> Ok
} }
_ -> { _ -> {
let numerator: Float = let numerator =
numerator_elements numerator_elements
|> arithmetics.float_sum(weights) |> arithmetics.float_sum(weights)
let assert Ok(xarr_norm) = norm(xarr, 2.0, weights) let assert Ok(xarr_norm) = norm(xarr, 2.0, weights)
let assert Ok(yarr_norm) = norm(yarr, 2.0, weights) let assert Ok(yarr_norm) = norm(yarr, 2.0, weights)
let denominator: Float = { let denominator = {
xarr_norm *. yarr_norm xarr_norm *. yarr_norm
} }
numerator /. denominator numerator /. denominator
@ -1135,7 +1135,7 @@ pub fn cosine_similarity(
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Calculate the (weighted) Canberra distance between two lists: /// Calculate the (weighted) Canberra distance between two lists:
/// ///
/// \\[ /// \\[
@ -1143,10 +1143,10 @@ pub fn cosine_similarity(
/// {\left| x_i \right| + \left| y_i \right|} /// {\left| x_i \right| + \left| y_i \right|}
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the /// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the
/// values in the respective input lists indexed by \\(i\\), while the /// values in the respective input lists indexed by \\(i\\), while the
/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -1159,15 +1159,15 @@ pub fn cosine_similarity(
/// // Empty lists returns an error /// // Empty lists returns an error
/// metrics.canberra_distance([], [], option.None) /// metrics.canberra_distance([], [], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Different sized lists returns an error /// // Different sized lists returns an error
/// metrics.canberra_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None) /// metrics.canberra_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Valid inputs /// // Valid inputs
/// metrics.canberra_distance([1.0, 2.0], [-2.0, -1.0], option.None) /// metrics.canberra_distance([1.0, 2.0], [-2.0, -1.0], option.None)
/// |> should.equal(Ok(2.0)) /// |> should.equal(Ok(2.0))
/// ///
/// metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 0.5])) /// metrics.canberra_distance([1.0, 0.0], [0.0, 2.0], option.Some([1.0, 0.5]))
/// } /// }
/// </details> /// </details>
@ -1188,7 +1188,7 @@ pub fn canberra_distance(
msg msg
|> Error |> Error
Ok(_) -> { Ok(_) -> {
let arr: List(Float) = let arr =
list.zip(xarr, yarr) list.zip(xarr, yarr)
|> list.map(canberra_distance_helper) |> list.map(canberra_distance_helper)
@ -1209,9 +1209,9 @@ pub fn canberra_distance(
} }
fn canberra_distance_helper(tuple: #(Float, Float)) -> Float { fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
let numerator: Float = let numerator =
piecewise.float_absolute_value({ pair.first(tuple) -. pair.second(tuple) }) piecewise.float_absolute_value({ pair.first(tuple) -. pair.second(tuple) })
let denominator: Float = { let denominator = {
piecewise.float_absolute_value(pair.first(tuple)) piecewise.float_absolute_value(pair.first(tuple))
+. piecewise.float_absolute_value(pair.second(tuple)) +. piecewise.float_absolute_value(pair.second(tuple))
} }
@ -1223,7 +1223,7 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
/// <small>Spot a typo? Open an issue!</small> /// <small>Spot a typo? Open an issue!</small>
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Calculate the (weighted) Bray-Curtis distance between two lists: /// Calculate the (weighted) Bray-Curtis distance between two lists:
/// ///
/// \\[ /// \\[
@ -1231,11 +1231,11 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
/// {\sum_{i=1}^n w_{i}\left| x_i + y_i \right|} /// {\sum_{i=1}^n w_{i}\left| x_i + y_i \right|}
/// \\] /// \\]
/// ///
/// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the values /// In the formula, \\(n\\) is the length of the two lists, and \\(x_i, y_i\\) are the values
/// in the respective input lists indexed by \\(i\\), while the /// in the respective input lists indexed by \\(i\\), while the
/// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights /// \\(w_i \in \mathbb{R}_{+}\\) are corresponding positive weights
/// (\\(w_i = 1.0\\;\forall i=1...n\\) by default). /// (\\(w_i = 1.0\\;\forall i=1...n\\) by default).
/// ///
/// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are /// The Bray-Curtis distance is in the range \\([0, 1]\\) if all entries \\(x_i, y_i\\) are
/// positive. /// positive.
/// ///
@ -1250,15 +1250,15 @@ fn canberra_distance_helper(tuple: #(Float, Float)) -> Float {
/// // Empty lists returns an error /// // Empty lists returns an error
/// metrics.braycurtis_distance([], [], option.None) /// metrics.braycurtis_distance([], [], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Different sized lists returns an error /// // Different sized lists returns an error
/// metrics.braycurtis_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None) /// metrics.braycurtis_distance([1.0, 2.0], [1.0, 2.0, 3.0, 4.0], option.None)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// // Valid inputs /// // Valid inputs
/// metrics.braycurtis_distance([1.0, 0.0], [0.0, 2.0], option.None) /// metrics.braycurtis_distance([1.0, 0.0], [0.0, 2.0], option.None)
/// |> should.equal(Ok(1.0)) /// |> should.equal(Ok(1.0))
/// ///
/// metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.5, 1.0])) /// metrics.braycurtis_distance([1.0, 2.0], [3.0, 4.0], option.Some([0.5, 1.0]))
/// |> should.equal(Ok(0.375)) /// |> should.equal(Ok(0.375))
/// } /// }
@ -1281,15 +1281,15 @@ pub fn braycurtis_distance(
msg msg
|> Error |> Error
Ok(_) -> { Ok(_) -> {
let zipped_arr: List(#(Float, Float)) = list.zip(xarr, yarr) let zipped_arr = list.zip(xarr, yarr)
let numerator_elements: List(Float) = let numerator_elements =
zipped_arr zipped_arr
|> list.map(fn(tuple: #(Float, Float)) -> Float { |> list.map(fn(tuple: #(Float, Float)) -> Float {
piecewise.float_absolute_value({ piecewise.float_absolute_value({
pair.first(tuple) -. pair.second(tuple) pair.first(tuple) -. pair.second(tuple)
}) })
}) })
let denominator_elements: List(Float) = let denominator_elements =
zipped_arr zipped_arr
|> list.map(fn(tuple: #(Float, Float)) -> Float { |> list.map(fn(tuple: #(Float, Float)) -> Float {
piecewise.float_absolute_value({ piecewise.float_absolute_value({

View file

@ -69,7 +69,7 @@ import gleam_community/maths/elementary
/// The ceiling function rounds a given input value \\(x \in \mathbb{R}\\) to the nearest integer /// The ceiling function rounds a given input value \\(x \in \mathbb{R}\\) to the nearest integer
/// value (at the specified digit) that is larger than or equal to the input \\(x\\). /// value (at the specified digit) that is larger than or equal to the input \\(x\\).
/// ///
/// Note: The ceiling function is used as an alias for the rounding function [`round`](#round) /// Note: The ceiling function is used as an alias for the rounding function [`round`](#round)
/// with rounding mode `RoundUp`. /// with rounding mode `RoundUp`.
/// ///
/// <details> /// <details>
@ -124,10 +124,10 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The floor function rounds input \\(x \in \mathbb{R}\\) to the nearest integer value (at the /// The floor function rounds input \\(x \in \mathbb{R}\\) to the nearest integer value (at the
/// specified digit) that is less than or equal to the input \\(x\\). /// specified digit) that is less than or equal to the input \\(x\\).
/// ///
/// Note: The floor function is used as an alias for the rounding function [`round`](#round) /// Note: The floor function is used as an alias for the rounding function [`round`](#round)
/// with rounding mode `RoundDown`. /// with rounding mode `RoundDown`.
/// ///
/// <details> /// <details>
@ -139,7 +139,7 @@ pub fn ceiling(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -182,11 +182,11 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The truncate function rounds a given input \\(x \in \mathbb{R}\\) to the nearest integer /// The truncate function rounds a given input \\(x \in \mathbb{R}\\) to the nearest integer
/// value (at the specified digit) that is less than or equal to the absolute value of the /// value (at the specified digit) that is less than or equal to the absolute value of the
/// input \\(x\\). /// input \\(x\\).
/// ///
/// Note: The truncate function is used as an alias for the rounding function [`round`](#round) /// Note: The truncate function is used as an alias for the rounding function [`round`](#round)
/// with rounding mode `RoundToZero`. /// with rounding mode `RoundToZero`.
/// ///
/// <details> /// <details>
@ -198,7 +198,7 @@ pub fn floor(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -241,18 +241,18 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function rounds a float to a specific number of digits (after the decimal place or before /// The function rounds a float to a specific number of digits (after the decimal place or before
/// if negative) using a specified rounding mode. /// if negative) using a specified rounding mode.
/// ///
/// Valid rounding modes include: /// Valid rounding modes include:
/// - `RoundNearest` (default): The input \\(x\\) is rounded to the nearest integer value (at the /// - `RoundNearest` (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 /// specified digit) with ties (fractional values of 0.5) being rounded to the nearest even
/// integer. /// integer.
/// - `RoundTiesAway`: The input \\(x\\) is rounded to the nearest integer value (at the /// - `RoundTiesAway`: 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++ /// specified digit) with ties (fractional values of 0.5) being rounded away from zero (C/C++
/// rounding behavior). /// rounding behavior).
/// - `RoundTiesUp`: The input \\(x\\) is rounded to the nearest integer value (at the specified /// - `RoundTiesUp`: 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\\) /// digit) with ties (fractional values of 0.5) being rounded towards \\(+\infty\\)
/// (Java/JavaScript rounding behaviour). /// (Java/JavaScript rounding behaviour).
/// - `RoundToZero`: The input \\(x\\) is rounded to the nearest integer value (at the specified /// - `RoundToZero`: 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 /// digit) that is less than or equal to the absolute value of the input \\(x\\). An alias for
@ -260,8 +260,8 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// - `RoundDown`: The input \\(x\\) is rounded to the nearest integer value (at the specified /// - `RoundDown`: 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 /// digit) that is less than or equal to the input \\(x\\). An alias for this rounding mode is
/// [`floor`](#floor). /// [`floor`](#floor).
/// - `RoundUp`: The input \\(x\\) is rounded to the nearest integer value (at the specified /// - `RoundUp`: 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 /// digit) that is larger than or equal to the input \\(x\\). An alias for this rounding mode
/// is [`ceiling`](#ceiling). /// is [`ceiling`](#ceiling).
/// ///
/// <details> /// <details>
@ -273,7 +273,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -285,7 +285,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.07\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -309,7 +309,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -321,7 +321,7 @@ pub fn truncate(x: Float, digits: option.Option(Int)) -> Float {
/// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`) /// - \\(12.06\\) for 2 digits after the decimal point (`digits = 2`)
/// - \\(12.065\\) for 3 digits after the decimal point (`digits = 3`) /// - \\(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 /// 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. /// number refers to the digits before the decimal point.
/// - \\(10.0\\) for 1 digit before the decimal point (`digits = -1`) /// - \\(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 2 digits before the decimal point (`digits = -2`)
@ -424,9 +424,9 @@ fn do_round(p: Float, x: Float, mode: option.Option(RoundingMode)) -> Float {
} }
fn round_to_nearest(p: Float, x: Float) -> Float { fn round_to_nearest(p: Float, x: Float) -> Float {
let xabs: Float = float_absolute_value(x) *. p let xabs = float_absolute_value(x) *. p
let xabs_truncated: Float = truncate_float(xabs) let xabs_truncated = truncate_float(xabs)
let remainder: Float = xabs -. xabs_truncated let remainder = xabs -. xabs_truncated
case remainder { case remainder {
_ if remainder >. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p _ if remainder >. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p
_ if remainder == 0.5 -> { _ if remainder == 0.5 -> {
@ -441,8 +441,8 @@ fn round_to_nearest(p: Float, x: Float) -> Float {
} }
fn round_ties_away(p: Float, x: Float) -> Float { fn round_ties_away(p: Float, x: Float) -> Float {
let xabs: Float = float_absolute_value(x) *. p let xabs = float_absolute_value(x) *. p
let remainder: Float = xabs -. truncate_float(xabs) let remainder = xabs -. truncate_float(xabs)
case remainder { case remainder {
_ if remainder >=. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p _ if remainder >=. 0.5 -> float_sign(x) *. truncate_float(xabs +. 1.0) /. p
_ -> float_sign(x) *. truncate_float(xabs) /. p _ -> float_sign(x) *. truncate_float(xabs) /. p
@ -450,9 +450,9 @@ fn round_ties_away(p: Float, x: Float) -> Float {
} }
fn round_ties_up(p: Float, x: Float) -> Float { fn round_ties_up(p: Float, x: Float) -> Float {
let xabs: Float = float_absolute_value(x) *. p let xabs = float_absolute_value(x) *. p
let xabs_truncated: Float = truncate_float(xabs) let xabs_truncated = truncate_float(xabs)
let remainder: Float = xabs -. xabs_truncated let remainder = xabs -. xabs_truncated
case remainder { case remainder {
_ if remainder >=. 0.5 && x >=. 0.0 -> _ if remainder >=. 0.5 && x >=. 0.0 ->
float_sign(x) *. truncate_float(xabs +. 1.0) /. p float_sign(x) *. truncate_float(xabs +. 1.0) /. p
@ -500,7 +500,7 @@ fn do_ceiling(a: Float) -> Float
/// The absolute value: /// The absolute value:
/// ///
/// \\[ /// \\[
/// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}. /// \forall x \in \mathbb{R}, \\; |x| \in \mathbb{R}_{+}.
/// \\] /// \\]
/// ///
/// The function takes an input \\(x\\) and returns a positive float value. /// The function takes an input \\(x\\) and returns a positive float value.
@ -529,7 +529,7 @@ pub fn float_absolute_value(x: Float) -> Float {
/// The absolute value: /// The absolute value:
/// ///
/// \\[ /// \\[
/// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}. /// \forall x \in \mathbb{Z}, \\; |x| \in \mathbb{Z}_{+}.
/// \\] /// \\]
/// ///
/// The function takes an input \\(x\\) and returns a positive integer value. /// The function takes an input \\(x\\) and returns a positive integer value.
@ -709,7 +709,7 @@ fn do_int_sign(a: Int) -> Int
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\) /// The function takes two arguments \\(x, y \in \mathbb{R}\\) and returns \\(x\\)
/// such that it has the same sign as \\(y\\). /// such that it has the same sign as \\(y\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
@ -735,7 +735,7 @@ pub fn float_copy_sign(x: Float, y: Float) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\) /// The function takes two arguments \\(x, y \in \mathbb{Z}\\) and returns \\(x\\)
/// such that it has the same sign as \\(y\\). /// such that it has the same sign as \\(y\\).
/// ///
/// <div style="text-align: right;"> /// <div style="text-align: right;">
@ -1211,8 +1211,8 @@ pub fn extrema(
[x, ..rest] -> [x, ..rest] ->
Ok( Ok(
list.fold(rest, #(x, x), fn(acc: #(a, a), element: a) { list.fold(rest, #(x, x), fn(acc: #(a, a), element: a) {
let first: a = pair.first(acc) let first = pair.first(acc)
let second: a = pair.second(acc) let second = pair.second(acc)
case compare(element, first), compare(second, element) { case compare(element, first), compare(second, element) {
order.Lt, order.Lt -> #(element, element) order.Lt, order.Lt -> #(element, element)
order.Lt, _ -> #(element, second) order.Lt, _ -> #(element, second)

View file

@ -20,12 +20,12 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Predicates: A module containing functions for testing various mathematical //// Predicates: A module containing functions for testing various mathematical
//// properties of numbers. //// properties of numbers.
//// ////
//// * **Tests** //// * **Tests**
//// * [`is_close`](#is_close) //// * [`is_close`](#is_close)
//// * [`list_all_close`](#all_close) //// * [`list_all_close`](#all_close)
@ -38,7 +38,7 @@
//// * [`is_divisible`](#is_divisible) //// * [`is_divisible`](#is_divisible)
//// * [`is_multiple`](#is_multiple) //// * [`is_multiple`](#is_multiple)
//// * [`is_prime`](#is_prime) //// * [`is_prime`](#is_prime)
//// ////
import gleam/int import gleam/int
import gleam/list import gleam/list
@ -54,16 +54,16 @@ import gleam_community/maths/piecewise
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// Determine if a given value \\(a\\) is close to or equivalent to a reference value /// Determine if a given value \\(a\\) is close to or equivalent to a reference value
/// \\(b\\) based on supplied relative \\(r_{tol}\\) and absolute \\(a_{tol}\\) tolerance /// \\(b\\) based on supplied relative \\(r_{tol}\\) and absolute \\(a_{tol}\\) tolerance
/// values. The equivalance of the two given values are then determined based on /// values. The equivalance of the two given values are then determined based on
/// the equation: /// the equation:
/// ///
/// \\[ /// \\[
/// \|a - b\| \leq (a_{tol} + r_{tol} \cdot \|b\|) /// \|a - b\| \leq (a_{tol} + r_{tol} \cdot \|b\|)
/// \\] /// \\]
/// ///
/// `True` is returned if statement holds, otherwise `False` is returned. /// `True` is returned if statement holds, otherwise `False` is returned.
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
/// ///
@ -71,12 +71,12 @@ import gleam_community/maths/piecewise
/// import gleam_community/maths/predicates /// import gleam_community/maths/predicates
/// ///
/// pub fn example () { /// pub fn example () {
/// let val: Float = 99. /// let val = 99.
/// let ref_val: Float = 100. /// let ref_val = 100.
/// // We set 'atol' and 'rtol' such that the values are equivalent /// // We set 'atol' and 'rtol' such that the values are equivalent
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1 /// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
/// let rtol: Float = 0.01 /// let rtol = 0.01
/// let atol: Float = 0.10 /// let atol = 0.10
/// floatx.is_close(val, ref_val, rtol, atol) /// floatx.is_close(val, ref_val, rtol, atol)
/// |> should.be_true() /// |> should.be_true()
/// } /// }
@ -89,8 +89,8 @@ import gleam_community/maths/piecewise
/// </div> /// </div>
/// ///
pub fn is_close(a: Float, b: Float, rtol: Float, atol: Float) -> Bool { pub fn is_close(a: Float, b: Float, rtol: Float, atol: Float) -> Bool {
let x: Float = float_absolute_difference(a, b) let x = float_absolute_difference(a, b)
let y: Float = atol +. rtol *. float_absolute_value(b) let y = atol +. rtol *. float_absolute_value(b)
case x <=. y { case x <=. y {
True -> True True -> True
False -> False False -> False
@ -126,14 +126,14 @@ fn float_absolute_difference(a: Float, b: Float) -> Float {
/// import gleam_community/maths/predicates /// import gleam_community/maths/predicates
/// ///
/// pub fn example () { /// pub fn example () {
/// let val: Float = 99. /// let val = 99.
/// let ref_val: Float = 100. /// let ref_val = 100.
/// let xarr: List(Float) = list.repeat(val, 42) /// let xarr = list.repeat(val, 42)
/// let yarr: List(Float) = list.repeat(ref_val, 42) /// let yarr = list.repeat(ref_val, 42)
/// // We set 'atol' and 'rtol' such that the values are equivalent /// // We set 'atol' and 'rtol' such that the values are equivalent
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1 /// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
/// let rtol: Float = 0.01 /// let rtol = 0.01
/// let atol: Float = 0.10 /// let atol = 0.10
/// predicates.all_close(xarr, yarr, rtol, atol) /// predicates.all_close(xarr, yarr, rtol, atol)
/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { /// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
/// case zarr { /// case zarr {
@ -160,8 +160,8 @@ pub fn all_close(
rtol: Float, rtol: Float,
atol: Float, atol: Float,
) -> Result(List(Bool), String) { ) -> Result(List(Bool), String) {
let xlen: Int = list.length(xarr) let xlen = list.length(xarr)
let ylen: Int = list.length(yarr) let ylen = list.length(yarr)
case xlen == ylen { case xlen == ylen {
False -> False ->
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
@ -182,10 +182,10 @@ pub fn all_close(
/// </div> /// </div>
/// ///
/// Determine if a given value is fractional. /// Determine if a given value is fractional.
/// ///
/// `True` is returned if the given value is fractional, otherwise `False` is /// `True` is returned if the given value is fractional, otherwise `False` is
/// returned. /// returned.
/// ///
/// <details> /// <details>
/// <summary>Example</summary> /// <summary>Example</summary>
/// ///
@ -195,7 +195,7 @@ pub fn all_close(
/// pub fn example () { /// pub fn example () {
/// predicates.is_fractional(0.3333) /// predicates.is_fractional(0.3333)
/// |> should.equal(True) /// |> should.equal(True)
/// ///
/// predicates.is_fractional(1.0) /// predicates.is_fractional(1.0)
/// |> should.equal(False) /// |> should.equal(False)
/// } /// }
@ -222,7 +222,7 @@ fn do_ceiling(a: Float) -> Float
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
/// power of another integer value \\(y \in \mathbb{Z}\\). /// power of another integer value \\(y \in \mathbb{Z}\\).
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -262,9 +262,9 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value \\(n \in \mathbb{Z}\\) is a /// A function that tests whether a given integer value \\(n \in \mathbb{Z}\\) is a
/// perfect number. A number is perfect if it is equal to the sum of its proper /// perfect number. A number is perfect if it is equal to the sum of its proper
/// positive divisors. /// positive divisors.
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
@ -314,7 +314,7 @@ fn do_sum(arr: List(Int)) -> Int {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even. /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is even.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -325,7 +325,7 @@ fn do_sum(arr: List(Int)) -> Int {
/// pub fn example() { /// pub fn example() {
/// predicates.is_even(-3) /// predicates.is_even(-3)
/// |> should.equal(False) /// |> should.equal(False)
/// ///
/// predicates.is_even(-4) /// predicates.is_even(-4)
/// |> should.equal(True) /// |> should.equal(True)
/// } /// }
@ -347,7 +347,7 @@ pub fn is_even(x: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd. /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is odd.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
@ -358,7 +358,7 @@ pub fn is_even(x: Int) -> Bool {
/// pub fn example() { /// pub fn example() {
/// predicates.is_odd(-3) /// predicates.is_odd(-3)
/// |> should.equal(True) /// |> should.equal(True)
/// ///
/// predicates.is_odd(-4) /// predicates.is_odd(-4)
/// |> should.equal(False) /// |> should.equal(False)
/// } /// }
@ -380,24 +380,24 @@ pub fn is_odd(x: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a /// A function that tests whether a given integer value \\(x \in \mathbb{Z}\\) is a
/// prime number. A prime number is a natural number greater than 1 that has no /// prime number. A prime number is a natural number greater than 1 that has no
/// positive divisors other than 1 and itself. /// positive divisors other than 1 and itself.
/// ///
/// The function uses the Miller-Rabin primality test to assess if \\(x\\) is prime. /// The function uses the Miller-Rabin primality test to assess if \\(x\\) is prime.
/// It is a probabilistic test, so it can mistakenly identify a composite number /// It is a probabilistic test, so it can mistakenly identify a composite number
/// as prime. However, the probability of such errors decreases with more testing /// as prime. However, the probability of such errors decreases with more testing
/// iterations (the function uses 64 iterations internally, which is typically /// iterations (the function uses 64 iterations internally, which is typically
/// more than sufficient). The Miller-Rabin test is particularly useful for large /// more than sufficient). The Miller-Rabin test is particularly useful for large
/// numbers. /// numbers.
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
/// Examples of prime numbers: /// Examples of prime numbers:
/// - \\(2\\) is a prime number since it has only two divisors: \\(1\\) and \\(2\\). /// - \\(2\\) is a prime number since it has only two divisors: \\(1\\) and \\(2\\).
/// - \\(7\\) is a prime number since it has only two divisors: \\(1\\) and \\(7\\). /// - \\(7\\) is a prime number since it has only two divisors: \\(1\\) and \\(7\\).
/// - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such /// - \\(4\\) is not a prime number since it has divisors other than \\(1\\) and itself, such
/// as \\(2\\). /// as \\(2\\).
/// ///
/// </details> /// </details>
@ -414,7 +414,7 @@ pub fn is_odd(x: Int) -> Bool {
/// ///
/// predicates.is_prime(4) /// predicates.is_prime(4)
/// |> should.equal(False) /// |> should.equal(False)
/// ///
/// // Test the 2nd Carmichael number /// // Test the 2nd Carmichael number
/// predicates.is_prime(1105) /// predicates.is_prime(1105)
/// |> should.equal(False) /// |> should.equal(False)
@ -446,7 +446,7 @@ fn miller_rabin_test(n: Int, k: Int) -> Bool {
_, 0 -> True _, 0 -> True
_, _ -> { _, _ -> {
// Generate a random int in the range [2, n] // Generate a random int in the range [2, n]
let random_candidate: Int = 2 + int.random(n - 2) let random_candidate = 2 + int.random(n - 2)
case powmod_with_check(random_candidate, n - 1, n) == 1 { case powmod_with_check(random_candidate, n - 1, n) == 1 {
True -> miller_rabin_test(n, k - 1) True -> miller_rabin_test(n, k - 1)
False -> False False -> False
@ -459,7 +459,7 @@ fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
case exponent, { exponent % 2 } == 0 { case exponent, { exponent % 2 } == 0 {
0, _ -> 1 0, _ -> 1
_, True -> { _, True -> {
let x: Int = powmod_with_check(base, exponent / 2, modulus) let x = powmod_with_check(base, exponent / 2, modulus)
case { x * x } % modulus, x != 1 && x != { modulus - 1 } { case { x * x } % modulus, x != 1 && x != { modulus - 1 } {
1, True -> 0 1, True -> 0
_, _ -> { x * x } % modulus _, _ -> { x * x } % modulus
@ -512,9 +512,9 @@ pub fn is_between(x: Float, lower: Float, upper: Float) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another /// A function that tests whether a given integer \\(n \in \mathbb{Z}\\) is divisible by another
/// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\). /// integer \\(d \in \mathbb{Z}\\), such that \\(n \mod d = 0\\).
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///
@ -555,9 +555,9 @@ pub fn is_divisible(n: Int, d: Int) -> Bool {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another /// A function that tests whether a given integer \\(m \in \mathbb{Z}\\) is a multiple of another
/// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \times q\\), with \\(q \in \mathbb{Z}\\). /// integer \\(k \in \mathbb{Z}\\), such that \\(m = k \times q\\), with \\(q \in \mathbb{Z}\\).
/// ///
/// <details> /// <details>
/// <summary>Details</summary> /// <summary>Details</summary>
/// ///

View file

@ -20,18 +20,18 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Sequences: A module containing functions for generating various types of //// Sequences: A module containing functions for generating various types of
//// sequences, ranges and intervals. //// sequences, ranges and intervals.
//// ////
//// * **Ranges and intervals** //// * **Ranges and intervals**
//// * [`arange`](#arange) //// * [`arange`](#arange)
//// * [`linear_space`](#linear_space) //// * [`linear_space`](#linear_space)
//// * [`logarithmic_space`](#logarithmic_space) //// * [`logarithmic_space`](#logarithmic_space)
//// * [`geometric_space`](#geometric_space) //// * [`geometric_space`](#geometric_space)
//// ////
import gleam/iterator import gleam/iterator
import gleam_community/maths/conversion import gleam_community/maths/conversion
@ -47,7 +47,7 @@ import gleam_community/maths/piecewise
/// The function returns an iterator generating evenly spaced values within a given interval. /// The function returns an iterator generating evenly spaced values within a given interval.
/// based on a start value but excludes the stop value. The spacing between values is determined /// based on a start value but excludes the stop value. The spacing between values is determined
/// by the step size provided. The function supports both positive and negative step values. /// by the step size provided. The function supports both positive and negative step values.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -59,13 +59,13 @@ import gleam_community/maths/piecewise
/// sequences.arange(1.0, 5.0, 1.0) /// sequences.arange(1.0, 5.0, 1.0)
/// |> iterator.to_list() /// |> iterator.to_list()
/// |> should.equal([1.0, 2.0, 3.0, 4.0]) /// |> should.equal([1.0, 2.0, 3.0, 4.0])
/// ///
/// // No points returned since /// // No points returned since
/// // start is smaller than stop and the step is positive /// // start is smaller than stop and the step is positive
/// sequences.arange(5.0, 1.0, 1.0) /// sequences.arange(5.0, 1.0, 1.0)
/// |> iterator.to_list() /// |> iterator.to_list()
/// |> should.equal([]) /// |> should.equal([])
/// ///
/// // Points returned since /// // Points returned since
/// // start smaller than stop but negative step /// // start smaller than stop but negative step
/// sequences.arange(5.0, 1.0, -1.0) /// sequences.arange(5.0, 1.0, -1.0)
@ -115,10 +115,10 @@ pub fn arange(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns an iterator for generating linearly spaced points over a specified /// The function returns an iterator for generating linearly spaced points over a specified
/// interval. The endpoint of the interval can optionally be included/excluded. The number of /// 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. /// points and whether the endpoint is included determine the spacing between values.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -138,7 +138,7 @@ pub fn arange(
/// 0.0, /// 0.0,
/// tol, /// tol,
/// ) /// )
/// ///
/// result /// result
/// |> list.all(fn(x) { x == True }) /// |> list.all(fn(x) { x == True })
/// |> should.be_true() /// |> should.be_true()
@ -161,7 +161,7 @@ pub fn linear_space(
num: Int, num: Int,
endpoint: Bool, endpoint: Bool,
) -> Result(iterator.Iterator(Float), String) { ) -> Result(iterator.Iterator(Float), String) {
let direction: Float = case start <=. stop { let direction = case start <=. stop {
True -> 1.0 True -> 1.0
False -> -1.0 False -> -1.0
} }
@ -196,10 +196,10 @@ pub fn linear_space(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns an iterator of logarithmically spaced points over a specified interval. /// The function returns an iterator of logarithmically spaced points over a specified interval.
/// The endpoint of the interval can optionally be included/excluded. The number of points, base, /// 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. /// and whether the endpoint is included determine the spacing between values.
/// ///
/// <details> /// <details>
/// <summary>Example:</summary> /// <summary>Example:</summary>
/// ///
@ -264,9 +264,9 @@ pub fn logarithmic_space(
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The function returns an iterator of numbers spaced evenly on a log scale (a geometric /// 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 /// 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 /// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
/// specified directly. /// specified directly.
/// ///
/// <details> /// <details>
@ -295,7 +295,7 @@ pub fn logarithmic_space(
/// // Input (start and stop can't be equal to 0.0) /// // Input (start and stop can't be equal to 0.0)
/// sequences.geometric_space(0.0, 1000.0, 3, False) /// sequences.geometric_space(0.0, 1000.0, 3, False)
/// |> should.be_error() /// |> should.be_error()
/// ///
/// sequences.geometric_space(-1000.0, 0.0, 3, False) /// sequences.geometric_space(-1000.0, 0.0, 3, False)
/// |> should.be_error() /// |> should.be_error()
/// ///

View file

@ -20,17 +20,17 @@
////<style> ////<style>
//// .katex { font-size: 1.1em; } //// .katex { font-size: 1.1em; }
////</style> ////</style>
//// ////
//// --- //// ---
//// ////
//// Special: A module containing special mathematical functions. //// Special: A module containing special mathematical functions.
//// ////
//// * **Special mathematical functions** //// * **Special mathematical functions**
//// * [`beta`](#beta) //// * [`beta`](#beta)
//// * [`erf`](#erf) //// * [`erf`](#erf)
//// * [`gamma`](#gamma) //// * [`gamma`](#gamma)
//// * [`incomplete_gamma`](#incomplete_gamma) //// * [`incomplete_gamma`](#incomplete_gamma)
//// ////
import gleam/list import gleam/list
import gleam_community/maths/conversion import gleam_community/maths/conversion
@ -76,17 +76,17 @@ pub fn beta(x: Float, y: Float) -> Float {
/// </div> /// </div>
/// ///
pub fn erf(x: Float) -> Float { pub fn erf(x: Float) -> Float {
let assert [a1, a2, a3, a4, a5]: List(Float) = [ let assert [a1, a2, a3, a4, a5] = [
0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429, 0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429,
] ]
let p: Float = 0.3275911 let p = 0.3275911
let sign: Float = piecewise.float_sign(x) let sign = piecewise.float_sign(x)
let x: Float = piecewise.float_absolute_value(x) let x = piecewise.float_absolute_value(x)
// Formula 7.1.26 given in Abramowitz and Stegun. // Formula 7.1.26 given in Abramowitz and Stegun.
let t: Float = 1.0 /. { 1.0 +. p *. x } let t = 1.0 /. { 1.0 +. p *. x }
let y: Float = let y =
1.0 1.0
-. { { { { a5 *. t +. a4 } *. t +. a3 } *. t +. a2 } *. t +. a1 } -. { { { { a5 *. t +. a4 } *. t +. a3 } *. t +. a2 } *. t +. a1 }
*. t *. t
@ -100,7 +100,7 @@ pub fn erf(x: Float) -> Float {
/// </a> /// </a>
/// </div> /// </div>
/// ///
/// The gamma function over the real numbers. The function is essentially equal to /// The gamma function over the real numbers. The function is essentially equal to
/// the factorial for any positive integer argument: \\(\Gamma(n) = (n - 1)!\\) /// the factorial for any positive integer argument: \\(\Gamma(n) = (n - 1)!\\)
/// ///
/// The implemented gamma function is approximated through Lanczos approximation /// The implemented gamma function is approximated through Lanczos approximation
@ -131,14 +131,14 @@ fn gamma_lanczos(x: Float) -> Float {
/. { elementary.sin(elementary.pi() *. x) *. gamma_lanczos(1.0 -. x) } /. { elementary.sin(elementary.pi() *. x) *. gamma_lanczos(1.0 -. x) }
False -> { False -> {
let z = x -. 1.0 let z = x -. 1.0
let x: Float = let x =
list.index_fold(lanczos_p, 0.0, fn(acc: Float, v: Float, index: Int) { list.index_fold(lanczos_p, 0.0, fn(acc: Float, v: Float, index: Int) {
case index > 0 { case index > 0 {
True -> acc +. v /. { z +. conversion.int_to_float(index) } True -> acc +. v /. { z +. conversion.int_to_float(index) }
False -> v False -> v
} }
}) })
let t: Float = z +. lanczos_g +. 0.5 let t = z +. lanczos_g +. 0.5
let assert Ok(v1) = elementary.power(2.0 *. elementary.pi(), 0.5) let assert Ok(v1) = elementary.power(2.0 *. elementary.pi(), 0.5)
let assert Ok(v2) = elementary.power(t, z +. 0.5) let assert Ok(v2) = elementary.power(t, z +. 0.5)
v1 *. v2 *. elementary.exponential(-1.0 *. t) *. x v1 *. v2 *. elementary.exponential(-1.0 *. t) *. x
@ -189,8 +189,8 @@ fn incomplete_gamma_sum(
case t { case t {
0.0 -> s 0.0 -> s
_ -> { _ -> {
let ns: Float = s +. t let ns = s +. t
let nt: Float = t *. { x /. { a +. n } } let nt = t *. { x /. { a +. n } }
incomplete_gamma_sum(a, x, nt, ns, n +. 1.0) incomplete_gamma_sum(a, x, nt, ns, n +. 1.0)
} }
} }

View file

@ -317,7 +317,7 @@ pub fn median_test() {
pub fn variance_test() { pub fn variance_test() {
// Degrees of freedom // Degrees of freedom
let ddof: Int = 1 let ddof = 1
// An empty list returns an error // An empty list returns an error
[] []
@ -332,7 +332,7 @@ pub fn variance_test() {
pub fn standard_deviation_test() { pub fn standard_deviation_test() {
// Degrees of freedom // Degrees of freedom
let ddof: Int = 1 let ddof = 1
// An empty list returns an error // An empty list returns an error
[] []
@ -349,19 +349,18 @@ pub fn jaccard_index_test() {
metrics.jaccard_index(set.from_list([]), set.from_list([])) metrics.jaccard_index(set.from_list([]), set.from_list([]))
|> should.equal(0.0) |> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9]) let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9]) let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.jaccard_index(set_a, set_b) metrics.jaccard_index(set_a, set_b)
|> should.equal(4.0 /. 10.0) |> should.equal(4.0 /. 10.0)
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5]) let set_c = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10]) let set_d = set.from_list([6, 7, 8, 9, 10])
metrics.jaccard_index(set_c, set_d) metrics.jaccard_index(set_c, set_d)
|> should.equal(0.0 /. 11.0) |> should.equal(0.0 /. 11.0)
let set_e: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"]) let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
let set_f: set.Set(String) = let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon"])
set.from_list(["monkey", "rhino", "ostrich", "salmon"])
metrics.jaccard_index(set_e, set_f) metrics.jaccard_index(set_e, set_f)
|> should.equal(1.0 /. 7.0) |> should.equal(1.0 /. 7.0)
} }
@ -370,19 +369,18 @@ pub fn sorensen_dice_coefficient_test() {
metrics.sorensen_dice_coefficient(set.from_list([]), set.from_list([])) metrics.sorensen_dice_coefficient(set.from_list([]), set.from_list([]))
|> should.equal(0.0) |> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9]) let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9]) let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.sorensen_dice_coefficient(set_a, set_b) metrics.sorensen_dice_coefficient(set_a, set_b)
|> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 }) |> should.equal(2.0 *. 4.0 /. { 7.0 +. 7.0 })
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5]) let set_c = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10]) let set_d = set.from_list([6, 7, 8, 9, 10])
metrics.sorensen_dice_coefficient(set_c, set_d) metrics.sorensen_dice_coefficient(set_c, set_d)
|> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 }) |> should.equal(2.0 *. 0.0 /. { 6.0 +. 5.0 })
let set_e: set.Set(String) = set.from_list(["cat", "dog", "hippo", "monkey"]) let set_e = set.from_list(["cat", "dog", "hippo", "monkey"])
let set_f: set.Set(String) = let set_f = set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
set.from_list(["monkey", "rhino", "ostrich", "salmon", "spider"])
metrics.sorensen_dice_coefficient(set_e, set_f) metrics.sorensen_dice_coefficient(set_e, set_f)
|> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 }) |> should.equal(2.0 *. 1.0 /. { 4.0 +. 5.0 })
} }
@ -391,20 +389,18 @@ pub fn overlap_coefficient_test() {
metrics.overlap_coefficient(set.from_list([]), set.from_list([])) metrics.overlap_coefficient(set.from_list([]), set.from_list([]))
|> should.equal(0.0) |> should.equal(0.0)
let set_a: set.Set(Int) = set.from_list([0, 1, 2, 5, 6, 8, 9]) let set_a = set.from_list([0, 1, 2, 5, 6, 8, 9])
let set_b: set.Set(Int) = set.from_list([0, 2, 3, 4, 5, 7, 9]) let set_b = set.from_list([0, 2, 3, 4, 5, 7, 9])
metrics.overlap_coefficient(set_a, set_b) metrics.overlap_coefficient(set_a, set_b)
|> should.equal(4.0 /. 7.0) |> should.equal(4.0 /. 7.0)
let set_c: set.Set(Int) = set.from_list([0, 1, 2, 3, 4, 5]) let set_c = set.from_list([0, 1, 2, 3, 4, 5])
let set_d: set.Set(Int) = set.from_list([6, 7, 8, 9, 10]) let set_d = set.from_list([6, 7, 8, 9, 10])
metrics.overlap_coefficient(set_c, set_d) metrics.overlap_coefficient(set_c, set_d)
|> should.equal(0.0 /. 5.0) |> should.equal(0.0 /. 5.0)
let set_e: set.Set(String) = let set_e = set.from_list(["horse", "dog", "hippo", "monkey", "bird"])
set.from_list(["horse", "dog", "hippo", "monkey", "bird"]) let set_f = set.from_list(["monkey", "bird", "ostrich", "salmon"])
let set_f: set.Set(String) =
set.from_list(["monkey", "bird", "ostrich", "salmon"])
metrics.overlap_coefficient(set_e, set_f) metrics.overlap_coefficient(set_e, set_f)
|> should.equal(2.0 /. 4.0) |> should.equal(2.0 /. 4.0)
} }
@ -440,7 +436,7 @@ pub fn cosine_similarity_test() {
metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None) metrics.cosine_similarity([-1.0, -2.0, -3.0], [1.0, 2.0, 3.0], option.None)
|> should.equal(Ok(-1.0)) |> should.equal(Ok(-1.0))
// Try with arbitrary valid input // Try with arbitrary valid input
let assert Ok(result) = let assert Ok(result) =
metrics.cosine_similarity([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None) metrics.cosine_similarity([1.0, 2.0, 3.0], [4.0, 5.0, 6.0], option.None)
result result

View file

@ -3,25 +3,25 @@ import gleam_community/maths/predicates
import gleeunit/should import gleeunit/should
pub fn float_is_close_test() { pub fn float_is_close_test() {
let val: Float = 99.0 let val = 99.0
let ref_val: Float = 100.0 let ref_val = 100.0
// We set 'atol' and 'rtol' such that the values are equivalent // We set 'atol' and 'rtol' such that the values are equivalent
// if 'val' is within 1 percent of 'ref_val' +/- 0.1 // if 'val' is within 1 percent of 'ref_val' +/- 0.1
let rtol: Float = 0.01 let rtol = 0.01
let atol: Float = 0.1 let atol = 0.1
predicates.is_close(val, ref_val, rtol, atol) predicates.is_close(val, ref_val, rtol, atol)
|> should.be_true() |> should.be_true()
} }
pub fn float_list_all_close_test() { pub fn float_list_all_close_test() {
let val: Float = 99.0 let val = 99.0
let ref_val: Float = 100.0 let ref_val = 100.0
let xarr: List(Float) = list.repeat(val, 42) let xarr = list.repeat(val, 42)
let yarr: List(Float) = list.repeat(ref_val, 42) let yarr = list.repeat(ref_val, 42)
// We set 'atol' and 'rtol' such that the values are equivalent // We set 'atol' and 'rtol' such that the values are equivalent
// if 'val' is within 1 percent of 'ref_val' +/- 0.1 // if 'val' is within 1 percent of 'ref_val' +/- 0.1
let rtol: Float = 0.01 let rtol = 0.01
let atol: Float = 0.1 let atol = 0.1
predicates.all_close(xarr, yarr, rtol, atol) predicates.all_close(xarr, yarr, rtol, atol)
|> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) { |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
case zarr { case zarr {