View Javadoc
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 org.hipparchus.analysis.UnivariateFunction;
25  import org.hipparchus.analysis.function.Sin;
26  import org.hipparchus.analysis.function.Sinc;
27  import org.hipparchus.exception.LocalizedCoreFormats;
28  import org.hipparchus.exception.MathIllegalArgumentException;
29  import org.hipparchus.exception.MathIllegalStateException;
30  import org.hipparchus.util.FastMath;
31  import org.junit.jupiter.params.ParameterizedTest;
32  import org.junit.jupiter.params.provider.MethodSource;
33  
34  import java.util.Arrays;
35  import java.util.Collection;
36  
37  import static org.junit.jupiter.api.Assertions.assertEquals;
38  import static org.junit.jupiter.api.Assertions.fail;
39  
40  /**
41   * Test case for fast cosine transformer.
42   * <p>
43   * FCT algorithm is exact, the small tolerance number is used only to account
44   * for round-off errors.
45   *
46   */
47  public final class FastCosineTransformerTest
48      extends RealTransformerAbstractTest<DctNormalization> {
49  
50      private DctNormalization normalization;
51  
52      private int[] invalidDataSize;
53  
54      private double[] relativeTolerance;
55  
56      private int[] validDataSize;
57  
58      public void initFastCosineTransformerTest(final DctNormalization normalization) {
59          this.normalization = normalization;
60          this.validDataSize = new int[] {
61              2, 3, 5, 9, 17, 33, 65, 129
62          };
63          this.invalidDataSize = new int[] {
64              128
65          };
66          this.relativeTolerance = new double[] {
67              1E-15, 1E-15, 1E-14, 1E-13, 1E-13, 1E-12, 1E-11, 1E-10
68          };
69      }
70  
71      @Override
72      void init(DctNormalization normalization) {
73          initFastCosineTransformerTest(normalization);
74      }
75  
76      /**
77       * Returns an array containing {@code true, false} in order to check both
78       * standard and orthogonal DCTs.
79       *
80       * @return an array of parameters for this parameterized test
81       */
82      public static Collection<Object[]> data() {
83          final DctNormalization[] normalization = DctNormalization.values();
84          final Object[][] data = new DctNormalization[normalization.length][1];
85          for (int i = 0; i < normalization.length; i++){
86              data[i][0] = normalization[i];
87          }
88          return Arrays.asList(data);
89      }
90  
91      @Override
92      RealTransformer createRealTransformer() {
93          return new FastCosineTransformer(normalization);
94      }
95  
96      @Override
97      int getInvalidDataSize(final int i) {
98          return invalidDataSize[i];
99      }
100 
101     @Override
102     int getNumberOfInvalidDataSizes() {
103         return invalidDataSize.length;
104     }
105 
106     @Override
107     int getNumberOfValidDataSizes() {
108         return validDataSize.length;
109     }
110 
111     @Override
112     double getRelativeTolerance(final int i) {
113         return relativeTolerance[i];
114     }
115 
116     @Override
117     int getValidDataSize(final int i) {
118         return validDataSize[i];
119     }
120 
121     @Override
122     UnivariateFunction getValidFunction() {
123         return new Sinc();
124     }
125 
126     @Override
127     double getValidLowerBound() {
128         return 0.0;
129     }
130 
131     @Override
132     double getValidUpperBound() {
133         return FastMath.PI;
134     }
135 
136     @Override
137     double[] transform(final double[] x, final TransformType type) {
138         final int n = x.length;
139         final double[] y = new double[n];
140         final double[] cos = new double[2 * (n - 1)];
141         for (int i = 0; i < cos.length; i++) {
142             cos[i] = FastMath.cos(FastMath.PI * i / (n - 1.0));
143         }
144         int sgn = 1;
145         for (int j = 0; j < n; j++) {
146             double yj = 0.5 * (x[0] + sgn * x[n - 1]);
147             for (int i = 1; i < n - 1; i++) {
148                 yj += x[i] * cos[(i * j) % cos.length];
149             }
150             y[j] = yj;
151             sgn *= -1;
152         }
153         final double s;
154         if (type == TransformType.FORWARD) {
155             if (normalization == DctNormalization.STANDARD_DCT_I) {
156                 s = 1.0;
157             } else if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
158                 s = FastMath.sqrt(2.0 / (n - 1.0));
159             } else {
160                 throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
161             }
162         } else if (type == TransformType.INVERSE) {
163             if (normalization == DctNormalization.STANDARD_DCT_I) {
164                 s = 2.0 / (n - 1.0);
165             } else if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
166                 s = FastMath.sqrt(2.0 / (n - 1.0));
167             } else {
168                 throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
169             }
170         } else {
171             /*
172              * Should never occur. This clause is a safeguard in case other
173              * types are used to TransformType (which should not be done).
174              */
175             throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
176         }
177         TransformUtils.scaleArray(y, s);
178         return y;
179     }
180 
181     /*
182      * Additional tests.
183      */
184 
185     /** Test of parameters for the transformer. */
186     @MethodSource("data")
187     @ParameterizedTest
188     void testParameters(final DctNormalization normalization)
189         throws Exception {
190         initFastCosineTransformerTest(normalization);
191         UnivariateFunction f = new Sin();
192         FastCosineTransformer transformer;
193         transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
194 
195         try {
196             // bad interval
197             transformer.transform(f, 1, -1, 65, TransformType.FORWARD);
198             fail("Expecting MathIllegalArgumentException - bad interval");
199         } catch (MathIllegalArgumentException ex) {
200             // expected
201         }
202         try {
203             // bad samples number
204             transformer.transform(f, -1, 1, 1, TransformType.FORWARD);
205             fail("Expecting MathIllegalArgumentException - bad samples number");
206         } catch (MathIllegalArgumentException ex) {
207             // expected
208         }
209         try {
210             // bad samples number
211             transformer.transform(f, -1, 1, 64, TransformType.FORWARD);
212             fail("Expecting MathIllegalArgumentException - bad samples number");
213         } catch (MathIllegalArgumentException ex) {
214             // expected
215         }
216     }
217 
218     /** Test of transformer for the sine function. */
219     @MethodSource("data")
220     @ParameterizedTest
221     void testSinFunction(final DctNormalization normalization) {
222         initFastCosineTransformerTest(normalization);
223         UnivariateFunction f = new Sin();
224         FastCosineTransformer transformer;
225         transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
226         double min;
227         double max;
228         double[] result;
229         double tolerance = 1E-12;
230         int N = 9;
231 
232         double[] expected =
233             {
234                 0.0, 3.26197262739567, 0.0, -2.17958042710327, 0.0,
235                 -0.648846697642915, 0.0, -0.433545502649478, 0.0
236             };
237         min = 0.0;
238         max = 2.0 * FastMath.PI * N / (N - 1);
239         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
240         for (int i = 0; i < N; i++) {
241             assertEquals(expected[i], result[i], tolerance);
242         }
243 
244         min = -FastMath.PI;
245         max = FastMath.PI * (N + 1) / (N - 1);
246         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
247         for (int i = 0; i < N; i++) {
248             assertEquals(-expected[i], result[i], tolerance);
249         }
250     }
251 
252     /** Test of transformer for the ad hoc data. */
253     @MethodSource("data")
254     @ParameterizedTest
255     void testAdHocData(final DctNormalization normalization) {
256         initFastCosineTransformerTest(normalization);
257         FastCosineTransformer transformer;
258         transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
259         double[] result;
260         double tolerance = 1E-12;
261 
262         double[] x = {
263                         0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0
264         };
265         double[] y =
266                         {
267                                         172.0, -105.096569476353, 27.3137084989848, -12.9593152353742,
268                                         8.0, -5.78585076868676, 4.68629150101524, -4.15826451958632,
269                                         4.0
270                         };
271 
272         result = transformer.transform(x, TransformType.FORWARD);
273         for (int i = 0; i < result.length; i++) {
274             assertEquals(y[i], result[i], tolerance);
275         }
276 
277         result = transformer.transform(y, TransformType.INVERSE);
278         for (int i = 0; i < result.length; i++) {
279             assertEquals(x[i], result[i], tolerance);
280         }
281 
282         TransformUtils.scaleArray(x, FastMath.sqrt(0.5 * (x.length - 1)));
283 
284         transformer = new FastCosineTransformer(DctNormalization.ORTHOGONAL_DCT_I);
285         result = transformer.transform(y, TransformType.FORWARD);
286         for (int i = 0; i < result.length; i++) {
287             assertEquals(x[i], result[i], tolerance);
288         }
289 
290         result = transformer.transform(x, TransformType.INVERSE);
291         for (int i = 0; i < result.length; i++) {
292             assertEquals(y[i], result[i], tolerance);
293         }
294     }
295 }