package uk.co.nickthecoder.feather.runtime;
import java.util.Iterator;
import java.util.Objects;
/**
* Primarily used to define `for` loops.
* Feather's `for` loop always loops over an Iterable (unlike Java's `for` loop, which is really a while loop).
* However, at compile-time, the feather compiler optimises iterations over IntProgressions.
*/
public class IntProgression implements Iterable<Integer> {
public final int start;
public final int endInclusive;
public final int step;
public IntProgression(int start, int endInclusive, int step) {
if (step == 0) throw new IllegalArgumentException("Step cannot be zero");
this.start = start;
this.endInclusive = endInclusive;
this.step = step;
}
public IntProgression(int start, int endInclusive) {
this(start, endInclusive, 1);
}
/**
* Note, the sign of `step` is ignored, as the direction of travel is always from start to endInclusive.
*/
public IntProgression step(int step) {
int absStep = (step < 0) ? -step : step;
if (start <= endInclusive) {
return new IntProgression(start, endInclusive, absStep);
} else {
return new IntProgression(start, endInclusive, -absStep);
}
}
public boolean isEmpty() {
return (step > 0) ? start > endInclusive : start < endInclusive;
}
// Override from Iterable
@Override
public Iterator<Integer> iterator() {
return new IntProgressionIterator();
}
// Override from Object
@Override
public int hashCode() {
if (isEmpty()) return -1;
return Objects.hash(start, endInclusive, step);
}
@Override
public boolean equals(Object other) {
if (other instanceof IntProgression) {
IntProgression otherProgression = (IntProgression) other;
return (isEmpty() && (otherProgression).isEmpty()) ||
(start == otherProgression.start &&
endInclusive == otherProgression.endInclusive &&
step == otherProgression.step
);
} else {
return false;
}
}
@Override
public String toString() {
if (step == 1) {
return start + ".." + endInclusive;
} else {
return start + ".." + endInclusive + " step " + step;
}
}
private class IntProgressionIterator implements Iterator<Integer> {
private int next = start;
@Override
public boolean hasNext() {
return step < 0 ? next >= endInclusive : next <= endInclusive;
}
@Override
public Integer next() {
int result = next;
next += step;
return result;
}
}
}