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