ninja.leaping.permissionsex.util.Combinations.java Source code

Java tutorial

Introduction

Here is the source code for ninja.leaping.permissionsex.util.Combinations.java

Source

/**
 * PermissionsEx
 * Copyright (C) zml and PermissionsEx contributors
 *
 * 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 ninja.leaping.permissionsex.util;

import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.PeekingIterator;

import java.util.Iterator;
import java.util.Set;

/**
 * An iterable class that provides all combinations of any of a given set of values
 * The algorithm used is a version of Algorithm T in section 7.2.1.3 of Knuth's The Art of Computer Programming,
 * modified to function as an Iterator. This implementation also begins with the original set,
 * and accepts the empty set as an input (unlike the original algorithm, only defined for n>0 where n is the length of the set).
 */
public class Combinations<T> implements Iterable<Set<T>> {
    private final T[] items;

    @SuppressWarnings("unchecked")
    private Combinations(Set<T> items) {
        this.items = (T[]) items.toArray();
    }

    public static <T> Combinations<T> of(Set<T> items) {
        return new Combinations<>(ImmutableSet.copyOf(items));
    }

    private class CombinationIterator extends AbstractIterator<Set<T>> implements PeekingIterator<Set<T>> {
        private int j;
        private int currentLength = items.length;
        private int[] c; // Reuse the same array -- it isn't getting any bigger

        private void init() {
            for (j = 0; j < currentLength; ++j) {
                c[j] = j;
            }
            c[currentLength] = items.length;
            c[currentLength + 1] = 0;
            j = currentLength - 1;
        }

        @Override
        protected Set<T> computeNext() {
            if (currentLength < 0) {
                return endOfData();
            }

            if (c == null) {
                c = new int[currentLength + 2];
                init();
            } else {
                if (c[0] + 1 < c[1]) {
                    c[0] = c[0] + 1;
                } else {
                    int x;
                    j = 1;
                    do {
                        ++j;
                        c[j - 2] = j - 2;
                        x = c[j - 1] + 1;
                    } while (x == c[j]);

                    if (j > currentLength) {
                        currentLength--;
                        init();
                    } else {
                        c[j - 1] = x;
                        j--;
                    }
                }
            }

            if (currentLength == 0) {
                --currentLength;
                return ImmutableSet.of();
            } else {
                ImmutableSet.Builder<T> build = ImmutableSet.builder();
                for (int i = 0; i < currentLength; ++i) {
                    build.add(items[c[i]]);
                }
                return build.build();
            }
        }
    }

    @Override
    public Iterator<Set<T>> iterator() {
        return new CombinationIterator();
    }
}