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