/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.math3.util;

import java.util.NoSuchElementException;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.NotStrictlyPositiveException;
import org.apache.commons.math3.exception.OutOfRangeException;

Converter between unidimensional storage structure and multidimensional conceptual structure. This utility will convert from indices in a multidimensional structure to the corresponding index in a one-dimensional array. For example, assuming that the ranges (in 3 dimensions) of indices are 2, 4 and 3, the following correspondences, between 3-tuples indices and unidimensional indices, will hold:
  • (0, 0, 0) corresponds to 0
  • (0, 0, 1) corresponds to 1
  • (0, 0, 2) corresponds to 2
  • (0, 1, 0) corresponds to 3
  • ...
  • (1, 0, 0) corresponds to 12
  • ...
  • (1, 3, 2) corresponds to 23
Since:2.2
/** * Converter between unidimensional storage structure and multidimensional * conceptual structure. * This utility will convert from indices in a multidimensional structure * to the corresponding index in a one-dimensional array. For example, * assuming that the ranges (in 3 dimensions) of indices are 2, 4 and 3, * the following correspondences, between 3-tuples indices and unidimensional * indices, will hold: * <ul> * <li>(0, 0, 0) corresponds to 0</li> * <li>(0, 0, 1) corresponds to 1</li> * <li>(0, 0, 2) corresponds to 2</li> * <li>(0, 1, 0) corresponds to 3</li> * <li>...</li> * <li>(1, 0, 0) corresponds to 12</li> * <li>...</li> * <li>(1, 3, 2) corresponds to 23</li> * </ul> * * @since 2.2 */
public class MultidimensionalCounter implements Iterable<Integer> {
Number of dimensions.
/** * Number of dimensions. */
private final int dimension;
Offset for each dimension.
/** * Offset for each dimension. */
private final int[] uniCounterOffset;
Counter sizes.
/** * Counter sizes. */
private final int[] size;
Total number of (one-dimensional) slots.
/** * Total number of (one-dimensional) slots. */
private final int totalSize;
Index of last dimension.
/** * Index of last dimension. */
private final int last;
Perform iteration over the multidimensional counter.
/** * Perform iteration over the multidimensional counter. */
public class Iterator implements java.util.Iterator<Integer> {
Multidimensional counter.
/** * Multidimensional counter. */
private final int[] counter = new int[dimension];
Unidimensional counter.
/** * Unidimensional counter. */
private int count = -1;
Maximum value for count.
/** * Maximum value for {@link #count}. */
private final int maxCount = totalSize - 1;
Create an iterator
See Also:
  • iterator()
/** * Create an iterator * @see #iterator() */
Iterator() { counter[last] = -1; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean hasNext() { return count < maxCount; }
Throws:
Returns:the unidimensional count after the counter has been incremented by 1.
/** * @return the unidimensional count after the counter has been * incremented by {@code 1}. * @throws NoSuchElementException if {@link #hasNext()} would have * returned {@code false}. */
public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } for (int i = last; i >= 0; i--) { if (counter[i] == size[i] - 1) { counter[i] = 0; } else { ++counter[i]; break; } } return ++count; }
Get the current unidimensional counter slot.
Returns:the index within the unidimensionl counter.
/** * Get the current unidimensional counter slot. * * @return the index within the unidimensionl counter. */
public int getCount() { return count; }
Get the current multidimensional counter slots.
Returns:the indices within the multidimensional counter.
/** * Get the current multidimensional counter slots. * * @return the indices within the multidimensional counter. */
public int[] getCounts() { return MathArrays.copyOf(counter); }
Get the current count in the selected dimension.
Params:
  • dim – Dimension index.
Throws:
Returns:the count at the corresponding index for the current state of the iterator.
/** * Get the current count in the selected dimension. * * @param dim Dimension index. * @return the count at the corresponding index for the current state * of the iterator. * @throws IndexOutOfBoundsException if {@code index} is not in the * correct interval (as defined by the length of the argument in the * {@link MultidimensionalCounter#MultidimensionalCounter(int[]) * constructor of the enclosing class}). */
public int getCount(int dim) { return counter[dim]; }
Throws:
  • UnsupportedOperationException –
/** * @throws UnsupportedOperationException */
public void remove() { throw new UnsupportedOperationException(); } }
Create a counter.
Params:
  • size – Counter sizes (number of slots in each dimension).
Throws:
/** * Create a counter. * * @param size Counter sizes (number of slots in each dimension). * @throws NotStrictlyPositiveException if one of the sizes is * negative or zero. */
public MultidimensionalCounter(int ... size) throws NotStrictlyPositiveException { dimension = size.length; this.size = MathArrays.copyOf(size); uniCounterOffset = new int[dimension]; last = dimension - 1; int tS = size[last]; for (int i = 0; i < last; i++) { int count = 1; for (int j = i + 1; j < dimension; j++) { count *= size[j]; } uniCounterOffset[i] = count; tS *= size[i]; } uniCounterOffset[last] = 0; if (tS <= 0) { throw new NotStrictlyPositiveException(tS); } totalSize = tS; }
Create an iterator over this counter.
Returns:the iterator.
/** * Create an iterator over this counter. * * @return the iterator. */
public Iterator iterator() { return new Iterator(); }
Get the number of dimensions of the multidimensional counter.
Returns:the number of dimensions.
/** * Get the number of dimensions of the multidimensional counter. * * @return the number of dimensions. */
public int getDimension() { return dimension; }
Convert to multidimensional counter.
Params:
  • index – Index in unidimensional counter.
Throws:
Returns:the multidimensional counts.
/** * Convert to multidimensional counter. * * @param index Index in unidimensional counter. * @return the multidimensional counts. * @throws OutOfRangeException if {@code index} is not between * {@code 0} and the value returned by {@link #getSize()} (excluded). */
public int[] getCounts(int index) throws OutOfRangeException { if (index < 0 || index >= totalSize) { throw new OutOfRangeException(index, 0, totalSize); } final int[] indices = new int[dimension]; int count = 0; for (int i = 0; i < last; i++) { int idx = 0; final int offset = uniCounterOffset[i]; while (count <= index) { count += offset; ++idx; } --idx; count -= offset; indices[i] = idx; } indices[last] = index - count; return indices; }
Convert to unidimensional counter.
Params:
  • c – Indices in multidimensional counter.
Throws:
Returns:the index within the unidimensionl counter.
/** * Convert to unidimensional counter. * * @param c Indices in multidimensional counter. * @return the index within the unidimensionl counter. * @throws DimensionMismatchException if the size of {@code c} * does not match the size of the array given in the constructor. * @throws OutOfRangeException if a value of {@code c} is not in * the range of the corresponding dimension, as defined in the * {@link MultidimensionalCounter#MultidimensionalCounter(int...) constructor}. */
public int getCount(int ... c) throws OutOfRangeException, DimensionMismatchException { if (c.length != dimension) { throw new DimensionMismatchException(c.length, dimension); } int count = 0; for (int i = 0; i < dimension; i++) { final int index = c[i]; if (index < 0 || index >= size[i]) { throw new OutOfRangeException(index, 0, size[i] - 1); } count += uniCounterOffset[i] * c[i]; } return count + c[last]; }
Get the total number of elements.
Returns:the total size of the unidimensional counter.
/** * Get the total number of elements. * * @return the total size of the unidimensional counter. */
public int getSize() { return totalSize; }
Get the number of multidimensional counter slots in each dimension.
Returns:the sizes of the multidimensional counter in each dimension.
/** * Get the number of multidimensional counter slots in each dimension. * * @return the sizes of the multidimensional counter in each dimension. */
public int[] getSizes() { return MathArrays.copyOf(size); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public String toString() { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < dimension; i++) { sb.append("[").append(getCount(i)).append("]"); } return sb.toString(); } }