/* * 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 java.util.List; import java.util.Spliterator; import java.util.function.BiFunction; import java.util.function.Consumer; import one.util.streamex.StreamExInternals.CloneableSpliterator; /** * @author Tagir Valeev */ /* package */final class PairPermutationSpliterator<T, R> extends CloneableSpliterator<R, PairPermutationSpliterator<T, R>> { private long cur; private long limit; private final int size; private int idx1; private int idx2; private final List<T> list; private final BiFunction<? super T, ? super T, ? extends R> mapper; public PairPermutationSpliterator(List<T> list, BiFunction<? super T, ? super T, ? extends R> mapper) { this.list = list; this.size = list.size(); this.idx2 = 1; this.limit = size * (size - 1L) / 2; this.mapper = mapper; } @Override public long estimateSize() { return limit - cur; } @Override public int characteristics() { return ORDERED | SIZED | SUBSIZED; } /* * Calculates (int) (Math.sqrt(8 * n + 1)-1)/2 Produces exact result for any * long input from 0 to 0x1FFFFFFFC0000000L (2^61-2^30). */ static int isqrt(long n) { int x = (int) ((Math.sqrt(8.0 * n + 1.0) - 1.0) / 2.0); if (x * (x + 1L) / 2 > n) x--; return x; } @Override public Spliterator<R> trySplit() { long size = limit - cur; if (size >= 2) { PairPermutationSpliterator<T, R> clone = doClone(); clone.limit = this.cur = this.cur + size / 2; int s = this.size; long rev = s * (s - 1L) / 2 - this.cur - 1; int row = isqrt(rev); int col = (int) (rev - (row) * (row + 1L) / 2); this.idx1 = s - row - 2; this.idx2 = s - col - 1; return clone; } return null; } @Override public boolean tryAdvance(Consumer<? super R> action) { if (cur == limit) return false; action.accept(mapper.apply(list.get(idx1), list.get(idx2))); cur++; if (++idx2 == size) { idx2 = ++idx1 + 1; } return true; } @Override public void forEachRemaining(Consumer<? super R> action) { int idx1 = this.idx1; int idx2 = this.idx2; int size = this.size; long cur = this.cur; long limit = this.limit; while (cur < limit) { T item1 = list.get(idx1++); while (cur < limit && idx2 < size) { T item2 = list.get(idx2++); action.accept(mapper.apply(item1, item2)); cur++; } idx2 = idx1 + 1; } this.cur = this.limit; } }