View Javadoc
1   package org.djunits.vecmat.def;
2   
3   import java.lang.reflect.Array;
4   
5   import org.djunits.quantity.SIQuantity;
6   import org.djunits.quantity.def.Quantity;
7   import org.djunits.unit.Unit;
8   import org.djutils.exceptions.Throw;
9   
10  /**
11   * Table contains a number of standard operations on 2-dimensional tables of relative quantities. The Matrix and QuantityTable
12   * are examples of classes that extends Table.
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   * @param <Q> the quantity type
19   * @param <T> the 'SELF' table type
20   * @param <SI> the table type with generics &lt;SIQuantity, SIUnit&lt;
21   * @param <H> the generic table type with generics &lt;?, ?&lt; for Hadamard operations
22   * @param <TT> the type of the transposed version of the table
23   */
24  public abstract class Table<Q extends Quantity<Q>, T extends Table<Q, T, SI, H, TT>, SI extends Table<SIQuantity, SI, ?, ?, ?>,
25          H extends Table<?, ?, ?, ?, ?>, TT extends Table<Q, TT, ?, ?, T>> extends VectorMatrix<Q, T, SI, H, TT>
26  {
27      /** */
28      private static final long serialVersionUID = 600L;
29  
30      /**
31       * Create a new Table with a unit.
32       * @param displayUnit the display unit to use
33       */
34      public Table(final Unit<?, Q> displayUnit)
35      {
36          super(displayUnit);
37      }
38  
39      /**
40       * Return the si-value at position (row, col), where both row and col are 0-based values.
41       * @param row the row (0-based)
42       * @param col the column (0-based)
43       * @return the si-value at position (row, col)
44       * @throws IndexOutOfBoundsException when row or col &lt; 0 or larger than number of rows/columns - 1.
45       */
46      public abstract double si(int row, int col) throws IndexOutOfBoundsException;
47  
48      /**
49       * Return the si-value at position (row, col), where both row and col are 1-based values.
50       * @param mRow the row (1-based)
51       * @param mCol the column (1-based)
52       * @return the si-value at position (row, col)
53       * @throws IndexOutOfBoundsException when row or col &lt; 1 or larger than number of rows/columns.
54       */
55      public double msi(final int mRow, final int mCol) throws IndexOutOfBoundsException
56      {
57          return si(mRow - 1, mCol - 1);
58      }
59  
60      /**
61       * Return the quantity at position (row, col), where both row and col are 0-based values.
62       * @param row the row (0-based)
63       * @param col the column (0-based)
64       * @return the quantity at position (row, col)
65       * @throws IndexOutOfBoundsException when row or col &lt; 0 or larger than number of rows/columns - 1.
66       */
67      public Q get(final int row, final int col) throws IndexOutOfBoundsException
68      {
69          return getDisplayUnit().ofSi(si(row, col)).setDisplayUnit(getDisplayUnit());
70      }
71  
72      /**
73       * Return the quantity at position (row, col), where both row and col are 1-based values.
74       * @param mRow the row (1-based)
75       * @param mCol the column (1-based)
76       * @return the quantity at position (row, col)
77       * @throws IndexOutOfBoundsException when row or col &lt; 1 or larger than number of rows/columns.
78       */
79      public Q mget(final int mRow, final int mCol) throws IndexOutOfBoundsException
80      {
81          return getDisplayUnit().ofSi(msi(mRow, mCol)).setDisplayUnit(getDisplayUnit());
82      }
83  
84      /**
85       * Return a row-major array of quantities for this QuantityTable.
86       * @return a row-major array of quantities for this QuantityTable
87       */
88      @SuppressWarnings("unchecked")
89      public Q[] getScalarArray()
90      {
91          // Determine the runtime type of Q using the first cell; constructors guarantee rows, cols >= 0.
92          final Q first = get(0, 0);
93          final Class<?> qClass = first.getClass();
94  
95          // Allocate a Q[rows() * cols()] array and fill it.
96          final Q[] out = (Q[]) Array.newInstance(qClass, rows() * cols());
97          double[] dataSi = unsafeSiArray();
98          var unit = getDisplayUnit();
99          for (int i = 0; i < rows() * cols(); i++)
100         {
101             out[i] = unit.ofSi(dataSi[i]).setDisplayUnit(unit);
102         }
103         return out;
104     }
105 
106     /**
107      * Return the table or matrix as a 2D array of scalars.
108      * @return a new Q[rows()][cols()] array; entry [i][j] contains get(i, j).
109      */
110     @SuppressWarnings("unchecked") // cast from Array.newInstance(...) to Q[][]
111     public Q[][] getScalarGrid()
112     {
113         // Determine the runtime type of Q using the first cell; constructors guarantee rows, cols >= 0.
114         final Q first = get(0, 0);
115         final Class<?> qClass = first.getClass();
116 
117         // Allocate a Q[rows()][cols()] array and fill it.
118         final Q[][] out = (Q[][]) Array.newInstance(qClass, rows(), cols());
119         for (int i = 0; i < rows(); i++)
120         {
121             for (int j = 0; j < cols(); j++)
122             {
123                 out[i][j] = get(i, j);
124             }
125         }
126         return out;
127     }
128 
129     /**
130      * Return the table or matrix as a 2D array of double SI values.
131      * @return a new double[rows()][cols()] array; entry [i][j] contains si(i, j).
132      */
133     public double[][] getSiGrid()
134     {
135         // Allocate a double[rows()][cols()] array and fill it.
136         final double[][] out = (double[][]) Array.newInstance(double.class, rows(), cols());
137         for (int i = 0; i < rows(); i++)
138         {
139             for (int j = 0; j < cols(); j++)
140             {
141                 out[i][j] = si(i, j);
142             }
143         }
144         return out;
145     }
146 
147     /**
148      * Return a quantity row (0-based) from the table or matrix. Note that the specific vector to return can be tightened by the
149      * implementing class.
150      * @param row the row number to retrieve (0-based)
151      * @return a row vector with the data at the given row
152      */
153     public abstract Vector<Q, ?, ?, ?, ?> getRowVector(int row);
154 
155     /**
156      * Return a quantity row (1-based) from the table or matrix. Note that the specific vector to return can be tightened by the
157      * implementing class.
158      * @param mRow the row number to retrieve (1-based)
159      * @return a row vector with the data at the given row
160      */
161     public abstract Vector<Q, ?, ?, ?, ?> mgetRowVector(int mRow);
162 
163     /**
164      * Return a quantity column (0-based) from the table or matrix. Note that the specific vector to return can be tightened by
165      * the implementing class.
166      * @param col the column number to retrieve (0-based)
167      * @return a column vector with the data at the given column
168      */
169     public abstract Vector<Q, ?, ?, ?, ?> getColumnVector(int col);
170 
171     /**
172      * Return a quantity column (1-based) from the table or matrix. Note that the specific vector to return can be tightened by
173      * the implementing class.
174      * @param mCol the column number to retrieve (1-based)
175      * @return a column vector with the data at the given column
176      */
177     public abstract Vector<Q, ?, ?, ?, ?> mgetColumnVector(int mCol);
178 
179     /**
180      * Return an array with SI-values for the given row (0-based) from the table or matrix.
181      * @param row the row number to retrieve (0-based)
182      * @return an array with SI-values with the data at the given row
183      */
184     public abstract double[] getRowSi(int row);
185 
186     /**
187      * Return an array with SI-values for the given row (1-based) from the table or matrix.
188      * @param mRow the row number to retrieve (1-based)
189      * @return an array with SI-values with the data at the given row
190      */
191     public double[] mgetRowSi(final int mRow)
192     {
193         mcheckRow(mRow);
194         return getRowSi(mRow - 1);
195     }
196 
197     /**
198      * Return an array with SI-values for the given column (0-based) from the table or matrix.
199      * @param col the column number to retrieve (0-based)
200      * @return an array with SI-values with the data at the given column
201      */
202     public abstract double[] getColumnSi(int col);
203 
204     /**
205      * Return an array with SI-values for the given column (1-based) from the table or matrix.
206      * @param mCol the column number to retrieve (1-based)
207      * @return an array with SI-values with the data at the given column
208      */
209     public double[] mgetColumnSi(final int mCol)
210     {
211         mcheckCol(mCol);
212         return getColumnSi(mCol - 1);
213     }
214 
215     /**
216      * Retrieve a row (0-based) from the table or matrix as an array of scalars.
217      * @param row row of the values to retrieve (0-based)
218      * @return the row as a Scalar array
219      * @throws IndexOutOfBoundsException in case row is out of bounds
220      */
221     @SuppressWarnings("unchecked")
222     public Q[] getRowScalars(final int row) throws IndexOutOfBoundsException
223     {
224         checkRow(row);
225 
226         // Build a Q[] of length cols() using the runtime class of the first element
227         Q first = get(row, 0);
228         Q[] out = (Q[]) Array.newInstance(first.getClass(), cols());
229         for (int c = 0; c < cols(); c++)
230         {
231             out[c] = get(row, c);
232         }
233         return out;
234     }
235 
236     /**
237      * Retrieve a row (1-based) from the table or matrix as an array of scalars.
238      * @param mRow row of the values to retrieve (1-based)
239      * @return the row as a Scalar array
240      * @throws IndexOutOfBoundsException in case row is out of bounds
241      */
242     public Q[] mgetRowScalars(final int mRow) throws IndexOutOfBoundsException
243     {
244         mcheckRow(mRow);
245         return getRowScalars(mRow - 1);
246     }
247 
248     /**
249      * Retrieve a column (0-based) from the table or matrix as an array of scalars.
250      * @param col column of the values to retrieve (0-based)
251      * @return the column as a Scalar array
252      * @throws IndexOutOfBoundsException in case column is out of bounds
253      */
254     @SuppressWarnings("unchecked")
255     public Q[] getColumnScalars(final int col) throws IndexOutOfBoundsException
256     {
257         checkCol(col);
258 
259         Q first = get(0, col);
260         Q[] out = (Q[]) Array.newInstance(first.getClass(), rows());
261         for (int r = 0; r < rows(); r++)
262         {
263             out[r] = get(r, col);
264         }
265         return out;
266     }
267 
268     /**
269      * Retrieve a column (1-based) from the table or matrix as an array of scalars.
270      * @param mCol column of the values to retrieve (1-based)
271      * @return the column as a Scalar array
272      * @throws IndexOutOfBoundsException in case column is out of bounds
273      */
274     public Q[] mgetColumnScalars(final int mCol) throws IndexOutOfBoundsException
275     {
276         mcheckCol(mCol);
277         return getColumnScalars(mCol - 1);
278     }
279 
280     // ------------------------------------ HELPER METHODS ------------------------------------
281 
282     /**
283      * Check if the 0-based row is within bounds.
284      * @param row the 0-based row to check
285      * @throws IndexOutOfBoundsException when row is out of bounds
286      */
287     protected void checkRow(final int row)
288     {
289         Throw.when(row < 0 || row >= rows(), IndexOutOfBoundsException.class, "Row %d out of bounds [0..%d]", row, rows() - 1);
290     }
291 
292     /**
293      * Check if the 0-based column is within bounds.
294      * @param col the 0-based column to check
295      * @throws IndexOutOfBoundsException when column is out of bounds
296      */
297     protected void checkCol(final int col)
298     {
299         Throw.when(col < 0 || col >= cols(), IndexOutOfBoundsException.class, "Column %d out of bounds [0..%d]", col,
300                 cols() - 1);
301     }
302 
303     /**
304      * Check if the 1-based row is within bounds.
305      * @param mRow the 1-based row to check
306      * @throws IndexOutOfBoundsException when row is out of bounds
307      */
308     protected void mcheckRow(final int mRow)
309     {
310         Throw.when(mRow < 1 || mRow > rows(), IndexOutOfBoundsException.class, "Row %d out of bounds [1..%d]", mRow, rows());
311     }
312 
313     /**
314      * Check if the 1-based column is within bounds.
315      * @param mCol the 1-based column to check
316      * @throws IndexOutOfBoundsException when column is out of bounds
317      */
318     protected void mcheckCol(final int mCol)
319     {
320         Throw.when(mCol < 1 || mCol > cols(), IndexOutOfBoundsException.class, "Column %d out of bounds [1..%d]", mCol, cols());
321     }
322 
323 }