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