View Javadoc
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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.util;
24  
25  import java.util.NoSuchElementException;
26  
27  import org.hipparchus.exception.LocalizedCoreFormats;
28  import org.hipparchus.exception.MathIllegalArgumentException;
29  
30  /**
31   * Converter between unidimensional storage structure and multidimensional
32   * conceptual structure.
33   * This utility will convert from indices in a multidimensional structure
34   * to the corresponding index in a one-dimensional array. For example,
35   * assuming that the ranges (in 3 dimensions) of indices are 2, 4 and 3,
36   * the following correspondences, between 3-tuples indices and unidimensional
37   * indices, will hold:
38   * <ul>
39   *  <li>(0, 0, 0) corresponds to 0</li>
40   *  <li>(0, 0, 1) corresponds to 1</li>
41   *  <li>(0, 0, 2) corresponds to 2</li>
42   *  <li>(0, 1, 0) corresponds to 3</li>
43   *  <li>...</li>
44   *  <li>(1, 0, 0) corresponds to 12</li>
45   *  <li>...</li>
46   *  <li>(1, 3, 2) corresponds to 23</li>
47   * </ul>
48   */
49  public class MultidimensionalCounter implements Iterable<Integer> {
50      /**
51       * Number of dimensions.
52       */
53      private final int dimension;
54      /**
55       * Offset for each dimension.
56       */
57      private final int[] uniCounterOffset;
58      /**
59       * Counter sizes.
60       */
61      private final int[] size;
62      /**
63       * Total number of (one-dimensional) slots.
64       */
65      private final int totalSize;
66      /**
67       * Index of last dimension.
68       */
69      private final int last;
70  
71      /**
72       * Perform iteration over the multidimensional counter.
73       */
74      public class Iterator implements java.util.Iterator<Integer> {
75          /**
76           * Multidimensional counter.
77           */
78          private final int[] counter = new int[dimension];
79          /**
80           * Unidimensional counter.
81           */
82          private int count = -1;
83          /**
84           * Maximum value for {@link #count}.
85           */
86          private final int maxCount = totalSize - 1;
87  
88          /**
89           * Create an iterator
90           * @see #iterator()
91           */
92          Iterator() {
93              counter[last] = -1;
94          }
95  
96          /**
97           * {@inheritDoc}
98           */
99          @Override
100         public boolean hasNext() {
101             return count < maxCount;
102         }
103 
104         /**
105          * @return the unidimensional count after the counter has been
106          * incremented by {@code 1}.
107          * @throws NoSuchElementException if {@link #hasNext()} would have
108          * returned {@code false}.
109          */
110         @Override
111         public Integer next() {
112             if (!hasNext()) {
113                 throw new NoSuchElementException();
114             }
115 
116             for (int i = last; i >= 0; i--) {
117                 if (counter[i] == size[i] - 1) {
118                     counter[i] = 0;
119                 } else {
120                     ++counter[i];
121                     break;
122                 }
123             }
124 
125             return ++count;
126         }
127 
128         /**
129          * Get the current unidimensional counter slot.
130          *
131          * @return the index within the unidimensionl counter.
132          */
133         public int getCount() {
134             return count;
135         }
136         /**
137          * Get the current multidimensional counter slots.
138          *
139          * @return the indices within the multidimensional counter.
140          */
141         public int[] getCounts() {
142             return counter.clone();
143         }
144 
145         /**
146          * Get the current count in the selected dimension.
147          *
148          * @param dim Dimension index.
149          * @return the count at the corresponding index for the current state
150          * of the iterator.
151          * @throws IndexOutOfBoundsException if {@code index} is not in the
152          * correct interval (as defined by the length of the argument in the
153          * {@link MultidimensionalCounter#MultidimensionalCounter(int[])
154          * constructor of the enclosing class}).
155          */
156         public int getCount(int dim) {
157             return counter[dim];
158         }
159 
160         /** {@inheritDoc} */
161         @Override
162         public void remove() {
163             throw new UnsupportedOperationException();
164         }
165     }
166 
167     /**
168      * Create a counter.
169      *
170      * @param size Counter sizes (number of slots in each dimension).
171      * @throws MathIllegalArgumentException if one of the sizes is
172      * negative or zero.
173      */
174     public MultidimensionalCounter(int... size) throws MathIllegalArgumentException {
175         dimension = size.length;
176         this.size = size.clone();
177 
178         uniCounterOffset = new int[dimension];
179 
180         last = dimension - 1;
181         int tS = size[last];
182         for (int i = 0; i < last; i++) {
183             int count = 1;
184             for (int j = i + 1; j < dimension; j++) {
185                 count *= size[j];
186             }
187             uniCounterOffset[i] = count;
188             tS *= size[i];
189         }
190         uniCounterOffset[last] = 0;
191 
192         if (tS <= 0) {
193             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
194                                                    tS, 0);
195         }
196 
197         totalSize = tS;
198     }
199 
200     /**
201      * Create an iterator over this counter.
202      *
203      * @return the iterator.
204      */
205     @Override
206     public Iterator iterator() {
207         return new Iterator();
208     }
209 
210     /**
211      * Get the number of dimensions of the multidimensional counter.
212      *
213      * @return the number of dimensions.
214      */
215     public int getDimension() {
216         return dimension;
217     }
218 
219     /**
220      * Convert to multidimensional counter.
221      *
222      * @param index Index in unidimensional counter.
223      * @return the multidimensional counts.
224      * @throws MathIllegalArgumentException if {@code index} is not between
225      * {@code 0} and the value returned by {@link #getSize()} (excluded).
226      */
227     public int[] getCounts(int index) throws MathIllegalArgumentException {
228         MathUtils.checkRangeInclusive(index, 0, totalSize - 1);
229 
230         final int[] indices = new int[dimension];
231 
232         int count = 0;
233         for (int i = 0; i < last; i++) {
234             int idx = 0;
235             final int offset = uniCounterOffset[i];
236             while (count <= index) {
237                 count += offset;
238                 ++idx;
239             }
240             --idx;
241             count -= offset;
242             indices[i] = idx;
243         }
244 
245         indices[last] = index - count;
246 
247         return indices;
248     }
249 
250     /**
251      * Convert to unidimensional counter.
252      *
253      * @param c Indices in multidimensional counter.
254      * @return the index within the unidimensionl counter.
255      * @throws MathIllegalArgumentException if the size of {@code c}
256      * does not match the size of the array given in the constructor.
257      * @throws MathIllegalArgumentException if a value of {@code c} is not in
258      * the range of the corresponding dimension, as defined in the
259      * {@link MultidimensionalCounter#MultidimensionalCounter(int...) constructor}.
260      */
261     public int getCount(int ... c) throws MathIllegalArgumentException {
262         MathUtils.checkDimension(c.length, dimension);
263         int count = 0;
264         for (int i = 0; i < dimension; i++) {
265             final int index = c[i];
266             MathUtils.checkRangeInclusive(index, 0, size[i] - 1);
267             count += uniCounterOffset[i] * c[i];
268         }
269         return count + c[last];
270     }
271 
272     /**
273      * Get the total number of elements.
274      *
275      * @return the total size of the unidimensional counter.
276      */
277     public int getSize() {
278         return totalSize;
279     }
280     /**
281      * Get the number of multidimensional counter slots in each dimension.
282      *
283      * @return the sizes of the multidimensional counter in each dimension.
284      */
285     public int[] getSizes() {
286         return size.clone();
287     }
288 
289     /**
290      * {@inheritDoc}
291      */
292     @Override
293     public String toString() {
294         final StringBuilder sb = new StringBuilder();
295         for (int i = 0; i < dimension; i++) {
296             sb.append('[').append(getCount(i)).append(']');
297         }
298         return sb.toString();
299     }
300 }