View Javadoc
1   package org.djunits.value.vfloat.vector;
2   
3   import java.io.Serializable;
4   import java.util.Arrays;
5   import java.util.List;
6   import java.util.SortedMap;
7   import java.util.stream.IntStream;
8   
9   import org.djunits.unit.scale.Scale;
10  import org.djunits.value.StorageType;
11  import org.djunits.value.ValueException;
12  import org.djunits.value.vfloat.scalar.FloatScalarInterface;
13  
14  /**
15   * Stores the data for a FloatVector and carries out basic operations.
16   * <p>
17   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
19   * </p>
20   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
21   * initial version Oct 3, 2015 <br>
22   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
24   */
25  abstract class FloatVectorData implements Serializable
26  {
27      /** */
28      private static final long serialVersionUID = 1L;
29  
30      /** the internal storage of the Vector; can be sparse or dense. */
31      @SuppressWarnings("checkstyle:visibilitymodifier")
32      protected float[] vectorSI;
33  
34      /** the data type. */
35      private final StorageType storageType;
36  
37      /**
38       * @param storageType StorageType; the data type.
39       */
40      FloatVectorData(final StorageType storageType)
41      {
42          super();
43          this.storageType = storageType;
44      }
45  
46      /** ============================================================================================ */
47      /** ====================================== INSTANTIATION ======================================= */
48      /** ============================================================================================ */
49  
50      /**
51       * Instantiate a FloatVectorData with the right data type.
52       * @param values float[]; the (SI) values to store
53       * @param scale Scale; the scale of the unit to use for conversion to SI
54       * @param storageType StorageType; the data type to use
55       * @return the FloatVectorData with the right data type
56       * @throws ValueException when values are null, or storageType is null
57       */
58      public static FloatVectorData instantiate(final float[] values, final Scale scale, final StorageType storageType)
59              throws ValueException
60      {
61          if (values == null)
62          {
63              throw new ValueException("FloatVectorData.instantiate: float[] values is null");
64          }
65  
66          float[] valuesSI = new float[values.length];
67          IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values[i]));
68  
69          switch (storageType)
70          {
71              case DENSE:
72                  return new FloatVectorDataDense(valuesSI);
73  
74              case SPARSE:
75                  return FloatVectorDataSparse.instantiate(valuesSI);
76  
77              default:
78                  throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
79          }
80      }
81  
82      /**
83       * Instantiate a FloatVectorData with the right data type.
84       * @param values List&lt;Float&gt;; the values to store
85       * @param scale Scale; the scale of the unit to use for conversion to SI
86       * @param storageType StorageType; the data type to use
87       * @return the FloatVectorData with the right data type
88       * @throws ValueException when list is null, or storageType is null
89       */
90      public static FloatVectorData instantiate(final List<Float> values, final Scale scale, final StorageType storageType)
91              throws ValueException
92      {
93          if (values == null)
94          {
95              throw new ValueException("FloatVectorData.instantiate: List<Float> values is null");
96          }
97  
98          float[] valuesSI = new float[values.size()];
99          IntStream.range(0, values.size()).parallel().forEach(i -> valuesSI[i] = (float) scale.toStandardUnit(values.get(i)));
100 
101         switch (storageType)
102         {
103             case DENSE:
104                 return new FloatVectorDataDense(valuesSI);
105 
106             case SPARSE:
107                 return FloatVectorDataSparse.instantiate(valuesSI);
108 
109             default:
110                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
111         }
112     }
113 
114     /**
115      * Instantiate a FloatVectorData with the right data type.
116      * @param values FloatScalarInterface[]; the values to store
117      * @param storageType StorageType; the data type to use
118      * @return the FloatVectorData with the right data type
119      * @throws ValueException when values is null, or storageType is null
120      */
121     public static FloatVectorData instantiate(final FloatScalarInterface[] values, final StorageType storageType)
122             throws ValueException
123     {
124         if (values == null)
125         {
126             throw new ValueException("FloatVectorData.instantiate: FloatScalar[] values is null");
127         }
128 
129         float[] valuesSI = new float[values.length];
130         IntStream.range(0, values.length).parallel().forEach(i -> valuesSI[i] = values[i].getSI());
131 
132         switch (storageType)
133         {
134             case DENSE:
135                 return new FloatVectorDataDense(valuesSI);
136 
137             case SPARSE:
138                 return FloatVectorDataSparse.instantiate(valuesSI);
139 
140             default:
141                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
142         }
143     }
144 
145     /**
146      * Instantiate a FloatVectorData with the right data type.
147      * @param values List&lt;? extends FloatScalarInterface&gt;; the FloatScalar values to store
148      * @param storageType StorageType; the data type to use
149      * @return the FloatVectorData with the right data type
150      * @throws ValueException when values is null, or storageType is null
151      */
152     public static FloatVectorData instantiateLD(final List<? extends FloatScalarInterface> values,
153             final StorageType storageType) throws ValueException
154     {
155         if (values == null)
156         {
157             throw new ValueException("FloatVectorData.instantiate: values list is null");
158         }
159 
160         float[] valuesSI = new float[values.size()];
161         IntStream.range(0, values.size()).parallel().forEach(i -> valuesSI[i] = values.get(i).getSI());
162 
163         switch (storageType)
164         {
165             case DENSE:
166                 return new FloatVectorDataDense(valuesSI);
167 
168             case SPARSE:
169                 return FloatVectorDataSparse.instantiate(valuesSI);
170 
171             default:
172                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
173         }
174     }
175 
176     /**
177      * Instantiate a FloatVectorData with the right data type.
178      * @param values SortedMap&lt;Integer,S&gt;; the FloatScalar values to store
179      * @param length int; the length of the vector to pad with 0 after last entry in map
180      * @param storageType StorageType; the data type to use
181      * @param <S> the scalar type to use
182      * @return the FloatVectorData with the right data type
183      * @throws ValueException when values is null, or storageType is null
184      */
185     public static <S extends FloatScalarInterface> FloatVectorData instantiateMD(final SortedMap<Integer, S> values,
186             final int length, final StorageType storageType) throws ValueException
187     {
188         if (values == null)
189         {
190             throw new ValueException("FloatVectorData.instantiate: values map is null");
191         }
192 
193         switch (storageType)
194         {
195             case DENSE:
196             {
197                 float[] valuesSI = new float[length];
198                 values.keySet().parallelStream().forEach(index -> valuesSI[index] = values.get(index).getSI());
199                 return new FloatVectorDataDense(valuesSI);
200             }
201 
202             case SPARSE:
203             {
204                 int[] indices = values.keySet().parallelStream().mapToInt(i -> i).toArray();
205                 float[] valuesSI = new float[values.size()];
206                 IntStream.range(0, values.size()).parallel()
207                         .forEach(index -> valuesSI[index] = values.get(indices[index]).getSI());
208                 return new FloatVectorDataSparse(valuesSI, indices, length);
209             }
210 
211             default:
212                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
213         }
214     }
215 
216     /**
217      * Instantiate a FloatVectorData with the right data type.
218      * @param values SortedMap&lt;Integer,Float&gt;; the FloatScalar values to store
219      * @param length int; the length of the vector to pad with 0 after last entry in map
220      * @param scale Scale; the scale of the unit to use for conversion to SI
221      * @param storageType StorageType; the data type to use
222      * @return the FloatVectorData with the right data type
223      * @throws ValueException when values is null, or storageType is null
224      */
225     public static FloatVectorData instantiate(final SortedMap<Integer, Float> values, final int length, final Scale scale,
226             final StorageType storageType) throws ValueException
227     {
228         if (values == null)
229         {
230             throw new ValueException("FloatVectorData.instantiate: values map is null");
231         }
232 
233         switch (storageType)
234         {
235             case DENSE:
236             {
237                 float[] valuesSI = new float[length];
238                 values.keySet().parallelStream()
239                         .forEach(index -> valuesSI[index] = (float) scale.toStandardUnit(values.get(index)));
240                 return new FloatVectorDataDense(valuesSI);
241             }
242 
243             case SPARSE:
244             {
245                 int[] indices = values.keySet().parallelStream().mapToInt(i -> i).toArray();
246                 float[] valuesSI = new float[values.size()];
247                 IntStream.range(0, values.size()).parallel()
248                         .forEach(index -> valuesSI[index] = (float) scale.toStandardUnit(values.get(indices[index])));
249                 return new FloatVectorDataSparse(valuesSI, indices, length);
250             }
251 
252             default:
253                 throw new ValueException("Unknown data type in FloatVectorData.instantiate: " + storageType);
254         }
255     }
256 
257     /** ============================================================================================ */
258     /** ==================================== UTILITY FUNCTIONS ===================================== */
259     /** ============================================================================================ */
260 
261     /**
262      * Return the StorageType (DENSE, SPARSE, etc.) for the stored Vector.
263      * @return the StorageType (DENSE, SPARSE, etc.) for the stored Vector
264      */
265     public final StorageType getStorageType()
266     {
267         return this.storageType;
268     }
269 
270     /**
271      * @return the size of the vector
272      */
273     public abstract int size();
274 
275     /**
276      * @return whether data type is sparse.
277      */
278     public boolean isSparse()
279     {
280         return this.storageType.equals(StorageType.SPARSE);
281     }
282 
283     /**
284      * @return the sparse transformation of this data
285      */
286     public FloatVectorDataSparse toSparse()
287     {
288         return isSparse() ? (FloatVectorDataSparse) this : ((FloatVectorDataDense) this).toSparse();
289     }
290 
291     /**
292      * @return whether data type is dense.
293      */
294     public boolean isDense()
295     {
296         return this.storageType.equals(StorageType.DENSE);
297     }
298 
299     /**
300      * @return the dense transformation of this data
301      */
302     public FloatVectorDataDense toDense()
303     {
304         return isDense() ? (FloatVectorDataDense) this : ((FloatVectorDataSparse) this).toDense();
305     }
306 
307     /**
308      * @param index int; the index to get the value for
309      * @return the value at the index
310      */
311     public abstract float getSI(int index);
312 
313     /**
314      * Sets a value at the index in the vector.
315      * @param index int; the index to set the value for
316      * @param valueSI float; the value at the index
317      */
318     public abstract void setSI(int index, float valueSI);
319 
320     /**
321      * @return the number of non-zero cells.
322      */
323     public final int cardinality()
324     {
325         // this does not copy the data. See http://stackoverflow.com/questions/23106093/how-to-get-a-stream-from-a-float
326         return (int) IntStream.range(0, this.vectorSI.length).parallel().mapToDouble(i -> this.vectorSI[i])
327                 .filter(d -> d != 0.0).count();
328     }
329 
330     /**
331      * @return the sum of the values of all cells.
332      */
333     public final float zSum()
334     {
335         // this does not copy the data. See http://stackoverflow.com/questions/23106093/how-to-get-a-stream-from-a-float
336         return (float) IntStream.range(0, this.vectorSI.length).parallel().mapToDouble(i -> this.vectorSI[i]).sum();
337     }
338 
339     /**
340      * @return a deep copy of the data.
341      */
342     public abstract FloatVectorData copy();
343 
344     /**
345      * @return a safe copy of VectorSI
346      */
347     public abstract float[] getDenseVectorSI();
348 
349     /**
350      * Check the sizes of this data object and the other data object.
351      * @param other FloatVectorData; the other data object
352      * @throws ValueException if vectors have different lengths
353      */
354     private void checkSizes(final FloatVectorData other) throws ValueException
355     {
356         if (this.size() != other.size())
357         {
358             throw new ValueException("Two data objects used in a FloatVector operation do not have the same size");
359         }
360     }
361 
362     /** ============================================================================================ */
363     /** ================================== CALCULATION FUNCTIONS =================================== */
364     /** ============================================================================================ */
365 
366     /**
367      * Add two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
368      * vector is returned.
369      * @param right FloatVectorData; the other data object to add
370      * @return the sum of this data object and the other data object
371      * @throws ValueException if vectors have different lengths
372      */
373     public FloatVectorData plus(final FloatVectorData right) throws ValueException
374     {
375         checkSizes(right);
376         float[] dv = new float[size()];
377         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) + right.getSI(i));
378         if (this instanceof FloatVectorDataSparse && right instanceof FloatVectorDataSparse)
379         {
380             return new FloatVectorDataDense(dv).toSparse();
381         }
382         return new FloatVectorDataDense(dv);
383     }
384 
385     /**
386      * Add a vector to this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
387      * @param right FloatVectorData; the other data object to add
388      * @throws ValueException if vectors have different lengths
389      */
390     public abstract void incrementBy(FloatVectorData right) throws ValueException;
391 
392     /**
393      * Add a number to this vector on a cell-by-cell basis.
394      * @param valueSI float; the value to add
395      */
396     public void incrementBy(final float valueSI)
397     {
398         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] += valueSI);
399     }
400 
401     /**
402      * Subtract two vectors on a cell-by-cell basis. If both vectors are sparse, a sparse vector is returned, otherwise a dense
403      * vector is returned.
404      * @param right FloatVectorData; the other data object to subtract
405      * @return the sum of this data object and the other data object
406      * @throws ValueException if vectors have different lengths
407      */
408     public FloatVectorData minus(final FloatVectorData right) throws ValueException
409     {
410         checkSizes(right);
411         float[] dv = new float[size()];
412         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) - right.getSI(i));
413         if (this instanceof FloatVectorDataSparse && right instanceof FloatVectorDataSparse)
414         {
415             return new FloatVectorDataDense(dv).toSparse();
416         }
417         return new FloatVectorDataDense(dv);
418     }
419 
420     /**
421      * Subtract a vector from this vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the same.
422      * @param right FloatVectorData; the other data object to subtract
423      * @throws ValueException if vectors have different lengths
424      */
425     public abstract void decrementBy(FloatVectorData right) throws ValueException;
426 
427     /**
428      * Subtract a number from this vector on a cell-by-cell basis.
429      * @param valueSI float; the value to subtract
430      */
431     public void decrementBy(final float valueSI)
432     {
433         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] -= valueSI);
434     }
435 
436     /**
437      * Multiply two vector on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
438      * vector is returned.
439      * @param right FloatVectorData; the other data object to multiply with
440      * @return the sum of this data object and the other data object
441      * @throws ValueException if vectors have different lengths
442      */
443     public FloatVectorData times(final FloatVectorData right) throws ValueException
444     {
445         checkSizes(right);
446         float[] dv = new float[size()];
447         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) * right.getSI(i));
448         if (this instanceof FloatVectorDataDense && right instanceof FloatVectorDataDense)
449         {
450             return new FloatVectorDataDense(dv);
451         }
452         return new FloatVectorDataDense(dv).toSparse();
453     }
454 
455     /**
456      * Multiply a vector with the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense) stays the
457      * same.
458      * @param right FloatVectorData; the other data object to multiply with
459      * @throws ValueException if vectors have different lengths
460      */
461     public abstract void multiplyBy(FloatVectorData right) throws ValueException;
462 
463     /**
464      * Multiply the values of this vector with a number on a cell-by-cell basis.
465      * @param valueSI float; the value to multiply with
466      */
467     public void multiplyBy(final float valueSI)
468     {
469         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] *= valueSI);
470     }
471 
472     /**
473      * Divide two vectors on a cell-by-cell basis. If both vectors are dense, a dense vector is returned, otherwise a sparse
474      * vector is returned.
475      * @param right FloatVectorData; the other data object to divide by
476      * @return the sum of this data object and the other data object
477      * @throws ValueException if vectors have different lengths
478      */
479     public FloatVectorData divide(final FloatVectorData right) throws ValueException
480     {
481         checkSizes(right);
482         float[] dv = new float[size()];
483         IntStream.range(0, size()).parallel().forEach(i -> dv[i] = getSI(i) / right.getSI(i));
484         if (this instanceof FloatVectorDataDense && right instanceof FloatVectorDataDense)
485         {
486             return new FloatVectorDataDense(dv);
487         }
488         return new FloatVectorDataDense(dv).toSparse();
489     }
490 
491     /**
492      * Divide the values of a vector by the values of another vector on a cell-by-cell basis. The type of vector (sparse, dense)
493      * stays the same.
494      * @param right FloatVectorData; the other data object to divide by
495      * @throws ValueException if vectors have different lengths
496      */
497     public abstract void divideBy(FloatVectorData right) throws ValueException;
498 
499     /**
500      * Divide the values of this vector by a number on a cell-by-cell basis.
501      * @param valueSI float; the value to multiply with
502      */
503     public void divideBy(final float valueSI)
504     {
505         IntStream.range(0, this.vectorSI.length).parallel().forEach(i -> this.vectorSI[i] /= valueSI);
506     }
507 
508     /** ============================================================================================ */
509     /** =============================== EQUALS, HASHCODE, TOSTRING ================================= */
510     /** ============================================================================================ */
511 
512     /** {@inheritDoc} */
513     @Override
514     public int hashCode()
515     {
516         final int prime = 31;
517         int result = 1;
518         result = prime * result + Arrays.hashCode(this.vectorSI);
519         return result;
520     }
521 
522     /** {@inheritDoc} */
523     @Override
524     @SuppressWarnings("checkstyle:needbraces")
525     public boolean equals(final Object obj)
526     {
527         if (this == obj)
528             return true;
529         if (obj == null)
530             return false;
531         if (getClass() != obj.getClass())
532             return false;
533         FloatVectorData other = (FloatVectorData) obj;
534         if (!Arrays.equals(this.vectorSI, other.vectorSI))
535             return false;
536         return true;
537     }
538 
539     /** {@inheritDoc} */
540     @Override
541     public String toString()
542     {
543         return "FloatVectorData [storageType=" + this.storageType + ", vectorSI=" + Arrays.toString(this.vectorSI) + "]";
544     }
545 }