MultidimensionalCounter.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.util;

  22. import java.util.NoSuchElementException;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;

  25. /**
  26.  * Converter between unidimensional storage structure and multidimensional
  27.  * conceptual structure.
  28.  * This utility will convert from indices in a multidimensional structure
  29.  * to the corresponding index in a one-dimensional array. For example,
  30.  * assuming that the ranges (in 3 dimensions) of indices are 2, 4 and 3,
  31.  * the following correspondences, between 3-tuples indices and unidimensional
  32.  * indices, will hold:
  33.  * <ul>
  34.  *  <li>(0, 0, 0) corresponds to 0</li>
  35.  *  <li>(0, 0, 1) corresponds to 1</li>
  36.  *  <li>(0, 0, 2) corresponds to 2</li>
  37.  *  <li>(0, 1, 0) corresponds to 3</li>
  38.  *  <li>...</li>
  39.  *  <li>(1, 0, 0) corresponds to 12</li>
  40.  *  <li>...</li>
  41.  *  <li>(1, 3, 2) corresponds to 23</li>
  42.  * </ul>
  43.  */
  44. public class MultidimensionalCounter implements Iterable<Integer> {
  45.     /**
  46.      * Number of dimensions.
  47.      */
  48.     private final int dimension;
  49.     /**
  50.      * Offset for each dimension.
  51.      */
  52.     private final int[] uniCounterOffset;
  53.     /**
  54.      * Counter sizes.
  55.      */
  56.     private final int[] size;
  57.     /**
  58.      * Total number of (one-dimensional) slots.
  59.      */
  60.     private final int totalSize;
  61.     /**
  62.      * Index of last dimension.
  63.      */
  64.     private final int last;

  65.     /**
  66.      * Perform iteration over the multidimensional counter.
  67.      */
  68.     public class Iterator implements java.util.Iterator<Integer> {
  69.         /**
  70.          * Multidimensional counter.
  71.          */
  72.         private final int[] counter = new int[dimension];
  73.         /**
  74.          * Unidimensional counter.
  75.          */
  76.         private int count = -1;
  77.         /**
  78.          * Maximum value for {@link #count}.
  79.          */
  80.         private final int maxCount = totalSize - 1;

  81.         /**
  82.          * Create an iterator
  83.          * @see #iterator()
  84.          */
  85.         Iterator() {
  86.             counter[last] = -1;
  87.         }

  88.         /**
  89.          * {@inheritDoc}
  90.          */
  91.         @Override
  92.         public boolean hasNext() {
  93.             return count < maxCount;
  94.         }

  95.         /**
  96.          * @return the unidimensional count after the counter has been
  97.          * incremented by {@code 1}.
  98.          * @throws NoSuchElementException if {@link #hasNext()} would have
  99.          * returned {@code false}.
  100.          */
  101.         @Override
  102.         public Integer next() {
  103.             if (!hasNext()) {
  104.                 throw new NoSuchElementException();
  105.             }

  106.             for (int i = last; i >= 0; i--) {
  107.                 if (counter[i] == size[i] - 1) {
  108.                     counter[i] = 0;
  109.                 } else {
  110.                     ++counter[i];
  111.                     break;
  112.                 }
  113.             }

  114.             return ++count;
  115.         }

  116.         /**
  117.          * Get the current unidimensional counter slot.
  118.          *
  119.          * @return the index within the unidimensionl counter.
  120.          */
  121.         public int getCount() {
  122.             return count;
  123.         }
  124.         /**
  125.          * Get the current multidimensional counter slots.
  126.          *
  127.          * @return the indices within the multidimensional counter.
  128.          */
  129.         public int[] getCounts() {
  130.             return counter.clone();
  131.         }

  132.         /**
  133.          * Get the current count in the selected dimension.
  134.          *
  135.          * @param dim Dimension index.
  136.          * @return the count at the corresponding index for the current state
  137.          * of the iterator.
  138.          * @throws IndexOutOfBoundsException if {@code index} is not in the
  139.          * correct interval (as defined by the length of the argument in the
  140.          * {@link MultidimensionalCounter#MultidimensionalCounter(int[])
  141.          * constructor of the enclosing class}).
  142.          */
  143.         public int getCount(int dim) {
  144.             return counter[dim];
  145.         }

  146.         /** {@inheritDoc} */
  147.         @Override
  148.         public void remove() {
  149.             throw new UnsupportedOperationException();
  150.         }
  151.     }

  152.     /**
  153.      * Create a counter.
  154.      *
  155.      * @param size Counter sizes (number of slots in each dimension).
  156.      * @throws MathIllegalArgumentException if one of the sizes is
  157.      * negative or zero.
  158.      */
  159.     public MultidimensionalCounter(int... size) throws MathIllegalArgumentException {
  160.         dimension = size.length;
  161.         this.size = size.clone();

  162.         uniCounterOffset = new int[dimension];

  163.         last = dimension - 1;
  164.         int tS = size[last];
  165.         for (int i = 0; i < last; i++) {
  166.             int count = 1;
  167.             for (int j = i + 1; j < dimension; j++) {
  168.                 count *= size[j];
  169.             }
  170.             uniCounterOffset[i] = count;
  171.             tS *= size[i];
  172.         }
  173.         uniCounterOffset[last] = 0;

  174.         if (tS <= 0) {
  175.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
  176.                                                    tS, 0);
  177.         }

  178.         totalSize = tS;
  179.     }

  180.     /**
  181.      * Create an iterator over this counter.
  182.      *
  183.      * @return the iterator.
  184.      */
  185.     @Override
  186.     public Iterator iterator() {
  187.         return new Iterator();
  188.     }

  189.     /**
  190.      * Get the number of dimensions of the multidimensional counter.
  191.      *
  192.      * @return the number of dimensions.
  193.      */
  194.     public int getDimension() {
  195.         return dimension;
  196.     }

  197.     /**
  198.      * Convert to multidimensional counter.
  199.      *
  200.      * @param index Index in unidimensional counter.
  201.      * @return the multidimensional counts.
  202.      * @throws MathIllegalArgumentException if {@code index} is not between
  203.      * {@code 0} and the value returned by {@link #getSize()} (excluded).
  204.      */
  205.     public int[] getCounts(int index) throws MathIllegalArgumentException {
  206.         MathUtils.checkRangeInclusive(index, 0, totalSize - 1);

  207.         final int[] indices = new int[dimension];

  208.         int count = 0;
  209.         for (int i = 0; i < last; i++) {
  210.             int idx = 0;
  211.             final int offset = uniCounterOffset[i];
  212.             while (count <= index) {
  213.                 count += offset;
  214.                 ++idx;
  215.             }
  216.             --idx;
  217.             count -= offset;
  218.             indices[i] = idx;
  219.         }

  220.         indices[last] = index - count;

  221.         return indices;
  222.     }

  223.     /**
  224.      * Convert to unidimensional counter.
  225.      *
  226.      * @param c Indices in multidimensional counter.
  227.      * @return the index within the unidimensionl counter.
  228.      * @throws MathIllegalArgumentException if the size of {@code c}
  229.      * does not match the size of the array given in the constructor.
  230.      * @throws MathIllegalArgumentException if a value of {@code c} is not in
  231.      * the range of the corresponding dimension, as defined in the
  232.      * {@link MultidimensionalCounter#MultidimensionalCounter(int...) constructor}.
  233.      */
  234.     public int getCount(int ... c) throws MathIllegalArgumentException {
  235.         MathUtils.checkDimension(c.length, dimension);
  236.         int count = 0;
  237.         for (int i = 0; i < dimension; i++) {
  238.             final int index = c[i];
  239.             MathUtils.checkRangeInclusive(index, 0, size[i] - 1);
  240.             count += uniCounterOffset[i] * c[i];
  241.         }
  242.         return count + c[last];
  243.     }

  244.     /**
  245.      * Get the total number of elements.
  246.      *
  247.      * @return the total size of the unidimensional counter.
  248.      */
  249.     public int getSize() {
  250.         return totalSize;
  251.     }
  252.     /**
  253.      * Get the number of multidimensional counter slots in each dimension.
  254.      *
  255.      * @return the sizes of the multidimensional counter in each dimension.
  256.      */
  257.     public int[] getSizes() {
  258.         return size.clone();
  259.     }

  260.     /**
  261.      * {@inheritDoc}
  262.      */
  263.     @Override
  264.     public String toString() {
  265.         final StringBuilder sb = new StringBuilder();
  266.         for (int i = 0; i < dimension; i++) {
  267.             sb.append('[').append(getCount(i)).append(']');
  268.         }
  269.         return sb.toString();
  270.     }
  271. }