package com.codepoetics.protonpack;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.util.function.Predicate.isEqual;
class ListZippingSpliterator<T, O> implements Spliterator<O> {
static <T, O> Spliterator<O> zipping(List<Spliterator<T>> spliterators, Function<List<T>, O> combiner) {
return new ListZippingSpliterator<>(spliterators, combiner);
}
private final List<Spliterator<T>> spliterators;
private final Function<List<T>, O> combiner;
private ListZippingSpliterator(List<Spliterator<T>> spliterators, Function<List<T>, O> combiner) {
this.spliterators = spliterators;
this.combiner = combiner;
}
@Override
public boolean tryAdvance(Consumer<? super O> action) {
if (spliterators.isEmpty()) {
return false;
}
List<T> acc = new ArrayList<>(spliterators.size());
boolean hadNext = spliterators.stream()
.map(s -> s.tryAdvance(acc::add))
.allMatch(isEqual(Boolean.TRUE));
if (hadNext) {
action.accept(combiner.apply(acc));
}
return hadNext;
}
@Override
public Spliterator<O> trySplit() {
return null;
}
@Override
public long estimateSize() {
// TODO: benchmark and cache?
return spliterators.stream()
.mapToLong(Spliterator::estimateSize)
.min().orElse(0);
}
@Override
public int characteristics() {
// TODO: benchmark and cache?
int characteristics = spliterators.stream()
.mapToInt(Spliterator::characteristics)
.reduce(0, (i, j) -> i & j);
return characteristics & ~(Spliterator.DISTINCT | Spliterator.SORTED);
}
}