package one.util.streamex; import java.util.Spliterator; import java.util.function.Consumer; /* package */ final class PermutationSpliterator implements Spliterator<int[]> { private static final long[] factorials = { 1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L, 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L, 2432902008176640000L }; private final int[] value; private long remainingSize; private final long fence; public PermutationSpliterator(int length) { if (length < 0) throw new IllegalArgumentException("Length must be non-negative"); if (length >= factorials.length) throw new IllegalArgumentException("Length " + length + " is bigger than " + factorials.length + ": not supported"); this.value = new int[length]; for (int i = 0; i < length; i++) this.value[i] = i; this.fence = this.remainingSize = factorials[length]; } private PermutationSpliterator(int[] startValue, long fence, long remainingSize) { this.value = startValue; this.fence = fence; this.remainingSize = remainingSize; } @Override public boolean tryAdvance(Consumer<? super int[]> action) { if (remainingSize == 0) return false; int[] value = this.value; action.accept(value.clone()); if (--remainingSize > 0) { step(value); } return true; } @Override public void forEachRemaining(Consumer<? super int[]> action) { long rs = remainingSize; if (rs == 0) return; remainingSize = 0; int[] value = this.value; action.accept(value.clone()); while (--rs > 0) { step(value); action.accept(value.clone()); } } private static void step(int[] value) { int r = value.length - 1, k = r - 1; while (value[k] > value[k + 1]) k--; int vk = value[k], l = r; while (vk > value[l]) l--; value[k] = value[l]; value[l] = vk; for (k++; k < r; k++, r--) { int tmp = value[k]; value[k] = value[r]; value[r] = tmp; } } @Override public Spliterator<int[]> trySplit() { if (remainingSize <= 1) return null; int[] newValue = value.clone(); long used = -1L; // clear bit = used position long newRemainingSize = remainingSize / 2; long newPos = fence - (remainingSize -= newRemainingSize); long s = newPos; for (int i = 0; i < value.length; i++) { long f = factorials[value.length - i - 1]; int rem = (int) (s / f); s %= f; int idx = -1; while (rem >= 0) { idx = Long.numberOfTrailingZeros(used >> (idx + 1)) + idx + 1; rem--; } used &= ~(1 << idx); value[i] = idx; } return new PermutationSpliterator(newValue, newPos, newRemainingSize); } @Override public long estimateSize() { return remainingSize; } @Override public int characteristics() { return ORDERED | DISTINCT | NONNULL | IMMUTABLE | SIZED | SUBSIZED; } }