mirror of
https://github.com/sigmasternchen/gleam-community-maths
synced 2025-03-15 07:59:01 +00:00
Remove duplicate function. Improve function input validation + tests
This commit is contained in:
parent
3b7629bd3e
commit
803abd516e
2 changed files with 135 additions and 146 deletions
|
@ -5419,6 +5419,11 @@ pub fn linear_space(
|
|||
/// interval. The endpoint of the interval can optionally be included/excluded. The number of
|
||||
/// points, base, and whether the endpoint is included determine the spacing between values.
|
||||
///
|
||||
/// The values in the sequence are computed as powers of the given base, where the exponents are
|
||||
/// evenly spaced between `start` and `stop`. The `base` parameter must be positive, as negative
|
||||
/// bases lead to undefined behavior when computing fractional exponents. Similarly, the number of
|
||||
/// points (`steps`) must be positive; specifying zero or a negative value will result in an error.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
|
@ -5454,7 +5459,7 @@ pub fn logarithmic_space(
|
|||
endpoint: Bool,
|
||||
base: Float,
|
||||
) -> Result(Yielder(Float), Nil) {
|
||||
case steps > 0 {
|
||||
case steps > 0 && base >=. 0.0 {
|
||||
True -> {
|
||||
let assert Ok(linspace) = linear_space(start, stop, steps, endpoint)
|
||||
|
||||
|
@ -5475,10 +5480,19 @@ pub fn logarithmic_space(
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The function returns an iterator of numbers spaced evenly on a log scale (a geometric
|
||||
/// progression). Each point in the list is a constant multiple of the previous. The function is
|
||||
/// similar to the [`logarithmic_space`](#logarithmic_space) function, but with endpoints
|
||||
/// specified directly.
|
||||
/// The function returns an iterator for generating a geometric progression between two specified
|
||||
/// values, where each value is a constant multiple of the previous one. Unlike
|
||||
/// [`logarithmic_space`](#logarithmic_space), this function allows specifying the starting
|
||||
/// and ending values (`start` and `stop`) directly, without requiring them to be transformed
|
||||
/// into exponents.
|
||||
///
|
||||
/// Internally, the function computes the logarithms of `start` and `stop` and generates evenly
|
||||
/// spaced points in the logarithmic domain (using base 10). These points are then transformed back
|
||||
/// into their original scale to create a sequence of values that grow multiplicatively.
|
||||
///
|
||||
/// The `start` and `stop` values must be positive, as logarithms are undefined for non-positive
|
||||
/// values. The number of points (`steps`) must also be positive; specifying zero or a negative
|
||||
/// value will result in an error.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
|
@ -5496,7 +5510,7 @@ pub fn logarithmic_space(
|
|||
/// |> list.all(fn(x) { x == True })
|
||||
/// |> should.be_true()
|
||||
///
|
||||
/// // Input (start and stop can't be equal to 0.0)
|
||||
/// // Input (start and stop can't be less than or equal to 0.0)
|
||||
/// maths.geometric_space(0.0, 1000.0, 3, False)
|
||||
/// |> should.be_error()
|
||||
///
|
||||
|
@ -5521,75 +5535,13 @@ pub fn geometric_space(
|
|||
steps: Int,
|
||||
endpoint: Bool,
|
||||
) -> Result(Yielder(Float), Nil) {
|
||||
case start == 0.0 || stop == 0.0 {
|
||||
case start <=. 0.0 || stop <=. 0.0 || steps < 0 {
|
||||
True -> Error(Nil)
|
||||
False ->
|
||||
case steps > 0 {
|
||||
True -> {
|
||||
let assert Ok(log_start) = logarithm_10(start)
|
||||
let assert Ok(log_stop) = logarithm_10(stop)
|
||||
logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
|
||||
}
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="https://github.com/gleam-community/maths/issues">
|
||||
/// <small>Spot a typo? Open an issue!</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// The function returns an iterator of exponentially spaced points over a specified interval. The
|
||||
/// endpoint of the interval can optionally be included/excluded. The number of points and whether
|
||||
/// the endpoint is included determine the spacing between values. The sequence is generated by
|
||||
/// computing intermediate values in a logarithmic domain and transforming them into the exponential
|
||||
/// domain.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
///
|
||||
/// import gleam/yielder
|
||||
/// import gleeunit/should
|
||||
/// import gleam_community/maths
|
||||
///
|
||||
/// pub fn example() {
|
||||
/// let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||
/// let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
|
||||
/// let expected = [1.0, 10.0, 100.0, 1000.0]
|
||||
/// let pairs = exp_space |> yielder.to_list() |> list.zip(expected)
|
||||
/// let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
|
||||
/// result |> list.all(fn(x) { x == True }) |> should.be_true()
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
/// <div style="text-align: right;">
|
||||
/// <a href="#">
|
||||
/// <small>Back to top ↑</small>
|
||||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
pub fn exponential_space(
|
||||
start: Float,
|
||||
stop: Float,
|
||||
steps: Int,
|
||||
endpoint: Bool,
|
||||
) -> Result(Yielder(Float), Nil) {
|
||||
case steps > 0 {
|
||||
True -> {
|
||||
False -> {
|
||||
let assert Ok(log_start) = logarithm_10(start)
|
||||
let assert Ok(log_stop) = logarithm_10(stop)
|
||||
let assert Ok(log_space) =
|
||||
linear_space(log_start, log_stop, steps, endpoint)
|
||||
Ok(
|
||||
yielder.map(log_space, fn(value) {
|
||||
let assert Ok(exp_value) = float.power(10.0, value)
|
||||
exp_value
|
||||
}),
|
||||
)
|
||||
logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
|
||||
}
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5599,8 +5551,8 @@ pub fn exponential_space(
|
|||
/// </a>
|
||||
/// </div>
|
||||
///
|
||||
/// Generates evenly spaced points around a center value. The total span is determined by
|
||||
/// the radius argument of the function.
|
||||
/// Generates evenly spaced points around a center value. The total span (around the center value)
|
||||
/// is determined by the `radius` argument of the function.
|
||||
///
|
||||
/// <details>
|
||||
/// <summary>Example:</summary>
|
||||
|
@ -5614,6 +5566,12 @@ pub fn exponential_space(
|
|||
/// sym_space
|
||||
/// |> yielder.to_list()
|
||||
/// |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
|
||||
///
|
||||
/// // A negative radius reverses the order of the values
|
||||
/// let assert Ok(sym_space) = maths.symmetric_space(0.0, -5.0, 5)
|
||||
/// sym_space
|
||||
/// |> yielder.to_list()
|
||||
/// |> should.equal([5.0, 2.5, 0.0, -2.5, -5.0])
|
||||
/// }
|
||||
/// </details>
|
||||
///
|
||||
|
|
|
@ -28,7 +28,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -42,7 +41,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -54,7 +52,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -69,7 +66,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -83,7 +79,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -96,7 +91,6 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -108,7 +102,31 @@ pub fn list_linear_space_test() {
|
|||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// Check that when start == stop and steps > 0, then
|
||||
// the value start/stop value is just repeated, since the
|
||||
// step increment will be 0
|
||||
let assert Ok(linspace) = maths.linear_space(10.0, 10.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
linspace |> yielder.to_list() |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(linspace) = maths.linear_space(10.0, 10.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
linspace |> yielder.to_list() |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
@ -123,7 +141,7 @@ pub fn list_logarithmic_space_test() {
|
|||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
// ---> With endpoint included
|
||||
// - Positive start, stop, base
|
||||
// - Positive start, stop
|
||||
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
|
@ -135,31 +153,7 @@ pub fn list_logarithmic_space_test() {
|
|||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// - Positive start, stop, negative base
|
||||
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace |> yielder.to_list() |> list.zip([-10.0, 100.0, -1000.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// - Positive start, negative stop, base
|
||||
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, -10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace |> yielder.to_list() |> list.zip([-10.0, -0.1, -0.001]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// - Positive start, base, negative stop
|
||||
// - Positive start, negative stop
|
||||
let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
|
@ -171,7 +165,7 @@ pub fn list_logarithmic_space_test() {
|
|||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// - Positive stop, base, negative start
|
||||
// - Positive stop, negative start
|
||||
let assert Ok(logspace) = maths.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
|
@ -184,7 +178,7 @@ pub fn list_logarithmic_space_test() {
|
|||
|> should.be_true()
|
||||
|
||||
// ----> Without endpoint included
|
||||
// - Positive start, stop, base
|
||||
// - Positive start, stop
|
||||
let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, False, 10.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
|
@ -198,9 +192,41 @@ pub fn list_logarithmic_space_test() {
|
|||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// Check that when start == stop and steps > 0, then
|
||||
// the value start/stop value is just repeated, since the
|
||||
// step increment will be 0
|
||||
let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, True, 5.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace
|
||||
|> yielder.to_list()
|
||||
|> list.zip([3125.0, 3125.0, 3125.0, 3125.0, 3125.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, False, 5.0)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace
|
||||
|> yielder.to_list()
|
||||
|> list.zip([3125.0, 3125.0, 3125.0, 3125.0, 3125.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// A negative number of points does not work (-3)
|
||||
maths.logarithmic_space(1.0, 3.0, -3, True, 10.0)
|
||||
|> should.be_error()
|
||||
|
||||
// A negative base does not work (-10)
|
||||
maths.logarithmic_space(1.0, 3.0, 3, True, -10.0)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn list_geometric_space_test() {
|
||||
|
@ -259,7 +285,36 @@ pub fn list_geometric_space_test() {
|
|||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// Test invalid input (start and stop can't be equal to 0.0)
|
||||
// Check that when start == stop and steps > 0, then
|
||||
// the value start/stop value is just repeated, since the
|
||||
// step increment will be 0
|
||||
let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, True)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace
|
||||
|> yielder.to_list()
|
||||
|> list.zip([5.0, 5.0, 5.0, 5.0, 5.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, False)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
logspace
|
||||
|> yielder.to_list()
|
||||
|> list.zip([5.0, 5.0, 5.0, 5.0, 5.0]),
|
||||
0.0,
|
||||
tol,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// Test invalid input (start and stop can't be less than or equal to 0.0)
|
||||
maths.geometric_space(0.0, 1000.0, 3, False)
|
||||
|> should.be_error()
|
||||
|
||||
|
@ -311,44 +366,6 @@ pub fn list_arange_test() {
|
|||
|> should.equal([-5.0, -4.0, -3.0, -2.0])
|
||||
}
|
||||
|
||||
pub fn list_exponential_space_test() {
|
||||
let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||
|
||||
// Check that the function agrees, at some arbitrary input
|
||||
// points, with known function values
|
||||
// ---> With endpoint included
|
||||
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, True)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
exp_space |> yielder.to_list() |> list.zip([1.0, 10.0, 100.0, 1000.0]),
|
||||
0.0,
|
||||
tolerance,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// ---> Without endpoint included
|
||||
let assert Ok(exp_space) = maths.exponential_space(1.0, 1000.0, 4, False)
|
||||
let assert Ok(result) =
|
||||
maths.all_close(
|
||||
exp_space
|
||||
|> yielder.to_list()
|
||||
|> list.zip([
|
||||
1.0, 5.623413251903491, 31.622776601683793, 177.82794100389228,
|
||||
]),
|
||||
0.0,
|
||||
tolerance,
|
||||
)
|
||||
result
|
||||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// A negative number of points does not work (-3)
|
||||
maths.exponential_space(1.0, 1000.0, -3, True)
|
||||
|> should.be_error()
|
||||
}
|
||||
|
||||
pub fn list_symmetric_space_test() {
|
||||
let assert Ok(tolerance) = float.power(10.0, -6.0)
|
||||
|
||||
|
@ -370,6 +387,12 @@ pub fn list_symmetric_space_test() {
|
|||
|> yielder.to_list()
|
||||
|> should.equal([-15.0, -12.5, -10.0, -7.5, -5.0])
|
||||
|
||||
// Negative Radius (simply reverses the order of the values)
|
||||
let assert Ok(sym_space) = maths.symmetric_space(0.0, -5.0, 5)
|
||||
sym_space
|
||||
|> yielder.to_list()
|
||||
|> should.equal([5.0, 2.5, 0.0, -2.5, -5.0])
|
||||
|
||||
// Uneven number of points
|
||||
let assert Ok(sym_space) = maths.symmetric_space(0.0, 2.0, 4)
|
||||
let assert Ok(result) =
|
||||
|
@ -384,6 +407,14 @@ pub fn list_symmetric_space_test() {
|
|||
|> list.all(fn(x) { x == True })
|
||||
|> should.be_true()
|
||||
|
||||
// Check that when radius == 0 and steps > 0, then
|
||||
// the value center value is just repeated, since the
|
||||
// step increment will be 0
|
||||
let assert Ok(sym_space) = maths.symmetric_space(10.0, 0.0, 4)
|
||||
sym_space
|
||||
|> yielder.to_list()
|
||||
|> should.equal([10.0, 10.0, 10.0, 10.0])
|
||||
|
||||
// A negative number of points does not work (-5)
|
||||
maths.symmetric_space(0.0, 5.0, -5)
|
||||
|> should.be_error()
|
||||
|
|
Loading…
Reference in a new issue