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
20
21
22
23
24
25
26
27
28
29
30
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
39 private final double xSi;
40
41
42 private final double ySi;
43
44
45
46
47
48
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
59
60
61
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();
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
112
113
114 public double xSi()
115 {
116 return this.xSi;
117 }
118
119
120
121
122
123 public double ySi()
124 {
125 return this.ySi;
126 }
127
128
129
130
131
132 @Override
133 public double[] si()
134 {
135 return new double[] {this.xSi, this.ySi};
136 }
137
138
139
140
141
142 public Q x()
143 {
144 return getDisplayUnit().ofSi(this.xSi).setDisplayUnit(getDisplayUnit());
145 }
146
147
148
149
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
268
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
316
317
318
319
320
321
322
323
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
333
334
335
336
337 public Col(final double xInUnit, final double yInUnit, final U displayUnit)
338 {
339 super(xInUnit, yInUnit, displayUnit);
340 }
341
342
343
344
345
346
347
348
349
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
447
448
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
467
468
469
470
471
472
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
487
488
489
490
491
492
493
494
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
504
505
506
507
508 public Row(final double xInUnit, final double yInUnit, final U displayUnit)
509 {
510 super(xInUnit, yInUnit, displayUnit);
511 }
512
513
514
515
516
517
518
519
520
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
638
639
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
649
650
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
669
670
671
672
673
674
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 }