All Downloads are FREE. Search and download functionalities are using the official Maven repository.

one.util.streamex.PermutationSpliterator Maven / Gradle / Ivy

There is a newer version: 0.8.3
Show newest version
package one.util.streamex;

import java.util.Spliterator;
import java.util.function.Consumer;

/* package */ final class PermutationSpliterator implements Spliterator {
    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 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 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 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;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy