blob: fcfe4f6248e583c679ca1d5b7fee29ccd2bad162 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 University of Illinois at Urbana-Champaign and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.rephraserengine.testing.combinatorial;
import java.util.Iterator;
/**
* Generates all combinations of non-negative integers within specified limits.
* <p>
* If a {@link CombinationGenerator} is constructed with parameters (5, 2), it will generate all
* combinations of up to two integers, each in the range 0..4. Successive calls to
* {@link #nextCombination(int[])} will return the following int arrays:
* <pre>
* 0
* 1
* 2
* 3
* 4
* 0 1
* 0 2
* 0 3
* 0 4
* 1 2
* 1 3
* 1 4
* 2 3
* 2 4
* 3 4
* null
* </pre>
*
* @author Jeff Overbey
*
* @since 2.0
*/
public class CombinationGenerator implements Iterable<int[]>
{
private int maxValuePlusOne, maxItems;
public CombinationGenerator(int maxValuePlusOne, int maxItems)
{
this.maxValuePlusOne = maxValuePlusOne;
this.maxItems = Math.min(maxItems, maxValuePlusOne);
}
/**
* Returns the first combination of integers. This is always {0}.
* <p>
* It can be passed to {@link #nextCombination(int[])} to generate the next combination.
*
* @return the first combination of integers (always {0})
* */
public int[] firstCombination()
{
return new int[] { 0 };
}
/**
* Returns the first combination of <i>n</i> integers. This is always {0, 1, ..., n-1}.
* <p>
* It can be passed to {@link #nextCombination(int[])} to generate the next combination.
*
* @return the first combination of <i>n</i> integers (always {0, 1, ..., n-1})
* */
public int[] firstCombination(int n)
{
int[] result = new int[n];
for (int i = 0; i < n; i++)
result[i] = i;
return result;
}
/**
* Returns the next combination of integers (according to the parameters pass to this
* {@link CombinationGenerator}'s constructor), given the previous combination returned by this
* method or by {@link #firstCombination()}. <i>Note that the <code>lastCombination</code>
* array is modified in-place.</i>
*
* @param lastCombination an array returned by {@link #firstCombination()} or
* {@link #nextCombination(int[])}
*
* @return a combination of integers, or <code>null</code> if there are no more combinations to
* generate
*/
public int[] nextCombination(int[] lastCombination)
{
if (lastCombination == null) return null;
int indexToIncrement = findIndexToIncrement(lastCombination);
if (indexToIncrement < 0)
{
return increaseLength(lastCombination);
}
else
{
incrementIndex(indexToIncrement, lastCombination);
return lastCombination;
}
}
private int findIndexToIncrement(int[] configuration)
{
int indexToIncrement = configuration.length - 1;
// The goal is to do something like this:
// if (configuration[7] == maxValue-1)
// indexToIncrement = 6;
// if (configuration[6] == maxValue-2)
// indexToIncrement = 5;
for (int i = configuration.length - 1; i >= 0; i--)
{
if (configuration[i] == maxValuePlusOne - (configuration.length - i))
{
indexToIncrement--;
}
}
return indexToIncrement;
}
private void incrementIndex(int indexToIncrement, int[] configuration)
{
configuration[indexToIncrement]++;
for (int i = indexToIncrement + 1; i < configuration.length; i++)
configuration[i] = configuration[i - 1] + 1;
}
private int[] increaseLength(int[] configuration)
{
if (configuration.length == maxItems) return null;
int[] newConfig = new int[configuration.length + 1];
for (int i = 0; i < newConfig.length; i++)
newConfig[i] = i;
return newConfig;
}
/* @see java.lang.Iterable#iterator() */
public Iterator<int[]> iterator()
{
return new Iterator<int[]>()
{
private int[] nextCombination = firstCombination();
public boolean hasNext()
{
return nextCombination != null;
}
public int[] next()
{
int[] result = nextCombination.clone();
nextCombination = nextCombination(nextCombination);
return result;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
}