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