diff --git a/src/gleam_community/maths.gleam b/src/gleam_community/maths.gleam
index 1e95fbd..2fcfc1c 100644
--- a/src/gleam_community/maths.gleam
+++ b/src/gleam_community/maths.gleam
@@ -21,8 +21,6 @@
//// .katex { font-size: 1.10em; }
////
////
-//// ---
-////
import gleam/bool
import gleam/float
@@ -5278,7 +5276,7 @@ fn incomplete_gamma_sum(
///
///
///
-/// The function returns an iterator generating evenly spaced values within a specified interval
+/// The function returns a list of evenly spaced values within a specified interval
/// `[start, stop)` based on a given increment size.
///
/// Note that if `increment > 0`, the sequence progresses from `start` towards `stop`, while if
@@ -5287,25 +5285,21 @@ fn incomplete_gamma_sum(
///
/// Example:
///
-/// import gleam/yielder
/// import gleeunit/should
/// import gleam_community/maths
///
/// pub fn example () {
-/// maths.arange(1.0, 5.0, 1.0)
-/// |> yielder.to_list()
+/// maths.step_range(1.0, 5.0, 1.0)
/// |> should.equal([1.0, 2.0, 3.0, 4.0])
-///
+///
/// // No points returned since
/// // start is smaller than stop and the step is positive
-/// maths.arange(5.0, 1.0, 1.0)
-/// |> yielder.to_list()
+/// maths.step_range(5.0, 1.0, 1.0)
/// |> should.equal([])
-///
+///
/// // Points returned since
/// // start smaller than stop but negative step
-/// maths.arange(5.0, 1.0, -1.0)
-/// |> yielder.to_list()
+/// maths.step_range(5.0, 1.0, -1.0)
/// |> should.equal([5.0, 4.0, 3.0, 2.0])
/// }
///
@@ -5316,7 +5310,91 @@ fn incomplete_gamma_sum(
///
///
///
-pub fn arange(start: Float, stop: Float, increment: Float) -> Yielder(Float) {
+pub fn step_range(start: Float, stop: Float, increment: Float) -> List(Float) {
+ case
+ { start >=. stop && increment >. 0.0 }
+ || { start <=. stop && increment <. 0.0 }
+ {
+ True -> []
+ False -> {
+ let direction = case start <=. stop {
+ True -> 1.0
+ False -> -1.0
+ }
+
+ let increment_abs = float.absolute_value(increment)
+ let distance = float.absolute_value(start -. stop)
+ let steps = float.round(distance /. increment_abs)
+ let adjusted_stop = stop -. increment_abs *. direction
+
+ // Generate the sequence from 'adjusted_stop' towards 'start'
+ do_step_range(adjusted_stop, increment_abs *. direction, steps, [])
+ }
+ }
+}
+
+fn do_step_range(
+ current: Float,
+ increment: Float,
+ remaining_steps: Int,
+ acc: List(Float),
+) -> List(Float) {
+ case remaining_steps {
+ 0 -> acc
+ _ ->
+ do_step_range(current -. increment, increment, remaining_steps - 1, [
+ current,
+ ..acc
+ ])
+ }
+}
+
+///
+///
+/// The function is similar to [`step_range`](#step_range) but instead returns a yielder
+/// (lazily evaluated sequence of elements). This function can be used whenever there is a need
+/// to generate a larger-than-usual sequence of elements.
+///
+///
+/// Example:
+///
+/// import gleam/yielder.{Next, Done}
+/// import gleeunit/should
+/// import gleam_community/maths
+///
+/// pub fn example () {
+/// let range = maths.yield_step_range(1.0, 2.5, 0.5)
+///
+/// let assert Next(element, rest) = yielder.step(range)
+/// should.equal(element, 1.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 1.5)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 2.0)
+///
+/// // We have generated 3 values over the interval [1.0, 2.5)
+/// // in increments of 0.5, so the 4th will be 'Done'
+/// should.equal(yielder.step(rest), Done)
+/// }
+///
+///
+///
+///
+pub fn yield_step_range(
+ start: Float,
+ stop: Float,
+ increment: Float,
+) -> Yielder(Float) {
// Check if the range would be empty due to direction and increment
case
{ start >=. stop && increment >. 0.0 }
@@ -5345,22 +5423,20 @@ pub fn arange(start: Float, stop: Float, increment: Float) -> Yielder(Float) {
///
///
///
-/// The function returns an iterator for generating linearly spaced points over a specified
+/// The function returns a list of linearly 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.
///
///
/// Example:
///
-/// 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(linspace) = maths.linear_space(10.0, 20.0, 5, True)
-/// let pairs =
-/// linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0])
+/// let pairs = linspace |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0])
/// let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
/// result
/// |> list.all(fn(x) { x == True })
@@ -5383,6 +5459,98 @@ pub fn linear_space(
stop: Float,
steps: Int,
endpoint: Bool,
+) -> Result(List(Float), Nil) {
+ let direction = case start <=. stop {
+ True -> 1.0
+ False -> -1.0
+ }
+
+ let increment_abs = case endpoint {
+ True -> float.absolute_value(start -. stop) /. int.to_float(steps - 1)
+ False -> float.absolute_value(start -. stop) /. int.to_float(steps)
+ }
+
+ let adjusted_stop = case endpoint {
+ True -> stop
+ False -> stop -. increment_abs *. direction
+ }
+
+ // Generate the sequence from 'adjusted_stop' towards 'start'
+ case steps > 0 {
+ True -> {
+ Ok(do_linear_space(adjusted_stop, increment_abs *. direction, steps, []))
+ }
+ False -> Error(Nil)
+ }
+}
+
+fn do_linear_space(
+ current: Float,
+ increment: Float,
+ remaining_steps: Int,
+ acc: List(Float),
+) -> List(Float) {
+ case remaining_steps {
+ 0 -> acc
+ _ ->
+ do_linear_space(current -. increment, increment, remaining_steps - 1, [
+ current,
+ ..acc
+ ])
+ }
+}
+
+///
+///
+/// The function is similar to [`linear_space`](#linear_space) but instead returns a yielder
+/// (lazily evaluated sequence of elements). This function can be used whenever there is a need
+/// to generate a larger-than-usual sequence of elements.
+///
+///
+/// Example:
+///
+/// import gleam/yielder.{Next, Done}
+/// import gleeunit/should
+/// import gleam_community/maths
+///
+/// pub fn example () {
+/// let assert Ok(linspace) = maths.yield_linear_space(10.0, 20.0, 5, True)
+///
+/// let assert Next(element, rest) = yielder.step(linspace)
+/// should.equal(element, 10.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 12.5)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 15.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 17.5)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 20.0)
+///
+/// // We have generated 5 values, so the 6th will be 'Done'
+/// should.equal(yielder.step(rest), Done)
+/// }
+///
+///
+///
+///
+pub fn yield_linear_space(
+ start: Float,
+ stop: Float,
+ steps: Int,
+ endpoint: Bool,
) -> Result(Yielder(Float), Nil) {
let direction = case start <=. stop {
True -> 1.0
@@ -5415,7 +5583,7 @@ pub fn linear_space(
///
///
///
-/// The function returns an iterator for generating logarithmically spaced points over a specified
+/// The function returns a list of logarithmically spaced points over a specified
/// 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.
///
@@ -5427,14 +5595,13 @@ pub fn linear_space(
///
/// Example:
///
-/// 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(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
-/// let pairs = logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0])
+/// let pairs = logspace |> list.zip([10.0, 100.0, 1000.0])
/// let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
/// result
/// |> list.all(fn(x) { x == True })
@@ -5458,11 +5625,74 @@ pub fn logarithmic_space(
steps: Int,
endpoint: Bool,
base: Float,
-) -> Result(Yielder(Float), Nil) {
+) -> Result(List(Float), Nil) {
case steps > 0 && base >=. 0.0 {
True -> {
let assert Ok(linspace) = linear_space(start, stop, steps, endpoint)
+ Ok(
+ list.map(linspace, fn(value) {
+ let assert Ok(result) = float.power(base, value)
+ result
+ }),
+ )
+ }
+ False -> Error(Nil)
+ }
+}
+
+///
+///
+/// The function is similar to [`logarithmic_space`](#logarithmic_space) but instead returns a yielder
+/// (lazily evaluated sequence of elements). This function can be used whenever there is a need
+/// to generate a larger-than-usual sequence of elements.
+///
+///
+/// Example:
+///
+/// import gleam/yielder.{Next, Done}
+/// import gleeunit/should
+/// import gleam_community/maths
+///
+/// pub fn example () {
+/// let assert Ok(logspace) =
+/// maths.yield_logarithmic_space(1.0, 3.0, 3, True, 10.0)
+///
+/// let assert Next(element, rest) = yielder.step(logspace)
+/// should.equal(element, 10.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 100.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 1000.0)
+///
+/// // We have generated 3 values, so the 4th will be 'Done'
+/// should.equal(yielder.step(rest), Done)
+/// }
+///
+///
+///
+///
+pub fn yield_logarithmic_space(
+ start: Float,
+ stop: Float,
+ steps: Int,
+ endpoint: Bool,
+ base: Float,
+) -> Result(Yielder(Float), Nil) {
+ case steps > 0 && base >=. 0.0 {
+ True -> {
+ let assert Ok(linspace) = yield_linear_space(start, stop, steps, endpoint)
+
Ok(
yielder.map(linspace, fn(value) {
let assert Ok(result) = float.power(base, value)
@@ -5480,7 +5710,7 @@ pub fn logarithmic_space(
///
///
///
-/// The function returns an iterator for generating a geometric progression between two specified
+/// The function returns a list of 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
@@ -5504,7 +5734,7 @@ pub fn logarithmic_space(
/// pub fn example () {
/// let assert Ok(tolerance) = float.power(10.0, -6.0)
/// let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
-/// let pairs = logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0])
+/// let pairs = logspace |> list.zip([10.0, 100.0, 1000.0])
/// let assert Ok(result) = maths.all_close(pairs, 0.0, tolerance)
/// result
/// |> list.all(fn(x) { x == True })
@@ -5534,7 +5764,7 @@ pub fn geometric_space(
stop: Float,
steps: Int,
endpoint: Bool,
-) -> Result(Yielder(Float), Nil) {
+) -> Result(List(Float), Nil) {
case start <=. 0.0 || stop <=. 0.0 || steps < 0 {
True -> Error(Nil)
False -> {
@@ -5545,6 +5775,62 @@ pub fn geometric_space(
}
}
+///
+///
+/// The function is similar to [`geometric_space`](#geometric_space) but instead returns a yielder
+/// (lazily evaluated sequence of elements). This function can be used whenever there is a need
+/// to generate a larger-than-usual sequence of elements.
+///
+///
+/// Example:
+///
+/// import gleam/yielder.{Next, Done}
+/// import gleeunit/should
+/// import gleam_community/maths
+///
+/// pub fn example () {
+/// let assert Ok(logspace) = maths.yield_geometric_space(10.0, 1000.0, 3, True)
+///
+/// let assert Next(element, rest) = yielder.step(logspace)
+/// should.equal(element, 10.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 100.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 1000.0)
+///
+/// // We have generated 3 values, so the 4th will be 'Done'
+/// should.equal(yielder.step(rest), Done)
+/// }
+///
+///
+///
+///
+pub fn yield_geometric_space(
+ start: Float,
+ stop: Float,
+ steps: Int,
+ endpoint: Bool,
+) -> Result(Yielder(Float), Nil) {
+ case start <=. 0.0 || stop <=. 0.0 || steps < 0 {
+ True -> Error(Nil)
+ False -> {
+ let assert Ok(log_start) = logarithm_10(start)
+ let assert Ok(log_stop) = logarithm_10(stop)
+ yield_logarithmic_space(log_start, log_stop, steps, endpoint, 10.0)
+ }
+ }
+}
+
///
///
/// Spot a typo? Open an issue!
@@ -5557,20 +5843,17 @@ pub fn geometric_space(
///
/// Example:
///
-/// import gleam/yielder
/// import gleeunit/should
/// import gleam_community/maths
///
/// pub fn example() {
-/// let assert Ok(sym_space) = maths.symmetric_space(0.0, 5.0, 5)
-/// sym_space
-/// |> yielder.to_list()
+/// let assert Ok(symspace) = maths.symmetric_space(0.0, 5.0, 5)
+/// symspace
/// |> 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()
+/// let assert Ok(symspace) = maths.symmetric_space(0.0, -5.0, 5)
+/// symspace
/// |> should.equal([5.0, 2.5, 0.0, -2.5, -5.0])
/// }
///
@@ -5585,7 +5868,7 @@ pub fn symmetric_space(
center: Float,
radius: Float,
steps: Int,
-) -> Result(Yielder(Float), Nil) {
+) -> Result(List(Float), Nil) {
case steps > 0 {
False -> Error(Nil)
True -> {
@@ -5595,3 +5878,64 @@ pub fn symmetric_space(
}
}
}
+
+///
+///
+/// The function is similar to [`symmetric_space`](#symmetric_space) but instead returns a yielder
+/// (lazily evaluated sequence of elements). This function can be used whenever there is a need
+/// to generate a larger-than-usual sequence of elements.
+///
+///
+/// Example:
+///
+/// import gleam/yielder.{Next, Done}
+/// import gleeunit/should
+/// import gleam_community/maths
+///
+/// pub fn example() {
+/// let assert Ok(symspace) = maths.yield_symmetric_space(0.0, 5.0, 5)
+///
+/// let assert Next(element, rest) = yielder.step(symspace)
+/// should.equal(element, -5.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, -2.5)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 0.0)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 2.5)
+///
+/// let assert Next(element, rest) = yielder.step(rest)
+/// should.equal(element, 5.0)
+///
+/// // We have generated 5 values, so the 6th will be 'Done'
+/// should.equal(yielder.step(rest), Done)
+/// }
+///
+///
+///
+///
+pub fn yield_symmetric_space(
+ center: Float,
+ radius: Float,
+ steps: Int,
+) -> Result(Yielder(Float), Nil) {
+ case steps > 0 {
+ False -> Error(Nil)
+ True -> {
+ let start = center -. radius
+ let stop = center +. radius
+ yield_linear_space(start, stop, steps, True)
+ }
+ }
+}
diff --git a/test/gleam_community/sequences_test.gleam b/test/gleam_community/sequences_test.gleam
index 2e4ff20..bb05a17 100644
--- a/test/gleam_community/sequences_test.gleam
+++ b/test/gleam_community/sequences_test.gleam
@@ -4,6 +4,138 @@ import gleam/yielder
import gleam_community/maths
import gleeunit/should
+pub fn yield_linear_space_test() {
+ let assert Ok(tol) = float.power(10.0, -6.0)
+
+ // Check that the function agrees, at some arbitrary input
+ // points, with known function values
+ // ---> With endpoint included
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, 50.0, 5, True)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([10.0, 20.0, 30.0, 40.0, 50.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, 20.0, 5, True)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Try with negative stop
+ // ----> Without endpoint included
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, 50.0, 5, False)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([10.0, 18.0, 26.0, 34.0, 42.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, 20.0, 5, False)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([10.0, 12.0, 14.0, 16.0, 18.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Try with negative stop
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, -50.0, 5, False)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace
+ |> yielder.to_list()
+ |> list.zip([10.0, -2.0, -14.0, -26.0, -38.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ let assert Ok(linspace) = maths.yield_linear_space(10.0, -20.0, 5, True)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace
+ |> yielder.to_list()
+ |> list.zip([10.0, 2.5, -5.0, -12.5, -20.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Try with negative start
+ let assert Ok(linspace) = maths.yield_linear_space(-10.0, 50.0, 5, False)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([-10.0, 2.0, 14.0, 26.0, 38.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ let assert Ok(linspace) = maths.yield_linear_space(-10.0, 20.0, 5, True)
+ let assert Ok(result) =
+ maths.all_close(
+ linspace |> yielder.to_list() |> list.zip([-10.0, -2.5, 5.0, 12.5, 20.0]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Check that when start == stop and steps > 0, then
+ // the value (start/stop) is just repeated, since the
+ // step increment will be 0
+ let assert Ok(linspace) = maths.yield_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.yield_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()
+
+ // A negative number of points does not work (-5)
+ maths.yield_linear_space(10.0, 50.0, -5, True)
+ |> should.be_error()
+}
+
pub fn list_linear_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
@@ -13,7 +145,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, True)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([10.0, 20.0, 30.0, 40.0, 50.0]),
+ linspace |> list.zip([10.0, 20.0, 30.0, 40.0, 50.0]),
0.0,
tol,
)
@@ -24,7 +156,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, True)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0]),
+ linspace |> list.zip([10.0, 12.5, 15.0, 17.5, 20.0]),
0.0,
tol,
)
@@ -37,7 +169,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(10.0, 50.0, 5, False)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([10.0, 18.0, 26.0, 34.0, 42.0]),
+ linspace |> list.zip([10.0, 18.0, 26.0, 34.0, 42.0]),
0.0,
tol,
)
@@ -48,7 +180,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(10.0, 20.0, 5, False)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([10.0, 12.0, 14.0, 16.0, 18.0]),
+ linspace |> list.zip([10.0, 12.0, 14.0, 16.0, 18.0]),
0.0,
tol,
)
@@ -61,7 +193,6 @@ pub fn list_linear_space_test() {
let assert Ok(result) =
maths.all_close(
linspace
- |> yielder.to_list()
|> list.zip([10.0, -2.0, -14.0, -26.0, -38.0]),
0.0,
tol,
@@ -74,7 +205,6 @@ pub fn list_linear_space_test() {
let assert Ok(result) =
maths.all_close(
linspace
- |> yielder.to_list()
|> list.zip([10.0, 2.5, -5.0, -12.5, -20.0]),
0.0,
tol,
@@ -87,7 +217,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(-10.0, 50.0, 5, False)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([-10.0, 2.0, 14.0, 26.0, 38.0]),
+ linspace |> list.zip([-10.0, 2.0, 14.0, 26.0, 38.0]),
0.0,
tol,
)
@@ -98,7 +228,7 @@ pub fn list_linear_space_test() {
let assert Ok(linspace) = maths.linear_space(-10.0, 20.0, 5, True)
let assert Ok(result) =
maths.all_close(
- linspace |> yielder.to_list() |> list.zip([-10.0, -2.5, 5.0, 12.5, 20.0]),
+ linspace |> list.zip([-10.0, -2.5, 5.0, 12.5, 20.0]),
0.0,
tol,
)
@@ -112,7 +242,7 @@ pub fn list_linear_space_test() {
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]),
+ linspace |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
0.0,
tol,
)
@@ -123,7 +253,7 @@ pub fn list_linear_space_test() {
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]),
+ linspace |> list.zip([10.0, 10.0, 10.0, 10.0, 10.0]),
0.0,
tol,
)
@@ -136,13 +266,14 @@ pub fn list_linear_space_test() {
|> should.be_error()
}
-pub fn list_logarithmic_space_test() {
+pub fn yield_logarithmic_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included
// - Positive start, stop
- let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
+ let assert Ok(logspace) =
+ maths.yield_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]),
@@ -154,7 +285,8 @@ pub fn list_logarithmic_space_test() {
|> should.be_true()
// - Positive start, negative stop
- let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
+ let assert Ok(logspace) =
+ maths.yield_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]),
@@ -166,7 +298,8 @@ pub fn list_logarithmic_space_test() {
|> should.be_true()
// - Positive stop, negative start
- let assert Ok(logspace) = maths.logarithmic_space(-1.0, 3.0, 3, True, 10.0)
+ let assert Ok(logspace) =
+ maths.yield_logarithmic_space(-1.0, 3.0, 3, True, 10.0)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
@@ -179,7 +312,8 @@ pub fn list_logarithmic_space_test() {
// ----> Without endpoint included
// - Positive start, stop
- let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, False, 10.0)
+ let assert Ok(logspace) =
+ maths.yield_logarithmic_space(1.0, 3.0, 3, False, 10.0)
let assert Ok(result) =
maths.all_close(
logspace
@@ -195,7 +329,8 @@ pub fn list_logarithmic_space_test() {
// Check that when start == stop and steps > 0, then
// the value (start/stop) is just repeated, since the
// step increment will be 0
- let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, True, 5.0)
+ let assert Ok(logspace) =
+ maths.yield_logarithmic_space(5.0, 5.0, 5, True, 5.0)
let assert Ok(result) =
maths.all_close(
logspace
@@ -207,7 +342,8 @@ pub fn list_logarithmic_space_test() {
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(logspace) =
+ maths.yield_logarithmic_space(5.0, 5.0, 5, False, 5.0)
let assert Ok(result) =
maths.all_close(
logspace
@@ -220,6 +356,84 @@ pub fn list_logarithmic_space_test() {
|> list.all(fn(x) { x == True })
|> should.be_true()
+ // A negative number of points does not work (-3)
+ maths.yield_logarithmic_space(1.0, 3.0, -3, True, 10.0)
+ |> should.be_error()
+
+ // A negative base does not work (-10)
+ maths.yield_logarithmic_space(1.0, 3.0, 3, True, -10.0)
+ |> should.be_error()
+}
+
+pub fn list_logarithmic_space_test() {
+ let assert Ok(tol) = float.power(10.0, -6.0)
+ // Check that the function agrees, at some arbitrary input
+ // points, with known function values
+ // ---> With endpoint included
+ // - Positive start, stop
+ let assert Ok(logspace) = maths.logarithmic_space(1.0, 3.0, 3, True, 10.0)
+ let assert Ok(result) =
+ maths.all_close(logspace |> 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
+ let assert Ok(logspace) = maths.logarithmic_space(1.0, -3.0, 3, True, 10.0)
+ let assert Ok(result) =
+ maths.all_close(logspace |> list.zip([10.0, 0.1, 0.001]), 0.0, tol)
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // - 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(logspace |> list.zip([0.1, 10.0, 1000.0]), 0.0, tol)
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // ----> Without endpoint included
+ // - 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(
+ logspace
+ |> list.zip([10.0, 46.41588834, 215.443469]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Check that when start == stop and steps > 0, then
+ // the value (start/stop) is just repeated, since the
+ // step increment will be 0
+ let assert Ok(logspace) = maths.logarithmic_space(5.0, 5.0, 5, True, 5.0)
+ let assert Ok(result) =
+ maths.all_close(
+ logspace
+ |> 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
+ |> 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()
@@ -229,13 +443,13 @@ pub fn list_logarithmic_space_test() {
|> should.be_error()
}
-pub fn list_geometric_space_test() {
+pub fn yield_geometric_space_test() {
let assert Ok(tol) = float.power(10.0, -6.0)
// Check that the function agrees, at some arbitrary input
// points, with known function values
// ---> With endpoint included
// - Positive start, stop
- let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
+ let assert Ok(logspace) = maths.yield_geometric_space(10.0, 1000.0, 3, True)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([10.0, 100.0, 1000.0]),
@@ -247,7 +461,7 @@ pub fn list_geometric_space_test() {
|> should.be_true()
// - Positive start, negative stop
- let assert Ok(logspace) = maths.geometric_space(10.0, 0.001, 3, True)
+ let assert Ok(logspace) = maths.yield_geometric_space(10.0, 0.001, 3, True)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([10.0, 0.1, 0.001]),
@@ -259,7 +473,7 @@ pub fn list_geometric_space_test() {
|> should.be_true()
// - Positive stop, negative start
- let assert Ok(logspace) = maths.geometric_space(0.1, 1000.0, 3, True)
+ let assert Ok(logspace) = maths.yield_geometric_space(0.1, 1000.0, 3, True)
let assert Ok(result) =
maths.all_close(
logspace |> yielder.to_list() |> list.zip([0.1, 10.0, 1000.0]),
@@ -272,7 +486,7 @@ pub fn list_geometric_space_test() {
// ----> Without endpoint included
// - Positive start, stop
- let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, False)
+ let assert Ok(logspace) = maths.yield_geometric_space(10.0, 1000.0, 3, False)
let assert Ok(result) =
maths.all_close(
logspace
@@ -288,7 +502,7 @@ pub fn list_geometric_space_test() {
// Check that when start == stop and steps > 0, then
// the value (start/stop) is just repeated, since the
// step increment will be 0
- let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, True)
+ let assert Ok(logspace) = maths.yield_geometric_space(5.0, 5.0, 5, True)
let assert Ok(result) =
maths.all_close(
logspace
@@ -301,7 +515,7 @@ pub fn list_geometric_space_test() {
|> 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(logspace) = maths.yield_geometric_space(5.0, 5.0, 5, False)
let assert Ok(result) =
maths.all_close(
logspace
@@ -314,6 +528,88 @@ pub fn list_geometric_space_test() {
|> 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.yield_geometric_space(0.0, 1000.0, 3, False)
+ |> should.be_error()
+
+ maths.yield_geometric_space(-1000.0, 0.0, 3, False)
+ |> should.be_error()
+
+ // A negative number of points does not work
+ maths.yield_geometric_space(-1000.0, 0.0, -3, False)
+ |> should.be_error()
+}
+
+pub fn list_geometric_space_test() {
+ let assert Ok(tol) = float.power(10.0, -6.0)
+ // Check that the function agrees, at some arbitrary input
+ // points, with known function values
+ // ---> With endpoint included
+ // - Positive start, stop
+ let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, True)
+ let assert Ok(result) =
+ maths.all_close(logspace |> 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
+ let assert Ok(logspace) = maths.geometric_space(10.0, 0.001, 3, True)
+ let assert Ok(result) =
+ maths.all_close(logspace |> list.zip([10.0, 0.1, 0.001]), 0.0, tol)
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // - Positive stop, negative start
+ let assert Ok(logspace) = maths.geometric_space(0.1, 1000.0, 3, True)
+ let assert Ok(result) =
+ maths.all_close(logspace |> list.zip([0.1, 10.0, 1000.0]), 0.0, tol)
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // ----> Without endpoint included
+ // - Positive start, stop
+ let assert Ok(logspace) = maths.geometric_space(10.0, 1000.0, 3, False)
+ let assert Ok(result) =
+ maths.all_close(
+ logspace
+ |> list.zip([10.0, 46.41588834, 215.443469]),
+ 0.0,
+ tol,
+ )
+ result
+ |> list.all(fn(x) { x == True })
+ |> should.be_true()
+
+ // Check that when start == stop and steps > 0, then
+ // the value (start/stop) is just repeated, since the
+ // step increment will be 0
+ let assert Ok(logspace) = maths.geometric_space(5.0, 5.0, 5, True)
+ let assert Ok(result) =
+ maths.all_close(
+ logspace
+ |> 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
+ |> 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()
@@ -326,46 +622,132 @@ pub fn list_geometric_space_test() {
|> should.be_error()
}
-pub fn list_arange_test() {
+pub fn list_step_range_test() {
+ // Positive start, stop, step
+ maths.step_range(1.0, 5.0, 1.0)
+ |> should.equal([1.0, 2.0, 3.0, 4.0])
+
+ maths.step_range(1.0, 5.0, 0.5)
+ |> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
+
+ maths.step_range(1.0, 2.0, 0.25)
+ |> should.equal([1.0, 1.25, 1.5, 1.75])
+
+ // Reverse (switch start/stop largest/smallest value)
+ maths.step_range(5.0, 1.0, 1.0)
+ |> should.equal([])
+
+ // Reverse negative step
+ maths.step_range(5.0, 1.0, -1.0)
+ |> should.equal([5.0, 4.0, 3.0, 2.0])
+
+ // Positive start, negative stop, step
+ maths.step_range(5.0, -1.0, -1.0)
+ |> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
+
+ // Negative start, stop, step
+ maths.step_range(-5.0, -1.0, -1.0)
+ |> should.equal([])
+
+ // Negative start, stop, positive step
+ maths.step_range(-5.0, -1.0, 1.0)
+ |> should.equal([-5.0, -4.0, -3.0, -2.0])
+}
+
+pub fn yield_step_range_test() {
// Positive start, stop, step
- maths.arange(1.0, 5.0, 1.0)
+ maths.yield_step_range(1.0, 5.0, 1.0)
|> yielder.to_list()
|> should.equal([1.0, 2.0, 3.0, 4.0])
- maths.arange(1.0, 5.0, 0.5)
+ maths.yield_step_range(1.0, 5.0, 0.5)
|> yielder.to_list()
|> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
- maths.arange(1.0, 2.0, 0.25)
+ maths.yield_step_range(1.0, 2.0, 0.25)
|> yielder.to_list()
|> should.equal([1.0, 1.25, 1.5, 1.75])
// Reverse (switch start/stop largest/smallest value)
- maths.arange(5.0, 1.0, 1.0)
+ maths.yield_step_range(5.0, 1.0, 1.0)
|> yielder.to_list()
|> should.equal([])
// Reverse negative step
- maths.arange(5.0, 1.0, -1.0)
+ maths.yield_step_range(5.0, 1.0, -1.0)
|> yielder.to_list()
|> should.equal([5.0, 4.0, 3.0, 2.0])
// Positive start, negative stop, step
- maths.arange(5.0, -1.0, -1.0)
+ maths.yield_step_range(5.0, -1.0, -1.0)
|> yielder.to_list()
|> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0])
// Negative start, stop, step
- maths.arange(-5.0, -1.0, -1.0)
+ maths.yield_step_range(-5.0, -1.0, -1.0)
|> yielder.to_list()
|> should.equal([])
// Negative start, stop, positive step
- maths.arange(-5.0, -1.0, 1.0)
+ maths.yield_step_range(-5.0, -1.0, 1.0)
|> yielder.to_list()
|> should.equal([-5.0, -4.0, -3.0, -2.0])
}
+pub fn yield_symmetric_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
+ let assert Ok(sym_space) = maths.yield_symmetric_space(0.0, 5.0, 5)
+ sym_space
+ |> yielder.to_list()
+ |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
+
+ let assert Ok(sym_space) = maths.yield_symmetric_space(0.0, 5.0, 5)
+ sym_space
+ |> yielder.to_list()
+ |> should.equal([-5.0, -2.5, 0.0, 2.5, 5.0])
+
+ // Negative center
+ let assert Ok(sym_space) = maths.yield_symmetric_space(-10.0, 5.0, 5)
+ sym_space
+ |> 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.yield_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.yield_symmetric_space(0.0, 2.0, 4)
+ let assert Ok(result) =
+ maths.all_close(
+ sym_space
+ |> yielder.to_list()
+ |> list.zip([-2.0, -0.6666666666666667, 0.6666666666666665, 2.0]),
+ 0.0,
+ tolerance,
+ )
+ result
+ |> 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.yield_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.yield_symmetric_space(0.0, 5.0, -5)
+ |> should.be_error()
+}
+
pub fn list_symmetric_space_test() {
let assert Ok(tolerance) = float.power(10.0, -6.0)
@@ -373,24 +755,20 @@ pub fn list_symmetric_space_test() {
// points, with known function 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])
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])
// Negative center
let assert Ok(sym_space) = maths.symmetric_space(-10.0, 5.0, 5)
sym_space
- |> 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
@@ -398,7 +776,6 @@ pub fn list_symmetric_space_test() {
let assert Ok(result) =
maths.all_close(
sym_space
- |> yielder.to_list()
|> list.zip([-2.0, -0.6666666666666667, 0.6666666666666665, 2.0]),
0.0,
tolerance,
@@ -412,7 +789,6 @@ pub fn list_symmetric_space_test() {
// 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)