mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-15 07:59:01 +00:00
Organize modules
This commit is contained in:
parent
62eb90da2b
commit
086cf17088
14 changed files with 4090 additions and 2162 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
393
src/gleam_community/maths/float_list.gleam
Normal file
393
src/gleam_community/maths/float_list.gleam
Normal file
|
@ -0,0 +1,393 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing several different kinds of mathematical functions
|
||||
//// applying to lists of real numbers.
|
||||
////
|
||||
//// Function naming has been adopted from <a href="https://en.wikipedia.org/wiki/C_mathematical_functions"> C mathematical function</a>.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Miscellaneous functions**
|
||||
//// * [`allclose`](#allclose)
|
||||
//// * [`amax`](#amax)
|
||||
//// * [`amin`](#amin)
|
||||
//// * [`argmax`](#argmax)
|
||||
//// * [`argmin`](#argmin)
|
||||
//// * [`allclose`](#allclose)
|
||||
//// * [`trim`](#trim)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
import gleam/pair
|
||||
import gleam_community/maths/float as floatx
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Determine if a list of values are close to or equivalent to a
|
||||
/// another list of reference values.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// let val: Float = 99.
|
||||
/// let ref_val: Float = 100.
|
||||
/// let xarr: List(Float) = list.repeat(val, 42)
|
||||
/// let yarr: List(Float) = list.repeat(ref_val, 42)
|
||||
/// // We set 'atol' and 'rtol' such that the values are equivalent
|
||||
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
||||
/// let rtol: Float = 0.01
|
||||
/// let atol: Float = 0.10
|
||||
/// stats.allclose(xarr, yarr, rtol, atol)
|
||||
/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
|
||||
/// case zarr {
|
||||
/// Ok(arr) ->
|
||||
/// arr
|
||||
/// |> list.all(fn(a: Bool) -> Bool { a })
|
||||
/// |> Ok
|
||||
/// _ -> Nil |> Error
|
||||
/// }
|
||||
/// }
|
||||
/// |> should.equal(Ok(True))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn allclose(
|
||||
xarr: List(Float),
|
||||
yarr: List(Float),
|
||||
rtol: Float,
|
||||
atol: Float,
|
||||
) -> Result(List(Bool), String) {
|
||||
let xlen: Int = list.length(xarr)
|
||||
let ylen: Int = list.length(yarr)
|
||||
case xlen == ylen {
|
||||
False ->
|
||||
"Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)."
|
||||
|> Error
|
||||
True ->
|
||||
list.zip(xarr, yarr)
|
||||
|> list.map(fn(z: #(Float, Float)) -> Bool {
|
||||
floatx.isclose(pair.first(z), pair.second(z), rtol, atol)
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the indices of the minimum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.argmin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.argmin()
|
||||
/// |> should.equal(Ok([4]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn argmin(arr: List(Float)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(min) =
|
||||
arr
|
||||
|> amin()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Float) -> Int {
|
||||
case a -. min {
|
||||
0. -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the indices of the maximum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.argmax()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.argmax()
|
||||
/// |> should.equal(Ok([0, 1]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn argmax(arr: List(Float)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(max) =
|
||||
arr
|
||||
|> amax()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Float) -> Int {
|
||||
case a -. max {
|
||||
0. -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the maximum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amax()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amax()
|
||||
/// |> should.equal(Ok(4.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn amax(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Float, a: Float) {
|
||||
case a >. acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the minimum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amin()
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn amin(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Float, a: Float) {
|
||||
case a <. acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the minimum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amin()
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn extrema(arr: List(Float)) -> Result(#(Float, Float), String) {
|
||||
todo
|
||||
// case arr {
|
||||
// [] ->
|
||||
// "Invalid input argument: The list is empty."
|
||||
// |> Error
|
||||
// _ -> {
|
||||
// assert Ok(val0) = list.at(arr, 0)
|
||||
// arr
|
||||
// |> list.fold(
|
||||
// val0,
|
||||
// fn(acc: Float, a: Float) {
|
||||
// case a <. acc {
|
||||
// True -> a
|
||||
// False -> acc
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
// |> Ok
|
||||
// }
|
||||
// }
|
||||
}
|
455
src/gleam_community/maths/int.gleam
Normal file
455
src/gleam_community/maths/int.gleam
Normal file
|
@ -0,0 +1,455 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing several different kinds of mathematical functions
|
||||
//// applying to integer numbers.
|
||||
////
|
||||
//// Function naming has been adopted from <a href="https://en.wikipedia.org/wiki/C_mathematical_functions"> C mathematical function</a>.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Mathematical functions**
|
||||
//// * [`absdiff`](#abs_diff)
|
||||
//// * [`flipsign`](#flipsign)
|
||||
//// * [`max`](#max)
|
||||
//// * [`min`](#min)
|
||||
//// * [`minmax`](#minmax)
|
||||
//// * [`round`](#round)
|
||||
//// * [`sign`](#sign)
|
||||
//// * **Combinatorial functions**
|
||||
//// * [`combination`](#combination)
|
||||
//// * [`factorial`](#factorial)
|
||||
//// * [`permutation`](#permutation)
|
||||
//// * **Tests**
|
||||
//// * [`ispow2`](#ispow2)
|
||||
//// * [`iseven`](#iseven)
|
||||
//// * [`isodd`](#isodd)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The min function.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// math.min(2.0, 1.5)
|
||||
/// |> should.equal(1.5)
|
||||
///
|
||||
/// math.min(1.5, 2.0)
|
||||
/// |> should.equal(1.5)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn min(x: Int, y: Int) -> Int {
|
||||
case x < y {
|
||||
True -> x
|
||||
False -> y
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The min function.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// math.min(2.0, 1.5)
|
||||
/// |> should.equal(1.5)
|
||||
///
|
||||
/// math.min(1.5, 2.0)
|
||||
/// |> should.equal(1.5)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn max(x: Int, y: Int) -> Int {
|
||||
case x > y {
|
||||
True -> x
|
||||
False -> y
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The minmax function.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// math.minmax(2.0, 1.5)
|
||||
/// |> should.equal(#(1.5, 2.0))
|
||||
///
|
||||
/// math.minmax(1.5, 2.0)
|
||||
/// |> should.equal(#(1.5, 2.0))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn minmax(x: Int, y: Int) -> #(Int, Int) {
|
||||
#(min(x, y), max(x, y))
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The sign function which returns the sign of the input, indicating
|
||||
/// whether it is positive, negative, or zero.
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn sign(x: Int) -> Int {
|
||||
do_sign(x)
|
||||
}
|
||||
|
||||
if erlang {
|
||||
fn do_sign(x: Int) -> Int {
|
||||
case x < 0 {
|
||||
True -> -1
|
||||
False ->
|
||||
case x == 0 {
|
||||
True -> 0
|
||||
False -> 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if javascript {
|
||||
external fn do_sign(Int) -> Int =
|
||||
"../math.mjs" "sign"
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn flipsign(x: Int) -> Int {
|
||||
-1 * x
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// A combinatorial function for computing the number of a $$k$$-combinations of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// C(n, k) = \binom{n}{k} = \frac{n!}{k! (n-k)!}
|
||||
/// \\]
|
||||
/// Also known as "$$n$$ choose $$k$$" or the binomial coefficient.
|
||||
///
|
||||
/// The implementation uses the effecient iterative multiplicative formula for the computation.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// math.combination(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// math.combination(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// math.combination(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// math.combination(4, 2)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn combination(n: Int, k: Int) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == 0 || k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
let min = case k < n - k {
|
||||
True -> k
|
||||
False -> n - k
|
||||
}
|
||||
list.range(1, min)
|
||||
|> list.fold(
|
||||
1,
|
||||
fn(acc: Int, x: Int) -> Int { acc * { n + 1 - x } / x },
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// A combinatorial function for computing the total number of combinations of $$n$$
|
||||
/// elements, that is $$n!$$.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// math.factorial(-1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// math.factorial(0)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// math.factorial(1)
|
||||
/// |> should.equal(Ok(1))
|
||||
/// math.factorial(2)
|
||||
/// |> should.equal(Ok(2))
|
||||
/// math.factorial(3)
|
||||
/// |> should.equal(Ok(6))
|
||||
/// math.factorial(4)
|
||||
/// |> should.equal(Ok(24))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn factorial(n) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case n {
|
||||
0 ->
|
||||
1
|
||||
|> Ok
|
||||
1 ->
|
||||
1
|
||||
|> Ok
|
||||
_ ->
|
||||
list.range(1, n)
|
||||
|> list.fold(1, fn(acc: Int, x: Int) { acc * x })
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// A combinatorial function for computing the number of $$k$$-permuations (without repetitions)
|
||||
/// of $$n$$ elements:
|
||||
///
|
||||
/// \\[
|
||||
/// P(n, k) = \frac{n!}{(n - k)!}
|
||||
/// \\]
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// // Invalid input gives an error
|
||||
/// // Error on: n = -1 < 0
|
||||
/// math.permutation(-1, 1)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// math.permutation(4, 0)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// math.permutation(4, 4)
|
||||
/// |> should.equal(Ok(1))
|
||||
///
|
||||
/// math.permutation(4, 2)
|
||||
/// |> should.equal(Ok(12))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn permutation(n: Int, k: Int) -> Result(Int, String) {
|
||||
case n < 0 {
|
||||
True ->
|
||||
"Invalid input argument: n < 0. Valid input is n > 0."
|
||||
|> Error
|
||||
False ->
|
||||
case k < 0 || k > n {
|
||||
True ->
|
||||
0
|
||||
|> Ok
|
||||
False ->
|
||||
case k == n {
|
||||
True ->
|
||||
1
|
||||
|> Ok
|
||||
False -> {
|
||||
assert Ok(v1) = factorial(n)
|
||||
assert Ok(v2) = factorial(n - k)
|
||||
v1 / v2
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The absolute difference:
|
||||
///
|
||||
/// \\[
|
||||
/// \forall x, y \in \mathbb{Z}, \\; |x - y| \in \mathbb{Z}_{+}.
|
||||
/// \\]
|
||||
///
|
||||
/// The function takes two inputs $$x$$ and $$y$$ and returns a positive integer
|
||||
/// value which is the the absolute difference of the inputs.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/math
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// math.absdiff(-10.0, 10.0)
|
||||
/// |> should.equal(20.0)
|
||||
///
|
||||
/// math.absdiff(0.0, -2.0)
|
||||
/// |> should.equal(2.0)
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn absdiff(a: Int, b: Int) -> Int {
|
||||
a - b
|
||||
|> int.absolute_value()
|
||||
}
|
328
src/gleam_community/maths/int_list.gleam
Normal file
328
src/gleam_community/maths/int_list.gleam
Normal file
|
@ -0,0 +1,328 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing several different kinds of mathematical functions
|
||||
//// applying to lists of real numbers.
|
||||
////
|
||||
//// Function naming has been adopted from <a href="https://en.wikipedia.org/wiki/C_mathematical_functions"> C mathematical function</a>.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Miscellaneous functions**
|
||||
//// * [`allclose`](#allclose)
|
||||
//// * [`amax`](#amax)
|
||||
//// * [`amin`](#amin)
|
||||
//// * [`argmax`](#argmax)
|
||||
//// * [`argmin`](#argmin)
|
||||
//// * [`allclose`](#allclose)
|
||||
//// * [`trim`](#trim)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
import gleam/pair
|
||||
import gleam_community/maths/int as intx
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the indices of the minimum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.argmin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.argmin()
|
||||
/// |> should.equal(Ok([4]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn argmin(arr: List(Float)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(min) =
|
||||
arr
|
||||
|> amin()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Float) -> Int {
|
||||
case a -. min {
|
||||
0. -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the indices of the maximum values in a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.argmax()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.argmax()
|
||||
/// |> should.equal(Ok([0, 1]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn argmax(arr: List(Float)) -> Result(List(Int), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(max) =
|
||||
arr
|
||||
|> amax()
|
||||
arr
|
||||
|> list.index_map(fn(index: Int, a: Float) -> Int {
|
||||
case a -. max {
|
||||
0. -> index
|
||||
_ -> -1
|
||||
}
|
||||
})
|
||||
|> list.filter(fn(index: Int) -> Bool {
|
||||
case index {
|
||||
-1 -> False
|
||||
_ -> True
|
||||
}
|
||||
})
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the maximum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amax()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amax()
|
||||
/// |> should.equal(Ok(4.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn amax(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Float, a: Float) {
|
||||
case a >. acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the minimum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amin()
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn amin(arr: List(Float)) -> Result(Float, String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ -> {
|
||||
assert Ok(val0) = list.at(arr, 0)
|
||||
arr
|
||||
|> list.fold(
|
||||
val0,
|
||||
fn(acc: Float, a: Float) {
|
||||
case a <. acc {
|
||||
True -> a
|
||||
False -> acc
|
||||
}
|
||||
},
|
||||
)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Returns the minimum value of a list.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.amin()
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Valid input returns a result
|
||||
/// [4., 4., 3., 2., 1.]
|
||||
/// |> stats.amin()
|
||||
/// |> should.equal(Ok(1.))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn extrema(arr: List(Float)) -> Result(#(Float, Float), String) {
|
||||
todo
|
||||
// case arr {
|
||||
// [] ->
|
||||
// "Invalid input argument: The list is empty."
|
||||
// |> Error
|
||||
// _ -> {
|
||||
// assert Ok(val0) = list.at(arr, 0)
|
||||
// arr
|
||||
// |> list.fold(
|
||||
// val0,
|
||||
// fn(acc: Float, a: Float) {
|
||||
// case a <. acc {
|
||||
// True -> a
|
||||
// False -> acc
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
// |> Ok
|
||||
// }
|
||||
// }
|
||||
}
|
87
src/gleam_community/maths/list.gleam
Normal file
87
src/gleam_community/maths/list.gleam
Normal file
|
@ -0,0 +1,87 @@
|
|||
////<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
||||
////<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
|
||||
////<script>
|
||||
//// document.addEventListener("DOMContentLoaded", function() {
|
||||
//// renderMathInElement(document.body, {
|
||||
//// // customised options
|
||||
//// // • auto-render specific keys, e.g.:
|
||||
//// delimiters: [
|
||||
//// {left: '$$', right: '$$', display: false},
|
||||
//// {left: '\\[', right: '\\]', display: true}
|
||||
//// ],
|
||||
//// // • rendering keys, e.g.:
|
||||
//// throwOnError : false
|
||||
//// });
|
||||
//// });
|
||||
////</script>
|
||||
////<style>
|
||||
//// .katex { font-size: 1.1em; }
|
||||
////</style>
|
||||
////
|
||||
//// A module containing several different kinds of mathematical functions
|
||||
//// applying to lists of real numbers.
|
||||
////
|
||||
//// Function naming has been adopted from <a href="https://en.wikipedia.org/wiki/C_mathematical_functions"> C mathematical function</a>.
|
||||
////
|
||||
//// ---
|
||||
////
|
||||
//// * **Miscellaneous functions**
|
||||
//// * [`trim`](#trim)
|
||||
|
||||
import gleam/list
|
||||
import gleam/int
|
||||
import gleam/float
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/nicklasxyz/gleam_stats/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Trim a list to a certain size given min/max indices. The min/max indices
|
||||
/// are inclusive.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleeunit/should
|
||||
/// import gleam_stats/stats
|
||||
///
|
||||
/// pub fn example () {
|
||||
/// // An empty lists returns an error
|
||||
/// []
|
||||
/// |> stats.trim(0, 0)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
/// // Trim the list to only the middle part of list
|
||||
/// [1., 2., 3., 4., 5., 6.]
|
||||
/// |> stats.trim(1, 4)
|
||||
/// |> should.equal(Ok([2., 3., 4., 5.]))
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn trim(arr: List(a), min: Int, max: Int) -> Result(List(a), String) {
|
||||
case arr {
|
||||
[] ->
|
||||
"Invalid input argument: The list is empty."
|
||||
|> Error
|
||||
_ ->
|
||||
case min >= 0 && max < list.length(arr) {
|
||||
False ->
|
||||
"Invalid input argument: min < 0 or max < length(arr). Valid input is min > 0 and max < length(arr)."
|
||||
|> Error
|
||||
True ->
|
||||
arr
|
||||
|> list.drop(min)
|
||||
|> list.take(max - min + 1)
|
||||
|> Ok
|
||||
}
|
||||
}
|
||||
}
|
410
temp.gleam
Normal file
410
temp.gleam
Normal file
|
@ -0,0 +1,410 @@
|
|||
//// Small examples ALSO used in the docs...
|
||||
|
||||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/pair
|
||||
import gleam_stats/stats
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn example_sum_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.sum()
|
||||
|> should.equal(0.)
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3.]
|
||||
|> stats.sum()
|
||||
|> should.equal(6.)
|
||||
}
|
||||
|
||||
pub fn example_mean_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.mean()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3.]
|
||||
|> stats.mean()
|
||||
|> should.equal(Ok(2.))
|
||||
}
|
||||
|
||||
pub fn example_median_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.median()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3.]
|
||||
|> stats.median()
|
||||
|> should.equal(Ok(2.))
|
||||
|
||||
[1., 2., 3., 4.]
|
||||
|> stats.median()
|
||||
|> should.equal(Ok(2.5))
|
||||
}
|
||||
|
||||
pub fn example_hmean_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.hmean()
|
||||
|> should.be_error()
|
||||
|
||||
// List with negative numbers returns an error
|
||||
[-1., -3., -6.]
|
||||
|> stats.hmean()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 3., 6.]
|
||||
|> stats.hmean()
|
||||
|> should.equal(Ok(2.))
|
||||
}
|
||||
|
||||
pub fn example_gmean_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.gmean()
|
||||
|> should.be_error()
|
||||
// List with negative numbers returns an error
|
||||
[-1., -3., -6.]
|
||||
|> stats.gmean()
|
||||
|> should.be_error()
|
||||
// Valid input returns a result
|
||||
[1., 3., 9.]
|
||||
|> stats.gmean()
|
||||
|> should.equal(Ok(3.))
|
||||
}
|
||||
|
||||
pub fn example_var_test() {
|
||||
// Degrees of freedom
|
||||
let ddof: Int = 1
|
||||
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.var(ddof)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3.]
|
||||
|> stats.var(ddof)
|
||||
|> should.equal(Ok(1.))
|
||||
}
|
||||
|
||||
pub fn example_std_test() {
|
||||
// Degrees of freedom
|
||||
let ddof: Int = 1
|
||||
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.std(ddof)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3.]
|
||||
|> stats.std(ddof)
|
||||
|> should.equal(Ok(1.))
|
||||
}
|
||||
|
||||
pub fn example_moment_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.moment(0)
|
||||
|> should.be_error()
|
||||
|
||||
// 0th moment about the mean is 1. per definition
|
||||
[0., 1., 2., 3., 4.]
|
||||
|> stats.moment(0)
|
||||
|> should.equal(Ok(1.))
|
||||
|
||||
// 1st moment about the mean is 0. per definition
|
||||
[0., 1., 2., 3., 4.]
|
||||
|> stats.moment(1)
|
||||
|> should.equal(Ok(0.))
|
||||
|
||||
// 2nd moment about the mean
|
||||
[0., 1., 2., 3., 4.]
|
||||
|> stats.moment(2)
|
||||
|> should.equal(Ok(2.))
|
||||
}
|
||||
|
||||
pub fn example_skewness_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.skewness()
|
||||
|> should.be_error()
|
||||
|
||||
// No skewness
|
||||
// -> Zero skewness
|
||||
[1., 2., 3., 4.]
|
||||
|> stats.skewness()
|
||||
|> should.equal(Ok(0.))
|
||||
|
||||
// Right-skewed distribution
|
||||
// -> Positive skewness
|
||||
[1., 1., 1., 2.]
|
||||
|> stats.skewness()
|
||||
|> fn(x: Result(Float, String)) -> Bool {
|
||||
case x {
|
||||
Ok(x) -> x >. 0.
|
||||
_ -> False
|
||||
}
|
||||
}
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn example_kurtosis_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.skewness()
|
||||
|> should.be_error()
|
||||
|
||||
// No tail
|
||||
// -> Fisher's definition gives kurtosis -3
|
||||
[1., 1., 1., 1.]
|
||||
|> stats.kurtosis()
|
||||
|> should.equal(Ok(-3.))
|
||||
|
||||
// Distribution with a tail
|
||||
// -> Higher kurtosis
|
||||
[1., 1., 1., 2.]
|
||||
|> stats.kurtosis()
|
||||
|> fn(x: Result(Float, String)) -> Bool {
|
||||
case x {
|
||||
Ok(x) -> x >. -3.
|
||||
_ -> False
|
||||
}
|
||||
}
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn example_zscore_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
// Use degrees of freedom = 1
|
||||
|> stats.zscore(1)
|
||||
|> should.be_error()
|
||||
|
||||
[1., 2., 3.]
|
||||
// Use degrees of freedom = 1
|
||||
|> stats.zscore(1)
|
||||
|> should.equal(Ok([-1., 0., 1.]))
|
||||
}
|
||||
|
||||
pub fn example_percentile_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.percentile(40)
|
||||
|> should.be_error()
|
||||
|
||||
// Calculate 40th percentile
|
||||
[15., 20., 35., 40., 50.]
|
||||
|> stats.percentile(40)
|
||||
|> should.equal(Ok(29.))
|
||||
}
|
||||
|
||||
pub fn example_iqr_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.iqr()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[1., 2., 3., 4., 5.]
|
||||
|> stats.iqr()
|
||||
|> should.equal(Ok(3.))
|
||||
}
|
||||
|
||||
pub fn example_freedman_diaconis_rule_test() {
|
||||
// An empty list returns an error
|
||||
[]
|
||||
|> stats.freedman_diaconis_rule()
|
||||
|> should.be_error()
|
||||
|
||||
// Calculate histogram bin widths
|
||||
list.range(0, 1000)
|
||||
|> list.map(fn(x: Int) -> Float { int.to_float(x) })
|
||||
|> stats.freedman_diaconis_rule()
|
||||
|> should.equal(Ok(10.))
|
||||
}
|
||||
|
||||
pub fn example_range_test() {
|
||||
// Create a range
|
||||
let range = stats.Range(0., 1.)
|
||||
// Retrieve min and max values
|
||||
let stats.Range(min, max) = range
|
||||
min
|
||||
|> should.equal(0.)
|
||||
max
|
||||
|> should.equal(1.)
|
||||
}
|
||||
|
||||
pub fn example_bin_test() {
|
||||
// Create a bin
|
||||
let bin: stats.Bin = #(stats.Range(0., 1.), 999)
|
||||
// Retrieve min and max values
|
||||
let stats.Range(min, max) = pair.first(bin)
|
||||
min
|
||||
|> should.equal(0.)
|
||||
max
|
||||
|> should.equal(1.)
|
||||
// Retrieve count
|
||||
let count = pair.second(bin)
|
||||
count
|
||||
|> should.equal(999)
|
||||
}
|
||||
|
||||
pub fn example_histogram_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.histogram(1.)
|
||||
|> should.be_error()
|
||||
// Create the bins of a histogram given a list of values
|
||||
list.range(0, 100)
|
||||
|> list.map(fn(x: Int) -> Float { int.to_float(x) })
|
||||
// Below 25. is the bin width
|
||||
// The Freedman-Diaconis’s Rule can be used to determine a decent value
|
||||
|> stats.histogram(25.)
|
||||
|> should.equal(Ok([
|
||||
#(stats.Range(0., 25.), 25),
|
||||
#(stats.Range(25., 50.), 25),
|
||||
#(stats.Range(50., 75.), 25),
|
||||
#(stats.Range(75., 100.), 25),
|
||||
]))
|
||||
}
|
||||
|
||||
pub fn example_correlation_test() {
|
||||
// An empty lists returns an error
|
||||
stats.correlation([], [])
|
||||
|> should.be_error()
|
||||
|
||||
// Lists with fewer than 2 elements return an error
|
||||
stats.correlation([1.0], [1.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Lists of uneqal length return an error
|
||||
stats.correlation([1.0, 2.0, 3.0], [1.0, 2.0])
|
||||
|> should.be_error()
|
||||
|
||||
// Perfect positive correlation
|
||||
let xarr0: List(Float) =
|
||||
list.range(0, 100)
|
||||
|> list.map(fn(x: Int) -> Float { int.to_float(x) })
|
||||
let yarr0: List(Float) =
|
||||
list.range(0, 100)
|
||||
|> list.map(fn(x: Int) -> Float { int.to_float(x) })
|
||||
stats.correlation(xarr0, yarr0)
|
||||
|> should.equal(Ok(1.))
|
||||
|
||||
// Perfect negative correlation
|
||||
let xarr0: List(Float) =
|
||||
list.range(0, 100)
|
||||
|> list.map(fn(x: Int) -> Float { -1. *. int.to_float(x) })
|
||||
let yarr0: List(Float) =
|
||||
list.range(0, 100)
|
||||
|> list.map(fn(x: Int) -> Float { int.to_float(x) })
|
||||
stats.correlation(xarr0, yarr0)
|
||||
|> should.equal(Ok(-1.))
|
||||
}
|
||||
|
||||
pub fn example_trim_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.trim(0, 0)
|
||||
|> should.be_error()
|
||||
|
||||
// Trim the list to only the middle part of list
|
||||
[1., 2., 3., 4., 5., 6.]
|
||||
|> stats.trim(1, 4)
|
||||
|> should.equal(Ok([2., 3., 4., 5.]))
|
||||
}
|
||||
|
||||
pub fn example_isclose_test() {
|
||||
let val: Float = 99.
|
||||
let ref_val: Float = 100.
|
||||
// We set 'atol' and 'rtol' such that the values are equivalent
|
||||
// if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
||||
let rtol: Float = 0.01
|
||||
let atol: Float = 0.10
|
||||
stats.isclose(val, ref_val, rtol, atol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn example_allclose_test() {
|
||||
let val: Float = 99.
|
||||
let ref_val: Float = 100.
|
||||
let xarr: List(Float) = list.repeat(val, 42)
|
||||
let yarr: List(Float) = list.repeat(ref_val, 42)
|
||||
// We set 'atol' and 'rtol' such that the values are equivalent
|
||||
// if 'val' is within 1 percent of 'ref_val' +/- 0.1
|
||||
let rtol: Float = 0.01
|
||||
let atol: Float = 0.10
|
||||
stats.allclose(xarr, yarr, rtol, atol)
|
||||
|> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
|
||||
case zarr {
|
||||
Ok(arr) ->
|
||||
arr
|
||||
|> list.all(fn(a: Bool) -> Bool { a })
|
||||
|> Ok
|
||||
_ ->
|
||||
Nil
|
||||
|> Error
|
||||
}
|
||||
}
|
||||
|> should.equal(Ok(True))
|
||||
}
|
||||
|
||||
pub fn example_amax_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.amax()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4., 4., 3., 2., 1.]
|
||||
|> stats.amax()
|
||||
|> should.equal(Ok(4.))
|
||||
}
|
||||
|
||||
pub fn example_amin_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.amin()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4., 4., 3., 2., 1.]
|
||||
|> stats.amin()
|
||||
|> should.equal(Ok(1.))
|
||||
}
|
||||
|
||||
pub fn example_argmax_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.argmax()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4., 4., 3., 2., 1.]
|
||||
|> stats.argmax()
|
||||
|> should.equal(Ok([0, 1]))
|
||||
}
|
||||
|
||||
pub fn example_argmin_test() {
|
||||
// An empty lists returns an error
|
||||
[]
|
||||
|> stats.argmin()
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
[4., 4., 3., 2., 1.]
|
||||
|> stats.argmin()
|
||||
|> should.equal(Ok([4]))
|
||||
}
|
592
test/eunit_progress.erl
Normal file
592
test/eunit_progress.erl
Normal file
|
@ -0,0 +1,592 @@
|
|||
%% eunit_formatters https://github.com/seancribbs/eunit_formatters
|
||||
%% Changes made to the original code:
|
||||
%% - Embedded binomial_heap.erl file contents into current file.
|
||||
%% - ignore warnings for heap implementation to keep complete implementation.
|
||||
%% - removed "namespaced_dicts" dependant preprocessor directive,
|
||||
%% as it does not apply for our project, we just assume OTP version >= 17.
|
||||
%% This is because the previous verison uses rebar, and we won't do that.
|
||||
|
||||
%% Copyright 2014 Sean Cribbs
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
|
||||
|
||||
%% @doc A listener/reporter for eunit that prints '.' for each
|
||||
%% success, 'F' for each failure, and 'E' for each error. It can also
|
||||
%% optionally summarize the failures at the end.
|
||||
-compile({nowarn_unused_function, [insert/2, to_list/1, to_list/2, size/1]}).
|
||||
-module(eunit_progress).
|
||||
-behaviour(eunit_listener).
|
||||
-define(NOTEST, true).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(RED, "\e[0;31m").
|
||||
-define(GREEN, "\e[0;32m").
|
||||
-define(YELLOW, "\e[0;33m").
|
||||
-define(WHITE, "\e[0;37m").
|
||||
-define(CYAN, "\e[0;36m").
|
||||
-define(RESET, "\e[0m").
|
||||
|
||||
-record(node,{
|
||||
rank = 0 :: non_neg_integer(),
|
||||
key :: term(),
|
||||
value :: term(),
|
||||
children = new() :: binomial_heap()
|
||||
}).
|
||||
|
||||
-export_type([binomial_heap/0, heap_node/0]).
|
||||
-type binomial_heap() :: [ heap_node() ].
|
||||
-type heap_node() :: #node{}.
|
||||
|
||||
%% eunit_listener callbacks
|
||||
-export([
|
||||
init/1,
|
||||
handle_begin/3,
|
||||
handle_end/3,
|
||||
handle_cancel/3,
|
||||
terminate/2,
|
||||
start/0,
|
||||
start/1
|
||||
]).
|
||||
|
||||
%% -- binomial_heap.erl content start --
|
||||
|
||||
-record(state, {
|
||||
status = dict:new() :: euf_dict(),
|
||||
failures = [] :: [[pos_integer()]],
|
||||
skips = [] :: [[pos_integer()]],
|
||||
timings = new() :: binomial_heap(),
|
||||
colored = true :: boolean(),
|
||||
profile = false :: boolean()
|
||||
}).
|
||||
|
||||
-type euf_dict() :: dict:dict().
|
||||
|
||||
-spec new() -> binomial_heap().
|
||||
new() ->
|
||||
[].
|
||||
|
||||
% Inserts a new pair into the heap (or creates a new heap)
|
||||
-spec insert(term(), term()) -> binomial_heap().
|
||||
insert(Key,Value) ->
|
||||
insert(Key,Value,[]).
|
||||
|
||||
-spec insert(term(), term(), binomial_heap()) -> binomial_heap().
|
||||
insert(Key,Value,Forest) ->
|
||||
insTree(#node{key=Key,value=Value},Forest).
|
||||
|
||||
% Merges two heaps
|
||||
-spec merge(binomial_heap(), binomial_heap()) -> binomial_heap().
|
||||
merge(TS1,[]) when is_list(TS1) -> TS1;
|
||||
merge([],TS2) when is_list(TS2) -> TS2;
|
||||
merge([#node{rank=R1}=T1|TS1]=F1,[#node{rank=R2}=T2|TS2]=F2) ->
|
||||
if
|
||||
R1 < R2 ->
|
||||
[T1 | merge(TS1,F2)];
|
||||
R2 < R1 ->
|
||||
[T2 | merge(F1, TS2)];
|
||||
true ->
|
||||
insTree(link(T1,T2),merge(TS1,TS2))
|
||||
end.
|
||||
|
||||
% Deletes the top entry from the heap and returns it
|
||||
-spec delete(binomial_heap()) -> {{term(), term()}, binomial_heap()}.
|
||||
delete(TS) ->
|
||||
{#node{key=Key,value=Value,children=TS1},TS2} = getMin(TS),
|
||||
{{Key,Value},merge(lists:reverse(TS1),TS2)}.
|
||||
|
||||
% Turns the heap into list in heap order
|
||||
-spec to_list(binomial_heap()) -> [{term(), term()}].
|
||||
to_list([]) -> [];
|
||||
to_list(List) when is_list(List) ->
|
||||
to_list([],List).
|
||||
to_list(Acc, []) ->
|
||||
lists:reverse(Acc);
|
||||
to_list(Acc,Forest) ->
|
||||
{Next, Trees} = delete(Forest),
|
||||
to_list([Next|Acc], Trees).
|
||||
|
||||
% Take N elements from the top of the heap
|
||||
-spec take(non_neg_integer(), binomial_heap()) -> [{term(), term()}].
|
||||
take(N,Trees) when is_integer(N), is_list(Trees) ->
|
||||
take(N,Trees,[]).
|
||||
take(0,_Trees,Acc) ->
|
||||
lists:reverse(Acc);
|
||||
take(_N,[],Acc)->
|
||||
lists:reverse(Acc);
|
||||
take(N,Trees,Acc) ->
|
||||
{Top,T2} = delete(Trees),
|
||||
take(N-1,T2,[Top|Acc]).
|
||||
|
||||
% Get an estimate of the size based on the binomial property
|
||||
-spec size(binomial_heap()) -> non_neg_integer().
|
||||
size(Forest) ->
|
||||
erlang:trunc(lists:sum([math:pow(2,R) || #node{rank=R} <- Forest])).
|
||||
|
||||
%% Private API
|
||||
-spec link(heap_node(), heap_node()) -> heap_node().
|
||||
link(#node{rank=R,key=X1,children=C1}=T1,#node{key=X2,children=C2}=T2) ->
|
||||
case X1 < X2 of
|
||||
true ->
|
||||
T1#node{rank=R+1,children=[T2|C1]};
|
||||
_ ->
|
||||
T2#node{rank=R+1,children=[T1|C2]}
|
||||
end.
|
||||
|
||||
insTree(Tree, []) ->
|
||||
[Tree];
|
||||
insTree(#node{rank=R1}=T1, [#node{rank=R2}=T2|Rest] = TS) ->
|
||||
case R1 < R2 of
|
||||
true ->
|
||||
[T1|TS];
|
||||
_ ->
|
||||
insTree(link(T1,T2),Rest)
|
||||
end.
|
||||
|
||||
getMin([T]) ->
|
||||
{T,[]};
|
||||
getMin([#node{key=K} = T|TS]) ->
|
||||
{#node{key=K1} = T1,TS1} = getMin(TS),
|
||||
case K < K1 of
|
||||
true -> {T,TS};
|
||||
_ -> {T1,[T|TS1]}
|
||||
end.
|
||||
|
||||
%% -- binomial_heap.erl content end --
|
||||
|
||||
%% Startup
|
||||
start() ->
|
||||
start([]).
|
||||
|
||||
start(Options) ->
|
||||
eunit_listener:start(?MODULE, Options).
|
||||
|
||||
%%------------------------------------------
|
||||
%% eunit_listener callbacks
|
||||
%%------------------------------------------
|
||||
init(Options) ->
|
||||
#state{colored=proplists:get_bool(colored, Options),
|
||||
profile=proplists:get_bool(profile, Options)}.
|
||||
|
||||
handle_begin(group, Data, St) ->
|
||||
GID = proplists:get_value(id, Data),
|
||||
Dict = St#state.status,
|
||||
St#state{status=dict:store(GID, orddict:from_list([{type, group}|Data]), Dict)};
|
||||
handle_begin(test, Data, St) ->
|
||||
TID = proplists:get_value(id, Data),
|
||||
Dict = St#state.status,
|
||||
St#state{status=dict:store(TID, orddict:from_list([{type, test}|Data]), Dict)}.
|
||||
|
||||
handle_end(group, Data, St) ->
|
||||
St#state{status=merge_on_end(Data, St#state.status)};
|
||||
handle_end(test, Data, St) ->
|
||||
NewStatus = merge_on_end(Data, St#state.status),
|
||||
St1 = print_progress(Data, St),
|
||||
St2 = record_timing(Data, St1),
|
||||
St2#state{status=NewStatus}.
|
||||
|
||||
handle_cancel(_, Data, #state{status=Status, skips=Skips}=St) ->
|
||||
Status1 = merge_on_end(Data, Status),
|
||||
ID = proplists:get_value(id, Data),
|
||||
St#state{status=Status1, skips=[ID|Skips]}.
|
||||
|
||||
terminate({ok, Data}, St) ->
|
||||
print_failures(St),
|
||||
print_pending(St),
|
||||
print_profile(St),
|
||||
print_timing(St),
|
||||
print_results(Data, St);
|
||||
terminate({error, Reason}, St) ->
|
||||
io:nl(), io:nl(),
|
||||
print_colored(io_lib:format("Eunit failed: ~25p~n", [Reason]), ?RED, St),
|
||||
sync_end(error).
|
||||
|
||||
sync_end(Result) ->
|
||||
receive
|
||||
{stop, Reference, ReplyTo} ->
|
||||
ReplyTo ! {result, Reference, Result},
|
||||
ok
|
||||
end.
|
||||
|
||||
%%------------------------------------------
|
||||
%% Print and collect information during run
|
||||
%%------------------------------------------
|
||||
print_progress(Data, St) ->
|
||||
TID = proplists:get_value(id, Data),
|
||||
case proplists:get_value(status, Data) of
|
||||
ok ->
|
||||
print_progress_success(St),
|
||||
St;
|
||||
{skipped, _Reason} ->
|
||||
print_progress_skipped(St),
|
||||
St#state{skips=[TID|St#state.skips]};
|
||||
{error, Exception} ->
|
||||
print_progress_failed(Exception, St),
|
||||
St#state{failures=[TID|St#state.failures]}
|
||||
end.
|
||||
|
||||
record_timing(Data, State=#state{timings=T, profile=true}) ->
|
||||
TID = proplists:get_value(id, Data),
|
||||
case lists:keyfind(time, 1, Data) of
|
||||
{time, Int} ->
|
||||
%% It's a min-heap, so we insert negative numbers instead
|
||||
%% of the actuals and normalize when we report on them.
|
||||
T1 = insert(-Int, TID, T),
|
||||
State#state{timings=T1};
|
||||
false ->
|
||||
State
|
||||
end;
|
||||
record_timing(_Data, State) ->
|
||||
State.
|
||||
|
||||
print_progress_success(St) ->
|
||||
print_colored(".", ?GREEN, St).
|
||||
|
||||
print_progress_skipped(St) ->
|
||||
print_colored("*", ?YELLOW, St).
|
||||
|
||||
print_progress_failed(_Exc, St) ->
|
||||
print_colored("F", ?RED, St).
|
||||
|
||||
merge_on_end(Data, Dict) ->
|
||||
ID = proplists:get_value(id, Data),
|
||||
dict:update(ID,
|
||||
fun(Old) ->
|
||||
orddict:merge(fun merge_data/3, Old, orddict:from_list(Data))
|
||||
end, Dict).
|
||||
|
||||
merge_data(_K, undefined, X) -> X;
|
||||
merge_data(_K, X, undefined) -> X;
|
||||
merge_data(_K, _, X) -> X.
|
||||
|
||||
%%------------------------------------------
|
||||
%% Print information at end of run
|
||||
%%------------------------------------------
|
||||
print_failures(#state{failures=[]}) ->
|
||||
ok;
|
||||
print_failures(#state{failures=Fails}=State) ->
|
||||
io:nl(),
|
||||
io:fwrite("Failures:~n",[]),
|
||||
lists:foldr(print_failure_fun(State), 1, Fails),
|
||||
ok.
|
||||
|
||||
print_failure_fun(#state{status=Status}=State) ->
|
||||
fun(Key, Count) ->
|
||||
TestData = dict:fetch(Key, Status),
|
||||
TestId = format_test_identifier(TestData),
|
||||
io:fwrite("~n ~p) ~ts~n", [Count, TestId]),
|
||||
print_failure_reason(proplists:get_value(status, TestData),
|
||||
proplists:get_value(output, TestData),
|
||||
State),
|
||||
io:nl(),
|
||||
Count + 1
|
||||
end.
|
||||
|
||||
print_failure_reason({skipped, Reason}, _Output, State) ->
|
||||
print_colored(io_lib:format(" ~ts~n", [format_pending_reason(Reason)]),
|
||||
?RED, State);
|
||||
print_failure_reason({error, {_Class, Term, Stack}}, Output, State) when
|
||||
is_tuple(Term), tuple_size(Term) == 2, is_list(element(2, Term)) ->
|
||||
print_assertion_failure(Term, Stack, Output, State),
|
||||
print_failure_output(5, Output, State);
|
||||
print_failure_reason({error, {error, Error, Stack}}, Output, State) when is_list(Stack) ->
|
||||
print_colored(indent(5, "Failure: ~p~n", [Error]), ?RED, State),
|
||||
print_stack(Stack, State),
|
||||
print_failure_output(5, Output, State);
|
||||
print_failure_reason({error, Reason}, Output, State) ->
|
||||
print_colored(indent(5, "Failure: ~p~n", [Reason]), ?RED, State),
|
||||
print_failure_output(5, Output, State).
|
||||
|
||||
print_stack(Stack, State) ->
|
||||
print_colored(indent(5, "Stacktrace:~n", []), ?CYAN, State),
|
||||
print_stackframes(Stack, State).
|
||||
print_stackframes([{eunit_test, _, _, _} | Stack], State) ->
|
||||
print_stackframes(Stack, State);
|
||||
print_stackframes([{eunit_proc, _, _, _} | Stack], State) ->
|
||||
print_stackframes(Stack, State);
|
||||
print_stackframes([{Module, Function, _Arity, _Location} | Stack], State) ->
|
||||
print_colored(indent(7, "~p.~p~n", [Module, Function]), ?CYAN, State),
|
||||
print_stackframes(Stack, State);
|
||||
print_stackframes([], _State) ->
|
||||
ok.
|
||||
|
||||
|
||||
print_failure_output(_, <<>>, _) -> ok;
|
||||
print_failure_output(_, undefined, _) -> ok;
|
||||
print_failure_output(Indent, Output, State) ->
|
||||
print_colored(indent(Indent, "Output: ~ts", [Output]), ?CYAN, State).
|
||||
|
||||
print_assertion_failure({Type, Props}, Stack, Output, State) ->
|
||||
FailureDesc = format_assertion_failure(Type, Props, 5),
|
||||
{M,F,A,Loc} = lists:last(Stack),
|
||||
LocationText = io_lib:format(" %% ~ts:~p:in `~ts`", [proplists:get_value(file, Loc),
|
||||
proplists:get_value(line, Loc),
|
||||
format_function_name(M,F,A)]),
|
||||
print_colored(FailureDesc, ?RED, State),
|
||||
io:nl(),
|
||||
print_colored(LocationText, ?CYAN, State),
|
||||
io:nl(),
|
||||
print_failure_output(5, Output, State),
|
||||
io:nl().
|
||||
|
||||
print_pending(#state{skips=[]}) ->
|
||||
ok;
|
||||
print_pending(#state{status=Status, skips=Skips}=State) ->
|
||||
io:nl(),
|
||||
io:fwrite("Pending:~n", []),
|
||||
lists:foreach(fun(ID) ->
|
||||
Info = dict:fetch(ID, Status),
|
||||
case proplists:get_value(reason, Info) of
|
||||
undefined ->
|
||||
ok;
|
||||
Reason ->
|
||||
print_pending_reason(Reason, Info, State)
|
||||
end
|
||||
end, lists:reverse(Skips)),
|
||||
io:nl().
|
||||
|
||||
print_pending_reason(Reason0, Data, State) ->
|
||||
Text = case proplists:get_value(type, Data) of
|
||||
group ->
|
||||
io_lib:format(" ~ts~n", [proplists:get_value(desc, Data)]);
|
||||
test ->
|
||||
io_lib:format(" ~ts~n", [format_test_identifier(Data)])
|
||||
end,
|
||||
Reason = io_lib:format(" %% ~ts~n", [format_pending_reason(Reason0)]),
|
||||
print_colored(Text, ?YELLOW, State),
|
||||
print_colored(Reason, ?CYAN, State).
|
||||
|
||||
print_profile(#state{timings=T, status=Status, profile=true}=State) ->
|
||||
TopN = take(10, T),
|
||||
TopNTime = abs(lists:sum([ Time || {Time, _} <- TopN ])),
|
||||
TLG = dict:fetch([], Status),
|
||||
TotalTime = proplists:get_value(time, TLG),
|
||||
if TotalTime =/= undefined andalso TotalTime > 0 andalso TopN =/= [] ->
|
||||
TopNPct = (TopNTime / TotalTime) * 100,
|
||||
io:nl(), io:nl(),
|
||||
io:fwrite("Top ~p slowest tests (~ts, ~.1f% of total time):", [length(TopN), format_time(TopNTime), TopNPct]),
|
||||
lists:foreach(print_timing_fun(State), TopN),
|
||||
io:nl();
|
||||
true -> ok
|
||||
end;
|
||||
print_profile(#state{profile=false}) ->
|
||||
ok.
|
||||
|
||||
print_timing(#state{status=Status}) ->
|
||||
TLG = dict:fetch([], Status),
|
||||
Time = proplists:get_value(time, TLG),
|
||||
io:nl(),
|
||||
io:fwrite("Finished in ~ts~n", [format_time(Time)]),
|
||||
ok.
|
||||
|
||||
print_results(Data, State) ->
|
||||
Pass = proplists:get_value(pass, Data, 0),
|
||||
Fail = proplists:get_value(fail, Data, 0),
|
||||
Skip = proplists:get_value(skip, Data, 0),
|
||||
Cancel = proplists:get_value(cancel, Data, 0),
|
||||
Total = Pass + Fail + Skip + Cancel,
|
||||
{Color, Result} = if Fail > 0 -> {?RED, error};
|
||||
Skip > 0; Cancel > 0 -> {?YELLOW, error};
|
||||
Pass =:= 0 -> {?YELLOW, ok};
|
||||
true -> {?GREEN, ok}
|
||||
end,
|
||||
print_results(Color, Total, Fail, Skip, Cancel, State),
|
||||
sync_end(Result).
|
||||
|
||||
print_results(Color, 0, _, _, _, State) ->
|
||||
print_colored(Color, "0 tests\n", State);
|
||||
print_results(Color, Total, Fail, Skip, Cancel, State) ->
|
||||
SkipText = format_optional_result(Skip, "skipped"),
|
||||
CancelText = format_optional_result(Cancel, "cancelled"),
|
||||
Text = io_lib:format("~p tests, ~p failures~ts~ts~n", [Total, Fail, SkipText, CancelText]),
|
||||
print_colored(Text, Color, State).
|
||||
|
||||
print_timing_fun(#state{status=Status}=State) ->
|
||||
fun({Time, Key}) ->
|
||||
TestData = dict:fetch(Key, Status),
|
||||
TestId = format_test_identifier(TestData),
|
||||
io:nl(),
|
||||
io:fwrite(" ~ts~n", [TestId]),
|
||||
print_colored([" "|format_time(abs(Time))], ?CYAN, State)
|
||||
end.
|
||||
|
||||
%%------------------------------------------
|
||||
%% Print to the console with the given color
|
||||
%% if enabled.
|
||||
%%------------------------------------------
|
||||
print_colored(Text, Color, #state{colored=true}) ->
|
||||
io:fwrite("~s~ts~s", [Color, Text, ?RESET]);
|
||||
print_colored(Text, _Color, #state{colored=false}) ->
|
||||
io:fwrite("~ts", [Text]).
|
||||
|
||||
%%------------------------------------------
|
||||
%% Generic data formatters
|
||||
%%------------------------------------------
|
||||
format_function_name(M, F, A) ->
|
||||
io_lib:format("~ts:~ts/~p", [M, F, A]).
|
||||
|
||||
format_optional_result(0, _) ->
|
||||
[];
|
||||
format_optional_result(Count, Text) ->
|
||||
io_lib:format(", ~p ~ts", [Count, Text]).
|
||||
|
||||
format_test_identifier(Data) ->
|
||||
{Mod, Fun, Arity} = proplists:get_value(source, Data),
|
||||
Line = case proplists:get_value(line, Data) of
|
||||
0 -> "";
|
||||
L -> io_lib:format(":~p", [L])
|
||||
end,
|
||||
Desc = case proplists:get_value(desc, Data) of
|
||||
undefined -> "";
|
||||
DescText -> io_lib:format(": ~ts", [DescText])
|
||||
end,
|
||||
io_lib:format("~ts~ts~ts", [format_function_name(Mod, Fun, Arity), Line, Desc]).
|
||||
|
||||
format_time(undefined) ->
|
||||
"? seconds";
|
||||
format_time(Time) ->
|
||||
io_lib:format("~.3f seconds", [Time / 1000]).
|
||||
|
||||
format_pending_reason({module_not_found, M}) ->
|
||||
io_lib:format("Module '~ts' missing", [M]);
|
||||
format_pending_reason({no_such_function, {M,F,A}}) ->
|
||||
io_lib:format("Function ~ts undefined", [format_function_name(M,F,A)]);
|
||||
format_pending_reason({exit, Reason}) ->
|
||||
io_lib:format("Related process exited with reason: ~p", [Reason]);
|
||||
format_pending_reason(Reason) ->
|
||||
io_lib:format("Unknown error: ~p", [Reason]).
|
||||
|
||||
%% @doc Formats all the known eunit assertions, you're on your own if
|
||||
%% you make an assertion yourself.
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertion_failed
|
||||
; Type =:= assert ->
|
||||
Keys = proplists:get_keys(Props),
|
||||
HasEUnitProps = ([expression, value] -- Keys) =:= [],
|
||||
HasHamcrestProps = ([expected, actual, matcher] -- Keys) =:= [],
|
||||
if
|
||||
HasEUnitProps ->
|
||||
[indent(I, "Failure: ?assert(~ts)~n", [proplists:get_value(expression, Props)]),
|
||||
indent(I, " expected: true~n", []),
|
||||
case proplists:get_value(value, Props) of
|
||||
false ->
|
||||
indent(I, " got: false", []);
|
||||
{not_a_boolean, V} ->
|
||||
indent(I, " got: ~p", [V])
|
||||
end];
|
||||
HasHamcrestProps ->
|
||||
[indent(I, "Failure: ?assertThat(~p)~n", [proplists:get_value(matcher, Props)]),
|
||||
indent(I, " expected: ~p~n", [proplists:get_value(expected, Props)]),
|
||||
indent(I, " got: ~p", [proplists:get_value(actual, Props)])];
|
||||
true ->
|
||||
[indent(I, "Failure: unknown assert: ~p", [Props])]
|
||||
end;
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertMatch_failed
|
||||
; Type =:= assertMatch ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Pattern = proplists:get_value(pattern, Props),
|
||||
Value = proplists:get_value(value, Props),
|
||||
[indent(I, "Failure: ?assertMatch(~ts, ~ts)~n", [Pattern, Expr]),
|
||||
indent(I, " expected: = ~ts~n", [Pattern]),
|
||||
indent(I, " got: ~p", [Value])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertNotMatch_failed
|
||||
; Type =:= assertNotMatch ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Pattern = proplists:get_value(pattern, Props),
|
||||
Value = proplists:get_value(value, Props),
|
||||
[indent(I, "Failure: ?assertNotMatch(~ts, ~ts)~n", [Pattern, Expr]),
|
||||
indent(I, " expected not: = ~ts~n", [Pattern]),
|
||||
indent(I, " got: ~p", [Value])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertEqual_failed
|
||||
; Type =:= assertEqual ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Expected = proplists:get_value(expected, Props),
|
||||
Value = proplists:get_value(value, Props),
|
||||
[indent(I, "Failure: ?assertEqual(~w, ~ts)~n", [Expected,
|
||||
Expr]),
|
||||
indent(I, " expected: ~p~n", [Expected]),
|
||||
indent(I, " got: ~p", [Value])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertNotEqual_failed
|
||||
; Type =:= assertNotEqual ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Value = proplists:get_value(value, Props),
|
||||
[indent(I, "Failure: ?assertNotEqual(~p, ~ts)~n",
|
||||
[Value, Expr]),
|
||||
indent(I, " expected not: == ~p~n", [Value]),
|
||||
indent(I, " got: ~p", [Value])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertException_failed
|
||||
; Type =:= assertException ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Pattern = proplists:get_value(pattern, Props),
|
||||
{Class, Term} = extract_exception_pattern(Pattern), % I hate that we have to do this, why not just give DATA
|
||||
[indent(I, "Failure: ?assertException(~ts, ~ts, ~ts)~n", [Class, Term, Expr]),
|
||||
case proplists:is_defined(unexpected_success, Props) of
|
||||
true ->
|
||||
[indent(I, " expected: exception ~ts but nothing was raised~n", [Pattern]),
|
||||
indent(I, " got: value ~p", [proplists:get_value(unexpected_success, Props)])];
|
||||
false ->
|
||||
Ex = proplists:get_value(unexpected_exception, Props),
|
||||
[indent(I, " expected: exception ~ts~n", [Pattern]),
|
||||
indent(I, " got: exception ~p", [Ex])]
|
||||
end];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertNotException_failed
|
||||
; Type =:= assertNotException ->
|
||||
Expr = proplists:get_value(expression, Props),
|
||||
Pattern = proplists:get_value(pattern, Props),
|
||||
{Class, Term} = extract_exception_pattern(Pattern), % I hate that we have to do this, why not just give DAT
|
||||
Ex = proplists:get_value(unexpected_exception, Props),
|
||||
[indent(I, "Failure: ?assertNotException(~ts, ~ts, ~ts)~n", [Class, Term, Expr]),
|
||||
indent(I, " expected not: exception ~ts~n", [Pattern]),
|
||||
indent(I, " got: exception ~p", [Ex])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= command_failed
|
||||
; Type =:= command ->
|
||||
Cmd = proplists:get_value(command, Props),
|
||||
Expected = proplists:get_value(expected_status, Props),
|
||||
Status = proplists:get_value(status, Props),
|
||||
[indent(I, "Failure: ?cmdStatus(~p, ~p)~n", [Expected, Cmd]),
|
||||
indent(I, " expected: status ~p~n", [Expected]),
|
||||
indent(I, " got: status ~p", [Status])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertCmd_failed
|
||||
; Type =:= assertCmd ->
|
||||
Cmd = proplists:get_value(command, Props),
|
||||
Expected = proplists:get_value(expected_status, Props),
|
||||
Status = proplists:get_value(status, Props),
|
||||
[indent(I, "Failure: ?assertCmdStatus(~p, ~p)~n", [Expected, Cmd]),
|
||||
indent(I, " expected: status ~p~n", [Expected]),
|
||||
indent(I, " got: status ~p", [Status])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) when Type =:= assertCmdOutput_failed
|
||||
; Type =:= assertCmdOutput ->
|
||||
Cmd = proplists:get_value(command, Props),
|
||||
Expected = proplists:get_value(expected_output, Props),
|
||||
Output = proplists:get_value(output, Props),
|
||||
[indent(I, "Failure: ?assertCmdOutput(~p, ~p)~n", [Expected, Cmd]),
|
||||
indent(I, " expected: ~p~n", [Expected]),
|
||||
indent(I, " got: ~p", [Output])];
|
||||
|
||||
format_assertion_failure(Type, Props, I) ->
|
||||
indent(I, "~p", [{Type, Props}]).
|
||||
|
||||
indent(I, Fmt, Args) ->
|
||||
io_lib:format("~" ++ integer_to_list(I) ++ "s" ++ Fmt, [" "|Args]).
|
||||
|
||||
extract_exception_pattern(Str) ->
|
||||
["{", Class, Term|_] = re:split(Str, "[, ]{1,2}", [unicode,{return,list}]),
|
||||
{Class, Term}.
|
764
test/gleam/gleam_community_maths_float_test.gleam
Normal file
764
test/gleam/gleam_community_maths_float_test.gleam
Normal file
|
@ -0,0 +1,764 @@
|
|||
import gleam_community/maths/float as floatx
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/result
|
||||
import gleam/io
|
||||
import gleam/option
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
pub fn float_acos_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
assert Ok(result) = floatx.acos(1.0)
|
||||
result
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
assert Ok(result) = floatx.acos(0.5)
|
||||
result
|
||||
|> floatx.isclose(1.047197, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.acos(1.1)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.acos(-1.1)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_acosh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
assert Ok(result) = floatx.acosh(1.0)
|
||||
result
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.acosh(0.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_asin_test() {
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.asin(0.0)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
assert Ok(result) = floatx.asin(0.5)
|
||||
result
|
||||
|> floatx.isclose(0.523598, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.asin(1.1)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.asin(-1.1)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_asinh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.asinh(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.asinh(0.5)
|
||||
|> floatx.isclose(0.481211, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_atan_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.atan(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.atan(0.5)
|
||||
|> floatx.isclose(0.463647, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn math_atan2_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.atan2(0.0, 0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.atan2(0.0, 1.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check atan2(y=1.0, x=0.5)
|
||||
// Should be equal to atan(y / x) for any x > 0 and any y
|
||||
let result = floatx.atan(1.0 /. 0.5)
|
||||
floatx.atan2(1.0, 0.5)
|
||||
|> floatx.isclose(result, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check atan2(y=2.0, x=-1.5)
|
||||
// Should be equal to pi + atan(y / x) for any x < 0 and y >= 0
|
||||
let result = floatx.pi() +. floatx.atan(2.0 /. -1.5)
|
||||
floatx.atan2(2.0, -1.5)
|
||||
|> floatx.isclose(result, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check atan2(y=-2.0, x=-1.5)
|
||||
// Should be equal to atan(y / x) - pi for any x < 0 and y < 0
|
||||
let result = floatx.atan(-2.0 /. -1.5) -. floatx.pi()
|
||||
floatx.atan2(-2.0, -1.5)
|
||||
|> floatx.isclose(result, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check atan2(y=1.5, x=0.0)
|
||||
// Should be equal to pi/2 for x = 0 and any y > 0
|
||||
let result = floatx.pi() /. 2.0
|
||||
floatx.atan2(1.5, 0.0)
|
||||
|> floatx.isclose(result, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check atan2(y=-1.5, x=0.0)
|
||||
// Should be equal to -pi/2 for x = 0 and any y < 0
|
||||
let result = -1.0 *. floatx.pi() /. 2.0
|
||||
floatx.atan2(-1.5, 0.0)
|
||||
|> floatx.isclose(result, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_atanh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
assert Ok(result) = floatx.atanh(0.0)
|
||||
result
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
assert Ok(result) = floatx.atanh(0.5)
|
||||
result
|
||||
|> floatx.isclose(0.549306, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.atanh(1.0)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.atanh(2.0)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.atanh(1.0)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.atanh(-2.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_cos_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.cos(0.0)
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.cos(floatx.pi())
|
||||
|> floatx.isclose(-1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.cos(0.5)
|
||||
|> floatx.isclose(0.877582, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_cosh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.cosh(0.0)
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.cosh(0.5)
|
||||
|> floatx.isclose(1.127625, 0.0, tol)
|
||||
|> should.be_true()
|
||||
// An (overflow) error might occur when given an input
|
||||
// value that will result in a too large output value
|
||||
// e.g. floatx.cosh(1000.0) but this is a property of the
|
||||
// runtime.
|
||||
}
|
||||
|
||||
pub fn float_exp_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.exp(0.0)
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.exp(0.5)
|
||||
|> floatx.isclose(1.648721, 0.0, tol)
|
||||
|> should.be_true()
|
||||
// An (overflow) error might occur when given an input
|
||||
// value that will result in a too large output value
|
||||
// e.g. floatx.exp(1000.0) but this is a property of the
|
||||
// runtime.
|
||||
}
|
||||
|
||||
pub fn float_log_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.log(1.0)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
assert Ok(result) = floatx.log(0.5)
|
||||
result
|
||||
|> floatx.isclose(-0.693147, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.log(-1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn floatx_log10_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
assert Ok(result) = floatx.log10(1.0)
|
||||
result
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
assert Ok(result) = floatx.log10(10.0)
|
||||
result
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
assert Ok(result) = floatx.log10(50.0)
|
||||
result
|
||||
|> floatx.isclose(1.698970, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.log10(-1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn floatx_log2_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.log2(1.0)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
floatx.log2(2.0)
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
assert Ok(result) = floatx.log2(5.0)
|
||||
result
|
||||
|> floatx.isclose(2.321928, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.log2(-1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn floatx_logb_test() {
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.logb(10.0, 10.0)
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.logb(10.0, 100.0)
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
floatx.logb(1.0, 0.25)
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
// Check that we get an error when the function is evaluated
|
||||
// outside its domain
|
||||
floatx.logb(1.0, 1.0)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.logb(10.0, 1.0)
|
||||
|> should.be_error()
|
||||
|
||||
floatx.logb(-1.0, 1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_pow_test() {
|
||||
floatx.pow(2.0, 2.0)
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
floatx.pow(-5.0, 3.0)
|
||||
|> should.equal(Ok(-125.0))
|
||||
|
||||
floatx.pow(10.5, 0.0)
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.pow(16.0, 0.5)
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
floatx.pow(2.0, -1.0)
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
floatx.pow(2.0, -1.0)
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
// floatx.pow(-1.0, 0.5) is equivalent to float.square_root(-1.0)
|
||||
// and should return an error as an imaginary number would otherwise
|
||||
// have to be returned
|
||||
floatx.pow(-1.0, 0.5)
|
||||
|> should.be_error()
|
||||
|
||||
// Check another case with a negative base and fractional exponent
|
||||
floatx.pow(-1.5, 1.5)
|
||||
|> should.be_error()
|
||||
|
||||
// floatx.pow(0.0, -1.0) is equivalent to 1. /. 0 and is expected
|
||||
// to be an error
|
||||
floatx.pow(0.0, -1.0)
|
||||
|> should.be_error()
|
||||
|
||||
// Check that a negative base and exponent is fine as long as the
|
||||
// exponent is not fractional
|
||||
floatx.pow(-2.0, -1.0)
|
||||
|> should.equal(Ok(-0.5))
|
||||
}
|
||||
|
||||
pub fn float_sqrt_test() {
|
||||
floatx.sqrt(1.0)
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.sqrt(9.0)
|
||||
|> should.equal(Ok(3.0))
|
||||
|
||||
// An error should be returned as an imaginary number would otherwise
|
||||
// have to be returned
|
||||
floatx.sqrt(-1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_cbrt_test() {
|
||||
floatx.cbrt(1.0)
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.cbrt(27.0)
|
||||
|> should.equal(Ok(3.0))
|
||||
|
||||
// An error should be returned as an imaginary number would otherwise
|
||||
// have to be returned
|
||||
floatx.cbrt(-1.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn float_hypot_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
|
||||
floatx.hypot(0.0, 0.0)
|
||||
|> should.equal(0.0)
|
||||
|
||||
floatx.hypot(1.0, 0.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.hypot(0.0, 1.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
let result = floatx.hypot(11.0, 22.0)
|
||||
result
|
||||
|> floatx.isclose(24.596747, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_sin_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.sin(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.sin(0.5 *. floatx.pi())
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.sin(0.5)
|
||||
|> floatx.isclose(0.479425, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_sinh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.sinh(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.sinh(0.5)
|
||||
|> floatx.isclose(0.521095, 0.0, tol)
|
||||
|> should.be_true()
|
||||
// An (overflow) error might occur when given an input
|
||||
// value that will result in a too large output value
|
||||
// e.g. floatx.sinh(1000.0) but this is a property of the
|
||||
// runtime.
|
||||
}
|
||||
|
||||
pub fn math_tan_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.tan(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.tan(0.5)
|
||||
|> floatx.isclose(0.546302, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn math_tanh_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
floatx.tanh(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.tanh(25.0)
|
||||
|> floatx.isclose(1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.tanh(-25.0)
|
||||
|> floatx.isclose(-1.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.tanh(0.5)
|
||||
|> floatx.isclose(0.462117, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_rad2deg_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
floatx.rad2deg(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.rad2deg(2.0 *. floatx.pi())
|
||||
|> floatx.isclose(360.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_deg2rads_test() {
|
||||
assert Ok(tol) = floatx.pow(-10.0, -6.0)
|
||||
floatx.deg2rad(0.0)
|
||||
|> floatx.isclose(0.0, 0.0, tol)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.deg2rad(360.0)
|
||||
|> floatx.isclose(2.0 *. floatx.pi(), 0.0, tol)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_ceil_test() {
|
||||
floatx.ceil(0.1)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.ceil(0.9)
|
||||
|> should.equal(1.0)
|
||||
}
|
||||
|
||||
pub fn float_floor_test() {
|
||||
floatx.floor(0.1)
|
||||
|> should.equal(0.0)
|
||||
|
||||
floatx.floor(0.9)
|
||||
|> should.equal(0.0)
|
||||
}
|
||||
|
||||
pub fn float_min_test() {
|
||||
floatx.min(0.75, 0.5)
|
||||
|> should.equal(0.5)
|
||||
|
||||
floatx.min(0.5, 0.75)
|
||||
|> should.equal(0.5)
|
||||
|
||||
floatx.min(-0.75, 0.5)
|
||||
|> should.equal(-0.75)
|
||||
|
||||
floatx.min(-0.75, 0.5)
|
||||
|> should.equal(-0.75)
|
||||
}
|
||||
|
||||
pub fn float_max_test() {
|
||||
floatx.max(0.75, 0.5)
|
||||
|> should.equal(0.75)
|
||||
|
||||
floatx.max(0.5, 0.75)
|
||||
|> should.equal(0.75)
|
||||
|
||||
floatx.max(-0.75, 0.5)
|
||||
|> should.equal(0.5)
|
||||
|
||||
floatx.max(-0.75, 0.5)
|
||||
|> should.equal(0.5)
|
||||
}
|
||||
|
||||
pub fn float_minmax_test() {
|
||||
floatx.minmax(0.75, 0.5)
|
||||
|> should.equal(#(0.5, 0.75))
|
||||
|
||||
floatx.minmax(0.5, 0.75)
|
||||
|> should.equal(#(0.5, 0.75))
|
||||
|
||||
floatx.minmax(-0.75, 0.5)
|
||||
|> should.equal(#(-0.75, 0.5))
|
||||
|
||||
floatx.minmax(-0.75, 0.5)
|
||||
|> should.equal(#(-0.75, 0.5))
|
||||
}
|
||||
|
||||
pub fn float_sign_test() {
|
||||
floatx.sign(100.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.sign(0.0)
|
||||
|> should.equal(0.0)
|
||||
|
||||
floatx.sign(-100.0)
|
||||
|> should.equal(-1.0)
|
||||
}
|
||||
|
||||
pub fn float_flipsign_test() {
|
||||
floatx.flipsign(100.0)
|
||||
|> should.equal(-100.0)
|
||||
|
||||
floatx.flipsign(0.0)
|
||||
|> should.equal(-0.0)
|
||||
|
||||
floatx.flipsign(-100.0)
|
||||
|> should.equal(100.0)
|
||||
}
|
||||
|
||||
pub fn float_beta_test() {
|
||||
io.debug("TODO: Implement tests for 'floatx.beta'.")
|
||||
}
|
||||
|
||||
pub fn float_erf_test() {
|
||||
io.debug("TODO: Implement tests for 'floatx.erf'.")
|
||||
}
|
||||
|
||||
pub fn float_gamma_test() {
|
||||
io.debug("TODO: Implement tests for 'floatx.gamma'.")
|
||||
}
|
||||
|
||||
pub fn math_round_to_nearest_test() {
|
||||
floatx.round(1.50, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
floatx.round(1.75, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
floatx.round(2.00, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
floatx.round(3.50, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
floatx.round(4.50, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(4.0))
|
||||
|
||||
floatx.round(-3.50, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(-4.0))
|
||||
|
||||
floatx.round(-4.50, option.Some(0), option.Some("Nearest"))
|
||||
|> should.equal(Ok(-4.0))
|
||||
}
|
||||
|
||||
pub fn math_round_up_test() {
|
||||
floatx.round(0.45, option.Some(0), option.Some("Up"))
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.round(0.50, option.Some(0), option.Some("Up"))
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.round(0.45, option.Some(1), option.Some("Up"))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
floatx.round(0.50, option.Some(1), option.Some("Up"))
|
||||
|> should.equal(Ok(0.5))
|
||||
|
||||
floatx.round(0.455, option.Some(2), option.Some("Up"))
|
||||
|> should.equal(Ok(0.46))
|
||||
|
||||
floatx.round(0.505, option.Some(2), option.Some("Up"))
|
||||
|> should.equal(Ok(0.51))
|
||||
}
|
||||
|
||||
pub fn math_round_down_test() {
|
||||
floatx.round(0.45, option.Some(0), option.Some("Down"))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
floatx.round(0.50, option.Some(0), option.Some("Down"))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
floatx.round(0.45, option.Some(1), option.Some("Down"))
|
||||
|> should.equal(Ok(0.4))
|
||||
|
||||
floatx.round(0.50, option.Some(1), option.Some("Down"))
|
||||
|> should.equal(Ok(0.50))
|
||||
|
||||
floatx.round(0.4550, option.Some(2), option.Some("Down"))
|
||||
|> should.equal(Ok(0.45))
|
||||
|
||||
floatx.round(0.5050, option.Some(2), option.Some("Down"))
|
||||
|> should.equal(Ok(0.50))
|
||||
}
|
||||
|
||||
pub fn math_round_to_zero_test() {
|
||||
floatx.round(0.50, option.Some(0), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
floatx.round(0.75, option.Some(0), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.0))
|
||||
|
||||
floatx.round(0.45, option.Some(1), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.4))
|
||||
|
||||
floatx.round(0.57, option.Some(1), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.50))
|
||||
|
||||
floatx.round(0.4575, option.Some(2), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.45))
|
||||
|
||||
floatx.round(0.5075, option.Some(2), option.Some("ToZero"))
|
||||
|> should.equal(Ok(0.50))
|
||||
}
|
||||
|
||||
pub fn math_round_ties_away_test() {
|
||||
floatx.round(-1.40, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
floatx.round(-1.50, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
floatx.round(-2.00, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
floatx.round(-2.50, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(-3.0))
|
||||
|
||||
floatx.round(1.40, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.round(1.50, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
floatx.round(2.50, option.Some(0), option.Some("TiesAway"))
|
||||
|> should.equal(Ok(3.0))
|
||||
}
|
||||
|
||||
pub fn math_round_ties_up_test() {
|
||||
floatx.round(-1.40, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
floatx.round(-1.50, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(-1.0))
|
||||
|
||||
floatx.round(-2.00, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
floatx.round(-2.50, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(-2.0))
|
||||
|
||||
floatx.round(1.40, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(1.0))
|
||||
|
||||
floatx.round(1.50, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(2.0))
|
||||
|
||||
floatx.round(2.50, option.Some(0), option.Some("TiesUp"))
|
||||
|> should.equal(Ok(3.0))
|
||||
}
|
||||
|
||||
pub fn float_gammainc_test() {
|
||||
// Invalid input gives an error
|
||||
// 1st arg is invalid
|
||||
floatx.gammainc(-1.0, 1.0)
|
||||
|> should.be_error()
|
||||
|
||||
// 2nd arg is invalid
|
||||
floatx.gammainc(1.0, -1.0)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
floatx.gammainc(1.0, 0.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> floatx.isclose(0.0, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.gammainc(1.0, 2.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> floatx.isclose(0.864664716763387308106, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.gammainc(2.0, 3.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> floatx.isclose(0.8008517265285442280826, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
|
||||
floatx.gammainc(3.0, 4.0)
|
||||
|> result.unwrap(-999.0)
|
||||
|> floatx.isclose(1.523793388892911312363, 0.0, 0.01)
|
||||
|> should.be_true()
|
||||
}
|
||||
|
||||
pub fn float_absdiff_test() {
|
||||
floatx.absdiff(0.0, 0.0)
|
||||
|> should.equal(0.0)
|
||||
|
||||
floatx.absdiff(1.0, 2.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.absdiff(2.0, 1.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.absdiff(-1.0, 0.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.absdiff(0.0, -1.0)
|
||||
|> should.equal(1.0)
|
||||
|
||||
floatx.absdiff(10.0, 20.0)
|
||||
|> should.equal(10.0)
|
||||
|
||||
floatx.absdiff(-10.0, -20.0)
|
||||
|> should.equal(10.0)
|
||||
|
||||
floatx.absdiff(-10.5, 10.5)
|
||||
|> should.equal(21.0)
|
||||
}
|
157
test/gleam/gleam_community_maths_int_test.gleam
Normal file
157
test/gleam/gleam_community_maths_int_test.gleam
Normal file
|
@ -0,0 +1,157 @@
|
|||
import gleam_community/maths/int as intx
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import gleam/result
|
||||
import gleam/io
|
||||
|
||||
pub fn int_absdiff_test() {
|
||||
intx.absdiff(0, 0)
|
||||
|> should.equal(0)
|
||||
|
||||
intx.absdiff(1, 2)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absdiff(2, 1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absdiff(-1, 0)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absdiff(0, -1)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.absdiff(10, 20)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.absdiff(-10, -20)
|
||||
|> should.equal(10)
|
||||
|
||||
intx.absdiff(-10, 10)
|
||||
|> should.equal(20)
|
||||
}
|
||||
|
||||
pub fn int_factorial_test() {
|
||||
// Invalid input gives an error
|
||||
intx.factorial(-1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.factorial(0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.factorial(1)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.factorial(2)
|
||||
|> should.equal(Ok(2))
|
||||
|
||||
intx.factorial(3)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
intx.factorial(4)
|
||||
|> should.equal(Ok(24))
|
||||
}
|
||||
|
||||
pub fn int_combination_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
intx.combination(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.combination(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.combination(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.combination(4, 2)
|
||||
|> should.equal(Ok(6))
|
||||
|
||||
intx.combination(7, 5)
|
||||
|> should.equal(Ok(21))
|
||||
// NOTE: Tests with the 'combination' function that produce values that
|
||||
// exceed precision of the JavaScript 'Number' primitive will result in
|
||||
// errors
|
||||
}
|
||||
|
||||
pub fn math_permutation_test() {
|
||||
// Invalid input gives an error
|
||||
// Error on: n = -1 < 0
|
||||
intx.permutation(-1, 1)
|
||||
|> should.be_error()
|
||||
|
||||
// Valid input returns a result
|
||||
intx.permutation(4, 0)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.permutation(4, 4)
|
||||
|> should.equal(Ok(1))
|
||||
|
||||
intx.permutation(4, 2)
|
||||
|> should.equal(Ok(12))
|
||||
}
|
||||
|
||||
pub fn float_min_test() {
|
||||
intx.min(75, 50)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.min(50, 75)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.min(-75, 50)
|
||||
|> should.equal(-75)
|
||||
|
||||
intx.min(-75, 50)
|
||||
|> should.equal(-75)
|
||||
}
|
||||
|
||||
pub fn float_max_test() {
|
||||
intx.max(75, 50)
|
||||
|> should.equal(75)
|
||||
|
||||
intx.max(50, 75)
|
||||
|> should.equal(75)
|
||||
|
||||
intx.max(-75, 50)
|
||||
|> should.equal(50)
|
||||
|
||||
intx.max(-75, 50)
|
||||
|> should.equal(50)
|
||||
}
|
||||
|
||||
pub fn int_minmax_test() {
|
||||
intx.minmax(75, 50)
|
||||
|> should.equal(#(50, 75))
|
||||
|
||||
intx.minmax(50, 75)
|
||||
|> should.equal(#(50, 75))
|
||||
|
||||
intx.minmax(-75, 50)
|
||||
|> should.equal(#(-75, 50))
|
||||
|
||||
intx.minmax(-75, 50)
|
||||
|> should.equal(#(-75, 50))
|
||||
}
|
||||
|
||||
pub fn int_sign_test() {
|
||||
intx.sign(100)
|
||||
|> should.equal(1)
|
||||
|
||||
intx.sign(0)
|
||||
|> should.equal(0)
|
||||
|
||||
intx.sign(-100)
|
||||
|> should.equal(-1)
|
||||
}
|
||||
|
||||
pub fn int_flipsign_test() {
|
||||
intx.flipsign(100)
|
||||
|> should.equal(-100)
|
||||
|
||||
intx.flipsign(0)
|
||||
|> should.equal(-0)
|
||||
|
||||
intx.flipsign(-100)
|
||||
|> should.equal(100)
|
||||
}
|
9
test/gleam_community_maths_test.gleam
Normal file
9
test/gleam_community_maths_test.gleam
Normal file
|
@ -0,0 +1,9 @@
|
|||
if erlang {
|
||||
pub external fn main() -> Nil =
|
||||
"gleam_community_maths_test_ffi" "main"
|
||||
}
|
||||
|
||||
if javascript {
|
||||
pub external fn main() -> Nil =
|
||||
"./gleam_community_maths_test_ffi.mjs" "main"
|
||||
}
|
40
test/gleam_community_maths_test_ffi.erl
Normal file
40
test/gleam_community_maths_test_ffi.erl
Normal file
|
@ -0,0 +1,40 @@
|
|||
-module(gleam_community_maths_test_ffi).
|
||||
|
||||
-export([
|
||||
main/0, should_equal/2, should_not_equal/2, should_be_ok/1,
|
||||
should_be_error/1
|
||||
]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
main() ->
|
||||
Options = [
|
||||
no_tty, {report, {eunit_progress, [colored]}}
|
||||
],
|
||||
Files = filelib:wildcard("test/**/*.{erl,gleam}"),
|
||||
Modules = lists:map(fun filepath_to_module/1, Files),
|
||||
case eunit:test(Modules, Options) of
|
||||
ok -> erlang:halt(0);
|
||||
_ -> erlang:halt(1)
|
||||
end.
|
||||
|
||||
filepath_to_module(Path0) ->
|
||||
Path1 = string:replace(Path0, "test/", ""),
|
||||
Path2 = string:replace(Path1, ".erl", ""),
|
||||
Path3 = string:replace(Path2, ".gleam", ""),
|
||||
Path4 = string:replace(Path3, "/", "@", all),
|
||||
Path5 = list_to_binary(Path4),
|
||||
binary_to_atom(Path5).
|
||||
|
||||
should_equal(Actual, Expected) ->
|
||||
?assertEqual(Expected, Actual),
|
||||
nil.
|
||||
should_not_equal(Actual, Expected) ->
|
||||
?assertNotEqual(Expected, Actual),
|
||||
nil.
|
||||
should_be_ok(A) ->
|
||||
?assertMatch({ok, _}, A),
|
||||
nil.
|
||||
should_be_error(A) ->
|
||||
?assertMatch({error, _}, A),
|
||||
nil.
|
35
test/gleam_community_maths_test_ffi.mjs
Executable file
35
test/gleam_community_maths_test_ffi.mjs
Executable file
|
@ -0,0 +1,35 @@
|
|||
import { opendir } from "fs/promises";
|
||||
|
||||
const dir = "build/dev/javascript/gleam_community_maths/dist/gleam/";
|
||||
|
||||
export async function main() {
|
||||
console.log("Running tests...");
|
||||
|
||||
let passes = 0;
|
||||
let failures = 0;
|
||||
|
||||
for await (let entry of await opendir(dir)) {
|
||||
if (!entry.name.endsWith("_test.mjs")) continue;
|
||||
let module = await import("./gleam/" + entry.name);
|
||||
|
||||
for (let fnName of Object.keys(module)) {
|
||||
if (!fnName.endsWith("_test")) continue;
|
||||
try {
|
||||
module[fnName]();
|
||||
process.stdout.write(`\u001b[32m.\u001b[0m`);
|
||||
passes++;
|
||||
} catch (error) {
|
||||
let moduleName = "\ngleam/" + entry.name.slice(0, -3);
|
||||
process.stdout.write(`\n❌ ${moduleName}.${fnName}: ${error}\n`);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`
|
||||
|
||||
${passes + failures} tests
|
||||
${passes} passes
|
||||
${failures} failures`);
|
||||
process.exit(failures ? 1 : 0);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import gleeunit
|
||||
import gleeunit/should
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
// gleeunit test functions end in `_test`
|
||||
pub fn hello_world_test() {
|
||||
1
|
||||
|> should.equal(1)
|
||||
}
|
Loading…
Reference in a new issue