1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.transform; 23 24 import java.io.Serializable; 25 26 import org.hipparchus.analysis.FunctionUtils; 27 import org.hipparchus.analysis.UnivariateFunction; 28 import org.hipparchus.complex.Complex; 29 import org.hipparchus.exception.MathIllegalArgumentException; 30 import org.hipparchus.util.ArithmeticUtils; 31 import org.hipparchus.util.FastMath; 32 import org.hipparchus.util.SinCos; 33 34 /** 35 * Implements the Fast Cosine Transform for transformation of one-dimensional 36 * real data sets. For reference, see James S. Walker, <em>Fast Fourier 37 * Transforms</em>, chapter 3 (ISBN 0849371635). 38 * <p> 39 * There are several variants of the discrete cosine transform. The present 40 * implementation corresponds to DCT-I, with various normalization conventions, 41 * which are specified by the parameter {@link DctNormalization}. 42 * <p> 43 * DCT-I is equivalent to DFT of an <em>even extension</em> of the data series. 44 * More precisely, if x<sub>0</sub>, …, x<sub>N-1</sub> is the data set 45 * to be cosine transformed, the extended data set 46 * x<sub>0</sub><sup>#</sup>, …, x<sub>2N-3</sub><sup>#</sup> 47 * is defined as follows 48 * <ul> 49 * <li>x<sub>k</sub><sup>#</sup> = x<sub>k</sub> if 0 ≤ k < N,</li> 50 * <li>x<sub>k</sub><sup>#</sup> = x<sub>2N-2-k</sub> 51 * if N ≤ k < 2N - 2.</li> 52 * </ul> 53 * <p> 54 * Then, the standard DCT-I y<sub>0</sub>, …, y<sub>N-1</sub> of the real 55 * data set x<sub>0</sub>, …, x<sub>N-1</sub> is equal to <em>half</em> 56 * of the N first elements of the DFT of the extended data set 57 * x<sub>0</sub><sup>#</sup>, …, x<sub>2N-3</sub><sup>#</sup> 58 * <br> 59 * y<sub>n</sub> = (1 / 2) ∑<sub>k=0</sub><sup>2N-3</sup> 60 * x<sub>k</sub><sup>#</sup> exp[-2πi nk / (2N - 2)] 61 * k = 0, …, N-1. 62 * <p> 63 * The present implementation of the discrete cosine transform as a fast cosine 64 * transform requires the length of the data set to be a power of two plus one 65 * (N = 2<sup>n</sup> + 1). Besides, it implicitly assumes 66 * that the sampled function is even. 67 * 68 */ 69 public class FastCosineTransformer implements RealTransformer, Serializable { 70 71 /** Serializable version identifier. */ 72 static final long serialVersionUID = 20120212L; 73 74 /** The type of DCT to be performed. */ 75 private final DctNormalization normalization; 76 77 /** 78 * Creates a new instance of this class, with various normalization 79 * conventions. 80 * 81 * @param normalization the type of normalization to be applied to the 82 * transformed data 83 */ 84 public FastCosineTransformer(final DctNormalization normalization) { 85 this.normalization = normalization; 86 } 87 88 /** 89 * {@inheritDoc} 90 * 91 * @throws MathIllegalArgumentException if the length of the data array is 92 * not a power of two plus one 93 */ 94 @Override 95 public double[] transform(final double[] f, final TransformType type) 96 throws MathIllegalArgumentException { 97 if (type == TransformType.FORWARD) { 98 if (normalization == DctNormalization.ORTHOGONAL_DCT_I) { 99 final double s = FastMath.sqrt(2.0 / (f.length - 1)); 100 return TransformUtils.scaleArray(fct(f), s); 101 } 102 return fct(f); 103 } 104 final double s2 = 2.0 / (f.length - 1); 105 final double s1; 106 if (normalization == DctNormalization.ORTHOGONAL_DCT_I) { 107 s1 = FastMath.sqrt(s2); 108 } else { 109 s1 = s2; 110 } 111 return TransformUtils.scaleArray(fct(f), s1); 112 } 113 114 /** 115 * {@inheritDoc} 116 * 117 * @throws org.hipparchus.exception.MathIllegalArgumentException 118 * if the lower bound is greater than, or equal to the upper bound 119 * @throws org.hipparchus.exception.MathIllegalArgumentException 120 * if the number of sample points is negative 121 * @throws MathIllegalArgumentException if the number of sample points is 122 * not a power of two plus one 123 */ 124 @Override 125 public double[] transform(final UnivariateFunction f, 126 final double min, final double max, final int n, 127 final TransformType type) throws MathIllegalArgumentException { 128 129 final double[] data = FunctionUtils.sample(f, min, max, n); 130 return transform(data, type); 131 } 132 133 /** 134 * Perform the FCT algorithm (including inverse). 135 * 136 * @param f the real data array to be transformed 137 * @return the real transformed array 138 * @throws MathIllegalArgumentException if the length of the data array is 139 * not a power of two plus one 140 */ 141 protected double[] fct(double[] f) 142 throws MathIllegalArgumentException { 143 144 final double[] transformed = new double[f.length]; 145 146 final int n = f.length - 1; 147 if (!ArithmeticUtils.isPowerOfTwo(n)) { 148 throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO_PLUS_ONE, 149 Integer.valueOf(f.length)); 150 } 151 if (n == 1) { // trivial case 152 transformed[0] = 0.5 * (f[0] + f[1]); 153 transformed[1] = 0.5 * (f[0] - f[1]); 154 return transformed; 155 } 156 157 // construct a new array and perform FFT on it 158 final double[] x = new double[n]; 159 x[0] = 0.5 * (f[0] + f[n]); 160 x[n >> 1] = f[n >> 1]; 161 // temporary variable for transformed[1] 162 double t1 = 0.5 * (f[0] - f[n]); 163 for (int i = 1; i < (n >> 1); i++) { 164 final SinCos sc = FastMath.sinCos(i * FastMath.PI / n); 165 final double a = 0.5 * (f[i] + f[n - i]); 166 final double b = sc.sin() * (f[i] - f[n - i]); 167 final double c = sc.cos() * (f[i] - f[n - i]); 168 x[i] = a - b; 169 x[n - i] = a + b; 170 t1 += c; 171 } 172 FastFourierTransformer transformer; 173 transformer = new FastFourierTransformer(DftNormalization.STANDARD); 174 Complex[] y = transformer.transform(x, TransformType.FORWARD); 175 176 // reconstruct the FCT result for the original array 177 transformed[0] = y[0].getReal(); 178 transformed[1] = t1; 179 for (int i = 1; i < (n >> 1); i++) { 180 transformed[2 * i] = y[i].getReal(); 181 transformed[2 * i + 1] = transformed[2 * i - 1] - y[i].getImaginary(); 182 } 183 transformed[n] = y[n >> 1].getReal(); 184 185 return transformed; 186 } 187 }