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 SparseDoubleDataSi implements DataGridSi<SparseDoubleDataSi>
22 {
23
24 private double[] 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 SparseDoubleDataSi(final double[] 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 SparseDoubleDataSi(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 @SuppressWarnings("checkstyle:needbraces")
87 public SparseDoubleDataSi(final double[][] denseData)
88 {
89 Throw.whenNull(denseData, "denseData");
90 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
91 this.rows = denseData.length;
92 this.cols = denseData[0].length;
93 for (int r = 1; r < this.rows; r++)
94 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
95 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
96 this.cols);
97 storeSparse(denseData);
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @SuppressWarnings("checkstyle:needbraces")
113 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseDoubleDataSi(final Q[] sparseData,
114 final int[] indexes, final int rows, final int cols)
115 {
116 Throw.whenNull(sparseData, "sparseData");
117 Throw.whenNull(indexes, "indexes");
118 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
119 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
120 Throw.when(sparseData.length != indexes.length, IllegalArgumentException.class,
121 "sparseData array (%d) has different length from indexes array (%d)", sparseData.length, indexes.length);
122 this.rows = rows;
123 this.cols = cols;
124 checkIndexes(indexes);
125 this.sparseData = new double[sparseData.length];
126 for (int i = 0; i < sparseData.length; i++)
127 this.sparseData[i] = sparseData[i].si();
128 this.indexes = indexes.clone();
129 }
130
131
132
133
134
135
136
137
138
139
140
141 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseDoubleDataSi(final Q[] denseData, final int rows,
142 final int cols)
143 {
144 Throw.whenNull(denseData, "denseData");
145 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
146 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
147 Throw.when(denseData.length != rows * cols, IllegalArgumentException.class,
148 "denseData.length (%d) != rows x cols (%d x %d)", denseData.length, rows, cols);
149 this.rows = rows;
150 this.cols = cols;
151 storeSparse(denseData);
152 }
153
154
155
156
157
158
159
160
161 @SuppressWarnings("checkstyle:needbraces")
162 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseDoubleDataSi(final Q[][] denseData)
163 {
164 Throw.whenNull(denseData, "denseData");
165 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
166 this.rows = denseData.length;
167 this.cols = denseData[0].length;
168 for (int r = 1; r < this.rows; r++)
169 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
170 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
171 this.cols);
172 storeSparse(denseData);
173 }
174
175
176
177
178
179
180
181
182
183
184 @SuppressWarnings("checkstyle:needbraces")
185 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> SparseDoubleDataSi(
186 final Collection<DoubleSparseValue<Q, U>> indexedData, final int rows, final int cols)
187 {
188 Throw.whenNull(indexedData, "indexedData");
189 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
190 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
191 this.rows = rows;
192 this.cols = cols;
193 this.sparseData = new double[indexedData.size()];
194 this.indexes = new int[indexedData.size()];
195 int index = 0;
196 for (var value : indexedData)
197 {
198 Throw.when(value.getRow() >= rows, IndexOutOfBoundsException.class, "Row index for indexed value %s out of bounds",
199 value.toString());
200 Throw.when(value.getColumn() >= cols, IndexOutOfBoundsException.class,
201 "Column index for indexed value %s out of bounds", value.toString());
202 this.sparseData[index] = value.si();
203 this.indexes[index++] = value.getRow() * this.cols + value.getColumn();
204 }
205 }
206
207
208
209
210
211
212
213 protected void checkIndexes(final int[] indexArray)
214 {
215 for (int i = 0; i < indexArray.length; i++)
216 {
217 if (indexArray[i] < 0 || indexArray[i] >= this.rows * this.cols)
218 {
219 throw new IndexOutOfBoundsException(
220 String.format("indexes[%d] out of bounds, %d rows x %d cols; value should be 0..%d", i, this.rows,
221 this.cols, this.rows * this.cols - 1));
222 }
223 }
224 for (int i = 1; i < indexArray.length; i++)
225 {
226 if (indexArray[i] <= indexArray[i - 1])
227 {
228 throw new IllegalArgumentException(
229 "indexes[] must be strictly increasing, found " + indexArray[i - 1] + " then " + indexArray[i]);
230 }
231 }
232 }
233
234
235
236
237
238 @SuppressWarnings("checkstyle:needbraces")
239 public void storeSparse(final double[] denseData)
240 {
241 int nonzero = 0;
242 for (int i = 0; i < denseData.length; i++)
243 if (denseData[i] != 0.0)
244 nonzero++;
245 this.sparseData = new double[nonzero];
246 this.indexes = new int[nonzero];
247 int index = 0;
248 for (int i = 0; i < denseData.length; i++)
249 if (denseData[i] != 0.0)
250 {
251 this.sparseData[index] = denseData[i];
252 this.indexes[index] = i;
253 index++;
254 }
255 }
256
257
258
259
260
261 @SuppressWarnings("checkstyle:needbraces")
262 public void storeSparse(final double[][] denseData)
263 {
264 int nonzero = 0;
265 for (int i = 0; i < denseData.length; i++)
266 for (int j = 0; j < denseData[i].length; j++)
267 if (denseData[i][j] != 0.0)
268 nonzero++;
269 this.sparseData = new double[nonzero];
270 this.indexes = new int[nonzero];
271 int index = 0;
272 for (int i = 0; i < denseData.length; i++)
273 for (int j = 0; j < denseData[i].length; j++)
274 if (denseData[i][j] != 0.0)
275 {
276 this.sparseData[index] = denseData[i][j];
277 this.indexes[index] = i * this.cols + j;
278 index++;
279 }
280 }
281
282
283
284
285
286
287
288 @SuppressWarnings("checkstyle:needbraces")
289 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> void storeSparse(final Q[] denseData)
290 {
291 int nonzero = 0;
292 for (int i = 0; i < denseData.length; i++)
293 if (denseData[i].ne0())
294 nonzero++;
295 this.sparseData = new double[nonzero];
296 this.indexes = new int[nonzero];
297 int index = 0;
298 for (int i = 0; i < denseData.length; i++)
299 if (denseData[i].ne0())
300 {
301 this.sparseData[index] = denseData[i].si();
302 this.indexes[index] = i;
303 index++;
304 }
305 }
306
307
308
309
310
311
312
313 @SuppressWarnings("checkstyle:needbraces")
314 public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> void storeSparse(final Q[][] denseData)
315 {
316 int nonzero = 0;
317 for (int i = 0; i < denseData.length; i++)
318 for (int j = 0; j < denseData[i].length; j++)
319 if (denseData[i][j].ne0())
320 nonzero++;
321 this.sparseData = new double[nonzero];
322 this.indexes = new int[nonzero];
323 int index = 0;
324 for (int i = 0; i < denseData.length; i++)
325 for (int j = 0; j < denseData[i].length; j++)
326 if (denseData[i][j].ne0())
327 {
328 this.sparseData[index] = denseData[i][j].si();
329 this.indexes[index] = i * this.cols + j;
330 index++;
331 }
332 }
333
334 @Override
335 public int rows()
336 {
337 return this.rows;
338 }
339
340 @Override
341 public int cols()
342 {
343 return this.cols;
344 }
345
346 @Override
347 public boolean isDense()
348 {
349 return false;
350 }
351
352 @Override
353 public boolean isDouble()
354 {
355 return true;
356 }
357
358
359
360
361
362
363
364 private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
365 {
366 Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row,
367 this.rows - 1);
368 Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
369 this.cols - 1);
370 }
371
372 @Override
373 public double get(final int row, final int col)
374 {
375 checkRowCol(row, col);
376 int index = row * this.cols + col;
377 final int pos = Arrays.binarySearch(this.indexes, index);
378 return (pos >= 0) ? this.sparseData[pos] : 0.0;
379 }
380
381 @SuppressWarnings("checkstyle:needbraces")
382 @Override
383 public double[] getDataArray()
384 {
385 double[] denseData = new double[rows() * cols()];
386 for (int i = 0; i < this.sparseData.length; i++)
387 denseData[this.indexes[i]] = this.sparseData[i];
388 return denseData;
389 }
390
391 @Override
392 public SparseDoubleDataSi copy()
393 {
394 return new SparseDoubleDataSi(this.sparseData.clone(), this.indexes.clone(), rows(), cols());
395 }
396
397 @SuppressWarnings("checkstyle:needbraces")
398 @Override
399 public int cardinality()
400 {
401 int result = 0;
402 for (int i = 0; i < this.sparseData.length; i++)
403 result += this.sparseData[i] == 0.0 ? 0 : 1;
404 return result;
405 }
406
407 @Override
408 public SparseDoubleDataSi instantiateNew(final double[] denseData)
409 {
410 Throw.when(denseData.length != rows() * cols(), IllegalArgumentException.class,
411 "Data object length != rows * cols, %d != %d * %d", denseData.length, rows(), cols());
412 return new SparseDoubleDataSi(denseData, rows(), cols());
413 }
414
415 @Override
416 public SparseDoubleDataSi instantiateNew(final double[] denseData, final int newRows, final int newCols)
417 {
418 Throw.when(denseData.length != newRows * newCols, IllegalArgumentException.class,
419 "Data object length != rows * cols, %d != %d * %d", denseData.length, newRows, newCols);
420 return new SparseDoubleDataSi(denseData, newRows, newCols);
421 }
422
423 @Override
424 public int hashCode()
425 {
426 final int prime = 31;
427 int result = 1;
428 result = prime * result + Arrays.hashCode(getDataArray());
429 result = prime * result + Objects.hash(this.cols, this.rows);
430 return result;
431 }
432
433 @SuppressWarnings("checkstyle:needbraces")
434 @Override
435 public boolean equals(final Object obj)
436 {
437 if (this == obj)
438 return true;
439 if (obj == null)
440 return false;
441 if (getClass() != obj.getClass())
442 {
443 if (obj instanceof DataGridSi dg)
444 return this.cols == dg.cols() && this.rows == dg.rows() && Arrays.equals(getDataArray(), dg.getDataArray());
445 return false;
446 }
447 SparseDoubleDataSi other = (SparseDoubleDataSi) obj;
448 return this.cols == other.cols && this.rows == other.rows && Arrays.equals(this.sparseData, other.sparseData)
449 && Arrays.equals(this.indexes, other.indexes);
450 }
451
452 }