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