FastFourierTransformer.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.  *      http://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. package org.apache.commons.math3.transform;

  18. import java.io.Serializable;
  19. import java.lang.reflect.Array;

  20. import org.apache.commons.math3.analysis.FunctionUtils;
  21. import org.apache.commons.math3.analysis.UnivariateFunction;
  22. import org.apache.commons.math3.complex.Complex;
  23. import org.apache.commons.math3.exception.DimensionMismatchException;
  24. import org.apache.commons.math3.exception.MathIllegalArgumentException;
  25. import org.apache.commons.math3.exception.MathIllegalStateException;
  26. import org.apache.commons.math3.exception.util.LocalizedFormats;
  27. import org.apache.commons.math3.util.ArithmeticUtils;
  28. import org.apache.commons.math3.util.FastMath;
  29. import org.apache.commons.math3.util.MathArrays;

  30. /**
  31.  * Implements the Fast Fourier Transform for transformation of one-dimensional
  32.  * real or complex data sets. For reference, see <em>Applied Numerical Linear
  33.  * Algebra</em>, ISBN 0898713897, chapter 6.
  34.  * <p>
  35.  * There are several variants of the discrete Fourier transform, with various
  36.  * normalization conventions, which are specified by the parameter
  37.  * {@link DftNormalization}.
  38.  * <p>
  39.  * The current implementation of the discrete Fourier transform as a fast
  40.  * Fourier transform requires the length of the data set to be a power of 2.
  41.  * This greatly simplifies and speeds up the code. Users can pad the data with
  42.  * zeros to meet this requirement. There are other flavors of FFT, for
  43.  * reference, see S. Winograd,
  44.  * <i>On computing the discrete Fourier transform</i>, Mathematics of
  45.  * Computation, 32 (1978), 175 - 199.
  46.  *
  47.  * @see DftNormalization
  48.  * @since 1.2
  49.  */
  50. public class FastFourierTransformer implements Serializable {

  51.     /** Serializable version identifier. */
  52.     static final long serialVersionUID = 20120210L;

  53.     /**
  54.      * {@code W_SUB_N_R[i]} is the real part of
  55.      * {@code exp(- 2 * i * pi / n)}:
  56.      * {@code W_SUB_N_R[i] = cos(2 * pi/ n)}, where {@code n = 2^i}.
  57.      */
  58.     private static final double[] W_SUB_N_R =
  59.             {  0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1
  60.             , 0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1
  61.             , 0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1
  62.             , 0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1
  63.             , 0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1
  64.             , 0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1
  65.             , 0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1
  66.             , 0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0
  67.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  68.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  69.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  70.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  71.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  72.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  73.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  74.             , 0x1.0p0, 0x1.0p0, 0x1.0p0 };

  75.     /**
  76.      * {@code W_SUB_N_I[i]} is the imaginary part of
  77.      * {@code exp(- 2 * i * pi / n)}:
  78.      * {@code W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}.
  79.      */
  80.     private static final double[] W_SUB_N_I =
  81.             {  0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1
  82.             , -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5
  83.             , -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9
  84.             , -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13
  85.             , -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17
  86.             , -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21
  87.             , -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25
  88.             , -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29
  89.             , -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33
  90.             , -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37
  91.             , -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41
  92.             , -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45
  93.             , -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49
  94.             , -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53
  95.             , -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57
  96.             , -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 };

  97.     /** The type of DFT to be performed. */
  98.     private final DftNormalization normalization;

  99.     /**
  100.      * Creates a new instance of this class, with various normalization
  101.      * conventions.
  102.      *
  103.      * @param normalization the type of normalization to be applied to the
  104.      * transformed data
  105.      */
  106.     public FastFourierTransformer(final DftNormalization normalization) {
  107.         this.normalization = normalization;
  108.     }

  109.     /**
  110.      * Performs identical index bit reversal shuffles on two arrays of identical
  111.      * size. Each element in the array is swapped with another element based on
  112.      * the bit-reversal of the index. For example, in an array with length 16,
  113.      * item at binary index 0011 (decimal 3) would be swapped with the item at
  114.      * binary index 1100 (decimal 12).
  115.      *
  116.      * @param a the first array to be shuffled
  117.      * @param b the second array to be shuffled
  118.      */
  119.     private static void bitReversalShuffle2(double[] a, double[] b) {
  120.         final int n = a.length;
  121.         assert b.length == n;
  122.         final int halfOfN = n >> 1;

  123.         int j = 0;
  124.         for (int i = 0; i < n; i++) {
  125.             if (i < j) {
  126.                 // swap indices i & j
  127.                 double temp = a[i];
  128.                 a[i] = a[j];
  129.                 a[j] = temp;

  130.                 temp = b[i];
  131.                 b[i] = b[j];
  132.                 b[j] = temp;
  133.             }

  134.             int k = halfOfN;
  135.             while (k <= j && k > 0) {
  136.                 j -= k;
  137.                 k >>= 1;
  138.             }
  139.             j += k;
  140.         }
  141.     }

  142.     /**
  143.      * Applies the proper normalization to the specified transformed data.
  144.      *
  145.      * @param dataRI the unscaled transformed data
  146.      * @param normalization the normalization to be applied
  147.      * @param type the type of transform (forward, inverse) which resulted in the specified data
  148.      */
  149.     private static void normalizeTransformedData(final double[][] dataRI,
  150.         final DftNormalization normalization, final TransformType type) {

  151.         final double[] dataR = dataRI[0];
  152.         final double[] dataI = dataRI[1];
  153.         final int n = dataR.length;
  154.         assert dataI.length == n;

  155.         switch (normalization) {
  156.             case STANDARD:
  157.                 if (type == TransformType.INVERSE) {
  158.                     final double scaleFactor = 1.0 / ((double) n);
  159.                     for (int i = 0; i < n; i++) {
  160.                         dataR[i] *= scaleFactor;
  161.                         dataI[i] *= scaleFactor;
  162.                     }
  163.                 }
  164.                 break;
  165.             case UNITARY:
  166.                 final double scaleFactor = 1.0 / FastMath.sqrt(n);
  167.                 for (int i = 0; i < n; i++) {
  168.                     dataR[i] *= scaleFactor;
  169.                     dataI[i] *= scaleFactor;
  170.                 }
  171.                 break;
  172.             default:
  173.                 /*
  174.                  * This should never occur in normal conditions. However this
  175.                  * clause has been added as a safeguard if other types of
  176.                  * normalizations are ever implemented, and the corresponding
  177.                  * test is forgotten in the present switch.
  178.                  */
  179.                 throw new MathIllegalStateException();
  180.         }
  181.     }

  182.     /**
  183.      * Computes the standard transform of the specified complex data. The
  184.      * computation is done in place. The input data is laid out as follows
  185.      * <ul>
  186.      *   <li>{@code dataRI[0][i]} is the real part of the {@code i}-th data point,</li>
  187.      *   <li>{@code dataRI[1][i]} is the imaginary part of the {@code i}-th data point.</li>
  188.      * </ul>
  189.      *
  190.      * @param dataRI the two dimensional array of real and imaginary parts of the data
  191.      * @param normalization the normalization to be applied to the transformed data
  192.      * @param type the type of transform (forward, inverse) to be performed
  193.      * @throws DimensionMismatchException if the number of rows of the specified
  194.      *   array is not two, or the array is not rectangular
  195.      * @throws MathIllegalArgumentException if the number of data points is not
  196.      *   a power of two
  197.      */
  198.     public static void transformInPlace(final double[][] dataRI,
  199.         final DftNormalization normalization, final TransformType type) {

  200.         if (dataRI.length != 2) {
  201.             throw new DimensionMismatchException(dataRI.length, 2);
  202.         }
  203.         final double[] dataR = dataRI[0];
  204.         final double[] dataI = dataRI[1];
  205.         if (dataR.length != dataI.length) {
  206.             throw new DimensionMismatchException(dataI.length, dataR.length);
  207.         }

  208.         final int n = dataR.length;
  209.         if (!ArithmeticUtils.isPowerOfTwo(n)) {
  210.             throw new MathIllegalArgumentException(
  211.                 LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING,
  212.                 Integer.valueOf(n));
  213.         }

  214.         if (n == 1) {
  215.             return;
  216.         } else if (n == 2) {
  217.             final double srcR0 = dataR[0];
  218.             final double srcI0 = dataI[0];
  219.             final double srcR1 = dataR[1];
  220.             final double srcI1 = dataI[1];

  221.             // X_0 = x_0 + x_1
  222.             dataR[0] = srcR0 + srcR1;
  223.             dataI[0] = srcI0 + srcI1;
  224.             // X_1 = x_0 - x_1
  225.             dataR[1] = srcR0 - srcR1;
  226.             dataI[1] = srcI0 - srcI1;

  227.             normalizeTransformedData(dataRI, normalization, type);
  228.             return;
  229.         }

  230.         bitReversalShuffle2(dataR, dataI);

  231.         // Do 4-term DFT.
  232.         if (type == TransformType.INVERSE) {
  233.             for (int i0 = 0; i0 < n; i0 += 4) {
  234.                 final int i1 = i0 + 1;
  235.                 final int i2 = i0 + 2;
  236.                 final int i3 = i0 + 3;

  237.                 final double srcR0 = dataR[i0];
  238.                 final double srcI0 = dataI[i0];
  239.                 final double srcR1 = dataR[i2];
  240.                 final double srcI1 = dataI[i2];
  241.                 final double srcR2 = dataR[i1];
  242.                 final double srcI2 = dataI[i1];
  243.                 final double srcR3 = dataR[i3];
  244.                 final double srcI3 = dataI[i3];

  245.                 // 4-term DFT
  246.                 // X_0 = x_0 + x_1 + x_2 + x_3
  247.                 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
  248.                 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
  249.                 // X_1 = x_0 - x_2 + j * (x_3 - x_1)
  250.                 dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
  251.                 dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
  252.                 // X_2 = x_0 - x_1 + x_2 - x_3
  253.                 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
  254.                 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
  255.                 // X_3 = x_0 - x_2 + j * (x_1 - x_3)
  256.                 dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
  257.                 dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
  258.             }
  259.         } else {
  260.             for (int i0 = 0; i0 < n; i0 += 4) {
  261.                 final int i1 = i0 + 1;
  262.                 final int i2 = i0 + 2;
  263.                 final int i3 = i0 + 3;

  264.                 final double srcR0 = dataR[i0];
  265.                 final double srcI0 = dataI[i0];
  266.                 final double srcR1 = dataR[i2];
  267.                 final double srcI1 = dataI[i2];
  268.                 final double srcR2 = dataR[i1];
  269.                 final double srcI2 = dataI[i1];
  270.                 final double srcR3 = dataR[i3];
  271.                 final double srcI3 = dataI[i3];

  272.                 // 4-term DFT
  273.                 // X_0 = x_0 + x_1 + x_2 + x_3
  274.                 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
  275.                 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
  276.                 // X_1 = x_0 - x_2 + j * (x_3 - x_1)
  277.                 dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
  278.                 dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
  279.                 // X_2 = x_0 - x_1 + x_2 - x_3
  280.                 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
  281.                 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
  282.                 // X_3 = x_0 - x_2 + j * (x_1 - x_3)
  283.                 dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
  284.                 dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
  285.             }
  286.         }

  287.         int lastN0 = 4;
  288.         int lastLogN0 = 2;
  289.         while (lastN0 < n) {
  290.             int n0 = lastN0 << 1;
  291.             int logN0 = lastLogN0 + 1;
  292.             double wSubN0R = W_SUB_N_R[logN0];
  293.             double wSubN0I = W_SUB_N_I[logN0];
  294.             if (type == TransformType.INVERSE) {
  295.                 wSubN0I = -wSubN0I;
  296.             }

  297.             // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2).
  298.             for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) {
  299.                 int destOddStartIndex = destEvenStartIndex + lastN0;

  300.                 double wSubN0ToRR = 1;
  301.                 double wSubN0ToRI = 0;

  302.                 for (int r = 0; r < lastN0; r++) {
  303.                     double grR = dataR[destEvenStartIndex + r];
  304.                     double grI = dataI[destEvenStartIndex + r];
  305.                     double hrR = dataR[destOddStartIndex + r];
  306.                     double hrI = dataI[destOddStartIndex + r];

  307.                     // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr
  308.                     dataR[destEvenStartIndex + r] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI;
  309.                     dataI[destEvenStartIndex + r] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR;
  310.                     // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr
  311.                     dataR[destOddStartIndex + r] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI);
  312.                     dataI[destOddStartIndex + r] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR);

  313.                     // WsubN0ToR *= WsubN0R
  314.                     double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
  315.                     double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
  316.                     wSubN0ToRR = nextWsubN0ToRR;
  317.                     wSubN0ToRI = nextWsubN0ToRI;
  318.                 }
  319.             }

  320.             lastN0 = n0;
  321.             lastLogN0 = logN0;
  322.         }

  323.         normalizeTransformedData(dataRI, normalization, type);
  324.     }

  325.     /**
  326.      * Returns the (forward, inverse) transform of the specified real data set.
  327.      *
  328.      * @param f the real data array to be transformed
  329.      * @param type the type of transform (forward, inverse) to be performed
  330.      * @return the complex transformed array
  331.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  332.      */
  333.     public Complex[] transform(final double[] f, final TransformType type) {
  334.         final double[][] dataRI = new double[][] {
  335.             MathArrays.copyOf(f, f.length), new double[f.length]
  336.         };

  337.         transformInPlace(dataRI, normalization, type);

  338.         return TransformUtils.createComplexArray(dataRI);
  339.     }

  340.     /**
  341.      * Returns the (forward, inverse) transform of the specified real function,
  342.      * sampled on the specified interval.
  343.      *
  344.      * @param f the function to be sampled and transformed
  345.      * @param min the (inclusive) lower bound for the interval
  346.      * @param max the (exclusive) upper bound for the interval
  347.      * @param n the number of sample points
  348.      * @param type the type of transform (forward, inverse) to be performed
  349.      * @return the complex transformed array
  350.      * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
  351.      *   if the lower bound is greater than, or equal to the upper bound
  352.      * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
  353.      *   if the number of sample points {@code n} is negative
  354.      * @throws MathIllegalArgumentException if the number of sample points
  355.      *   {@code n} is not a power of two
  356.      */
  357.     public Complex[] transform(final UnivariateFunction f,
  358.                                final double min, final double max, final int n,
  359.                                final TransformType type) {

  360.         final double[] data = FunctionUtils.sample(f, min, max, n);
  361.         return transform(data, type);
  362.     }

  363.     /**
  364.      * Returns the (forward, inverse) transform of the specified complex data set.
  365.      *
  366.      * @param f the complex data array to be transformed
  367.      * @param type the type of transform (forward, inverse) to be performed
  368.      * @return the complex transformed array
  369.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  370.      */
  371.     public Complex[] transform(final Complex[] f, final TransformType type) {
  372.         final double[][] dataRI = TransformUtils.createRealImaginaryArray(f);

  373.         transformInPlace(dataRI, normalization, type);

  374.         return TransformUtils.createComplexArray(dataRI);
  375.     }

  376.     /**
  377.      * Performs a multi-dimensional Fourier transform on a given array. Use
  378.      * {@link #transform(Complex[], TransformType)} in a row-column
  379.      * implementation in any number of dimensions with
  380.      * O(N&times;log(N)) complexity with
  381.      * N = n<sub>1</sub> &times; n<sub>2</sub> &times;n<sub>3</sub> &times; ...
  382.      * &times; n<sub>d</sub>, where n<sub>k</sub> is the number of elements in
  383.      * dimension k, and d is the total number of dimensions.
  384.      *
  385.      * @param mdca Multi-Dimensional Complex Array, i.e. {@code Complex[][][][]}
  386.      * @param type the type of transform (forward, inverse) to be performed
  387.      * @return transform of {@code mdca} as a Multi-Dimensional Complex Array, i.e. {@code Complex[][][][]}
  388.      * @throws IllegalArgumentException if any dimension is not a power of two
  389.      * @deprecated see MATH-736
  390.      */
  391.     @Deprecated
  392.     public Object mdfft(Object mdca, TransformType type) {
  393.         MultiDimensionalComplexMatrix mdcm = (MultiDimensionalComplexMatrix)
  394.                 new MultiDimensionalComplexMatrix(mdca).clone();
  395.         int[] dimensionSize = mdcm.getDimensionSizes();
  396.         //cycle through each dimension
  397.         for (int i = 0; i < dimensionSize.length; i++) {
  398.             mdfft(mdcm, type, i, new int[0]);
  399.         }
  400.         return mdcm.getArray();
  401.     }

  402.     /**
  403.      * Performs one dimension of a multi-dimensional Fourier transform.
  404.      *
  405.      * @param mdcm input matrix
  406.      * @param type the type of transform (forward, inverse) to be performed
  407.      * @param d index of the dimension to process
  408.      * @param subVector recursion subvector
  409.      * @throws IllegalArgumentException if any dimension is not a power of two
  410.      * @deprecated see MATH-736
  411.      */
  412.     @Deprecated
  413.     private void mdfft(MultiDimensionalComplexMatrix mdcm,
  414.             TransformType type, int d, int[] subVector) {

  415.         int[] dimensionSize = mdcm.getDimensionSizes();
  416.         //if done
  417.         if (subVector.length == dimensionSize.length) {
  418.             Complex[] temp = new Complex[dimensionSize[d]];
  419.             for (int i = 0; i < dimensionSize[d]; i++) {
  420.                 //fft along dimension d
  421.                 subVector[d] = i;
  422.                 temp[i] = mdcm.get(subVector);
  423.             }

  424.             temp = transform(temp, type);

  425.             for (int i = 0; i < dimensionSize[d]; i++) {
  426.                 subVector[d] = i;
  427.                 mdcm.set(temp[i], subVector);
  428.             }
  429.         } else {
  430.             int[] vector = new int[subVector.length + 1];
  431.             System.arraycopy(subVector, 0, vector, 0, subVector.length);
  432.             if (subVector.length == d) {
  433.                 //value is not important once the recursion is done.
  434.                 //then an fft will be applied along the dimension d.
  435.                 vector[d] = 0;
  436.                 mdfft(mdcm, type, d, vector);
  437.             } else {
  438.                 for (int i = 0; i < dimensionSize[subVector.length]; i++) {
  439.                     vector[subVector.length] = i;
  440.                     //further split along the next dimension
  441.                     mdfft(mdcm, type, d, vector);
  442.                 }
  443.             }
  444.         }
  445.     }

  446.     /**
  447.      * Complex matrix implementation. Not designed for synchronized access may
  448.      * eventually be replaced by jsr-83 of the java community process
  449.      * http://jcp.org/en/jsr/detail?id=83
  450.      * may require additional exception throws for other basic requirements.
  451.      *
  452.      * @deprecated see MATH-736
  453.      */
  454.     @Deprecated
  455.     private static class MultiDimensionalComplexMatrix
  456.         implements Cloneable {

  457.         /** Size in all dimensions. */
  458.         protected int[] dimensionSize;

  459.         /** Storage array. */
  460.         protected Object multiDimensionalComplexArray;

  461.         /**
  462.          * Simple constructor.
  463.          *
  464.          * @param multiDimensionalComplexArray array containing the matrix
  465.          * elements
  466.          */
  467.         public MultiDimensionalComplexMatrix(
  468.                 Object multiDimensionalComplexArray) {

  469.             this.multiDimensionalComplexArray = multiDimensionalComplexArray;

  470.             // count dimensions
  471.             int numOfDimensions = 0;
  472.             for (Object lastDimension = multiDimensionalComplexArray;
  473.                  lastDimension instanceof Object[];) {
  474.                 final Object[] array = (Object[]) lastDimension;
  475.                 numOfDimensions++;
  476.                 lastDimension = array[0];
  477.             }

  478.             // allocate array with exact count
  479.             dimensionSize = new int[numOfDimensions];

  480.             // fill array
  481.             numOfDimensions = 0;
  482.             for (Object lastDimension = multiDimensionalComplexArray;
  483.                  lastDimension instanceof Object[];) {
  484.                 final Object[] array = (Object[]) lastDimension;
  485.                 dimensionSize[numOfDimensions++] = array.length;
  486.                 lastDimension = array[0];
  487.             }

  488.         }

  489.         /**
  490.          * Get a matrix element.
  491.          *
  492.          * @param vector indices of the element
  493.          * @return matrix element
  494.          * @exception DimensionMismatchException if dimensions do not match
  495.          */
  496.         public Complex get(int... vector)
  497.                 throws DimensionMismatchException {

  498.             if (vector == null) {
  499.                 if (dimensionSize.length > 0) {
  500.                     throw new DimensionMismatchException(
  501.                             0,
  502.                             dimensionSize.length);
  503.                 }
  504.                 return null;
  505.             }
  506.             if (vector.length != dimensionSize.length) {
  507.                 throw new DimensionMismatchException(
  508.                         vector.length,
  509.                         dimensionSize.length);
  510.             }

  511.             Object lastDimension = multiDimensionalComplexArray;

  512.             for (int i = 0; i < dimensionSize.length; i++) {
  513.                 lastDimension = ((Object[]) lastDimension)[vector[i]];
  514.             }
  515.             return (Complex) lastDimension;
  516.         }

  517.         /**
  518.          * Set a matrix element.
  519.          *
  520.          * @param magnitude magnitude of the element
  521.          * @param vector indices of the element
  522.          * @return the previous value
  523.          * @exception DimensionMismatchException if dimensions do not match
  524.          */
  525.         public Complex set(Complex magnitude, int... vector)
  526.                 throws DimensionMismatchException {

  527.             if (vector == null) {
  528.                 if (dimensionSize.length > 0) {
  529.                     throw new DimensionMismatchException(
  530.                             0,
  531.                             dimensionSize.length);
  532.                 }
  533.                 return null;
  534.             }
  535.             if (vector.length != dimensionSize.length) {
  536.                 throw new DimensionMismatchException(
  537.                         vector.length,
  538.                         dimensionSize.length);
  539.             }

  540.             Object[] lastDimension = (Object[]) multiDimensionalComplexArray;
  541.             for (int i = 0; i < dimensionSize.length - 1; i++) {
  542.                 lastDimension = (Object[]) lastDimension[vector[i]];
  543.             }

  544.             Complex lastValue = (Complex) lastDimension[vector[dimensionSize.length - 1]];
  545.             lastDimension[vector[dimensionSize.length - 1]] = magnitude;

  546.             return lastValue;
  547.         }

  548.         /**
  549.          * Get the size in all dimensions.
  550.          *
  551.          * @return size in all dimensions
  552.          */
  553.         public int[] getDimensionSizes() {
  554.             return dimensionSize.clone();
  555.         }

  556.         /**
  557.          * Get the underlying storage array.
  558.          *
  559.          * @return underlying storage array
  560.          */
  561.         public Object getArray() {
  562.             return multiDimensionalComplexArray;
  563.         }

  564.         /** {@inheritDoc} */
  565.         @Override
  566.         public Object clone() {
  567.             MultiDimensionalComplexMatrix mdcm =
  568.                     new MultiDimensionalComplexMatrix(Array.newInstance(
  569.                     Complex.class, dimensionSize));
  570.             clone(mdcm);
  571.             return mdcm;
  572.         }

  573.         /**
  574.          * Copy contents of current array into mdcm.
  575.          *
  576.          * @param mdcm array where to copy data
  577.          */
  578.         private void clone(MultiDimensionalComplexMatrix mdcm) {

  579.             int[] vector = new int[dimensionSize.length];
  580.             int size = 1;
  581.             for (int i = 0; i < dimensionSize.length; i++) {
  582.                 size *= dimensionSize[i];
  583.             }
  584.             int[][] vectorList = new int[size][dimensionSize.length];
  585.             for (int[] nextVector : vectorList) {
  586.                 System.arraycopy(vector, 0, nextVector, 0,
  587.                                  dimensionSize.length);
  588.                 for (int i = 0; i < dimensionSize.length; i++) {
  589.                     vector[i]++;
  590.                     if (vector[i] < dimensionSize[i]) {
  591.                         break;
  592.                     } else {
  593.                         vector[i] = 0;
  594.                     }
  595.                 }
  596.             }

  597.             for (int[] nextVector : vectorList) {
  598.                 mdcm.set(get(nextVector), nextVector);
  599.             }
  600.         }
  601.     }
  602. }