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 }