View Javadoc
1   package org.djunits.value.vdouble.vector.data;
2   
3   import java.io.Serializable;
4   import java.util.Arrays;
5   import java.util.List;
6   import java.util.Map.Entry;
7   import java.util.SortedMap;
8   import java.util.stream.IntStream;
9   
10  import org.djunits.Throw;
11  import org.djunits.unit.Unit;
12  import org.djunits.unit.scale.Scale;
13  import org.djunits.value.ValueRuntimeException;
14  import org.djunits.value.storage.AbstractStorage;
15  import org.djunits.value.storage.StorageType;
16  import org.djunits.value.vdouble.function.DoubleFunction;
17  import org.djunits.value.vdouble.function.DoubleFunction2;
18  import org.djunits.value.vdouble.scalar.base.DoubleScalarInterface;
19  
20  /**
21   * Stores the data for a DoubleVector and carries out basic operations.
22   * <p>
23   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
25   * </p>
26   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
28   */
29  public abstract class DoubleVectorData extends AbstractStorage<DoubleVectorData> implements Serializable
30  {
31      /** */
32      private static final long serialVersionUID = 1L;
33  
34      /** The internal storage of the Vector; can be sparse or dense. */
35      @SuppressWarnings("checkstyle:visibilitymodifier")
36      protected double[] vectorSI;
37  
38      /** Threshold to do parallel execution. */
39      protected static final int PARALLEL_THRESHOLD = 1000;
40  
41      /**
42       * Construct a new DoubleVectorData object.
43       * @param storageType StorageType; the data type.
44       */
45      DoubleVectorData(final StorageType storageType)
46      {
47          super(storageType);
48      }
49  
50      /* ============================================================================================ */
51      /* ====================================== INSTANTIATION ======================================= */
52      /* ============================================================================================ */
53  
54      /**
55       * Instantiate a DoubleVectorData with the right data type.
56       * @param values double[]; the (SI) values to store
57       * @param scale Scale; the scale of the unit to use for conversion to SI
58       * @param storageType StorageType; the data type to use
59       * @return DoubleVectorData; the DoubleVectorData with the right data type
60       * @throws ValueRuntimeException when values are null, or storageType is null
61       */
62      public static DoubleVectorData instantiate(final double[] values, final Scale scale, final StorageType storageType)
63              throws ValueRuntimeException
64      {
65          Throw.whenNull(values, "DoubleVectorData.instantiate: double[] values is null");
66          Throw.whenNull(scale, "DoubleVectorData.instantiate: scale is null");
67          Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
68          Throw.when(values.length == 0, ValueRuntimeException.class,
69                  "DoubleVectorData.instantiate: double[] values wrong: values.length == 0");
70  
71          double[] valuesSI = scale.isBaseSIScale() ? values : new double[values.length];
72          if (!scale.isBaseSIScale())
73          {
74              if (values.length > PARALLEL_THRESHOLD)
75              {
76                  IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = scale.toStandardUnit(values[i]));
77              }
78              else
79              {
80                  IntStream.range(0, values.length).forEach(i -> valuesSI[i] = scale.toStandardUnit(values[i]));
81              }
82          }
83  
84          switch (storageType)
85          {
86              case DENSE:
87                  return new DoubleVectorDataDense(valuesSI);
88  
89              case SPARSE:
90                  return DoubleVectorDataSparse.instantiate(valuesSI);
91  
92              default:
93                  throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
94          }
95      }
96  
97      /**
98       * Instantiate a DoubleVectorData with the right data type.
99       * @param values List&lt;Double&gt;; the values to store
100      * @param scale Scale; the scale of the unit to use for conversion to SI
101      * @param storageType StorageType; the data type to use
102      * @return DoubleVectorData; the DoubleVectorData with the right data type
103      * @throws ValueRuntimeException when list is null, or storageType is null
104      */
105     public static DoubleVectorData instantiate(final List<Double> values, final Scale scale, final StorageType storageType)
106             throws ValueRuntimeException
107     {
108         Throw.whenNull(values, "DoubleVectorData.instantiate: double[] values is null");
109         Throw.whenNull(scale, "DoubleVectorData.instantiate: scale is null");
110         Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
111         Throw.when(values.size() == 0, ValueRuntimeException.class, "DoubleVectorData.instantiate: values.size() == 0");
112         Throw.when(values.parallelStream().filter(d -> d == null).count() > 0, NullPointerException.class,
113                 "values contains one or more null values");
114 
115         switch (storageType)
116         {
117             case DENSE:
118             {
119                 double[] valuesSI;
120                 if (values.size() > PARALLEL_THRESHOLD)
121                 {
122                     if (scale.isBaseSIScale())
123                     {
124                         valuesSI = values.parallelStream().mapToDouble(d -> d).toArray();
125                     }
126                     else
127                     {
128                         valuesSI = values.parallelStream().mapToDouble(d -> scale.toStandardUnit(d)).toArray();
129                     }
130                 }
131                 else
132                 {
133                     if (scale.isBaseSIScale())
134                     {
135                         valuesSI = values.stream().mapToDouble(d -> d).toArray();
136                     }
137                     else
138                     {
139                         valuesSI = values.stream().mapToDouble(d -> scale.toStandardUnit(d)).toArray();
140                     }
141                 }
142                 return new DoubleVectorDataDense(valuesSI);
143             }
144 
145             case SPARSE:
146             {
147                 int nonZeroCount;
148                 if (values.size() > PARALLEL_THRESHOLD)
149                 {
150                     if (scale.isBaseSIScale())
151                     {
152                         nonZeroCount = (int) values.parallelStream().filter(d -> d != 0d).count();
153                     }
154                     else
155                     {
156                         nonZeroCount = (int) values.parallelStream().filter(d -> scale.toStandardUnit(d) != 0d).count();
157                     }
158                 }
159                 else
160                 {
161                     if (scale.isBaseSIScale())
162                     {
163                         nonZeroCount = (int) values.stream().filter(d -> d != 0d).count();
164                     }
165                     else
166                     {
167                         nonZeroCount = values.size()
168                                 - (int) values.parallelStream().filter(d -> scale.toStandardUnit(d) == 0d).count();
169                     }
170                 }
171                 int[] indices = new int[nonZeroCount];
172                 double[] valuesSI = new double[nonZeroCount];
173                 // Counting non zeros could be done in parallel; but filling the arrays has to be done sequentially
174                 int index = 0;
175                 for (int i = 0; i < values.size(); i++)
176                 {
177                     double d = scale.toStandardUnit(values.get(i));
178                     if (d != 0.0)
179                     {
180                         indices[index] = i;
181                         valuesSI[index] = d;
182                         index++;
183                     }
184                 }
185                 return new DoubleVectorDataSparse(valuesSI, indices, values.size());
186             }
187 
188             default:
189                 throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
190         }
191     }
192 
193     /**
194      * Instantiate a DoubleVectorData with the right data type.
195      * @param values DoubleScalarInterface[]; the values to store
196      * @param storageType StorageType; the data type to use
197      * @return DoubleVectorData; the DoubleVectorData with the right data type
198      * @throws ValueRuntimeException when values is null, or storageType is null
199      * @param <U> the unit type
200      * @param <S> the corresponding scalar type
201      */
202     public static <U extends Unit<U>, S extends DoubleScalarInterface<U, S>> DoubleVectorData instantiate(final S[] values,
203             final StorageType storageType) throws ValueRuntimeException
204     {
205         Throw.whenNull(values, "DoubleVectorData.instantiate: double[] values is null");
206         Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
207         Throw.when(values.length == 0, ValueRuntimeException.class,
208                 "DoubleVectorData.instantiate: DoubleScalar[] values wrong: values.length == 0");
209         for (S s : values)
210         {
211             Throw.whenNull(s, "null value in values");
212         }
213 
214         switch (storageType)
215         {
216             case DENSE:
217             {
218                 double[] valuesSI;
219                 if (values.length > PARALLEL_THRESHOLD)
220                 {
221                     valuesSI = Arrays.stream(values).parallel().mapToDouble(s -> s.getSI()).toArray();
222                 }
223                 else
224                 {
225                     valuesSI = Arrays.stream(values).mapToDouble(s -> s.getSI()).toArray();
226                 }
227                 return new DoubleVectorDataDense(valuesSI);
228             }
229 
230             case SPARSE:
231             {
232                 int nonZeroCount;
233                 if (values.length > PARALLEL_THRESHOLD)
234                 {
235                     nonZeroCount = (int) Arrays.stream(values).parallel().filter(s -> s.getSI() != 0).count();
236                 }
237                 else
238                 {
239                     nonZeroCount = (int) Arrays.stream(values).filter(s -> s.getSI() != 0.0).count();
240                 }
241                 int[] indices = new int[nonZeroCount];
242                 double[] valuesSI = new double[nonZeroCount];
243                 // Counting non zeros could be done in parallel; but filling the arrays has to be done sequentially
244                 int index = 0;
245                 for (int i = 0; i < values.length; i++)
246                 {
247                     double d = values[i].getSI();
248                     if (d != 0.0)
249                     {
250                         indices[index] = i;
251                         valuesSI[index] = d;
252                         index++;
253                     }
254                 }
255                 return new DoubleVectorDataSparse(valuesSI, indices, values.length);
256             }
257 
258             default:
259                 throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
260         }
261     }
262 
263     /**
264      * Instantiate a DoubleVectorData with the right data type.
265      * @param valueList List&lt;S&gt;; the values to store
266      * @param storageType StorageType; the data type to use
267      * @return DoubleVectorData; the DoubleVectorData with the right data type
268      * @throws ValueRuntimeException when values is null, or storageType is null
269      * @param <U> the unit type
270      * @param <S> the corresponding scalar type
271      */
272     public static <U extends Unit<U>, S extends DoubleScalarInterface<U, S>> DoubleVectorData instantiateList(
273             final List<S> valueList, final StorageType storageType) throws ValueRuntimeException
274     {
275         Throw.whenNull(valueList, "DoubleVectorData.instantiate: valueList is null");
276         Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
277         Throw.when(valueList.size() == 0, ValueRuntimeException.class, "DoubleVectorData.instantiate: valueList.size() == 0");
278         for (S s : valueList)
279         {
280             Throw.whenNull(s, "null value in valueList");
281         }
282 
283         switch (storageType)
284         {
285             case DENSE:
286             {
287                 double[] valuesSI;
288                 if (valueList.size() > PARALLEL_THRESHOLD)
289                 {
290                     valuesSI = valueList.stream().parallel().mapToDouble(s -> s.getSI()).toArray();
291                 }
292                 else
293                 {
294                     valuesSI = valueList.stream().mapToDouble(s -> s.getSI()).toArray();
295                 }
296                 return new DoubleVectorDataDense(valuesSI);
297             }
298 
299             case SPARSE:
300             {
301                 int nonZeroCount = (int) valueList.parallelStream().filter(s -> s.getSI() != 0.0).count();
302                 int[] indices = new int[nonZeroCount];
303                 double[] valuesSI = new double[nonZeroCount];
304                 // Counting non zeros could be done in parallel; but filling the arrays has to be done sequentially
305                 int index = 0;
306                 for (int i = 0; i < valueList.size(); i++)
307                 {
308                     double d = valueList.get(i).getSI();
309                     if (d != 0.0)
310                     {
311                         indices[index] = i;
312                         valuesSI[index] = d;
313                         index++;
314                     }
315                 }
316                 return new DoubleVectorDataSparse(valuesSI, indices, valueList.size());
317             }
318 
319             default:
320                 throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
321         }
322     }
323 
324     /**
325      * Instantiate a DoubleVectorData with the right data type.
326      * @param valueMap SortedMap&lt;Integer,Double&gt;; the DoubleScalar values to store
327      * @param length int; the length of the vector to pad with 0 after last entry in map
328      * @param scale Scale; the scale of the unit to use for conversion to SI
329      * @param storageType StorageType; the data type to use
330      * @return DoubleVectorData; the DoubleVectorData with the right data type
331      * @throws ValueRuntimeException when values is null, or storageType is null
332      */
333     public static DoubleVectorData instantiate(final SortedMap<Integer, Double> valueMap, final int length, final Scale scale,
334             final StorageType storageType) throws ValueRuntimeException
335     {
336         Throw.whenNull(valueMap, "DoubleVectorData.instantiate: values is null");
337         Throw.when(length < 1, ValueRuntimeException.class, "Length must be > 0");
338         Throw.whenNull(scale, "DoubleVectorData.instantiate: scale is null");
339         Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
340         for (Integer key : valueMap.keySet())
341         {
342             Throw.when(key < 0 || key >= length, ValueRuntimeException.class, "Key in values out of range");
343         }
344 
345         switch (storageType)
346         {
347             case DENSE:
348             {
349                 double[] valuesSI = new double[length];
350                 if (scale.isBaseSIScale())
351                 {
352                     valueMap.entrySet().parallelStream().forEach(entry -> valuesSI[entry.getKey()] = entry.getValue());
353                 }
354                 else
355                 {
356                     Arrays.fill(valuesSI, scale.toStandardUnit(0.0));
357                     valueMap.entrySet().parallelStream()
358                             .forEach(entry -> valuesSI[entry.getKey()] = scale.toStandardUnit(entry.getValue()));
359                 }
360                 return new DoubleVectorDataDense(valuesSI);
361             }
362 
363             case SPARSE:
364             {
365                 int nonZeroCount;
366                 if (scale.isBaseSIScale())
367                 {
368                     nonZeroCount = (int) valueMap.keySet().parallelStream().filter(d -> d != 0d).count();
369                 }
370                 else
371                 {
372                     // Much harder, and the result is unlikely to be very sparse
373                     nonZeroCount = length
374                             - (int) valueMap.values().parallelStream().filter(d -> scale.toStandardUnit(d) == 0d).count();
375                 }
376                 int[] indices = new int[nonZeroCount];
377                 double[] valuesSI = new double[nonZeroCount];
378                 if (scale.isBaseSIScale())
379                 {
380                     int index = 0;
381                     for (Integer key : valueMap.keySet())
382                     {
383                         double value = valueMap.get(key);
384                         if (0.0 != value)
385                         {
386                             indices[index] = key;
387                             valuesSI[index] = value;
388                             index++;
389                         }
390                     }
391                 }
392                 else
393                 {
394                     Arrays.fill(valuesSI, scale.toStandardUnit(0.0));
395                     int index = 0;
396                     int lastKey = 0;
397                     for (Integer key : valueMap.keySet())
398                     {
399                         for (int i = lastKey; i < key; i++)
400                         {
401                             indices[index++] = i;
402                         }
403                         lastKey = key;
404                         double value = scale.toStandardUnit(valueMap.get(key));
405                         if (0.0 != value)
406                         {
407                             indices[index] = key;
408                             valuesSI[index] = value;
409                             index++;
410                         }
411                         lastKey = key + 1;
412                     }
413                     while (index < indices.length)
414                     {
415                         indices[index++] = lastKey++;
416                     }
417                 }
418                 return new DoubleVectorDataSparse(valuesSI, indices, length);
419             }
420 
421             default:
422                 throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
423         }
424     }
425 
426     /**
427      * Instantiate a DoubleVectorData with the right data type.
428      * @param values SortedMap&lt;Integer,S&gt;; the DoubleScalar values to store
429      * @param length int; the length of the vector to pad with 0 after last entry in map
430      * @param storageType StorageType; the data type to use
431      * @return DoubleVectorData; the DoubleVectorData with the right data type
432      * @throws ValueRuntimeException when values is null, or storageType is null
433      * @param <U> the unit
434      * @param <S> the corresponding scalar type
435      */
436     public static <U extends Unit<U>, S extends DoubleScalarInterface<U, S>> DoubleVectorData instantiateMap(
437             final SortedMap<Integer, S> values, final int length, final StorageType storageType) throws ValueRuntimeException
438     {
439         Throw.whenNull(values, "DoubleVectorData.instantiate: values is null");
440         Throw.when(length < 1, ValueRuntimeException.class, "Length must be > 0");
441         Throw.whenNull(storageType, "DoubleVectorData.instantiate: storageType is null");
442         for (Entry<Integer, S> e : values.entrySet())
443         {
444             Throw.when(e.getKey() < 0 || e.getKey() >= length, ValueRuntimeException.class, "Key in values out of range");
445             Throw.whenNull(e.getValue(), "null value in map");
446         }
447 
448         switch (storageType)
449         {
450             case DENSE:
451             {
452                 double[] valuesSI = new double[length];
453                 values.entrySet().parallelStream().forEach(entry -> valuesSI[entry.getKey()] = entry.getValue().getSI());
454                 return new DoubleVectorDataDense(valuesSI);
455             }
456 
457             case SPARSE:
458             {
459                 int nonZeroCount = (int) values.values().parallelStream().filter(s -> s.getSI() != 0d).count();
460                 int[] indices = new int[nonZeroCount];
461                 double[] valuesSI = new double[nonZeroCount];
462                 int index = 0;
463                 for (Integer key : values.keySet())
464                 {
465                     double value = values.get(key).getSI();
466                     if (0.0 != value)
467                     {
468                         indices[index] = key;
469                         valuesSI[index] = value;
470                         index++;
471                     }
472                 }
473                 return new DoubleVectorDataSparse(valuesSI, indices, length);
474             }
475 
476             default:
477                 throw new ValueRuntimeException("Unknown storage type in DoubleVectorData.instantiate: " + storageType);
478         }
479     }
480 
481     /* ============================================================================================ */
482     /* ==================================== UTILITY FUNCTIONS ===================================== */
483     /* ============================================================================================ */
484 
485     /**
486      * Retrieve the size of the vector.
487      * @return int; the size of the vector
488      */
489     public abstract int size();
490 
491     /**
492      * Return the densely stored equivalent of this data.
493      * @return DoubleVectorDataDense; the dense transformation of this data
494      */
495     public abstract DoubleVectorDataDense toDense();
496 
497     /**
498      * Return the sparsely stored equivalent of this data.
499      * @return DoubleVectorDataSparse; the sparse transformation of this data
500      */
501     public abstract DoubleVectorDataSparse toSparse();
502 
503     /**
504      * Retrieve the SI value of one element of this data.
505      * @param index int; the index to get the value for
506      * @return double; the value at the index
507      */
508     public abstract double getSI(int index);
509 
510     /**
511      * Sets a value at the index in the vector.
512      * @param index int; the index to set the value for
513      * @param valueSI double; the value at the index
514      */
515     public abstract void setSI(int index, double valueSI);
516 
517     /**
518      * Compute and return the sum of all values.
519      * @return double; the sum of the values of all cells
520      */
521     public final double zSum()
522     {
523         return Arrays.stream(this.vectorSI).parallel().sum();
524     }
525 
526     /**
527      * Create and return a dense copy of the data.
528      * @return double[]; a safe copy of VectorSI
529      */
530     public abstract double[] getDenseVectorSI();
531 
532     /**
533      * Check the sizes of this data object and the other data object.
534      * @param other DoubleVectorData; the other data object
535      * @throws ValueRuntimeException if vectors have different lengths
536      */
537     protected void checkSizes(final DoubleVectorData other) throws ValueRuntimeException
538     {
539         if (this.size() != other.size())
540         {
541             throw new ValueRuntimeException("Two data objects used in a DoubleVector operation do not have the same size");
542         }
543     }
544 
545     /* ============================================================================================ */
546     /* ================================== CALCULATION FUNCTIONS =================================== */
547     /* ============================================================================================ */
548 
549     /**
550      * Apply an operation to each cell.
551      * @param doubleFunction DoubleFunction; the operation to apply
552      * @return DoubleVectorData; this (modified) double vector data object
553      */
554     public abstract DoubleVectorData assign(DoubleFunction doubleFunction);
555 
556     /**
557      * Apply a binary operation on a cell by cell basis.
558      * @param doubleFunction2 DoubleFunction2; the binary operation to apply
559      * @param right DoubleVectorData; the right operand for the binary operation
560      * @return DoubleVectorData; this (modified) double vector data object
561      * @throws ValueRuntimeException when the sizes of the vectors do not match
562      */
563     abstract DoubleVectorDatable/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData assign(DoubleFunction2 doubleFunction2, DoubleVectorData right) throws ValueRuntimeException;
564 
565     /**
566      * Add two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
567      * vector is returned. Neither of the two objects is changed.
568      * @param right DoubleVectorData; the other data object to add
569      * @return DoubleVectorData; the sum of this data object and the other data object as a new data object
570      * @throws ValueRuntimeException if vectors have different lengths
571      */
572     public abstract DoubleVectorData/../../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData plus(DoubleVectorData right) throws ValueRuntimeException;
573 
574     /**
575      * Add a vector to this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
576      * @param right DoubleVectorData; the other data object to add
577      * @return DoubleVectorData; this modified double vector data object
578      * @throws ValueRuntimeException if vectors have different lengths
579      */
580     public final DoubleVectorData/DoubleVectorData.html#DoubleVectorData">DoubleVectorData incrementBy(final DoubleVectorData right) throws ValueRuntimeException
581     {
582         return assign(new DoubleFunction2()
583         {
584             @Override
585             public double apply(final double leftValue, final double rightValue)
586             {
587                 return leftValue + rightValue;
588             }
589         }, right);
590     }
591 
592     /**
593      * Subtract two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
594      * vector is returned. Neither of the two objects is changed.
595      * @param right DoubleVectorData; the other data object to subtract
596      * @return DoubleVectorData; the difference of this data object and the other data object as a new data object
597      * @throws ValueRuntimeException if vectors have different lengths
598      */
599     public abstract DoubleVectorData../../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData minus(DoubleVectorData right) throws ValueRuntimeException;
600 
601     /**
602      * Subtract a vector from this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
603      * @param right DoubleVectorData; the other data object to subtract
604      * @return DoubleVectorData; this modified double vector data object
605      * @throws ValueRuntimeException if vectors have different lengths
606      */
607     public final DoubleVectorData/DoubleVectorData.html#DoubleVectorData">DoubleVectorData decrementBy(final DoubleVectorData right) throws ValueRuntimeException
608     {
609         return assign(new DoubleFunction2()
610         {
611             @Override
612             public double apply(final double leftValue, final double rightValue)
613             {
614                 return leftValue - rightValue;
615             }
616         }, right);
617     }
618 
619     /**
620      * Multiply two vectors on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
621      * vector is returned.
622      * @param right DoubleVectorData; the other data object to multiply with
623      * @return DoubleVectorData; a new double vector data store holding the result of the multiplications
624      * @throws ValueRuntimeException if vectors have different lengths
625      */
626     public abstract DoubleVectorData../../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData times(DoubleVectorData right) throws ValueRuntimeException;
627 
628     /**
629      * Multiply a vector with the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the
630      * same.
631      * @param right DoubleVectorData; the other data object to multiply with
632      * @return DoubleVectordata; this modified double vector data store
633      * @throws ValueRuntimeException if vectors have different lengths
634      */
635     public final DoubleVectorDataa/DoubleVectorData.html#DoubleVectorData">DoubleVectorData multiplyBy(final DoubleVectorData right) throws ValueRuntimeException
636     {
637         assign(new DoubleFunction2()
638         {
639             @Override
640             public double apply(final double leftValue, final double rightValue)
641             {
642                 return leftValue * rightValue;
643             }
644         }, right);
645         return this;
646     }
647 
648     /**
649      * Multiply the values of this vector with a number on a cell-by-cell basis.
650      * @param valueSI double; the value to multiply with
651      */
652     public final void multiplyBy(final double valueSI)
653     {
654         assign(new DoubleFunction()
655         {
656             @Override
657             public double apply(final double value)
658             {
659                 return value * valueSI;
660             }
661         });
662     }
663 
664     /**
665      * Divide two vectors on a cell-by-cell basis. If this vector is sparse and <code>right</code> is dense, a sparse vector is
666      * returned, otherwise a dense vector is returned.
667      * @param right DoubleVectorData; the other data object to divide by
668      * @return DoubleVectorData; the ratios of the values of this data object and the other data object
669      * @throws ValueRuntimeException if vectors have different lengths
670      */
671     public abstract DoubleVectorData./../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData divide(DoubleVectorData right) throws ValueRuntimeException;
672 
673     /**
674      * Divide the values of a vector by the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense)
675      * stays the same.
676      * @param right DoubleVectorData; the other data object to divide by
677      * @return DoubleVectorData; this modified double vector data store
678      * @throws ValueRuntimeException if vectors have different lengths
679      */
680     public final DoubleVectorDataata/DoubleVectorData.html#DoubleVectorData">DoubleVectorData divideBy(final DoubleVectorData right) throws ValueRuntimeException
681     {
682         return assign(new DoubleFunction2()
683         {
684             @Override
685             public double apply(final double leftValue, final double rightValue)
686             {
687                 return leftValue / rightValue;
688             }
689         }, right);
690     }
691 
692     /**
693      * Divide the values of this vector by a number on a cell-by-cell basis.
694      * @param valueSI double; the value to multiply with
695      */
696     public final void divideBy(final double valueSI)
697     {
698         assign(new DoubleFunction()
699         {
700             @Override
701             public double apply(final double value)
702             {
703                 return value / valueSI;
704             }
705         });
706     }
707 
708     /* ============================================================================================ */
709     /* =============================== EQUALS, HASHCODE, TOSTRING ================================= */
710     /* ============================================================================================ */
711 
712     /** {@inheritDoc} */
713     @Override
714     public int hashCode()
715     {
716         final int prime = 31;
717         int result = 1;
718         result = prime * result + this.size();
719         for (int index = 0; index < this.size(); index++)
720         {
721             long bits = Double.doubleToLongBits(getSI(index));
722             result = 31 * result + (int) (bits ^ (bits >>> 32));
723         }
724         return result;
725     }
726 
727     /**
728      * Compare contents of a dense and a sparse vector.
729      * @param dm DoubleVectorDataDense; the dense vector
730      * @param sm DoubleVectorDataSparse; the sparse vector
731      * @return boolean; true if the contents are equal
732      */
733     protected boolean compareDenseVectorWithSparseVector(final DoubleVectorDataDense dm, final DoubleVectorDataSparse sm)
734     {
735         for (int index = 0; index < dm.size(); index++)
736         {
737             if (dm.getSI(index) != sm.getSI(index))
738             {
739                 return false;
740             }
741         }
742         return true;
743     }
744 
745     /** {@inheritDoc} */
746     @Override
747     @SuppressWarnings("checkstyle:needbraces")
748     public boolean equals(final Object obj)
749     {
750         if (this == obj)
751             return true;
752         if (obj == null)
753             return false;
754         if (!(obj instanceof DoubleVectorData))
755             return false;
756         DoubleVectorData../../../org/djunits/value/vdouble/vector/data/DoubleVectorData.html#DoubleVectorData">DoubleVectorData other = (DoubleVectorData) obj;
757         if (this.size() != other.size())
758             return false;
759         if (other instanceof DoubleVectorDataSparse && this instanceof DoubleVectorDataDense)
760         {
761             return compareDenseVectorWithSparseVector((DoubleVectorDataDense) this, (DoubleVectorDataSparse) other);
762         }
763         else if (other instanceof DoubleVectorDataDense && this instanceof DoubleVectorDataSparse)
764         {
765             return compareDenseVectorWithSparseVector((DoubleVectorDataDense) other, (DoubleVectorDataSparse) this);
766         }
767         // Both are dense (both sparse is handled in DoubleVectorDataSparse class)
768         return Arrays.equals(this.vectorSI, other.vectorSI);
769     }
770 
771     /** {@inheritDoc} */
772     @Override
773     public String toString()
774     {
775         return "DoubleVectorData [storageType=" + getStorageType() + ", vectorSI=" + Arrays.toString(this.vectorSI) + "]";
776     }
777 
778 }