1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.core.jdkmath;
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.lang.reflect.Type;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
26
27 import org.apache.commons.numbers.core.Precision;
28 import org.junit.Assert;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.junit.runners.Parameterized;
32 import org.junit.runners.Parameterized.Parameters;
33
34
35
36 import org.apache.commons.math4.core.jdkmath.AccurateMath;
37
38
39
40
41
42
43
44
45
46 @SuppressWarnings("boxing")
47 @RunWith(Parameterized.class)
48 public class AccurateMathStrictComparisonTest {
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 AccurateMathStrictComparisonTest(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
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 Assert.fail(message);
162 } else {
163
164 System.out.println(message);
165
166 }
167 }
168
169 private static void callMethods(Method mathMethod, Method fastMethod,
170 Object[] params, int[] entries) throws IllegalAccessException {
171 try {
172 Object expected;
173 try {
174 expected = mathMethod.invoke(mathMethod, params);
175 } catch (InvocationTargetException ite) {
176 expected = ite.getCause();
177 }
178 Object actual;
179 try {
180 actual = fastMethod.invoke(mathMethod, params);
181 } catch (InvocationTargetException ite) {
182 actual = ite.getCause();
183 }
184 if (expected instanceof ArithmeticException) {
185 Assert.assertTrue(actual instanceof ArithmeticException);
186 } else if (!expected.equals(actual)) {
187 reportFailedResults(mathMethod, params, expected, actual, entries);
188 }
189 } catch (IllegalArgumentException e) {
190 Assert.fail(mathMethod + " " + e);
191 }
192 }
193
194 private static void setupMethodCall(Method mathMethod, Method fastMethod,
195 Type[] types, Object[][] valueArrays) throws Exception {
196 Object[] params = new Object[types.length];
197 int entry1 = 0;
198 int[] entries = new int[types.length];
199 for (Object d : valueArrays[0]) {
200 entry1++;
201 params[0] = d;
202 entries[0] = entry1;
203 if (params.length > 1) {
204 int entry2 = 0;
205 for (Object d1 : valueArrays[1]) {
206 entry2++;
207 params[1] = d1;
208 entries[1] = entry2;
209 callMethods(mathMethod, fastMethod, params, entries);
210 }
211 } else {
212 callMethods(mathMethod, fastMethod, params, entries);
213 }
214 }
215 }
216
217 @Parameters
218 public static List<Object[]> data() throws Exception {
219
220 String singleMethod = System.getProperty("testMethod");
221 List<Object[]> list = new ArrayList<>();
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 = AccurateMath.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
260 .println("Cannot find public AccurateMath method corresponding to: " + mathMethod);
261 }
262 } catch (NoSuchMethodException e) {
263 System.out.println("Cannot find AccurateMath method corresponding to: " + mathMethod);
264 }
265 }
266 }
267 }
268 return list;
269
270 }
271 }