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