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 static final long serialVersionUID = 601L;
24
25
26 private double[] 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 SparseDoubleDataSi(final double[] 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 SparseDoubleDataSi(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 @SuppressWarnings("checkstyle:needbraces")
89 public SparseDoubleDataSi(final double[][] denseData)
90 {
91 Throw.whenNull(denseData, "denseData");
92 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
93 this.rows = denseData.length;
94 this.cols = denseData[0].length;
95 for (int r = 1; r < this.rows; r++)
96 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
97 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
98 this.cols);
99 storeSparse(denseData);
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113 @SuppressWarnings("checkstyle:needbraces")
114 public <Q extends Quantity<Q>> SparseDoubleDataSi(final Q[] sparseData, 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 public <Q extends Quantity<Q>> SparseDoubleDataSi(final Q[] denseData, final int rows, final int cols)
141 {
142 Throw.whenNull(denseData, "denseData");
143 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
144 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
145 Throw.when(denseData.length != rows * cols, IllegalArgumentException.class,
146 "denseData.length (%d) != rows x cols (%d x %d)", denseData.length, rows, cols);
147 this.rows = rows;
148 this.cols = cols;
149 storeSparse(denseData);
150 }
151
152
153
154
155
156
157
158 @SuppressWarnings("checkstyle:needbraces")
159 public <Q extends Quantity<Q>> SparseDoubleDataSi(final Q[][] denseData)
160 {
161 Throw.whenNull(denseData, "denseData");
162 Throw.when(denseData.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
163 this.rows = denseData.length;
164 this.cols = denseData[0].length;
165 for (int r = 1; r < this.rows; r++)
166 Throw.when(denseData[r].length != this.cols, IllegalArgumentException.class,
167 "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, denseData[r].length,
168 this.cols);
169 storeSparse(denseData);
170 }
171
172
173
174
175
176
177
178
179
180 @SuppressWarnings("checkstyle:needbraces")
181 public <Q extends Quantity<Q>> SparseDoubleDataSi(final Collection<DoubleSparseValue<Q>> indexedData, final int rows,
182 final int cols)
183 {
184 Throw.whenNull(indexedData, "indexedData");
185 Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
186 Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
187 this.rows = rows;
188 this.cols = cols;
189 this.sparseData = new double[indexedData.size()];
190 this.indexes = new int[indexedData.size()];
191 int index = 0;
192 for (var value : indexedData)
193 {
194 Throw.when(value.getRow() >= rows, IndexOutOfBoundsException.class, "Row index for indexed value %s out of bounds",
195 value.toString());
196 Throw.when(value.getColumn() >= cols, IndexOutOfBoundsException.class,
197 "Column index for indexed value %s out of bounds", value.toString());
198 this.sparseData[index] = value.si();
199 this.indexes[index++] = value.getRow() * this.cols + value.getColumn();
200 }
201 }
202
203
204
205
206
207
208
209 protected void checkIndexes(final int[] indexArray)
210 {
211 for (int i = 0; i < indexArray.length; i++)
212 {
213 if (indexArray[i] < 0 || indexArray[i] >= this.rows * this.cols)
214 {
215 throw new IndexOutOfBoundsException(
216 String.format("indexes[%d] out of bounds, %d rows x %d cols; value should be 0..%d", i, this.rows,
217 this.cols, this.rows * this.cols - 1));
218 }
219 }
220 for (int i = 1; i < indexArray.length; i++)
221 {
222 if (indexArray[i] <= indexArray[i - 1])
223 {
224 throw new IllegalArgumentException(
225 "indexes[] must be strictly increasing, found " + indexArray[i - 1] + " then " + indexArray[i]);
226 }
227 }
228 }
229
230
231
232
233
234 @SuppressWarnings("checkstyle:needbraces")
235 public void storeSparse(final double[] denseData)
236 {
237 int nonzero = 0;
238 for (int i = 0; i < denseData.length; i++)
239 if (denseData[i] != 0.0)
240 nonzero++;
241 this.sparseData = new double[nonzero];
242 this.indexes = new int[nonzero];
243 int index = 0;
244 for (int i = 0; i < denseData.length; i++)
245 if (denseData[i] != 0.0)
246 {
247 this.sparseData[index] = denseData[i];
248 this.indexes[index] = i;
249 index++;
250 }
251 }
252
253
254
255
256
257 @SuppressWarnings("checkstyle:needbraces")
258 public void storeSparse(final double[][] denseData)
259 {
260 int nonzero = 0;
261 for (int i = 0; i < denseData.length; i++)
262 for (int j = 0; j < denseData[i].length; j++)
263 if (denseData[i][j] != 0.0)
264 nonzero++;
265 this.sparseData = new double[nonzero];
266 this.indexes = new int[nonzero];
267 int index = 0;
268 for (int i = 0; i < denseData.length; i++)
269 for (int j = 0; j < denseData[i].length; j++)
270 if (denseData[i][j] != 0.0)
271 {
272 this.sparseData[index] = denseData[i][j];
273 this.indexes[index] = i * this.cols + j;
274 index++;
275 }
276 }
277
278
279
280
281
282
283 @SuppressWarnings("checkstyle:needbraces")
284 public <Q extends Quantity<Q>> void storeSparse(final Q[] denseData)
285 {
286 int nonzero = 0;
287 for (int i = 0; i < denseData.length; i++)
288 if (denseData[i].ne0())
289 nonzero++;
290 this.sparseData = new double[nonzero];
291 this.indexes = new int[nonzero];
292 int index = 0;
293 for (int i = 0; i < denseData.length; i++)
294 if (denseData[i].ne0())
295 {
296 this.sparseData[index] = denseData[i].si();
297 this.indexes[index] = i;
298 index++;
299 }
300 }
301
302
303
304
305
306
307 @SuppressWarnings("checkstyle:needbraces")
308 public <Q extends Quantity<Q>> void storeSparse(final Q[][] denseData)
309 {
310 int nonzero = 0;
311 for (int i = 0; i < denseData.length; i++)
312 for (int j = 0; j < denseData[i].length; j++)
313 if (denseData[i][j].ne0())
314 nonzero++;
315 this.sparseData = new double[nonzero];
316 this.indexes = new int[nonzero];
317 int index = 0;
318 for (int i = 0; i < denseData.length; i++)
319 for (int j = 0; j < denseData[i].length; j++)
320 if (denseData[i][j].ne0())
321 {
322 this.sparseData[index] = denseData[i][j].si();
323 this.indexes[index] = i * this.cols + j;
324 index++;
325 }
326 }
327
328 @Override
329 public int rows()
330 {
331 return this.rows;
332 }
333
334 @Override
335 public int cols()
336 {
337 return this.cols;
338 }
339
340 @Override
341 public boolean isDense()
342 {
343 return false;
344 }
345
346 @Override
347 public boolean isDouble()
348 {
349 return true;
350 }
351
352
353
354
355
356
357
358 private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
359 {
360 Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row,
361 this.rows - 1);
362 Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
363 this.cols - 1);
364 }
365
366 @Override
367 public double get(final int row, final int col)
368 {
369 checkRowCol(row, col);
370 int index = row * this.cols + col;
371 final int pos = Arrays.binarySearch(this.indexes, index);
372 return (pos >= 0) ? this.sparseData[pos] : 0.0;
373 }
374
375 @SuppressWarnings("checkstyle:needbraces")
376 @Override
377 public double[] getSiArray()
378 {
379 double[] denseData = new double[rows() * cols()];
380 for (int i = 0; i < this.sparseData.length; i++)
381 denseData[this.indexes[i]] = this.sparseData[i];
382 return denseData;
383 }
384
385 @Override
386 public double[] unsafeSiArray()
387 {
388 return getSiArray();
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 nonZeroCount()
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(unsafeSiArray());
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(unsafeSiArray(), dg.unsafeSiArray());
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 }