View Javadoc
1   package org.djunits.vecmat.d3;
2   
3   import java.lang.reflect.Array;
4   import java.util.Arrays;
5   import java.util.Iterator;
6   import java.util.Objects;
7   
8   import org.djunits.quantity.SIQuantity;
9   import org.djunits.quantity.def.Quantity;
10  import org.djunits.unit.UnitInterface;
11  import org.djunits.unit.si.SIUnit;
12  import org.djunits.util.Math2;
13  import org.djunits.util.MatrixMath;
14  import org.djunits.vecmat.d1.Vector1;
15  import org.djunits.vecmat.def.Vector;
16  import org.djunits.vecmat.operations.VectorTransposable;
17  import org.djutils.exceptions.Throw;
18  
19  /**
20   * Vector3 implements a vector with three real-valued entries. The vector is immutable, except for the display unit, which can
21   * be changed. Many of the method that have been defined already for a generic vector have been re-implemented for efficiency.
22   * <p>
23   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
24   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
25   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
26   * @author Alexander Verbraeck
27   * @param <Q> the quantity type
28   * @param <U> the unit type
29   * @param <V> the vector type (Col or Row)
30   * @param <SI> the vector type with generics &lt;SIQuantity, SIUnit&lt;
31   * @param <H> the generic vector type with generics &lt;?, ?&lt; for Hadamard operations
32   */
33  public abstract class Vector3<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>, V extends Vector3<Q, U, V, SI, H>,
34          SI extends Vector3<SIQuantity, SIUnit, SI, ?, ?>, H extends Vector3<?, ?, ?, ?, ?>> extends Vector<Q, U, V, SI, H>
35  {
36      /** */
37      private static final long serialVersionUID = 600L;
38  
39      /** The x value in si-units. */
40      private final double xSi;
41  
42      /** The y value in si-units. */
43      private final double ySi;
44  
45      /** The z value in si-units. */
46      private final double zSi;
47  
48      /**
49       * Create a new Vector3 with a unit.
50       * @param xInUnit the x-value expressed in the display unit
51       * @param yInUnit the y-value expressed in the display unit
52       * @param zInUnit the z-value expressed in the display unit
53       * @param displayUnit the display unit to use
54       */
55      protected Vector3(final double xInUnit, final double yInUnit, final double zInUnit, final U displayUnit)
56      {
57          super(displayUnit);
58          this.xSi = displayUnit.toBaseValue(xInUnit);
59          this.ySi = displayUnit.toBaseValue(yInUnit);
60          this.zSi = displayUnit.toBaseValue(zInUnit);
61      }
62  
63      /**
64       * Return a new column or row vector with adapted x, y and z values.
65       * @param xSiNew the x value to use
66       * @param ySiNew the y value to use
67       * @param zSiNew the z value to use
68       * @return a new column or row vector with adapted x, y and z values
69       */
70      protected abstract V instantiateSi(double xSiNew, double ySiNew, double zSiNew);
71  
72      @Override
73      public V instantiateSi(final double[] siNew)
74      {
75          Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector3 != 3, but %d",
76                  siNew.length);
77          return instantiateSi(siNew[0], siNew[1], siNew[2]);
78      }
79  
80      @Override
81      public int size()
82      {
83          return 3;
84      }
85  
86      @Override
87      public double si(final int index) throws IndexOutOfBoundsException
88      {
89          return switch (index)
90          {
91              case 0 -> this.xSi;
92              case 1 -> this.ySi;
93              case 2 -> this.zSi;
94              default -> throw new IndexOutOfBoundsException("Cannot retrieve Vector3[" + index + "]");
95          };
96      }
97  
98      @Override
99      public Iterator<Q> iterator()
100     {
101         final double[] si = new double[] {this.xSi, this.ySi, this.zSi};
102         final U frozenDisplayUnit = getDisplayUnit(); // capture once
103         return Arrays.stream(si).mapToObj(v -> frozenDisplayUnit.ofSi(v).setDisplayUnit(frozenDisplayUnit)).iterator();
104     }
105 
106     @Override
107     public Q[] getScalarArray()
108     {
109         final Q qx = x();
110         final Class<?> qClass = qx.getClass();
111         @SuppressWarnings("unchecked")
112         final Q[] out = (Q[]) Array.newInstance(qClass, 3);
113         out[0] = qx;
114         out[1] = y();
115         out[2] = z();
116         return out;
117     }
118 
119     /**
120      * Return the x-value of the vector in SI or BASE units.
121      * @return the x-value of the vector in SI or BASE units
122      */
123     public double xSi()
124     {
125         return this.xSi;
126     }
127 
128     /**
129      * Return the y-value of the vector in SI or BASE units.
130      * @return the y-value of the vector in SI or BASE units
131      */
132     public double ySi()
133     {
134         return this.ySi;
135     }
136 
137     /**
138      * Return the z-value of the vector in SI or BASE units.
139      * @return the z-value of the vector in SI or BASE units
140      */
141     public double zSi()
142     {
143         return this.zSi;
144     }
145 
146     /**
147      * Return the contents of the vector as an array.
148      * @return the contents of the vector as an array
149      */
150     @Override
151     public double[] si()
152     {
153         return new double[] {this.xSi, this.ySi, this.zSi};
154     }
155 
156     /**
157      * Return the x-value of the vector as a quantity with the correct unit.
158      * @return the x-value of the vector as a quantity with the correct unit
159      */
160     public Q x()
161     {
162         return getDisplayUnit().ofSi(this.xSi).setDisplayUnit(getDisplayUnit());
163     }
164 
165     /**
166      * Return the y-value of the vector as a quantity with the correct unit.
167      * @return the y-value of the vector as a quantity with the correct unit
168      */
169     public Q y()
170     {
171         return getDisplayUnit().ofSi(this.ySi).setDisplayUnit(getDisplayUnit());
172     }
173 
174     /**
175      * Return the z-value of the vector as a quantity with the correct unit.
176      * @return the z-value of the vector as a quantity with the correct unit
177      */
178     public Q z()
179     {
180         return getDisplayUnit().ofSi(this.zSi).setDisplayUnit(getDisplayUnit());
181     }
182 
183     @Override
184     public V scaleBy(final double factor)
185     {
186         return instantiateSi(this.xSi * factor, this.ySi * factor, this.zSi * factor);
187     }
188 
189     @Override
190     public V add(final V other)
191     {
192         return instantiateSi(this.xSi + other.xSi(), this.ySi + other.ySi(), this.zSi + other.zSi());
193     }
194 
195     @Override
196     public V subtract(final V other)
197     {
198         return instantiateSi(this.xSi - other.xSi(), this.ySi - other.ySi(), this.zSi - other.zSi());
199     }
200 
201     @Override
202     public V negate()
203     {
204         return instantiateSi(-this.xSi, -this.ySi, -this.zSi);
205     }
206 
207     @Override
208     public V abs()
209     {
210         return instantiateSi(Math.abs(this.xSi), Math.abs(this.ySi), Math.abs(this.zSi));
211     }
212 
213     @Override
214     public Q normL1()
215     {
216         return getDisplayUnit().ofSi(Math.abs(this.xSi) + Math.abs(this.ySi) + Math.abs(this.zSi))
217                 .setDisplayUnit(getDisplayUnit());
218     }
219 
220     @Override
221     public Q normL2()
222     {
223         return getDisplayUnit().ofSi(Math.sqrt(this.xSi * this.xSi + this.ySi * this.ySi + this.zSi * this.zSi))
224                 .setDisplayUnit(getDisplayUnit());
225     }
226 
227     @Override
228     public Q normLp(final int p)
229     {
230         return getDisplayUnit().ofSi(Math.pow(
231                 Math.pow(Math.abs(this.xSi), p) + Math.pow(Math.abs(this.ySi), p) + Math.pow(Math.abs(this.zSi), p), 1.0 / p))
232                 .setDisplayUnit(getDisplayUnit());
233     }
234 
235     @Override
236     public Q normLinf()
237     {
238         return getDisplayUnit().ofSi(Math2.maxAbs(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
239     }
240 
241     @Override
242     public Q mean()
243     {
244         return getDisplayUnit().ofSi((this.xSi + this.ySi + this.zSi) / 3.0).setDisplayUnit(getDisplayUnit());
245     }
246 
247     @Override
248     public Q min()
249     {
250         return getDisplayUnit().ofSi(Math2.min(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
251     }
252 
253     @Override
254     public Q max()
255     {
256         return getDisplayUnit().ofSi(Math2.max(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
257     }
258 
259     @Override
260     public Q mode()
261     {
262         return max();
263     }
264 
265     @Override
266     public Q median()
267     {
268         return getDisplayUnit().ofSi(Math2.median(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
269     }
270 
271     @Override
272     public Q sum()
273     {
274         return getDisplayUnit().ofSi(this.xSi + this.ySi + this.zSi).setDisplayUnit(getDisplayUnit());
275     }
276 
277     @Override
278     public V add(final Q increment)
279     {
280         return instantiateSi(this.xSi + increment.si(), this.ySi + increment.si(), this.zSi + increment.si());
281     }
282 
283     @Override
284     public V subtract(final Q decrement)
285     {
286         return instantiateSi(this.xSi - decrement.si(), this.ySi - decrement.si(), this.zSi - decrement.si());
287     }
288 
289     @Override
290     public boolean isRelative()
291     {
292         return x().isRelative();
293     }
294 
295     /**
296      * Return whether this vector is a column vector.
297      * @return whether this vector is a column vector
298      */
299     @Override
300     public abstract boolean isColumnVector();
301 
302     @Override
303     public int hashCode()
304     {
305         return Objects.hash(this.xSi, this.ySi, this.zSi);
306     }
307 
308     @SuppressWarnings("checkstyle:needbraces")
309     @Override
310     public boolean equals(final Object obj)
311     {
312         if (this == obj)
313             return true;
314         if (obj == null)
315             return false;
316         if (getClass() != obj.getClass())
317             return false;
318         Vector3<?, ?, ?, ?, ?> other = (Vector3<?, ?, ?, ?, ?>) obj;
319         return Double.doubleToLongBits(this.xSi) == Double.doubleToLongBits(other.xSi)
320                 && Double.doubleToLongBits(this.ySi) == Double.doubleToLongBits(other.ySi)
321                 && Double.doubleToLongBits(this.zSi) == Double.doubleToLongBits(other.zSi);
322     }
323 
324     @Override
325     public String toString(final U withUnit)
326     {
327         var s = new StringBuilder();
328         s.append(isColumnVector() ? "Col" : "Row");
329         s.append("[");
330         s.append(withUnit.fromBaseValue(this.xSi));
331         s.append(", ");
332         s.append(withUnit.fromBaseValue(this.ySi));
333         s.append(", ");
334         s.append(withUnit.fromBaseValue(this.zSi));
335         s.append("] ");
336         s.append(withUnit.getDisplayAbbreviation());
337         return s.toString();
338     }
339 
340     @Override
341     public String toString()
342     {
343         return toString(getDisplayUnit());
344     }
345 
346     /**
347      * Vector3.Col implements a column vector with three real-valued entries. The vector is immutable, except for the display
348      * unit, which can be changed.
349      * <p>
350      * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
351      * See for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
352      * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
353      * @author Alexander Verbraeck
354      * @param <Q> the quantity type
355      * @param <U> the unit type
356      */
357     public static class Col<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>>
358             extends Vector3<Q, U, Col<Q, U>, Col<SIQuantity, SIUnit>, Col<?, ?>> implements VectorTransposable<Row<Q, U>>
359     {
360         /** */
361         private static final long serialVersionUID = 600L;
362 
363         /**
364          * Create a new 3 column vector with a unit.
365          * @param xInUnit the x-value expressed in the display unit
366          * @param yInUnit the y-value expressed in the display unit
367          * @param zInUnit the z-value expressed in the display unit
368          * @param displayUnit the display unit to use
369          */
370         public Col(final double xInUnit, final double yInUnit, final double zInUnit, final U displayUnit)
371         {
372             super(xInUnit, yInUnit, zInUnit, displayUnit);
373         }
374 
375         /**
376          * Create a Vector3 column vector without needing generics.
377          * @param xInUnit the x-value expressed in the display unit
378          * @param yInUnit the y-value expressed in the display unit
379          * @param zInUnit the z-value expressed in the display unit
380          * @param displayUnit the display unit to use
381          * @return a new Vector3 with a unit
382          * @param <Q> the quantity type
383          * @param <U> the unit type
384          */
385         public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> Vector3.Col<Q, U> of(final double xInUnit,
386                 final double yInUnit, final double zInUnit, final U displayUnit)
387         {
388             return new Vector3.Col<>(xInUnit, yInUnit, zInUnit, displayUnit);
389         }
390 
391         @Override
392         public boolean isColumnVector()
393         {
394             return true;
395         }
396 
397         @Override
398         public int rows()
399         {
400             return 3;
401         }
402 
403         @Override
404         public int cols()
405         {
406             return 1;
407         }
408 
409         @Override
410         protected Vector3.Col<Q, U> instantiateSi(final double xSi, final double ySi, final double zSi)
411         {
412             return new Vector3.Col<>(xSi, ySi, zSi, getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
413         }
414 
415         @Override
416         public Vector3.Col<SIQuantity, SIUnit> instantiateSi(final double[] siNew, final SIUnit siUnit)
417         {
418             Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector2 != 2, but %d",
419                     siNew.length);
420             return new Vector3.Col<SIQuantity, SIUnit>(siNew[0], siNew[1], siNew[2], siUnit);
421         }
422 
423         @Override
424         public double si(final int row, final int col) throws IndexOutOfBoundsException
425         {
426             checkRow(row);
427             checkCol(col);
428             return row == 0 ? xSi() : row == 1 ? ySi() : zSi();
429         }
430 
431         @Override
432         public Vector1<Q, U> getRowVector(final int row)
433         {
434             checkRow(row);
435             return new Vector1<Q, U>(si(row, 0), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
436         }
437 
438         @Override
439         public Vector1<Q, U> mgetRowVector(final int mRow)
440         {
441             mcheckRow(mRow);
442             return new Vector1<Q, U>(msi(mRow, 1), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
443         }
444 
445         @Override
446         public Vector3.Col<Q, U> getColumnVector(final int col)
447         {
448             checkCol(col);
449             return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
450         }
451 
452         @Override
453         public Vector3.Col<Q, U> mgetColumnVector(final int mCol)
454         {
455             mcheckCol(mCol);
456             return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
457         }
458 
459         @Override
460         public double[] getRowSi(final int row)
461         {
462             checkRow(row);
463             return new double[] {si(row, 0)};
464         }
465 
466         @Override
467         public double[] getColumnSi(final int col)
468         {
469             checkCol(col);
470             return new double[] {xSi(), ySi(), zSi()};
471         }
472 
473         @Override
474         public Vector3.Row<Q, U> transpose()
475         {
476             return new Vector3.Row<Q, U>(xSi(), ySi(), zSi(), getDisplayUnit());
477         }
478 
479         @Override
480         public Vector3.Col<SIQuantity, SIUnit> invertElements()
481         {
482             return new Vector3.Col<SIQuantity, SIUnit>(1.0 / xSi(), 1.0 / ySi(), 1.0 / zSi(),
483                     getDisplayUnit().siUnit().invert());
484         }
485 
486         @Override
487         public Vector3.Col<SIQuantity, SIUnit> multiplyElements(final Vector3.Col<?, ?> other)
488         {
489             SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
490             return new Vector3.Col<SIQuantity, SIUnit>(xSi() * other.xSi(), ySi() * other.ySi(), zSi() * other.zSi(), siUnit);
491         }
492 
493         @Override
494         public Vector3.Col<SIQuantity, SIUnit> divideElements(final Vector3.Col<?, ?> other)
495         {
496             SIUnit siUnit = SIUnit.subtract(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
497             return new Vector3.Col<SIQuantity, SIUnit>(xSi() / other.xSi(), ySi() / other.ySi(), zSi() / other.zSi(), siUnit);
498         }
499 
500         /**
501          * Multiply this column vector with a row vector, resulting in a square matrix.
502          * @param otherVec the row vector to multiply with
503          * @return the resulting matrix from the multiplication
504          */
505         public Matrix3x3<SIQuantity, SIUnit> multiply(final Vector3.Row<?, ?> otherVec)
506         {
507             checkMultiply(otherVec);
508             double[] resultData = MatrixMath.multiply(si(), otherVec.si(), 3, 1, 3);
509             return new Matrix3x3<SIQuantity, SIUnit>(resultData,
510                     getDisplayUnit().siUnit().plus(otherVec.getDisplayUnit().siUnit()));
511         }
512 
513         @Override
514         public Vector3.Col<SIQuantity, SIUnit> multiplyElements(final Quantity<?, ?> quantity)
515         {
516             SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), quantity.getDisplayUnit().siUnit());
517             return new Vector3.Col<SIQuantity, SIUnit>(xSi() * quantity.si(), ySi() * quantity.si(), zSi() * quantity.si(),
518                     siUnit);
519         }
520 
521         /**
522          * Return the vector 'as' a vector with a known quantity, using a unit to express the result in. Throw a Runtime
523          * exception when the SI units of this vector and the target vector do not match.
524          * @param targetUnit the unit to convert the vector to
525          * @return a quantity typed in the target vector class
526          * @throws IllegalArgumentException when the units do not match
527          * @param <TQ> target quantity type
528          * @param <TU> target unit type
529          */
530         public <TQ extends Quantity<TQ, TU>, TU extends UnitInterface<TU, TQ>> Vector3.Col<TQ, TU> as(final TU targetUnit)
531                 throws IllegalArgumentException
532         {
533             Throw.when(!getDisplayUnit().siUnit().equals(targetUnit.siUnit()), IllegalArgumentException.class,
534                     "Quantity.as(%s) called, but units do not match: %s <> %s", targetUnit,
535                     getDisplayUnit().siUnit().getDisplayAbbreviation(), targetUnit.siUnit().getDisplayAbbreviation());
536             return new Vector3.Col<TQ, TU>(xSi(), ySi(), zSi(), targetUnit.getBaseUnit()).setDisplayUnit(targetUnit);
537         }
538 
539     }
540 
541     /**
542      * Vector3.Row implements a row vector with three real-valued entries. The vector is immutable, except for the display unit,
543      * which can be changed.
544      * <p>
545      * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
546      * See for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
547      * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
548      * @author Alexander Verbraeck
549      * @param <Q> the quantity type
550      * @param <U> the unit type
551      */
552     public static class Row<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>>
553             extends Vector3<Q, U, Row<Q, U>, Row<SIQuantity, SIUnit>, Row<?, ?>> implements VectorTransposable<Col<Q, U>>
554     {
555         /** */
556         private static final long serialVersionUID = 600L;
557 
558         /**
559          * Create a new 3 row vector with a unit.
560          * @param xSi the x-value expressed in SI or BASE units
561          * @param ySi the y-value expressed in SI or BASE units
562          * @param zSi the z-value expressed in SI or BASE units
563          * @param displayUnit the display unit to use
564          */
565         public Row(final double xSi, final double ySi, final double zSi, final U displayUnit)
566         {
567             super(xSi, ySi, zSi, displayUnit);
568         }
569 
570         /**
571          * Create a Vector3 row vector without needing generics.
572          * @param xInUnit the x-value expressed in the display unit
573          * @param yInUnit the y-value expressed in the display unit
574          * @param zInUnit the z-value expressed in the display unit
575          * @param displayUnit the display unit to use
576          * @return a new Vector3 with a unit
577          * @param <Q> the quantity type
578          * @param <U> the unit type
579          */
580         public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> Vector3.Row<Q, U> of(final double xInUnit,
581                 final double yInUnit, final double zInUnit, final U displayUnit)
582         {
583             return new Vector3.Row<>(xInUnit, yInUnit, zInUnit, displayUnit);
584         }
585 
586         @Override
587         public boolean isColumnVector()
588         {
589             return false;
590         }
591 
592         @Override
593         public int rows()
594         {
595             return 1;
596         }
597 
598         @Override
599         public int cols()
600         {
601             return 3;
602         }
603 
604         @Override
605         protected Vector3.Row<Q, U> instantiateSi(final double xSi, final double ySi, final double zSi)
606         {
607             return new Vector3.Row<>(xSi, ySi, zSi, getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
608         }
609 
610         @Override
611         public Vector3.Row<SIQuantity, SIUnit> instantiateSi(final double[] siNew, final SIUnit siUnit)
612         {
613             Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector2 != 2, but %d",
614                     siNew.length);
615             return new Vector3.Row<SIQuantity, SIUnit>(siNew[0], siNew[1], siNew[2], siUnit);
616         }
617 
618         @Override
619         public double si(final int row, final int col) throws IndexOutOfBoundsException
620         {
621             checkRow(row);
622             checkCol(col);
623             return col == 0 ? xSi() : col == 1 ? ySi() : zSi();
624         }
625 
626         @Override
627         public Vector3.Row<Q, U> getRowVector(final int row)
628         {
629             checkRow(row);
630             return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
631         }
632 
633         @Override
634         public Vector3.Row<Q, U> mgetRowVector(final int mRow)
635         {
636             mcheckRow(mRow);
637             return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
638         }
639 
640         @Override
641         public Vector1<Q, U> getColumnVector(final int col)
642         {
643             checkCol(col);
644             return new Vector1<Q, U>(si(0, col), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
645         }
646 
647         @Override
648         public Vector1<Q, U> mgetColumnVector(final int mCol)
649         {
650             mcheckCol(mCol);
651             return new Vector1<Q, U>(msi(1, mCol), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
652         }
653 
654         @Override
655         public double[] getRowSi(final int row)
656         {
657             checkRow(row);
658             return new double[] {xSi(), ySi(), zSi()};
659         }
660 
661         @Override
662         public double[] getColumnSi(final int col)
663         {
664             checkCol(col);
665             return new double[] {si(0, col)};
666         }
667 
668         @Override
669         public Vector3.Col<Q, U> transpose()
670         {
671             return new Vector3.Col<Q, U>(xSi(), ySi(), zSi(), getDisplayUnit());
672         }
673 
674         @Override
675         public Vector3.Row<SIQuantity, SIUnit> invertElements()
676         {
677             return new Vector3.Row<SIQuantity, SIUnit>(1.0 / xSi(), 1.0 / ySi(), 1.0 / zSi(),
678                     getDisplayUnit().siUnit().invert());
679         }
680 
681         @Override
682         public Vector3.Row<SIQuantity, SIUnit> multiplyElements(final Vector3.Row<?, ?> other)
683         {
684             SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
685             return new Vector3.Row<SIQuantity, SIUnit>(xSi() * other.xSi(), ySi() * other.ySi(), zSi() * other.zSi(), siUnit);
686         }
687 
688         @Override
689         public Vector3.Row<SIQuantity, SIUnit> divideElements(final Vector3.Row<?, ?> other)
690         {
691             SIUnit siUnit = SIUnit.subtract(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
692             return new Vector3.Row<SIQuantity, SIUnit>(xSi() / other.xSi(), ySi() / other.ySi(), zSi() / other.zSi(), siUnit);
693         }
694 
695         /**
696          * Multiply this row vector with a column vector, resulting in a scalar quantity.
697          * @param otherVec the column vector to multiply with
698          * @return the resulting matrix from the multiplication
699          */
700         public SIQuantity multiply(final Vector3.Col<?, ?> otherVec)
701         {
702             double[] resultData = MatrixMath.multiply(si(), otherVec.si(), 1, 3, 1);
703             return new SIQuantity(resultData[0], getDisplayUnit().siUnit().plus(otherVec.getDisplayUnit().siUnit()));
704         }
705 
706         /**
707          * Multiply this row vector with a matrix, resulting in a column vector.
708          * @param otherMat the matrix to multiply with
709          * @return the resulting column vector from the multiplication
710          */
711         public Vector3.Col<SIQuantity, SIUnit> multiply(final Matrix3x3<?, ?> otherMat)
712         {
713             checkMultiply(otherMat);
714             double[] resultData = MatrixMath.multiply(si(), otherMat.si(), 1, 3, 3);
715             return new Vector3.Col<SIQuantity, SIUnit>(resultData[0], resultData[1], resultData[2],
716                     getDisplayUnit().siUnit().plus(otherMat.getDisplayUnit().siUnit()));
717         }
718 
719         @Override
720         public Vector3.Row<SIQuantity, SIUnit> multiplyElements(final Quantity<?, ?> quantity)
721         {
722             SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), quantity.getDisplayUnit().siUnit());
723             return new Vector3.Row<SIQuantity, SIUnit>(xSi() * quantity.si(), ySi() * quantity.si(), zSi() * quantity.si(),
724                     siUnit);
725         }
726 
727         /**
728          * Return the vector 'as' a vector with a known quantity, using a unit to express the result in. Throw a Runtime
729          * exception when the SI units of this vector and the target vector do not match.
730          * @param targetUnit the unit to convert the vector to
731          * @return a quantity typed in the target vector class
732          * @throws IllegalArgumentException when the units do not match
733          * @param <TQ> target quantity type
734          * @param <TU> target unit type
735          */
736         public <TQ extends Quantity<TQ, TU>, TU extends UnitInterface<TU, TQ>> Vector3.Row<TQ, TU> as(final TU targetUnit)
737                 throws IllegalArgumentException
738         {
739             Throw.when(!getDisplayUnit().siUnit().equals(targetUnit.siUnit()), IllegalArgumentException.class,
740                     "Quantity.as(%s) called, but units do not match: %s <> %s", targetUnit,
741                     getDisplayUnit().siUnit().getDisplayAbbreviation(), targetUnit.siUnit().getDisplayAbbreviation());
742             return new Vector3.Row<TQ, TU>(xSi(), ySi(), zSi(), targetUnit.getBaseUnit()).setDisplayUnit(targetUnit);
743         }
744 
745     }
746 
747 }