1 package org.djunits.vecmat.storage;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.Objects;
6
7 import org.djunits.quantity.def.Quantity;
8 import org.djunits.unit.UnitInterface;
9 import org.djutils.exceptions.Throw;
10
11
12
13
14
15
16
17
18
19
20
21 public class SparseFloatDataSi implements DataGridSi<SparseFloatDataSi>
22 {
23
24 private float[] sparseData;
25
26
27 private int[] indexes;
28
29
30 private final int rows;
31
32
33 private final int cols;
34
35
36
37
38
39
40
41
42
43
44
45
46 protected SparseFloatDataSi(final float[] sparseData, final int[] indexes, final int rows, final int cols)
47 {
48 Throw.whenNull(sparseData, "sparseData");
49 Throw.whenNull(indexes, "indexes");
50 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
51 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
52 Throw.when(sparseData.length != indexes.length, IllegalArgumentException.class,
53 "sparseData array (%d) has different length from indexes array (%d)", sparseData.length, indexes.length);
54 this.rows = rows;
55 this.cols = cols;
56 checkIndexes(indexes);
57 this.sparseData = sparseData;
58 this.indexes = indexes;
59 }
60
61
62
63
64
65
66
67
68
69 public SparseFloatDataSi(final double[] denseData, final int rows, final int cols)
70 {
71 Throw.whenNull(denseData, "denseData");
72 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
73 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
74 Throw.when(denseData.length != rows * cols, IllegalArgumentException.class,
75 "denseData.length (%d) != rows x cols (%d x %d)", denseData.length, rows, cols);
76 this.rows = rows;
77 this.cols = cols;
78 storeSparse(denseData);
79 }
80
81
82
83
84
85
86
87
88
89 public SparseFloatDataSi(final float[] denseData, final int rows, final int cols)
90 {
91 Throw.whenNull(denseData, "denseData");
92 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
93 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
94 Throw.when(denseData.length != rows * cols, IllegalArgumentException.class,
95 "denseData.length (%d) != rows x cols (%d x %d)", denseData.length, rows, cols);
96 this.rows = rows;
97 this.cols = cols;
98 storeSparse(denseData);
99 }
100
101
102
103
104
105
106 @SuppressWarnings("checkstyle:needbraces")
107 public SparseFloatDataSi(final double[][] denseData)
108 {
109 Throw.whenNull(denseData, "denseData");
110 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
111 this.rows = denseData.length;
112 this.cols = denseData[0].length;
113 for (int r = 1; r < this.rows; r++)
114 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
115 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
116 this.cols);
117 storeSparse(denseData);
118 }
119
120
121
122
123
124
125 @SuppressWarnings("checkstyle:needbraces")
126 public SparseFloatDataSi(final float[][] denseData)
127 {
128 Throw.whenNull(denseData, "denseData");
129 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
130 this.rows = denseData.length;
131 this.cols = denseData[0].length;
132 for (int r = 1; r < this.rows; r++)
133 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
134 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
135 this.cols);
136 storeSparse(denseData);
137 }
138
139
140
141
142
143
144
145
146
147
148
149
150
151 @SuppressWarnings("checkstyle:needbraces")
152 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseFloatDataSi(final Q[] sparseData,
153 final int[] indexes, final int rows, final int cols)
154 {
155 Throw.whenNull(sparseData, "sparseData");
156 Throw.whenNull(indexes, "indexes");
157 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
158 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
159 Throw.when(sparseData.length != indexes.length, IllegalArgumentException.class,
160 "sparseData array (%d) has different length from indexes array (%d)", sparseData.length, indexes.length);
161 this.rows = rows;
162 this.cols = cols;
163 checkIndexes(indexes);
164 this.sparseData = new float[sparseData.length];
165 for (int i = 0; i < sparseData.length; i++)
166 this.sparseData[i] = sparseData[i].floatValue();
167 this.indexes = indexes.clone();
168 }
169
170
171
172
173
174
175
176
177
178
179
180 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseFloatDataSi(final Q[] denseData, final int rows,
181 final int cols)
182 {
183 Throw.whenNull(denseData, "denseData");
184 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
185 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
186 Throw.when(denseData.length != rows * cols, IllegalArgumentException.class,
187 "denseData.length (%d) != rows x cols (%d x %d)", denseData.length, rows, cols);
188 this.rows = rows;
189 this.cols = cols;
190 storeSparse(denseData);
191 }
192
193
194
195
196
197
198
199
200 @SuppressWarnings("checkstyle:needbraces")
201 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseFloatDataSi(final Q[][] denseData)
202 {
203 Throw.whenNull(denseData, "denseData");
204 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
205 this.rows = denseData.length;
206 this.cols = denseData[0].length;
207 for (int r = 1; r < this.rows; r++)
208 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
209 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
210 this.cols);
211 storeSparse(denseData);
212 }
213
214
215
216
217
218
219
220
221
222
223 @SuppressWarnings("checkstyle:needbraces")
224 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseFloatDataSi(
225 final Collection<FloatSparseValue<Q, U>> indexedData, final int rows, final int cols)
226 {
227 Throw.whenNull(indexedData, "indexedData");
228 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
229 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
230 this.rows = rows;
231 this.cols = cols;
232 this.sparseData = new float[indexedData.size()];
233 this.indexes = new int[indexedData.size()];
234 int index = 0;
235 for (var value : indexedData)
236 {
237 Throw.when(value.getRow() >= rows, IndexOutOfBoundsException.class, "Row index for indexed value %s out of bounds",
238 value.toString());
239 Throw.when(value.getColumn() >= cols, IndexOutOfBoundsException.class,
240 "Column index for indexed value %s out of bounds", value.toString());
241 this.sparseData[index] = (float) value.si();
242 this.indexes[index++] = value.getRow() * this.cols + value.getColumn();
243 }
244 }
245
246
247
248
249
250
251
252 protected void checkIndexes(final int[] indexArray)
253 {
254 for (int i = 0; i < indexArray.length; i++)
255 {
256 if (indexArray[i] < 0 || indexArray[i] >= this.rows * this.cols)
257 {
258 throw new IndexOutOfBoundsException(
259 String.format("indexes[%d] out of bounds, %d rows x %d cols; value should be 0..%d", i, this.rows,
260 this.cols, this.rows * this.cols - 1));
261 }
262 }
263 for (int i = 1; i < indexArray.length; i++)
264 {
265 if (indexArray[i] <= indexArray[i - 1])
266 {
267 throw new IllegalArgumentException(
268 "indexes[] must be strictly increasing, found " + indexArray[i - 1] + " then " + indexArray[i]);
269 }
270 }
271 }
272
273
274
275
276
277 @SuppressWarnings("checkstyle:needbraces")
278 public void storeSparse(final double[] denseData)
279 {
280 int nonzero = 0;
281 for (int i = 0; i < denseData.length; i++)
282 if (denseData[i] != 0.0)
283 nonzero++;
284 this.sparseData = new float[nonzero];
285 this.indexes = new int[nonzero];
286 int index = 0;
287 for (int i = 0; i < denseData.length; i++)
288 if (denseData[i] != 0.0)
289 {
290 this.sparseData[index] = (float) denseData[i];
291 this.indexes[index] = i;
292 index++;
293 }
294 }
295
296
297
298
299
300 @SuppressWarnings("checkstyle:needbraces")
301 public void storeSparse(final float[] denseData)
302 {
303 int nonzero = 0;
304 for (int i = 0; i < denseData.length; i++)
305 if (denseData[i] != 0.0f)
306 nonzero++;
307 this.sparseData = new float[nonzero];
308 this.indexes = new int[nonzero];
309 int index = 0;
310 for (int i = 0; i < denseData.length; i++)
311 if (denseData[i] != 0.0f)
312 {
313 this.sparseData[index] = denseData[i];
314 this.indexes[index] = i;
315 index++;
316 }
317 }
318
319
320
321
322
323 @SuppressWarnings("checkstyle:needbraces")
324 public void storeSparse(final double[][] denseData)
325 {
326 int nonzero = 0;
327 for (int i = 0; i < denseData.length; i++)
328 for (int j = 0; j < denseData[i].length; j++)
329 if (denseData[i][j] != 0.0)
330 nonzero++;
331 this.sparseData = new float[nonzero];
332 this.indexes = new int[nonzero];
333 int index = 0;
334 for (int i = 0; i < denseData.length; i++)
335 for (int j = 0; j < denseData[i].length; j++)
336 if (denseData[i][j] != 0.0)
337 {
338 this.sparseData[index] = (float) denseData[i][j];
339 this.indexes[index] = i * this.cols + j;
340 index++;
341 }
342 }
343
344
345
346
347
348 @SuppressWarnings("checkstyle:needbraces")
349 public void storeSparse(final float[][] denseData)
350 {
351 int nonzero = 0;
352 for (int i = 0; i < denseData.length; i++)
353 for (int j = 0; j < denseData[i].length; j++)
354 if (denseData[i][j] != 0.0f)
355 nonzero++;
356 this.sparseData = new float[nonzero];
357 this.indexes = new int[nonzero];
358 int index = 0;
359 for (int i = 0; i < denseData.length; i++)
360 for (int j = 0; j < denseData[i].length; j++)
361 if (denseData[i][j] != 0.0f)
362 {
363 this.sparseData[index] = (float) denseData[i][j];
364 this.indexes[index] = i * this.cols + j;
365 index++;
366 }
367 }
368
369
370
371
372
373
374
375 @SuppressWarnings("checkstyle:needbraces")
376 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> void storeSparse(final Q[] denseData)
377 {
378 int nonzero = 0;
379 for (int i = 0; i < denseData.length; i++)
380 if (denseData[i].ne0())
381 nonzero++;
382 this.sparseData = new float[nonzero];
383 this.indexes = new int[nonzero];
384 int index = 0;
385 for (int i = 0; i < denseData.length; i++)
386 if (denseData[i].ne0())
387 {
388 this.sparseData[index] = denseData[i].floatValue();
389 this.indexes[index] = i;
390 index++;
391 }
392 }
393
394
395
396
397
398
399
400 @SuppressWarnings("checkstyle:needbraces")
401 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> void storeSparse(final Q[][] denseData)
402 {
403 int nonzero = 0;
404 for (int i = 0; i < denseData.length; i++)
405 for (int j = 0; j < denseData[i].length; j++)
406 if (denseData[i][j].ne0())
407 nonzero++;
408 this.sparseData = new float[nonzero];
409 this.indexes = new int[nonzero];
410 int index = 0;
411 for (int i = 0; i < denseData.length; i++)
412 for (int j = 0; j < denseData[i].length; j++)
413 if (denseData[i][j].ne0())
414 {
415 this.sparseData[index] = denseData[i][j].floatValue();
416 this.indexes[index] = i * this.cols + j;
417 index++;
418 }
419 }
420
421 @Override
422 public int rows()
423 {
424 return this.rows;
425 }
426
427 @Override
428 public int cols()
429 {
430 return this.cols;
431 }
432
433 @Override
434 public boolean isDense()
435 {
436 return false;
437 }
438
439 @Override
440 public boolean isDouble()
441 {
442 return false;
443 }
444
445
446
447
448
449
450
451 private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
452 {
453 Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row,
454 this.rows - 1);
455 Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
456 this.cols - 1);
457 }
458
459 @Override
460 public double get(final int row, final int col)
461 {
462 checkRowCol(row, col);
463 int index = row * this.cols + col;
464 final int pos = Arrays.binarySearch(this.indexes, index);
465 return (pos >= 0) ? this.sparseData[pos] : 0.0;
466 }
467
468 @SuppressWarnings("checkstyle:needbraces")
469 @Override
470 public double[] getDataArray()
471 {
472 double[] denseData = new double[rows() * cols()];
473 for (int i = 0; i < this.sparseData.length; i++)
474 denseData[this.indexes[i]] = this.sparseData[i];
475 return denseData;
476 }
477
478 @Override
479 public SparseFloatDataSi copy()
480 {
481 return new SparseFloatDataSi(this.sparseData.clone(), this.indexes.clone(), rows(), cols());
482 }
483
484 @SuppressWarnings("checkstyle:needbraces")
485 @Override
486 public int cardinality()
487 {
488 int result = 0;
489 for (int i = 0; i < this.sparseData.length; i++)
490 result += this.sparseData[i] == 0.0 ? 0 : 1;
491 return result;
492 }
493
494 @Override
495 public SparseFloatDataSi instantiateNew(final double[] denseData)
496 {
497 Throw.when(denseData.length != rows() * cols(), IllegalArgumentException.class,
498 "Data object length != rows * cols, %d != %d * %d", denseData.length, rows(), cols());
499 return new SparseFloatDataSi(denseData, rows(), cols());
500 }
501
502 @Override
503 public SparseFloatDataSi instantiateNew(final double[] denseData, final int newRows, final int newCols)
504 {
505 Throw.when(denseData.length != newRows * newCols, IllegalArgumentException.class,
506 "Data object length != rows * cols, %d != %d * %d", denseData.length, newRows, newCols);
507 return new SparseFloatDataSi(denseData, newRows, newCols);
508 }
509
510 @Override
511 public int hashCode()
512 {
513 final int prime = 31;
514 int result = 1;
515 result = prime * result + Arrays.hashCode(getDataArray());
516 result = prime * result + Objects.hash(this.cols, this.rows);
517 return result;
518 }
519
520 @SuppressWarnings("checkstyle:needbraces")
521 @Override
522 public boolean equals(final Object obj)
523 {
524 if (this == obj)
525 return true;
526 if (obj == null)
527 return false;
528 if (getClass() != obj.getClass())
529 {
530 if (obj instanceof DataGridSi dg)
531 return this.cols == dg.cols() && this.rows == dg.rows() && Arrays.equals(getDataArray(), dg.getDataArray());
532 return false;
533 }
534 SparseFloatDataSi other = (SparseFloatDataSi) obj;
535 return this.cols == other.cols && this.rows == other.rows && Arrays.equals(this.sparseData, other.sparseData)
536 && Arrays.equals(this.indexes, other.indexes);
537 }
538
539 }