/******************************************************************************* * Copyright 2015 Analog Devices, Inc. * * 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.analog.lyric.collect; import java.lang.reflect.Array; import java.util.Arrays; import com.google.common.collect.UnmodifiableIterator; /** * Iterates through all combinations of arrays of elements. * <p> * Given an array of arrays defining the possible elements for position of the output array, * this will iterate over every combination of the elements. * <p> * For example, the following code: * <pre> * String[] first = new String[] { "a", "b" }; * String[] second = new String[] { "x", "y", "z" }; * CombinatoricIterator<String> iter = new CombinatoricIterator<>(String.class, first, second); * while (iter.hasNext()) * { * System.out.format("%s\n", Arrays.toString(iter.next()); * } * </pre> * will produce the output: * <pre> * [a, x] * [b, x] * [a, y] * [b, y] * [a, z] * [b, z] * </pre> * Unlike most iterators, this always returns the same int[] instance, so if you want * a fresh copy, you must make sure to clone it. * <p> * @since 0.08 * @author Christopher Barber */ public class CombinatoricIterator<T> extends UnmodifiableIterator<T[]> { /*------- * State */ /** * The elements that will be combinatorially explored. */ private final T[][] _elements; private final int[] _indices; private final T[] _values; private final int[] _limits; private int _lastNotAtLimit; /*-------------- * Construction */ @SuppressWarnings("unchecked") public CombinatoricIterator(Class<T> elementType, T[] ... elements) { final int dimensions = elements.length; _elements = elements; _indices = new int[dimensions]; _limits = new int[dimensions]; _values = (T[]) Array.newInstance(elementType, dimensions); for (int i = 0; i < dimensions; ++i) { _limits[i] = elements[i].length - 1; } reset(); } /*------------------ * Iterator methods */ @Override public boolean hasNext() { return _lastNotAtLimit >= 0; } /** * Returns the next combination of values. * <p> * Note that this will always return the same array object to avoid * allocation. So if you need a fresh copy, you must explicitly clone it! */ @Override public T[] next() { final int[] indices = _indices; final int[] limits = _limits; final T[] values = _values; final T[][] elements = _elements; final int lastNotAtLimit = _lastNotAtLimit; for (int i = 0; i <= lastNotAtLimit; ++i) { final int limit = limits[i]; int val = indices[i] + 1; if (val <= limit) { indices[i] = val; values[i] = elements[i][val]; if (val == limit && i == lastNotAtLimit) { while (--i >= 0 && indices[i] == limits[i]) {} _lastNotAtLimit = i; } break; } else // val > limit { indices[i] = 0; values[i] = elements[i][0]; } } return values; } /*------------------------------ * CombinatoricIterator methods */ /** * Returns the underlying elements passed to the constructor. * @since 0.08 */ public T[][] elements() { return _elements; } /** * Returns the last set of indices for the values returned by {@link #next}. * <p> * This returns the actual underlying array used by this object. * @since 0.08 */ public int[] indices() { return _indices; } /** * Reset position of iterator back to beginning. */ public void reset() { Arrays.fill(_indices, 0); _indices[0] = -1; _lastNotAtLimit = _indices.length - 1; while (_lastNotAtLimit > 0 && _limits[_lastNotAtLimit] == 0) --_lastNotAtLimit; for (int i = _values.length; --i >=0; ) { _values[i] = _elements[i][0]; } } }