/*
 * Copyright (c) 2019 Goldman Sachs and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v. 1.0 which accompany this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */

package org.eclipse.collections.impl.list.primitive;

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.function.LongConsumer;

import org.eclipse.collections.api.LazyLongIterable;
import org.eclipse.collections.api.LongIterable;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.bag.primitive.MutableLongBag;
import org.eclipse.collections.api.block.function.primitive.LongToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.ObjectLongIntToObjectFunction;
import org.eclipse.collections.api.block.function.primitive.ObjectLongToObjectFunction;
import org.eclipse.collections.api.block.predicate.primitive.LongPredicate;
import org.eclipse.collections.api.block.procedure.primitive.LongIntProcedure;
import org.eclipse.collections.api.block.procedure.primitive.LongLongProcedure;
import org.eclipse.collections.api.block.procedure.primitive.LongProcedure;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.iterator.LongIterator;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.list.primitive.ImmutableLongList;
import org.eclipse.collections.api.list.primitive.LongList;
import org.eclipse.collections.api.list.primitive.MutableLongList;
import org.eclipse.collections.api.set.primitive.MutableLongSet;
import org.eclipse.collections.api.tuple.primitive.LongLongPair;
import org.eclipse.collections.api.tuple.primitive.LongObjectPair;
import org.eclipse.collections.impl.bag.mutable.primitive.LongHashBag;
import org.eclipse.collections.impl.block.factory.primitive.LongPredicates;
import org.eclipse.collections.impl.factory.primitive.LongLists;
import org.eclipse.collections.impl.lazy.primitive.CollectLongToObjectIterable;
import org.eclipse.collections.impl.lazy.primitive.LazyLongIterableAdapter;
import org.eclipse.collections.impl.lazy.primitive.ReverseLongIterable;
import org.eclipse.collections.impl.lazy.primitive.SelectLongIterable;
import org.eclipse.collections.impl.list.IntervalUtils;
import org.eclipse.collections.impl.list.mutable.primitive.LongArrayList;
import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet;
import org.eclipse.collections.impl.tuple.primitive.PrimitiveTuples;
import org.eclipse.collections.impl.utility.Iterate;

An LongInterval is a range of longs that may be iterated over using a step value. Note that the size of the interval (the number of elements in the list it represents) is limited by the maximum value of the integer index.
/** * An LongInterval is a range of longs that may be iterated over using a step value. * Note that the size of the interval (the number of elements in the list it represents) * is limited by the maximum value of the integer index. */
public final class LongInterval implements ImmutableLongList, Serializable { private static final long serialVersionUID = 1L; private final long from; private final long to; private final long step; private final int size; private LongInterval(long from, long to, long step) { this.from = from; this.to = to; this.step = step; this.size = IntervalUtils.intSize(this.from, this.to, this.step); }
This static from method allows LongInterval to act as a fluent builder for itself. It works in conjunction with the instance methods to(long) and by(long).

Usage Example:

LongInterval interval1 = LongInterval.from(1).to(5);         // results in: 1, 2, 3, 4, 5.
LongInterval interval2 = LongInterval.from(1).to(10).by(2);  // results in: 1, 3, 5, 7, 9.
/** * This static {@code from} method allows LongInterval to act as a fluent builder for itself. * It works in conjunction with the instance methods {@link #to(long)} and {@link #by(long)}. * <p> * Usage Example: * <pre> * LongInterval interval1 = LongInterval.from(1).to(5); // results in: 1, 2, 3, 4, 5. * LongInterval interval2 = LongInterval.from(1).to(10).by(2); // results in: 1, 3, 5, 7, 9. * </pre> */
public static LongInterval from(long newFrom) { return LongInterval.fromToBy(newFrom, newFrom, 1); }
This instance to method allows LongInterval to act as a fluent builder for itself. It works in conjunction with the static method from(long) and instance method by(long).

Usage Example:

LongInterval interval1 = LongInterval.from(1).to(5);         // results in: 1, 2, 3, 4, 5.
LongInterval interval2 = LongInterval.from(1).to(10).by(2);  // results in: 1, 3, 5, 7, 9.
/** * This instance {@code to} method allows LongInterval to act as a fluent builder for itself. * It works in conjunction with the static method {@link #from(long)} and instance method {@link #by(long)}. * <p> * Usage Example: * <pre> * LongInterval interval1 = LongInterval.from(1).to(5); // results in: 1, 2, 3, 4, 5. * LongInterval interval2 = LongInterval.from(1).to(10).by(2); // results in: 1, 3, 5, 7, 9. * </pre> */
public LongInterval to(long newTo) { return LongInterval.fromToBy(this.from, newTo, this.step); }
This instance by method allows LongInterval to act as a fluent builder for itself. It works in conjunction with the static method from(long) and instance method to(long).

Usage Example:

LongInterval interval1 = LongInterval.from(1).to(5);         // results in: 1, 2, 3, 4, 5.
LongInterval interval2 = LongInterval.from(1).to(10).by(2);  // results in: 1, 3, 5, 7, 9.
/** * This instance {@code by} method allows LongInterval to act as a fluent builder for itself. * It works in conjunction with the static method {@link #from(long)} and instance method {@link #to(long)}. * <p> * Usage Example: * <pre> * LongInterval interval1 = LongInterval.from(1).to(5); // results in: 1, 2, 3, 4, 5. * LongInterval interval2 = LongInterval.from(1).to(10).by(2); // results in: 1, 3, 5, 7, 9. * </pre> */
public LongInterval by(long newStep) { return LongInterval.fromToBy(this.from, this.to, newStep); }
Returns an LongInterval starting at zero.

Usage Example:

LongInterval interval1 = LongInterval.zero().to(5);         // results in: 0, 1, 2, 3, 4, 5.
LongInterval interval2 = LongInterval.zero().to(10).by(2);  // results in: 0, 2, 4, 6, 8, 10.
/** * Returns an LongInterval starting at zero. * <p> * Usage Example: * <pre> * LongInterval interval1 = LongInterval.zero().to(5); // results in: 0, 1, 2, 3, 4, 5. * LongInterval interval2 = LongInterval.zero().to(10).by(2); // results in: 0, 2, 4, 6, 8, 10. * </pre> */
public static LongInterval zero() { return LongInterval.from(0); }
Returns an LongInterval starting from 1 to the specified count value with a step value of 1.
/** * Returns an LongInterval starting from 1 to the specified count value with a step value of 1. */
public static LongInterval oneTo(long count) { return LongInterval.oneToBy(count, 1); }
Returns an LongInterval starting from 1 to the specified count value with a step value of step.
/** * Returns an LongInterval starting from 1 to the specified count value with a step value of step. */
public static LongInterval oneToBy(long count, long step) { if (count < 1) { throw new IllegalArgumentException("Only positive ranges allowed using oneToBy"); } return LongInterval.fromToBy(1, count, step); }
Returns an LongInterval starting from 0 to the specified count value with a step value of 1.
/** * Returns an LongInterval starting from 0 to the specified count value with a step value of 1. */
public static LongInterval zeroTo(long count) { return LongInterval.zeroToBy(count, 1); }
Returns an LongInterval starting from 0 to the specified count value with a step value of step.
/** * Returns an LongInterval starting from 0 to the specified count value with a step value of step. */
public static LongInterval zeroToBy(long count, long step) { return LongInterval.fromToBy(0, count, step); }
Returns an LongInterval starting from the value from to the specified value to with a step value of 1.
/** * Returns an LongInterval starting from the value from to the specified value to with a step value of 1. */
public static LongInterval fromTo(long from, long to) { if (from <= to) { return LongInterval.fromToBy(from, to, 1); } return LongInterval.fromToBy(from, to, -1); }
Returns an LongInterval representing the even values from the value from to the value to.
/** * Returns an LongInterval representing the even values from the value from to the value to. */
public static LongInterval evensFromTo(long from, long to) { if (from % 2 != 0) { if (from < to) { from++; } else { from--; } } if (to % 2 != 0) { if (to > from) { to--; } else { to++; } } return LongInterval.fromToBy(from, to, to > from ? 2 : -2); }
Returns an LongInterval representing the odd values from the value from to the value to.
/** * Returns an LongInterval representing the odd values from the value from to the value to. */
public static LongInterval oddsFromTo(long from, long to) { if (from % 2 == 0) { if (from < to) { from++; } else { from--; } } if (to % 2 == 0) { if (to > from) { to--; } else { to++; } } return LongInterval.fromToBy(from, to, to > from ? 2 : -2); }
Returns an LongInterval for the range of integers inclusively between from and to with the specified stepBy value.
/** * Returns an LongInterval for the range of integers inclusively between from and to with the specified * stepBy value. */
public static LongInterval fromToBy(long from, long to, long stepBy) { IntervalUtils.checkArguments(from, to, stepBy); return new LongInterval(from, to, stepBy); }
Returns true if the LongInterval contains all of the specified long values.
/** * Returns true if the LongInterval contains all of the specified long values. */
@Override public boolean containsAll(long... values) { for (long value : values) { if (!this.contains(value)) { return false; } } return true; } @Override public boolean containsAll(LongIterable source) { for (LongIterator iterator = source.longIterator(); iterator.hasNext(); ) { if (!this.contains(iterator.next())) { return false; } } return true; }
Returns true if the LongInterval contains none of the specified long values.
/** * Returns true if the LongInterval contains none of the specified long values. */
public boolean containsNone(int... values) { for (long value : values) { if (this.contains(value)) { return false; } } return true; }
Returns true if the LongInterval contains the specified long value.
/** * Returns true if the LongInterval contains the specified long value. */
@Override public boolean contains(long value) { return IntervalUtils.contains(value, this.from, this.to, this.step); } @Override public void forEachWithIndex(LongIntProcedure procedure) { int index = 0; if (this.goForward()) { for (long i = this.from; i <= this.to; i += this.step) { procedure.value((int) i, index++); } } else { for (long i = this.from; i >= this.to; i += this.step) { procedure.value((int) i, index++); } } } public void forEachWithLongIndex(LongLongProcedure procedure) { long index = 0; if (this.goForward()) { for (long i = this.from; i <= this.to; i += this.step) { procedure.value((int) i, index++); } } else { for (long i = this.from; i >= this.to; i += this.step) { procedure.value((int) i, index++); } } } @Override public void forEach(LongProcedure procedure) { this.each(procedure); } private boolean goForward() { return this.from <= this.to && this.step > 0; }
Since:7.0.
/** * @since 7.0. */
@Override public void each(LongProcedure procedure) { if (this.goForward()) { for (long i = this.from; i <= this.to; i += this.step) { procedure.value((int) i); } } else { for (long i = this.from; i >= this.to; i += this.step) { procedure.value((int) i); } } } @Override public int count(LongPredicate predicate) { int count = 0; for (int i = 0; i < this.size(); i++) { if (predicate.accept(this.get(i))) { count++; } } return count; } @Override public boolean anySatisfy(LongPredicate predicate) { for (int i = 0; i < this.size(); i++) { if (predicate.accept(this.get(i))) { return true; } } return false; } @Override public boolean allSatisfy(LongPredicate predicate) { for (int i = 0; i < this.size(); i++) { if (!predicate.accept(this.get(i))) { return false; } } return true; } @Override public boolean noneSatisfy(LongPredicate predicate) { for (int i = 0; i < this.size(); i++) { if (predicate.accept(this.get(i))) { return false; } } return true; } @Override public boolean equals(Object otherList) { if (otherList == this) { return true; } if (!(otherList instanceof LongList)) { return false; } LongList list = (LongList) otherList; if (this.size() != list.size()) { return false; } if (this.from == this.to) { return this.from == list.get(0); } if (otherList instanceof LongInterval) { LongInterval otherInterval = (LongInterval) otherList; return (this.getFirst() == otherInterval.getFirst()) && (this.getLast() == otherInterval.getLast()) && (this.step == otherInterval.step); } if (this.from < this.to) { int listIndex = 0; for (long i = this.from; i <= this.to; i += this.step) { if (i != list.get(listIndex++)) { return false; } } } else { int listIndex = 0; for (long i = this.from; i >= this.to; i += this.step) { if (i != list.get(listIndex++)) { return false; } } } return true; } @Override public int hashCode() { int hashCode = 1; if (this.from == this.to) { hashCode = 31 * hashCode + (int) (this.from ^ this.from >>> 32); } else if (this.from < this.to) { for (long i = this.from; i <= this.to; i += this.step) { hashCode = 31 * hashCode + (int) (i ^ i >>> 32); } } else { for (long i = this.from; i >= this.to; i += this.step) { hashCode = 31 * hashCode + (int) (i ^ i >>> 32); } } return hashCode; }
Returns a new LongInterval with the from and to values reversed and the step value negated.
/** * Returns a new LongInterval with the from and to values reversed and the step value negated. */
@Override public LongInterval toReversed() { return LongInterval.fromToBy(this.to, this.from, -this.step); }
Since:6.0
/** * @since 6.0 */
@Override public ImmutableLongList distinct() { return this; } @Override public ImmutableLongList subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException("subList not yet implemented!"); }
Returns the size of the interval.
/** * Returns the size of the interval. */
@Override public int size() { return this.size; } @Override public long dotProduct(LongList list) { if (this.size() != list.size()) { throw new IllegalArgumentException("Lists used in dotProduct must be the same size"); } long sum = 0L; for (int i = 0; i < this.size(); i++) { sum += this.get(i) * list.get(i); } return sum; } @Override public boolean isEmpty() { return this.size() == 0; } @Override public boolean notEmpty() { return !this.isEmpty(); } @Override public String makeString() { return this.makeString(", "); } @Override public String makeString(String separator) { return this.makeString("", separator, ""); } @Override public String makeString(String start, String separator, String end) { Appendable stringBuilder = new StringBuilder(); this.appendString(stringBuilder, start, separator, end); return stringBuilder.toString(); } @Override public void appendString(Appendable appendable) { this.appendString(appendable, ", "); } @Override public void appendString(Appendable appendable, String separator) { this.appendString(appendable, "", separator, ""); } @Override public void appendString( Appendable appendable, String start, String separator, String end) { try { appendable.append(start); for (int i = 0; i < this.size(); i++) { if (i > 0) { appendable.append(separator); } long value = this.get(i); appendable.append(String.valueOf(value)); } appendable.append(end); } catch (IOException e) { throw new RuntimeException(e); } } @Override public long[] toArray() { long[] result = new long[this.size()]; this.forEachWithIndex((each, index) -> result[index] = each); return result; } @Override public long[] toArray(long[] result) { if (result.length < this.size()) { result = new long[this.size()]; } long[] finalBypass = result; this.forEachWithIndex((each, index) -> finalBypass[index] = each); return result; } @Override public <T> T injectInto(T injectedValue, ObjectLongToObjectFunction<? super T, ? extends T> function) { T result = injectedValue; if (this.goForward()) { for (long i = this.from; i <= this.to; i += this.step) { result = function.valueOf(result, (int) i); } } else { for (long i = this.from; i >= this.to; i += this.step) { result = function.valueOf(result, (int) i); } } return result; } @Override public <T> T injectIntoWithIndex(T injectedValue, ObjectLongIntToObjectFunction<? super T, ? extends T> function) { T result = injectedValue; int index = 0; if (this.goForward()) { for (long i = this.from; i <= this.to; i += this.step) { result = function.valueOf(result, (int) i, index); index++; } } else { for (long i = this.from; i >= this.to; i += this.step) { result = function.valueOf(result, (int) i, index); index++; } } return result; } @Override public RichIterable<LongIterable> chunk(int size) { if (size <= 0) { throw new IllegalArgumentException("Size for groups must be positive but was: " + size); } MutableList<LongIterable> result = Lists.mutable.empty(); if (this.notEmpty()) { long innerFrom = this.from; long lastUpdated = this.from; if (this.from <= this.to) { while ((lastUpdated + this.step) <= this.to) { MutableLongList batch = LongLists.mutable.empty(); for (long i = innerFrom; i <= this.to && batch.size() < size; i += this.step) { batch.add((int) i); lastUpdated = (int) i; } result.add(batch); innerFrom = lastUpdated + this.step; } } else { while ((lastUpdated + this.step) >= this.to) { MutableLongList batch = LongLists.mutable.empty(); for (long i = innerFrom; i >= this.to && batch.size() < size; i += this.step) { batch.add((int) i); lastUpdated = (int) i; } result.add(batch); innerFrom = lastUpdated + this.step; } } } return result; } @Override public String toString() { return this.makeString("[", ", ", "]"); } @Override public LongIterator longIterator() { return new LongIntervalIterator(); } @Override public long getFirst() { return this.from; } @Override public long getLast() { return IntervalUtils.valueAtIndex(this.size() - 1, this.from, this.to, this.step); } @Override public long get(int index) { this.checkBounds("index", index); return IntervalUtils.valueAtIndex(index, this.from, this.to, this.step); } private void checkBounds(String name, int index) { if (index < 0 || index >= this.size()) { throw new IndexOutOfBoundsException(name + ": " + index + ' ' + this); } } @Override public int indexOf(long value) { return IntervalUtils.indexOf(value, this.from, this.to, this.step); } @Override public int lastIndexOf(long value) { return this.indexOf(value); } @Override public ImmutableLongList select(LongPredicate predicate) { return LongArrayList.newList(new SelectLongIterable(this, predicate)).toImmutable(); } @Override public ImmutableLongList reject(LongPredicate predicate) { return LongArrayList.newList(new SelectLongIterable(this, LongPredicates.not(predicate))).toImmutable(); } @Override public long detectIfNone(LongPredicate predicate, long ifNone) { return new SelectLongIterable(this, predicate).detectIfNone(predicate, ifNone); } @Override public <V> ImmutableList<V> collect(LongToObjectFunction<? extends V> function) { return new CollectLongToObjectIterable<V>(this, function).toList().toImmutable(); } @Override public LazyLongIterable asReversed() { return ReverseLongIterable.adapt(this); } @Override public long sum() { if (this.size() == 1) { return this.getFirst(); } long fl = this.getFirst() + this.getLast(); long s = this.size(); if (s % 2 == 0) { s /= 2L; } else { fl /= 2L; } return s * fl; } @Override public long max() { if (this.from >= this.to) { return this.getFirst(); } return this.getLast(); } @Override public long min() { if (this.from <= this.to) { return this.getFirst(); } return this.getLast(); } @Override public long minIfEmpty(long defaultValue) { return this.min(); } @Override public long maxIfEmpty(long defaultValue) { return this.max(); } @Override public double average() { // for an arithmetic sequence its median and its average are the same return this.median(); } @Override public double median() { return ((double) this.getFirst() + (double) this.getLast()) / 2.0; } @Override public int binarySearch(long value) { return IntervalUtils.binarySearch(value, this.from, this.to, this.step); } @Override public long[] toSortedArray() { long[] array = this.toArray(); Arrays.sort(array); return array; } @Override public MutableLongList toList() { return LongArrayList.newList(this); } @Override public MutableLongList toSortedList() { return LongArrayList.newList(this).sortThis(); } @Override public MutableLongSet toSet() { return LongHashSet.newSet(this); } @Override public MutableLongBag toBag() { return LongHashBag.newBag(this); } @Override public LazyLongIterable asLazy() { return new LazyLongIterableAdapter(this); } @Override public ImmutableLongList toImmutable() { return this; } @Override public ImmutableLongList newWith(long element) { return LongArrayList.newList(this).with(element).toImmutable(); } @Override public ImmutableLongList newWithout(long element) { return LongArrayList.newList(this).without(element).toImmutable(); } @Override public ImmutableLongList newWithAll(LongIterable elements) { return LongArrayList.newList(this).withAll(elements).toImmutable(); } @Override public ImmutableLongList newWithoutAll(LongIterable elements) { return LongArrayList.newList(this).withoutAll(elements).toImmutable(); } @Override public ImmutableList<LongLongPair> zipLong(LongIterable iterable) { int size = this.size(); int othersize = iterable.size(); MutableList<LongLongPair> target = Lists.mutable.withInitialCapacity(Math.min(size, othersize)); LongIterator iterator = this.longIterator(); LongIterator otherIterator = iterable.longIterator(); for (int i = 0; i < size && otherIterator.hasNext(); i++) { target.add(PrimitiveTuples.pair(iterator.next(), otherIterator.next())); } return target.toImmutable(); } @Override public <T> ImmutableList<LongObjectPair<T>> zip(Iterable<T> iterable) { int size = this.size(); int othersize = Iterate.sizeOf(iterable); MutableList<LongObjectPair<T>> target = Lists.mutable.withInitialCapacity(Math.min(size, othersize)); LongIterator iterator = this.longIterator(); Iterator<T> otherIterator = iterable.iterator(); for (int i = 0; i < size && otherIterator.hasNext(); i++) { target.add(PrimitiveTuples.pair(iterator.next(), otherIterator.next())); } return target.toImmutable(); } @Override public Spliterator.OfLong spliterator() { return new LongIntervalSpliterator(this.from, this.to, this.step); } private class LongIntervalIterator implements LongIterator { private long current = LongInterval.this.from; @Override public boolean hasNext() { if (LongInterval.this.from <= LongInterval.this.to) { return this.current <= LongInterval.this.to; } return this.current >= LongInterval.this.to; } @Override public long next() { if (this.hasNext()) { long result = (int) this.current; this.current += LongInterval.this.step; return result; } throw new NoSuchElementException(); } } private static final class LongIntervalSpliterator implements Spliterator.OfLong { private long current; private final long to; private final long step; private final boolean isAscending; private LongIntervalSpliterator(long from, long to, long step) { this.current = from; this.to = to; this.step = step; this.isAscending = from <= to; } @Override public Comparator<? super Long> getComparator() { if (this.isAscending) { return Comparator.naturalOrder(); } return Comparator.reverseOrder(); } @Override public OfLong trySplit() { OfLong leftSpliterator = null; long numberOfStepsToMid = (int) (this.estimateSize() / 2); long mid = this.current + this.step * numberOfStepsToMid; if (this.isAscending) { if (this.current < mid) { leftSpliterator = new LongIntervalSpliterator(this.current, mid - 1, this.step); this.current = mid; } } else { if (this.current > mid) { leftSpliterator = new LongIntervalSpliterator(this.current, mid + 1, this.step); this.current = mid; } } return leftSpliterator; } @Override public long estimateSize() { return ((long) this.to - (long) this.current) / (long) this.step + 1; } @Override public int characteristics() { return Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED; } @Override public boolean tryAdvance(LongConsumer action) { action.accept(this.current); this.current += this.step; if (this.isAscending) { return this.current <= this.to; } return this.current >= this.to; } } }