View Javadoc
1   package org.djunits.vecmat.storage;
2   
3   import java.util.Arrays;
4   import java.util.Objects;
5   
6   import org.djunits.quantity.def.Quantity;
7   import org.djunits.unit.Unit;
8   import org.djutils.exceptions.Throw;
9   
10  /**
11   * DenseFloatData implements a dense data grid for N x M matrices or N x 1 or 1 x N vectors with float values. Calculations are
12   * carried out in double precision, so the instantiate() and si() method work with double[]. DenseFloatData always stores a safe
13   * copy of the data.
14   * <p>
15   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
16   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
17   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
18   * @author Alexander Verbraeck
19   */
20  public class DenseFloatDataSi implements DataGridSi<DenseFloatDataSi>
21  {
22      /** The data stored in row-major format. */
23      private final float[] data;
24  
25      /** The number of rows. */
26      private final int rows;
27  
28      /** the number of columns. */
29      private final int cols;
30  
31      /**
32       * Instantiate a data object with one array in row-major format. NO safe copy of the data is stored. The constructor is very
33       * useful to store data after a calculation that already made a new safe copy.
34       * @param dataSi the data with SI-values in row-major format
35       * @param rows the number of rows
36       * @param cols the number of columns
37       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
38       *             or columns is not positive
39       */
40      public DenseFloatDataSi(final float[] dataSi, final int rows, final int cols)
41      {
42          Throw.whenNull(dataSi, "dataSi");
43          Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
44          Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
45          Throw.when(dataSi.length != rows * cols, IllegalArgumentException.class,
46                  "Data object length != rows * cols, %d != %d * %d", dataSi.length, rows, cols);
47          this.data = dataSi.clone();
48          this.rows = rows;
49          this.cols = cols;
50      }
51  
52      /**
53       * Instantiate a data object with one array in row-major format. A safe copy of the data is stored, since the data has to be
54       * transferred from double format to float format.
55       * @param dataSi the data with SI-values in row-major format
56       * @param rows the number of rows
57       * @param cols the number of columns
58       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
59       *             or columns is not positive
60       */
61      public DenseFloatDataSi(final double[] dataSi, final int rows, final int cols)
62      {
63          Throw.whenNull(dataSi, "dataSi");
64          Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
65          Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
66          Throw.when(dataSi.length != rows * cols, IllegalArgumentException.class,
67                  "Data object length != rows * cols, %d != %d * %d", dataSi.length, rows, cols);
68          this.data = new float[dataSi.length];
69          for (int i = 0; i < dataSi.length; i++)
70          {
71              this.data[i] = (float) dataSi[i];
72          }
73          this.rows = rows;
74          this.cols = cols;
75      }
76  
77      /**
78       * Instantiate a data object with one array in row-major format. A safe copy of the data is stored.
79       * @param dataSi the data with SI-values in row-major format
80       * @param rows the number of rows
81       * @param cols the number of columns
82       * @return a dense float data object with SI values for vectors, matrices and tables
83       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
84       *             or columns is not positive
85       */
86      public static DenseFloatDataSi ofSi(final float[] dataSi, final int rows, final int cols)
87      {
88          return new DenseFloatDataSi(dataSi, rows, cols);
89      }
90  
91      /**
92       * Instantiate a data object with one array in row-major format. A safe copy of the data is stored.
93       * @param dataSi the data with SI-values in row-major format
94       * @param rows the number of rows
95       * @param cols the number of columns
96       * @return a dense float data object with SI values for vectors, matrices and tables
97       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
98       *             or columns is not positive
99       */
100     public static DenseFloatDataSi ofSi(final double[] dataSi, final int rows, final int cols)
101     {
102         return new DenseFloatDataSi(dataSi, rows, cols);
103     }
104 
105     /**
106      * Instantiate a data object with one array in row-major format. A safe copy of the data is stored.
107      * @param dataInUnit the data expressed in the given unit in row-major format
108      * @param rows the number of rows
109      * @param cols the number of columns
110      * @param unit the unit of the data
111      * @return a dense float data object with SI values for vectors, matrices and tables
112      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
113      *             or columns is not positive
114      * @param <Q> the quantity type
115      */
116     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final float[] dataInUnit, final int rows, final int cols,
117             final Unit<?, Q> unit)
118     {
119         Throw.whenNull(dataInUnit, "dataInUnit");
120         Throw.whenNull(unit, "unit");
121         float[] dataSi = new float[dataInUnit.length];
122         for (int i = 0; i < dataInUnit.length; i++)
123         {
124             dataSi[i] = (float) unit.toBaseValue(dataInUnit[i]);
125         }
126         return new DenseFloatDataSi(dataSi, rows, cols);
127     }
128 
129     /**
130      * Instantiate a data object with one array in row-major format. A safe copy of the data is stored.
131      * @param dataInUnit the data expressed in the given unit in row-major format
132      * @param rows the number of rows
133      * @param cols the number of columns
134      * @param unit the unit of the data
135      * @return a dense float data object with SI values for vectors, matrices and tables
136      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
137      *             or columns is not positive
138      * @param <Q> the quantity type
139      */
140     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final double[] dataInUnit, final int rows, final int cols,
141             final Unit<?, Q> unit)
142     {
143         Throw.whenNull(dataInUnit, "dataInUnit");
144         Throw.whenNull(unit, "unit");
145         float[] dataSi = new float[dataInUnit.length];
146         for (int i = 0; i < dataInUnit.length; i++)
147         {
148             dataSi[i] = (float) unit.toBaseValue(dataInUnit[i]);
149         }
150         return new DenseFloatDataSi(dataSi, rows, cols);
151     }
152 
153     /**
154      * Instantiate a data object with one array in row-major format. A safe copy of the data is stored.
155      * @param data the quantity data in row-major format
156      * @param rows the number of rows
157      * @param cols the number of columns
158      * @return a dense float data object with SI values for vectors, matrices and tables
159      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
160      *             or columns is not positive
161      * @param <Q> the quantity type
162      */
163     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final Q[] data, final int rows, final int cols)
164     {
165         Throw.whenNull(data, "data");
166         float[] dataSi = new float[data.length];
167         for (int i = 0; i < data.length; i++)
168         {
169             Throw.whenNull(data[i], "data[%d] = null", i);
170             dataSi[i] = (float) data[i].si();
171         }
172         return new DenseFloatDataSi(dataSi, rows, cols);
173     }
174 
175     /**
176      * Instantiate a data object with a double[rows][cols]. A safe copy of the data is stored.
177      * @param gridSi the data as a double[][] array in row-major format, with SI-values
178      * @return a dense float data object with SI values for vectors, matrices and tables
179      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
180      */
181     public static DenseFloatDataSi ofSi(final double[][] gridSi)
182     {
183         Throw.whenNull(gridSi, "gridSi");
184         Throw.when(gridSi.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
185         int rows = gridSi.length;
186         Throw.whenNull(gridSi[0], "gridSi[0] = null");
187         int cols = gridSi[0].length;
188         float[] dataSi = new float[rows * cols];
189         for (int r = 0; r < rows; r++)
190         {
191             Throw.whenNull(gridSi[r], "gridSi[%d] = null", r);
192             Throw.when(gridSi[r].length != cols, IllegalArgumentException.class,
193                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, gridSi[r].length,
194                     cols);
195             for (int c = 0; c < cols; c++)
196             {
197                 dataSi[r * cols + c] = (float) gridSi[r][c];
198             }
199         }
200         return new DenseFloatDataSi(dataSi, rows, cols);
201     }
202 
203     /**
204      * Instantiate a data object with a float[rows][cols]. A safe copy of the data is stored.
205      * @param gridSi the data as a float[][] array in row-major format, with SI-values
206      * @return a dense float data object with SI values for vectors, matrices and tables
207      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
208      */
209     public static DenseFloatDataSi ofSi(final float[][] gridSi)
210     {
211         Throw.whenNull(gridSi, "gridSi");
212         Throw.when(gridSi.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
213         int rows = gridSi.length;
214         Throw.whenNull(gridSi[0], "gridSi[0] = null");
215         int cols = gridSi[0].length;
216         float[] dataSi = new float[rows * cols];
217         for (int r = 0; r < rows; r++)
218         {
219             Throw.whenNull(gridSi[r], "gridSi[%d] = null", r);
220             Throw.when(gridSi[r].length != cols, IllegalArgumentException.class,
221                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, gridSi[r].length,
222                     cols);
223             for (int c = 0; c < cols; c++)
224             {
225                 dataSi[r * cols + c] = gridSi[r][c];
226             }
227         }
228         return new DenseFloatDataSi(dataSi, rows, cols);
229     }
230 
231     /**
232      * Instantiate a data object with a double[rows][cols]. A safe copy of the data is stored.
233      * @param gridInUnit the data as a double[][] array in row-major format, expressed in the given unit
234      * @param unit the unit of the data
235      * @return a dense float data object with SI values for vectors, matrices and tables
236      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
237      * @param <Q> the quantity type
238      */
239     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final double[][] gridInUnit, final Unit<?, Q> unit)
240     {
241         Throw.whenNull(gridInUnit, "gridInUnit");
242         Throw.whenNull(unit, "unit");
243         Throw.when(gridInUnit.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
244         int rows = gridInUnit.length;
245         Throw.whenNull(gridInUnit[0], "gridInUnit[0] = null");
246         int cols = gridInUnit[0].length;
247         float[] dataSi = new float[rows * cols];
248         for (int r = 0; r < rows; r++)
249         {
250             Throw.whenNull(gridInUnit[r], "gridInUnit[%d] = null", r);
251             Throw.when(gridInUnit[r].length != cols, IllegalArgumentException.class,
252                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, gridInUnit[r].length,
253                     cols);
254             for (int c = 0; c < cols; c++)
255             {
256                 dataSi[r * cols + c] = (float) unit.toBaseValue(gridInUnit[r][c]);
257             }
258         }
259         return new DenseFloatDataSi(dataSi, rows, cols);
260     }
261 
262     /**
263      * Instantiate a data object with a float[rows][cols]. A safe copy of the data is stored.
264      * @param gridInUnit the data as a double[][] array in row-major format, expressed in the given unit
265      * @param unit the unit of the data
266      * @return a dense float data object with SI values for vectors, matrices and tables
267      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
268      * @param <Q> the quantity type
269      */
270     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final float[][] gridInUnit, final Unit<?, Q> unit)
271     {
272         Throw.whenNull(gridInUnit, "gridInUnit");
273         Throw.whenNull(unit, "unit");
274         Throw.when(gridInUnit.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
275         int rows = gridInUnit.length;
276         Throw.whenNull(gridInUnit[0], "gridInUnit[0] = null");
277         int cols = gridInUnit[0].length;
278         float[] dataSi = new float[rows * cols];
279         for (int r = 0; r < rows; r++)
280         {
281             Throw.whenNull(gridInUnit[r], "gridInUnit[%d] = null", r);
282             Throw.when(gridInUnit[r].length != cols, IllegalArgumentException.class,
283                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, gridInUnit[r].length,
284                     cols);
285             for (int c = 0; c < cols; c++)
286             {
287                 dataSi[r * cols + c] = (float) unit.toBaseValue(gridInUnit[r][c]);
288             }
289         }
290         return new DenseFloatDataSi(dataSi, rows, cols);
291     }
292 
293     /**
294      * Instantiate a data object with a Q[rows][cols]. A safe copy of the data is stored.
295      * @param grid the quantities as a [][] array in row-major format
296      * @return a dense float data object with SI values for vectors, matrices and tables
297      * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
298      * @param <Q> the quantity type
299      */
300     public static <Q extends Quantity<Q>> DenseFloatDataSi of(final Q[][] grid)
301     {
302         Throw.whenNull(grid, "grid");
303         Throw.when(grid.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
304         int rows = grid.length;
305         Throw.whenNull(grid[0], "grid[0] = null");
306         int cols = grid[0].length;
307         float[] dataSi = new float[rows * cols];
308         for (int r = 0; r < rows; r++)
309         {
310             Throw.whenNull(grid[r], "grid[%d] = null", r);
311             Throw.when(grid[r].length != cols, IllegalArgumentException.class,
312                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, grid[r].length,
313                     cols);
314             for (int c = 0; c < cols; c++)
315             {
316                 Throw.whenNull(grid[r][c], "grid[%d][%d] = null", r, c);
317                 dataSi[r * cols + c] = (float) grid[r][c].si();
318             }
319         }
320         return new DenseFloatDataSi(dataSi, rows, cols);
321     }
322 
323     @Override
324     public int rows()
325     {
326         return this.rows;
327     }
328 
329     @Override
330     public int cols()
331     {
332         return this.cols;
333     }
334 
335     @Override
336     public boolean isDense()
337     {
338         return true;
339     }
340 
341     @Override
342     public boolean isDouble()
343     {
344         return false;
345     }
346 
347     /**
348      * Check whether the row and column are within bounds.
349      * @param row the row number
350      * @param col the column number
351      * @throws IndexOutOfBoundsException when row &gt; rows() or col &gt; cols() or row &lt; 0 or col &lt; 0
352      */
353     private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
354     {
355         Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row, this.rows);
356         Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
357                 this.cols);
358     }
359 
360     @Override
361     public double get(final int row, final int col)
362     {
363         checkRowCol(row, col);
364         return this.data[row * this.cols + col];
365     }
366 
367     @SuppressWarnings("checkstyle:needbraces")
368     @Override
369     public double[] getSiArray()
370     {
371         double[] doubleData = new double[this.data.length];
372         for (int i = 0; i < doubleData.length; i++)
373             doubleData[i] = this.data[i];
374         return doubleData;
375     }
376 
377     @Override
378     public double[] unsafeSiArray()
379     {
380         return getSiArray();
381     }
382 
383     @Override
384     public DenseFloatDataSi copy()
385     {
386         return new DenseFloatDataSi(this.data.clone(), rows(), cols());
387     }
388 
389     @SuppressWarnings("checkstyle:needbraces")
390     @Override
391     public int nonZeroCount()
392     {
393         int result = 0;
394         for (int i = 0; i < this.data.length; i++)
395             result += this.data[i] == 0.0f ? 0 : 1;
396         return result;
397     }
398 
399     @SuppressWarnings("checkstyle:needbraces")
400     @Override
401     public DenseFloatDataSi instantiateNew(final double[] newData)
402     {
403         Throw.when(newData.length != rows() * cols(), IllegalArgumentException.class,
404                 "Data object length != rows * cols, %d != %d * %d", newData.length, rows(), cols());
405         float[] floatData = new float[newData.length];
406         for (int i = 0; i < floatData.length; i++)
407             floatData[i] = (float) newData[i];
408         return new DenseFloatDataSi(floatData, rows(), cols());
409     }
410 
411     @SuppressWarnings("checkstyle:needbraces")
412     @Override
413     public DenseFloatDataSi instantiateNew(final double[] newData, final int newRows, final int newCols)
414     {
415         Throw.when(newData.length != newRows * newCols, IllegalArgumentException.class,
416                 "Data object length != rows * cols, %d != %d * %d", newData.length, newRows, newCols);
417         float[] floatData = new float[newData.length];
418         for (int i = 0; i < floatData.length; i++)
419             floatData[i] = (float) newData[i];
420         return new DenseFloatDataSi(floatData, 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         DenseFloatDataSi other = (DenseFloatDataSi) obj;
448         return this.cols == other.cols && this.rows == other.rows && Arrays.equals(this.data, other.data);
449     }
450 
451 }