View Javadoc
1   package org.djunits.vecmat.dnxm;
2   
3   import java.util.Objects;
4   
5   import org.djunits.quantity.SIQuantity;
6   import org.djunits.quantity.def.Quantity;
7   import org.djunits.unit.UnitInterface;
8   import org.djunits.unit.si.SIUnit;
9   import org.djunits.util.MatrixMath;
10  import org.djunits.vecmat.d1.Matrix1x1;
11  import org.djunits.vecmat.d1.Vector1;
12  import org.djunits.vecmat.d2.Matrix2x2;
13  import org.djunits.vecmat.d2.Vector2;
14  import org.djunits.vecmat.d3.Matrix3x3;
15  import org.djunits.vecmat.d3.Vector3;
16  import org.djunits.vecmat.def.Matrix;
17  import org.djunits.vecmat.dn.MatrixNxN;
18  import org.djunits.vecmat.dn.VectorN;
19  import org.djunits.vecmat.storage.DataGridSi;
20  import org.djunits.vecmat.storage.DenseDoubleDataSi;
21  import org.djunits.vecmat.storage.DenseFloatDataSi;
22  import org.djutils.exceptions.Throw;
23  
24  /**
25   * MatrixNxM implements a matrix with NxM real-valued entries. The matrix is immutable, except for the display unit, which can
26   * be changed. Internal storage can be float or double, and dense or sparse. MatrixNxN and VectorN extend from this class.
27   * <p>
28   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
29   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
30   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
31   * @author Alexander Verbraeck
32   * @param <Q> the quantity type
33   * @param <U> the unit type
34   */
35  public class MatrixNxM<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>>
36          extends Matrix<Q, U, MatrixNxM<Q, U>, MatrixNxM<SIQuantity, SIUnit>, MatrixNxM<?, ?>>
37  {
38      /** */
39      private static final long serialVersionUID = 600L;
40  
41      /** The data of the table, in SI unit. */
42      private final DataGridSi<?> dataSi;
43  
44      /**
45       * Create a new NxM Matrix with a unit, based on a DataGrid storage object that contains SI data.
46       * @param dataSi the data of the matrix, in SI unit.
47       * @param displayUnit the display unit to use
48       * @throws IllegalArgumentException when the number of rows or columns does not have a positive value
49       */
50      public MatrixNxM(final DataGridSi<?> dataSi, final U displayUnit)
51      {
52          super(displayUnit);
53          Throw.whenNull(dataSi, "dataSi");
54          this.dataSi = dataSi;
55      }
56  
57      /**
58       * Create a new MatrixNxM with a unit, based on a 1-dimensional double array.
59       * @param valueArrayInUnit the matrix values {a11, a12, ..., a1M, aN2, ..., aNM} expressed in the display unit
60       * @param displayUnit the display unit to use
61       * @param <Q> the quantity type
62       * @param <U> the unit type
63       * @param rows the number of rows in the valueArray
64       * @param cols the number of columns in the valueArray
65       * @return a new MatrixNxM with a unit
66       * @throws IllegalArgumentException when rows or cols is not positive, or when the number of entries in valueArray is not
67       *             equal to rows*cols
68       */
69      @SuppressWarnings("checkstyle:needbraces")
70      public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> MatrixNxM<Q, U> of(final double[] valueArrayInUnit,
71              final int rows, final int cols, final U displayUnit)
72      {
73          Throw.whenNull(valueArrayInUnit, "valueArrayInUnit");
74          Throw.whenNull(displayUnit, "displayUnit");
75          Throw.when(rows <= 0, IllegalArgumentException.class, "rows <= 0");
76          Throw.when(cols <= 0, IllegalArgumentException.class, "cols <= 0");
77          Throw.when(rows * cols != valueArrayInUnit.length, IllegalArgumentException.class,
78                  "valueArrayInUnit does not contain the correct number of entries (%d x %d != %d)", rows, cols,
79                  valueArrayInUnit.length);
80          double[] aSi = new double[rows * cols];
81          for (int i = 0; i < valueArrayInUnit.length; i++)
82              aSi[i] = displayUnit.toBaseValue(valueArrayInUnit[i]);
83          return new MatrixNxM<Q, U>(new DenseDoubleDataSi(aSi, rows, cols), displayUnit);
84      }
85  
86      /**
87       * Create a new MatrixNxM with a unit, based on a 2-dimensional double grid.
88       * @param valueGridInUnit the matrix values {{a11, a12, a1M}, ..., {aN1, aN2, aNM}} expressed in the display unit
89       * @param displayUnit the display unit to use
90       * @param <Q> the quantity type
91       * @param <U> the unit type
92       * @return a new MatrixNxM with a unit
93       * @throws IllegalArgumentException when valueGrid has 0 rows, or when the number of columns for one of the rows is not
94       *             equal to the number of columns in another row
95       */
96      @SuppressWarnings("checkstyle:needbraces")
97      public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> MatrixNxM<Q, U> of(final double[][] valueGridInUnit,
98              final U displayUnit)
99      {
100         Throw.whenNull(valueGridInUnit, "valueGridInUnit");
101         Throw.whenNull(displayUnit, "displayUnit");
102         int rows = valueGridInUnit.length;
103         Throw.when(rows == 0, IllegalArgumentException.class, "valueGridInUnit has 0 rows");
104         int cols = valueGridInUnit[0].length;
105         Throw.when(cols == 0, IllegalArgumentException.class, "row 0 in valueGridInUnit has 0 columns");
106         double[] aSi = new double[rows * cols];
107         for (int r = 0; r < rows; r++)
108         {
109             Throw.when(valueGridInUnit[r].length != cols, IllegalArgumentException.class,
110                     "valueGridInUnit is not a NxM array; row %d has a length of %d, not %d", r, valueGridInUnit[r].length,
111                     cols);
112             for (int c = 0; c < cols; c++)
113                 aSi[cols * r + c] = displayUnit.toBaseValue(valueGridInUnit[r][c]);
114         }
115         return new MatrixNxM<Q, U>(new DenseDoubleDataSi(aSi, rows, cols), displayUnit);
116     }
117 
118     /**
119      * Create a new MatrixNxM with a unit, based on a 2-dimensional quantity grid.
120      * @param quantityGrid the matrix values {{a11, a12, ..., a1M}, {aN2, ..., aNM}}, each with their own unit
121      * @param displayUnit the display unit to use for the resulting matrix
122      * @param <Q> the quantity type
123      * @param <U> the unit type
124      * @return a new MatrixNxM with a unit
125      * @throws IllegalArgumentException when rows or cols is not positive, or when the number of entries in quantityGrid is not
126      *             equal to rows*cols
127      */
128     @SuppressWarnings("checkstyle:needbraces")
129     public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> MatrixNxM<Q, U> of(final Q[][] quantityGrid,
130             final U displayUnit)
131     {
132         return new MatrixNxM<Q, U>(new DenseDoubleDataSi(quantityGrid), displayUnit);
133     }
134 
135     @Override
136     public MatrixNxM<Q, U> instantiateSi(final double[] siNew)
137     {
138         return new MatrixNxM<Q, U>(this.dataSi.instantiateNew(siNew), getDisplayUnit().getBaseUnit())
139                 .setDisplayUnit(getDisplayUnit());
140     }
141 
142     @Override
143     public MatrixNxM<SIQuantity, SIUnit> instantiateSi(final double[] siNew, final SIUnit siUnit)
144     {
145         return new MatrixNxM<SIQuantity, SIUnit>(this.dataSi.instantiateNew(siNew), siUnit);
146     }
147 
148     /**
149      * Return the internal datagrid object, so we can retrieve data from it.
150      * @return the internal datagrid object
151      */
152     public DataGridSi<?> getDataGrid()
153     {
154         return this.dataSi;
155     }
156 
157     @Override
158     public double[] si()
159     {
160         return this.dataSi.getDataArray();
161     }
162 
163     @Override
164     public double si(final int row, final int col) throws IndexOutOfBoundsException
165     {
166         checkRow(row);
167         checkCol(col);
168         return this.dataSi.get(row, col);
169     }
170 
171     @Override
172     public VectorN.Row<Q, U> getRowVector(final int row)
173     {
174         return VectorN.Row.ofSi(getRowSi(row), getDisplayUnit());
175     }
176 
177     @Override
178     public VectorN.Row<Q, U> mgetRowVector(final int mRow)
179     {
180         return VectorN.Row.ofSi(mgetRowSi(mRow), getDisplayUnit());
181     }
182 
183     @Override
184     public VectorN.Col<Q, U> getColumnVector(final int col)
185     {
186         return VectorN.Col.ofSi(getColumnSi(col), getDisplayUnit());
187     }
188 
189     @Override
190     public VectorN.Col<Q, U> mgetColumnVector(final int mCol)
191     {
192         return VectorN.Col.ofSi(mgetColumnSi(mCol), getDisplayUnit());
193     }
194 
195     @Override
196     public double[] getRowSi(final int row)
197     {
198         checkRow(row);
199         return this.dataSi.getRowArray(row);
200     }
201 
202     @Override
203     public double[] getColumnSi(final int col)
204     {
205         checkCol(col);
206         return this.dataSi.getColArray(col);
207     }
208 
209     @Override
210     public int rows()
211     {
212         return this.dataSi.rows();
213     }
214 
215     @Override
216     public int cols()
217     {
218         return this.dataSi.cols();
219     }
220 
221     @Override
222     public int hashCode()
223     {
224         return Objects.hash(this.dataSi);
225     }
226 
227     @SuppressWarnings("checkstyle:needbraces")
228     @Override
229     public boolean equals(final Object obj)
230     {
231         if (this == obj)
232             return true;
233         if (obj == null)
234             return false;
235         if (getClass() != obj.getClass())
236             return false;
237         MatrixNxM<?, ?> other = (MatrixNxM<?, ?>) obj;
238         return Objects.equals(this.dataSi, other.dataSi);
239     }
240 
241     // ---------------------------------------------- MULTIPLY() METHODS ----------------------------------------------
242 
243     /**
244      * Multiply this vector or matrix with a Matrix1x1, resulting in a MatrixNxM. The multiplication is a (Mx1) x (1x1) matrix
245      * multiplication resulting in an (Mx1) matrix.
246      * @param matrix the matrix to multiply with
247      * @return a MatrixNxM of an SIQuantity as the result of the matrix multiplication
248      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
249      *             matrix or vector to multiply with
250      */
251     public MatrixNxM<SIQuantity, SIUnit> multiply(final Matrix1x1<?, ?> matrix)
252     {
253         checkMultiply(matrix);
254         double[] result = MatrixMath.multiply(si(), matrix.si(), rows(), cols(), matrix.cols());
255         SIUnit siUnit = getDisplayUnit().siUnit().plus(matrix.getDisplayUnit().siUnit());
256         return new MatrixNxM<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), matrix.cols()), siUnit);
257     }
258 
259     /**
260      * Multiply this vector or matrix with a Matrix2x2, resulting in a MatrixNxM. The multiplication is a (Mx2) x (2x2) matrix
261      * multiplication resulting in an (Mx2) matrix.
262      * @param matrix the matrix to multiply with
263      * @return a MatrixNxM of an SIQuantity as the result of the matrix multiplication
264      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
265      *             matrix or vector to multiply with
266      */
267     public MatrixNxM<SIQuantity, SIUnit> multiply(final Matrix2x2<?, ?> matrix)
268     {
269         checkMultiply(matrix);
270         double[] result = MatrixMath.multiply(si(), matrix.si(), rows(), cols(), matrix.cols());
271         SIUnit siUnit = getDisplayUnit().siUnit().plus(matrix.getDisplayUnit().siUnit());
272         return new MatrixNxM<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), matrix.cols()), siUnit);
273     }
274 
275     /**
276      * Multiply this vector or matrix with a Matrix3x3, resulting in a MatrixNxM. The multiplication is a (Mx3) x (3x3) matrix
277      * multiplication resulting in an (Mx3) matrix.
278      * @param matrix the matrix to multiply with
279      * @return a MatrixNxM of an SIQuantity as the result of the matrix multiplication
280      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
281      *             matrix or vector to multiply with
282      */
283     public MatrixNxM<SIQuantity, SIUnit> multiply(final Matrix3x3<?, ?> matrix)
284     {
285         checkMultiply(matrix);
286         double[] result = MatrixMath.multiply(si(), matrix.si(), rows(), cols(), matrix.cols());
287         SIUnit siUnit = getDisplayUnit().siUnit().plus(matrix.getDisplayUnit().siUnit());
288         return new MatrixNxM<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), matrix.cols()), siUnit);
289     }
290 
291     /**
292      * Multiply this vector or matrix with a MatrixNxM, resulting in a MatrixNxM. The multiplication is a (NxM) x (MxP) matrix
293      * multiplication resulting in an (NxP) matrix.
294      * @param matrix the matrix to multiply with
295      * @return a MatrixNxM of an SIQuantity as the result of the matrix multiplication
296      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
297      *             matrix or vector to multiply with
298      */
299     public MatrixNxM<SIQuantity, SIUnit> multiply(final MatrixNxN<?, ?> matrix)
300     {
301         checkMultiply(matrix);
302         double[] result = MatrixMath.multiply(si(), matrix.si(), rows(), cols(), matrix.cols());
303         SIUnit siUnit = getDisplayUnit().siUnit().plus(matrix.getDisplayUnit().siUnit());
304         if (matrix.getDataGrid().isDouble())
305         {
306             return new MatrixNxM<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), matrix.cols()), siUnit);
307         }
308         return new MatrixNxM<SIQuantity, SIUnit>(new DenseFloatDataSi(result, rows(), matrix.cols()), siUnit);
309     }
310 
311     /**
312      * Multiply this vector or matrix with a Vector1, resulting in a MatrixNxM. The multiplication is a (Mx1) x (1x1) matrix
313      * multiplication resulting in an (Mx1) matrix.
314      * @param vector the vector to multiply with
315      * @return a MatrixNxM of an SIQuantity as the result of the matrix multiplication
316      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
317      *             vector to multiply with
318      */
319     public MatrixNxM<SIQuantity, SIUnit> multiply(final Vector1<?, ?> vector)
320     {
321         checkMultiply(vector);
322         double[] result = MatrixMath.multiply(si(), vector.si(), rows(), cols(), vector.cols());
323         SIUnit siUnit = getDisplayUnit().siUnit().plus(vector.getDisplayUnit().siUnit());
324         return new MatrixNxM<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), vector.cols()), siUnit);
325     }
326 
327     /**
328      * Multiply this vector or matrix with a Vector2.Col, resulting in a Vector2.Col. The multiplication is a (Mx2) x (2x1)
329      * matrix multiplication resulting in an (Mx1) column vector.
330      * @param vector the vector to multiply with
331      * @return a VectorN.Col of an SIQuantity as the result of the matrix multiplication
332      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
333      *             vector to multiply with
334      */
335     public VectorN.Col<SIQuantity, SIUnit> multiply(final Vector2.Col<?, ?> vector)
336     {
337         checkMultiply(vector);
338         double[] result = MatrixMath.multiply(si(), vector.si(), rows(), cols(), vector.cols());
339         SIUnit siUnit = getDisplayUnit().siUnit().plus(vector.getDisplayUnit().siUnit());
340         return new VectorN.Col<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), vector.cols()), siUnit);
341     }
342 
343     /**
344      * Multiply this vector or matrix with a Vector3.Col, resulting in a Vector3.Col. The multiplication is a (Mx3) x (3x1)
345      * matrix multiplication resulting in an (Mx1) column vector.
346      * @param vector the vector to multiply with
347      * @return a VectorN.Col of an SIQuantity as the result of the matrix multiplication
348      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
349      *             vector to multiply with
350      */
351     public VectorN.Col<SIQuantity, SIUnit> multiply(final Vector3.Col<?, ?> vector)
352     {
353         checkMultiply(vector);
354         double[] result = MatrixMath.multiply(si(), vector.si(), rows(), cols(), vector.cols());
355         SIUnit siUnit = getDisplayUnit().siUnit().plus(vector.getDisplayUnit().siUnit());
356         return new VectorN.Col<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), vector.cols()), siUnit);
357     }
358 
359     /**
360      * Multiply this vector or matrix with a VectorN.Col, resulting in a VectorN.Col. The multiplication is a (MxN) x (Nx1)
361      * matrix multiplication resulting in an (Mx1) column vector.
362      * @param vector the vector to multiply with
363      * @return a VectorN.Col of an SIQuantity as the result of the matrix multiplication
364      * @throws IllegalArgumentException when the number of columns of this matrix does not equal the number of rows of the
365      *             vector to multiply with
366      */
367     public VectorN.Col<SIQuantity, SIUnit> multiply(final VectorN.Col<?, ?> vector)
368     {
369         checkMultiply(vector);
370         double[] result = MatrixMath.multiply(si(), vector.si(), rows(), cols(), vector.cols());
371         SIUnit siUnit = getDisplayUnit().siUnit().plus(vector.getDisplayUnit().siUnit());
372         return new VectorN.Col<SIQuantity, SIUnit>(new DenseDoubleDataSi(result, rows(), vector.cols()), siUnit);
373     }
374 
375     // ------------------------------------------------- AS() METHODS -------------------------------------------------
376 
377     /**
378      * Return the matrix 'as' a matrix with a known quantity, using a unit to express the result in. Throw a Runtime exception
379      * when the SI units of this vector and the target vector do not match.
380      * @param targetUnit the unit to convert the matrix to
381      * @return a matrix typed in the target matrix class
382      * @throws IllegalArgumentException when the units do not match
383      * @param <TQ> target quantity type
384      * @param <TU> target unit type
385      */
386     public <TQ extends Quantity<TQ, TU>, TU extends UnitInterface<TU, TQ>> MatrixNxM<TQ, TU> as(final TU targetUnit)
387             throws IllegalArgumentException
388     {
389         Throw.when(!getDisplayUnit().siUnit().equals(targetUnit.siUnit()), IllegalArgumentException.class,
390                 "MatrixNxM.as(%s) called, but units do not match: %s <> %s", targetUnit,
391                 getDisplayUnit().siUnit().getDisplayAbbreviation(), targetUnit.siUnit().getDisplayAbbreviation());
392         return new MatrixNxM<TQ, TU>(this.dataSi.instantiateNew(si()), targetUnit.getBaseUnit()).setDisplayUnit(targetUnit);
393     }
394 
395     /**
396      * Convert this matrix to a {@link Matrix1x1}. The shape must be 1 x 1.
397      * @return a {@code Matrix1x1} with identical SI data and display unit
398      * @throws IllegalStateException if this matrix is not 1 x 1
399      */
400     public Matrix1x1<Q, U> asMatrix1x1()
401     {
402         Throw.when(rows() != 1 || cols() != 1, IllegalStateException.class,
403                 "asMatrix1x1() called, but matrix is no 1x1 but %dx%d", rows(), cols());
404         return Matrix1x1.of(si(), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
405     }
406 
407     /**
408      * Convert this matrix to a {@link Matrix2x2}. The shape must be 2 x 2.
409      * @return a {@code Matrix2x2} with identical SI data and display unit
410      * @throws IllegalStateException if this matrix is not 2 x 2
411      */
412     public Matrix2x2<Q, U> asMatrix2x2()
413     {
414         Throw.when(rows() != 2 || cols() != 2, IllegalStateException.class,
415                 "asMatrix2x2() called, but matrix is no 2x2 but %dx%d", rows(), cols());
416         return Matrix2x2.of(si(), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
417     }
418 
419     /**
420      * Convert this matrix to a {@link Matrix3x3}. The shape must be 3 x 3.
421      * @return a {@code Matrix3x3} with identical SI data and display unit
422      * @throws IllegalStateException if this matrix is not 3 x 3
423      */
424     public Matrix3x3<Q, U> asMatrix3x3()
425     {
426         Throw.when(rows() != 3 || cols() != 3, IllegalStateException.class,
427                 "asMatrix3x3() called, but matrix is no 3x3 but %dx%d", rows(), cols());
428         return Matrix3x3.of(si(), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
429     }
430 
431     /**
432      * Convert this matrix to a {@link MatrixNxN}. The shape must be square.
433      * @return a {@code MatrixNxN} with identical SI data and display unit
434      * @throws IllegalStateException if this matrix is not square
435      */
436     public MatrixNxN<Q, U> asMatrixNxN()
437     {
438         Throw.when(rows() != cols(), IllegalStateException.class, "asMatrixNxN() called, but matrix is no square but %dx%d",
439                 rows(), cols());
440         return new MatrixNxN<Q, U>(new DenseDoubleDataSi(si(), rows(), cols()), getDisplayUnit().getBaseUnit())
441                 .setDisplayUnit(getDisplayUnit());
442     }
443 
444     /**
445      * Return this matrix as a 1-element column vector. Shape must be 1 x 1.
446      * @return a {@code Vector1} with identical SI data and display unit
447      * @throws IllegalStateException if shape is not 1 x 1
448      */
449     public Vector1<Q, U> asVector1()
450     {
451         Throw.when(rows() != 1 || cols() != 1, IllegalStateException.class, "Matrix is not 1x1");
452         final double[] data = si();
453         return new Vector1<Q, U>(data[0], getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
454     }
455 
456     /**
457      * Return this matrix as a 2-element column vector. Shape must be 2 x 1.
458      * @return a {@code Vector2.Col} with identical SI data and display unit
459      * @throws IllegalStateException if shape is not 2 x 1
460      */
461     public Vector2.Col<Q, U> asVector2Col()
462     {
463         Throw.when(rows() != 2 || cols() != 1, IllegalStateException.class, "Matrix is not 2x1");
464         final double[] data = si();
465         return new Vector2.Col<Q, U>(data[0], data[1], getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
466     }
467 
468     /**
469      * Return this matrix as a 3-element column vector. Shape must be 3 x 1.
470      * @return a {@code Vector3.Col} with identical SI data and display unit
471      * @throws IllegalStateException if shape is not 3 x 1
472      */
473     public Vector3.Col<Q, U> asVector3Col()
474     {
475         Throw.when(rows() != 3 || cols() != 1, IllegalStateException.class, "Matrix is not 3x1");
476         final double[] data = si();
477         return new Vector3.Col<Q, U>(data[0], data[1], data[2], getDisplayUnit().getBaseUnit())
478                 .setDisplayUnit(getDisplayUnit());
479     }
480 
481     /**
482      * Return this matrix as an N-element column vector. Shape must be N x 1.
483      * @return a {@code VectorN.Col} with identical SI data and display unit
484      * @throws IllegalStateException if {@code cols() != 1}
485      */
486     public VectorN.Col<Q, U> asVectorNCol()
487     {
488         Throw.when(cols() != 1, IllegalStateException.class, "Matrix is not Nx1");
489         return VectorN.Col.ofSi(si(), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
490     }
491 
492     /**
493      * Return this matrix as a 2-element row vector. Shape must be 1 x 2.
494      * @return a {@code Vector2.Row} with identical SI data and display unit
495      * @throws IllegalStateException if shape is not 1 x 2
496      */
497     public Vector2.Row<Q, U> asVector2Row()
498     {
499         Throw.when(rows() != 1 || cols() != 2, IllegalStateException.class, "Matrix is not 1x2");
500         final double[] data = si();
501         return new Vector2.Row<Q, U>(data[0], data[1], getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
502     }
503 
504     /**
505      * Return this matrix as a 3-element row vector. Shape must be 1 x 3.
506      * @return a {@code Vector3.Row} with identical SI data and display unit
507      * @throws IllegalStateException if shape is not 1 x 3
508      */
509     public Vector3.Row<Q, U> asVector3Row()
510     {
511         Throw.when(rows() != 1 || cols() != 3, IllegalStateException.class, "Matrix is not 1x3");
512         final double[] data = si();
513         return new Vector3.Row<Q, U>(data[0], data[1], data[2], getDisplayUnit().getBaseUnit())
514                 .setDisplayUnit(getDisplayUnit());
515     }
516 
517     /**
518      * Return this matrix as an N-element row vector. Shape must be 1 x N.
519      * @return a {@code VectorN.Row} with identical SI data and display unit
520      * @throws IllegalStateException if {@code rows() != 1}
521      */
522     public VectorN.Row<Q, U> asVectorNRow()
523     {
524         Throw.when(rows() != 1, IllegalStateException.class, "Matrix is not 1xN");
525         return VectorN.Row.of(si(), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
526     }
527 
528 }