1 package org.djunits.value;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.fail;
5
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Field;
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Modifier;
11
12 import org.djunits.quantity.Quantity;
13 import org.djunits.unit.Unit;
14 import org.djunits.unit.scale.LinearScale;
15 import org.djunits.unit.si.SIPrefixes;
16 import org.djunits.unit.unitsystem.UnitSystem;
17 import org.djunits.util.ClassUtil;
18 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
19 import org.djunits.value.vdouble.scalar.base.DoubleScalarAbs;
20 import org.djunits.value.vdouble.scalar.base.DoubleScalarRel;
21 import org.djunits.value.vfloat.scalar.base.FloatScalar;
22 import org.djunits.value.vfloat.scalar.base.FloatScalarAbs;
23 import org.djunits.value.vfloat.scalar.base.FloatScalarRel;
24 import org.junit.jupiter.api.Test;
25
26
27
28
29
30
31
32
33
34
35
36 public class ScalarOperationsTest
37 {
38
39
40
41
42
43
44
45
46
47
48
49 @Test
50 public final void scalarOperationsTest() throws NoSuchMethodException, InstantiationException, IllegalAccessException,
51 InvocationTargetException, NoSuchFieldException, SecurityException, IllegalArgumentException, ClassNotFoundException
52 {
53 doubleOrFloatScalarOperationsTest(true);
54 doubleOrFloatScalarOperationsTest(false);
55 }
56
57
58
59
60
61
62
63
64
65
66
67
68
69 private void doubleOrFloatScalarOperationsTest(final boolean doubleType)
70 throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
71 NoSuchFieldException, SecurityException, IllegalArgumentException, ClassNotFoundException
72 {
73 final String upperType = doubleType ? "Double" : "Float";
74 final String type = upperType.toLowerCase();
75
76 for (int i = 0; i < CLASSNAMES.ABS_LIST.size(); i++)
77 {
78 String scalarNameAbs = CLASSNAMES.ABS_LIST.get(i);
79 String scalarNameRel = CLASSNAMES.REL_WITH_ABS_LIST.get(i);
80 String scalarClassNameAbs = doubleType ? scalarNameAbs : "Float" + scalarNameAbs;
81 String scalarClassNameRel = doubleType ? scalarNameRel : "Float" + scalarNameRel;
82 Class<?> scalarClassAbs = null;
83 Class<?> scalarClassRel = null;
84
85 try
86 {
87 scalarClassAbs = Class.forName("org.djunits.value.v" + type + ".scalar." + scalarClassNameAbs);
88 }
89 catch (ClassNotFoundException exception)
90 {
91 fail("Class Rel not found for " + upperType + "Scalar class " + "org.djunits.value.v" + type + ".scalar."
92 + scalarClassNameAbs);
93 }
94 try
95 {
96 scalarClassRel = Class.forName("org.djunits.value.v" + type + ".scalar." + scalarClassNameRel);
97 }
98 catch (ClassNotFoundException exception)
99 {
100 fail("Class Rel not found for " + upperType + "Scalar class " + "org.djunits.value.v" + type + ".scalar."
101 + scalarClassNameRel);
102 }
103 testMethods(scalarClassAbs, true, doubleType);
104 testMethods(scalarClassRel, false, doubleType);
105 }
106
107
108 for (String scalarName : CLASSNAMES.REL_LIST)
109 {
110 String scalarClassName = doubleType ? scalarName : "Float" + scalarName;
111 Class<?> scalarClassRel = null;
112 try
113 {
114 scalarClassRel = Class.forName("org.djunits.value.v" + type + ".scalar." + scalarClassName);
115 }
116 catch (ClassNotFoundException exception)
117 {
118 fail("Class Rel not found for " + upperType + "DoubleScalar class " + "org.djunits.value.v" + type + ".scalar."
119 + scalarClassName);
120 }
121 testMethods(scalarClassRel, false, doubleType);
122 }
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 private void testMethods(final Class<?> scalarClassAbsRel, final boolean isAbs, final boolean doubleType)
139 throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
140 NoSuchFieldException, ClassNotFoundException
141 {
142 for (Method method : scalarClassAbsRel.getMethods())
143 {
144 if (method.getName().equals("times"))
145 {
146
147 testMultiplyOrDivideMethodAbsRel(scalarClassAbsRel, isAbs, method, true, doubleType);
148 }
149 else if (method.getName().equals("divide"))
150 {
151
152 testMultiplyOrDivideMethodAbsRel(scalarClassAbsRel, isAbs, method, false, doubleType);
153 }
154 }
155 testUnaryMethods(scalarClassAbsRel, isAbs, doubleType);
156 testStaticMethods(scalarClassAbsRel, isAbs, doubleType);
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 private void testMultiplyOrDivideMethodAbsRel(final Class<?> scalarClass, final boolean abs, final Method method,
173 final boolean multiply, final boolean doubleType) throws NoSuchMethodException, InstantiationException,
174 IllegalAccessException, InvocationTargetException, NoSuchFieldException
175 {
176 if (Modifier.isStatic(method.getModifiers()))
177 {
178
179 return;
180 }
181 Class<?> relativeOrAbsoluteClass = null;
182 try
183 {
184 relativeOrAbsoluteClass = Class.forName("org.djunits.value." + (abs ? "Absolute" : "Relative"));
185 }
186 catch (ClassNotFoundException exception)
187 {
188 fail("Could not find org.djunits.value.Relative class");
189 }
190 Class<?>[] parTypes = method.getParameterTypes();
191 if (parTypes.length != 1)
192 {
193 fail("DoubleScalar class " + scalarClass.getName() + "." + method.getName() + "() has " + parTypes.length
194 + " parameters, <> 1");
195 }
196 Class<?> parameterClass = parTypes[0];
197 if (parameterClass.toString().equals("double") || parameterClass.toString().equals("float"))
198 {
199
200 return;
201 }
202 if (parameterClass.getSimpleName().startsWith("Abstract"))
203 {
204
205 return;
206 }
207 if (parameterClass.getSimpleName().endsWith("ScalarRel"))
208 {
209
210 return;
211 }
212 if (!relativeOrAbsoluteClass.isAssignableFrom(parameterClass))
213 {
214
215
216 fail("DoubleScalar class " + scalarClass.getName() + "." + method.getName() + "() has parameter with non-"
217 + relativeOrAbsoluteClass + " class: " + relativeOrAbsoluteClass.getName());
218 }
219
220 Class<?> returnClass = method.getReturnType();
221 if (!relativeOrAbsoluteClass.isAssignableFrom(returnClass))
222 {
223 fail("DoubleScalar class " + scalarClass.getName() + ".times() has return type with non-relative class: "
224 + returnClass.getName());
225 }
226
227
228 String returnSI = getCoefficients(getUnitClass(returnClass));
229
230
231
232
233
234
235
236
237
238 Constructor<?> constructor = scalarClass.getConstructor(double.class, getUnitClass(scalarClass));
239 if (abs)
240 {
241 fail("Absolute types should not have a multiply or divide method");
242 DoubleScalarAbs<?, ?, ?, ?> left = (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(123d,
243 getSIUnitInstance(getUnitClass(scalarClass), abs));
244
245 constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
246 DoubleScalarAbs<?, ?, ?, ?> right = (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(456d,
247 getSIUnitInstance(getUnitClass(parameterClass), abs));
248
249 double expectedValue = multiply ? 123d * 456 : 123d / 456;
250
251 if (multiply)
252 {
253 Method multiplyMethod = ClassUtil.resolveMethod(scalarClass, "times", new Class[] {parameterClass});
254 Object result = multiplyMethod.invoke(left, right);
255 double resultSI = ((DoubleScalarAbs<?, ?, ?, ?>) result).getSI();
256 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
257 }
258 else
259 {
260 Method divideMethod = ClassUtil.resolveMethod(scalarClass, "divide", new Class[] {parameterClass});
261 Object result = divideMethod.invoke(left, right);
262 double resultSI = ((DoubleScalarAbs<?, ?, ?, ?>) result).getSI();
263 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
264 }
265 }
266 else
267 {
268 if (doubleType)
269 {
270 DoubleScalarRel<?, ?> left = (DoubleScalarRel<?, ?>) constructor.newInstance(123d,
271 getSIUnitInstance(getUnitClass(scalarClass), abs));
272
273 constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
274 DoubleScalarRel<?, ?> right = (DoubleScalarRel<?, ?>) constructor.newInstance(456d,
275 getSIUnitInstance(getUnitClass(parameterClass), abs));
276
277 double expectedValue = multiply ? 123d * 456 : 123d / 456;
278
279 if (multiply)
280 {
281 Method multiplyMethod = ClassUtil.resolveMethod(scalarClass, "times", new Class[] {parameterClass});
282 Object result = multiplyMethod.invoke(left, right);
283 double resultSI = ((DoubleScalarRel<?, ?>) result).getSI();
284 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
285 }
286 else
287 {
288 Method divideMethod = ClassUtil.resolveMethod(scalarClass, "divide", new Class[] {parameterClass});
289 Object result = divideMethod.invoke(left, right);
290 double resultSI = ((DoubleScalarRel<?, ?>) result).getSI();
291 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
292 }
293 DoubleScalarRel<?, ?> result = multiply ? DoubleScalar.multiply(left, right) : DoubleScalar.divide(left, right);
294
295 String resultCoefficients = result.getDisplayUnit().getQuantity().getSiDimensions().toString();
296 assertEquals(resultCoefficients, returnSI, "SI coefficients of result of " + left.getClass().getSimpleName()
297 + " x " + right.getClass().getSimpleName() + " should match expected SI coefficients");
298 }
299 else
300 {
301 FloatScalarRel<?, ?> left =
302 (FloatScalarRel<?, ?>) constructor.newInstance(123f, getSIUnitInstance(getUnitClass(scalarClass), abs));
303
304 constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
305 FloatScalarRel<?, ?> right = (FloatScalarRel<?, ?>) constructor.newInstance(456f,
306 getSIUnitInstance(getUnitClass(parameterClass), abs));
307
308 float expectedValue = multiply ? 123f * 456 : 123f / 456;
309
310 if (multiply)
311 {
312 Method multiplyMethod = ClassUtil.resolveMethod(scalarClass, "times", new Class[] {parameterClass});
313 Object result = multiplyMethod.invoke(left, right);
314 double resultSI = ((FloatScalarRel<?, ?>) result).getSI();
315 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
316 }
317 else
318 {
319 Method divideMethod = ClassUtil.resolveMethod(scalarClass, "divide", new Class[] {parameterClass});
320 Object result = divideMethod.invoke(left, right);
321 float resultSI = ((FloatScalarRel<?, ?>) result).getSI();
322 assertEquals(expectedValue, resultSI, 0.01, "Result of operation");
323 }
324 FloatScalarRel<?, ?> result = multiply ? FloatScalar.multiply(left, right) : FloatScalar.divide(left, right);
325
326 String resultCoefficients = result.getDisplayUnit().getQuantity().getSiDimensions().toString();
327 assertEquals(resultCoefficients, returnSI, "SI coefficients of result should match expected SI coefficients");
328 }
329 }
330 }
331
332
333
334
335
336
337
338
339 private String getCoefficients(final Class<?> clas) throws IllegalAccessException, NoSuchFieldException
340 {
341 Field si = clas.getField("SI");
342 Unit<?> u = ((Unit<?>) si.get(clas));
343 String r = u.getQuantity().getSiDimensions().toString();
344 return r;
345 }
346
347
348
349
350
351
352
353
354
355 private Unit<?> getSIUnitInstance(final Class<?> clas, final boolean isAbs)
356 throws NoSuchFieldException, IllegalAccessException
357 {
358 Field si = isAbs ? clas.getField("DEFAULT") : clas.getField("SI");
359 return ((Unit<?>) si.get(clas));
360 }
361
362
363
364
365
366
367 private Class<?> getUnitClass(final Class<?> scalarClass)
368 {
369 Constructor<?>[] constructors = scalarClass.getConstructors();
370 for (Constructor<?> constructor : constructors)
371 {
372 Class<?>[] parTypes = constructor.getParameterTypes();
373 if (parTypes.length == 2 && Unit.class.isAssignableFrom(parTypes[1]))
374 {
375 return parTypes[1];
376 }
377 }
378 fail("Could not find constructor with one unit for Scalar class " + scalarClass.getName());
379 return null;
380 }
381
382
383
384
385
386
387
388
389 private double verifyAbsRelPrecisionAndExtractSI(final boolean abs, final boolean doubleType, final Object o)
390 {
391 double result = Double.NaN;
392 if (doubleType)
393 {
394 if (!(o instanceof DoubleScalar))
395 {
396 fail("object is not a DoubleScalar");
397 }
398 result = ((DoubleScalar<?, ?>) o).getSI();
399 }
400 else
401 {
402 if (!(o instanceof FloatScalar))
403 {
404 fail("object is not a FloatScalar");
405 }
406 result = ((FloatScalar<?, ?>) o).getSI();
407 }
408 if (o instanceof Absolute)
409 {
410 if (!abs)
411 {
412 fail("Result should have been Absolute");
413 }
414 }
415 else if (o instanceof Relative)
416 {
417 if (abs)
418 {
419 fail("Result should have been Relative");
420 }
421 }
422 else
423 {
424 fail("Result is neither Absolute, nor Relative");
425 }
426 return result;
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440 @SuppressWarnings({"rawtypes", "unchecked"})
441 private void testUnaryMethods(final Class<?> scalarClass, final boolean abs, final boolean doubleType)
442 throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
443 NoSuchFieldException, ClassNotFoundException
444 {
445 double value = 1.23456;
446 Constructor<?> constructor =
447 scalarClass.getConstructor(doubleType ? double.class : float.class, getUnitClass(scalarClass));
448 Object left;
449 if (doubleType)
450 {
451 left = abs
452 ? (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(value,
453 getSIUnitInstance(getUnitClass(scalarClass), abs))
454 : (DoubleScalarRel<?, ?>) constructor.newInstance(value, getSIUnitInstance(getUnitClass(scalarClass), abs));
455
456 Constructor<?>[] constructors = scalarClass.getConstructors();
457 for (Constructor<?> c : constructors)
458 {
459 Class<?>[] parTypes = c.getParameterTypes();
460 if (parTypes.length == 1)
461 {
462 DoubleScalar<?, ?> newInstance = (DoubleScalar<?, ?>) c.newInstance(left);
463 assertEquals(value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, newInstance), 0.01,
464 "Result of constructor should be equal to original");
465 }
466 }
467 }
468 else
469 {
470 left = abs
471 ? (FloatScalarAbs<?, ?, ?, ?>) constructor.newInstance((float) value,
472 getSIUnitInstance(getUnitClass(scalarClass), abs))
473 : (FloatScalarRel<?, ?>) constructor.newInstance((float) value,
474 getSIUnitInstance(getUnitClass(scalarClass), abs));
475
476 Constructor<?>[] constructors = scalarClass.getConstructors();
477 for (Constructor<?> c : constructors)
478 {
479 Class<?>[] parTypes = c.getParameterTypes();
480 if (parTypes.length == 1)
481 {
482
483 FloatScalar<?, ?> newInstance = (FloatScalar<?, ?>) c.newInstance(left);
484 assertEquals(value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, newInstance), 0.01,
485 "Result of constructor should be equal to original");
486 }
487 }
488 }
489 Object result;
490
491 Method ceil = ClassUtil.resolveMethod(scalarClass, "ceil", new Class[] {});
492 result = ceil.invoke(left);
493 assertEquals(Math.ceil(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01, "Result of operation");
494
495 Method floor = ClassUtil.resolveMethod(scalarClass, "floor", new Class[] {});
496 result = floor.invoke(left);
497 assertEquals(Math.floor(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
498 "Result of operation");
499
500 Method rint = ClassUtil.resolveMethod(scalarClass, "rint", new Class[] {});
501 result = rint.invoke(left);
502 assertEquals(Math.rint(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01, "Result of operation");
503
504 if (!abs)
505 {
506 Method methodAbs = ClassUtil.resolveMethod(scalarClass, "abs", new Class[] {});
507 result = methodAbs.invoke(left);
508 assertEquals(Math.abs(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
509 "Result of operation");
510 }
511
512 if (scalarClass.getName().contains("Dimensionless"))
513 {
514 Method asin = ClassUtil.resolveMethod(scalarClass, "asin", new Class[] {});
515 result = asin.invoke(left);
516 assertEquals(Math.asin(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
517 "Result of operation");
518
519 Method acos = ClassUtil.resolveMethod(scalarClass, "acos", new Class[] {});
520 result = acos.invoke(left);
521 assertEquals(Math.acos(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
522 "Result of operation");
523
524 Method atan = ClassUtil.resolveMethod(scalarClass, "atan", new Class[] {});
525 result = atan.invoke(left);
526 assertEquals(Math.atan(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
527 "Result of operation");
528
529 Method cbrt = ClassUtil.resolveMethod(scalarClass, "cbrt", new Class[] {});
530 result = cbrt.invoke(left);
531 assertEquals(Math.cbrt(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
532 "Result of operation");
533
534 Method cos = ClassUtil.resolveMethod(scalarClass, "cos", new Class[] {});
535 result = cos.invoke(left);
536 assertEquals(Math.cos(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
537 "Result of operation");
538
539 Method cosh = ClassUtil.resolveMethod(scalarClass, "cosh", new Class[] {});
540 result = cosh.invoke(left);
541 assertEquals(Math.cosh(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
542 "Result of operation");
543
544 Method exp = ClassUtil.resolveMethod(scalarClass, "exp", new Class[] {});
545 result = exp.invoke(left);
546 assertEquals(Math.exp(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
547 "Result of operation");
548
549 Method expm1 = ClassUtil.resolveMethod(scalarClass, "expm1", new Class[] {});
550 result = expm1.invoke(left);
551 assertEquals(Math.expm1(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
552 "Result of operation");
553
554 Method log = ClassUtil.resolveMethod(scalarClass, "log", new Class[] {});
555 result = log.invoke(left);
556 assertEquals(Math.log(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
557 "Result of operation");
558
559 Method log10 = ClassUtil.resolveMethod(scalarClass, "log10", new Class[] {});
560 result = log10.invoke(left);
561 assertEquals(Math.log10(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
562 "Result of operation");
563
564 Method log1p = ClassUtil.resolveMethod(scalarClass, "log1p", new Class[] {});
565 result = log1p.invoke(left);
566 assertEquals(Math.log1p(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
567 "Result of operation");
568
569 Method signum = ClassUtil.resolveMethod(scalarClass, "signum", new Class[] {});
570 result = signum.invoke(left);
571 assertEquals(Math.signum(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
572 "Result of operation");
573
574 Method sin = ClassUtil.resolveMethod(scalarClass, "sin", new Class[] {});
575 result = sin.invoke(left);
576 assertEquals(Math.sin(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
577 "Result of operation");
578
579 Method sinh = ClassUtil.resolveMethod(scalarClass, "sinh", new Class[] {});
580 result = sinh.invoke(left);
581 assertEquals(Math.sinh(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
582 "Result of operation");
583
584 Method sqrt = ClassUtil.resolveMethod(scalarClass, "sqrt", new Class[] {});
585 result = sqrt.invoke(left);
586 assertEquals(Math.sqrt(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
587 "Result of operation");
588
589 Method tan = ClassUtil.resolveMethod(scalarClass, "tan", new Class[] {});
590 result = tan.invoke(left);
591 assertEquals(Math.tan(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
592 "Result of operation");
593
594 Method tanh = ClassUtil.resolveMethod(scalarClass, "tanh", new Class[] {});
595 result = tanh.invoke(left);
596 assertEquals(Math.tanh(value), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
597 "Result of operation");
598
599 Method inv = ClassUtil.resolveMethod(scalarClass, "inv", new Class[] {});
600 result = inv.invoke(left);
601 assertEquals(1 / value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01, "Result of operation");
602
603 Method pow = ClassUtil.resolveMethod(scalarClass, "pow", new Class[] {double.class});
604 result = pow.invoke(left, Math.PI);
605 assertEquals(Math.pow(value, Math.PI), verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
606 "Result of operation");
607 }
608
609 Object compatibleRight = null;
610
611 if (!scalarClass.getName().contains("Dimensionless") && !scalarClass.getName().contains("AbsoluteTemperature")
612 && !scalarClass.getName().contains("Position") && !scalarClass.getName().contains("Time")
613 && !scalarClass.getName().contains("Direction"))
614 {
615
616 Class<?> unitClass = getUnitClass(scalarClass);
617 UnitSystem unitSystem = UnitSystem.SI_DERIVED;
618
619
620 Constructor<?> unitConstructor = unitClass.getConstructor();
621 Unit newUnit = (Unit) unitConstructor.newInstance();
622 Method buildMethod = ClassUtil.resolveMethod(Unit.class, "build", Unit.Builder.class);
623 Unit.Builder<?> builder = new Unit.Builder<>();
624 builder.setId("7abbr");
625 builder.setName("7fullName");
626 builder.setUnitSystem(unitSystem);
627 builder.setScale(new LinearScale(7));
628 builder.setQuantity((Quantity) getSIUnitInstance(unitClass, false).getQuantity());
629 builder.setSiPrefixes(SIPrefixes.NONE, 1.0);
630 buildMethod.setAccessible(true);
631 buildMethod.invoke(newUnit, builder);
632
633
634 if (doubleType)
635 {
636 compatibleRight = abs ? (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(value, newUnit)
637 : (DoubleScalarRel<?, ?>) constructor.newInstance(value, newUnit);
638 }
639 else
640 {
641 compatibleRight = abs ? (FloatScalarAbs<?, ?, ?, ?>) constructor.newInstance((float) value, newUnit)
642 : (FloatScalarRel<?, ?>) constructor.newInstance((float) value, newUnit);
643 }
644
645 newUnit.getQuantity().unregister(newUnit);
646 }
647 if (!abs)
648 {
649 Method times = ClassUtil.resolveMethod(scalarClass, "times", new Class[] {double.class});
650 result = doubleType ? times.invoke(left, Math.PI) : times.invoke(left, (float) Math.PI);
651 assertEquals(Math.PI * value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
652 "Result of operation");
653 if (!doubleType)
654 {
655 times = ClassUtil.resolveMethod(scalarClass, "times", new Class[] {float.class});
656 result = doubleType ? times.invoke(left, Math.PI) : times.invoke(left, (float) Math.PI);
657 assertEquals(Math.PI * value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
658 "Result of operation");
659 }
660
661 Method divide = ClassUtil.resolveMethod(scalarClass, "divide", new Class[] {double.class});
662 result = doubleType ? divide.invoke(left, Math.PI) : divide.invoke(left, (float) Math.PI);
663 assertEquals(value / Math.PI, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
664 "Result of operation");
665 if (!doubleType)
666 {
667 divide = ClassUtil.resolveMethod(scalarClass, "divide", new Class[] {float.class});
668 result = doubleType ? divide.invoke(left, Math.PI) : divide.invoke(left, (float) Math.PI);
669 assertEquals(value / Math.PI, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
670 "Result of operation");
671 }
672
673 Method plus = ClassUtil.resolveMethod(scalarClass, "plus", new Class[] {scalarClass});
674 result = plus.invoke(left, left);
675 assertEquals(value + value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
676 "Result of operation");
677
678 if (null != compatibleRight)
679 {
680 result = plus.invoke(left, compatibleRight);
681 assertEquals(8 * value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
682 "Result of mixed operation");
683
684
685
686 plus = ClassUtil.resolveMethod(scalarClass, "plus", new Class[] {compatibleRight.getClass().getSuperclass()});
687 result = plus.invoke(compatibleRight, left);
688 assertEquals(8 * value, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
689 "Result of mixed operation");
690 if (scalarClass.getName().contains("$Rel"))
691 {
692
693 String absScalarClassName = scalarClass.getName().replace("$Rel", "$Abs");
694 Class<?> absScalarClass = Class.forName(absScalarClassName);
695 Constructor<?> absScalarConstructor = absScalarClass.getConstructor(doubleType ? double.class : float.class,
696 getUnitClass(absScalarClass));
697 Object absOperand = null;
698
699 if (doubleType)
700 {
701 absOperand =
702 absScalarConstructor.newInstance(value, getSIUnitInstance(getUnitClass(absScalarClass), true));
703 }
704 else
705 {
706 absOperand = absScalarConstructor.newInstance((float) value,
707 getSIUnitInstance(getUnitClass(absScalarClass), true));
708 }
709
710 plus = ClassUtil.resolveMethod(absScalarClass, "plus", scalarClass);
711 result = plus.invoke(absOperand, left);
712 assertEquals(2 * value, verifyAbsRelPrecisionAndExtractSI(true, doubleType, result), 0.01,
713 "Result of mixed abs + rel");
714 Method toAbs = compatibleRight.getClass().getMethod("toAbs");
715 Object absCompatible = toAbs.invoke(compatibleRight);
716 result = plus.invoke(absCompatible, left);
717 assertEquals(8 * value, verifyAbsRelPrecisionAndExtractSI(true, doubleType, result), 0.01,
718 "Result of mixed compatible abs + rel");
719
720 plus = ClassUtil.resolveMethod(scalarClass, "plus", absScalarClass);
721 result = plus.invoke(left, absOperand);
722 assertEquals(2 * value, verifyAbsRelPrecisionAndExtractSI(true, doubleType, result), 0.01,
723 "Result of mixed rel + abs");
724 result = plus.invoke(left, absCompatible);
725 assertEquals(8 * value, verifyAbsRelPrecisionAndExtractSI(true, doubleType, result), 0.01,
726 "Result of mixed rel + compatible abs");
727 }
728 }
729 }
730
731 Method minus = ClassUtil.resolveMethod(scalarClass, "minus", new Class[] {scalarClass});
732 result = minus.invoke(left, left);
733 assertEquals(0, verifyAbsRelPrecisionAndExtractSI(false, doubleType, result), 0.01, "Result of minus");
734 if (null != compatibleRight)
735 {
736 result = minus.invoke(left, compatibleRight);
737 assertEquals(-6 * value, verifyAbsRelPrecisionAndExtractSI(false, doubleType, result), 0.01,
738 "Result of minus with compatible arg for " + left + " and " + compatibleRight);
739 }
740 if (scalarClass.getName().contains("$Rel") || scalarClass.getName().contains("$Abs"))
741 {
742
743 String absScalarClassName = scalarClass.getName().replace("$Rel", "$Abs");
744 Class<?> absScalarClass = Class.forName(absScalarClassName);
745 Constructor<?> absScalarConstructor =
746 absScalarClass.getConstructor(doubleType ? double.class : float.class, getUnitClass(absScalarClass));
747 Object absOperand = null;
748
749 if (doubleType)
750 {
751 absOperand = absScalarConstructor.newInstance(value, getSIUnitInstance(getUnitClass(absScalarClass), true));
752 }
753 else
754 {
755 absOperand =
756 absScalarConstructor.newInstance((float) value, getSIUnitInstance(getUnitClass(absScalarClass), true));
757 }
758 minus = ClassUtil.resolveMethod(absScalarClass, "minus", scalarClass);
759 result = minus.invoke(absOperand, left);
760 assertEquals(0, verifyAbsRelPrecisionAndExtractSI(!abs, doubleType, result), 0.01,
761 "Result of abs or rel minus rel");
762 if (null != compatibleRight && scalarClass.getName().contains("$Rel"))
763 {
764 Method toAbs = compatibleRight.getClass().getMethod("toAbs");
765 Object absCompatible = toAbs.invoke(compatibleRight);
766 result = minus.invoke(absCompatible, left);
767 assertEquals(6 * value, verifyAbsRelPrecisionAndExtractSI(!abs, doubleType, result), 0.01,
768 "Result of compatible abs or rel minus rel");
769 }
770
771 }
772 }
773
774
775
776
777
778
779
780
781
782
783
784
785 private void testStaticMethods(final Class<?> scalarClass, final boolean abs, final boolean doubleType)
786 throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
787 NoSuchFieldException
788 {
789 Constructor<?> constructor = scalarClass.getConstructor(double.class, getUnitClass(scalarClass));
790 if (doubleType)
791 {
792 double zeroValue = 1.23456;
793 DoubleScalar<?,
794 ?> zero = abs
795 ? (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(zeroValue,
796 getSIUnitInstance(getUnitClass(scalarClass), abs))
797 : (DoubleScalarRel<?, ?>) constructor.newInstance(zeroValue,
798 getSIUnitInstance(getUnitClass(scalarClass), abs));
799 double oneValue = 3.45678;
800 DoubleScalar<?,
801 ?> one = abs
802 ? (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(oneValue,
803 getSIUnitInstance(getUnitClass(scalarClass), abs))
804 : (DoubleScalarRel<?, ?>) constructor.newInstance(oneValue,
805 getSIUnitInstance(getUnitClass(scalarClass), abs));
806 for (double ratio : new double[] {-5, -1, 0, 0.3, 1, 2, 10})
807 {
808 double expectedResult = (1.0 - ratio) * zeroValue + ratio * oneValue;
809 Method interpolate =
810 ClassUtil.resolveMethod(scalarClass, "interpolate", scalarClass, scalarClass, double.class);
811 DoubleScalar<?, ?> result;
812 result = (DoubleScalar<?, ?>) interpolate.invoke(null, zero, one, ratio);
813 assertEquals(expectedResult, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
814 "Result of operation");
815 }
816 double biggestValue = 345.678;
817 DoubleScalar<?,
818 ?> biggest = abs
819 ? (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(biggestValue,
820 getSIUnitInstance(getUnitClass(scalarClass), abs))
821 : (DoubleScalarRel<?, ?>) constructor.newInstance(biggestValue,
822 getSIUnitInstance(getUnitClass(scalarClass), abs));
823 Method max = ClassUtil.resolveMethod(scalarClass, "max", scalarClass, scalarClass);
824 DoubleScalar<?, ?> result = (DoubleScalar<?, ?>) max.invoke(null, zero, one);
825 assertEquals(one, result, "max returns object with maximum value");
826 result = (DoubleScalar<?, ?>) max.invoke(null, one, zero);
827 assertEquals(one, result, "max returns object with maximum value");
828
829 Class<?> emptyClassArrayClass = java.lang.reflect.Array.newInstance(scalarClass, 0).getClass();
830 max = ClassUtil.resolveMethod(scalarClass, "max", scalarClass, scalarClass, emptyClassArrayClass);
831 DoubleScalar<?, ?>[] additionalArguments =
832 (DoubleScalar<?, ?>[]) java.lang.reflect.Array.newInstance(scalarClass, 1);
833 additionalArguments[0] = biggest;
834 result = (DoubleScalar<?, ?>) max.invoke(null, zero, one, additionalArguments);
835 assertEquals(biggest, result, "max return object with maximum value");
836 result = (DoubleScalar<?, ?>) max.invoke(null, one, zero, additionalArguments);
837 assertEquals(biggest, result, "max return object with maximum value");
838 additionalArguments[0] = zero;
839 result = (DoubleScalar<?, ?>) max.invoke(null, biggest, zero, additionalArguments);
840 assertEquals(biggest, result, "max return object with maximum value");
841
842 Method min = ClassUtil.resolveMethod(scalarClass, "min", scalarClass, scalarClass);
843 result = (DoubleScalar<?, ?>) min.invoke(null, zero, one);
844 assertEquals(zero, result, "min returns object with maximum value");
845 result = (DoubleScalar<?, ?>) min.invoke(null, one, zero);
846 assertEquals(zero, result, "min returns object with maximum value");
847 min = ClassUtil.resolveMethod(scalarClass, "min", scalarClass, scalarClass, emptyClassArrayClass);
848 result = (DoubleScalar<?, ?>) min.invoke(null, one, biggest, additionalArguments);
849 assertEquals(zero, result, "min return object with minimum value");
850 result = (DoubleScalar<?, ?>) min.invoke(null, biggest, one, additionalArguments);
851 assertEquals(zero, result, "min return object with minimum value");
852 additionalArguments[0] = biggest;
853 result = (DoubleScalar<?, ?>) min.invoke(null, zero, one, additionalArguments);
854 assertEquals(zero, result, "min return object with minimum value");
855
856 Method valueOf = ClassUtil.resolveMethod(scalarClass, "valueOf", String.class);
857 String string = zero.toString();
858 result = (DoubleScalar<?, ?>) valueOf.invoke(null, string);
859 assertEquals(zeroValue, result.getSI(), 0.001, "valueOf toString returns a decent approximation of the input");
860 try
861 {
862 valueOf.invoke(null, (String) null);
863 fail("Null string in valueOf should have thrown an IllegalArgumentException (which may have been converted "
864 + "into an InvocationTargetException)");
865 }
866 catch (IllegalArgumentException | InvocationTargetException iae)
867 {
868
869 }
870
871 try
872 {
873 valueOf.invoke(null, "");
874 fail("Empty string in valueOf should have thrown an IllegalArgumentException");
875 }
876 catch (IllegalArgumentException | InvocationTargetException iae)
877 {
878
879 }
880
881 try
882 {
883 valueOf.invoke(null, "NONSENSEVALUE");
884 fail("Nonsense string in valueOf should have thrown an IllegalArgumentException");
885 }
886 catch (IllegalArgumentException | InvocationTargetException iae)
887 {
888
889 }
890
891 try
892 {
893 valueOf.invoke(null, "1.0 xyzuwv");
894 fail("Nonsense unit string in valueOf argument should have thrown an IllegalArgumentException");
895 }
896 catch (IllegalArgumentException | InvocationTargetException iae)
897 {
898
899 }
900
901 Method instantiateSI = ClassUtil.resolveMethod(scalarClass, "instantiateSI", double.class);
902 result = (DoubleScalar<?, ?>) instantiateSI.invoke(null, zeroValue);
903 assertEquals(zeroValue, result.getSI(), 0.0001, "SI value was correctly set");
904 }
905 else
906 {
907 float zeroValue = 1.23456f;
908 FloatScalar<?,
909 ?> zero = abs
910 ? (FloatScalarAbs<?, ?, ?, ?>) constructor.newInstance(zeroValue,
911 getSIUnitInstance(getUnitClass(scalarClass), abs))
912 : (FloatScalarRel<?, ?>) constructor.newInstance(zeroValue,
913 getSIUnitInstance(getUnitClass(scalarClass), abs));
914 float oneValue = 3.45678f;
915 FloatScalar<?,
916 ?> one = abs
917 ? (FloatScalarAbs<?, ?, ?, ?>) constructor.newInstance(oneValue,
918 getSIUnitInstance(getUnitClass(scalarClass), abs))
919 : (FloatScalarRel<?, ?>) constructor.newInstance(oneValue,
920 getSIUnitInstance(getUnitClass(scalarClass), abs));
921 for (float ratio : new float[] {-5, -1, 0, 0.3f, 1, 2, 10})
922 {
923 float expectedResult = (1.0f - ratio) * zeroValue + ratio * oneValue;
924 Method interpolate = ClassUtil.resolveMethod(scalarClass, "interpolate", scalarClass, scalarClass, float.class);
925 FloatScalar<?, ?> result;
926 result = (FloatScalar<?, ?>) interpolate.invoke(null, zero, one, ratio);
927 assertEquals(expectedResult, verifyAbsRelPrecisionAndExtractSI(abs, doubleType, result), 0.01,
928 "Result of operation");
929 }
930 float biggestValue = 345.678f;
931 FloatScalar<?,
932 ?> biggest = abs
933 ? (FloatScalarAbs<?, ?, ?, ?>) constructor.newInstance(biggestValue,
934 getSIUnitInstance(getUnitClass(scalarClass), abs))
935 : (FloatScalarRel<?, ?>) constructor.newInstance(biggestValue,
936 getSIUnitInstance(getUnitClass(scalarClass), abs));
937 Method max = ClassUtil.resolveMethod(scalarClass, "max", scalarClass, scalarClass);
938 FloatScalar<?, ?> result = (FloatScalar<?, ?>) max.invoke(null, zero, one);
939 assertEquals(one, result, "max return object with maximum value");
940 result = (FloatScalar<?, ?>) max.invoke(null, one, zero);
941 assertEquals(one, result, "max return object with maximum value");
942
943 Class<?> emptyClassArrayClass = java.lang.reflect.Array.newInstance(scalarClass, 0).getClass();
944 max = ClassUtil.resolveMethod(scalarClass, "max", scalarClass, scalarClass, emptyClassArrayClass);
945 FloatScalar<?, ?>[] additionalArguments = (FloatScalar<?, ?>[]) java.lang.reflect.Array.newInstance(scalarClass, 1);
946 additionalArguments[0] = biggest;
947 result = (FloatScalar<?, ?>) max.invoke(null, zero, one, additionalArguments);
948 assertEquals(biggest, result, "max return object with maximum value");
949 result = (FloatScalar<?, ?>) max.invoke(null, one, zero, additionalArguments);
950 assertEquals(biggest, result, "max return object with maximum value");
951 additionalArguments[0] = zero;
952 result = (FloatScalar<?, ?>) max.invoke(null, biggest, zero, additionalArguments);
953 assertEquals(biggest, result, "max return object with maximum value");
954
955 Method min = ClassUtil.resolveMethod(scalarClass, "min", scalarClass, scalarClass);
956 result = (FloatScalar<?, ?>) min.invoke(null, zero, one);
957 assertEquals(zero, result, "min returns object with maximum value");
958 result = (FloatScalar<?, ?>) min.invoke(null, one, zero);
959 assertEquals(zero, result, "min returns object with maximum value");
960 min = ClassUtil.resolveMethod(scalarClass, "min", scalarClass, scalarClass, emptyClassArrayClass);
961 result = (FloatScalar<?, ?>) min.invoke(null, one, biggest, additionalArguments);
962 assertEquals(zero, result, "min return object with minimum value");
963 result = (FloatScalar<?, ?>) min.invoke(null, biggest, one, additionalArguments);
964 assertEquals(zero, result, "min return object with minimum value");
965 additionalArguments[0] = biggest;
966 result = (FloatScalar<?, ?>) min.invoke(null, zero, one, additionalArguments);
967 assertEquals(zero, result, "min return object with minimum value");
968
969 Method valueOf = ClassUtil.resolveMethod(scalarClass, "valueOf", String.class);
970 String string = zero.toString();
971 result = (FloatScalar<?, ?>) valueOf.invoke(null, string);
972 assertEquals(zeroValue, result.getSI(), 0.001, "valueOf toString returns a decent approximation of the input");
973 try
974 {
975 valueOf.invoke(null, (String) null);
976 fail("Null string in valueOf should have thrown an IllegalArgumentException (which may have been converted "
977 + "into an InvocationTargetException)");
978 }
979 catch (IllegalArgumentException | InvocationTargetException iae)
980 {
981
982 }
983
984 try
985 {
986 valueOf.invoke(null, "");
987 fail("Empty string in valueOf should have thrown an IllegalArgumentException");
988 }
989 catch (IllegalArgumentException | InvocationTargetException iae)
990 {
991
992 }
993
994 try
995 {
996 valueOf.invoke(null, "NONSENSEVALUE");
997 fail("Nonsense string in valueOf should have thrown an IllegalArgumentException");
998 }
999 catch (IllegalArgumentException | InvocationTargetException iae)
1000 {
1001
1002 }
1003
1004 try
1005 {
1006 valueOf.invoke(null, "1.0 xyzuwv");
1007 fail("Nonsense string in valueOf should have thrown an IllegalArgumentException");
1008 }
1009 catch (IllegalArgumentException | InvocationTargetException iae)
1010 {
1011
1012 }
1013
1014 Method instantiateSI = ClassUtil.resolveMethod(scalarClass, "instantiateSI", float.class);
1015 result = (FloatScalar<?, ?>) instantiateSI.invoke(null, zeroValue);
1016 assertEquals(zeroValue, result.getSI(), 0.0001, "SI value was correctly set");
1017 }
1018 }
1019
1020 }