View Javadoc
1   /*
2    * Licensed to the Hipparchus project 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  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 }