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 java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.lang.reflect.Type;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31
32 import org.hipparchus.exception.MathRuntimeException;
33 import org.junit.Assert;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.junit.runners.Parameterized;
37 import org.junit.runners.Parameterized.Parameters;
38
39
40
41
42
43
44
45
46
47 @RunWith(Parameterized.class)
48 public class FastMathStrictComparisonTest {
49
50
51 private static final Double[] DOUBLE_SPECIAL_VALUES = {
52 -0.0, +0.0,
53 Double.NaN,
54 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
55 -Double.MAX_VALUE, Double.MAX_VALUE,
56
57 -Precision.EPSILON, Precision.EPSILON,
58 -Precision.SAFE_MIN, Precision.SAFE_MIN,
59 -Double.MIN_VALUE, Double.MIN_VALUE,
60 };
61
62 private static final Float [] FLOAT_SPECIAL_VALUES = {
63 -0.0f, +0.0f,
64 Float.NaN,
65 Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY,
66 Float.MIN_VALUE, Float.MAX_VALUE,
67 -Float.MIN_VALUE, -Float.MAX_VALUE,
68 };
69
70 private static final Object [] LONG_SPECIAL_VALUES = {
71 -1,0,1,
72 Long.MIN_VALUE, Long.MAX_VALUE,
73 };
74
75 private static final Object[] INT_SPECIAL_VALUES = {
76 -1,0,1,
77 Integer.MIN_VALUE, Integer.MAX_VALUE,
78 };
79
80 private final Method mathMethod;
81 private final Method fastMethod;
82 private final Type[] types;
83 private final Object[][] valueArrays;
84
85 public FastMathStrictComparisonTest(Method m, Method f, Type[] types, Object[][] data) throws Exception{
86 this.mathMethod=m;
87 this.fastMethod=f;
88 this.types=types;
89 this.valueArrays=data;
90 }
91
92 @Test
93 public void test1() throws Exception{
94 setupMethodCall(mathMethod, fastMethod, types, valueArrays);
95 }
96 private static boolean isNumber(Double d) {
97 return !(d.isInfinite() || d.isNaN());
98 }
99
100 private static boolean isNumber(Float f) {
101 return !(f.isInfinite() || f.isNaN());
102 }
103
104 private static void reportFailedResults(Method mathMethod, Object[] params, Object expected, Object actual, int[] entries){
105 final String methodName = mathMethod.getName();
106 String format = null;
107 long actL=0;
108 long expL=0;
109 if (expected instanceof Double) {
110 Double exp = (Double) expected;
111 Double act = (Double) actual;
112 if (isNumber(exp) && isNumber(act) && exp != 0) {
113 actL = Double.doubleToLongBits(act);
114 expL = Double.doubleToLongBits(exp);
115 if (Math.abs(actL-expL)==1) {
116
117 if (methodName.equals("toRadians") || methodName.equals("atan2")) {
118 return;
119 }
120 }
121 format = "%016x";
122 }
123 } else if (expected instanceof Float ){
124 Float exp = (Float) expected;
125 Float act = (Float) actual;
126 if (isNumber(exp) && isNumber(act) && exp != 0) {
127 actL = Float.floatToIntBits(act);
128 expL = Float.floatToIntBits(exp);
129 format = "%08x";
130 }
131 }
132 StringBuilder sb = new StringBuilder();
133 sb.append(mathMethod.getReturnType().getSimpleName());
134 sb.append(" ");
135 sb.append(methodName);
136 sb.append("(");
137 String sep = "";
138 for(Object o : params){
139 sb.append(sep);
140 sb.append(o);
141 sep=", ";
142 }
143 sb.append(") expected ");
144 if (format != null){
145 sb.append(String.format(format, expL));
146 } else {
147 sb.append(expected);
148 }
149 sb.append(" actual ");
150 if (format != null){
151 sb.append(String.format(format, actL));
152 } else {
153 sb.append(actual);
154 }
155 sb.append(" entries ");
156 sb.append(Arrays.toString(entries));
157 String message = sb.toString();
158 final boolean fatal = true;
159 if (fatal) {
160 Assert.fail(message);
161 } else {
162 System.out.println(message);
163 }
164 }
165
166 private static void callMethods(Method mathMethod, Method fastMethod,
167 Object[] params, int[] entries) throws IllegalAccessException {
168 try {
169 Object expected;
170 try {
171 expected = mathMethod.invoke(mathMethod, params);
172 } catch (InvocationTargetException ite) {
173 expected = ite.getCause();
174 }
175 Object actual;
176 try {
177 actual = fastMethod.invoke(mathMethod, params);
178 } catch (InvocationTargetException ite) {
179 actual = ite.getCause();
180 }
181 if (expected instanceof ArithmeticException) {
182 Assert.assertEquals(MathRuntimeException.class, actual.getClass());
183 } else if (!expected.equals(actual)) {
184 reportFailedResults(mathMethod, params, expected, actual, entries);
185 }
186 } catch (IllegalArgumentException e) {
187 System.out.println(mathMethod);
188 System.out.println(fastMethod);
189 System.out.print("params = ");
190 for (Object o : params) {
191 System.out.print(" " + o);
192 }
193 System.out.println();
194 System.out.print("entries = ");
195 for (int i : entries) {
196 System.out.print(" " + i);
197 }
198 System.out.println();
199 e.printStackTrace();
200 Assert.fail(mathMethod+" "+e);
201 }
202 }
203
204 private static void setupMethodCall(Method mathMethod, Method fastMethod,
205 Type[] types, Object[][] valueArrays) throws Exception {
206 Object[] params = new Object[types.length];
207 int[] entries = new int[types.length];
208 for (int i = 0; i < params.length; ++i) {
209 for (int j = 0; j < valueArrays[i].length; ++j) {
210 Object d = valueArrays[i][j];
211 params[i] = d;
212 entries[i] = j;
213 }
214 }
215 callMethods(mathMethod, fastMethod, params, entries);
216 }
217
218 @Parameters
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 }