1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.transform;
18
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.function.DoubleUnaryOperator;
22
23 import org.junit.Assert;
24 import org.junit.Test;
25 import org.junit.runner.RunWith;
26 import org.junit.runners.Parameterized;
27 import org.junit.runners.Parameterized.Parameters;
28
29 import org.apache.commons.math3.analysis.UnivariateFunction;
30 import org.apache.commons.math3.analysis.function.Sin;
31 import org.apache.commons.math3.analysis.function.Sinc;
32
33
34
35
36
37
38
39 @RunWith(value = Parameterized.class)
40 public final class FastCosineTransformerTest
41 extends RealTransformerAbstractTest {
42
43 private final FastCosineTransform.Norm normalization;
44
45 private final int[] invalidDataSize;
46
47 private final double[] relativeTolerance;
48
49 private final int[] validDataSize;
50
51 public FastCosineTransformerTest(final FastCosineTransform.Norm normalization) {
52 this.normalization = normalization;
53 this.validDataSize = new int[] {
54 2, 3, 5, 9, 17, 33, 65, 129
55 };
56 this.invalidDataSize = new int[] {
57 128
58 };
59 this.relativeTolerance = new double[] {
60 1e-15, 1e-15, 1e-14, 1e-13, 1e-13, 1e-12, 1e-11, 1e-10
61 };
62 }
63
64
65
66
67
68
69
70 @Parameters
71 public static Collection<Object[]> data() {
72 final FastCosineTransform.Norm[] normalization = FastCosineTransform.Norm.values();
73 final Object[][] data = new FastCosineTransform.Norm[normalization.length][1];
74 for (int i = 0; i < normalization.length; i++) {
75 data[i][0] = normalization[i];
76 }
77 return Arrays.asList(data);
78 }
79
80 @Override
81 RealTransform createRealTransformer(boolean inverse) {
82 return new FastCosineTransform(normalization, inverse);
83 }
84
85 @Override
86 int getInvalidDataSize(final int i) {
87 return invalidDataSize[i];
88 }
89
90 @Override
91 int getNumberOfInvalidDataSizes() {
92 return invalidDataSize.length;
93 }
94
95 @Override
96 int getNumberOfValidDataSizes() {
97 return validDataSize.length;
98 }
99
100 @Override
101 double getRelativeTolerance(final int i) {
102 return relativeTolerance[i];
103 }
104
105 @Override
106 int getValidDataSize(final int i) {
107 return validDataSize[i];
108 }
109
110 @Override
111 DoubleUnaryOperator getValidFunction() {
112 final UnivariateFunction sinc = new Sinc();
113 return x -> sinc.value(x);
114 }
115
116 @Override
117 double getValidLowerBound() {
118 return 0.0;
119 }
120
121 @Override
122 double getValidUpperBound() {
123 return Math.PI;
124 }
125
126 @Override
127 double[] transform(final double[] x,
128 final boolean inverse) {
129 final int n = x.length;
130 final double[] y = new double[n];
131 final double[] cos = new double[2 * (n - 1)];
132 for (int i = 0; i < cos.length; i++) {
133 cos[i] = Math.cos(Math.PI * i / (n - 1.0));
134 }
135 int sgn = 1;
136 for (int j = 0; j < n; j++) {
137 double yj = 0.5 * (x[0] + sgn * x[n - 1]);
138 for (int i = 1; i < n - 1; i++) {
139 yj += x[i] * cos[(i * j) % cos.length];
140 }
141 y[j] = yj;
142 sgn *= -1;
143 }
144 final double s;
145 if (!inverse) {
146 if (normalization == FastCosineTransform.Norm.STD) {
147 s = 1.0;
148 } else if (normalization == FastCosineTransform.Norm.ORTHO) {
149 s = Math.sqrt(2.0 / (n - 1.0));
150 } else {
151 throw new IllegalStateException();
152 }
153 } else {
154 if (normalization == FastCosineTransform.Norm.STD) {
155 s = 2.0 / (n - 1.0);
156 } else if (normalization == FastCosineTransform.Norm.ORTHO) {
157 s = Math.sqrt(2.0 / (n - 1.0));
158 } else {
159 throw new IllegalStateException();
160 }
161 }
162 TransformUtils.scaleInPlace(y, s);
163 return y;
164 }
165
166
167
168
169 @Test
170 public void testAdHocData() {
171 FastCosineTransform transformer;
172 double[] result;
173 double tolerance = 1e-12;
174
175 final double[] x = {
176 0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0
177 };
178 final double[] y = {
179 172.0, -105.096569476353, 27.3137084989848, -12.9593152353742,
180 8.0, -5.78585076868676, 4.68629150101524, -4.15826451958632,
181 4.0
182 };
183
184 transformer = new FastCosineTransform(FastCosineTransform.Norm.STD);
185 result = transformer.apply(x);
186 for (int i = 0; i < result.length; i++) {
187 Assert.assertEquals(y[i], result[i], tolerance);
188 }
189
190 transformer = new FastCosineTransform(FastCosineTransform.Norm.STD, true);
191 result = transformer.apply(y);
192 for (int i = 0; i < result.length; i++) {
193 Assert.assertEquals(x[i], result[i], tolerance);
194 }
195
196 TransformUtils.scaleInPlace(x, Math.sqrt(0.5 * (x.length - 1)));
197
198 transformer = new FastCosineTransform(FastCosineTransform.Norm.ORTHO);
199 result = transformer.apply(y);
200 for (int i = 0; i < result.length; i++) {
201 Assert.assertEquals(x[i], result[i], tolerance);
202 }
203
204 transformer = new FastCosineTransform(FastCosineTransform.Norm.ORTHO, true);
205 result = transformer.apply(x);
206 for (int i = 0; i < result.length; i++) {
207 Assert.assertEquals(y[i], result[i], tolerance);
208 }
209 }
210
211
212 @Test
213 public void testParameters() throws Exception {
214 final UnivariateFunction sinFunction = new Sin();
215 final DoubleUnaryOperator f = x -> sinFunction.value(x);
216 final FastCosineTransform transformer = new FastCosineTransform(FastCosineTransform.Norm.STD);
217
218 try {
219
220 transformer.apply(f, 1, -1, 65);
221 Assert.fail("Expecting IllegalArgumentException - bad interval");
222 } catch (IllegalArgumentException ex) {
223
224 }
225 try {
226
227 transformer.apply(f, -1, 1, 1);
228 Assert.fail("Expecting IllegalArgumentException - bad samples number");
229 } catch (IllegalArgumentException ex) {
230
231 }
232 try {
233
234 transformer.apply(f, -1, 1, 64);
235 Assert.fail("Expecting IllegalArgumentException - bad samples number");
236 } catch (IllegalArgumentException ex) {
237
238 }
239 }
240
241
242 @Test
243 public void testSinFunction() {
244 final UnivariateFunction sinFunction = new Sin();
245 final DoubleUnaryOperator f = x -> sinFunction.value(x);
246 final FastCosineTransform transformer = new FastCosineTransform(FastCosineTransform.Norm.STD);
247 double tolerance = 1e-12;
248 int size = 9;
249
250 final double[] expected = {
251 0.0, 3.26197262739567, 0.0, -2.17958042710327, 0.0,
252 -0.648846697642915, 0.0, -0.433545502649478, 0.0
253 };
254 double min = 0.0;
255 double max = 2.0 * Math.PI * size / (size - 1);
256 double[] result = transformer.apply(f, min, max, size);
257 for (int i = 0; i < size; i++) {
258 Assert.assertEquals(expected[i], result[i], tolerance);
259 }
260
261 min = -Math.PI;
262 max = Math.PI * (size + 1) / (size - 1);
263 result = transformer.apply(f, min, max, size);
264 for (int i = 0; i < size; i++) {
265 Assert.assertEquals(-expected[i], result[i], tolerance);
266 }
267 }
268 }