View Javadoc
1   package org.djunits.value.vfloat.scalar.base;
2   
3   import java.lang.reflect.Constructor;
4   import java.lang.reflect.InvocationTargetException;
5   import java.util.HashMap;
6   import java.util.Map;
7   
8   import org.djunits.unit.AbsoluteLinearUnit;
9   import org.djunits.unit.SIUnit;
10  import org.djunits.unit.Unit;
11  import org.djunits.unit.si.SIPrefixes;
12  import org.djunits.unit.util.UnitRuntimeException;
13  import org.djunits.value.Absolute;
14  import org.djunits.value.base.Scalar;
15  import org.djunits.value.formatter.Format;
16  import org.djunits.value.util.ValueUtil;
17  import org.djunits.value.vfloat.scalar.FloatSIScalar;
18  
19  /**
20   * Static methods to create and operate on FloatScalars.
21   * <p>
22   * Copyright (c) 2015-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
24   * </p>
25   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
26   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
27   * @param <U> the unit
28   * @param <S> the type
29   */
30  public abstract class FloatScalar<U extends Unit<U>, S extends FloatScalar<U, S>> extends Scalar<U, S>
31  {
32      /** */
33      private static final long serialVersionUID = 20161015L;
34  
35      /** The value, stored in the standard SI unit. */
36      @SuppressWarnings("checkstyle:visibilitymodifier")
37      public final float si;
38  
39      /**
40       * Construct a new FloatScalar.
41       * @param unit U; the unit
42       * @param si float; the si value to store
43       */
44      public FloatScalar(final U unit, final float si)
45      {
46          super(unit);
47          this.si = si;
48      }
49  
50      /**
51       * Return the value in the underlying SI unit.
52       * @return float; the value in the underlying SI unit
53       */
54      public final float getSI()
55      {
56          return this.si;
57      }
58  
59      /**
60       * Retrieve the value in the original unit.
61       * @return float
62       */
63      public final float getInUnit()
64      {
65          return (float) ValueUtil.expressAsUnit(getSI(), getDisplayUnit());
66      }
67  
68      /**
69       * Retrieve the value converted into some specified unit.
70       * @param targetUnit U; the unit to convert the value into
71       * @return float
72       */
73      public final float getInUnit(final U targetUnit)
74      {
75          return (float) ValueUtil.expressAsUnit(getSI(), targetUnit);
76      }
77  
78      @Override
79      public final boolean lt(final S o)
80      {
81          return this.getSI() < o.getSI();
82      }
83  
84      @Override
85      public final boolean le(final S o)
86      {
87          return this.getSI() <= o.getSI();
88      }
89  
90      @Override
91      public final boolean gt(final S o)
92      {
93          return this.getSI() > o.getSI();
94      }
95  
96      @Override
97      public final boolean ge(final S o)
98      {
99          return this.getSI() >= o.getSI();
100     }
101 
102     @Override
103     public final boolean eq(final S o)
104     {
105         return this.getSI() == o.getSI();
106     }
107 
108     @Override
109     public final boolean ne(final S o)
110     {
111         return this.getSI() != o.getSI();
112     }
113 
114     @Override
115     public final boolean lt0()
116     {
117         return this.getSI() < 0.0;
118     }
119 
120     @Override
121     public final boolean le0()
122     {
123         return this.getSI() <= 0.0;
124     }
125 
126     @Override
127     public final boolean gt0()
128     {
129         return this.getSI() > 0.0;
130     }
131 
132     @Override
133     public final boolean ge0()
134     {
135         return this.getSI() >= 0.0;
136     }
137 
138     @Override
139     public final boolean eq0()
140     {
141         return this.getSI() == 0.0;
142     }
143 
144     @Override
145     public final boolean ne0()
146     {
147         return this.getSI() != 0.0;
148     }
149 
150     @Override
151     public final int compareTo(final S o)
152     {
153         return Float.compare(this.getSI(), o.getSI());
154     }
155 
156     @Override
157     public int intValue()
158     {
159         return (int) this.getSI();
160     }
161 
162     @Override
163     public long longValue()
164     {
165         return (long) this.getSI();
166     }
167 
168     @Override
169     public float floatValue()
170     {
171         return this.getSI();
172     }
173 
174     @Override
175     public double doubleValue()
176     {
177         return this.getSI();
178     }
179 
180     /**********************************************************************************/
181     /********************************* GENERIC METHODS ********************************/
182     /**********************************************************************************/
183 
184     @Override
185     public String toString()
186     {
187         return toString(getDisplayUnit(), false, true);
188     }
189 
190     @Override
191     public String toString(final U displayUnit)
192     {
193         return toString(displayUnit, false, true);
194     }
195 
196     @Override
197     public String toString(final boolean verbose, final boolean withUnit)
198     {
199         return toString(getDisplayUnit(), verbose, withUnit);
200     }
201 
202     @Override
203     public String toString(final U displayUnit, final boolean verbose, final boolean withUnit)
204     {
205         StringBuffer buf = new StringBuffer();
206         if (verbose)
207         {
208             buf.append(this instanceof Absolute ? "Abs " : "Rel ");
209         }
210         float d = (float) ValueUtil.expressAsUnit(getSI(), displayUnit);
211         buf.append(Format.format(d));
212         if (withUnit)
213         {
214             buf.append(" "); // Insert one space as prescribed by SI writing conventions
215             buf.append(displayUnit.getLocalizedDisplayAbbreviation());
216         }
217         return buf.toString();
218     }
219 
220     /**
221      * Format this DoubleScalar in SI unit using prefixes when possible. If the value is too small or too large,
222      * e-notation and the plain SI unit are used.
223      * @return String; formatted value of this DoubleScalar
224      */
225     public String toStringSIPrefixed()
226     {
227         return toStringSIPrefixed(-24, 26);
228     }
229 
230     /**
231      * Format this DoubleScalar in SI unit using prefixes when possible and within the specified size range. If the
232      * value is too small or too large, e-notation and the plain SI unit are used.
233      * @param smallestPower int; the smallest exponent value that will be written using an SI prefix
234      * @param biggestPower int; the largest exponent value that will be written using an SI prefix
235      * @return String; formatted value of this DoubleScalar
236      */
237     public String toStringSIPrefixed(final int smallestPower, final int biggestPower)
238     {
239         // Override this method for weights, nonlinear units and DimensionLess.
240         if (!Double.isFinite(this.si))
241         {
242             return toString(getDisplayUnit().getStandardUnit());
243         }
244         // PK: I can't think of an easier way to figure out what the exponent will be; rounding of the mantissa to the available
245         // width makes this hard; This feels like an expensive way.
246         String check = String.format(this.si >= 0 ? "%10.8E" : "%10.7E", this.si);
247         int exponent = Integer.parseInt(check.substring(check.indexOf("E") + 1));
248         if (exponent < -24 || exponent < smallestPower || exponent > 24 + 2 || exponent > biggestPower)
249         {
250             // Out of SI prefix range; do not scale.
251             return String.format(this.si >= 0 ? "%10.4E" : "%10.3E", this.si) + " "
252                     + getDisplayUnit().getStandardUnit().getId();
253         }
254         Integer roundedExponent = (int) Math.ceil((exponent - 2.0) / 3) * 3;
255         // System.out.print(String.format("exponent=%d; roundedExponent=%d ", exponent, roundedExponent));
256         String key =
257                 SIPrefixes.FACTORS.get(roundedExponent).getDefaultTextualPrefix() + getDisplayUnit().getStandardUnit().getId();
258         U displayUnit = getDisplayUnit().getQuantity().getUnitByAbbreviation(key);
259         return toString(displayUnit);
260     }
261 
262     @Override
263     public String toTextualString()
264     {
265         return toTextualString(getDisplayUnit());
266     }
267 
268     @Override
269     public String toTextualString(final U displayUnit)
270     {
271         float f = (float) ValueUtil.expressAsUnit(getSI(), displayUnit);
272         return format(f) + " " + displayUnit.getLocalizedTextualAbbreviation();
273     }
274 
275     @Override
276     public String toDisplayString()
277     {
278         return toDisplayString(getDisplayUnit());
279     }
280 
281     @Override
282     public String toDisplayString(final U displayUnit)
283     {
284         float f = (float) ValueUtil.expressAsUnit(getSI(), displayUnit);
285         return format(f) + " " + displayUnit.getLocalizedDisplayAbbreviation();
286     }
287 
288     @Override
289     @SuppressWarnings("checkstyle:designforextension")
290     public int hashCode()
291     {
292         final int prime = 31;
293         int result = getDisplayUnit().getStandardUnit().hashCode();
294         long temp;
295         temp = Float.floatToIntBits(this.getSI());
296         result = prime * result + (int) (temp ^ (temp >>> 32));
297         return result;
298     }
299 
300     @Override
301     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces", "unchecked"})
302     public boolean equals(final Object obj)
303     {
304         if (this == obj)
305             return true;
306         if (obj == null)
307             return false;
308         if (getClass() != obj.getClass())
309             return false;
310         FloatScalar<U, S> other = (FloatScalar<U, S>) obj;
311         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
312             return false;
313         if (Float.floatToIntBits(this.getSI()) != Float.floatToIntBits(other.getSI()))
314             return false;
315         return true;
316     }
317 
318     /**********************************************************************************/
319     /********************************* STATIC METHODS *********************************/
320     /**********************************************************************************/
321 
322     /** The cache to make the lookup of the constructor for a Scalar belonging to a unit faster. */
323     private static final Map<Unit<?>, Constructor<? extends FloatScalar<?, ?>>> CACHE = new HashMap<>();
324 
325     /**
326      * Instantiate the FloatScalar based on its unit. Rigid check on types by the compiler.
327      * @param value float; the value
328      * @param unit U; the unit in which the value is expressed
329      * @return S; an instantiated FloatScalar with the value expressed in the unit
330      * @param <U> the unit
331      * @param <S> the return type
332      */
333     public static <U extends Unit<U>, S extends FloatScalar<U, S>> S instantiate(final float value, final U unit)
334     {
335         return instantiateAnonymous(value, unit);
336     }
337 
338     /**
339      * Instantiate the FloatScalar with an SI value and add the displayUnit later. Rigid check on types by the compiler.
340      * @param valueSI float; the SIvalue
341      * @param displayUnit U; the unit in which the value will be displayed
342      * @return S; an instantiated FloatScalar with the SI value and the display unit
343      * @param <U> the unit
344      * @param <S> the return type
345      */
346     public static <U extends Unit<U>, S extends FloatScalar<U, S>> S instantiateSI(final float valueSI, final U displayUnit)
347     {
348         S result = instantiateAnonymous(valueSI, displayUnit.getStandardUnit());
349         result.setDisplayUnit(displayUnit);
350         return result;
351     }
352 
353     /**
354      * Instantiate the FloatScalar based on its unit. Loose check for types on the compiler. This allows the unit to be
355      * specified as a Unit&lt;?&gt; type.<br>
356      * <b>Note</b> that it is possible to make mistakes with anonymous units.
357      * @param value float; the value
358      * @param unit Unit&lt;?&gt;; the unit in which the value is expressed
359      * @return S; an instantiated FloatScalar with the value expressed in the unit
360      * @param <S> the return type
361      */
362     @SuppressWarnings("unchecked")
363     public static <S extends FloatScalar<?, S>> S instantiateAnonymous(final float value, final Unit<?> unit)
364     {
365         try
366         {
367             Constructor<? extends FloatScalar<?, ?>> scalarConstructor = CACHE.get(unit);
368             if (scalarConstructor == null)
369             {
370                 if (!unit.getClass().getSimpleName().endsWith("Unit"))
371                 {
372                     throw new ClassNotFoundException("Unit " + unit.getClass().getSimpleName()
373                             + " name does noet end with 'Unit'. Cannot find corresponding scalar");
374                 }
375                 Class<? extends FloatScalar<?, ?>> scalarClass;
376                 if (unit instanceof SIUnit)
377                 {
378                     scalarClass = FloatSIScalar.class;
379                 }
380                 else
381                 {
382                     scalarClass = (Class<FloatScalar<?, ?>>) Class.forName(
383                             "org.djunits.value.vfloat.scalar.Float" + unit.getClass().getSimpleName().replace("Unit", ""));
384                 }
385                 scalarConstructor = scalarClass.getDeclaredConstructor(float.class, unit.getClass());
386                 CACHE.put(unit, scalarConstructor);
387             }
388             return (S) scalarConstructor.newInstance(value, unit);
389         }
390         catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
391                 | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception)
392         {
393             throw new UnitRuntimeException(
394                     "Cannot instantiate FloatScalar of unit " + unit.toString() + ". Reason: " + exception.getMessage());
395         }
396     }
397 
398     /**
399      * Add a Relative value to an Absolute value. Return a new instance of the value. The unit of the return value will be the
400      * unit of the left argument.
401      * @param left A, an absolute typed FloatScalar; the left argument
402      * @param right R, a relative typed FloatScalar; the right argument
403      * @param <AU> Unit; the absolute unit of the parameters and the result
404      * @param <RU> Unit; the relative unit of the parameters and the result
405      * @param <R> the relative type
406      * @param <A> the corresponding absolute type
407      * @return A; an absolute typed FloatScalar; the sum of the values as an Absolute value
408      */
409     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
410             R extends FloatScalarRelWithAbs<AU, A, RU, R>,
411             A extends FloatScalarAbs<AU, A, RU, R>> A plus(final A left, final R right)
412     {
413         return left.plus(right);
414     }
415 
416     /**
417      * Add an Absolute value to a Relative value. Return a new instance of the value. The unit of the return value will be the
418      * unit of the left argument.
419      * @param left A, an absolute typed FloatScalar; the left argument
420      * @param right R, a relative typed FloatScalar; the right argument
421      * @param <AU> Unit; the absolute unit of the parameters and the result
422      * @param <RU> Unit; the relative unit of the parameters and the result
423      * @param <R> the relative type
424      * @param <A> the corresponding absolute type
425      * @return A; an absolute typed FloatScalar; the sum of the values as an Absolute value
426      */
427     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
428             R extends FloatScalarRelWithAbs<AU, A, RU, R>,
429             A extends FloatScalarAbs<AU, A, RU, R>> A plus(final R left, final A right)
430     {
431         return right.plus(left);
432     }
433 
434     /**
435      * Add a Relative value to a Relative value. Return a new instance of the value. The unit of the return value will be the
436      * unit of the left argument.
437      * @param left R, a relative typed FloatScalar; the left argument
438      * @param right R, a relative typed FloatScalar; the right argument
439      * @param <U> Unit; the unit of the parameters and the result
440      * @param <R> the relative type
441      * @return R; a relative typed FloatScalar; the sum of the values as a Relative value
442      */
443     public static <U extends Unit<U>, R extends FloatScalarRel<U, R>> R plus(final R left, final R right)
444     {
445         return left.plus(right);
446     }
447 
448     /**
449      * Subtract a Relative value from an absolute value. Return a new instance of the value. The unit of the return value will
450      * be the unit of the left argument.
451      * @param left A, an absolute typed FloatScalar; the left value
452      * @param right R, a relative typed FloatScalar; the right value
453      * @param <AU> Unit; the absolute unit of the parameters and the result
454      * @param <RU> Unit; the relative unit of the parameters and the result
455      * @param <R> the relative type
456      * @param <A> the corresponding absolute type
457      * @return A; an absolute typed FloatScalar; the resulting value as an absolute value
458      */
459     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
460             R extends FloatScalarRelWithAbs<AU, A, RU, R>,
461             A extends FloatScalarAbs<AU, A, RU, R>> A minus(final A left, final R right)
462     {
463         return left.minus(right);
464     }
465 
466     /**
467      * Subtract a relative value from a relative value. Return a new instance of the value. The unit of the value will be the
468      * unit of the first argument.
469      * @param left R, a relative typed FloatScalar; the left value
470      * @param right R, a relative typed FloatScalar; the right value
471      * @param <U> Unit; the unit of the parameters and the result
472      * @param <R> the relative type
473      * @return R; a relative typed FloatScalar; the resulting value as a relative value
474      */
475     public static <U extends Unit<U>, R extends FloatScalarRel<U, R>> R minus(final R left, final R right)
476     {
477         return left.minus(right);
478     }
479 
480     /**
481      * Subtract two absolute values. Return a new instance of a relative value of the difference. The unit of the value will be
482      * the unit of the first argument.
483      * @param left A, an absolute typed FloatScalar; value 1
484      * @param right A, an absolute typed FloatScalar; value 2
485      * @param <AU> Unit; the absolute unit of the parameters and the result
486      * @param <RU> Unit; the relative unit of the parameters and the result
487      * @param <R> the relative type
488      * @param <A> the corresponding absolute type
489      * @return R; a relative typed FloatScalar; the difference of the two absolute values as a relative value
490      */
491     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
492             R extends FloatScalarRelWithAbs<AU, A, RU, R>,
493             A extends FloatScalarAbs<AU, A, RU, R>> R minus(final A left, final A right)
494     {
495         return left.minus(right);
496     }
497 
498     /**
499      * Multiply two values; the result is a new instance with a different (existing or generated) SI unit.
500      * @param left FloatScalarRel&lt;?, ?&gt;; the left operand
501      * @param right FloatScalarRel&lt;?, ?&gt;; the right operand
502      * @return FloatScalarRel&lt;SIUnit&gt;; the product of the two values
503      */
504     public static FloatSIScalar multiply(final FloatScalarRel<?, ?> left, final FloatScalarRel<?, ?> right)
505     {
506         SIUnit targetUnit = Unit.lookupOrCreateUnitWithSIDimensions(left.getDisplayUnit().getQuantity().getSiDimensions()
507                 .plus(right.getDisplayUnit().getQuantity().getSiDimensions()));
508         return new FloatSIScalar(left.getSI() * right.getSI(), targetUnit);
509     }
510 
511     /**
512      * Divide two values; the result is a new instance with a different (existing or generated) SI unit.
513      * @param left FloatScalarRel&lt;?, ?&gt;; the left operand
514      * @param right FloatScalarRel&lt;?, ?&gt;; the right operand
515      * @return FloatScalarRel&lt;SIUnit&gt;; the ratio of the two values
516      */
517     public static FloatSIScalar divide(final FloatScalarRel<?, ?> left, final FloatScalarRel<?, ?> right)
518     {
519         SIUnit targetUnit = Unit.lookupOrCreateUnitWithSIDimensions(left.getDisplayUnit().getQuantity().getSiDimensions()
520                 .minus(right.getDisplayUnit().getQuantity().getSiDimensions()));
521         return new FloatSIScalar(left.getSI() / right.getSI(), targetUnit);
522     }
523 
524     /**
525      * Interpolate between two values. Made to be able to call e.g., Area a = FloatScalarinterpolate(a1, a2, 0.4);
526      * @param zero R; the low value
527      * @param one R; the high value
528      * @param ratio float; the ratio between 0 and 1, inclusive
529      * @param <U> Unit; the unit of the parameters and the result
530      * @param <R> the relative type
531      * @return R; an Absolute Scalar at the <code>ratio</code> between <code>zero</code> and <code>one</code>
532      */
533     public static <U extends Unit<U>, R extends FloatScalarRel<U, R>> R interpolate(final R zero, final R one,
534             final float ratio)
535     {
536         return zero.instantiateRel(zero.getInUnit() * (1 - ratio) + one.getInUnit(zero.getDisplayUnit()) * ratio,
537                 zero.getDisplayUnit());
538     }
539 
540     /**
541      * Interpolate between two values. Made to be able to call e.g., Time t = FloatScalarinterpolate(t1, t2, 0.4);
542      * @param zero A; the low value
543      * @param one A; the high value
544      * @param ratio float; the ratio between 0 and 1, inclusive
545      * @param <AU> Unit; the absolute unit of the parameters and the result
546      * @param <RU> Unit; the relative unit of the parameters and the result
547      * @param <R> the relative type
548      * @param <A> the corresponding absolute type
549      * @return R; a Relative Scalar at the <code>ratio</code> between <code>zero</code> and <code>one</code>
550      */
551     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
552             R extends FloatScalarRelWithAbs<AU, A, RU, R>,
553             A extends FloatScalarAbs<AU, A, RU, R>> A interpolate(final A zero, final A one, final float ratio)
554     {
555         return zero.instantiateAbs(zero.getInUnit() * (1 - ratio) + one.getInUnit(zero.getDisplayUnit()) * ratio,
556                 zero.getDisplayUnit());
557     }
558 
559     /**
560      * Return the maximum value of two relative scalars.
561      * @param r1 T; the first scalar
562      * @param r2 T; the second scalar
563      * @param <U> Unit; the unit of the parameters and the result
564      * @param <T> the argument and result type
565      * @return T; the maximum value of two relative scalars
566      */
567     public static <U extends Unit<U>, T extends FloatScalar<U, T>> T max(final T r1, final T r2)
568     {
569         return (r1.gt(r2)) ? r1 : r2;
570     }
571 
572     /**
573      * Return the maximum value of more than two relative scalars.
574      * @param r1 T; the first scalar
575      * @param r2 T; the second scalar
576      * @param rn T...; the other scalars
577      * @param <U> Unit; the unit of the parameters and the result
578      * @param <T> the argument and result type
579      * @return T; the maximum value of more than two relative scalars
580      */
581     @SafeVarargs
582     public static <U extends Unit<U>, T extends FloatScalar<U, T>> T max(final T r1, final T r2, final T... rn)
583     {
584         T maxr = (r1.gt(r2)) ? r1 : r2;
585         for (T r : rn)
586         {
587             if (r.gt(maxr))
588             {
589                 maxr = r;
590             }
591         }
592         return maxr;
593     }
594 
595     /**
596      * Return the minimum value of two relative scalars.
597      * @param r1 T; the first scalar
598      * @param r2 T; the second scalar
599      * @param <U> Unit; the unit of the parameters and the result
600      * @param <T> the argument and result type
601      * @return T; the minimum value of two relative scalars
602      */
603     public static <U extends Unit<U>, T extends FloatScalar<U, T>> T min(final T r1, final T r2)
604     {
605         return r1.lt(r2) ? r1 : r2;
606     }
607 
608     /**
609      * Return the minimum value of more than two relative scalars.
610      * @param r1 T; the first scalar
611      * @param r2 T; the second scalar
612      * @param rn T...; the other scalars
613      * @param <U> Unit; the unit of the parameters and the result
614      * @param <T> the argument and result type
615      * @return T; the minimum value of more than two relative scalars
616      */
617     @SafeVarargs
618     public static <U extends Unit<U>, T extends FloatScalar<U, T>> T min(final T r1, final T r2, final T... rn)
619     {
620         T minr = r1.lt(r2) ? r1 : r2;
621         for (T r : rn)
622         {
623             if (r.lt(minr))
624             {
625                 minr = r;
626             }
627         }
628         return minr;
629     }
630 
631 }