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 }