Exit Full View

Feather2 / feather2-runtime / src / main / java / uk / co / nickthecoder / feather / runtime / IntProgression.java

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;
        }
    }
}