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