View Javadoc
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   * Find all plus, minus, times and divide operations and prove the type correctness.
28   * <p>
29   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
30   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
31   * </p>
32   * version Sep 14, 2015 <br>
33   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
34   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
35   */
36  public class ScalarOperationsTest
37  {
38      /**
39       * Test constructor on the specified double scalar classes.
40       * @throws IllegalAccessException on class or method resolving error
41       * @throws InstantiationException on class or method resolving error
42       * @throws NoSuchMethodException on class or method resolving error
43       * @throws InvocationTargetException on class or method resolving error
44       * @throws NoSuchFieldException on class or method resolving error
45       * @throws ClassNotFoundException on reflection error
46       * @throws IllegalArgumentException on reflection error
47       * @throws SecurityException on reflection error
48       */
49      @Test
50      public final void scalarOperationsTest() throws NoSuchMethodException, InstantiationException, IllegalAccessException,
51              InvocationTargetException, NoSuchFieldException, SecurityException, IllegalArgumentException, ClassNotFoundException
52      {
53          doubleOrFloatScalarOperationsTest(true); // Double precision versions
54          doubleOrFloatScalarOperationsTest(false); // Float versions
55      }
56  
57      /**
58       * Perform many tests on scalar types.
59       * @param doubleType boolean; if true; perform tests on DoubleScalar types; if false; perform tests on FloatScalar types
60       * @throws NoSuchFieldException on class or method resolving error
61       * @throws InvocationTargetException on class or method resolving error
62       * @throws IllegalAccessException on class or method resolving error
63       * @throws InstantiationException on class or method resolving error
64       * @throws NoSuchMethodException on class or method resolving error
65       * @throws ClassNotFoundException on reflection error
66       * @throws IllegalArgumentException on reflection error
67       * @throws SecurityException on reflection error
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          // get the interfaces such as org.djunits.value.vdouble.scalar.Time
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              // get the subClassName implementation of that class
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         // get the interfaces such as org.djunits.value.vXXXX.scalar.Area
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      * Find the methods defined in the class itself (not in a superclass) called times or divide and test the method. Also test
127      * the Unary methods of the class.
128      * @param scalarClassAbsRel class to test
129      * @param isAbs boolean; if true; the scalarClassAbsRel must be aAsolute; if false; the scalarClassAbsRel must be Relative
130      * @param doubleType boolean; if true; perform tests on DoubleScalar; if false perform tests on FloatScalar
131      * @throws InvocationTargetException on class or method resolving error
132      * @throws IllegalAccessException on class or method resolving error
133      * @throws InstantiationException on class or method resolving error
134      * @throws NoSuchMethodException on class or method resolving error
135      * @throws NoSuchFieldException on class or method resolving error
136      * @throws ClassNotFoundException on reflection error
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                 // note: filter out the method that multiplies by a constant or a general scalar...
147                 testMultiplyOrDivideMethodAbsRel(scalarClassAbsRel, isAbs, method, true, doubleType);
148             }
149             else if (method.getName().equals("divide"))
150             {
151                 // note: filter out the method that divides by a constant or a general scalar...
152                 testMultiplyOrDivideMethodAbsRel(scalarClassAbsRel, isAbs, method, false, doubleType);
153             }
154         }
155         testUnaryMethods(scalarClassAbsRel, isAbs, doubleType);
156         testStaticMethods(scalarClassAbsRel, isAbs, doubleType);
157     }
158 
159     /**
160      * Test a multiplication method for an Abs or Rel scalar. Note: filter out the method that multiplies by a constant...
161      * @param scalarClass the Abs or Rel class for the multiplication, e.g. Length
162      * @param abs boolean; true to test the Abs sub-class; false to test the Rel sub-class
163      * @param method the method 'times' for that class
164      * @param multiply boolean; if true; test a times method; if false; test a divide method
165      * @param doubleType boolean; if true; perform tests on DoubleScalar; if false; perform tests on FloatScalar
166      * @throws NoSuchMethodException on class or method resolving error
167      * @throws InvocationTargetException on class or method resolving error
168      * @throws IllegalAccessException on class or method resolving error
169      * @throws InstantiationException on class or method resolving error
170      * @throws NoSuchFieldException on class or method resolving error
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             // not interested in static methods
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             // not interested in multiplying a scalar with a double.
200             return;
201         }
202         if (parameterClass.getSimpleName().startsWith("Abstract"))
203         {
204             // not interested in multiplying a scalar with a generic scalar.
205             return;
206         }
207         if (parameterClass.getSimpleName().endsWith("ScalarRel"))
208         {
209             // not interested in DoubleScalarRel paremeter
210             return;
211         }
212         if (!relativeOrAbsoluteClass.isAssignableFrom(parameterClass))
213         {
214             // system.out.println("abs=" + abs + ", method=" + scalarClass.getName() + "." + method.getName() + " param="
215             // + parameterClass.getName());
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         // get the SI coefficients of the unit classes, scalar type, parameter type and return type
228         String returnSI = getCoefficients(getUnitClass(returnClass));
229         // String scalarSI = getCoefficients(getUnitClass(scalarClass));
230         // String paramSI = getCoefficients(getUnitClass(parameterClass));
231         // print what we just have found
232         // system.out.println(scalarClass.getName().replaceFirst("org.djunits.value.vdouble.scalar.", "") + "."
233         // + (multiply ? "times" : "divide") + "("
234         // + parameterClass.getName().replaceFirst("org.djunits.value.vdouble.scalar.", "") + ") => "
235         // + returnClass.getName().replaceFirst("org.djunits.value.vdouble.scalar.", "") + ": " + scalarSI
236         // + (multiply ? " * " : " : ") + paramSI + " => " + returnSI);
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             // System.out.println("constructed left: " + left);
245             constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
246             DoubleScalarAbs<?, ?, ?, ?> right = (DoubleScalarAbs<?, ?, ?, ?>) constructor.newInstance(456d,
247                     getSIUnitInstance(getUnitClass(parameterClass), abs));
248             // System.out.println("constructed right: " + right);
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                 // System.out.println("constructed left: " + left);
273                 constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
274                 DoubleScalarRel<?, ?> right = (DoubleScalarRel<?, ?>) constructor.newInstance(456d,
275                         getSIUnitInstance(getUnitClass(parameterClass), abs));
276                 // System.out.println("constructed right: " + right);
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                 // System.out.println("result is " + result);
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                 // System.out.println("constructed left: " + left);
304                 constructor = parameterClass.getConstructor(double.class, getUnitClass(parameterClass));
305                 FloatScalarRel<?, ?> right = (FloatScalarRel<?, ?>) constructor.newInstance(456f,
306                         getSIUnitInstance(getUnitClass(parameterClass), abs));
307                 // System.out.println("constructed right: " + right);
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                 // System.out.println("result is " + result);
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      * Obtain the SI coefficient string of a DJUNITS class.
334      * @param clas Class&lt;?&gt;; the DJUNITS class
335      * @return String
336      * @throws IllegalAccessException on class or method resolving error
337      * @throws NoSuchFieldException on class or method resolving error
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      * Obtain the SI coefficient string of a DJUNITS class.
349      * @param clas Class&lt;?&gt;; the DJUNITS class
350      * @param isAbs true when abs
351      * @return String
352      * @throws NoSuchFieldException on class or method resolving error
353      * @throws IllegalAccessException on class or method resolving error
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      * Get the unit of a Scalar class by looking at the constructor with two arguments -- the second argument is the unit type.
364      * @param scalarClass the class to find the unit for
365      * @return the unit class for this scalar class
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      * Verify the Absolute-ness or Relative-ness of a DoubleScalar and return the SI value.
384      * @param abs boolean; expected Absolute- or Relative-ness
385      * @param doubleType boolean; if true; double is expected; if false; float is expected
386      * @param o the (DoubleScalar?) object
387      * @return double; the SI value
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      * @param scalarClass the class to test
431      * @param abs boolean; if true; test Absolute class; if false; test the Relative class
432      * @param doubleType boolean; if true; perform tests on DoubleScalar; if false; perform tests on FloatScalar
433      * @throws NoSuchMethodException on class or method resolving error
434      * @throws InstantiationException on class or method resolving error
435      * @throws IllegalAccessException on class or method resolving error
436      * @throws InvocationTargetException on class or method resolving error
437      * @throws NoSuchFieldException on class or method resolving error
438      * @throws ClassNotFoundException on class or method resolving error
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             // Find the constructor that takes an object of the current class as the single argument
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             // Find the constructor that takes an object of the current class as the single argument
476             Constructor<?>[] constructors = scalarClass.getConstructors();
477             for (Constructor<?> c : constructors)
478             {
479                 Class<?>[] parTypes = c.getParameterTypes();
480                 if (parTypes.length == 1)
481                 {
482                     // System.out.println("parType is " + parTypes[0]);
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         // TODO: Probably we exclude too much here for the tests...
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             // Construct a new unit to test mixed unit plus and minus
616             Class<?> unitClass = getUnitClass(scalarClass);
617             UnitSystem unitSystem = UnitSystem.SI_DERIVED;
618             // Call the getUnit method of left
619             // Method getUnitMethod = ClassUtil.resolveMethod(scalarClass, "getDisplayUnit");
620             Constructor<?> unitConstructor = unitClass.getConstructor(); // empty constructor -- provide Builder
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             // System.out.println("new unit prints like " + newUnit);
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             // System.out.println("compatibleRight prints like \"" + compatibleRight + "\"");
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                 // Swap the operands
684                 // System.out.println("finding plus method for " + compatibleRight.getClass().getName() + " left type is "
685                 // + left.getClass().getName());
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                     // Make an Absolute for one operand
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                     // System.out.println("unit is " + getUnitClass(absScalarClass));
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                     // abs plus rel yields abs
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                     // rel plus abs yields abs
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             // Make an Absolute for one operand
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             // System.out.println("unit is " + getUnitClass(absScalarClass));
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      * Test the various static methods.
776      * @param scalarClass Class&lt;?&gt;; the class to test
777      * @param abs boolean; if true; scalarClass is Absolute; if false; scalarClass is Relative
778      * @param doubleType boolean; if true; perform tests on DoubleScalar; if false; perform tests on FloatScalar
779      * @throws NoSuchMethodException on class or method resolving error
780      * @throws NoSuchFieldException on class or method resolving error
781      * @throws InvocationTargetException on class or method resolving error
782      * @throws IllegalAccessException on class or method resolving error
783      * @throws InstantiationException on class or method resolving error
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             // https://stackoverflow.com/questions/1679421/how-to-get-the-array-class-for-a-given-class-in-java
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                 // Ignore expected exception
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                 // Ignore expected exception
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                 // Ignore expected exception
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                 // Ignore expected exception
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             // https://stackoverflow.com/questions/1679421/how-to-get-the-array-class-for-a-given-class-in-java
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                 // Ignore expected exception
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                 // Ignore expected exception
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                 // Ignore expected exception
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                 // Ignore expected exception
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 }