com.landawn.abacus.util.PermutationIterator Maven / Gradle / Ivy
Show all versions of abacus-common Show documentation
/*
* Copyright (C) 2008 The Guava Authors
*
* 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 com.landawn.abacus.util;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import com.landawn.abacus.util.stream.ObjIteratorEx;
/**
* Copied from Google Guava.
*
* Provides static methods for working with {@code Collection} instances.
*
* @author Chris Povirk
* @author Mike Bostock
* @author Jared Levy
* @since 2.0
*/
public final class PermutationIterator {
private PermutationIterator() {
}
/**
* Returns a {@link Collection} of all the permutations of the specified
* {@link Collection}.
*
* Notes: This is an implementation of the Plain Changes algorithm
* for permutations generation, described in Knuth's "The Art of Computer
* Programming", Volume 4, Chapter 7, Section 7.2.1.2.
*
*
If the input list contains equal elements, some of the generated
* permutations will be equal.
*
*
An empty collection has only one permutation, which is an empty list.
*
* @param
* @param elements the original collection whose elements have to be permuted.
* @return an immutable {@link Collection} containing all the different
* permutations of the original collection.
* @throws NullPointerException if the specified collection is null or has any
* null elements.
* @since 12.0
*/
public static ObjIterator> of(final Collection elements) {
return new ObjIteratorEx<>() {
final T[] items = (T[]) elements.toArray();
final int[] c = Array.repeat(0, items.length);
final int[] o = Array.repeat(1, items.length);
int j = Integer.MAX_VALUE;
int hasNext = items.length == 0 ? -1 : 1; // 0 = read; 1 = yes, -1 = done.
@Override
public boolean hasNext() {
switch (hasNext) {
case -1:
return false;
case 1:
return true;
default:
computeNext();
}
return hasNext == 1;
}
@Override
public List next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
hasNext = 0;
return N.asList(items);
}
private void computeNext() {
j = items.length - 1;
int s = 0;
while (true) {
int q = c[j] + o[j];
if (q < 0) {
switchDirection();
continue;
}
if (q == j + 1) {
if (j == 0) {
break;
}
s++;
switchDirection();
continue;
}
N.swap(items, j - c[j] + s, j - q + s);
c[j] = q;
break;
}
hasNext = j <= 0 ? -1 : 1;
}
private void switchDirection() {
o[j] = -o[j];
j--;
}
};
}
/**
* Returns a {@link Collection} of all the permutations of the specified
* {@link Iterable}.
*
* Notes: This is an implementation of the algorithm for
* Lexicographical Permutations Generation, described in Knuth's "The Art of
* Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The
* iteration order follows the lexicographical order. This means that
* the first permutation will be in ascending order, and the last will be in
* descending order.
*
*
Duplicate elements are considered equal. For example, the list [1, 1]
* will have only one permutation, instead of two. This is why the elements
* have to implement {@link Comparable}.
*
*
An empty iterable has only one permutation, which is an empty list.
*
*
This method is equivalent to
* {@code Collections2.orderedPermutations(list, Ordering.natural())}.
*
* @param
* @param elements the original iterable whose elements have to be permuted.
* @return an immutable {@link Collection} containing all the different
* permutations of the original iterable.
* @throws NullPointerException if the specified iterable is null or has any
* null elements.
* @since 12.0
*/
public static > ObjIterator> ordered(Collection elements) {
return ordered(elements, Comparators.naturalOrder());
}
/**
* Returns a {@link Collection} of all the permutations of the specified
* {@link Iterable} using the specified {@link Comparator} for establishing
* the lexicographical ordering.
*
* Examples:
{@code
*
* for (List perm : orderedPermutations(asList("b", "c", "a"))) {
* println(perm);
* }
* // -> ["a", "b", "c"]
* // -> ["a", "c", "b"]
* // -> ["b", "a", "c"]
* // -> ["b", "c", "a"]
* // -> ["c", "a", "b"]
* // -> ["c", "b", "a"]
*
* for (List perm : orderedPermutations(asList(1, 2, 2, 1))) {
* println(perm);
* }
* // -> [1, 1, 2, 2]
* // -> [1, 2, 1, 2]
* // -> [1, 2, 2, 1]
* // -> [2, 1, 1, 2]
* // -> [2, 1, 2, 1]
* // -> [2, 2, 1, 1]}
*
* Notes: This is an implementation of the algorithm for
* Lexicographical Permutations Generation, described in Knuth's "The Art of
* Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The
* iteration order follows the lexicographical order. This means that
* the first permutation will be in ascending order, and the last will be in
* descending order.
*
*
Elements that compare equal are considered equal and no new permutations
* are created by swapping them.
*
*
An empty iterable has only one permutation, which is an empty list.
*
* @param
* @param elements the original iterable whose elements have to be permuted.
* @param comparator a comparator for the iterable's elements.
* @return an immutable {@link Collection} containing all the different
* permutations of the original iterable.
* @throws NullPointerException If the specified iterable is null, has any
* null elements, or if the specified comparator is null.
* @since 12.0
*/
public static ObjIterator> ordered(final Collection elements, final Comparator super T> comparator) {
return new ObjIteratorEx<>() {
T[] next = (T[]) elements.toArray();
{
N.sort(next, comparator);
}
int hasNext = next.length == 0 ? -1 : 1; // 0 = read; 1 = yes, -1 = done.
@Override
public boolean hasNext() {
switch (hasNext) {
case -1:
return false;
case 1:
return true;
default:
computeNext();
}
return hasNext == 1;
}
@Override
public List next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
hasNext = 0;
return N.asList(next);
}
private void computeNext() {
if (next == null) {
hasNext = -1;
return;
}
int j = findNextJ();
if (j == -1) {
next = null;
hasNext = -1;
return;
}
int l = findNextL(j);
N.swap(next, j, l);
int n = next.length;
N.reverse(next, j + 1, n);
hasNext = 1;
}
private int findNextJ() {
for (int k = next.length - 2; k >= 0; k--) {
if (comparator.compare(next[k], next[k + 1]) < 0) {
return k;
}
}
return -1;
}
private int findNextL(int j) {
T ak = next[j];
for (int l = next.length - 1; l > j; l--) {
if (comparator.compare(ak, next[l]) < 0) {
return l;
}
}
throw new AssertionError("this statement should be unreachable");
}
};
}
}