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