# range.pony

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122``` ``````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 `` 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 ``````