001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.math.util; 019 020 import org.apache.commons.math.exception.DimensionMismatchException; 021 import org.apache.commons.math.exception.OutOfRangeException; 022 import org.apache.commons.math.exception.NotStrictlyPositiveException; 023 024 /** 025 * Converter between unidimensional storage structure and multidimensional 026 * conceptual structure. 027 * This utility will convert from indices in a multidimensional structure 028 * to the corresponding index in a one-dimensional array. For example, 029 * assuming that the ranges (in 3 dimensions) of indices are 2, 4 and 3, 030 * the following correspondences, between 3-tuples indices and unidimensional 031 * indices, will hold: 032 * <ul> 033 * <li>(0, 0, 0) corresponds to 0</li> 034 * <li>(0, 0, 1) corresponds to 1</li> 035 * <li>(0, 0, 2) corresponds to 2</li> 036 * <li>(0, 1, 0) corresponds to 3</li> 037 * <li>...</li> 038 * <li>(1, 0, 0) corresponds to 12</li> 039 * <li>...</li> 040 * <li>(1, 3, 2) corresponds to 23</li> 041 * </ul> 042 * @version $Revision$ $Date$ 043 * @since 2.2 044 */ 045 public class MultidimensionalCounter implements Iterable<Integer> { 046 /** 047 * Number of dimensions. 048 */ 049 private final int dimension; 050 /** 051 * Offset for each dimension. 052 */ 053 private final int[] uniCounterOffset; 054 /** 055 * Counter sizes. 056 */ 057 private final int[] size; 058 /** 059 * Total number of (one-dimensional) slots. 060 */ 061 private final int totalSize; 062 /** 063 * Index of last dimension. 064 */ 065 private final int last; 066 067 /** 068 * Perform iteration over the multidimensional counter. 069 */ 070 public class Iterator implements java.util.Iterator<Integer> { 071 /** 072 * Multidimensional counter. 073 */ 074 private final int[] counter = new int[dimension]; 075 /** 076 * Unidimensional counter. 077 */ 078 private int count = -1; 079 080 /** 081 * Create an iterator 082 * @see #iterator() 083 */ 084 Iterator() { 085 counter[last] = -1; 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 public boolean hasNext() { 092 for (int i = 0; i < dimension; i++) { 093 if (counter[i] != size[i] - 1) { 094 return true; 095 } 096 } 097 return false; 098 } 099 100 /** 101 * @return the unidimensional count after the counter has been 102 * incremented by {@code 1}. 103 */ 104 public Integer next() { 105 for (int i = last; i >= 0; i--) { 106 if (counter[i] == size[i] - 1) { 107 counter[i] = 0; 108 } else { 109 ++counter[i]; 110 break; 111 } 112 } 113 114 return ++count; 115 } 116 117 /** 118 * Get the current unidimensional counter slot. 119 * 120 * @return the index within the unidimensionl counter. 121 */ 122 public int getCount() { 123 return count; 124 } 125 /** 126 * Get the current multidimensional counter slots. 127 * 128 * @return the indices within the multidimensional counter. 129 */ 130 public int[] getCounts() { 131 return /* Arrays.*/ copyOf(counter, dimension); // Java 1.5 does not support Arrays.copyOf() 132 } 133 134 /** 135 * Get the current count in the selected dimension. 136 * 137 * @param dim Dimension index. 138 * @return the count at the corresponding index for the current state 139 * of the iterator. 140 * @throws IndexOutOfBoundsException if {@code index} is not in the 141 * correct interval (as defined by the length of the argument in the 142 * {@link MultidimensionalCounter#MultidimensionalCounter(int[]) 143 * constructor of the enclosing class}). 144 */ 145 public int getCount(int dim) { 146 return counter[dim]; 147 } 148 149 /** 150 * @throws UnsupportedOperationException 151 */ 152 public void remove() { 153 throw new UnsupportedOperationException(); 154 } 155 } 156 157 /** 158 * Create a counter. 159 * 160 * @param size Counter sizes (number of slots in each dimension). 161 * @throws NotStrictlyPositiveException if one of the sizes is 162 * negative or zero. 163 */ 164 public MultidimensionalCounter(int ... size) { 165 dimension = size.length; 166 this.size = /* Arrays.*/ copyOf(size, dimension); // Java 1.5 does not support Arrays.copyOf() 167 168 uniCounterOffset = new int[dimension]; 169 170 last = dimension - 1; 171 int tS = size[last]; 172 for (int i = 0; i < last; i++) { 173 int count = 1; 174 for (int j = i + 1; j < dimension; j++) { 175 count *= size[j]; 176 } 177 uniCounterOffset[i] = count; 178 tS *= size[i]; 179 } 180 uniCounterOffset[last] = 0; 181 182 if (tS <= 0) { 183 throw new NotStrictlyPositiveException(tS); 184 } 185 186 totalSize = tS; 187 } 188 189 /** 190 * Create an iterator over this counter. 191 * 192 * @return the iterator. 193 */ 194 public Iterator iterator() { 195 return new Iterator(); 196 } 197 198 /** 199 * Get the number of dimensions of the multidimensional counter. 200 * 201 * @return the number of dimensions. 202 */ 203 public int getDimension() { 204 return dimension; 205 } 206 207 /** 208 * Convert to multidimensional counter. 209 * 210 * @param index Index in unidimensional counter. 211 * @return the multidimensional counts. 212 * @throws OutOfRangeException if {@code index} is not between 213 * {@code 0} and the value returned by {@link #getSize()} (excluded). 214 */ 215 public int[] getCounts(int index) { 216 if (index < 0 || 217 index >= totalSize) { 218 throw new OutOfRangeException(index, 0, totalSize); 219 } 220 221 final int[] indices = new int[dimension]; 222 223 int count = 0; 224 for (int i = 0; i < last; i++) { 225 int idx = 0; 226 final int offset = uniCounterOffset[i]; 227 while (count <= index) { 228 count += offset; 229 ++idx; 230 } 231 --idx; 232 count -= offset; 233 indices[i] = idx; 234 } 235 236 int idx = 1; 237 while (count < index) { 238 count += idx; 239 ++idx; 240 } 241 --idx; 242 indices[last] = idx; 243 244 return indices; 245 } 246 247 /** 248 * Convert to unidimensional counter. 249 * 250 * @param c Indices in multidimensional counter. 251 * @return the index within the unidimensionl counter. 252 * @throws DimensionMismatchException if the size of {@code c} 253 * does not match the size of the array given in the constructor. 254 * @throws OutOfRangeException if a value of {@code c} is not in 255 * the range of the corresponding dimension, as defined in the 256 * {@link MultidimensionalCounter#MultidimensionalCounter(int...) constructor}. 257 */ 258 public int getCount(int ... c) throws OutOfRangeException { 259 if (c.length != dimension) { 260 throw new DimensionMismatchException(c.length, dimension); 261 } 262 int count = 0; 263 for (int i = 0; i < dimension; i++) { 264 final int index = c[i]; 265 if (index < 0 || 266 index >= size[i]) { 267 throw new OutOfRangeException(index, 0, size[i] - 1); 268 } 269 count += uniCounterOffset[i] * c[i]; 270 } 271 return count + c[last]; 272 } 273 274 /** 275 * Get the total number of elements. 276 * 277 * @return the total size of the unidimensional counter. 278 */ 279 public int getSize() { 280 return totalSize; 281 } 282 /** 283 * Get the number of multidimensional counter slots in each dimension. 284 * 285 * @return the sizes of the multidimensional counter in each dimension. 286 */ 287 public int[] getSizes() { 288 return /* Arrays.*/ copyOf(size, dimension); // Java 1.5 does not support Arrays.copyOf() 289 } 290 291 /** 292 * {@inheritDoc} 293 */ 294 @Override 295 public String toString() { 296 final StringBuilder sb = new StringBuilder(); 297 for (int i = 0; i < dimension; i++) { 298 sb.append("[").append(getCount(i)).append("]"); 299 } 300 return sb.toString(); 301 } 302 303 /** 304 * Java 1.5 does not support Arrays.copyOf() 305 * 306 * @param source the array to be copied 307 * @param newLen the length of the copy to be returned 308 * @return the copied array, truncated or padded as necessary. 309 */ 310 private int[] copyOf(int[] source, int newLen) { 311 int[] output = new int[newLen]; 312 System.arraycopy(source, 0, output, 0, Math.min(source.length, newLen)); 313 return output; 314 } 315 316 }