View Javadoc
1   package org.djunits.vecmat.def;
2   
3   import java.util.Objects;
4   
5   import org.djunits.quantity.def.AbsQuantity;
6   import org.djunits.quantity.def.Quantity;
7   import org.djunits.quantity.def.Reference;
8   import org.djunits.unit.Unit;
9   import org.djunits.util.ArrayMath;
10  import org.djunits.value.Value;
11  import org.djunits.vecmat.d1.AbsMatrix1x1;
12  import org.djunits.vecmat.d1.AbsVector1;
13  import org.djunits.vecmat.d1.Matrix1x1;
14  import org.djunits.vecmat.d1.Vector1;
15  import org.djunits.vecmat.d2.AbsMatrix2x2;
16  import org.djunits.vecmat.d2.AbsVector2;
17  import org.djunits.vecmat.d2.Matrix2x2;
18  import org.djunits.vecmat.d2.Vector2;
19  import org.djunits.vecmat.d3.AbsMatrix3x3;
20  import org.djunits.vecmat.d3.AbsVector3;
21  import org.djunits.vecmat.d3.Matrix3x3;
22  import org.djunits.vecmat.d3.Vector3;
23  import org.djunits.vecmat.dn.AbsMatrixNxN;
24  import org.djunits.vecmat.dn.AbsVectorN;
25  import org.djunits.vecmat.dn.MatrixNxN;
26  import org.djunits.vecmat.dn.VectorN;
27  import org.djunits.vecmat.dnxm.AbsMatrixNxM;
28  import org.djunits.vecmat.dnxm.MatrixNxM;
29  import org.djunits.vecmat.table.AbsQuantityTable;
30  import org.djunits.vecmat.table.QuantityTable;
31  import org.djutils.exceptions.Throw;
32  
33  /**
34   * AbsVectorMatrix contains a number of standard operations on vectors and matrices of absolute quantities.
35   * <p>
36   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
37   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
38   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
39   * @author Alexander Verbraeck
40   * @param <A> the absolute quantity type
41   * @param <Q> the quantity type
42   * @param <VMA> the absolute vector or matrix type
43   * @param <VMQ> the relative vector or matrix type
44   * @param <VMAT> the type of the transposed version of the absolute vector or matrix
45   */
46  public abstract class AbsVectorMatrix<A extends AbsQuantity<A, Q, ?>, Q extends Quantity<Q>,
47          VMA extends AbsVectorMatrix<A, Q, VMA, VMQ, VMAT>, VMQ extends VectorMatrix<Q, VMQ, ?, ?, ?>,
48          VMAT extends AbsVectorMatrix<A, Q, VMAT, ?, VMA>> implements Value<VMA, Q>
49  {
50      /** */
51      private static final long serialVersionUID = 600L;
52  
53      /** The underlying relative vector or matrix with SI values relative to the reference point. */
54      private final VMQ relativeVecMat;
55  
56      /** The reference point for the absolute values. */
57      private final Reference<?, A, Q> reference;
58  
59      /**
60       * Create a new vector or matrix of absolute values with a reference point.
61       * @param relativeVecMat the underlying relative vector or matrix with SI values relative to the reference point
62       * @param reference the reference point for the absolute values
63       */
64      public AbsVectorMatrix(final VMQ relativeVecMat, final Reference<?, A, Q> reference)
65      {
66          Throw.whenNull(relativeVecMat, "relativeVecMat");
67          Throw.whenNull(reference, "reference");
68          this.relativeVecMat = relativeVecMat;
69          this.reference = reference;
70      }
71  
72      @Override
73      public Unit<?, Q> getDisplayUnit()
74      {
75          return this.relativeVecMat.getDisplayUnit();
76      }
77  
78      @SuppressWarnings("unchecked")
79      @Override
80      public VMA setDisplayUnit(final Unit<?, Q> newUnit)
81      {
82          this.relativeVecMat.setDisplayUnit(newUnit);
83          return (VMA) this;
84      }
85  
86      /**
87       * Return the number of rows.
88       * @return the number of rows
89       */
90      public int rows()
91      {
92          return this.relativeVecMat.rows();
93      }
94  
95      /**
96       * Return the number of columns.
97       * @return the number of columns
98       */
99      public int cols()
100     {
101         return this.relativeVecMat.cols();
102     }
103 
104     /**
105      * Return a new vector or matrix with the given SI or BASE values for the relative vector or matrix.
106      * @param siNew the values for the new vector or matrix in row-major format
107      * @param newReference the reference point for the relative SI values
108      * @return a new matrix with the provided SI or BASE values
109      */
110     public VMA instantiateSi(final double[] siNew, final Reference<?, A, Q> newReference)
111     {
112         VMQ rel = this.relativeVecMat.instantiateSi(siNew).setDisplayUnit(getDisplayUnit());
113         return instantiate(rel, newReference);
114     }
115 
116     /**
117      * Return a new vector or matrix with the given SI or BASE values for the relative vector or matrix.
118      * @param relVecMat the underlying relative vector or matrix with SI values relative to the reference point
119      * @param newReference the reference point for the relative SI values
120      * @return a new matrix with the provided SI or BASE values
121      */
122     public abstract VMA instantiate(VMQ relVecMat, Reference<?, A, Q> newReference);
123 
124     /**
125      * Return the underlying relative vector or matrix with SI values relative to the reference point.
126      * @return the underlying relative vector or matrix with SI values relative to the reference point
127      */
128     public VMQ getRelativeVecMat()
129     {
130         return this.relativeVecMat;
131     }
132 
133     /**
134      * Return the reference point for the absolute values.
135      * @return the reference point for the absolute values
136      */
137     public Reference<?, A, Q> getReference()
138     {
139         return this.reference;
140     }
141 
142     /**
143      * Return a transposed absolute vector or matrix, where rows and columns have been swapped.
144      * @return a transposed absolute vector or matrix, where rows and columns have been swapped
145      */
146     public abstract VMAT transpose();
147 
148     @Override
149     public boolean isRelative()
150     {
151         return false;
152     }
153 
154     /**
155      * Check if the 0-based row is within bounds.
156      * @param row the 0-based row to check
157      * @throws IndexOutOfBoundsException when row is out of bounds
158      */
159     protected void checkRow(final int row)
160     {
161         Throw.when(row < 0 || row >= rows(), IndexOutOfBoundsException.class, "Row %d out of bounds [0..%d]", row, rows() - 1);
162     }
163 
164     /**
165      * Check if the 0-based column is within bounds.
166      * @param col the 0-based column to check
167      * @throws IndexOutOfBoundsException when column is out of bounds
168      */
169     protected void checkCol(final int col)
170     {
171         Throw.when(col < 0 || col >= cols(), IndexOutOfBoundsException.class, "Column %d out of bounds [0..%d]", col,
172                 cols() - 1);
173     }
174 
175     /**
176      * Check if the 1-based row is within bounds.
177      * @param mRow the 1-based row to check
178      * @throws IndexOutOfBoundsException when row is out of bounds
179      */
180     protected void mcheckRow(final int mRow)
181     {
182         Throw.when(mRow < 1 || mRow > rows(), IndexOutOfBoundsException.class, "Row %d out of bounds [1..%d]", mRow, rows());
183     }
184 
185     /**
186      * Check if the 1-based column is within bounds.
187      * @param mCol the 1-based column to check
188      * @throws IndexOutOfBoundsException when column is out of bounds
189      */
190     protected void mcheckCol(final int mCol)
191     {
192         Throw.when(mCol < 1 || mCol > cols(), IndexOutOfBoundsException.class, "Column %d out of bounds [1..%d]", mCol, cols());
193     }
194 
195     /**
196      * Return the minimum value of the entries of the vector or matrix.
197      * @return the minimum value of the entries of the vector or matrix
198      */
199     public A min()
200     {
201         return getReference().instantiate(getDisplayUnit().ofSi(this.relativeVecMat.min().si()))
202                 .setDisplayUnit(getDisplayUnit());
203     }
204 
205     /**
206      * Return the maximum value of the entries of the vector or matrix.
207      * @return the maximum value of the entries of the vector or matrix
208      */
209     public A max()
210     {
211         return getReference().instantiate(getDisplayUnit().ofSi(this.relativeVecMat.max().si()))
212                 .setDisplayUnit(getDisplayUnit());
213     }
214 
215     /**
216      * Return the median value of the entries of the vector or matrix.
217      * @return the median value of the entries of the vector or matrix
218      */
219     public A median()
220     {
221         return getReference().instantiate(getDisplayUnit().ofSi(this.relativeVecMat.median().si()))
222                 .setDisplayUnit(getDisplayUnit());
223     }
224 
225     /**
226      * Return a vector or matrix with entries that contain the sum of the element and the increment.
227      * @param increment the quantity by which to increase the values of the vector or matrix
228      * @return a vector or matrix with entries that are incremented by the given quantity
229      */
230     public VMA add(final Q increment)
231     {
232         return instantiateSi(ArrayMath.add(this.relativeVecMat.unsafeSiArray(), increment.si()), getReference())
233                 .setDisplayUnit(getDisplayUnit());
234     }
235 
236     /**
237      * Return a vector or matrix with entries that contain the value minus the decrement.
238      * @param decrement the quantity by which to decrease the values of the vector or matrix
239      * @return a vector or matrix with entries that are decremented by the given quantity
240      */
241     public VMA subtract(final Q decrement)
242     {
243         return instantiateSi(ArrayMath.add(this.relativeVecMat.unsafeSiArray(), -decrement.si()), getReference())
244                 .setDisplayUnit(getDisplayUnit());
245     }
246 
247     /**
248      * Return a vector or matrix with entries that contain the sum of the element and the increment.
249      * @param other the vector or matrix that contains the values by which to increase the values of the vector or matrix
250      * @return a vector or matrix with entries that are decremented by the given quantity
251      */
252     public VMA add(final VMQ other)
253     {
254         return instantiateSi(ArrayMath.add(this.relativeVecMat.unsafeSiArray(), other.unsafeSiArray()), getReference())
255                 .setDisplayUnit(getDisplayUnit());
256     }
257 
258     /**
259      * Return a vector or matrix with entries that contain the value minus the decrement.
260      * @param other the vector or matrix that contains the values by which to decrease the values of the vector or matrix
261      * @return a vector or matrix with entries that are decremented by the given vector or matrix values
262      */
263     public VMA subtract(final VMQ other)
264     {
265         return instantiateSi(ArrayMath.subtract(this.relativeVecMat.unsafeSiArray(), other.unsafeSiArray()), getReference())
266                 .setDisplayUnit(getDisplayUnit());
267     }
268 
269     /**
270      * Return a relative vector or matrix with entries that contain the absolute value minus the absolute decrement.
271      * @param other the vector or matrix that contains the values by which to decrease the values of the vector or matrix
272      * @return a vector or matrix with entries that are decremented by the given vector or matrix values
273      */
274     public VMQ subtract(final VMA other)
275     {
276         return this.relativeVecMat
277                 .instantiateSi(
278                         ArrayMath.subtract(this.relativeVecMat.unsafeSiArray(), other.getRelativeVecMat().unsafeSiArray()))
279                 .setDisplayUnit(getDisplayUnit());
280     }
281 
282     /**
283      * Return a relative vector or matrix with entries that contain the absolute value minus the absolute decrement.
284      * @param decrement the absolute quantity by which to decrease the values of the vector or matrix
285      * @return a vector or matrix with entries that are decremented by the given decrement
286      */
287     public VMQ subtract(final A decrement)
288     {
289         return this.relativeVecMat.instantiateSi(ArrayMath.add(this.relativeVecMat.unsafeSiArray(), -decrement.si()))
290                 .setDisplayUnit(getDisplayUnit());
291     }
292 
293     // ------------------------------------ AS() METHODS ------------------------------------
294 
295     /**
296      * Convert this absolute vector or matrix to a {@link AbsMatrixNxM}. The underlying data MIGHT be shared between this object
297      * and the new AbsMatrixNxM.
298      * @return a {@code AbsMatrixNxN} with identical SI data, display unit, and reference point
299      */
300     public AbsMatrixNxM<A, Q> asAbsMatrixNxM()
301     {
302         return new AbsMatrixNxM<A, Q>(MatrixNxM.ofSi(getRelativeVecMat().unsafeSiArray(), rows(), cols(), getDisplayUnit()),
303                 getReference());
304     }
305 
306     /**
307      * Convert this absolute vector or matrix to a {@link AbsQuantityTable}. The underlying data MIGHT be shared between this
308      * object and the new AbsQuantityTable.
309      * @return a {@code AbsQuantityTable} with identical SI data, display unit, and reference point
310      */
311     public AbsQuantityTable<A, Q> asAbsQuantityTable()
312     {
313         return new AbsQuantityTable<A, Q>(
314                 QuantityTable.ofSi(getRelativeVecMat().unsafeSiArray(), rows(), cols(), getDisplayUnit()), getReference());
315     }
316 
317     /**
318      * Return this absolute vector, matrix or table as a 1-element column vector. Shape must be 1 x 1.
319      * @return a {@code AbsVector1} with identical SI data and display unit
320      * @throws IllegalStateException if shape is not 1 x 1
321      */
322     public AbsVector1<A, Q> asAbsVector1()
323     {
324         Throw.when(rows() != 1 || cols() != 1, IllegalStateException.class, "Matrix is not 1x1");
325         final double[] data = getRelativeVecMat().unsafeSiArray();
326         return new AbsVector1<A, Q>(new Vector1<Q>(data[0], getDisplayUnit()), getReference());
327     }
328 
329     /**
330      * Return this absolute vector, matrix or table as a 2-element column vector. Shape must be 2 x 1.
331      * @return a {@code AbsVector2.Col} with identical SI data and display unit
332      * @throws IllegalStateException if shape is not 2 x 1
333      */
334     public AbsVector2.Col<A, Q> asAbsVector2Col()
335     {
336         Throw.when(rows() != 2 || cols() != 1, IllegalStateException.class, "Matrix is not 2x1");
337         final double[] data = getRelativeVecMat().unsafeSiArray();
338         return new AbsVector2.Col<A, Q>(new Vector2.Col<Q>(data[0], data[1], getDisplayUnit()), getReference());
339     }
340 
341     /**
342      * Return this absolute vector, matrix or table as a 3-element column vector. Shape must be 3 x 1.
343      * @return a {@code AbsVector3.Col} with identical SI data and display unit
344      * @throws IllegalStateException if shape is not 3 x 1
345      */
346     public AbsVector3.Col<A, Q> asAbsVector3Col()
347     {
348         Throw.when(rows() != 3 || cols() != 1, IllegalStateException.class, "Matrix is not 3x1");
349         final double[] data = getRelativeVecMat().unsafeSiArray();
350         return new AbsVector3.Col<A, Q>(new Vector3.Col<Q>(data[0], data[1], data[2], getDisplayUnit()), getReference());
351     }
352 
353     /**
354      * Convert this absolute vector, matrix or table to an N-element column vector. Shape must be N x 1. The underlying data
355      * MIGHT be shared between this object and the AbsVectorN.Col.
356      * @return a {@code AbsVectorN.Col} with identical SI data and display unit
357      * @throws IllegalStateException if {@code cols() != 1}
358      */
359     public AbsVectorN.Col<A, Q> asAbsVectorNCol()
360     {
361         Throw.when(cols() != 1, IllegalStateException.class, "Matrix is not Nx1");
362         return new AbsVectorN.Col<A, Q>(VectorN.Col.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()),
363                 getReference());
364     }
365 
366     /**
367      * Return this absolute vector, matrix or table as a 2-element row vector. Shape must be 1 x 2.
368      * @return a {@code AbsVector2.Row} with identical SI data and display unit
369      * @throws IllegalStateException if shape is not 1 x 2
370      */
371     public AbsVector2.Row<A, Q> asAbsVector2Row()
372     {
373         Throw.when(rows() != 1 || cols() != 2, IllegalStateException.class, "Matrix is not 1x2");
374         final double[] data = getRelativeVecMat().unsafeSiArray();
375         return new AbsVector2.Row<A, Q>(new Vector2.Row<Q>(data[0], data[1], getDisplayUnit()), getReference());
376     }
377 
378     /**
379      * Return this absolute vector, matrix or table as a 3-element row vector. Shape must be 1 x 3.
380      * @return a {@code AbsVector3.Row} with identical SI data and display unit
381      * @throws IllegalStateException if shape is not 1 x 3
382      */
383     public AbsVector3.Row<A, Q> asAbsVector3Row()
384     {
385         Throw.when(rows() != 1 || cols() != 3, IllegalStateException.class, "Matrix is not 1x3");
386         final double[] data = getRelativeVecMat().unsafeSiArray();
387         return new AbsVector3.Row<A, Q>(new Vector3.Row<Q>(data[0], data[1], data[2], getDisplayUnit()), getReference());
388     }
389 
390     /**
391      * Convert this absolute vector, matrix or table to an N-element row vector. Shape must be 1 x N. The underlying data MIGHT
392      * be shared between this object and the AbsVectorN.Row.
393      * @return a {@code AbsVectorN.Row} with identical SI data and display unit
394      * @throws IllegalStateException if {@code rows() != 1}
395      */
396     public AbsVectorN.Row<A, Q> asAbsVectorNRow()
397     {
398         Throw.when(rows() != 1, IllegalStateException.class, "Matrix is not 1xN");
399         return new AbsVectorN.Row<A, Q>(VectorN.Row.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()),
400                 getReference());
401     }
402 
403     /**
404      * Convert this absolute vector, matrix or table to a {@link AbsMatrix1x1}. The shape must be 1 x 1.
405      * @return a {@code AbsMatrix1x1} with identical SI data and display unit
406      * @throws IllegalStateException if this matrix is not 1 x 1
407      */
408     public AbsMatrix1x1<A, Q> asAbsMatrix1x1()
409     {
410         Throw.when(rows() != 1 || cols() != 1, IllegalStateException.class,
411                 "asAbsMatrix1x1() called, but matrix is no 1x1 but %dx%d", rows(), cols());
412         return new AbsMatrix1x1<A, Q>(Matrix1x1.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()), getReference());
413     }
414 
415     /**
416      * Convert this absolute vector, matrix or table to a {@link AbsMatrix2x2}. The shape must be 2 x 2.
417      * @return a {@code AbsMatrix2x2} with identical SI data and display unit
418      * @throws IllegalStateException if this matrix is not 2 x 2
419      */
420     public AbsMatrix2x2<A, Q> asAbsMatrix2x2()
421     {
422         Throw.when(rows() != 2 || cols() != 2, IllegalStateException.class,
423                 "asAbsMatrix2x2() called, but matrix is no 2x2 but %dx%d", rows(), cols());
424         return new AbsMatrix2x2<A, Q>(Matrix2x2.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()), getReference());
425     }
426 
427     /**
428      * Convert this absolute vector, matrix or table to a {@link AbsMatrix3x3}. The shape must be 3 x 3.
429      * @return a {@code AbsMatrix3x3} with identical SI data and display unit
430      * @throws IllegalStateException if this matrix is not 3 x 3
431      */
432     public AbsMatrix3x3<A, Q> asAbsMatrix3x3()
433     {
434         Throw.when(rows() != 3 || cols() != 3, IllegalStateException.class,
435                 "asAbsMatrix3x3() called, but matrix is no 3x3 but %dx%d", rows(), cols());
436         return new AbsMatrix3x3<A, Q>(Matrix3x3.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()), getReference());
437     }
438 
439     /**
440      * Convert this absolute vector, matrix or table to a {@link AbsMatrixNxN}. The shape must be square. The underlying data
441      * MIGHT be shared between this object and the AbsMatrixNxN.
442      * @return a {@code AbsMatrixNxN} with identical SI data and display unit
443      * @throws IllegalStateException if this matrix is not square
444      */
445     public AbsMatrixNxN<A, Q> asAbsMatrixNxN()
446     {
447         Throw.when(rows() != cols(), IllegalStateException.class, "asAbsMatrixNxN() called, but matrix is no square but %dx%d",
448                 rows(), cols());
449         return new AbsMatrixNxN<A, Q>(MatrixNxN.ofSi(getRelativeVecMat().unsafeSiArray(), getDisplayUnit()), getReference());
450     }
451 
452     // ======================================= hashCode() and equals() ===============================================
453 
454     @Override
455     public int hashCode()
456     {
457         return Objects.hash(this.reference, this.relativeVecMat);
458     }
459 
460     @Override
461     @SuppressWarnings("checkstyle:needbraces")
462     public boolean equals(final Object obj)
463     {
464         if (this == obj)
465             return true;
466         if (obj == null)
467             return false;
468         if (getClass() != obj.getClass())
469             return false;
470         AbsVectorMatrix<?, ?, ?, ?, ?> other = (AbsVectorMatrix<?, ?, ?, ?, ?>) obj;
471         return Objects.equals(this.reference, other.reference) && Objects.equals(this.relativeVecMat, other.relativeVecMat);
472     }
473 
474     // -------------------------------- TOSTRING / FORMAT METHODS -------------------------------
475 
476     @Override
477     public String toString()
478     {
479         return format();
480     }
481  
482 }