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