View Javadoc
1   package org.djunits.value.vdouble.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.vdouble.scalar.SIScalar;
18  
19  /**
20   * The most basic abstract class for the DoubleScalar.
21   * <p>
22   * Copyright (c) 2013-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 DoubleScalar<U extends Unit<U>, S extends DoubleScalar<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 double si;
38  
39      /**
40       * Construct a new DoubleScalar.
41       * @param unit U; the unit
42       * @param si double; the si value to store
43       */
44      public DoubleScalar(final U unit, final double si)
45      {
46          super(unit);
47          this.si = si;
48      }
49  
50      /**
51       * Retrieve the value in the underlying SI unit.
52       * @return double
53       */
54      public final double getSI()
55      {
56          return this.si;
57      }
58  
59      /**
60       * Retrieve the value in the original unit.
61       * @return double
62       */
63      public final double getInUnit()
64      {
65          return 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 double
72       */
73      public final double getInUnit(final U targetUnit)
74      {
75          return 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 Double.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 (float) 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         double d = 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         double d = ValueUtil.expressAsUnit(getSI(), displayUnit);
272         return format(d) + " " + 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         double d = ValueUtil.expressAsUnit(getSI(), displayUnit);
285         return format(d) + " " + 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 = Double.doubleToLongBits(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         DoubleScalar<U, S> other = (DoubleScalar<U, S>) obj;
311         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
312             return false;
313         if (Double.doubleToLongBits(this.getSI()) != Double.doubleToLongBits(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 DoubleScalar<?, ?>>> CACHE = new HashMap<>();
324     
325     /**
326      * Instantiate the DoubleScalar based on its unit. Rigid check on types by the compiler.
327      * @param value double; the value
328      * @param unit U; the unit in which the value is expressed
329      * @return S; an instantiated DoubleScalar 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 DoubleScalar<U, S>> S instantiate(final double value, final U unit)
334     {
335         return instantiateAnonymous(value, unit);
336     }
337 
338     /**
339      * Instantiate the DoubleScalar with an SI value and add the displayUnit later. Rigid check on types by the compiler.
340      * @param valueSI double; the SIvalue
341      * @param displayUnit U; the unit in which the value will be displayed
342      * @return S; an instantiated DoubleScalar 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 DoubleScalar<U, S>> S instantiateSI(final double valueSI,
347             final U displayUnit)
348     {
349         S result = instantiateAnonymous(valueSI, displayUnit.getStandardUnit());
350         result.setDisplayUnit(displayUnit);
351         return result;
352     }
353 
354     /**
355      * Instantiate the DoubleScalar based on its unit. Loose check for types on the compiler. This allows the unit to be
356      * specified as a Unit&lt;?&gt; type.<br>
357      * <b>Note</b> that it is possible to make mistakes with anonymous units.
358      * @param value double; the value
359      * @param unit Unit&lt;?&gt;; the unit in which the value is expressed
360      * @return S; an instantiated DoubleScalar with the value expressed in the unit
361      * @param <S> the return type
362      */
363     @SuppressWarnings("unchecked")
364     public static <S extends DoubleScalar<?, S>> S instantiateAnonymous(final double value, final Unit<?> unit)
365     {
366         try
367         {
368             Constructor<? extends DoubleScalar<?, ?>> scalarConstructor = CACHE.get(unit);
369             if (scalarConstructor == null)
370             {
371                 if (!unit.getClass().getSimpleName().endsWith("Unit"))
372                 {
373                     throw new ClassNotFoundException("Unit " + unit.getClass().getSimpleName()
374                             + " name does noet end with 'Unit'. Cannot find corresponding scalar");
375                 }
376                 Class<? extends DoubleScalar<?, ?>> scalarClass;
377                 if (unit instanceof SIUnit)
378                 {
379                     scalarClass = SIScalar.class;
380                 }
381                 else
382                 {
383                     scalarClass = (Class<DoubleScalar<?, ?>>) Class
384                             .forName("org.djunits.value.vdouble.scalar." + unit.getClass().getSimpleName().replace("Unit", ""));
385                 }
386                 scalarConstructor = scalarClass.getDeclaredConstructor(double.class, unit.getClass());
387                 CACHE.put(unit, scalarConstructor);
388             }
389             return (S) scalarConstructor.newInstance(value, unit);
390         }
391         catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
392                 | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception)
393         {
394             throw new UnitRuntimeException("Cannot instantiate DoubleScalar of unit " + unit.toString() + ". Reason: "
395                     + exception.getMessage());
396         }
397     }
398 
399     /**
400      * Add a Relative value to an Absolute value. Return a new instance of the value. The unit of the return value will be the
401      * unit of the left argument.
402      * @param left A, an absolute typed DoubleScalar; the left argument
403      * @param right R, a relative typed DoubleScalar; the right argument
404      * @param <AU> Unit; the absolute unit of the parameters and the result
405      * @param <RU> Unit; the relative unit of the parameters and the result
406      * @param <R> the relative type
407      * @param <A> the corresponding absolute type
408      * @return A; an absolute typed DoubleScalar; the sum of the values as an Absolute value
409      */
410     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
411             R extends DoubleScalarRelWithAbs<AU, A, RU, R>,
412             A extends DoubleScalarAbs<AU, A, RU, R>> A plus(final A left, final R right)
413     {
414         return left.plus(right);
415     }
416 
417     /**
418      * Add an Absolute value to a Relative value. Return a new instance of the value. The unit of the return value will be the
419      * unit of the left argument.
420      * @param left A, an absolute typed DoubleScalar; the left argument
421      * @param right R, a relative typed DoubleScalar; the right argument
422      * @param <AU> Unit; the absolute unit of the parameters and the result
423      * @param <RU> Unit; the relative unit of the parameters and the result
424      * @param <R> the relative type
425      * @param <A> the corresponding absolute type
426      * @return A; an absolute typed DoubleScalar; the sum of the values as an Absolute value
427      */
428     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
429             R extends DoubleScalarRelWithAbs<AU, A, RU, R>,
430             A extends DoubleScalarAbs<AU, A, RU, R>> A plus(final R left, final A right)
431     {
432         return right.plus(left);
433     }
434 
435     /**
436      * Add a Relative value to a Relative value. Return a new instance of the value. The unit of the return value will be the
437      * unit of the left argument.
438      * @param left R, a relative typed DoubleScalar; the left argument
439      * @param right R, a relative typed DoubleScalar; the right argument
440      * @param <U> Unit; the unit of the parameters and the result
441      * @param <R> the relative type
442      * @return R; a relative typed DoubleScalar; the sum of the values as a Relative value
443      */
444     public static <U extends Unit<U>, R extends DoubleScalarRel<U, R>> R plus(final R left, final R right)
445     {
446         return left.plus(right);
447     }
448 
449     /**
450      * Subtract a Relative value from an absolute value. Return a new instance of the value. The unit of the return value will
451      * be the unit of the left argument.
452      * @param left A, an absolute typed DoubleScalar; the left value
453      * @param right R, a relative typed DoubleScalar; the right value
454      * @param <AU> Unit; the absolute unit of the parameters and the result
455      * @param <RU> Unit; the relative unit of the parameters and the result
456      * @param <R> the relative type
457      * @param <A> the corresponding absolute type
458      * @return A; an absolute typed DoubleScalar; the resulting value as an absolute value
459      */
460     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
461             R extends DoubleScalarRelWithAbs<AU, A, RU, R>,
462             A extends DoubleScalarAbs<AU, A, RU, R>> A minus(final A left, final R right)
463     {
464         return left.minus(right);
465     }
466 
467     /**
468      * Subtract a relative value from a relative value. Return a new instance of the value. The unit of the value will be the
469      * unit of the first argument.
470      * @param left R, a relative typed DoubleScalar; the left value
471      * @param right R, a relative typed DoubleScalar; the right value
472      * @param <U> Unit; the unit of the parameters and the result
473      * @param <R> the relative type
474      * @return R; a relative typed DoubleScalar; the resulting value as a relative value
475      */
476     public static <U extends Unit<U>, R extends DoubleScalarRel<U, R>> R minus(final R left, final R right)
477     {
478         return left.minus(right);
479     }
480 
481     /**
482      * Subtract two absolute values. Return a new instance of a relative value of the difference. The unit of the value will be
483      * the unit of the first argument.
484      * @param left A, an absolute typed DoubleScalar; value 1
485      * @param right A, an absolute typed DoubleScalar; value 2
486      * @param <AU> Unit; the absolute unit of the parameters and the result
487      * @param <RU> Unit; the relative unit of the parameters and the result
488      * @param <R> the relative type
489      * @param <A> the corresponding absolute type
490      * @return R; a relative typed DoubleScalar; the difference of the two absolute values as a relative value
491      */
492     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
493             R extends DoubleScalarRelWithAbs<AU, A, RU, R>,
494             A extends DoubleScalarAbs<AU, A, RU, R>> R minus(final A left, final A right)
495     {
496         return left.minus(right);
497     }
498 
499     /**
500      * Multiply two values; the result is a new instance with a different (existing or generated) SI unit.
501      * @param left DoubleScalarRel&lt;?, ?&gt;; the left operand
502      * @param right DoubleScalarRel&lt;?, ?&gt;; the right operand
503      * @return DoubleScalarRel&lt;SIUnit&gt;; the product of the two values
504      */
505     public static SIScalar multiply(final DoubleScalarRel<?, ?> left, final DoubleScalarRel<?, ?> right)
506     {
507         SIUnit targetUnit = Unit.lookupOrCreateUnitWithSIDimensions(left.getDisplayUnit().getQuantity().getSiDimensions()
508                 .plus(right.getDisplayUnit().getQuantity().getSiDimensions()));
509         return new SIScalar(left.getSI() * right.getSI(), targetUnit);
510     }
511 
512     /**
513      * Divide two values; the result is a new instance with a different (existing or generated) SI unit.
514      * @param left DoubleScalarRel&lt;?, ?&gt;; the left operand
515      * @param right DoubleScalarRel&lt;?, ?&gt;; the right operand
516      * @return DoubleScalarRel&lt;SIUnit&gt;; the ratio of the two values
517      */
518     public static SIScalar divide(final DoubleScalarRel<?, ?> left, final DoubleScalarRel<?, ?> right)
519     {
520         SIUnit targetUnit = Unit.lookupOrCreateUnitWithSIDimensions(left.getDisplayUnit().getQuantity().getSiDimensions()
521                 .minus(right.getDisplayUnit().getQuantity().getSiDimensions()));
522         return new SIScalar(left.getSI() / right.getSI(), targetUnit);
523     }
524 
525     /**
526      * Interpolate between two values. Made to be able to call e.g., Area a = DoubleScalar.interpolate(a1, a2, 0.4);
527      * @param zero R; the low value
528      * @param one R; the high value
529      * @param ratio double; the ratio between 0 and 1, inclusive
530      * @param <U> Unit; the unit of the parameters and the result
531      * @param <R> the relative type
532      * @return R; an Absolute Scalar at the <code>ratio</code> between <code>zero</code> and <code>one</code>
533      */
534     public static <U extends Unit<U>, R extends DoubleScalarRel<U, R>> R interpolate(final R zero, final R one,
535             final double ratio)
536     {
537         return zero.instantiateRel(zero.getInUnit() * (1 - ratio) + one.getInUnit(zero.getDisplayUnit()) * ratio,
538                 zero.getDisplayUnit());
539     }
540 
541     /**
542      * Interpolate between two values. Made to be able to call e.g., Time t = DoubleScalar.interpolate(t1, t2, 0.4);
543      * @param zero A; the low value
544      * @param one A; the high value
545      * @param ratio double; the ratio between 0 and 1, inclusive
546      * @param <AU> Unit; the absolute unit of the parameters and the result
547      * @param <RU> Unit; the relative unit of the parameters and the result
548      * @param <R> the relative type
549      * @param <A> the corresponding absolute type
550      * @return R; a Relative Scalar at the <code>ratio</code> between <code>zero</code> and <code>one</code>
551      */
552     public static <AU extends AbsoluteLinearUnit<AU, RU>, RU extends Unit<RU>,
553             R extends DoubleScalarRelWithAbs<AU, A, RU, R>,
554             A extends DoubleScalarAbs<AU, A, RU, R>> A interpolate(final A zero, final A one, final double ratio)
555     {
556         return zero.instantiateAbs(zero.getInUnit() * (1 - ratio) + one.getInUnit(zero.getDisplayUnit()) * ratio,
557                 zero.getDisplayUnit());
558     }
559 
560     /**
561      * Return the maximum value of two relative scalars.
562      * @param r1 T; the first scalar
563      * @param r2 T; the second scalar
564      * @param <U> Unit; the unit of the parameters and the result
565      * @param <T> the argument and result type
566      * @return T; the maximum value of two relative scalars
567      */
568     public static <U extends Unit<U>, T extends DoubleScalar<U, T>> T max(final T r1, final T r2)
569     {
570         return (r1.gt(r2)) ? r1 : r2;
571     }
572 
573     /**
574      * Return the maximum value of more than two relative scalars.
575      * @param r1 T; the first scalar
576      * @param r2 T; the second scalar
577      * @param rn T...; the other scalars
578      * @param <U> Unit; the unit of the parameters and the result
579      * @param <T> the argument and result type
580      * @return T; the maximum value of more than two relative scalars
581      */
582     @SafeVarargs
583     public static <U extends Unit<U>, T extends DoubleScalar<U, T>> T max(final T r1, final T r2, final T... rn)
584     {
585         T maxr = (r1.gt(r2)) ? r1 : r2;
586         for (T r : rn)
587         {
588             if (r.gt(maxr))
589             {
590                 maxr = r;
591             }
592         }
593         return maxr;
594     }
595 
596     /**
597      * Return the minimum value of two relative scalars.
598      * @param r1 T; the first scalar
599      * @param r2 T; the second scalar
600      * @param <U> Unit; the unit of the parameters and the result
601      * @param <T> the argument and result type
602      * @return T; the minimum value of two relative scalars
603      */
604     public static <U extends Unit<U>, T extends DoubleScalar<U, T>> T min(final T r1, final T r2)
605     {
606         return r1.lt(r2) ? r1 : r2;
607     }
608 
609     /**
610      * Return the minimum value of more than two relative scalars.
611      * @param r1 T; the first scalar
612      * @param r2 T; the second scalar
613      * @param rn T...; the other scalars
614      * @param <U> Unit; the unit of the parameters and the result
615      * @param <T> the argument and result type
616      * @return T; the minimum value of more than two relative scalars
617      */
618     @SafeVarargs
619     public static <U extends Unit<U>, T extends DoubleScalar<U, T>> T min(final T r1, final T r2, final T... rn)
620     {
621         T minr = r1.lt(r2) ? r1 : r2;
622         for (T r : rn)
623         {
624             if (r.lt(minr))
625             {
626                 minr = r;
627             }
628         }
629         return minr;
630     }
631 
632 }