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;
188         double tolerance = 1E-12;
189 
190         double[] x = {
191             0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0
192         };
193         double[] y =
194             {
195                 172.0, -105.096569476353, 27.3137084989848, -12.9593152353742,
196                 8.0, -5.78585076868676, 4.68629150101524, -4.15826451958632,
197                 4.0
198             };
199 
200         result = transformer.transform(x, TransformType.FORWARD);
201         for (int i = 0; i < result.length; i++) {
202             Assert.assertEquals(y[i], result[i], tolerance);
203         }
204 
205         result = transformer.transform(y, TransformType.INVERSE);
206         for (int i = 0; i < result.length; i++) {
207             Assert.assertEquals(x[i], result[i], tolerance);
208         }
209 
210         TransformUtils.scaleArray(x, FastMath.sqrt(0.5 * (x.length - 1)));
211 
212         transformer = new FastCosineTransformer(DctNormalization.ORTHOGONAL_DCT_I);
213         result = transformer.transform(y, TransformType.FORWARD);
214         for (int i = 0; i < result.length; i++) {
215             Assert.assertEquals(x[i], result[i], tolerance);
216         }
217 
218         result = transformer.transform(x, TransformType.INVERSE);
219         for (int i = 0; i < result.length; i++) {
220             Assert.assertEquals(y[i], result[i], tolerance);
221         }
222     }
223 
224     /** Test of parameters for the transformer. */
225     @Test
226     public void testParameters()
227         throws Exception {
228         UnivariateFunction f = new Sin();
229         FastCosineTransformer transformer;
230         transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
231 
232         try {
233             // bad interval
234             transformer.transform(f, 1, -1, 65, TransformType.FORWARD);
235             Assert.fail("Expecting MathIllegalArgumentException - bad interval");
236         } catch (MathIllegalArgumentException ex) {
237             // expected
238         }
239         try {
240             // bad samples number
241             transformer.transform(f, -1, 1, 1, TransformType.FORWARD);
242             Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
243         } catch (MathIllegalArgumentException ex) {
244             // expected
245         }
246         try {
247             // bad samples number
248             transformer.transform(f, -1, 1, 64, TransformType.FORWARD);
249             Assert.fail("Expecting MathIllegalArgumentException - bad samples number");
250         } catch (MathIllegalArgumentException ex) {
251             // expected
252         }
253     }
254 
255     /** Test of transformer for the sine function. */
256     @Test
257     public void testSinFunction() {
258         UnivariateFunction f = new Sin();
259         FastCosineTransformer transformer;
260         transformer = new FastCosineTransformer(DctNormalization.STANDARD_DCT_I);
261         double min;
262         double max;
263         double[] result;
264         double tolerance = 1E-12;
265         int N = 9;
266 
267         double[] expected =
268             {
269                 0.0, 3.26197262739567, 0.0, -2.17958042710327, 0.0,
270                 -0.648846697642915, 0.0, -0.433545502649478, 0.0
271             };
272         min = 0.0;
273         max = 2.0 * FastMath.PI * N / (N - 1);
274         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
275         for (int i = 0; i < N; i++) {
276             Assert.assertEquals(expected[i], result[i], tolerance);
277         }
278 
279         min = -FastMath.PI;
280         max = FastMath.PI * (N + 1) / (N - 1);
281         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
282         for (int i = 0; i < N; i++) {
283             Assert.assertEquals(-expected[i], result[i], tolerance);
284         }
285     }
286 }