| /* |
| * 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.numbers.arrays; |
| |
| import java.util.Arrays; |
| |
| /** |
| * 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> |
| */ |
| public final class MultidimensionalCounter { |
| /** |
| * Number of dimensions. |
| */ |
| private final int dimension; |
| /** |
| * Offset for each dimension. |
| */ |
| private final int[] uniCounterOffset; |
| /** |
| * Counter sizes. |
| */ |
| private final int[] size; |
| /** |
| * Total number of (one-dimensional) slots. |
| */ |
| private final int totalSize; |
| /** |
| * Index of last dimension. |
| */ |
| private final int last; |
| |
| /** |
| * Creates a counter. |
| * |
| * @param size Counter sizes (number of slots in each dimension). |
| * @throws IllegalArgumentException if one of the sizes is negative |
| * or zero. |
| */ |
| private MultidimensionalCounter(int... size) { |
| dimension = size.length; |
| this.size = Arrays.copyOf(size, size.length); |
| |
| uniCounterOffset = new int[dimension]; |
| |
| last = dimension - 1; |
| uniCounterOffset[last] = 1; |
| |
| int tS = 1; |
| for (int i = last - 1; i >= 0; i--) { |
| final int index = i + 1; |
| checkStrictlyPositive("index size", size[index]); |
| tS *= size[index]; |
| checkStrictlyPositive("cumulative size", tS); |
| uniCounterOffset[i] = tS; |
| } |
| |
| totalSize = tS * size[0]; |
| checkStrictlyPositive("total size", totalSize); |
| } |
| |
| /** |
| * Creates a counter. |
| * |
| * @param size Counter sizes (number of slots in each dimension). |
| * @return a new instance. |
| * @throws IllegalArgumentException if one of the sizes is negative |
| * or zero. |
| */ |
| public static MultidimensionalCounter of(int... size) { |
| return new MultidimensionalCounter(size); |
| } |
| |
| /** |
| * Gets the number of dimensions of the multidimensional counter. |
| * |
| * @return the number of dimensions. |
| */ |
| public int getDimension() { |
| return dimension; |
| } |
| |
| /** |
| * Converts to a multidimensional counter. |
| * |
| * @param index Index in unidimensional counter. |
| * @return the multidimensional counts. |
| * @throws IndexOutOfBoundsException if {@code index} is not between |
| * {@code 0} and the value returned by {@link #getSize()} (excluded). |
| */ |
| public int[] toMulti(int index) { |
| if (index < 0 || |
| index >= totalSize) { |
| throw new IndexOutOfBoundsException(createIndexOutOfBoundsMessage(totalSize, index)); |
| } |
| |
| final int[] indices = new int[dimension]; |
| |
| for (int i = 0; i < last; i++) { |
| indices[i] = index / uniCounterOffset[i]; |
| // index = index % uniCounterOffset[i] |
| index = index - indices[i] * uniCounterOffset[i]; |
| } |
| |
| indices[last] = index; |
| |
| return indices; |
| } |
| |
| /** |
| * Converts to a unidimensional counter. |
| * |
| * @param c Indices in multidimensional counter. |
| * @return the index within the unidimensionl counter. |
| * @throws IllegalArgumentException if the size of {@code c} |
| * does not match the size of the array given in the constructor. |
| * @throws IndexOutOfBoundsException if a value of {@code c} is not in |
| * the range of the corresponding dimension, as defined in the |
| * {@link MultidimensionalCounter#of(int...) constructor}. |
| */ |
| public int toUni(int... c) { |
| if (c.length != dimension) { |
| throw new IllegalArgumentException("Wrong number of arguments: " + c.length + |
| "(expected: " + dimension + ")"); |
| } |
| int count = 0; |
| for (int i = 0; i < dimension; i++) { |
| final int index = c[i]; |
| if (index < 0 || |
| index >= size[i]) { |
| throw new IndexOutOfBoundsException(createIndexOutOfBoundsMessage(size[i], index)); |
| } |
| count += uniCounterOffset[i] * index; |
| } |
| return count; |
| } |
| |
| /** |
| * Gets the total number of elements. |
| * |
| * @return the total size of the unidimensional counter. |
| */ |
| public int getSize() { |
| return totalSize; |
| } |
| |
| /** |
| * Gets the number of multidimensional counter slots in each dimension. |
| * |
| * @return the number of slots in each dimension. |
| */ |
| public int[] getSizes() { |
| return Arrays.copyOf(size, size.length); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public String toString() { |
| return Arrays.toString(size); |
| } |
| |
| /** |
| * Check the size is strictly positive: {@code size > 0}. |
| * |
| * @param name the name of the size |
| * @param size the size |
| */ |
| private static void checkStrictlyPositive(String name, int size) { |
| if (size <= 0) { |
| throw new IllegalArgumentException("Not positive " + name + ": " + size); |
| } |
| } |
| |
| /** |
| * Creates the message for the index out of bounds exception. |
| * |
| * @param size the size |
| * @param index the index |
| * @return the message |
| */ |
| private static String createIndexOutOfBoundsMessage(int size, int index) { |
| return "Index out of bounds [0, " + (size - 1) + "]: " + index; |
| } |
| } |