View Javadoc
1   package org.djunits.value.vfloat.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.vfloat.function.FloatFunction;
17  import org.djunits.value.vfloat.function.FloatFunction2;
18  import org.djunits.value.vfloat.scalar.base.FloatScalarInterface;
19  
20  /**
21   * Stores the data for a FloatVector 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 FloatVectorData extends AbstractStorage<FloatVectorData> 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 float[] vectorSI;
37  
38      /**
39       * Construct a new FloatVectorData object.
40       * @param storageType StorageType; the data type.
41       */
42      FloatVectorData(final StorageType storageType)
43      {
44          super(storageType);
45      }
46  
47      /* ============================================================================================ */
48      /* ====================================== INSTANTIATION ======================================= */
49      /* ============================================================================================ */
50  
51      /**
52       * Instantiate a FloatVectorData with the right data type.
53       * @param values float[]; the (SI) values to store
54       * @param scale Scale; the scale of the unit to use for conversion to SI
55       * @param storageType StorageType; the data type to use
56       * @return FloatVectorData; the FloatVectorData with the right data type
57       * @throws ValueRuntimeException when values are null, or storageType is null
58       */
59      public static FloatVectorData instantiate(final float[] values, final Scale scale, final StorageType storageType)
60              throws ValueRuntimeException
61      {
62          Throw.whenNull(values, "FloatVectorData.instantiate: float[] values is null");
63          Throw.whenNull(scale, "FloatVectorData.instantiate: scale is null");
64          Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
65          Throw.when(values.length == 0, ValueRuntimeException.class,
66                  "FloatVectorData.instantiate: float[] values wrong: values.length == 0");
67  
68          float[] valuesSI = new float[values.length];
69          IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values[i]));
70  
71          switch (storageType)
72          {
73              case DENSE:
74                  return new FloatVectorDataDense(valuesSI);
75  
76              case SPARSE:
77                  return FloatVectorDataSparse.instantiate(valuesSI);
78  
79              default:
80                  throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
81          }
82      }
83  
84      /**
85       * Instantiate a FloatVectorData with the right data type.
86       * @param values List&lt;Float&gt;; the values to store
87       * @param scale Scale; the scale of the unit to use for conversion to SI
88       * @param storageType StorageType; the data type to use
89       * @return FloatVectorData; the FloatVectorData with the right data type
90       * @throws ValueRuntimeException when list is null, or storageType is null
91       */
92      public static FloatVectorData instantiate(final List<Float> values, final Scale scale, final StorageType storageType)
93              throws ValueRuntimeException
94      {
95          Throw.whenNull(values, "FloatVectorData.instantiate: float[] values is null");
96          Throw.whenNull(scale, "FloatVectorData.instantiate: scale is null");
97          Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
98          Throw.when(values.size() == 0, ValueRuntimeException.class, "FloatVectorData.instantiate: values.size() == 0");
99          Throw.when(values.parallelStream().filter(d -> d == null).count() > 0, NullPointerException.class,
100                 "values contains one or more null values");
101 
102         float[] valuesSI = new float[values.size()];
103         IntStream.range(0, values.size()).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values.get(i)));
104 
105         switch (storageType)
106         {
107             case DENSE:
108                 return new FloatVectorDataDense(valuesSI);
109 
110             case SPARSE:
111                 return FloatVectorDataSparse.instantiate(valuesSI);
112 
113             default:
114                 throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
115         }
116     }
117 
118     /**
119      * Instantiate a FloatVectorData with the right data type.
120      * @param values FloatScalarInterface[]; the values to store
121      * @param storageType StorageType; the data type to use
122      * @return FloatVectorData; the FloatVectorData with the right data type
123      * @throws ValueRuntimeException when values is null, or storageType is null
124      * @param <U> the unit type
125      * @param <S> the corresponding scalar type
126      */
127     public static <U extends Unit<U>, S extends FloatScalarInterface<U, S>> FloatVectorData instantiate(final S[] values,
128             final StorageType storageType) throws ValueRuntimeException
129     {
130         Throw.whenNull(values, "FloatVectorData.instantiate: double[] values is null");
131         Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
132         Throw.when(values.length == 0, ValueRuntimeException.class,
133                 "FloatVectorData.instantiate: FloatScalar[] values wrong: values.length == 0");
134         for (S s : values)
135         {
136             Throw.whenNull(s, "null value in values");
137         }
138 
139         float[] valuesSI = new float[values.length];
140         IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = values[i].getSI());
141 
142         switch (storageType)
143         {
144             case DENSE:
145                 return new FloatVectorDataDense(valuesSI);
146 
147             case SPARSE:
148                 return FloatVectorDataSparse.instantiate(valuesSI);
149 
150             default:
151                 throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
152         }
153     }
154 
155     /**
156      * Instantiate a FloatVectorData with the right data type.
157      * @param valueList List&lt;? extends FloatScalarInterface&gt;; the FloatScalar values to store
158      * @param storageType StorageType; the data type to use
159      * @return FloatVectorData; the FloatVectorData with the right data type
160      * @throws ValueRuntimeException when values is null, or storageType is null
161      * @param <U> the unit type
162      * @param <S> the corresponding scalar type
163      */
164     public static <U extends Unit<U>, S extends FloatScalarInterface<U, S>> FloatVectorData instantiateList(
165             final List<S> valueList, final StorageType storageType) throws ValueRuntimeException
166     {
167         Throw.whenNull(valueList, "FloatVectorData.instantiate: valueList is null");
168         Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
169         Throw.when(valueList.size() == 0, ValueRuntimeException.class, "FloatVectorData.instantiate: valueList.size() == 0");
170         for (S s : valueList)
171         {
172             Throw.whenNull(s, "null value in valueList");
173         }
174 
175         float[] valuesSI = new float[valueList.size()];
176         IntStream.range(0, valueList.size()).parallel().forEach(i -> valuesSI[i] = valueList.get(i).getSI());
177 
178         switch (storageType)
179         {
180             case DENSE:
181                 return new FloatVectorDataDense(valuesSI);
182 
183             case SPARSE:
184                 return FloatVectorDataSparse.instantiate(valuesSI);
185 
186             default:
187                 throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
188         }
189     }
190 
191     /**
192      * Instantiate a FloatVectorData with the right data type.
193      * @param valueMap SortedMap&lt;Integer,Float&gt;; the FloatScalar values to store
194      * @param length int; the length of the vector to pad with 0 after last entry in map
195      * @param scale Scale; the scale of the unit to use for conversion to SI
196      * @param storageType StorageType; the data type to use
197      * @return FloatVectorData; the FloatVectorData with the right data type
198      * @throws ValueRuntimeException when values is null, or storageType is null
199      */
200     public static FloatVectorData instantiate(final SortedMap<Integer, Float> valueMap, final int length, final Scale scale,
201             final StorageType storageType) throws ValueRuntimeException
202     {
203         Throw.whenNull(valueMap, "FloatVectorData.instantiate: values is null");
204         Throw.when(length < 1, ValueRuntimeException.class, "Length must be > 0");
205         Throw.whenNull(scale, "FloatVectorData.instantiate: scale is null");
206         Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
207         for (Integer key : valueMap.keySet())
208         {
209             Throw.when(key < 0 || key >= length, ValueRuntimeException.class, "Key in values out of range");
210         }
211 
212         switch (storageType)
213         {
214             case DENSE:
215             {
216                 float[] valuesSI = new float[length];
217                 if (scale.isBaseSIScale())
218                 {
219                     valueMap.entrySet().parallelStream().forEach(entry -> valuesSI[entry.getKey()] = entry.getValue());
220                 }
221                 else
222                 {
223                     Arrays.fill(valuesSI, (float) scale.toStandardUnit(0.0));
224                     valueMap.entrySet().parallelStream()
225                             .forEach(entry -> valuesSI[entry.getKey()] = (float) scale.toStandardUnit(entry.getValue()));
226                 }
227                 return new FloatVectorDataDense(valuesSI);
228             }
229 
230             case SPARSE:
231             {
232                 int nonZeroCount;
233                 if (scale.isBaseSIScale())
234                 {
235                     nonZeroCount = (int) valueMap.keySet().parallelStream().filter(d -> d != 0d).count();
236                 }
237                 else
238                 {
239                     // Much harder, and the result is unlikely to be very sparse
240                     nonZeroCount = length
241                             - (int) valueMap.values().parallelStream().filter(d -> scale.toStandardUnit(d) == 0d).count();
242                 }
243                 int[] indices = new int[nonZeroCount];
244                 float[] valuesSI = new float[nonZeroCount];
245                 if (scale.isBaseSIScale())
246                 {
247                     int index = 0;
248                     for (Integer key : valueMap.keySet())
249                     {
250                         float value = valueMap.get(key);
251                         if (0.0 != value)
252                         {
253                             indices[index] = key;
254                             valuesSI[index] = value;
255                             index++;
256                         }
257                     }
258                 }
259                 else
260                 {
261                     Arrays.fill(valuesSI, (float) scale.toStandardUnit(0.0));
262                     int index = 0;
263                     int lastKey = 0;
264                     for (Integer key : valueMap.keySet())
265                     {
266                         for (int i = lastKey; i < key; i++)
267                         {
268                             indices[index++] = i;
269                         }
270                         lastKey = key;
271                         float value = (float) scale.toStandardUnit(valueMap.get(key));
272                         if (0.0 != value)
273                         {
274                             indices[index] = key;
275                             valuesSI[index] = value;
276                             index++;
277                         }
278                         lastKey = key + 1;
279                     }
280                     while (index < indices.length)
281                     {
282                         indices[index++] = lastKey++;
283                     }
284                 }
285                 return new FloatVectorDataSparse(valuesSI, indices, length);
286             }
287 
288             default:
289                 throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
290         }
291     }
292 
293     /**
294      * Instantiate a FloatVectorData with the right data type.
295      * @param values SortedMap&lt;Integer,S&gt;; the FloatScalar values to store
296      * @param length int; the length of the vector to pad with 0 after last entry in map
297      * @param storageType StorageType; the data type to use
298      * @return FloatVectorData; the FloatVectorData with the right data type
299      * @throws ValueRuntimeException when values is null, or storageType is null
300      * @param <U> the unit type
301      * @param <S> the scalar type to use
302      */
303     public static <U extends Unit<U>, S extends FloatScalarInterface<U, S>> FloatVectorData instantiateMap(
304             final SortedMap<Integer, S> values, final int length, final StorageType storageType) throws ValueRuntimeException
305     {
306         Throw.whenNull(values, "FloatVectorData.instantiate: values is null");
307         Throw.when(length < 1, ValueRuntimeException.class, "Length must be > 0");
308         Throw.whenNull(storageType, "FloatVectorData.instantiate: storageType is null");
309         for (Entry<Integer, S> e : values.entrySet())
310         {
311             Throw.when(e.getKey() < 0 || e.getKey() >= length, ValueRuntimeException.class, "Key in values out of range");
312             Throw.whenNull(e.getValue(), "null value in map");
313         }
314 
315         switch (storageType)
316         {
317             case DENSE:
318             {
319                 float[] valuesSI = new float[length];
320                 values.entrySet().parallelStream().forEach(entry -> valuesSI[entry.getKey()] = entry.getValue().getSI());
321                 return new FloatVectorDataDense(valuesSI);
322             }
323 
324             case SPARSE:
325             {
326                 int nonZeroCount = (int) values.values().parallelStream().filter(s -> s.getSI() != 0f).count();
327                 int[] indices = new int[nonZeroCount];
328                 float[] valuesSI = new float[nonZeroCount];
329                 int index = 0;
330                 for (Integer key : values.keySet())
331                 {
332                     float value = values.get(key).getSI();
333                     if (0.0f != value)
334                     {
335                         indices[index] = key;
336                         valuesSI[index] = value;
337                         index++;
338                     }
339                 }
340                 return new FloatVectorDataSparse(valuesSI, indices, length);
341             }
342 
343             default:
344                 throw new ValueRuntimeException("Unknown storage type in FloatVectorData.instantiate: " + storageType);
345         }
346     }
347 
348     /* ============================================================================================ */
349     /* ==================================== UTILITY FUNCTIONS ===================================== */
350     /* ============================================================================================ */
351 
352     /**
353      * Retrieve the size of the vector.
354      * @return int; the size of the vector
355      */
356     public abstract int size();
357 
358     /**
359      * Return the densely stored equivalent of this data.
360      * @return FloatVectorDataDense; the dense transformation of this data
361      */
362     public abstract FloatVectorDataDense toDense();
363 
364     /**
365      * Return the sparsely stored equivalent of this data.
366      * @return FloatVectorDataSparse; the sparse transformation of this data
367      */
368     public abstract FloatVectorDataSparse toSparse();
369 
370     /**
371      * Retrieve the SI value of one element of this data.
372      * @param index int; the index to get the value for
373      * @return the value at the index
374      */
375     public abstract float getSI(int index);
376 
377     /**
378      * Sets a value at the index in the vector.
379      * @param index int; the index to set the value for
380      * @param valueSI float; the value at the index
381      */
382     public abstract void setSI(int index, float valueSI);
383 
384     /**
385      * Compute and return the sum of all values.
386      * @return double; the sum of the values of all cells
387      */
388     public final float zSum()
389     {
390         // this does not copy the data. See http://stackoverflow.com/questions/23106093/how-to-get-a-stream-from-a-float
391         return (float) IntStream.range(0, this.vectorSI.length).parallel().mapToDouble(i -> this.vectorSI[i]).sum();
392     }
393 
394     /**
395      * Create and return a dense copy of the data.
396      * @return float[]; a safe copy of VectorSI
397      */
398     public abstract float[] getDenseVectorSI();
399 
400     /**
401      * Check the sizes of this data object and the other data object.
402      * @param other FloatVectorData; the other data object
403      * @throws ValueRuntimeException if vectors have different lengths
404      */
405     protected void checkSizes(final FloatVectorData other) throws ValueRuntimeException
406     {
407         if (this.size() != other.size())
408         {
409             throw new ValueRuntimeException("Two data objects used in a FloatVector operation do not have the same size");
410         }
411     }
412 
413     /* ============================================================================================ */
414     /* ================================== CALCULATION FUNCTIONS =================================== */
415     /* ============================================================================================ */
416 
417     /**
418      * Apply an operation to each cell.
419      * @param floatFunction FloatFunction; the operation to apply
420      * @return FloatVectorData; this (modified) float vector data object
421      */
422     public abstract FloatVectorData assign(FloatFunction floatFunction);
423 
424     /**
425      * Apply a binary operation on a cell by cell basis.
426      * @param floatFunction2 FloatFunction2; the binary operation to apply
427      * @param right FloatVectorData; the right operand for the binary operation
428      * @return DoubleMatrixData; this (modified) float vector data object
429      * @throws ValueRuntimeException when the sizes of the vectors do not match
430      */
431     abstract FloatVectorDatafloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData assign(FloatFunction2 floatFunction2, FloatVectorData right) throws ValueRuntimeException;
432 
433     /**
434      * Add two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
435      * vector is returned.
436      * @param right FloatVectorData; the other data object to add
437      * @return FloatVectorData; the sum of this data object and the other data object
438      * @throws ValueRuntimeException if vectors have different lengths
439      */
440     public abstract FloatVectorData./../../../../org/djunits/value/vfloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData plus(FloatVectorData right) throws ValueRuntimeException;
441 
442     /**
443      * Add a vector to this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
444      * @param right FloatVectorData; the other data object to add
445      * @return FloatVectorData; this modified float vector data object
446      * @throws ValueRuntimeException if vectors have different lengths
447      */
448     public final FloatVectorData/FloatVectorData.html#FloatVectorData">FloatVectorData incrementBy(final FloatVectorData right) throws ValueRuntimeException
449     {
450         return assign(new FloatFunction2()
451         {
452             @Override
453             public float apply(final float leftValue, final float rightValue)
454             {
455                 return leftValue + rightValue;
456             }
457         }, right);
458     }
459 
460     /**
461      * Subtract two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
462      * vector is returned.
463      * @param right FloatVectorData; the other data object to subtract
464      * @return FloatVectorData; the difference of this data object and the other data object
465      * @throws ValueRuntimeException if vectors have different lengths
466      */
467     public abstract FloatVectorData/../../../../org/djunits/value/vfloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData minus(FloatVectorData right) throws ValueRuntimeException;
468 
469     /**
470      * Subtract a vector from this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
471      * @param right FloatVectorData; the other data object to subtract
472      * @return FloatVectorData; this modified float vector data object
473      * @throws ValueRuntimeException if vectors have different lengths
474      */
475     public final FloatVectorData/FloatVectorData.html#FloatVectorData">FloatVectorData decrementBy(final FloatVectorData right) throws ValueRuntimeException
476     {
477         return assign(new FloatFunction2()
478         {
479             @Override
480             public float apply(final float leftValue, final float rightValue)
481             {
482                 return leftValue - rightValue;
483             }
484         }, right);
485     }
486 
487     /**
488      * Multiply two vector on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
489      * vector is returned.
490      * @param right FloatVectorData; the other data object to multiply with
491      * @return FloatVectorData; a new double vector data store holding the result of the multiplications
492      * @throws ValueRuntimeException if vectors have different lengths
493      */
494     public abstract FloatVectorData/../../../../org/djunits/value/vfloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData times(FloatVectorData right) throws ValueRuntimeException;
495 
496     /**
497      * Multiply a vector with the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the
498      * same.
499      * @param right FloatVectorData; the other data object to multiply with
500      * @return FloatVectorData; this modified float vector data store
501      * @throws ValueRuntimeException if vectors have different lengths
502      */
503     public final FloatVectorDataa/FloatVectorData.html#FloatVectorData">FloatVectorData multiplyBy(final FloatVectorData right) throws ValueRuntimeException
504     {
505         assign(new FloatFunction2()
506         {
507             @Override
508             public float apply(final float leftValue, final float rightValue)
509             {
510                 return leftValue * rightValue;
511             }
512         }, right);
513         return this;
514     }
515 
516     /**
517      * Divide two vectors on a cell-by-cell basis. If this vector is sparse and <code>right</code> is dense, a sparse vector is
518      * returned, otherwise a dense vector is returned.
519      * @param right FloatVectorData; the other data object to divide by
520      * @return FloatVectorData; the ratios of the values of this data object and the other data object
521      * @throws ValueRuntimeException if vectors have different lengths
522      */
523     public abstract FloatVectorData../../../../org/djunits/value/vfloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData divide(FloatVectorData right) throws ValueRuntimeException;
524 
525     /**
526      * Divide the values of a vector by the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense)
527      * stays the same.
528      * @param right FloatVectorData; the other data object to divide by
529      * @return FloatVectorData; this modified float vector data store
530      * @throws ValueRuntimeException if vectors have different lengths
531      */
532     public final FloatVectorDataata/FloatVectorData.html#FloatVectorData">FloatVectorData divideBy(final FloatVectorData right) throws ValueRuntimeException
533     {
534         return assign(new FloatFunction2()
535         {
536             @Override
537             public float apply(final float leftValue, final float rightValue)
538             {
539                 return leftValue / rightValue;
540             }
541         }, right);
542     }
543 
544     /* ============================================================================================ */
545     /* =============================== EQUALS, HASHCODE, TOSTRING ================================= */
546     /* ============================================================================================ */
547 
548     /** {@inheritDoc} */
549     @Override
550     public int hashCode()
551     {
552         final int prime = 31;
553         int result = 1;
554         result = prime * result + this.size();
555         for (int index = 0; index < this.size(); index++)
556         {
557             result = 31 * result + Float.floatToIntBits(getSI(index));
558         }
559         return result;
560     }
561 
562     /**
563      * Compare contents of a dense and a sparse vector.
564      * @param dm FloatVectorDataDense; the dense vector
565      * @param sm FloatVectorDataSparse; the sparse vector
566      * @return boolean; true if the contents are equal
567      */
568     protected boolean compareDenseVectorWithSparseVector(final FloatVectorDataDense dm, final FloatVectorDataSparse sm)
569     {
570         for (int index = 0; index < dm.size(); index++)
571         {
572             if (dm.getSI(index) != sm.getSI(index))
573             {
574                 return false;
575             }
576         }
577         return true;
578     }
579 
580     /** {@inheritDoc} */
581     @Override
582     @SuppressWarnings("checkstyle:needbraces")
583     public boolean equals(final Object obj)
584     {
585         if (this == obj)
586             return true;
587         if (obj == null)
588             return false;
589         if (!(obj instanceof FloatVectorData))
590             return false;
591         FloatVectorData/../../../org/djunits/value/vfloat/vector/data/FloatVectorData.html#FloatVectorData">FloatVectorData other = (FloatVectorData) obj;
592         if (this.size() != other.size())
593             return false;
594         if (other instanceof FloatVectorDataSparse && this instanceof FloatVectorDataDense)
595         {
596             return compareDenseVectorWithSparseVector((FloatVectorDataDense) this, (FloatVectorDataSparse) other);
597         }
598         else if (other instanceof FloatVectorDataDense && this instanceof FloatVectorDataSparse)
599         {
600             return compareDenseVectorWithSparseVector((FloatVectorDataDense) other, (FloatVectorDataSparse) this);
601         }
602         // Both are dense (both sparse is handled in FloatVectorDataSparse class)
603         return Arrays.equals(this.vectorSI, other.vectorSI);
604     }
605 
606     /** {@inheritDoc} */
607     @Override
608     public String toString()
609     {
610         return "FloatVectorData [storageType=" + getStorageType() + ", vectorSI=" + Arrays.toString(this.vectorSI) + "]";
611     }
612 
613 }