001/* A library of signal processing operations.
002
003 The algorithms for the FFT and DCT are based on the FFCT algorithm
004 described in:
005
006 Martin Vetterli and Henri J. Nussbaumer."Simple FFT and DCT Algorithms with
007 Reduced Number of Operations". Signal Processing 6 (1984) 267-278.
008
009 Copyright (c) 1998-2018 The Regents of the University of California.
010 All rights reserved.
011
012 Permission is hereby granted, without written agreement and without
013 license or royalty fees, to use, copy, modify, and distribute this
014 software and its documentation for any purpose, provided that the above
015 copyright notice and the following two paragraphs appear in all copies
016 of this software.
017
018 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
019 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
020 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
021 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
022 SUCH DAMAGE.
023
024 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
025 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
026 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
027 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
028 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
029 ENHANCEMENTS, OR MODIFICATIONS.
030
031 PT_COPYRIGHT_VERSION_2
032 COPYRIGHTENDKEY
033
034
035 */
036package ptolemy.math;
037
038///////////////////////////////////////////////////////////////////
039//// SignalProcessing
040
041/**
042 This class provides signal processing functions.
043
044 The algorithms for the FFT and DCT are based on the FFCT algorithm
045 described in:
046
047 Martin Vetterli and Henri J. Nussbaumer."Simple FFT and DCT Algorithms with
048 Reduced Number of Operations". Signal Processing 6 (1984) 267-278.
049
050 @author Albert Chen, William Wu, Edward A. Lee, Jeff Tsay, Elaine Cheong
051 @version $Id$
052 @since Ptolemy II 0.2
053 @Pt.ProposedRating Yellow (ctsay)
054 @Pt.AcceptedRating Red (cxh)
055 */
056public class SignalProcessing {
057    // The only constructor is private so that this class cannot
058    // be instantiated.
059    private SignalProcessing() {
060    }
061
062    ///////////////////////////////////////////////////////////////////
063    ////                         public methods                    ////
064
065    /** Return true if the first argument is close to the second (within
066     *  EPSILON, where EPSILON is a static public variable of this class).
067     */
068    public static final boolean close(double first, double second) {
069        double diff = first - second;
070        return Math.abs(diff) < EPSILON;
071    }
072
073    /** Return a new array that is the convolution of the two argument arrays.
074     *  The length of the new array is equal to the sum of the lengths of the
075     *  two argument arrays minus one.  Note that convolution is the same
076     *  as polynomial multiplication.  If the two argument arrays represent
077     *  the coefficients of two polynomials, then the resulting array
078     *  represents the coefficients of the product polynomial.
079     *  @param array1 The first array.
080     *  @param array2 The second array.
081     *  @return A new array of doubles.
082     */
083    public static final double[] convolve(double[] array1, double[] array2) {
084        double[] result;
085        int resultSize = array1.length + array2.length - 1;
086
087        if (resultSize < 0) {
088            // If we attempt to convolve two zero length arrays, return
089            // a zero length array.
090            result = new double[0];
091            return result;
092        }
093
094        result = new double[resultSize];
095
096        // The result is assumed initialized to zero.
097        for (int i = 0; i < array1.length; i++) {
098            for (int j = 0; j < array2.length; j++) {
099                result[i + j] += array1[i] * array2[j];
100            }
101        }
102
103        return result;
104    }
105
106    /** Return a new array that is the convolution of two complex arrays.
107     *  The length of the new array is equal to the sum of the lengths of the
108     *  two argument arrays minus one.  Note that some authors define
109     *  complex convolution slightly differently as the convolution of the
110     *  first array with the <em>conjugate</em> of the second.  If you need
111     *  to use that definition, then conjugate the second array before
112     *  calling this method. Convolution defined as we do here is the
113     *  same as polynomial multiplication.  If the two argument arrays
114     *  represent the coefficients of two polynomials, then the resulting
115     *  array represents the coefficients of the product polynomial.
116     *  @param array1 The first array.
117     *  @param array2 The second array.
118     *  @return A new array of complex numbers.
119     */
120    public static final Complex[] convolve(Complex[] array1, Complex[] array2) {
121        Complex[] result;
122        int resultSize = array1.length + array2.length - 1;
123
124        if (resultSize < 0) {
125            // If we attempt to convolve two zero length arrays, return
126            // a zero length array.
127            result = new Complex[0];
128            return result;
129        }
130
131        double[] reals = new double[resultSize];
132        double[] imags = new double[resultSize];
133
134        for (int i = 0; i < array1.length; i++) {
135            for (int j = 0; j < array2.length; j++) {
136                reals[i + j] += array1[i].real * array2[j].real
137                        - array1[i].imag * array2[j].imag;
138                imags[i + j] += array1[i].imag * array2[j].real
139                        + array1[i].real * array2[j].imag;
140            }
141        }
142
143        result = new Complex[resultSize];
144
145        for (int i = 0; i < result.length; i++) {
146            result[i] = new Complex(reals[i], imags[i]);
147        }
148
149        return result;
150    }
151
152    /** Return a new array of doubles that is the forward, normalized
153     *  DCT of the input array of doubles.
154     *  This method automatically computes the order of the transform
155     *  based on the length of the input array. It is equivalent to
156     *  DCT(x, order, DCT_TYPE_NORMALIZED), where 2^order is the smallest
157     *  power of two greater than or equal to the length of the specified
158     *  array.  The length of the result is 2^order.
159     *  @param x An array of doubles.
160     *  @return A new array of doubles, with length 2^order.
161     */
162    public static final double[] DCT(double[] x) {
163        return DCT(x, order(x.length), DCT_TYPE_NORMALIZED);
164    }
165
166    /** Return a new array of doubles that is the forward, normalized
167     *  DCT of the input array of doubles.
168     *  This method is equivalent to
169     *  DCT(x, order, DCT_TYPE_NORMALIZED).
170     *  @param x An array of doubles.
171     *  @param order Log base 2 of the size of the transform.
172     *  @return A new array of doubles, with length 2^order.
173     */
174    public static final double[] DCT(double[] x, int order) {
175        return DCT(x, order, DCT_TYPE_NORMALIZED);
176    }
177
178    /** Return a new array of doubles that is the forward DCT of the
179     *  input array of doubles.
180     *  See the DCT_TYPE_XXX constants for documentation of the
181     *  exact formula, which depends on the type.
182     *  @param x An array of doubles.
183     *  @param order Log base 2 of the size of the transform.
184     *  @param type The type of DCT, which is one of DCT_TYPE_NORMALIZED,
185     *   DCT_TYPE_UNNORMALIZED, or DCT_TYPE_ORTHONORMAL.
186     *  @see #DCT_TYPE_NORMALIZED
187     *  @see #DCT_TYPE_UNNORMALIZED
188     *  @see #DCT_TYPE_ORTHONORMAL
189     *  @return A new array of doubles, with length 2^order.
190     */
191    public static final double[] DCT(double[] x, int order, int type) {
192        _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
193
194        if (type >= DCT_TYPES) {
195            throw new IllegalArgumentException(
196                    "ptolemy.math.SignalProcessing.DCT(): Unrecognized DCT type");
197        }
198
199        int size = 1 << order;
200
201        // Make sure the tables have enough entries for the DCT computation
202        if (order > _FFCTGenLimit) {
203            _FFCTTableGen(order);
204        }
205
206        double[] returnValue = _DCT(x, size, order);
207
208        switch (type) {
209        case DCT_TYPE_ORTHONORMAL:
210
211            double factor = Math.sqrt(2.0 / size);
212            returnValue = DoubleArrayMath.scale(returnValue, factor);
213
214            // no break here
215        case DCT_TYPE_NORMALIZED:
216            returnValue[0] *= ExtendedMath.ONE_OVER_SQRT_2;
217            break;
218        }
219
220        return returnValue;
221    }
222
223    /** Return the value of the argument
224     *  in decibels, which is defined to be 20*log<sub>10</sub>(<em>z</em>),
225     *  where <em>z</em> is the argument.
226     *  Note that if the input represents power, which is proportional to a
227     *  magnitude squared, then this should be divided
228     *  by two to get 10*log<sub>10</sub>(<em>z</em>).
229     *  @param value The value to convert to decibels.
230     *  @deprecated Use toDecibels() instead.
231     *  @see #toDecibels(double)
232     */
233    @Deprecated
234    public static final double decibel(double value) {
235        return toDecibels(value);
236    }
237
238    /** Return a new array the value of the argument array
239     *  in decibels, using the previous decibel() method.
240     *  You may wish to combine this with DoubleArrayMath.limit().
241     *  @deprecated Use toDecibels() instead.
242     */
243    @Deprecated
244    public static final double[] decibel(double[] values) {
245        double[] result = new double[values.length];
246
247        for (int i = values.length - 1; i >= 0; i--) {
248            result[i] = toDecibels(values[i]);
249        }
250
251        return result;
252    }
253
254    /** Return a new array that is formed by taking every nth sample
255     *  starting with the 0th sample, and discarding the rest.
256     *  This method calls :
257     *  downsample(x, n, 0)
258     *  @param x An array of doubles.
259     *  @param n An integer specifying the downsampling factor.
260     *  @return A new array of doubles of length = floor(L / n), where
261     *  L is the size of the input array.
262     */
263    public static final double[] downsample(double[] x, int n) {
264        return downsample(x, n, 0);
265    }
266
267    /** Return a new array that is formed by taking every nth sample
268     *  starting at startIndex, and discarding the samples in between.
269     *  @param x An array of doubles.
270     *  @param n An integer specifying the downsampling factor.
271     *  @param startIndex An integer specifying the index of sample at
272     *  which to start downsampling. This integer must be between 0 and
273     *  L - 1, where L is the size of the input array.
274     *  @return A new array of doubles of length =
275     *  floor((L - startIndex) / n),
276     *  where L is the size of the input array.
277     */
278    public static final double[] downsample(double[] x, int n, int startIndex) {
279        if (x.length <= 0) {
280            throw new IllegalArgumentException(
281                    "ptolemy.math.SignalProcessing.downsample(): "
282                            + "array length must be greater than 0.");
283        }
284
285        if (n <= 0) {
286            throw new IllegalArgumentException(
287                    "ptolemy.math.SignalProcessing.downsample(): "
288                            + "downsampling factor must be greater than 0.");
289        }
290
291        if (startIndex < 0 || startIndex > x.length - 1) {
292            throw new IllegalArgumentException(
293                    "ptolemy.math.SignalProcessing.downsample(): "
294                            + "startIndex must be between 0 and L - 1, where L is the "
295                            + "size of the input array.");
296        }
297
298        int length = (x.length + 1 - startIndex) / n;
299        double[] returnValue = new double[length];
300
301        int destIndex;
302        int srcIndex = startIndex;
303
304        for (destIndex = 0; destIndex < length; destIndex++) {
305            returnValue[destIndex] = x[srcIndex];
306            srcIndex += n;
307        }
308
309        return returnValue;
310    }
311
312    /** Return a new array of complex numbers which is the FFT
313     *  of an input array of complex numbers.  The order of the transform
314     *  is the next power of two greater than the length of the argument.
315     *  The input is zero-padded if it does not match this length.
316     *  @param x An array of complex numbers.
317     *  @return The FFT of the argument.
318     */
319    public static final Complex[] FFT(Complex[] x) {
320        return FFTComplexOut(x, order(x.length));
321    }
322
323    /** Return a new array of complex numbers which is the FFT
324     *  of an input array of complex numbers.
325     *  The input is zero-padded if it does not match the length.
326     *  @param x An array of complex numbers.
327     *  @param order The log base 2 of the length of the FFT.
328     *  @return The FFT of the argument.
329     */
330    public static final Complex[] FFT(Complex[] x, int order) {
331        return FFTComplexOut(x, order);
332    }
333
334    /** Return a new array of Complex's which is the forward FFT
335     *  of an input array of Complex's.
336     *  This method automatically computes the order of the transform
337     *  based on the length of the input array, and calls
338     *  FFTComplexOut(x, order).
339     *  @param x An array of Complex's.
340     *  @return A new array of Complex's.
341     */
342    public static final Complex[] FFTComplexOut(Complex[] x) {
343        return FFTComplexOut(x, order(x.length));
344    }
345
346    /** Return a new array of Complex's which is the forward FFT
347     *  of an input array of Complex's.
348     *  @param x An array of Complex's.
349     *  @param order The base-2 logarithm of the size of the transform.
350     *  @return A new array of Complex's.
351     */
352    public static final Complex[] FFTComplexOut(Complex[] x, int order) {
353        x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
354
355        double[] realx = ComplexArrayMath.realParts(x);
356        double[] realrealX = FFTRealOut(realx, order);
357        double[] imagrealX = FFTImagOut(realx, order);
358
359        double[] imagx = ComplexArrayMath.imagParts(x);
360        double[] realimagX = FFTRealOut(imagx, order);
361        double[] imagimagX = FFTImagOut(imagx, order);
362
363        realrealX = DoubleArrayMath.subtract(realrealX, imagimagX);
364        imagrealX = DoubleArrayMath.add(imagrealX, realimagX);
365
366        return ComplexArrayMath.formComplexArray(realrealX, imagrealX);
367    }
368
369    /** Return a new array of Complex's which is the forward FFT
370     *  of a real input array of doubles.
371     *  This method automatically computes the order of the transform
372     *  based on the length of the input array, and calls
373     *  FFTComplexOut(x, order).
374     *  @param x An array of doubles.
375     *  @return A new array of Complex's.
376     */
377    public static final Complex[] FFTComplexOut(double[] x) {
378        return FFTComplexOut(x, order(x.length));
379    }
380
381    /** Return a new array of Complex's which is the forward FFT
382     *  of a real input array of doubles.
383     *  This method is half as expensive as computing the FFT of a
384     *  Complex array.
385     *  @param x An array of doubles.
386     *  @param order The base-2 logarithm of the size of the transform.
387     *  @return A new array of Complex's.
388     */
389    public static final Complex[] FFTComplexOut(double[] x, int order) {
390        // Argument checking is done inside FFTRealOut() and FFTImagOut()
391        double[] realPart = FFTRealOut(x, order);
392        double[] imagPart = FFTImagOut(x, order);
393
394        return ComplexArrayMath.formComplexArray(realPart, imagPart);
395    }
396
397    /** Return a new array of doubles which is the imaginary part of the
398     *  FFT of an input array of Complex's.
399     *  This method automatically computes the order of the transform
400     *  based on the length of the input array, and calls
401     *  FFTImagOut(x, order).
402     *  @param x An array of Complex's.
403     *  @return A new array of doubles.
404     */
405    public static final double[] FFTImagOut(Complex[] x) {
406        return FFTImagOut(x, order(x.length));
407    }
408
409    /** Return a new array of doubles which is the imaginary part of the
410     *  FFT of an input array of Complex's.
411     *  This method is half as expensive as computing both the real and
412     *  imaginary parts of an FFT on a array of Complex's. It is especially
413     *  useful when the output is known to be purely imaginary.
414     *  @param x An array of Complex's.
415     *  @param order The base-2 logarithm of the size of the transform.
416     *  @return A new array of doubles.
417     */
418    public static final double[] FFTImagOut(Complex[] x, int order) {
419        x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
420
421        double[] realx = ComplexArrayMath.realParts(x);
422        double[] imagrealX = FFTImagOut(realx, order);
423
424        double[] imagx = ComplexArrayMath.imagParts(x);
425        double[] realimagX = FFTRealOut(imagx, order);
426
427        return DoubleArrayMath.add(imagrealX, realimagX);
428    }
429
430    /** Return a new array of doubles that is the imaginary part of the FFT
431     *  of the real input array of doubles.
432     *  This method is half as expensive as computing both the real and
433     *  imaginary parts of a FFT on a real array. It is especially useful when
434     *  the output is known to be purely imaginary (input is odd).
435     *  This method automatically computes the order of the transform
436     *  based on the length of the input array, and calls:
437     *  FFTImagOut(x, order)
438     *  @param x An array of doubles.
439     *  @return A new array of doubles.
440     */
441    public static final double[] FFTImagOut(double[] x) {
442        return FFTImagOut(x, order(x.length));
443    }
444
445    /** Return a new array of doubles that is the imaginary part of the FFT
446     *  of the real input array of doubles.
447     *  This method is half as expensive as computing both the real and
448     *  imaginary parts of a FFT on a real array. It is especially useful when
449     *  the output is known to be purely imaginary (input is odd).
450     *  @param x An array of doubles.
451     *  @param order The base-2 logarithm of the size of the transform.
452     *  @return A new array of doubles.
453     */
454    public static final double[] FFTImagOut(double[] x, int order) {
455        x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
456
457        int size = 1 << order;
458        int halfN = size >> 1;
459
460        // Make sure the tables have enough entries for the DCT computation
461        // at size N/4
462        if (order - 2 > _FFCTGenLimit) {
463            _FFCTTableGen(order - 2);
464        }
465
466        double[] imagPart = _sinDFT(x, size, order);
467
468        double[] returnValue = new double[size];
469
470        // Don't bother to look at the array for element 0
471        // returnValue[0] = 0.0; // not necessary in Java
472        for (int k = 1; k < halfN; k++) {
473            returnValue[k] = -imagPart[k];
474            returnValue[size - k] = imagPart[k];
475        }
476
477        // returnValue[halfN] = 0.0; // not necessary in Java
478        return returnValue;
479    }
480
481    /** Return a new array of doubles which is the real part of the
482     *  forward FFT of an input array of Complex's.
483     *  This method automatically computes the order of the transform
484     *  based on the length of the input array, and calls :
485     *  FFTRealOut(x, order)
486     *  @param x An array of Complex's.
487     *  @return A new array of doubles.
488     */
489    public static final double[] FFTRealOut(Complex[] x) {
490        return FFTRealOut(x, order(x.length));
491    }
492
493    /** Return a new array of doubles which is the real part of the
494     *  forward FFT of an input array of Complex's.
495     *  This method is half as expensive as computing both the real and
496     *  imaginary parts of an FFT on a array of Complex's. It is especially
497     *  useful when the output is known to be purely real.
498     *  @param x An array of Complex's.
499     *  @param order The base-2 logarithm of the size of the transform.
500     *  @return A new array of doubles.
501     */
502    public static final double[] FFTRealOut(Complex[] x, int order) {
503        x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
504
505        double[] realx = ComplexArrayMath.realParts(x);
506        double[] realrealX = FFTRealOut(realx, order);
507
508        double[] imagx = ComplexArrayMath.imagParts(x);
509        double[] imagimagX = FFTImagOut(imagx, order);
510
511        return DoubleArrayMath.subtract(realrealX, imagimagX);
512    }
513
514    /** Return a new array of doubles that is the real part of the FFT of
515     *  the real input array of doubles.
516     *  This method automatically computes the order of the transform
517     *  based on the length of the input array, and calls :
518     *  FFTRealOut(x, order).
519     *  @param x An array of doubles.
520     *  @return A new array of doubles.
521     */
522    public static final double[] FFTRealOut(double[] x) {
523        return FFTRealOut(x, order(x.length));
524    }
525
526    /** Return a new array of doubles that is the real part of the FFT of
527     *  the real input array of doubles.
528     *  This method is half as expensive as computing both the real and
529     *  imaginary parts of an FFT on a real array. It is especially useful
530     *  when the output is known to be purely real (input is even).
531     *  @param x An array of doubles.
532     *  @param order The base-2 logarithm of the size of the transform
533     *  @return A new array of doubles.
534     */
535    public static final double[] FFTRealOut(double[] x, int order) {
536        x = _checkTransformArgs(x, order, _FORWARD_TRANSFORM);
537
538        int size = 1 << order;
539        int halfN = size >> 1;
540
541        if (x.length < size) {
542            x = DoubleArrayMath.resize(x, size);
543        }
544
545        // Make sure the tables have enough entries for the DCT computation
546        // at size N/4
547        if (order - 2 > _FFCTGenLimit) {
548            _FFCTTableGen(order - 2);
549        }
550
551        double[] realPart = _cosDFT(x, size, order);
552        double[] returnValue = new double[size];
553
554        System.arraycopy(realPart, 0, returnValue, 0, halfN + 1);
555
556        for (int k = halfN + 1; k < size; k++) {
557            returnValue[k] = realPart[size - k];
558        }
559
560        return returnValue;
561    }
562
563    /** Return a new array of doubles that is the inverse, normalized
564     *  DCT of the input array of doubles.
565     *  This method automatically computes the order of the transform
566     *  based on the length of the input array. It is equivalent to
567     *  IDCT(x, order, DCT_TYPE_NORMALIZED), where 2^order is the
568     *  next power of two larger than or equal to the length of the
569     *  specified array.  The returned array has length 2^order.
570     *  @param x An array of doubles.
571     *  @return A new array of doubles with length 2^order.
572     */
573    public static final double[] IDCT(double[] x) {
574        return IDCT(x, order(x.length), DCT_TYPE_NORMALIZED);
575    }
576
577    /** Return a new array of doubles that is the inverse, normalized
578     *  DCT of the input array of doubles, using the specified order.
579     *  The length of the DCT is 2^<i>order</i>.  This is equivalent to
580     *  IDCT(x, order, DCT_TYPE_NORMALIZED).
581     *  @param x An array of doubles.
582     *  @return A new array of doubles with length 2^order.
583     */
584    public static final double[] IDCT(double[] x, int order) {
585        return IDCT(x, order, DCT_TYPE_NORMALIZED);
586    }
587
588    /** Return a new array of doubles that is the inverse DCT of the
589     *  input array of doubles.
590     *  See the DCT_TYPE_XXX constants for documentation of the
591     *  exact formula, which depends on the type.
592     *  @param x An array of doubles.
593     *  @param order The base-2 logarithm of the size of the transform.
594     *  @param type The type of IDCT, which is one of DCT_TYPE_NORMALIZED,
595     *   DCT_TYPE_UNNORMALIZED, or DCT_TYPE_ORTHONORMAL.
596     *  @see #DCT_TYPE_NORMALIZED
597     *  @see #DCT_TYPE_UNNORMALIZED
598     *  @see #DCT_TYPE_ORTHONORMAL
599     *  @return A new array of doubles with length 2^order.
600     */
601    public static final double[] IDCT(double[] x, int order, int type) {
602        // check if order > 31
603        if (type >= DCT_TYPES) {
604            throw new IllegalArgumentException(
605                    "ptolemy.math.SignalProcessing.IDCT() : Bad DCT type");
606        }
607
608        int size = 1 << order;
609        int twoSize = 2 << order;
610
611        // Generate scaleFactors if necessary
612        if (_IDCTfactors[type][order] == null) {
613            _IDCTfactors[type][order] = new Complex[twoSize];
614
615            double oneOverTwoSize = 1.0 / twoSize;
616
617            double factor = 1.0;
618            double oneOverE0 = 2.0;
619
620            switch (type) {
621            case DCT_TYPE_NORMALIZED:
622                factor = 2.0;
623                oneOverE0 = ExtendedMath.SQRT_2;
624                break;
625
626            case DCT_TYPE_ORTHONORMAL:
627                factor = Math.sqrt(twoSize); // == 2 * sqrt(N/2)
628                oneOverE0 = ExtendedMath.SQRT_2;
629                break;
630
631            case DCT_TYPE_UNNORMALIZED:
632                factor = 2.0;
633                oneOverE0 = 1.0;
634                break;
635            }
636
637            _IDCTfactors[type][order][0] = new Complex(oneOverE0 * factor, 0.0);
638
639            for (int k = 1; k < twoSize; k++) {
640                Complex c = new Complex(0, k * Math.PI * oneOverTwoSize);
641                _IDCTfactors[type][order][k] = c.exp().scale(factor);
642            }
643        }
644
645        Complex[] evenX = new Complex[twoSize];
646        Complex[] myFactors = _IDCTfactors[type][order];
647
648        // Convert to Complex, while multiplying by scaleFactors
649        evenX[0] = myFactors[0].scale(x[0]);
650
651        for (int k = 1; k < size; k++) {
652            // Do zero-padding here
653            if (k >= x.length) {
654                evenX[k] = new Complex(0.0);
655                evenX[twoSize - k] = new Complex(0.0);
656            } else {
657                evenX[k] = myFactors[k].scale(x[k]);
658                evenX[twoSize - k] = myFactors[twoSize - k].scale(-x[k]);
659            }
660        }
661
662        evenX[size] = new Complex(0.0, 0.0);
663
664        double[] longOutput = IFFTRealOut(evenX, order + 1);
665
666        // Truncate result
667        return DoubleArrayMath.resize(longOutput, size);
668    }
669
670    /** Return a new array of complex numbers which is the inverse FFT
671     *  of an input array of complex numbers.  The length of the result
672     *  is the next power of two greater than the length of the argument.
673     *  The input is zero-padded if it does not match this length.
674     *  @param x An array of complex numbers.
675     *  @return The inverse FFT of the argument.
676     */
677    public static final Complex[] IFFT(Complex[] x) {
678        return IFFTComplexOut(x, order(x.length));
679    }
680
681    /** Return a new array of complex numbers which is the inverse FFT
682     *  of an input array of complex numbers.
683     *  The input is zero-padded if it does not match this length.
684     *  @param x An array of complex numbers.
685     *  @param order The log base 2 of the length of the FFT.
686     *  @return The inverse FFT of the argument.
687     */
688    public static final Complex[] IFFT(Complex[] x, int order) {
689        return IFFTComplexOut(x, order);
690    }
691
692    /** Return a new array of Complex's which is the inverse FFT
693     *  of an input array of Complex's.
694     *  This method automatically computes the order of the transform
695     *  based on the length of the input array.
696     *  @param x An array of Complex's.
697     *  @return A new array of Complex's.
698     */
699    public static final Complex[] IFFTComplexOut(Complex[] x) {
700        return IFFTComplexOut(x, order(x.length));
701    }
702
703    /** Return a new array of Complex's which is the forward FFT
704     *  of an input array of Complex's.
705     *  @param x An array of Complex's.
706     *  @param order The base-2 logarithm of the size of the transform.
707     *  @return A new array of Complex's.
708     */
709    public static final Complex[] IFFTComplexOut(Complex[] x, int order) {
710        x = _checkTransformArgs(x, order, _INVERSE_TRANSFORM);
711
712        Complex[] conjX = ComplexArrayMath.conjugate(x);
713        Complex[] yConj = FFTComplexOut(conjX, order);
714        Complex[] y = ComplexArrayMath.conjugate(yConj);
715
716        // scale by 1/N
717        double oneOverN = 1.0 / (1 << order);
718        return ComplexArrayMath.scale(y, oneOverN);
719    }
720
721    /** Return a new array of doubles which is the real part of the inverse
722     *  FFT of an input array of Complex's.
723     *  This is less than half as expensive as computing both the real and
724     *  imaginary parts. It is especially useful when it is known that the
725     *  output is purely real.
726     *  This method automatically computes the order of the transform
727     *  based on the length of the input array, and calls :
728     *  IFFTRealOut(x, order)
729     *  @param x An array of Complex's.
730     *  @return A new array of doubles.
731     */
732    public static final double[] IFFTRealOut(Complex[] x) {
733        return IFFTRealOut(x, order(x.length));
734    }
735
736    /** Return a new array of doubles which is the real part of the inverse
737     *  FFT of an input array of Complex's.
738     *  This method is less than half as expensive as computing both the
739     *  real and imaginary parts of an IFFT of an array of Complex's. It is
740     *  especially useful when it is known that the output is purely real.
741     *  @param x An array of Complex's.
742     *  @return A new array of doubles.
743     */
744    public static final double[] IFFTRealOut(Complex[] x, int order) {
745        x = _checkTransformArgs(x, order, _INVERSE_TRANSFORM);
746
747        double[] realx = ComplexArrayMath.realParts(x);
748        double[] realrealX = FFTRealOut(realx, order);
749
750        double[] imagx = ComplexArrayMath.imagParts(x);
751        double[] imagimagX = FFTImagOut(imagx, order);
752
753        realrealX = DoubleArrayMath.add(realrealX, imagimagX);
754
755        // scale by 1/N
756        double oneOverN = 1.0 / (1 << order);
757        return DoubleArrayMath.scale(realrealX, oneOverN);
758    }
759
760    /** Return a new array of doubles which is the real part of the inverse
761     *  FFT of an input array of doubles.
762     *  This method automatically computes the order of the transform
763     *  based on the length of the input array, and calls :
764     *  IFFTRealOut(x, order)
765     *  @param x An array of doubles.
766     *  @return A new array of doubles.
767     */
768    public static double[] IFFTRealOut(double[] x) {
769        return IFFTRealOut(x, order(x.length));
770    }
771
772    /** Return a new array of doubles which is the real part of the inverse
773     *  FFT of an input array of doubles. This method is less than half
774     *  as expensive as computing the real part of an IFFT of an array of
775     *  Complex's. It is especially useful when both the input and output
776     *  are known to be purely real.
777     *  @param x An array of doubles.
778     *  @param order The base-2 logarithm of the size of the transform.
779     *  @return A new array of doubles.
780     */
781    public static double[] IFFTRealOut(double[] x, int order) {
782        double[] y = FFTRealOut(x, order);
783
784        // scale by 1/N
785        double oneOverN = 1.0 / (1 << order);
786        return DoubleArrayMath.scale(y, oneOverN);
787    }
788
789    /** Return a new array that is filled with samples of a Bartlett
790     *  window of a specified length. Throw an IllegalArgumentException
791     *  if the length is less than 1 or the window type is unknown.
792     *  @param length The length of the window to be generated.
793     *  @return A new array of doubles.
794     */
795    public static final double[] generateBartlettWindow(int length) {
796        if (length < 1) {
797            throw new IllegalArgumentException("ptolemy.math.SignalProcessing"
798                    + ".generateBartlettWindow(): "
799                    + " length of window should be greater than 0.");
800        }
801
802        int M = length - 1;
803        int n;
804        double[] window = new double[length];
805
806        int halfM = M / 2;
807        double twoOverM = 2.0 / M;
808
809        for (n = 0; n <= halfM; n++) {
810            window[n] = n * twoOverM;
811        }
812
813        for (n = halfM + 1; n < length; n++) {
814            window[n] = 2.0 - n * twoOverM;
815        }
816
817        return window;
818    }
819
820    /** Return a new array that is filled with samples of a Blackman
821     *  window of a specified length. Throw an IllegalArgumentException
822     *  if the length is less than 1 or the window type is unknown.
823     *  @param length The length of the window to be generated.
824     *  @return A new array of doubles.
825     */
826    public static final double[] generateBlackmanWindow(int length) {
827        if (length < 1) {
828            throw new IllegalArgumentException("ptolemy.math.SignalProcessing"
829                    + ".generateBlackmanWindow(): "
830                    + " length of window should be greater than 0.");
831        }
832
833        int M = length - 1;
834        int n;
835        double[] window = new double[length];
836
837        double twoPiOverM = 2.0 * Math.PI / M;
838        double fourPiOverM = 2.0 * twoPiOverM;
839
840        for (n = 0; n < length; n++) {
841            window[n] = 0.42 - 0.5 * Math.cos(twoPiOverM * n)
842                    + 0.08 * Math.cos(fourPiOverM * n);
843        }
844
845        return window;
846    }
847
848    /** Return a new array that is filled with samples of a Blackman Harris
849     *  window of a specified length. Throw an IllegalArgumentException
850     *  if the length is less than 1 or the window type is unknown.
851     *  @param length The length of the window to be generated.
852     *  @return A new array of doubles.
853     */
854    public static final double[] generateBlackmanHarrisWindow(int length) {
855        if (length < 1) {
856            throw new IllegalArgumentException("ptolemy.math.SignalProcessing"
857                    + ".generateBlackmanHarrisWindow(): "
858                    + " length of window should be greater than 0.");
859        }
860
861        int M = length - 1;
862        int n;
863        double[] window = new double[length];
864
865        double twoPiOverM = 2.0 * Math.PI / M;
866        double fourPiOverM = 2.0 * twoPiOverM;
867        double sixPiOverM = 3.0 * twoPiOverM;
868
869        for (n = 0; n < length; n++) {
870            window[n] = 0.35875 - 0.48829 * Math.cos(twoPiOverM * n)
871                    + 0.14128 * Math.cos(fourPiOverM * n)
872                    - 0.01168 * Math.cos(sixPiOverM * n);
873        }
874
875        return window;
876    }
877
878    /** Return an array with samples the Gaussian curve (the "bell curve").
879     *  The returned array is symmetric.  E.g., to get a Gaussian curve
880     *  that extends out to "four sigma," then the <i>extent</i> argument
881     *  should be 4.0.
882     *  @param standardDeviation The standard deviation.
883     *  @param extent The multiple of the standard deviation out to
884     *   which the curve is plotted.
885     *  @param length The length of the returned array.
886     *  @return An array that contains samples of the Gaussian curve.
887     */
888    public static final double[] generateGaussianCurve(double standardDeviation,
889            double extent, int length) {
890        GaussianSampleGenerator generator = new GaussianSampleGenerator(0.0,
891                standardDeviation);
892        return sampleWave(length, -extent * standardDeviation,
893                2.0 * extent * standardDeviation / length, generator);
894    }
895
896    /** Return a new array that is filled with samples of a Hamming
897     *  window of a specified length. Throw an IllegalArgumentException
898     *  if the length is less than 1 or the window type is unknown.
899     *  @param length The length of the window to be generated.
900     *  @return A new array of doubles.
901     */
902    public static final double[] generateHammingWindow(int length) {
903        if (length < 1) {
904            throw new IllegalArgumentException(
905                    "ptolemy.math.SignalProcessing.generateHammingWindow(): "
906                            + " length of window should be greater than 0.");
907        }
908
909        int M = length - 1;
910        int n;
911        double[] window = new double[length];
912
913        double twoPiOverM = 2.0 * Math.PI / M;
914
915        for (n = 0; n < length; n++) {
916            window[n] = 0.54 - 0.46 * Math.cos(twoPiOverM * n);
917        }
918
919        return window;
920    }
921
922    /** Return a new array that is filled with samples of a Hanning
923     *  window of a specified length. Throw an IllegalArgumentException
924     *  if the length is less than 1 or the window type is unknown.
925     *  @param length The length of the window to be generated.
926     *  @return A new array of doubles.
927     */
928    public static final double[] generateHanningWindow(int length) {
929        if (length < 1) {
930            throw new IllegalArgumentException(
931                    "ptolemy.math.SignalProcessing.generateHanningWindow(): "
932                            + " length of window should be greater than 0.");
933        }
934
935        int M = length - 1;
936        int n;
937        double[] window = new double[length];
938
939        double twoPiOverM = 2.0 * Math.PI / M;
940
941        for (n = 0; n < length; n++) {
942            window[n] = 0.5 - 0.5 * Math.cos(twoPiOverM * n);
943        }
944
945        return window;
946    }
947
948    /** Return an array with samples a polynomial curve.
949     *  The first argument is an array giving the coefficients
950     *  of the polynomial, starting with the constant term, followed
951     *  by the linear term, followed by the quadratic term, etc.
952     *  The remaining coefficients determine the points at which
953     *  the polynomial curve is sampled. That is, they determine
954     *  the values of the polynomial variable at which the polynomial
955     *  is evaluated.
956     *  @param polynomial An array with polynomial coefficients.
957     *  @param start The point of the first sample.
958     *  @param step The step size between samples.
959     *  @param length The length of the returned array.
960     *  @return An array that contains samples of a polynomial curve.
961     */
962    public static final double[] generatePolynomialCurve(double[] polynomial,
963            double start, double step, int length) {
964        PolynomialSampleGenerator generator = new PolynomialSampleGenerator(
965                polynomial, 1);
966        return sampleWave(length, start, step, generator);
967    }
968
969    /** Return an array containing a symmetric raised-cosine pulse.
970     *  This pulse is widely used in communication systems, and is called
971     *  a "raised cosine pulse" because the magnitude its Fourier transform
972     *  has a shape that ranges from rectangular (if the excess bandwidth
973     *  is zero) to a cosine curved that has been raised to be non-negative
974     *  (for excess bandwidth of 1.0).  The elements of the returned array
975     *  are samples of the function:
976     *  <pre>
977     *         sin(PI t/T)   cos(x PI t/T)
978     *  h(t) = ----------- * -----------------
979     *          PI t/T      1-(2 x t/T)<sup>2</sup>
980     *  </pre>
981     *  where x is the excess bandwidth and T is the number of samples
982     *  from the center of the pulse to the first zero crossing.
983     *  The samples are taken with a
984     *  sampling interval of 1.0, and the returned array is symmetric.
985     *  With an excessBandwidth of 0.0, this pulse is a sinc pulse.
986     *  @param excessBandwidth The excess bandwidth.
987     *  @param firstZeroCrossing The number of samples from the center of the
988     *   pulse to the first zero crossing.
989     *  @param length The length of the returned array.
990     *  @return An array containing a symmetric raised-cosine pulse.
991     */
992    public static final double[] generateRaisedCosinePulse(
993            double excessBandwidth, double firstZeroCrossing, int length) {
994        RaisedCosineSampleGenerator generator = new RaisedCosineSampleGenerator(
995                firstZeroCrossing, excessBandwidth);
996        return sampleWave(length, -(length - 1) / 2.0, 1.0, generator);
997    }
998
999    /** Return a new array that is filled with samples of a rectangular
1000     *  window of a specified length. Throw an IllegalArgumentException
1001     *  if the length is less than 1 or the window type is unknown.
1002     *  @param length The length of the window to be generated.
1003     *  @return A new array of doubles.
1004     */
1005    public static final double[] generateRectangularWindow(int length) {
1006        if (length < 1) {
1007            throw new IllegalArgumentException("ptolemy.math.SignalProcessing"
1008                    + ".generateRectangularWindow(): "
1009                    + " length of window should be greater than 0.");
1010        }
1011
1012        int n;
1013        double[] window = new double[length];
1014
1015        for (n = 0; n < length; n++) {
1016            window[n] = 1.0;
1017        }
1018
1019        return window;
1020    }
1021
1022    /** Return an array containing a symmetric raised-cosine pulse.
1023     *  This pulse is widely used in communication systems, and is called
1024     *  a "raised cosine pulse" because the magnitude its Fourier transform
1025     *  has a shape that ranges from rectangular (if the excess bandwidth
1026     *  is zero) to a cosine curved that has been raised to be non-negative
1027     *  (for excess bandwidth of 1.0).  The elements of the returned array
1028     *  are samples of the function:
1029     *  <pre>
1030     *           4 x(cos((1+x)PI t/T) + T sin((1-x)PI t/T)/(4x t/T))
1031     *  h(t) =  ---------------------------------------------------
1032     *                PI sqrt(T)(1-(4 x t/T)<sup>2</sup>)
1033     *  </pre>
1034     *  <p>
1035     *  where <i>x</i> is the the excess bandwidth.
1036     *  This pulse convolved with itself will, in principle, be equal
1037     *  to a raised cosine pulse.  However, because the pulse decays rather
1038     *  slowly for low excess bandwidth, this ideal is not
1039     *  closely approximated by short finite approximations of the pulse.
1040     *  The samples are taken with a
1041     *  sampling interval of 1.0, and the returned array is symmetric.
1042     *  With an excessBandwidth of 0.0, this pulse is a scaled sinc pulse.
1043     *  @param excessBandwidth The excess bandwidth.
1044     *  @param firstZeroCrossing The number of samples from the center of the
1045     *   pulse to the first zero crossing.
1046     *  @param length The length of the returned array.
1047     *  @return A new array containing a square-root raised-cosine pulse.
1048     */
1049    public static final double[] generateSqrtRaisedCosinePulse(
1050            double excessBandwidth, double firstZeroCrossing, int length) {
1051        RaisedCosineSampleGenerator generator = new RaisedCosineSampleGenerator(
1052                firstZeroCrossing, excessBandwidth);
1053        return sampleWave(length, -(length - 1) / 2.0, 1.0, generator);
1054    }
1055
1056    /** Return a new array that is filled with samples of a window of a
1057     *  specified length and type. Throw an IllegalArgumentException
1058     *  if the length is less than 1 or the window type is unknown.
1059     *  @param length The length of the window to be generated.
1060     *  @param windowType The type of window to generate.
1061     *  @return A new array of doubles.
1062     */
1063    public static final double[] generateWindow(int length, int windowType) {
1064        if (length < 1) {
1065            throw new IllegalArgumentException(
1066                    "ptolemy.math.SignalProcessing.generateWindow(): "
1067                            + " length of window should be greater than 0.");
1068        }
1069
1070        switch (windowType) {
1071        case WINDOW_TYPE_RECTANGULAR:
1072            return generateRectangularWindow(length);
1073
1074        case WINDOW_TYPE_BARTLETT:
1075            return generateBartlettWindow(length);
1076
1077        case WINDOW_TYPE_HANNING:
1078            return generateHanningWindow(length);
1079
1080        case WINDOW_TYPE_HAMMING:
1081            return generateHammingWindow(length);
1082
1083        case WINDOW_TYPE_BLACKMAN:
1084            return generateBlackmanWindow(length);
1085
1086        case WINDOW_TYPE_BLACKMAN_HARRIS:
1087            return generateBlackmanHarrisWindow(length);
1088
1089        default:
1090            throw new IllegalArgumentException(
1091                    "ptolemy.math.SignalProcessing.generateWindow(): "
1092                            + "Unknown window type (" + windowType + ").");
1093        }
1094    }
1095
1096    /** Return the next power of two larger than the argument.
1097     *  @param x A positive real number.
1098     *  @exception IllegalArgumentException If the argument is less than
1099     *   or equal to zero.
1100     */
1101    public static final int nextPowerOfTwo(double x) {
1102        if (x <= 0.0) {
1103            throw new IllegalArgumentException(
1104                    "ptolemy.math.SignalProcessing.nextPowerOfTwo(): "
1105                            + "argument (" + x + ") is not a positive number.");
1106        }
1107
1108        double m = Math.log(x) * _LOG2SCALE;
1109        int exp = (int) Math.ceil(m);
1110        return 1 << exp;
1111    }
1112
1113    /** Return the "order" of a transform size, i.e. the base-2 logarithm
1114     *  of the size. The order will be rounded up to the nearest integer.
1115     *  If the size is zero or negative, throw an IllegalArgumentException.
1116     *  @param size The size of the transform.
1117     *  @return The order of the transform.
1118     */
1119    public static final int order(int size) {
1120        if (size <= 0) {
1121            throw new IllegalArgumentException("ptolemy.math.SignalProcessing:"
1122                    + " size of transform must be positive.");
1123        }
1124
1125        double m = Math.log(size) * _LOG2SCALE;
1126        double exp = Math.ceil(m);
1127        return (int) exp;
1128    }
1129
1130    /** Given an array of pole locations, an array of zero locations, and a
1131     *  gain term, return frequency response specified by these.
1132     *  This is calculated by walking around the unit circle and forming
1133     *  the product of the distances to the zeros, dividing by the product
1134     *  of the distances to the poles, and multiplying by the gain.
1135     *  The length of the returned array is <i>numSteps</i>.
1136     *
1137     *  @param poles An array of pole locations.
1138     *  @param zeros An array of zero locations.
1139     *  @param gain A complex gain.
1140     *  @param numSteps The number of samples in the returned
1141     *  frequency response.
1142     */
1143    public static final Complex[] poleZeroToFrequency(Complex[] poles,
1144            Complex[] zeros, Complex gain, int numSteps) {
1145        double step = 2 * Math.PI / numSteps;
1146        Complex[] freq = new Complex[numSteps];
1147
1148        double angle = -Math.PI;
1149
1150        for (int index = 0; index < freq.length; index++) {
1151            Complex polesContribution = Complex.ONE;
1152            Complex zerosContribution = Complex.ONE;
1153            Complex ejw = new Complex(Math.cos(angle), Math.sin(angle));
1154
1155            if (poles.length > 0) {
1156                Complex[] diffPoles = ComplexArrayMath.subtract(poles, ejw);
1157                polesContribution = ComplexArrayMath.product(diffPoles);
1158            }
1159
1160            if (zeros.length > 0) {
1161                Complex[] diffZeros = ComplexArrayMath.subtract(zeros, ejw);
1162                zerosContribution = ComplexArrayMath.product(diffZeros);
1163            }
1164
1165            freq[index] = zerosContribution.divide(polesContribution);
1166            freq[index] = freq[index].multiply(gain);
1167            angle += step;
1168        }
1169
1170        return freq;
1171    }
1172
1173    /** Return a new array that is filled with samples of a waveform of a
1174     *  specified length. The waveform is sampled with starting at startTime,
1175     *  at a sampling period of interval.
1176     *  @param length The number of samples to generate.
1177     *  @param startTime The corresponding time for the first sample.
1178     *  @param interval The time between successive samples. This may
1179     *  be negative if the waveform is to be reversed, or zero if the
1180     *  array is to be filled with a constant.
1181     *  @param sampleGen A DoubleUnaryOperation.
1182     *  @return A new array of doubles.
1183     *  @see ptolemy.math.DoubleUnaryOperation
1184     */
1185    public static final double[] sampleWave(int length, double startTime,
1186            double interval, DoubleUnaryOperation sampleGen) {
1187        double time = startTime;
1188
1189        double[] returnValue = new double[length];
1190
1191        for (int t = 0; t < length; t++) {
1192            returnValue[t] = sampleGen.operate(time);
1193            time += interval;
1194        }
1195
1196        return returnValue;
1197    }
1198
1199    /** Return a sample of a sawtooth wave with the specified period and
1200     *  phase at the specified time.  The returned value ranges between
1201     *  -1.0 and 1.0.  The phase is given as a fraction of a cycle,
1202     *  typically ranging from 0.0 to 1.0.  If the phase is 0.0 or 1.0,
1203     *  the wave begins at zero with a rising slope.  If it is 0.5, it
1204     *  begins at the falling edge with value -1.0.
1205     *  If it is 0.25, it begins at +0.5.
1206     *
1207     *  Throw an exception if the period is less than or equal to 0.
1208     *
1209     *  @param period The period of the sawtooth wave.
1210     *  @param phase The phase of the sawtooth wave.
1211     *  @param time The time of the sample.
1212     *  @return A double in the range -1.0 to +1.0.
1213     */
1214    public static double sawtooth(double period, double phase, double time) {
1215        if (period <= 0) {
1216            throw new IllegalArgumentException(
1217                    "ptolemy.math.SignalProcessing.sawtooth(): "
1218                            + "period should be greater than 0.");
1219        }
1220
1221        double point = 2 / period
1222                * Math.IEEEremainder(time + phase * period, period);
1223
1224        // get rid of negative zero
1225        point = point == -0.0 ? 0.0 : point;
1226
1227        // hole at +1.0
1228        return point == 1.0 ? -1.0 : point;
1229    }
1230
1231    /** Return sin(x)/x, the so-called sinc function.
1232     *  If the argument is very close to zero, significant quantization
1233     *  errors may result (exactly 0.0 is OK, since this just returns 1.0).
1234     *
1235     *  @param x A number.
1236     *  @return The sinc function.
1237     */
1238    public static final double sinc(double x) {
1239        if (x == 0.0) {
1240            return 1.0;
1241        }
1242
1243        return Math.sin(x) / x;
1244    }
1245
1246    /** Return a sample of a square wave with the specified period and
1247     *  phase at the specified time.  The returned value is 1 or -1.
1248     *  A sample that falls on the rising edge of the square wave is
1249     *  assigned value +1.  A sample that falls on the falling edge is
1250     *  assigned value -1.  The phase is given as a fraction of a
1251     *  cycle, typically ranging from 0.0 to 1.0.  If the phase is 0.0
1252     *  or 1.0, the square wave begins at the start of the +1.0 phase.
1253     *  If it is 0.5, it begins at the start of the -1.0 phase. If it
1254     *  is 0.25, it begins halfway through the +1.0 portion of the
1255     *  wave.
1256     *
1257     *  Throw an exception if the period is less than or equal to 0.
1258     *
1259     *  @param period The period of the square wave.
1260     *  @param phase The phase of the square wave.
1261     *  @param time The time of the sample.
1262     *  @return +1.0 or -1.0.
1263     */
1264    public static double square(double period, double phase, double time) {
1265        if (period <= 0) {
1266            throw new IllegalArgumentException(
1267                    "ptolemy.math.SignalProcessing.square(): "
1268                            + "period should be greater than 0.");
1269        }
1270
1271        double point = 2 / period
1272                * Math.IEEEremainder(time + phase * period, period);
1273
1274        // hole at +1.0
1275        return point >= 0 && point < 1 ? 1.0 : -1.0;
1276    }
1277
1278    /** Return a sample of a triangle wave with the specified period and
1279     *  phase at the specified time.  The returned value ranges between
1280     *  -1.0 and 1.0.  The phase is given as a fraction of a cycle,
1281     *  typically ranging from 0.0 to 1.0.  If the phase is 0.0 or 1.0,
1282     *  the wave begins at zero with a rising slope.  If it is 0.5, it
1283     *  begins at zero with a falling slope. If it is 0.25, it begins at +1.0.
1284     *
1285     *  Throw an exception if the period is less than or equal to 0.
1286     *
1287     *  @param period The period of the triangle wave.
1288     *  @param phase The phase of the triangle wave.
1289     *  @param time The time of the sample.
1290     *  @return A number in the range -1.0 to +1.0.
1291     */
1292    public static double triangle(double period, double phase, double time) {
1293        if (period <= 0) {
1294            throw new IllegalArgumentException(
1295                    "ptolemy.math.SignalProcessing.triangle(): "
1296                            + "period should be greater than 0.");
1297        }
1298
1299        double point = Math.IEEEremainder(time + phase * period, period);
1300
1301        if (-period / 2.0 <= point && point < -period / 4.0) {
1302            point = -(4.0 / period * point) - 2;
1303        } else if (-period / 4.0 <= point && point < period / 4.0) {
1304            point = 4.0 / period * point;
1305        } else { // if (T/4.0 <= point && point < T/2.0)
1306            point = -(4.0 / period * point) + 2;
1307        }
1308
1309        return point;
1310    }
1311
1312    /** Return the value of the argument
1313     *  in decibels, which is defined to be 20*log<sub>10</sub>(<em>z</em>),
1314     *  where <em>z</em> is the argument.
1315     *  Note that if the input represents power, which is proportional to a
1316     *  magnitude squared, then this should be divided
1317     *  by two to get 10*log<sub>10</sub>(<em>z</em>).
1318     *  @param value The value to convert to decibels.
1319     */
1320    public static final double toDecibels(double value) {
1321        return 20.0 * Math.log(value) * _LOG10SCALE;
1322    }
1323
1324    /** Return a new array that is constructed from the specified
1325     *  array by unwrapping the angles.  That is, if
1326     *  the difference between successive values is greater than
1327     *  <em>PI</em> in magnitude, then the second value is modified by
1328     *  multiples of 2<em>PI</em> until the difference is less than
1329     *  or equal to <em>PI</em>.
1330     *  In addition, the first element is modified so that its
1331     *  difference from zero is less than or equal to <em>PI</em> in
1332     *  magnitude.  This method is used for generating more meaningful
1333     *  phase plots.
1334     *  @param angles An array of angles.
1335     *  @return A new array of phase-unwrapped angles.
1336     */
1337    public static final double[] unwrap(double[] angles) {
1338        double previous = 0.0;
1339        double[] result = new double[angles.length];
1340
1341        for (int i = 0; i < angles.length; i++) {
1342            result[i] = angles[i];
1343
1344            while (result[i] - previous < -Math.PI) {
1345                result[i] += 2 * Math.PI;
1346            }
1347
1348            while (result[i] - previous > Math.PI) {
1349                result[i] -= 2 * Math.PI;
1350            }
1351
1352            previous = result[i];
1353        }
1354
1355        return result;
1356    }
1357
1358    /** Return a new array that is the result of inserting (n-1) zeroes
1359     *  between each successive sample in the input array, resulting in an
1360     *  array of length n * L, where L is the length of the original array.
1361     *  Throw an exception for n &le; 0.
1362     *  @param x The input array of doubles.
1363     *  @param n An integer specifying the upsampling factor.
1364     *  @return A new array of doubles.
1365     *  @exception IllegalArgumentException If the second argument is not
1366     *   strictly positive.
1367     */
1368    public static final double[] upsample(double[] x, int n) {
1369        if (n <= 0) {
1370            throw new IllegalArgumentException(
1371                    "ptolemy.math.SignalProcessing.upsample(): "
1372                            + "upsampling factor must be greater than or equal to 0.");
1373        }
1374
1375        int length = x.length * n;
1376        double[] returnValue = new double[length];
1377        int srcIndex = 0;
1378        int destIndex;
1379
1380        // Assume returnValue has been zeroed out
1381        for (destIndex = 0; destIndex < length; destIndex += n) {
1382            returnValue[destIndex] = x[srcIndex];
1383            srcIndex++;
1384        }
1385
1386        return returnValue;
1387    }
1388
1389    ///////////////////////////////////////////////////////////////////
1390    ////                         public variables                  ////
1391
1392    /** A small number ( = 1.0e-9). This number is used by algorithms to
1393     *  detect whether a double is close to zero.
1394     */
1395    public static final double EPSILON = 1.0e-9;
1396
1397    // Scale factor types for the DCT/IDCT
1398    // In the following formulas,
1399    //  e[0] = 1/sqrt(2)
1400    //  e[k] = 1 for k != 0
1401
1402    /** To select the forward transform :
1403     *  <p>
1404     *               N - 1 <br>
1405     *   X[k] = e[k]  sum  x[n] * cos ((2n + 1)k * PI / 2N) <br>
1406     *               n = 0 <br>
1407     *  </p>
1408     *  <p>
1409     *  and the inverse transform :
1410     *              N - 1 <br>
1411     *   x(n) = (2/N) sum  e(k) X[k] * cos ((2n + 1)k * PI / 2N)<br>
1412     *              k = 0 <br>
1413     *  </p>
1414     *  use this DCT type.
1415     */
1416    public static final int DCT_TYPE_NORMALIZED = 0;
1417
1418    /** To select the forward transform :
1419     *         N - 1
1420     *   X[k] = sum  x[n] * cos ((2n + 1)k * PI / 2N)
1421     *         n = 0
1422     *  and the inverse transform :
1423     *         N - 1
1424     *   x[n] = sum  X[k] * cos ((2n + 1)k * PI / 2N)
1425     *         k = 0
1426     *  use this DCT type.
1427     *  This is the definition of the DCT used by MPEG.
1428     */
1429    public static final int DCT_TYPE_UNNORMALIZED = 1;
1430
1431    /** To select the forward transform :
1432     *                         N - 1
1433     *   X[k] = sqrt(2/N) e[k]  sum  x[n] * cos ((2n + 1)k * PI / 2N)
1434     *                         n = 0
1435     *  and the inverse transform :
1436     *                   N - 1
1437     *   x[n] = sqrt(2/N) sum  e[k] X[k] * cos ((2n + 1)k * PI / 2N)
1438     *                   k = 0
1439     *  use this DCT type.
1440     *  This is the definition of the DCT used in Matlab.
1441     */
1442    public static final int DCT_TYPE_ORTHONORMAL = 2;
1443
1444    /** The number of DCT types supported. */
1445    public static final int DCT_TYPES = 3;
1446
1447    // Window types for generateWindow
1448    // In all of the formulas below, M = length of window - 1
1449
1450    /** To select the rectangular window,
1451     *  <p>
1452     *   w[n] = 1 for 0 &le; n &le; M
1453     *  </p>
1454     *  use this window type.
1455     */
1456    public static final int WINDOW_TYPE_RECTANGULAR = 0;
1457
1458    /** To select the Bartlett (triangular) window,
1459     *  <p>
1460     *   w[n] = 2n/M      for 0 &le; n &le; M/2 <br>
1461     *   w[n] = 2 - 2n/M  for M/2 &lt; n &le; M
1462     *  </p>
1463     *  use this window type.
1464     */
1465    public static final int WINDOW_TYPE_BARTLETT = 1;
1466
1467    /** To select the Hanning window,
1468     *  <p>
1469     *   w[n] = 0.5 - 0.5 cos(2 * PI * n / M)  <br>
1470     *   for 0 &le; n &le; M
1471     *  </p>
1472     *  use this window type.
1473     */
1474    public static final int WINDOW_TYPE_HANNING = 2;
1475
1476    /** To select the Hamming window,
1477     *  <p>
1478     *   w[n] = 0.54 - 0.46 cos(2 * PI * n / M) <br>
1479     *   for 0 &le; n &le; M
1480     *  </p>
1481     *  use this window type.
1482     */
1483    public static final int WINDOW_TYPE_HAMMING = 3;
1484
1485    /** To select the Blackman window,
1486     *  <p>
1487     *   w[n] = 0.42 - 0.5 cos(2 * PI * n /M)  + <br>
1488     *          0.08 cos (4 * PI * n / M) <br>
1489     *   for 0 &le; n &le; M
1490     *  </p>
1491     *  use this window type.
1492     */
1493    public static final int WINDOW_TYPE_BLACKMAN = 4;
1494
1495    /** To select the 4-term Blackman-Harris window,
1496     *  <p>
1497     *   w[n] = 0.35875 - 0.48829 cos(2 * PI * n /M)  + <br>
1498     *          0.14128 cos (4 * PI * n / M) - 0.01168 cos(6 * PI * n / M) <br>
1499     *   for 0 &le; n &le; M
1500     *  </p>
1501     *  use this window type.
1502     */
1503    public static final int WINDOW_TYPE_BLACKMAN_HARRIS = 5;
1504
1505    /** The number of window types that can be generated. */
1506    public static final int WINDOW_TYPES = 6;
1507
1508    ///////////////////////////////////////////////////////////////////
1509    ////                         inner classes                     ////
1510
1511    /** This class generates samples of a Gaussian function with the
1512     *  specified mean and standard deviation.
1513     *  The function computed is :
1514     *  <pre>
1515     *  h(t) = (1/(sqrt(2 * PI) * stdDev) *
1516     *         exp(-(t - mean)<sup>2</sup> / (2 * stdDev<sup>2</sup>))
1517     *  </pre>
1518
1519     */
1520    public static class GaussianSampleGenerator
1521            implements DoubleUnaryOperation {
1522        /** Construct a GaussianSampleGenerator.
1523         *  @param standardDeviation The standard deviation of the
1524         *  Gaussian function.
1525         */
1526        public GaussianSampleGenerator(double mean, double standardDeviation) {
1527            _mean = mean;
1528            _oneOverTwoVariance = 1.0
1529                    / (2.0 * standardDeviation * standardDeviation);
1530            _factor = ONE_OVER_SQRT_TWO_PI / standardDeviation;
1531        }
1532
1533        /** Return a sample of the Gaussian function, sampled at the
1534         *  specified time.
1535         */
1536        @Override
1537        public final double operate(double time) {
1538            double shiftedTime = time - _mean;
1539            return _factor * Math
1540                    .exp(-shiftedTime * shiftedTime * _oneOverTwoVariance);
1541        }
1542
1543        private final double _mean;
1544
1545        private final double _oneOverTwoVariance;
1546
1547        private final double _factor;
1548
1549        private static final double ONE_OVER_SQRT_TWO_PI = 1.0
1550                / Math.sqrt(2 * Math.PI);
1551    }
1552
1553    /** This class generates samples of a polynomial.
1554     *  The function computed is:
1555     *  <pre>
1556     *  h(t) = a<sub>0</sub> + a<sub>1</sub>t + a<sub>2</sub>t<sup>2</sup> + ...
1557     *         + a<sub>n-1</sub>t<sup>n-1</sup>
1558     *  </pre>
1559     *  or
1560     *  <pre>
1561     *  h(t) = a<sub>0</sub> + a<sub>1</sub>t<sup>-1</sup>
1562     *         + a<sub>2</sub>t<sup>-2</sup> + ...
1563     *         + a<sub>n-1</sub>t<sup>-(n-1)</sup>
1564     *  </pre>
1565     *  depending on the direction specified.
1566     */
1567    public static class PolynomialSampleGenerator
1568            implements DoubleUnaryOperation {
1569        /** Construct a PolynomialSampleGenerator. The input argument is
1570         *  an array of doubles
1571         *  a[0] = a<sub>0</sub> .. a[n-1] = a<sub>n-1</sub> used to compute
1572         *  the formula :
1573         *  h(t) = a<sub>0</sub> + a<sub>1</sub>t + ... + a<sub>n-1</sub>t<sup>n-1</sup>
1574         *  The array of doubles must be of length 1 or greater.
1575         *  The array of doubles is copied, so the user is free to modify
1576         *  it after construction.
1577         *  The exponents on t in the above equation will all be negated if
1578         *  the direction parameter is -1; otherwise the direction parameter
1579         *  should be 1.
1580         *  @param coefficients An array of double coefficients.
1581         *  @param direction 1 for positive exponents, -1 for negative
1582         *  exponents.
1583         */
1584        public PolynomialSampleGenerator(double[] coefficients, int direction) {
1585            if (direction != 1 && direction != -1) {
1586                throw new IllegalArgumentException(
1587                        "ptolemy.math.SignalProcessing.LineSampleGenerator: "
1588                                + "direction must be either 1 or -1");
1589            }
1590
1591            _coeffLength = coefficients.length;
1592
1593            // copy coefficient array
1594            _coefficients = DoubleArrayMath.resize(coefficients, _coeffLength);
1595            _direction = direction;
1596        }
1597
1598        /** Return a sample of the line, sampled at the specified time.
1599         *  Note that at time = 0, with a negative direction, the sample
1600         *  will be positive or negative infinity.
1601         */
1602        @Override
1603        public final double operate(double time) {
1604            double sum = _coefficients[0];
1605            double tn = time;
1606
1607            for (int i = 1; i < _coeffLength; i++) {
1608                if (_direction == 1) {
1609                    sum += _coefficients[i] * tn;
1610                } else {
1611                    sum += _coefficients[i] / tn;
1612                }
1613
1614                tn *= time;
1615            }
1616
1617            return sum;
1618        }
1619
1620        private final double[] _coefficients;
1621
1622        private final int _coeffLength;
1623
1624        private final int _direction;
1625    }
1626
1627    /** This class generates samples of a sawtooth wave with the specified
1628     *  period and phase. The returned values range between -1.0 and 1.0.
1629     */
1630    public static class SawtoothSampleGenerator
1631            implements DoubleUnaryOperation {
1632        /** Construct a SawtoothSampleGenerator with the given period and
1633         *  phase.  The phase is given as a fraction of a cycle,
1634         *  typically ranging from 0.0 to 1.0.  If the phase is 0.0 or 1.0,
1635         *  the wave begins at zero with a rising slope.  If it is 0.5, it
1636         *  begins at the falling edge with value -1.0.
1637         *  If it is 0.25, it begins at +0.5.
1638         */
1639        public SawtoothSampleGenerator(double period, double phase) {
1640            _period = period;
1641            _phase = phase;
1642        }
1643
1644        /** Return a sample of the sawtooth wave, sampled at the
1645         *  specified time.
1646         */
1647        @Override
1648        public final double operate(double time) {
1649            return sawtooth(_period, _phase, time);
1650        }
1651
1652        private final double _period;
1653
1654        private final double _phase;
1655    }
1656
1657    /** This class generates samples of a sinusoidal wave.
1658     *  The function computed is :
1659     *  <pre>
1660     *  h(t) = cos(frequency * t + phase)
1661     *  </pre>
1662     *  where the argument is taken to be in radians.
1663     *  To use this class to generate a sine wave, simply
1664     *  set the phase to -Math.PI*0.5 from the
1665     *  phase, since sin(t) = cos(t - PI/2).
1666     */
1667    public static class SinusoidSampleGenerator
1668            implements DoubleUnaryOperation {
1669        /**
1670         *  Construct a SinusoidSampleGenerator.
1671         *  @param frequency The frequency of the cosine wave, in radians per
1672         *  unit time.
1673         *  @param phase The phase shift, in radians.
1674         */
1675        public SinusoidSampleGenerator(double frequency, double phase) {
1676            _frequency = frequency;
1677            _phase = phase;
1678        }
1679
1680        @Override
1681        public final double operate(double time) {
1682            return Math.cos(_frequency * time + _phase);
1683        }
1684
1685        private final double _frequency;
1686
1687        private final double _phase;
1688    }
1689
1690    /** This class generates samples of a raised cosine pulse, or if the
1691     *  excess is zero, a modified sinc function.
1692     *  <p>
1693     *  The function that is computed is:
1694     *  <p>
1695     *  <pre>
1696     *         sin(PI t/T)   cos(excess PI t/T)
1697     *  h(t) = ----------- * -----------------
1698     *          PI t/T      1-(2 excess t/T)<sup>2</sup>
1699     *  </pre>
1700     *  <p>
1701     *  This is called a "raised cosine pulse" because in the frequency
1702     *  domain its shape is that of a raised cosine.
1703     *  <p>
1704     *  For some applications, you may wish to apply a window function to this
1705     *  impulse response, since it is rather abruptly terminated at the two \
1706     *  ends.
1707     *  <p>
1708     *  This implementation is ported from the Ptolemy 0.x implementation
1709     *  by Joe Buck, Brian Evans, and Edward A. Lee.
1710     *  Reference: <a href=http://www.amazon.com/exec/obidos/ASIN/0792393910/qid%3D910596335/002-4907626-8092437>E. A. Lee and D. G. Messerschmitt,
1711     *  <i>Digital Communication, Second Edition</i>,
1712     *  Kluwer Academic Publishers, Boston, 1994.</a>
1713     *
1714     */
1715    public static class RaisedCosineSampleGenerator
1716            implements DoubleUnaryOperation {
1717        /** Construct a RaisedCosineSampleGenerator.
1718         *  @param firstZeroCrossing The time of the first zero crossing,
1719         *  after time zero. This would be the symbol interval in a
1720         *  communications application of this pulse.
1721         *  @param excess The excess bandwidth (in the range 0.0 to 1.0), also
1722         *  called the rolloff factor.
1723         */
1724        public RaisedCosineSampleGenerator(double firstZeroCrossing,
1725                double excess) {
1726            _oneOverFZC = 1.0 / firstZeroCrossing;
1727            _excess = excess;
1728        }
1729
1730        /**  Return a sample of the raised cosine pulse, sampled at the
1731         *  specified time.
1732         */
1733        @Override
1734        public final double operate(double time) {
1735            if (time == 0.0) {
1736                return 1.0;
1737            }
1738
1739            double x = time * _oneOverFZC;
1740            double s = sinc(Math.PI * x);
1741
1742            if (_excess == 0.0) {
1743                return s;
1744            }
1745
1746            x *= _excess;
1747
1748            double denominator = 1.0 - 4.0 * x * x;
1749
1750            // If the denominator is close to zero, take it to be zero.
1751            if (close(denominator, 0.0)) {
1752                return s * ExtendedMath.PI_OVER_4;
1753            }
1754
1755            return s * Math.cos(Math.PI * x) / denominator;
1756        }
1757
1758        private final double _oneOverFZC;
1759
1760        private final double _excess;
1761    }
1762
1763    /** This class generates samples of a sinc wave with the specified
1764     *  first zero crossing.
1765     */
1766    public static class SincSampleGenerator implements DoubleUnaryOperation {
1767        @Override
1768        public final double operate(double time) {
1769            return sinc(time);
1770        }
1771    }
1772
1773    /** This class generates samples of a square-root raised cosine pulse.
1774     *  The function computed is:
1775     *  <p>
1776     *  <pre>
1777     *           4 x(cos((1+x)PI t/T) + T sin((1-x)PI t/T)/(4x t/T))
1778     *  h(t) =  ---------------------------------------------------
1779     *                PI sqrt(T)(1-(4 x t/T)<sup>2</sup>)
1780     *  </pre>
1781     *  <p>
1782     *  where <i>x</i> is the the excess bandwidth.
1783     *  This pulse convolved with itself will, in principle, be equal
1784     *  to a raised cosine pulse.  However, because the pulse decays rather
1785     *  slowly for low excess bandwidth, this ideal is not
1786     *  closely approximated by short finite approximations of the pulse.
1787     *  <p>
1788     *  This implementation was ported from the Ptolemy 0.x implementation
1789     *  by Joe Buck, Brian Evans, and Edward A. Lee.
1790     *  Reference: E. A. Lee and D. G. Messerschmitt,
1791     *  <i>Digital Communication, Second Edition</i>,
1792     *  Kluwer Academic Publishers, Boston, 1994.
1793     *
1794     *  The implementation was then further optimized to cache computations
1795     *  that are independent of time.
1796     */
1797    public static class SqrtRaisedCosineSampleGenerator
1798            implements DoubleUnaryOperation {
1799        /** Construct a SqrtRaisedCosineSampleGenerator.
1800         *  @param firstZeroCrossing The time of the first zero crossing of
1801         *  the corresponding raised cosine pulse.
1802         *  @param excess The excess bandwidth of the corresponding raised
1803         *  cosine pulse (also called the rolloff factor).
1804         */
1805        public SqrtRaisedCosineSampleGenerator(double firstZeroCrossing,
1806                double excess) {
1807            _excess = excess;
1808
1809            _oneOverFZC = 1.0 / firstZeroCrossing;
1810            _sqrtFZC = Math.sqrt(firstZeroCrossing);
1811            _squareFZC = firstZeroCrossing * firstZeroCrossing;
1812
1813            _onePlus = (1.0 + _excess) * Math.PI * _oneOverFZC;
1814            _oneMinus = (1.0 - _excess) * Math.PI * _oneOverFZC;
1815
1816            _fourExcess = 4.0 * _excess;
1817            _eightExcessPI = 8.0 * _excess * Math.PI;
1818            _sixteenExcessSquared = _fourExcess * _fourExcess;
1819
1820            double fourExcessOverPI = _fourExcess / Math.PI;
1821            double oneOverSqrtFZC = 1.0 / _sqrtFZC;
1822
1823            _sampleAtZero = (fourExcessOverPI + 1.0 - _excess) * oneOverSqrtFZC;
1824            _fourExcessOverPISqrtFZC = fourExcessOverPI * oneOverSqrtFZC;
1825
1826            _fzcSqrtFZCOverEightExcessPI = firstZeroCrossing * _sqrtFZC
1827                    / _eightExcessPI;
1828            _fzcOverFourExcess = firstZeroCrossing / _fourExcess;
1829
1830            _oneMinusFZCOverFourExcess = _oneMinus * _fzcOverFourExcess;
1831        }
1832
1833        /*  Return a sample of the raised cosine pulse, sampled at the
1834         *  specified time.
1835         *  @param time The time at which to sample the pulse.
1836         *  @return A double.
1837         */
1838        @Override
1839        public final double operate(double time) {
1840            if (time == 0.0) {
1841                return _sampleAtZero;
1842            }
1843
1844            double x = time * _oneOverFZC;
1845
1846            if (_excess == 0.0) {
1847                return _sqrtFZC * Math.sin(Math.PI * x) / (Math.PI * time);
1848            }
1849
1850            double oneMinusTime = _oneMinus * time;
1851            double onePlusTime = _onePlus * time;
1852
1853            // Check to see whether we will get divide by zero.
1854            double denominator = time * time * _sixteenExcessSquared
1855                    - _squareFZC;
1856
1857            if (close(denominator, 0.0)) {
1858                double oneOverTime = 1.0 / time;
1859                double oneOverTimeSquared = oneOverTime * oneOverTime;
1860
1861                return _fzcSqrtFZCOverEightExcessPI * oneOverTime
1862                        * (_onePlus * Math.sin(onePlusTime)
1863                                - _oneMinusFZCOverFourExcess * oneOverTime
1864                                        * Math.cos(oneMinusTime)
1865                                + _fzcOverFourExcess * oneOverTimeSquared
1866                                        * Math.sin(oneMinusTime));
1867            }
1868
1869            return _fourExcessOverPISqrtFZC
1870                    * (Math.cos(onePlusTime)
1871                            + Math.sin(oneMinusTime) / (x * _fourExcess))
1872                    / (1.0 - _sixteenExcessSquared * x * x);
1873        }
1874
1875        private final double _oneOverFZC;
1876
1877        private final double _sqrtFZC;
1878
1879        private final double _squareFZC;
1880
1881        private final double _onePlus;
1882
1883        private final double _oneMinus;
1884
1885        private final double _excess;
1886
1887        private final double _fourExcess;
1888
1889        private final double _eightExcessPI;
1890
1891        private final double _sixteenExcessSquared;
1892
1893        private final double _sampleAtZero;
1894
1895        private final double _fourExcessOverPISqrtFZC;
1896
1897        private final double _fzcSqrtFZCOverEightExcessPI;
1898
1899        private final double _fzcOverFourExcess;
1900
1901        private final double _oneMinusFZCOverFourExcess;
1902    }
1903
1904    ///////////////////////////////////////////////////////////////////
1905    ////                         private methods                   ////
1906    // Check that the order of a transform is between 0 and 31, inclusive.
1907    // Throw an exception otherwise.
1908    private static void _checkTransformOrder(int order) {
1909        if (order < 0) {
1910            throw new IllegalArgumentException(
1911                    "ptolemy.math.SignalProcessing : order of transform "
1912                            + "must be non-negative.");
1913        } else if (order > 31) {
1914            throw new IllegalArgumentException(
1915                    "ptolemy.math.SignalProcessing : order of transform "
1916                            + "must be less than 32.");
1917        }
1918    }
1919
1920    // Check the arguments for a transform on an array of doubles, using
1921    // _checkTransformInput() and _checkTransformOrder(). Return an
1922    // appropriately padded array on which to perform the transform.
1923    private static double[] _checkTransformArgs(double[] x, int order,
1924            boolean inverse) {
1925        _checkTransformOrder(order);
1926
1927        int size = 1 << order;
1928
1929        // Zero pad the array if necessary
1930        if (x.length < size) {
1931            x = inverse ? DoubleArrayMath.padMiddle(x, size)
1932                    : DoubleArrayMath.resize(x, size);
1933        }
1934
1935        return x;
1936    }
1937
1938    // Check the arguments for a transform on an array of Complex's, using
1939    // _checkTransformInput() and _checkTransformOrder(). Return an
1940    // appropriately padded array on which to perform the transform.
1941    private static Complex[] _checkTransformArgs(Complex[] x, int order,
1942            boolean inverse) {
1943        _checkTransformOrder(order);
1944
1945        int size = 1 << order;
1946
1947        // Zero pad the array if necessary
1948        if (x.length < size) {
1949            x = inverse == _INVERSE_TRANSFORM
1950                    ? ComplexArrayMath.padMiddle(x, size)
1951                    : ComplexArrayMath.resize(x, size);
1952        }
1953
1954        return x;
1955    }
1956
1957    // Returns an array with half the size + 1 because of the symmetry
1958    // of the cosDFT function.
1959    private static double[] _cosDFT(double[] x, int size, int order) {
1960        switch (size) {
1961        // Base cases for lower orders
1962        case 0:
1963            return null; // should never be used
1964
1965        case 1: {
1966            double[] returnValue = new double[1];
1967            returnValue[0] = x[0];
1968            return returnValue;
1969        }
1970
1971        case 2: {
1972            double[] returnValue = new double[2];
1973            returnValue[0] = x[0] + x[1];
1974            returnValue[1] = x[0] - x[1];
1975            return returnValue;
1976        }
1977
1978        // Optimized base case for higher orders
1979        case 4: {
1980            double[] returnValue = new double[3];
1981            returnValue[0] = x[0] + x[1] + x[2] + x[3];
1982            returnValue[1] = x[0] - x[2];
1983            returnValue[2] = x[0] - x[1] + x[2] - x[3];
1984            return returnValue;
1985        }
1986        }
1987
1988        int halfN = size >> 1;
1989        int quarterN = size >> 2;
1990
1991        double[] x1 = new double[halfN];
1992
1993        for (int k = 0; k < halfN; k++) {
1994            x1[k] = x[k << 1];
1995        }
1996
1997        double[] x2 = new double[quarterN];
1998
1999        for (int k = 0; k < quarterN; k++) {
2000            int twoIp = (k << 1) + 1;
2001            x2[k] = x[twoIp] + x[size - twoIp];
2002        }
2003
2004        double[] halfCosDFT = _cosDFT(x1, halfN, order - 1);
2005        double[] quarterDCT = _DCT(x2, quarterN, order - 2);
2006
2007        double[] returnValue = new double[halfN + 1];
2008
2009        for (int k = 0; k < quarterN; k++) {
2010            returnValue[k] = halfCosDFT[k] + quarterDCT[k];
2011        }
2012
2013        returnValue[quarterN] = halfCosDFT[quarterN];
2014
2015        for (int k = quarterN + 1; k <= halfN; k++) {
2016            int idx = halfN - k;
2017            returnValue[k] = halfCosDFT[idx] - quarterDCT[idx];
2018        }
2019
2020        return returnValue;
2021    }
2022
2023    // Returns an array with half the size because of the symmetry
2024    // of the sinDFT function.
2025    private static double[] _sinDFT(double[] x, int size, int order) {
2026        switch (size) {
2027        // Base cases for lower orders
2028        case 0:
2029        case 1:
2030        case 2:
2031            return null; // should never be used
2032
2033        // Optimized base case for higher orders
2034        case 4: {
2035            double[] returnValue = new double[2];
2036
2037            // returnValue[0] = 0.0; // not necessary for Java,
2038            // also not read
2039            returnValue[1] = x[1] - x[3];
2040            return returnValue;
2041        }
2042        }
2043
2044        int halfN = size >> 1;
2045        int quarterN = size >> 2;
2046
2047        double[] x1 = new double[halfN];
2048
2049        for (int k = 0; k < halfN; k++) {
2050            x1[k] = x[k << 1];
2051        }
2052
2053        double[] x3 = new double[quarterN];
2054
2055        for (int k = 0; k < quarterN; k++) {
2056            int twoIp = (k << 1) + 1;
2057            x3[k] = (k & 1) == 1 ? x[size - twoIp] - x[twoIp]
2058                    : x[twoIp] - x[size - twoIp];
2059        }
2060
2061        double[] halfSinDFT = _sinDFT(x1, halfN, order - 1);
2062        double[] quarterDCT = _DCT(x3, quarterN, order - 2);
2063
2064        double[] returnValue = new double[halfN];
2065
2066        // returnValue[0] = 0.0; // not necessary in Java
2067        for (int k = 1; k < quarterN; k++) {
2068            returnValue[k] = halfSinDFT[k] + quarterDCT[quarterN - k];
2069        }
2070
2071        returnValue[quarterN] = quarterDCT[0];
2072
2073        for (int k = quarterN + 1; k < halfN; k++) {
2074            returnValue[k] = quarterDCT[k - quarterN] - halfSinDFT[halfN - k];
2075        }
2076
2077        return returnValue;
2078    }
2079
2080    private static double[] _DCT(double[] x, int size, int order) {
2081        double[] returnValue;
2082
2083        if (size == 1) {
2084            returnValue = new double[1];
2085            returnValue[0] = x[0];
2086            return returnValue;
2087        }
2088
2089        if (size == 2) {
2090            returnValue = new double[2];
2091            returnValue[0] = x[0] + x[1];
2092            returnValue[1] = ExtendedMath.ONE_OVER_SQRT_2 * (x[0] - x[1]);
2093            return returnValue;
2094        }
2095
2096        int halfN = size >> 1;
2097
2098        double[] x4 = new double[size];
2099
2100        for (int n = 0; n < halfN; n++) {
2101            int twoN = n << 1;
2102
2103            // Do zero padding here
2104            if (twoN >= x.length) {
2105                x4[n] = 0.0;
2106            } else {
2107                x4[n] = x[twoN];
2108            }
2109
2110            if (twoN + 1 >= x.length) {
2111                x4[size - n - 1] = 0.0;
2112            } else {
2113                x4[size - n - 1] = x[twoN + 1];
2114            }
2115        }
2116
2117        double[] cosDFTarray = _cosDFT(x4, size, order);
2118        double[] sinDFTarray = _sinDFT(x4, size, order);
2119
2120        double[] p1tab = _P1Table[order];
2121        double[] p2tab = _P2Table[order];
2122        double[] ctab = _CTable[order];
2123
2124        returnValue = new double[size];
2125
2126        returnValue[0] = cosDFTarray[0];
2127
2128        for (int k = 1; k < halfN; k++) {
2129            double m1 = (cosDFTarray[k] + sinDFTarray[k]) * ctab[k];
2130            double m2 = sinDFTarray[k] * p1tab[k];
2131            double m3 = cosDFTarray[k] * p2tab[k];
2132            returnValue[k] = m1 - m2;
2133            returnValue[size - k] = m1 + m3;
2134        }
2135
2136        returnValue[halfN] = ExtendedMath.ONE_OVER_SQRT_2 * cosDFTarray[halfN];
2137
2138        return returnValue;
2139    }
2140
2141    private synchronized static void _FFCTTableGen(int limit) {
2142        for (int i = _FFCTGenLimit; i <= limit; i++) {
2143            int N = 1 << i; // Watch out for this if i ever becomes > 31
2144            _P1Table[i] = new double[N];
2145            _P2Table[i] = new double[N];
2146            _CTable[i] = new double[N];
2147
2148            double[] p1t = _P1Table[i];
2149            double[] p2t = _P2Table[i];
2150            double[] ct = _CTable[i];
2151
2152            for (int k = 0; k < N; k++) {
2153                double arg = Math.PI * k / (2.0 * N);
2154                double c = Math.cos(arg);
2155                double s = Math.sin(arg);
2156                p1t[k] = c + s;
2157                p2t[k] = s - c;
2158                ct[k] = c;
2159            }
2160        }
2161
2162        _FFCTGenLimit = Math.max(_FFCTGenLimit, limit);
2163    }
2164
2165    ///////////////////////////////////////////////////////////////////
2166    ////                         private members                         ////
2167    // Tables needed for the FFCT algorithm. We assume no one will attempt
2168    // to do a transform of size greater than 2^31.
2169    private static final double[][] _P1Table = new double[32][];
2170
2171    private static final double[][] _P2Table = new double[32][];
2172
2173    private static final double[][] _CTable = new double[32][];
2174
2175    private static int _FFCTGenLimit = 0;
2176
2177    // Table of scaleFactors for the IDCT.
2178    private static final Complex[][][] _IDCTfactors = new Complex[DCT_TYPES][32][];
2179
2180    // Various constants
2181    private static final double _LOG10SCALE = 1.0 / Math.log(10.0);
2182
2183    private static final double _LOG2SCALE = 1.0 / Math.log(2.0);
2184
2185    // Indicates a forward/inverse transform for checking arguments
2186    private static final boolean _FORWARD_TRANSFORM = false;
2187
2188    private static final boolean _INVERSE_TRANSFORM = true;
2189}