How are IntProgression equality, emptiness, and the `last` value defined? Why might two differently-written ranges be equal, and when is a progression empty?
answer
- Empty: first past last for the step direction
- last is snapped final element, not written bound
- equals = both empty OR first/last/step match
- empty hashCode is -1; IntRange.EMPTY = 1..0
- 1..10 step 3 == 1..11 step 3 (both end at 10)
basics
~10 sA progression is empty when it would produce no numbers (start past end for its direction). Two progressions count as equal when they're both empty, or when their start, end, and step all match.
solid answer
~40 s`IntProgression` defines structural `equals`/`hashCode`: two progressions are equal if both are empty, or if their `first`, `last`, and `step` are all equal. Because `last` is the *snapped* final element rather than the written bound, `1..10 step 3` and `1..11 step 3` have the same `first=1`, `last=10`, `step=3` and are **equal** even though written differently. A progression `isEmpty()` is `true` when, given its step direction, `first` is past `last`: for positive step, `first > last`; for negative step, `first < last`. `5..1` is empty (first 5 > last 1, step +1). An empty progression's `hashCode` is `-1` by contract. Note IntRange overrides nothing here beyond what's needed; `IntRange.EMPTY` (`1..0`) is the canonical empty range.
code
kotlin · 8 linesprintln((5..1).isEmpty()) // true
println(IntRange.EMPTY) // 1..0
val a = 1..10 step 3 // 1,4,7,10
val b = 1..11 step 3 // 1,4,7,10
println(a == b) // true (snapped last = 10)
println(a.last) // 10
println((1..10 step 3).count()) // 4 (computed, no iteration)go deeper
Knows a backwards range like 5..1 just produces nothing.
Can state when a progression is empty and that 1..10 step 3 ends at 10.
Explains structural equality over (first,last,step) and why snapped last makes differently-written ranges equal.
Reasons about the equals/hashCode contract (empty => -1), Set/Map key implications, and arithmetic count().
## Emptiness A progression iterates `first`, `first+step`, ... toward `last`. It is **empty** (produces nothing) when the first element is already past the boundary for its direction: - Positive step: empty when `first > last`. - Negative step: empty when `first < last`. ```kotlin println((5..1).isEmpty()) // true (first 5 > last 1, step +1) println((1 downTo 5).isEmpty()) // true (first 1 < last 5, step -1) println((1..1).isEmpty()) // false (single element) ``` `IntRange.EMPTY` is the canonical empty range, defined as `1..0`. ## The snapped `last` and why ranges can be 'equal' Progressions store `last` as the **last actually reachable element**, computed at construction. So a bound you write may be snapped down: ```kotlin val a = 1..10 step 3 // 1,4,7,10 val b = 1..11 step 3 // 1,4,7,10 (11 snapped to 10) println(a.last == b.last) // true (both 10) println(a == b) // true ``` This is intentional: equality reflects the **actual sequence produced**, not the literal text. ## equals / hashCode contract `IntProgression.equals` returns `true` when: - both are empty, OR - `first == other.first && last == other.last && step == other.step`. `hashCode` is `-1` when empty; otherwise it is derived from `first`, `last`, and `step`. This means two empty progressions with different written bounds (e.g. `5..1` and `10..3`) are equal and share hashCode `-1`. ## Practical implications - Using a range as a `Map` key or in a `Set` relies on this structural equality — different literals that produce the same sequence collide. - Don't assume `(1..11 step 3).last == 11`; always think in terms of the produced sequence. - `count()` on a progression is computed arithmetically from first/last/step (no iteration needed): for `1..10 step 3` it's 4. ## Key takeaways - Empty when first is past last for the step's direction; `IntRange.EMPTY == 1..0`. - `last` is snapped to the real final element; differently-written ranges can be equal. - Equality is structural over (first, last, step); empty progressions all hash to -1.
- Are `1..10 step 3` and `1..11 step 3` equal?Yes. Both snap to first=1, last=10, step=3 and produce 1,4,7,10, so structural equality returns true.
- What is the hashCode of any empty progression?-1, by contract — so all empty progressions (regardless of written bounds) share that hashCode and compare equal to each other.
saying these in an interview costs you the question
- Saying `(5..1)` throws instead of being empty
- Assuming `last` equals the written bound after a step
- Claiming equality compares the literal text, not (first,last,step)
- Thinking two ranges producing the same sequence are unequal
- Believing count() iterates the whole progression