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 }