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