1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  package org.hipparchus.util;
23  
24  import org.hipparchus.exception.MathRuntimeException;
25  import org.junit.jupiter.params.ParameterizedTest;
26  import org.junit.jupiter.params.provider.MethodSource;
27  
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.lang.reflect.Modifier;
31  import java.lang.reflect.Type;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.List;
35  
36  import static org.junit.jupiter.api.Assertions.assertEquals;
37  import static org.junit.jupiter.api.Assertions.fail;
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  public class FastMathStrictComparisonTest {
48  
49      
50      private static final Double[] DOUBLE_SPECIAL_VALUES = {
51          -0.0, +0.0,                                         
52          Double.NaN,                                         
53          Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 
54          -Double.MAX_VALUE, Double.MAX_VALUE,                
55          
56          -Precision.EPSILON, Precision.EPSILON,              
57          -Precision.SAFE_MIN, Precision.SAFE_MIN,            
58          -Double.MIN_VALUE, Double.MIN_VALUE,                
59      };
60  
61      private static final Float [] FLOAT_SPECIAL_VALUES = {
62          -0.0f, +0.0f,                                       
63          Float.NaN,                                          
64          Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY,   
65          Float.MIN_VALUE, Float.MAX_VALUE,                   
66          -Float.MIN_VALUE, -Float.MAX_VALUE,                 
67      };
68  
69      private static final Object [] LONG_SPECIAL_VALUES = {
70          -1,0,1,                                             
71          Long.MIN_VALUE, Long.MAX_VALUE,                     
72      };
73  
74      private static final Object[] INT_SPECIAL_VALUES = {
75          -1,0,1,                                             
76          Integer.MIN_VALUE, Integer.MAX_VALUE,               
77      };
78  
79      private Method mathMethod;
80      private Method fastMethod;
81      private Type[] types;
82      private Object[][] valueArrays;
83  
84      public void initFastMathStrictComparisonTest(Method m, Method f, Type[] types, Object[][] data) throws Exception {
85          this.mathMethod=m;
86          this.fastMethod=f;
87          this.types=types;
88          this.valueArrays=data;
89      }
90  
91      @MethodSource("data")
92      @ParameterizedTest
93      void test1(Method m, Method f, Type[] types, Object[][] data) throws Exception {
94          initFastMathStrictComparisonTest(m, f, types, data);
95          setupMethodCall(mathMethod, fastMethod, types, valueArrays);
96      }
97      private static boolean isNumber(Double d) {
98          return !(d.isInfinite() || d.isNaN());
99      }
100 
101     private static boolean isNumber(Float f) {
102         return !(f.isInfinite() || f.isNaN());
103     }
104 
105     private static void reportFailedResults(Method mathMethod, Object[] params, Object expected, Object actual, int[] entries){
106         final String methodName = mathMethod.getName();
107         String format = null;
108         long actL=0;
109         long expL=0;
110         if (expected instanceof Double) {
111             Double exp = (Double) expected;
112             Double act = (Double) actual;
113             if (isNumber(exp) && isNumber(act) && exp != 0) { 
114                 actL = Double.doubleToLongBits(act);
115                 expL = Double.doubleToLongBits(exp);
116                 if (Math.abs(actL-expL)==1) {
117                     
118                     if (methodName.equals("toRadians") || methodName.equals("atan2")) {
119                         return;
120                     }
121                 }
122                 format = "%016x";
123             }
124         } else if (expected instanceof Float ){
125             Float exp = (Float) expected;
126             Float act = (Float) actual;
127             if (isNumber(exp) && isNumber(act) && exp != 0) { 
128                 actL = Float.floatToIntBits(act);
129                 expL = Float.floatToIntBits(exp);
130                 format = "%08x";
131             }
132         }
133         StringBuilder sb = new StringBuilder();
134         sb.append(mathMethod.getReturnType().getSimpleName());
135         sb.append(" ");
136         sb.append(methodName);
137         sb.append("(");
138         String sep = "";
139         for(Object o : params){
140             sb.append(sep);
141             sb.append(o);
142             sep=", ";
143         }
144         sb.append(") expected ");
145         if (format != null){
146             sb.append(String.format(format, expL));
147         } else {
148             sb.append(expected);
149         }
150         sb.append(" actual ");
151         if (format != null){
152             sb.append(String.format(format, actL));
153         } else {
154             sb.append(actual);
155         }
156         sb.append(" entries ");
157         sb.append(Arrays.toString(entries));
158         String message = sb.toString();
159         final boolean fatal = true;
160         if (fatal) {
161             fail(message);
162         } else {
163             System.out.println(message);
164         }
165     }
166 
167     private static void callMethods(Method mathMethod, Method fastMethod,
168             Object[] params, int[] entries) throws IllegalAccessException {
169         try {
170             Object expected;
171             try {
172                 expected = mathMethod.invoke(mathMethod, params);
173             } catch (InvocationTargetException ite) {
174                 expected = ite.getCause();
175             }
176             Object actual;
177             try {
178                 actual = fastMethod.invoke(mathMethod, params);
179             } catch (InvocationTargetException ite) {
180                 actual = ite.getCause();
181             }
182             if (expected instanceof ArithmeticException) {
183                 assertEquals(MathRuntimeException.class, actual.getClass());
184             } else  if (!expected.equals(actual)) {
185                 reportFailedResults(mathMethod, params, expected, actual, entries);
186             }
187         } catch (IllegalArgumentException e) {
188             System.out.println(mathMethod);
189             System.out.println(fastMethod);
190             System.out.print("params = ");
191             for (Object o : params) {
192                 System.out.print(" " + o);
193             }
194             System.out.println();
195             System.out.print("entries = ");
196             for (int i : entries) {
197                 System.out.print(" " + i);
198             }
199             System.out.println();
200             e.printStackTrace();
201             fail(mathMethod+" "+e);
202         }
203     }
204 
205     private static void setupMethodCall(Method mathMethod, Method fastMethod,
206                                         Type[] types, Object[][] valueArrays) throws Exception {
207         Object[] params = new Object[types.length];
208         int[] entries = new int[types.length];
209         for (int i = 0; i < params.length; ++i) {
210             for (int j = 0; j < valueArrays[i].length; ++j) {
211                 Object d = valueArrays[i][j];
212                 params[i] = d;
213                 entries[i] = j;
214             }
215         }
216         callMethods(mathMethod, fastMethod, params, entries);
217     }
218 
219     public static List<Object[]> data() throws Exception {
220         String singleMethod = System.getProperty("testMethod");
221         List<Object[]> list = new ArrayList<Object[]>();
222         for(Method mathMethod : StrictMath.class.getDeclaredMethods()) {
223             method:
224             if (Modifier.isPublic(mathMethod.getModifiers())){
225                 Type []types = mathMethod.getGenericParameterTypes();
226                 if (types.length >=1) { 
227                     try {
228                         
229                         Method fastMethod = FastMath.class.getDeclaredMethod(mathMethod.getName(), (Class[]) types);
230                         if (Modifier.isPublic(fastMethod.getModifiers())) { 
231                             if (singleMethod != null && !fastMethod.getName().equals(singleMethod)) {
232                                 break method;
233                             }
234                             Object [][] values = new Object[types.length][];
235                             int index = 0;
236                             for(Type t : types) {
237                                 if (t.equals(double.class)){
238                                     values[index]=DOUBLE_SPECIAL_VALUES;
239                                 } else if (t.equals(float.class)) {
240                                     values[index]=FLOAT_SPECIAL_VALUES;
241                                 } else if (t.equals(long.class)) {
242                                     values[index]=LONG_SPECIAL_VALUES;
243                                 } else if (t.equals(int.class)) {
244                                     values[index]=INT_SPECIAL_VALUES;
245                                 } else {
246                                     System.out.println("Cannot handle class "+t+" for "+mathMethod);
247                                     break method;
248                                 }
249                                 index++;
250                             }
251 
252                             
253 
254 
255 
256                             list.add(new Object[]{mathMethod, fastMethod, types, values});
257 
258                         } else {
259                             System.out.println("Cannot find public FastMath method corresponding to: "+mathMethod);
260                         }
261                     } catch (NoSuchMethodException e) {
262                         System.out.println("Cannot find FastMath method corresponding to: "+mathMethod);
263                     }
264                 }
265             }
266         }
267         return list;
268     }
269 }