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