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.UnitInterface;
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[].
13   * <p>
14   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
15   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
16   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
17   * @author Alexander Verbraeck
18   */
19  public class DenseFloatDataSi implements DataGridSi<DenseFloatDataSi>
20  {
21      /** The data stored in row-major format. */
22      private final float[] data;
23  
24      /** The number of rows. */
25      private final int rows;
26  
27      /** the number of columns. */
28      private final int cols;
29  
30      /**
31       * Instantiate a data object with one array in row-major format. Note that NO safe copy of the data is stored.
32       * @param data the data in row-major format
33       * @param rows the number of rows
34       * @param cols the number of columns
35       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
36       *             or columns is not positive
37       */
38      public DenseFloatDataSi(final float[] data, final int rows, final int cols)
39      {
40          Throw.whenNull(data, "data");
41          Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
42          Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
43          Throw.when(data.length != rows * cols, IllegalArgumentException.class,
44                  "Data object length != rows * cols, %d != %d * %d", data.length, rows, cols);
45          this.data = data;
46          this.rows = rows;
47          this.cols = cols;
48      }
49  
50      /**
51       * Instantiate a data object with one array in row-major format. Note that NO safe copy of the data is stored.
52       * @param data the data in row-major format
53       * @param rows the number of rows
54       * @param cols the number of columns
55       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
56       *             or columns is not positive
57       */
58      public DenseFloatDataSi(final double[] data, final int rows, final int cols)
59      {
60          Throw.whenNull(data, "data");
61          Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
62          Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
63          Throw.when(data.length != rows * cols, IllegalArgumentException.class,
64                  "Data object length != rows * cols, %d != %d * %d", data.length, rows, cols);
65          this.data = new float[data.length];
66          for (int i = 0; i < data.length; i++)
67          {
68              this.data[i] = (float) data[i];
69          }
70          this.rows = rows;
71          this.cols = cols;
72      }
73  
74      /**
75       * Instantiate a data object with a float[rows][cols]. A safe copy of the data is stored.
76       * @param data the data in row-major format
77       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
78       */
79      @SuppressWarnings("checkstyle:needbraces")
80      public DenseFloatDataSi(final float[][] data)
81      {
82          Throw.whenNull(data, "data");
83          Throw.when(data.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
84          this.rows = data.length;
85          this.cols = data[0].length;
86          for (int r = 1; r < this.rows; r++)
87              Throw.when(data[r].length != this.cols, IllegalArgumentException.class,
88                      "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, data[r].length,
89                      this.cols);
90          this.data = new float[this.rows * this.cols];
91          for (int r = 0; r < this.rows; r++)
92              for (int c = 0; c < this.cols; c++)
93                  this.data[r * this.cols + c] = data[r][c];
94      }
95  
96      /**
97       * Instantiate a data object with a Q[rows][cols]. A safe copy of the data is stored.
98       * @param data the data in row-major format
99       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
100      * @param <Q> the quantity type
101      * @param <U> the unit type
102      */
103     @SuppressWarnings("checkstyle:needbraces")
104     public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> DenseFloatDataSi(final Q[][] data)
105     {
106         Throw.whenNull(data, "data");
107         Throw.when(data.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
108         this.rows = data.length;
109         this.cols = data[0].length;
110         this.data = new float[this.rows * this.cols];
111         for (int r = 0; r < this.rows; r++)
112         {
113             Throw.when(data[r].length != this.cols, IllegalArgumentException.class,
114                     "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, data[r].length,
115                     this.cols);
116             for (int c = 0; c < this.cols; c++)
117                 this.data[r * this.cols + c] = data[r][c].floatValue();
118         }
119     }
120 
121     @Override
122     public int rows()
123     {
124         return this.rows;
125     }
126 
127     @Override
128     public int cols()
129     {
130         return this.cols;
131     }
132 
133     @Override
134     public boolean isDense()
135     {
136         return true;
137     }
138 
139     @Override
140     public boolean isDouble()
141     {
142         return false;
143     }
144 
145     /**
146      * Check whether the row and column are within bounds.
147      * @param row the row number
148      * @param col the column number
149      * @throws IndexOutOfBoundsException when row &gt; rows() or col &gt; cols() or row &lt; 0 or col &lt; 0
150      */
151     private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
152     {
153         Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row, this.rows);
154         Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
155                 this.cols);
156     }
157 
158     @Override
159     public double get(final int row, final int col)
160     {
161         checkRowCol(row, col);
162         return this.data[row * this.cols + col];
163     }
164 
165     @SuppressWarnings("checkstyle:needbraces")
166     @Override
167     public double[] getDataArray()
168     {
169         double[] doubleData = new double[this.data.length];
170         for (int i = 0; i < doubleData.length; i++)
171             doubleData[i] = this.data[i];
172         return doubleData;
173     }
174 
175     @Override
176     public DenseFloatDataSi copy()
177     {
178         return new DenseFloatDataSi(this.data.clone(), rows(), cols());
179     }
180 
181     @SuppressWarnings("checkstyle:needbraces")
182     @Override
183     public int cardinality()
184     {
185         int result = 0;
186         for (int i = 0; i < this.data.length; i++)
187             result += this.data[i] == 0.0f ? 0 : 1;
188         return result;
189     }
190 
191     @SuppressWarnings("checkstyle:needbraces")
192     @Override
193     public DenseFloatDataSi instantiateNew(final double[] newData)
194     {
195         Throw.when(newData.length != rows() * cols(), IllegalArgumentException.class,
196                 "Data object length != rows * cols, %d != %d * %d", newData.length, rows(), cols());
197         float[] floatData = new float[newData.length];
198         for (int i = 0; i < floatData.length; i++)
199             floatData[i] = (float) newData[i];
200         return new DenseFloatDataSi(floatData, rows(), cols());
201     }
202 
203     @SuppressWarnings("checkstyle:needbraces")
204     @Override
205     public DenseFloatDataSi instantiateNew(final double[] newData, final int newRows, final int newCols)
206     {
207         Throw.when(newData.length != newRows * newCols, IllegalArgumentException.class,
208                 "Data object length != rows * cols, %d != %d * %d", newData.length, newRows, newCols);
209         float[] floatData = new float[newData.length];
210         for (int i = 0; i < floatData.length; i++)
211             floatData[i] = (float) newData[i];
212         return new DenseFloatDataSi(floatData, newRows, newCols);
213     }
214 
215     @Override
216     public int hashCode()
217     {
218         final int prime = 31;
219         int result = 1;
220         result = prime * result + Arrays.hashCode(getDataArray());
221         result = prime * result + Objects.hash(this.cols, this.rows);
222         return result;
223     }
224 
225     @SuppressWarnings("checkstyle:needbraces")
226     @Override
227     public boolean equals(final Object obj)
228     {
229         if (this == obj)
230             return true;
231         if (obj == null)
232             return false;
233         if (getClass() != obj.getClass())
234         {
235             if (obj instanceof DataGridSi dg)
236                 return this.cols == dg.cols() && this.rows == dg.rows() && Arrays.equals(getDataArray(), dg.getDataArray());
237             return false;
238         }
239         DenseFloatDataSi other = (DenseFloatDataSi) obj;
240         return this.cols == other.cols && this.rows == other.rows && Arrays.equals(this.data, other.data);
241     }
242 
243 }