1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.hipparchus.util;
18
19 import org.junit.jupiter.api.Test;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.io.PrintStream;
24 import java.lang.reflect.Field;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.nio.charset.StandardCharsets;
28 import java.util.Arrays;
29
30 import static org.junit.jupiter.api.Assertions.assertEquals;
31 import static org.junit.jupiter.api.Assertions.assertTrue;
32 import static org.junit.jupiter.api.Assertions.fail;
33
34 class FastMathCalcTest {
35
36 @Test
37 void testExpIntTables() {
38
39 final double[] fmTableA = getD1("ExpIntTable", "EXP_INT_TABLE_A");
40 final double[] fmTableB = getD1("ExpIntTable", "EXP_INT_TABLE_B");
41 final int len = getInt("EXP_INT_TABLE_LEN");
42 final int max = getInt("EXP_INT_TABLE_MAX_INDEX");
43 assertEquals(len, fmTableA.length);
44 assertEquals(len, fmTableB.length);
45
46 final double[] tmp = new double[2];
47 final double[] recip = new double[2];
48 for (int i = 0; i < max; i++) {
49 FastMathCalc.expint(i, tmp);
50 if (i == 0) {
51 if (Double.isNaN(FastMath.ulp(fmTableA[i])) ||
52 Double.isNaN(FastMath.ulp(fmTableB[i]))) {
53 assertEquals(fmTableA[max], tmp[0]);
54 assertEquals(fmTableB[max], tmp[1]);
55 } else {
56 assertEquals(fmTableA[max], tmp[0],
57 FastMath.ulp(fmTableA[i]));
58 assertEquals(fmTableB[max], tmp[1],
59 FastMath.ulp(fmTableB[i]));
60 }
61 } else {
62 FastMathCalc.splitReciprocal(tmp, recip);
63 if (Double.isNaN(FastMath.ulp(fmTableA[i])) ||
64 Double.isNaN(FastMath.ulp(fmTableB[i]))) {
65 assertEquals(fmTableA[max - i], recip[0]);
66 assertEquals(fmTableB[max - i], recip[1]);
67 } else {
68 assertEquals(fmTableA[max - i], recip[0], FastMath.ulp(fmTableA[i]));
69 assertEquals(fmTableB[max - i], recip[1], FastMath.ulp(fmTableB[i]));
70 }
71 }
72 }
73
74 }
75
76 @Test
77 void testExpFracTables() {
78
79 final double[] fmTableA = getD1("ExpFracTable", "EXP_FRAC_TABLE_A");
80 final double[] fmTableB = getD1("ExpFracTable", "EXP_FRAC_TABLE_B");
81 final int len = getInt("EXP_FRAC_TABLE_LEN");
82 assertEquals(len, fmTableA.length);
83 assertEquals(len, fmTableB.length);
84
85 final double factor = 1d / (len - 1);
86 final double[] tmp = new double[2];
87 for (int i = 0; i < len; i++) {
88 FastMathCalc.slowexp(i * factor, tmp);
89 assertEquals(fmTableA[i], tmp[0], FastMath.ulp(fmTableA[i]));
90 assertEquals(fmTableB[i], tmp[1], FastMath.ulp(fmTableB[i]));
91 }
92
93 }
94
95 @Test
96 void testLnMantTables() {
97 final double[][] fmTable = getD2("lnMant", "LN_MANT");
98 final int len = getInt("LN_MANT_LEN");
99 assertEquals(len, fmTable.length);
100
101 for (int i = 0; i < len; i++) {
102 final double d = Double.longBitsToDouble( (((long) i) << 42) | 0x3ff0000000000000L );
103 final double[] tmp = FastMathCalc.slowLog(d);
104 assertEquals(fmTable[i].length, tmp.length);
105 for (int j = 0; j < fmTable[i].length; ++j) {
106 assertEquals(fmTable[i][j], tmp[j], FastMath.ulp(fmTable[i][j]));
107 }
108 }
109
110 }
111
112 @Test
113 void testSplit() {
114 checkSplit(0x3ffe0045dab7321fl, 0x3ffe0045c0000000l, 0x3e7ab7321f000000l);
115 checkSplit(0x3ffe0045fab7321fl, 0x3ffe004600000000l, 0xbe55233784000000l);
116 checkSplit(0x7dfedcba9876543fl, 0x7dfedcba80000000l, 0x7c7876543f000000l);
117 checkSplit(0x7dfedcbaf876543fl, 0x7dfedcbb00000000l, 0xfc5e26af04000000l);
118 checkSplit(0xfdfedcba9876543fl, 0xfdfedcba80000000l, 0xfc7876543f000000l);
119 checkSplit(0xfdfedcbaf876543fl, 0xfdfedcbb00000000l, 0x7c5e26af04000000l);
120 }
121
122 private void checkSplit(final long bits, final long high, final long low) {
123 try {
124 Method split = FastMathCalc.class.getDeclaredMethod("split", Double.TYPE, double[].class);
125 split.setAccessible(true);
126 double d = Double.longBitsToDouble(bits);
127 double[] result = new double[2];
128 split.invoke(null, d, result);
129 assertEquals(bits, Double.doubleToRawLongBits(result[0] + result[1]));
130 assertEquals(high, Double.doubleToRawLongBits(result[0]));
131 assertEquals(low, Double.doubleToRawLongBits(result[1]));
132
133 } catch (NoSuchMethodException | SecurityException | IllegalArgumentException |
134 IllegalAccessException | InvocationTargetException e) {
135 fail(e.getLocalizedMessage());
136 }
137 }
138
139 @Test
140 void testSinCosTanTables() {
141 try {
142 final double[] sinA = getFastMathTable("SINE_TABLE_A");
143 final double[] sinB = getFastMathTable("SINE_TABLE_B");
144 final double[] cosA = getFastMathTable("COSINE_TABLE_A");
145 final double[] cosB = getFastMathTable("COSINE_TABLE_B");
146 final double[] tanA = getFastMathTable("TANGENT_TABLE_A");
147 final double[] tanB = getFastMathTable("TANGENT_TABLE_B");
148 Method buildSinCosTables = FastMathCalc.class.getDeclaredMethod("buildSinCosTables",
149 double[].class, double[].class, double[].class, double[].class,
150 Integer.TYPE,
151 double[].class, double[].class);
152 buildSinCosTables.setAccessible(true);
153 final double[] calcSinA = new double[sinA.length];
154 final double[] calcSinB = new double[sinB.length];
155 final double[] calcCosA = new double[cosA.length];
156 final double[] calcCosB = new double[cosB.length];
157 final double[] calcTanA = new double[tanA.length];
158 final double[] calcTanB = new double[tanB.length];
159 buildSinCosTables.invoke(null, calcSinA, calcSinB, calcCosA, calcCosB, sinA.length, calcTanA, calcTanB);
160 checkTable(sinA, calcSinA, 0);
161 checkTable(sinB, calcSinB, 0);
162 checkTable(cosA, calcCosA, 0);
163 checkTable(cosB, calcCosB, 0);
164 checkTable(tanA, calcTanA, 0);
165 checkTable(tanB, calcTanB, 0);
166
167 } catch (NoSuchMethodException | SecurityException | IllegalArgumentException |
168 IllegalAccessException | InvocationTargetException e) {
169 fail(e.getLocalizedMessage());
170 }
171 }
172
173 private double[] getFastMathTable(final String name) {
174 try {
175 final Field field = FastMath.class.getDeclaredField(name);
176 field.setAccessible(true);
177 return (double[]) field.get(null);
178 } catch (NoSuchFieldException | SecurityException |
179 IllegalArgumentException | IllegalAccessException e) {
180 fail(e.getLocalizedMessage());
181 return null;
182 }
183 }
184
185 private void checkTable(final double[] reference, final double[] actual, int maxUlps) {
186 assertEquals(reference.length, actual.length);
187 for (int i = 0; i < reference.length; ++i) {
188 assertTrue(Precision.equals(reference[i], actual[i], maxUlps));
189 }
190 }
191
192 @Test
193 void testPrintArray1() {
194 try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
195 PrintStream ps = new PrintStream(bos, true, StandardCharsets.UTF_8.name())) {
196 Method printArray = FastMathCalc.class.getDeclaredMethod("printarray", PrintStream.class,
197 String.class, Integer.TYPE, double[].class);
198 printArray.setAccessible(true);
199 printArray.invoke(null, ps, "name", 2, new double[] { 1.25, -0.5 });
200 assertEquals(String.format("name=%n" +
201 " {%n" +
202 " +1.25d,%n" +
203 " -0.5d,%n" +
204 " };%n"),
205 bos.toString(StandardCharsets.UTF_8.name()));
206 } catch (IOException | NoSuchMethodException | IllegalAccessException |
207 IllegalArgumentException | InvocationTargetException e) {
208 e.printStackTrace();
209 fail(e.getLocalizedMessage());
210 }
211 }
212
213 @Test
214 void testPrintArray2() {
215 try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
216 PrintStream ps = new PrintStream(bos, true, StandardCharsets.UTF_8.name())) {
217 Method printArray = FastMathCalc.class.getDeclaredMethod("printarray", PrintStream.class,
218 String.class, Integer.TYPE, double[][].class);
219 printArray.setAccessible(true);
220 printArray.invoke(null, ps, "name", 2, new double[][] { { 1.25, -0.5 }, { 0.0, 3.0 } });
221 assertEquals(String.format("name%n" +
222 " { %n" +
223 " {+1.25d, -0.5d, }, // 0%n" +
224 " {+0.0d, +3.0d, }, // 1%n" +
225 " };%n"),
226 bos.toString(StandardCharsets.UTF_8.name()));
227 } catch (IOException | NoSuchMethodException | IllegalAccessException |
228 IllegalArgumentException | InvocationTargetException e) {
229 e.printStackTrace();
230 fail(e.getLocalizedMessage());
231 }
232 }
233
234 private double[] getD1(final String innerClassName, final String tableName) {
235 try {
236
237 final Class<?> inerClass = Arrays.stream(FastMath.class.getDeclaredClasses()).
238 filter(c -> c.getName().endsWith("$" + innerClassName)).
239 findFirst().
240 get();
241 final Field fmTableField = inerClass.getDeclaredField(tableName);
242 fmTableField.setAccessible(true);
243 return (double[]) fmTableField.get(null);
244
245 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
246 fail(e.getLocalizedMessage());
247 return null;
248 }
249 }
250
251 private double[][] getD2(final String innerClassName, final String tableName) {
252 try {
253
254 final Class<?> inerClass = Arrays.stream(FastMath.class.getDeclaredClasses()).
255 filter(c -> c.getName().endsWith("$" + innerClassName)).
256 findFirst().
257 get();
258 final Field fmTableField = inerClass.getDeclaredField(tableName);
259 fmTableField.setAccessible(true);
260 return (double[][]) fmTableField.get(null);
261
262 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
263 fail(e.getLocalizedMessage());
264 return null;
265 }
266 }
267
268 private int getInt(String lenName) {
269 try {
270
271 final Field fmLen = FastMath.class.getDeclaredField(lenName);
272 fmLen.setAccessible(true);
273 return ((Integer) fmLen.get(null)).intValue();
274
275 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
276 fail(e.getLocalizedMessage());
277 return -1;
278 }
279 }
280
281 }