/*******************************************************************************
* $Id: $
* Copyright (c) 2009-2010 Tim Tiemens.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
*
* Contributors:
* Tim Tiemens - initial API and implementation
******************************************************************************/
package com.aegiswallet.helpers.secretshare.math;
import com.aegiswallet.helpers.secretshare.SecretShareException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class CombinationGenerator<E>
implements Iterator<List<E>>,
Iterable<List<E>> {
// ==================================================
// class static data
// ==================================================
// ==================================================
// class static methods
// ==================================================
public static void main(String[] args) {
System.out.println("fact(5)=" + factorial(5));
System.out.println("fact(3)=" + factorial(3));
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
CombinationGenerator<String> combos = new CombinationGenerator<String>(list, 3);
int count = 1;
System.out.println("Total number=" + combos.getTotalNumberOfCombinations());
for (List<String> combination : combos) {
System.out.println(count + ": " + combination + " {" + combos.indexesAsString + "}");
count++;
}
}
// ==================================================
// instance data
// ==================================================
private final List<E> list;
// currentIndexes contains the indexes to use for the NEXT iteration
private int[] currentIndexes;
// indexesAsString contains either null or the CURRENT iteration's indexes
private String indexesAsString;
//
private final BigInteger totalNumberOfCombinations;
// ranges 0 to totalNumber, where "0" means "you haven't called next() yet"
private BigInteger combinationNumber;
// ==================================================
// factories
// ==================================================
// ==================================================
// constructors
// ==================================================
public CombinationGenerator(final List<E> inList,
final int inChoiceSize) {
if (inChoiceSize < 1) {
throw new SecretShareException("choice size cannot be less than 1:" + inChoiceSize);
}
if (inChoiceSize > inList.size()) {
throw new SecretShareException("choice size cannot be greater than size");
}
List<E> ourlist = new ArrayList<E>(inList);
this.list = Collections.unmodifiableList(ourlist);
this.currentIndexes = new int[inChoiceSize];
for (int i = 0; i < inChoiceSize; i++) {
this.currentIndexes[i] = i;
}
totalNumberOfCombinations = computeNfactdivkNkFact(this.list.size(), inChoiceSize);
combinationNumber = BigInteger.ZERO;
}
/**
* Return (n!) / ( k! * (n-k)! ).
*
* @param n
* @param k
* @return
*/
private BigInteger computeNfactdivkNkFact(final int n,
final int k) {
final int nminusk = n - k;
BigInteger kfactTimesnmkfact = factorial(k).multiply(factorial(nminusk));
BigInteger nfactorial = factorial(n);
return nfactorial.divide(kfactTimesnmkfact);
}
// ==================================================
// public methods
// ==================================================
public final BigInteger getCurrentCombinationNumber() {
return combinationNumber;
}
public final BigInteger getTotalNumberOfCombinations() {
return totalNumberOfCombinations;
}
@Override
public Iterator<List<E>> iterator() {
return this;
}
@Override
public boolean hasNext() {
return (currentIndexes != null);
}
public String getIndexesAsString() {
return indexesAsString;
}
@Override
public List<E> next() {
if (!hasNext()) {
// ouch - Java's exception type design makes this a hard choice:
// It would be nice to throw new SecretShareException() here.
throw new NoSuchElementException();
}
combinationNumber = combinationNumber.add(BigInteger.ONE);
List<E> currentCombination = new ArrayList<E>();
for (int i : currentIndexes) {
currentCombination.add(list.get(i));
}
// capture before moving the indexes:
indexesAsString = Arrays.toString(currentIndexes);
moveIndexesToNextCombination();
return currentCombination;
}
public void remove() {
// ouch again
throw new UnsupportedOperationException();
}
// ==================================================
// private methods
// ==================================================
private void moveIndexesToNextCombination() {
for (int i = currentIndexes.length - 1, j = list.size() - 1; i >= 0; i--, j--) {
if (currentIndexes[i] != j) {
currentIndexes[i]++;
for (int k = i + 1; k < currentIndexes.length; k++) {
currentIndexes[k] = currentIndexes[k - 1] + 1;
}
return;
}
}
// otherwise, we are all done:
currentIndexes = null;
}
// int i = r - 1;
// while (a[i] == (n - r + i))
// {
// i--;
// }
// a[i] = a[i] + 1;
// for (int j = i + 1; j < r; j++)
// {
// a[j] = a[i] + j - i;
// }
private static BigInteger factorial(int n) {
BigInteger ret = BigInteger.ONE;
for (int i = n; i > 1; i--) {
ret = ret.multiply(BigInteger.valueOf(i));
}
return ret;
}
}