View Javadoc
1   package org.djunits.value.vdouble.vector.base;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.Array;
5   import java.util.Iterator;
6   import java.util.NoSuchElementException;
7   
8   import org.djunits.unit.Unit;
9   import org.djunits.value.Absolute;
10  import org.djunits.value.ValueRuntimeException;
11  import org.djunits.value.base.Vector;
12  import org.djunits.value.formatter.Format;
13  import org.djunits.value.storage.StorageType;
14  import org.djunits.value.util.ValueUtil;
15  import org.djunits.value.vdouble.function.DoubleFunction;
16  import org.djunits.value.vdouble.function.DoubleMathFunctions;
17  import org.djunits.value.vdouble.scalar.base.DoubleScalar;
18  import org.djunits.value.vdouble.vector.data.DoubleVectorData;
19  import org.djunits.value.vdouble.vector.data.DoubleVectorDataDense;
20  import org.djunits.value.vdouble.vector.data.DoubleVectorDataSparse;
21  import org.djutils.exceptions.Throw;
22  
23  /**
24   * The most basic abstract class for the DoubleVector.
25   * <p>
26   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
28   * </p>
29   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
30   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
31   * @param <U> the unit
32   * @param <S> the scalar with unit U
33   * @param <V> the generic vector type
34   */
35  public abstract class DoubleVector<U extends Unit<U>, S extends DoubleScalar<U, S>, V extends DoubleVector<U, S, V>>
36          extends Vector<U, S, V, DoubleVectorData>
37  {
38      /** */
39      private static final long serialVersionUID = 20161015L;
40  
41      /** The stored data as an object, can be sparse or dense. */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      protected DoubleVectorData data;
44  
45      /**
46       * Construct a new DoubleVector.
47       * @param data DoubleVectorData; an internal data object
48       * @param unit U; the unit
49       */
50      DoubleVector(final DoubleVectorData data, final U unit)
51      {
52          super(unit);
53          Throw.whenNull(data, "data cannot be null");
54          this.data = data;
55      }
56  
57      /**
58       * Instantiate a new vector of the class of this vector. This can be used instead of the DoubleVector.instiantiate() methods
59       * in case another vector of this class is known. The method is faster than DoubleVector.instantiate, and it will also work
60       * if the vector is user-defined.
61       * @param dvd DoubleVectorData; the data used to instantiate the vector
62       * @param displayUnit U; the display unit of the vector
63       * @return V; a vector of the correct type
64       */
65      public abstract V instantiateVector(DoubleVectorData dvd, U displayUnit);
66  
67      /**
68       * Instantiate a new scalar for the class of this vector. This can be used instead of the DoubleScalar.instiantiate()
69       * methods in case a vector of this class is known. The method is faster than DoubleScalar.instantiate, and it will also
70       * work if the vector and/or scalar are user-defined.
71       * @param valueSI double; the SI value of the scalar
72       * @param displayUnit U; the unit in which the value will be displayed
73       * @return S; a scalar of the correct type, belonging to the vector type
74       */
75      public abstract S instantiateScalarSI(double valueSI, U displayUnit);
76  
77      @Override
78      protected final DoubleVectorData getData()
79      {
80          return this.data;
81      }
82  
83      @Override
84      protected void setData(final DoubleVectorData data)
85      {
86          this.data = data;
87      }
88  
89      /**
90       * Create a double[] array filled with the values in the standard SI unit.
91       * @return double[]; array of values in the standard SI unit
92       */
93      public final double[] getValuesSI()
94      {
95          return getData().getDenseVectorSI();
96      }
97  
98      /**
99       * Create a double[] array filled with the values in the original unit.
100      * @return double[]; the values in the original unit
101      */
102     public final double[] getValuesInUnit()
103     {
104         return getValuesInUnit(getDisplayUnit());
105     }
106 
107     /**
108      * Create a double[] array filled with the values converted into a specified unit.
109      * @param targetUnit U; the unit into which the values are converted for use
110      * @return double[]; the values converted into the specified unit
111      */
112     public final double[] getValuesInUnit(final U targetUnit)
113     {
114         double[] values = getValuesSI();
115         for (int i = values.length; --i >= 0;)
116         {
117             values[i] = ValueUtil.expressAsUnit(values[i], targetUnit);
118         }
119         return values;
120     }
121 
122     @Override
123     public final int size()
124     {
125         return getData().size();
126     }
127 
128     /**
129      * Check that a provided index is valid.
130      * @param index int; the value to check
131      * @throws IndexOutOfBoundsException when index is invalid
132      */
133     protected final void checkIndex(final int index) throws IndexOutOfBoundsException
134     {
135         if (index < 0 || index >= size())
136         {
137             throw new IndexOutOfBoundsException(
138                     "index out of range (valid range is 0.." + (size() - 1) + ", got " + index + ")");
139         }
140     }
141 
142     /**
143      * Retrieve the value stored at a specified position in the standard SI unit.
144      * @param index int; index of the value to retrieve
145      * @return double; value at position index in the standard SI unit
146      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
147      */
148     public final double getSI(final int index) throws IndexOutOfBoundsException
149     {
150         checkIndex(index);
151         return getData().getSI(index);
152     }
153 
154     @Override
155     public S get(final int index) throws IndexOutOfBoundsException
156     {
157         return DoubleScalar.instantiateSI(getSI(index), getDisplayUnit());
158     }
159 
160     /**
161      * Retrieve the value stored at a specified position in the original unit.
162      * @param index int; index of the value to retrieve
163      * @return double; value at position index in the original unit
164      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
165      */
166     public final double getInUnit(final int index) throws IndexOutOfBoundsException
167     {
168         return ValueUtil.expressAsUnit(getSI(index), getDisplayUnit());
169     }
170 
171     /**
172      * Retrieve the value stored at a specified position converted into a specified unit.
173      * @param index int; index of the value to retrieve
174      * @param targetUnit U; the unit for the result
175      * @return double; value at position index converted into the specified unit
176      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
177      */
178     public final double getInUnit(final int index, final U targetUnit) throws IndexOutOfBoundsException
179     {
180         return ValueUtil.expressAsUnit(getSI(index), targetUnit);
181     }
182 
183     /**
184      * Set the value, specified in the standard SI unit, at the specified position.
185      * @param index int; the index of the value to set
186      * @param valueSI double; the value, specified in the standard SI unit
187      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
188      */
189     public final void setSI(final int index, final double valueSI) throws IndexOutOfBoundsException
190     {
191         checkIndex(index);
192         checkCopyOnWrite();
193         getData().setSI(index, valueSI);
194     }
195 
196     /**
197      * Set the value, specified in the (current) display unit, at the specified position.
198      * @param index int; the index of the value to set
199      * @param valueInUnit double; the value, specified in the (current) display unit
200      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
201      */
202     public void setInUnit(final int index, final double valueInUnit) throws IndexOutOfBoundsException
203     {
204         setSI(index, ValueUtil.expressAsSIUnit(valueInUnit, getDisplayUnit()));
205     }
206 
207     /**
208      * Set the value, specified in the <code>valueUnit</code>, at the specified position.
209      * @param index int; the index of the value to set
210      * @param valueInUnit double; the value, specified in the (current) display unit
211      * @param valueUnit U; the unit in which the <code>valueInUnit</code> is expressed
212      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
213      */
214     public void setInUnit(final int index, final double valueInUnit, final U valueUnit) throws IndexOutOfBoundsException
215     {
216         setSI(index, ValueUtil.expressAsSIUnit(valueInUnit, valueUnit));
217     }
218 
219     /**
220      * Set the scalar value at the specified position.
221      * @param index int; the index of the value to set
222      * @param value S; the value to set
223      * @throws IndexOutOfBoundsException when index out of range (index &lt; 0 or index &gt;= size())
224      */
225     public void set(final int index, final S value) throws IndexOutOfBoundsException
226     {
227         setSI(index, value.si);
228     }
229 
230     @SuppressWarnings("unchecked")
231     @Override
232     public S[] getScalars()
233     {
234         S[] array = (S[]) Array.newInstance(getScalarClass(), size());
235         for (int i = 0; i < size(); i++)
236         {
237             array[i] = get(i);
238         }
239         return array;
240     }
241 
242     @SuppressWarnings("unchecked")
243     @Override
244     public V toSparse()
245     {
246         V result;
247         if (getStorageType().equals(StorageType.SPARSE))
248         {
249             result = (V) this;
250             result.setDisplayUnit(getDisplayUnit());
251         }
252         else
253         {
254             result = instantiateVector(getData().toSparse(), getDisplayUnit());
255         }
256         result.setDisplayUnit(getDisplayUnit());
257         return result;
258     }
259 
260     @SuppressWarnings("unchecked")
261     @Override
262     public V toDense()
263     {
264         V result;
265         if (getStorageType().equals(StorageType.DENSE))
266         {
267             result = (V) this;
268             result.setDisplayUnit(getDisplayUnit());
269         }
270         else
271         {
272             result = instantiateVector(getData().toDense(), getDisplayUnit());
273         }
274         return result;
275     }
276 
277     /**
278      * Execute a function on a cell by cell basis. Note: May be expensive when used on sparse data.
279      * @param doubleFunction DoubleFunction; the function to apply
280      * @return V; this updated vector
281      */
282     @SuppressWarnings("unchecked")
283     public final V assign(final DoubleFunction doubleFunction)
284     {
285         checkCopyOnWrite();
286         if (getData() instanceof DoubleVectorDataDense)
287         {
288             ((DoubleVectorDataDense) getData()).assign(doubleFunction);
289         }
290         else
291         {
292             this.data = ((DoubleVectorDataSparse) getData()).toDense().assign(doubleFunction).toSparse();
293         }
294         return (V) this;
295     }
296 
297     @Override
298     public final V abs()
299     {
300         return assign(DoubleMathFunctions.ABS);
301     }
302 
303     @Override
304     public final V ceil()
305     {
306         return assign(DoubleMathFunctions.CEIL);
307     }
308 
309     @Override
310     public final V floor()
311     {
312         return assign(DoubleMathFunctions.FLOOR);
313     }
314 
315     @Override
316     public final V neg()
317     {
318         return assign(DoubleMathFunctions.NEG);
319     }
320 
321     @Override
322     public final V rint()
323     {
324         return assign(DoubleMathFunctions.RINT);
325     }
326 
327     @Override
328     public String toString()
329     {
330         return toString(getDisplayUnit(), false, true);
331     }
332 
333     @Override
334     public String toString(final U displayUnit)
335     {
336         return toString(displayUnit, false, true);
337     }
338 
339     @Override
340     public String toString(final boolean verbose, final boolean withUnit)
341     {
342         return toString(getDisplayUnit(), verbose, withUnit);
343     }
344 
345     @Override
346     public String toString(final U displayUnit, final boolean verbose, final boolean withUnit)
347     {
348         StringBuffer buf = new StringBuffer();
349         if (verbose)
350         {
351             String ar = this instanceof Absolute ? "Abs " : "Rel ";
352             String ds = getData().isDense() ? "Dense  " : getData().isSparse() ? "Sparse " : "?????? ";
353             if (isMutable())
354             {
355                 buf.append("Mutable   " + ar + ds);
356             }
357             else
358             {
359                 buf.append("Immutable " + ar + ds);
360             }
361         }
362         buf.append("[");
363         for (int i = 0; i < size(); i++)
364         {
365             try
366             {
367                 double d = ValueUtil.expressAsUnit(getSI(i), displayUnit);
368                 buf.append(" " + Format.format(d));
369             }
370             catch (IndexOutOfBoundsException ve)
371             {
372                 buf.append(" " + "********************".substring(0, Format.DEFAULTSIZE));
373             }
374         }
375         buf.append("]");
376         if (withUnit)
377         {
378             buf.append(" " + displayUnit.getLocalizedDisplayAbbreviation());
379         }
380         return buf.toString();
381     }
382 
383     /**
384      * Centralized size equality check.
385      * @param other DoubleVector&lt;?, ?, ?&gt;; other DoubleVector
386      * @throws NullPointerException when other vector is null
387      * @throws ValueRuntimeException when vectors have unequal size
388      */
389     protected final void checkSize(final DoubleVector<?, ?, ?> other) throws ValueRuntimeException
390     {
391         Throw.whenNull(other, "Other vector is null");
392         Throw.when(size() != other.size(), ValueRuntimeException.class, "The vectors have different sizes: %d != %d", size(),
393                 other.size());
394     }
395 
396     @Override
397     @SuppressWarnings("checkstyle:designforextension")
398     public int hashCode()
399     {
400         final int prime = 31;
401         int result = getDisplayUnit().getStandardUnit().hashCode();
402         result = prime * result + ((this.data == null) ? 0 : this.data.hashCode());
403         return result;
404     }
405 
406     @Override
407     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
408     public boolean equals(final Object obj)
409     {
410         if (this == obj)
411             return true;
412         if (obj == null)
413             return false;
414         if (getClass() != obj.getClass())
415             return false;
416         DoubleVector<?, ?, ?> other = (DoubleVector<?, ?, ?>) obj;
417         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
418             return false;
419         if (this.data == null)
420         {
421             if (other.data != null)
422                 return false;
423         }
424         else if (!this.data.equals(other.data))
425             return false;
426         return true;
427     }
428 
429     /* ============================================================================================ */
430     /* =============================== ITERATOR METHODS AND CLASS ================================= */
431     /* ============================================================================================ */
432 
433     @Override
434     public Iterator<S> iterator()
435     {
436         return new Itr();
437     }
438 
439     /**
440      * The iterator class is loosely based in AbstractList.Itr. It does not throw a ConcurrentModificationException, because the
441      * size of the vector does not change. Normal (non-mutable) vectors cannot change their size, nor their content. The only
442      * thing for the MutableVector that can change is its content, not its length.
443      */
444     protected class Itr implements Iterator<S>, Serializable
445     {
446         /** ... */
447         private static final long serialVersionUID = 20191018L;
448 
449         /** index of next element to return. */
450         private int cursor = 0;
451 
452         @Override
453         public boolean hasNext()
454         {
455             return this.cursor != size();
456         }
457 
458         @Override
459         public S next()
460         {
461             if (this.cursor >= size())
462             {
463                 throw new NoSuchElementException();
464             }
465             try
466             {
467                 int i = this.cursor;
468                 S next = get(i);
469                 this.cursor = i + 1;
470                 return next;
471             }
472             catch (IndexOutOfBoundsException exception)
473             {
474                 throw new RuntimeException(exception);
475             }
476         }
477 
478         @Override
479         public void remove()
480         {
481             throw new RuntimeException("Remove function cannot be applied on fixed-size DJUNITS Vector");
482         }
483 
484         @Override
485         public String toString()
486         {
487             return "Itr [cursor=" + this.cursor + "]";
488         }
489 
490     }
491 
492 }