blob: 5275fa89bfb29a9508ca56608a647d3cf94e574b [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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 org.onosproject.pi.demo.app.tor;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.math.IntMath;
import java.util.AbstractSet;
import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Combination algorithm implementation, taken from Guava 23.
*/
final class Combination {
private Combination() {
// Hides constructor.
}
/**
* Returns a map from the ith element of list to i.
*/
private static <E> ImmutableMap<E, Integer> indexMap(Collection<E> list) {
ImmutableMap.Builder<E, Integer> builder = ImmutableMap.builder();
int i = 0;
for (E e : list) {
builder.put(e, i++);
}
return builder.build();
}
/**
* Returns the set of all subsets of {@code set} of size {@code size}. For example, {@code
* combinations(ImmutableSet.of(1, 2, 3), 2)} returns the set {@code {{1, 2}, {1, 3}, {2, 3}}}.
* <p>
* <p>Elements appear in these subsets in the same iteration order as they appeared in the input set. The order in
* which these subsets appear in the outer set is undefined.
* <p>
* <p>The returned set and its constituent sets use {@code equals} to decide whether two elements are identical,
* even if the input set uses a different concept of equivalence.
* <p>
* <p><i>Performance notes:</i> the memory usage of the returned set is only {@code O(n)}. When the result set is
* constructed, the input set is merely copied. Only as the result set is iterated are the individual subsets
* created. Each of these subsets occupies an additional O(n) memory but only for as long as the user retains a
* reference to it. That is, the set returned by {@code combinations} does not retain the individual subsets.
*
* @param set the set of elements to take combinations of
* @param size the number of elements per combination
* @return the set of all combinations of {@code size} elements from {@code set}
* @throws IllegalArgumentException if {@code size} is not between 0 and {@code set.size()} inclusive
* @throws NullPointerException if {@code set} is or contains {@code null}
* @since 23.0
*/
static <E> Set<Set<E>> combinations(Set<E> set, final int size) {
final ImmutableMap<E, Integer> index = indexMap(set);
checkArgument(size > 0, "size");
checkArgument(size <= index.size(), "size (%s) must be <= set.size() (%s)", size, index.size());
if (size == 0) {
return ImmutableSet.of(ImmutableSet.<E>of());
} else if (size == index.size()) {
return ImmutableSet.of(index.keySet());
}
return new AbstractSet<Set<E>>() {
@Override
public boolean contains(Object o) {
if (o instanceof Set) {
Set<?> s = (Set<?>) o;
return s.size() == size && index.keySet().containsAll(s);
}
return false;
}
@Override
public Iterator<Set<E>> iterator() {
return new AbstractIterator<Set<E>>() {
final BitSet bits = new BitSet(index.size());
@Override
protected Set<E> computeNext() {
if (bits.isEmpty()) {
bits.set(0, size);
} else {
int firstSetBit = bits.nextSetBit(0);
int bitToFlip = bits.nextClearBit(firstSetBit);
if (bitToFlip == index.size()) {
return endOfData();
}
/*
* The current set in sorted order looks like
* {firstSetBit, firstSetBit + 1, ..., bitToFlip - 1, ...}
* where it does *not* contain bitToFlip.
*
* The next combination is
*
* {0, 1, ..., bitToFlip - firstSetBit - 2, bitToFlip, ...}
*
* This is lexicographically next if you look at the combinations in descending order
* e.g. {2, 1, 0}, {3, 1, 0}, {3, 2, 0}, {3, 2, 1}, {4, 1, 0}...
*/
bits.set(0, bitToFlip - firstSetBit - 1);
bits.clear(bitToFlip - firstSetBit - 1, bitToFlip);
bits.set(bitToFlip);
}
final BitSet copy = (BitSet) bits.clone();
return new AbstractSet<E>() {
@Override
public boolean contains(Object o) {
Integer i = index.get(o);
return i != null && copy.get(i);
}
@Override
public Iterator<E> iterator() {
return new AbstractIterator<E>() {
int i = -1;
@Override
protected E computeNext() {
i = copy.nextSetBit(i + 1);
if (i == -1) {
return endOfData();
}
return index.keySet().asList().get(i);
}
};
}
@Override
public int size() {
return size;
}
};
}
};
}
@Override
public int size() {
return IntMath.binomial(index.size(), size);
}
@Override
public String toString() {
return "Sets.combinations(" + index.keySet() + ", " + size + ")";
}
};
}
}