/*
* Copyright 2015, 2016 Tagir Valeev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package one.util.streamex;
import static one.util.streamex.StreamExInternals.*;
import java.util.Spliterator;
import java.util.Spliterators.AbstractDoubleSpliterator;
import java.util.Spliterators.AbstractIntSpliterator;
import java.util.Spliterators.AbstractLongSpliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleConsumer;
import java.util.function.IntBinaryOperator;
import java.util.function.IntConsumer;
import java.util.function.LongBinaryOperator;
import java.util.function.LongConsumer;
/**
* @author Tagir Valeev
*/
/* package */ abstract class PrefixOps<T, S extends Spliterator<T>> extends CloneableSpliterator<T, PrefixOps<T, S>>{
private static final int BUF_SIZE = 128;
S source;
AtomicReference<T> accRef;
T acc = none();
int idx = 0;
final BinaryOperator<T> op;
PrefixOps(S source, BinaryOperator<T> op) {
this.source = source;
this.op = op;
}
@Override
public Spliterator<T> trySplit() {
if(acc != NONE) {
return null;
}
@SuppressWarnings("unchecked")
S prefix = (S) source.trySplit();
if(prefix == null) {
return null;
}
if(accRef == null) {
accRef = new AtomicReference<>(none());
}
PrefixOps<T, S> pref = doClone();
pref.source = prefix;
return pref;
}
@Override
public long estimateSize() {
return source.estimateSize();
}
@Override
public int characteristics() {
return source.characteristics() & (ORDERED | IMMUTABLE | CONCURRENT | SIZED | SUBSIZED);
}
static final class OfRef<T> extends AbstractSpliterator<T> implements Consumer<T> {
private final BinaryOperator<T> op;
private final Spliterator<T> source;
private boolean started;
private T acc;
OfRef(Spliterator<T> source, BinaryOperator<T> op) {
super(source.estimateSize(), source.characteristics() & (ORDERED | IMMUTABLE | CONCURRENT | SIZED));
this.source = source;
this.op = op;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if(!source.tryAdvance(this))
return false;
action.accept(acc);
return true;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
source.forEachRemaining(next -> {
this.accept(next);
action.accept(acc);
});
}
@Override
public void accept(T next) {
if(started) {
acc = op.apply(acc, next);
} else {
started = true;
acc = next;
}
}
}
static final class OfInt extends AbstractIntSpliterator implements IntConsumer {
private final IntBinaryOperator op;
private final Spliterator.OfInt source;
private boolean started;
private int acc;
OfInt(Spliterator.OfInt source, IntBinaryOperator op) {
super(source.estimateSize(), source.characteristics() & (ORDERED | IMMUTABLE | CONCURRENT | SIZED | NONNULL));
this.source = source;
this.op = op;
}
@Override
public boolean tryAdvance(IntConsumer action) {
if(!source.tryAdvance(this))
return false;
action.accept(acc);
return true;
}
@Override
public void forEachRemaining(IntConsumer action) {
source.forEachRemaining((int next) -> {
this.accept(next);
action.accept(acc);
});
}
@Override
public void accept(int next) {
if(started) {
acc = op.applyAsInt(acc, next);
} else {
started = true;
acc = next;
}
}
}
static final class OfLong extends AbstractLongSpliterator implements LongConsumer {
private final LongBinaryOperator op;
private final Spliterator.OfLong source;
private boolean started;
private long acc;
OfLong(Spliterator.OfLong source, LongBinaryOperator op) {
super(source.estimateSize(), source.characteristics() & (ORDERED | IMMUTABLE | CONCURRENT | SIZED | NONNULL));
this.source = source;
this.op = op;
}
@Override
public boolean tryAdvance(LongConsumer action) {
if(!source.tryAdvance(this))
return false;
action.accept(acc);
return true;
}
@Override
public void forEachRemaining(LongConsumer action) {
source.forEachRemaining((long next) -> {
this.accept(next);
action.accept(acc);
});
}
@Override
public void accept(long next) {
if(started) {
acc = op.applyAsLong(acc, next);
} else {
started = true;
acc = next;
}
}
}
static final class OfDouble extends AbstractDoubleSpliterator implements DoubleConsumer {
private final DoubleBinaryOperator op;
private final Spliterator.OfDouble source;
private boolean started;
private double acc;
OfDouble(Spliterator.OfDouble source, DoubleBinaryOperator op) {
super(source.estimateSize(), source.characteristics() & (ORDERED | IMMUTABLE | CONCURRENT | SIZED | NONNULL));
this.source = source;
this.op = op;
}
@Override
public boolean tryAdvance(DoubleConsumer action) {
if(!source.tryAdvance(this))
return false;
action.accept(acc);
return true;
}
@Override
public void forEachRemaining(DoubleConsumer action) {
source.forEachRemaining((double next) -> {
this.accept(next);
action.accept(acc);
});
}
@Override
public void accept(double next) {
if(started) {
acc = op.applyAsDouble(acc, next);
} else {
started = true;
acc = next;
}
}
}
static final class OfUnordRef<T> extends PrefixOps<T, Spliterator<T>> implements Consumer<T> {
private final BinaryOperator<T> localOp;
OfUnordRef(Spliterator<T> source, BinaryOperator<T> op) {
super(source, (a, b) -> a == NONE ? b : op.apply(a, b));
this.localOp = op;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if(!source.tryAdvance(this)) {
return false;
}
action.accept(acc);
return true;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
if(accRef == null) {
source.forEachRemaining(next -> action.accept(acc = op.apply(acc, next)));
} else {
@SuppressWarnings("unchecked")
T[] buf = (T[]) new Object[BUF_SIZE];
source.forEachRemaining(next -> {
if(idx == 0) {
buf[idx++] = next;
} else {
T prev = buf[idx-1];
buf[idx++] = localOp.apply(prev, next);
if(idx == buf.length) {
drain(action, buf);
idx = 0;
}
}
});
if(idx > 0)
drain(action, buf);
}
}
private void drain(Consumer<? super T> action, T[] buf) {
T last = buf[idx-1];
T acc = accRef.getAndAccumulate(last, op);
if(acc != NONE) {
for(int i=0; i<idx; i++) {
action.accept(localOp.apply(buf[i], acc));
}
} else {
for(int i=0; i<idx; i++) {
action.accept(buf[i]);
}
}
}
@Override
public void accept(T next) {
if(accRef == null) {
acc = op.apply(acc, next);
} else {
acc = accRef.accumulateAndGet(next, op);
}
}
}
}