clojure.lang.LongRange Maven / Gradle / Ivy
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Implements the special common case of a finite range based on long start, end, and step,
* with no more than Integer.MAX_VALUE items.
*/
public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce, IDrop {
private static final long serialVersionUID = -1467242400566893909L;
// Invariants guarantee this is never an empty or infinite seq
// assert(start != end && step != 0)
final long start;
final long end;
final long step;
final int count;
private LongRange(long start, long end, long step, int count){
this.start = start;
this.end = end;
this.step = step;
this.count = count;
}
private LongRange(IPersistentMap meta, long start, long end, long step, int count){
super(meta);
this.start = start;
this.end = end;
this.step = step;
this.count = count;
}
// returns exact size of remaining items OR throws ArithmeticException for overflow case
static long rangeCount(long start, long end, long step) {
// (1) count = ceiling ( (end - start) / step )
// (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
// thus: count = end - start + step + o / step
return Numbers.add(Numbers.add(Numbers.minus(end, start), step), step > 0 ? -1 : 1) / step;
}
public static ISeq create(long end) {
if(end > 0) {
try {
return new LongRange(0L, end, 1L, Math.toIntExact(rangeCount(0L, end, 1L)));
} catch(ArithmeticException e) {
return Range.create(end); // count > Integer.MAX_VALUE
}
} else {
return PersistentList.EMPTY;
}
}
public static ISeq create(long start, long end) {
if(start >= end) {
return PersistentList.EMPTY;
} else {
try {
return new LongRange(start, end, 1L, Math.toIntExact(rangeCount(start, end, 1L)));
} catch(ArithmeticException e) {
return Range.create(start, end);
}
}
}
public static ISeq create(final long start, long end, long step) {
if(step > 0) {
if(end <= start) return PersistentList.EMPTY;
try {
return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
} catch(ArithmeticException e) {
return Range.create(start, end, step);
}
} else if(step < 0) {
if(end >= start) return PersistentList.EMPTY;
try {
return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
} catch(ArithmeticException e) {
return Range.create(start, end, step);
}
} else {
if(end == start) return PersistentList.EMPTY;
return Repeat.create(start);
}
}
public Obj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return new LongRange(meta, start, end, step, count);
}
public Object first() {
return start;
}
public ISeq next() {
if(count > 1) {
return new LongRange(start + step, end, step, count-1);
} else {
return null;
}
}
private static final int CHUNK_SIZE = 32;
public IChunk chunkedFirst() {
return new LongChunk(start, step, Math.min(count, CHUNK_SIZE));
}
public ISeq chunkedNext() {
return chunkedMore().seq();
}
public ISeq chunkedMore() {
if(count <= CHUNK_SIZE) {
return PersistentList.EMPTY;
} else {
return LongRange.create(start + (step * CHUNK_SIZE), end, step);
}
}
public Sequential drop(int n) {
if(n <= 0) {
return this;
} else if(n < count) {
return new LongRange(start+(step*n), end, step, count - n);
} else {
return null;
}
}
public int count() {
return count;
}
public Object reduce(IFn f) {
Object acc = start;
long i = start + step;
int n = count;
while(n > 1) {
acc = f.invoke(acc, i);
if (acc instanceof Reduced) return ((Reduced)acc).deref();
i += step;
n--;
}
return acc;
}
public Object reduce(IFn f, Object val) {
Object acc = val;
int n = count;
long i = start;
do {
acc = f.invoke(acc, i);
if (RT.isReduced(acc)) return ((Reduced)acc).deref();
i += step;
n--;
} while(n > 0);
return acc;
}
public Iterator iterator() {
return new LongRangeIterator();
}
class LongRangeIterator implements Iterator {
private long next;
private int remaining;
public LongRangeIterator() {
this.next = start;
this.remaining = count;
}
public boolean hasNext() {
return remaining > 0;
}
public Object next() {
if (remaining > 0) {
long ret = next;
next = next + step;
remaining = remaining - 1;
return ret;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static class LongChunk implements IChunk, Serializable {
final long start;
final long step;
final int count;
public LongChunk(long start, long step, int count) {
this.start = start;
this.step = step;
this.count = count;
}
public long first(){
return start;
}
public Object nth(int i){
return start + (i * step);
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < count)
return start + (i * step);
return notFound;
}
public int count(){
return count;
}
public LongChunk dropFirst(){
if(count <= 1)
throw new IllegalStateException("dropFirst of empty chunk");
return new LongChunk(start+step, step, count-1);
}
public Object reduce(IFn f, Object init) {
long x = start;
Object ret = init;
for(int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy