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 */ 017package org.apache.commons.math3.transform; 018 019import java.io.Serializable; 020import java.lang.reflect.Array; 021 022import org.apache.commons.math3.analysis.FunctionUtils; 023import org.apache.commons.math3.analysis.UnivariateFunction; 024import org.apache.commons.math3.complex.Complex; 025import org.apache.commons.math3.exception.DimensionMismatchException; 026import org.apache.commons.math3.exception.MathIllegalArgumentException; 027import org.apache.commons.math3.exception.MathIllegalStateException; 028import org.apache.commons.math3.exception.util.LocalizedFormats; 029import org.apache.commons.math3.util.ArithmeticUtils; 030import org.apache.commons.math3.util.FastMath; 031import org.apache.commons.math3.util.MathArrays; 032 033/** 034 * Implements the Fast Fourier Transform for transformation of one-dimensional 035 * real or complex data sets. For reference, see <em>Applied Numerical Linear 036 * Algebra</em>, ISBN 0898713897, chapter 6. 037 * <p> 038 * There are several variants of the discrete Fourier transform, with various 039 * normalization conventions, which are specified by the parameter 040 * {@link DftNormalization}. 041 * <p> 042 * The current implementation of the discrete Fourier transform as a fast 043 * Fourier transform requires the length of the data set to be a power of 2. 044 * This greatly simplifies and speeds up the code. Users can pad the data with 045 * zeros to meet this requirement. There are other flavors of FFT, for 046 * reference, see S. Winograd, 047 * <i>On computing the discrete Fourier transform</i>, Mathematics of 048 * Computation, 32 (1978), 175 - 199. 049 * 050 * @see DftNormalization 051 * @since 1.2 052 */ 053public class FastFourierTransformer implements Serializable { 054 055 /** Serializable version identifier. */ 056 static final long serialVersionUID = 20120210L; 057 058 /** 059 * {@code W_SUB_N_R[i]} is the real part of 060 * {@code exp(- 2 * i * pi / n)}: 061 * {@code W_SUB_N_R[i] = cos(2 * pi/ n)}, where {@code n = 2^i}. 062 */ 063 private static final double[] W_SUB_N_R = 064 { 0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1 065 , 0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1 066 , 0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1 067 , 0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1 068 , 0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1 069 , 0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1 070 , 0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1 071 , 0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0 072 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 073 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 074 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 075 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 076 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 077 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 078 , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0 079 , 0x1.0p0, 0x1.0p0, 0x1.0p0 }; 080 081 /** 082 * {@code W_SUB_N_I[i]} is the imaginary part of 083 * {@code exp(- 2 * i * pi / n)}: 084 * {@code W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}. 085 */ 086 private static final double[] W_SUB_N_I = 087 { 0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1 088 , -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5 089 , -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9 090 , -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13 091 , -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17 092 , -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21 093 , -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25 094 , -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29 095 , -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33 096 , -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37 097 , -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41 098 , -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45 099 , -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49 100 , -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53 101 , -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57 102 , -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 }; 103 104 /** The type of DFT to be performed. */ 105 private final DftNormalization normalization; 106 107 /** 108 * Creates a new instance of this class, with various normalization 109 * conventions. 110 * 111 * @param normalization the type of normalization to be applied to the 112 * transformed data 113 */ 114 public FastFourierTransformer(final DftNormalization normalization) { 115 this.normalization = normalization; 116 } 117 118 /** 119 * Performs identical index bit reversal shuffles on two arrays of identical 120 * size. Each element in the array is swapped with another element based on 121 * the bit-reversal of the index. For example, in an array with length 16, 122 * item at binary index 0011 (decimal 3) would be swapped with the item at 123 * binary index 1100 (decimal 12). 124 * 125 * @param a the first array to be shuffled 126 * @param b the second array to be shuffled 127 */ 128 private static void bitReversalShuffle2(double[] a, double[] b) { 129 final int n = a.length; 130 assert b.length == n; 131 final int halfOfN = n >> 1; 132 133 int j = 0; 134 for (int i = 0; i < n; i++) { 135 if (i < j) { 136 // swap indices i & j 137 double temp = a[i]; 138 a[i] = a[j]; 139 a[j] = temp; 140 141 temp = b[i]; 142 b[i] = b[j]; 143 b[j] = temp; 144 } 145 146 int k = halfOfN; 147 while (k <= j && k > 0) { 148 j -= k; 149 k >>= 1; 150 } 151 j += k; 152 } 153 } 154 155 /** 156 * Applies the proper normalization to the specified transformed data. 157 * 158 * @param dataRI the unscaled transformed data 159 * @param normalization the normalization to be applied 160 * @param type the type of transform (forward, inverse) which resulted in the specified data 161 */ 162 private static void normalizeTransformedData(final double[][] dataRI, 163 final DftNormalization normalization, final TransformType type) { 164 165 final double[] dataR = dataRI[0]; 166 final double[] dataI = dataRI[1]; 167 final int n = dataR.length; 168 assert dataI.length == n; 169 170 switch (normalization) { 171 case STANDARD: 172 if (type == TransformType.INVERSE) { 173 final double scaleFactor = 1.0 / ((double) n); 174 for (int i = 0; i < n; i++) { 175 dataR[i] *= scaleFactor; 176 dataI[i] *= scaleFactor; 177 } 178 } 179 break; 180 case UNITARY: 181 final double scaleFactor = 1.0 / FastMath.sqrt(n); 182 for (int i = 0; i < n; i++) { 183 dataR[i] *= scaleFactor; 184 dataI[i] *= scaleFactor; 185 } 186 break; 187 default: 188 /* 189 * This should never occur in normal conditions. However this 190 * clause has been added as a safeguard if other types of 191 * normalizations are ever implemented, and the corresponding 192 * test is forgotten in the present switch. 193 */ 194 throw new MathIllegalStateException(); 195 } 196 } 197 198 /** 199 * Computes the standard transform of the specified complex data. The 200 * computation is done in place. The input data is laid out as follows 201 * <ul> 202 * <li>{@code dataRI[0][i]} is the real part of the {@code i}-th data point,</li> 203 * <li>{@code dataRI[1][i]} is the imaginary part of the {@code i}-th data point.</li> 204 * </ul> 205 * 206 * @param dataRI the two dimensional array of real and imaginary parts of the data 207 * @param normalization the normalization to be applied to the transformed data 208 * @param type the type of transform (forward, inverse) to be performed 209 * @throws DimensionMismatchException if the number of rows of the specified 210 * array is not two, or the array is not rectangular 211 * @throws MathIllegalArgumentException if the number of data points is not 212 * a power of two 213 */ 214 public static void transformInPlace(final double[][] dataRI, 215 final DftNormalization normalization, final TransformType type) { 216 217 if (dataRI.length != 2) { 218 throw new DimensionMismatchException(dataRI.length, 2); 219 } 220 final double[] dataR = dataRI[0]; 221 final double[] dataI = dataRI[1]; 222 if (dataR.length != dataI.length) { 223 throw new DimensionMismatchException(dataI.length, dataR.length); 224 } 225 226 final int n = dataR.length; 227 if (!ArithmeticUtils.isPowerOfTwo(n)) { 228 throw new MathIllegalArgumentException( 229 LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, 230 Integer.valueOf(n)); 231 } 232 233 if (n == 1) { 234 return; 235 } else if (n == 2) { 236 final double srcR0 = dataR[0]; 237 final double srcI0 = dataI[0]; 238 final double srcR1 = dataR[1]; 239 final double srcI1 = dataI[1]; 240 241 // X_0 = x_0 + x_1 242 dataR[0] = srcR0 + srcR1; 243 dataI[0] = srcI0 + srcI1; 244 // X_1 = x_0 - x_1 245 dataR[1] = srcR0 - srcR1; 246 dataI[1] = srcI0 - srcI1; 247 248 normalizeTransformedData(dataRI, normalization, type); 249 return; 250 } 251 252 bitReversalShuffle2(dataR, dataI); 253 254 // Do 4-term DFT. 255 if (type == TransformType.INVERSE) { 256 for (int i0 = 0; i0 < n; i0 += 4) { 257 final int i1 = i0 + 1; 258 final int i2 = i0 + 2; 259 final int i3 = i0 + 3; 260 261 final double srcR0 = dataR[i0]; 262 final double srcI0 = dataI[i0]; 263 final double srcR1 = dataR[i2]; 264 final double srcI1 = dataI[i2]; 265 final double srcR2 = dataR[i1]; 266 final double srcI2 = dataI[i1]; 267 final double srcR3 = dataR[i3]; 268 final double srcI3 = dataI[i3]; 269 270 // 4-term DFT 271 // X_0 = x_0 + x_1 + x_2 + x_3 272 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3; 273 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3; 274 // X_1 = x_0 - x_2 + j * (x_3 - x_1) 275 dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1); 276 dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3); 277 // X_2 = x_0 - x_1 + x_2 - x_3 278 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3; 279 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3; 280 // X_3 = x_0 - x_2 + j * (x_1 - x_3) 281 dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3); 282 dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1); 283 } 284 } else { 285 for (int i0 = 0; i0 < n; i0 += 4) { 286 final int i1 = i0 + 1; 287 final int i2 = i0 + 2; 288 final int i3 = i0 + 3; 289 290 final double srcR0 = dataR[i0]; 291 final double srcI0 = dataI[i0]; 292 final double srcR1 = dataR[i2]; 293 final double srcI1 = dataI[i2]; 294 final double srcR2 = dataR[i1]; 295 final double srcI2 = dataI[i1]; 296 final double srcR3 = dataR[i3]; 297 final double srcI3 = dataI[i3]; 298 299 // 4-term DFT 300 // X_0 = x_0 + x_1 + x_2 + x_3 301 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3; 302 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3; 303 // X_1 = x_0 - x_2 + j * (x_3 - x_1) 304 dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3); 305 dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1); 306 // X_2 = x_0 - x_1 + x_2 - x_3 307 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3; 308 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3; 309 // X_3 = x_0 - x_2 + j * (x_1 - x_3) 310 dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1); 311 dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3); 312 } 313 } 314 315 int lastN0 = 4; 316 int lastLogN0 = 2; 317 while (lastN0 < n) { 318 int n0 = lastN0 << 1; 319 int logN0 = lastLogN0 + 1; 320 double wSubN0R = W_SUB_N_R[logN0]; 321 double wSubN0I = W_SUB_N_I[logN0]; 322 if (type == TransformType.INVERSE) { 323 wSubN0I = -wSubN0I; 324 } 325 326 // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2). 327 for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) { 328 int destOddStartIndex = destEvenStartIndex + lastN0; 329 330 double wSubN0ToRR = 1; 331 double wSubN0ToRI = 0; 332 333 for (int r = 0; r < lastN0; r++) { 334 double grR = dataR[destEvenStartIndex + r]; 335 double grI = dataI[destEvenStartIndex + r]; 336 double hrR = dataR[destOddStartIndex + r]; 337 double hrI = dataI[destOddStartIndex + r]; 338 339 // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr 340 dataR[destEvenStartIndex + r] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI; 341 dataI[destEvenStartIndex + r] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR; 342 // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr 343 dataR[destOddStartIndex + r] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI); 344 dataI[destOddStartIndex + r] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR); 345 346 // WsubN0ToR *= WsubN0R 347 double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I; 348 double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R; 349 wSubN0ToRR = nextWsubN0ToRR; 350 wSubN0ToRI = nextWsubN0ToRI; 351 } 352 } 353 354 lastN0 = n0; 355 lastLogN0 = logN0; 356 } 357 358 normalizeTransformedData(dataRI, normalization, type); 359 } 360 361 /** 362 * Returns the (forward, inverse) transform of the specified real data set. 363 * 364 * @param f the real data array to be transformed 365 * @param type the type of transform (forward, inverse) to be performed 366 * @return the complex transformed array 367 * @throws MathIllegalArgumentException if the length of the data array is not a power of two 368 */ 369 public Complex[] transform(final double[] f, final TransformType type) { 370 final double[][] dataRI = new double[][] { 371 MathArrays.copyOf(f, f.length), new double[f.length] 372 }; 373 374 transformInPlace(dataRI, normalization, type); 375 376 return TransformUtils.createComplexArray(dataRI); 377 } 378 379 /** 380 * Returns the (forward, inverse) transform of the specified real function, 381 * sampled on the specified interval. 382 * 383 * @param f the function to be sampled and transformed 384 * @param min the (inclusive) lower bound for the interval 385 * @param max the (exclusive) upper bound for the interval 386 * @param n the number of sample points 387 * @param type the type of transform (forward, inverse) to be performed 388 * @return the complex transformed array 389 * @throws org.apache.commons.math3.exception.NumberIsTooLargeException 390 * if the lower bound is greater than, or equal to the upper bound 391 * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException 392 * if the number of sample points {@code n} is negative 393 * @throws MathIllegalArgumentException if the number of sample points 394 * {@code n} is not a power of two 395 */ 396 public Complex[] transform(final UnivariateFunction f, 397 final double min, final double max, final int n, 398 final TransformType type) { 399 400 final double[] data = FunctionUtils.sample(f, min, max, n); 401 return transform(data, type); 402 } 403 404 /** 405 * Returns the (forward, inverse) transform of the specified complex data set. 406 * 407 * @param f the complex data array to be transformed 408 * @param type the type of transform (forward, inverse) to be performed 409 * @return the complex transformed array 410 * @throws MathIllegalArgumentException if the length of the data array is not a power of two 411 */ 412 public Complex[] transform(final Complex[] f, final TransformType type) { 413 final double[][] dataRI = TransformUtils.createRealImaginaryArray(f); 414 415 transformInPlace(dataRI, normalization, type); 416 417 return TransformUtils.createComplexArray(dataRI); 418 } 419 420 /** 421 * Performs a multi-dimensional Fourier transform on a given array. Use 422 * {@link #transform(Complex[], TransformType)} in a row-column 423 * implementation in any number of dimensions with 424 * O(N×log(N)) complexity with 425 * N = n<sub>1</sub> × n<sub>2</sub> ×n<sub>3</sub> × ... 426 * × n<sub>d</sub>, where n<sub>k</sub> is the number of elements in 427 * dimension k, and d is the total number of dimensions. 428 * 429 * @param mdca Multi-Dimensional Complex Array, i.e. {@code Complex[][][][]} 430 * @param type the type of transform (forward, inverse) to be performed 431 * @return transform of {@code mdca} as a Multi-Dimensional Complex Array, i.e. {@code Complex[][][][]} 432 * @throws IllegalArgumentException if any dimension is not a power of two 433 * @deprecated see MATH-736 434 */ 435 @Deprecated 436 public Object mdfft(Object mdca, TransformType type) { 437 MultiDimensionalComplexMatrix mdcm = (MultiDimensionalComplexMatrix) 438 new MultiDimensionalComplexMatrix(mdca).clone(); 439 int[] dimensionSize = mdcm.getDimensionSizes(); 440 //cycle through each dimension 441 for (int i = 0; i < dimensionSize.length; i++) { 442 mdfft(mdcm, type, i, new int[0]); 443 } 444 return mdcm.getArray(); 445 } 446 447 /** 448 * Performs one dimension of a multi-dimensional Fourier transform. 449 * 450 * @param mdcm input matrix 451 * @param type the type of transform (forward, inverse) to be performed 452 * @param d index of the dimension to process 453 * @param subVector recursion subvector 454 * @throws IllegalArgumentException if any dimension is not a power of two 455 * @deprecated see MATH-736 456 */ 457 @Deprecated 458 private void mdfft(MultiDimensionalComplexMatrix mdcm, 459 TransformType type, int d, int[] subVector) { 460 461 int[] dimensionSize = mdcm.getDimensionSizes(); 462 //if done 463 if (subVector.length == dimensionSize.length) { 464 Complex[] temp = new Complex[dimensionSize[d]]; 465 for (int i = 0; i < dimensionSize[d]; i++) { 466 //fft along dimension d 467 subVector[d] = i; 468 temp[i] = mdcm.get(subVector); 469 } 470 471 temp = transform(temp, type); 472 473 for (int i = 0; i < dimensionSize[d]; i++) { 474 subVector[d] = i; 475 mdcm.set(temp[i], subVector); 476 } 477 } else { 478 int[] vector = new int[subVector.length + 1]; 479 System.arraycopy(subVector, 0, vector, 0, subVector.length); 480 if (subVector.length == d) { 481 //value is not important once the recursion is done. 482 //then an fft will be applied along the dimension d. 483 vector[d] = 0; 484 mdfft(mdcm, type, d, vector); 485 } else { 486 for (int i = 0; i < dimensionSize[subVector.length]; i++) { 487 vector[subVector.length] = i; 488 //further split along the next dimension 489 mdfft(mdcm, type, d, vector); 490 } 491 } 492 } 493 } 494 495 /** 496 * Complex matrix implementation. Not designed for synchronized access may 497 * eventually be replaced by jsr-83 of the java community process 498 * http://jcp.org/en/jsr/detail?id=83 499 * may require additional exception throws for other basic requirements. 500 * 501 * @deprecated see MATH-736 502 */ 503 @Deprecated 504 private static class MultiDimensionalComplexMatrix 505 implements Cloneable { 506 507 /** Size in all dimensions. */ 508 protected int[] dimensionSize; 509 510 /** Storage array. */ 511 protected Object multiDimensionalComplexArray; 512 513 /** 514 * Simple constructor. 515 * 516 * @param multiDimensionalComplexArray array containing the matrix 517 * elements 518 */ 519 MultiDimensionalComplexMatrix(Object multiDimensionalComplexArray) { 520 521 this.multiDimensionalComplexArray = multiDimensionalComplexArray; 522 523 // count dimensions 524 int numOfDimensions = 0; 525 for (Object lastDimension = multiDimensionalComplexArray; 526 lastDimension instanceof Object[];) { 527 final Object[] array = (Object[]) lastDimension; 528 numOfDimensions++; 529 lastDimension = array[0]; 530 } 531 532 // allocate array with exact count 533 dimensionSize = new int[numOfDimensions]; 534 535 // fill array 536 numOfDimensions = 0; 537 for (Object lastDimension = multiDimensionalComplexArray; 538 lastDimension instanceof Object[];) { 539 final Object[] array = (Object[]) lastDimension; 540 dimensionSize[numOfDimensions++] = array.length; 541 lastDimension = array[0]; 542 } 543 544 } 545 546 /** 547 * Get a matrix element. 548 * 549 * @param vector indices of the element 550 * @return matrix element 551 * @exception DimensionMismatchException if dimensions do not match 552 */ 553 public Complex get(int... vector) 554 throws DimensionMismatchException { 555 556 if (vector == null) { 557 if (dimensionSize.length > 0) { 558 throw new DimensionMismatchException( 559 0, 560 dimensionSize.length); 561 } 562 return null; 563 } 564 if (vector.length != dimensionSize.length) { 565 throw new DimensionMismatchException( 566 vector.length, 567 dimensionSize.length); 568 } 569 570 Object lastDimension = multiDimensionalComplexArray; 571 572 for (int i = 0; i < dimensionSize.length; i++) { 573 lastDimension = ((Object[]) lastDimension)[vector[i]]; 574 } 575 return (Complex) lastDimension; 576 } 577 578 /** 579 * Set a matrix element. 580 * 581 * @param magnitude magnitude of the element 582 * @param vector indices of the element 583 * @return the previous value 584 * @exception DimensionMismatchException if dimensions do not match 585 */ 586 public Complex set(Complex magnitude, int... vector) 587 throws DimensionMismatchException { 588 589 if (vector == null) { 590 if (dimensionSize.length > 0) { 591 throw new DimensionMismatchException( 592 0, 593 dimensionSize.length); 594 } 595 return null; 596 } 597 if (vector.length != dimensionSize.length) { 598 throw new DimensionMismatchException( 599 vector.length, 600 dimensionSize.length); 601 } 602 603 Object[] lastDimension = (Object[]) multiDimensionalComplexArray; 604 for (int i = 0; i < dimensionSize.length - 1; i++) { 605 lastDimension = (Object[]) lastDimension[vector[i]]; 606 } 607 608 Complex lastValue = (Complex) lastDimension[vector[dimensionSize.length - 1]]; 609 lastDimension[vector[dimensionSize.length - 1]] = magnitude; 610 611 return lastValue; 612 } 613 614 /** 615 * Get the size in all dimensions. 616 * 617 * @return size in all dimensions 618 */ 619 public int[] getDimensionSizes() { 620 return dimensionSize.clone(); 621 } 622 623 /** 624 * Get the underlying storage array. 625 * 626 * @return underlying storage array 627 */ 628 public Object getArray() { 629 return multiDimensionalComplexArray; 630 } 631 632 /** {@inheritDoc} */ 633 @Override 634 public Object clone() { 635 MultiDimensionalComplexMatrix mdcm = 636 new MultiDimensionalComplexMatrix(Array.newInstance( 637 Complex.class, dimensionSize)); 638 clone(mdcm); 639 return mdcm; 640 } 641 642 /** 643 * Copy contents of current array into mdcm. 644 * 645 * @param mdcm array where to copy data 646 */ 647 private void clone(MultiDimensionalComplexMatrix mdcm) { 648 649 int[] vector = new int[dimensionSize.length]; 650 int size = 1; 651 for (int i = 0; i < dimensionSize.length; i++) { 652 size *= dimensionSize[i]; 653 } 654 int[][] vectorList = new int[size][dimensionSize.length]; 655 for (int[] nextVector : vectorList) { 656 System.arraycopy(vector, 0, nextVector, 0, 657 dimensionSize.length); 658 for (int i = 0; i < dimensionSize.length; i++) { 659 vector[i]++; 660 if (vector[i] < dimensionSize[i]) { 661 break; 662 } else { 663 vector[i] = 0; 664 } 665 } 666 } 667 668 for (int[] nextVector : vectorList) { 669 mdcm.set(get(nextVector), nextVector); 670 } 671 } 672 } 673}