Exit Full View

Feather2 / feather2-runtime / src / main / java / uk / co / nickthecoder / feather / runtime / LongProgression.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 LongProgression implements Iterable<Long> {

    public final long start;
    public final long endInclusive;
    public final long step;

    public LongProgression(long start, long endInclusive, long step) {
        if (step == 0) throw new IllegalArgumentException("Step cannot be zero");
        this.start = start;
        this.endInclusive = endInclusive;
        this.step = step;
    }

    public LongProgression(long start, long endInclusive) {
        this(start, endInclusive, 1);
    }


    /**
     * Note, the sign of `step` is ignored, as the direction of travel is always from start to endInclusive.
     */
    public LongProgression step(long step) {
        long absStep = (step < 0) ? -step : step;
        if (start <= endInclusive) {
            return new LongProgression(start, endInclusive, absStep);
        } else {
            return new LongProgression(start, endInclusive, -absStep);
        }
    }

    public boolean isEmpty() {
        return (step > 0) ? start > endInclusive : start < endInclusive;
    }

    // Override from Iterable
    @Override
    public Iterator<Long> iterator() {
        return new LongProgressionIterator();
    }

    // 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 LongProgression) {
            LongProgression otherProgression = (LongProgression) 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 LongProgressionIterator implements Iterator<Long> {

        private long next = start;

        @Override
        public boolean hasNext() {
            return step < 0 ? next >= endInclusive : next <= endInclusive;
        }

        @Override
        public Long next() {
            long result = next;
            next += step;
            return result;
        }
    }
}