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