class Range[A: (Real[A] val & Number) = USize] is Iterator[A]
"""
Produces `[min, max)` with a step of `inc` for any `Number` type.
```pony
// iterating with for-loop
for i in Range(0, 10) do
env.out.print(i.string())
end
// iterating over Range of U8 with while-loop
let range = Range[U8](5, 100, 5)
while range.has_next() do
try
handle_u8(range.next()?)
end
end
```
Supports `min` being smaller than `max` with negative `inc`
but only for signed integer types and floats:
```pony
var previous = 11
for left in Range[I64](10, -5, -1) do
if not (left < previous) then
error
end
previous = left
end
```
If `inc` is nonzero, but cannot produce progress towards `max` because of its sign, the `Range` is considered empty and will not produce any iterations. The `Range` is also empty if either `min` equals `max`, independent of the value of `inc`, or if `inc` is zero.
```pony
let empty_range1 = Range(0, 10, -1)
let empty_range2 = Range(0, 10, 0)
let empty_range3 = Range(10, 10)
empty_range1.is_empty() == true
empty_range2.is_empty() == true
empty_range3.is_empty() == true
```
Note that when using unsigned integers, a negative literal wraps around so while `Range[ISize](0, 10, -1)` is empty as above, `Range[USize](0, 10, -1)` produces a single value of `min` or `[0]` here.
When using `Range` with floating point types (`F32` and `F64`) `inc` steps < 1.0 are possible. If any arguments contains NaN, the `Range` is considered empty. It is also empty if the lower bound `min` or the step `inc` are +Inf or -Inf. However, if only the upper bound `max` is +Inf or -Inf and the step parameter `inc` has the same sign, then the `Range` is considered infinite and will iterate indefinitely.
```pony
let p_inf: F64 = F64.max_value() + F64.max_value()
let n_inf: F64 = -p_inf
let nan: F64 = F64(0) / F64(0)
let infinite_range1 = Range[F64](0, p_inf, 1)
let infinite_range2 = Range[F64](0, n_inf, -1_000_000)
infinite_range1.is_infinite() == true
infinite_range2.is_infinite() == true
for i in Range[F64](0.5, 100, nan) do
// will not be executed as `inc` is nan
end
for i in Range[F64](0.5, 100, p_inf) do
// will not be executed as `inc` is +Inf
end
```
"""
let _min: A
let _max: A
let _inc: A
let _forward: Bool
let _infinite: Bool
let _empty: Bool
var _idx: A
new create(min: A, max: A, inc: A = 1) =>
_min = min
_max = max
_inc = inc
_forward = (_min < _max) and (_inc > 0)
(let min_finite, let max_finite, let inc_finite) =
iftype A <: FloatingPoint[A] then
(_min.finite(), _max.finite(), _inc.finite())
else
(true, true, true)
end
let progress = ((_min < _max) and (_inc > 0))
or ((_min > _max) and (_inc < 0)) // false if any is NaN!
if progress and min_finite and inc_finite then
_empty = false
_infinite = not max_finite // ok to use not max_finite for max_infinite
// since progress excludes _max == nan
_idx = _min
else
_empty = true
_infinite = false
_idx = _max // has_next() will return false without code modification
end
fun has_next(): Bool =>
if _infinite then
return true
end
if _forward then
_idx < _max
else
_idx > _max
end
fun ref next(): A ? =>
if has_next() then
_idx = _idx + _inc
else
error
end
fun ref rewind() =>
_idx = _min
fun is_infinite(): Bool =>
_infinite
fun is_empty(): Bool =>
_empty