View Javadoc
1   package org.djunits.value.vdouble.vector;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertNotEquals;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   import static org.junit.jupiter.api.Assertions.fail;
8   
9   import org.djunits.unit.AbsoluteTemperatureUnit;
10  import org.djunits.unit.AngleUnit;
11  import org.djunits.unit.AreaUnit;
12  import org.djunits.unit.DirectionUnit;
13  import org.djunits.unit.DurationUnit;
14  import org.djunits.unit.LengthUnit;
15  import org.djunits.unit.PositionUnit;
16  import org.djunits.unit.TemperatureUnit;
17  import org.djunits.unit.TimeUnit;
18  import org.djunits.unit.util.UnitException;
19  import org.djunits.value.ValueRuntimeException;
20  import org.djunits.value.storage.StorageType;
21  import org.djunits.value.vdouble.function.DoubleMathFunctions;
22  import org.djunits.value.vdouble.scalar.AbsoluteTemperature;
23  import org.djunits.value.vdouble.scalar.Area;
24  import org.djunits.value.vdouble.scalar.Direction;
25  import org.djunits.value.vdouble.scalar.Duration;
26  import org.djunits.value.vdouble.scalar.Position;
27  import org.djunits.value.vdouble.scalar.Time;
28  import org.djunits.value.vdouble.vector.data.DoubleVectorData;
29  import org.djutils.exceptions.Try;
30  import org.junit.jupiter.api.Test;
31  
32  /**
33   * Test the incrementBy, etc. methods.
34   * <p>
35   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
36   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
37   * </p>
38   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
39   */
40  public class DoubleVectorMethodTest
41  {
42  
43      /**
44       * Test the standard methods of all vector classes.
45       * @throws UnitException on error
46       * @throws ValueRuntimeException on error
47       */
48      @Test
49      public void testVectorMethods() throws ValueRuntimeException, UnitException
50      {
51          double[] denseTestData = DOUBLEVECTOR.denseArray(105);
52          double[] sparseTestData = DOUBLEVECTOR.sparseArray(105);
53          double[] reverseSparseTestData = new double[sparseTestData.length];
54          // sparseTestData and reverseSparseTestData should not "run out of values" at the same index
55          for (int index = 0; index < sparseTestData.length; index++)
56          {
57              reverseSparseTestData[reverseSparseTestData.length - 1 - index] = sparseTestData[index];
58          }
59          // Ensure that both have a value at some index (i.c. 10)
60          sparseTestData[10] = 123.456;
61          reverseSparseTestData[10] = sparseTestData[10];
62          // Ensure that there are > 50% positions where both have a non-zero value
63          for (int index = 20; index < 90; index++)
64          {
65              sparseTestData[index] = 10000.456 + index;
66              reverseSparseTestData[index] = 20000.567 + index;
67          }
68          for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
69          {
70              for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
71              {
72                  double[] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
73                  AreaVector am = new AreaVector(DoubleVectorData.instantiate(testData, au.getScale(), storageType), au);
74  
75                  // SPARSE AND DENSE
76                  assertEquals(am, am.toSparse());
77                  assertEquals(am, am.toDense());
78                  assertEquals(am, am.toSparse().toDense());
79                  assertEquals(am, am.toDense().toSparse());
80                  assertEquals(am.hashCode(), am.toSparse().hashCode());
81                  assertEquals(am.hashCode(), am.toDense().hashCode());
82                  assertTrue(am.toDense().isDense());
83                  assertFalse(am.toDense().isSparse());
84                  assertTrue(am.toSparse().isSparse());
85                  assertFalse(am.toSparse().isDense());
86  
87                  // EQUALS
88                  assertEquals(am, am);
89                  assertNotEquals(am, new Object());
90                  assertNotEquals(am, null);
91                  assertNotEquals(am, new LengthVector(
92                          DoubleVectorData.instantiate(testData, LengthUnit.METER.getScale(), storageType), LengthUnit.METER));
93                  assertNotEquals(am, am.divide(2.0d));
94  
95                  // MUTABLE
96                  assertFalse(am.isMutable());
97                  AreaVector ammut = am.mutable();
98                  assertTrue(ammut.isMutable());
99                  assertFalse(am.isMutable());
100                 AreaVector ammut2 = ammut.multiplyBy(1.0);
101                 assertEquals(am, ammut2);
102                 assertTrue(ammut.isMutable());
103                 assertFalse(am.isMutable());
104                 assertTrue(ammut2.isMutable());
105                 ammut2 = ammut2.mutable().divideBy(2.0);
106                 assertEquals(am, ammut);
107                 assertNotEquals(am, ammut2);
108                 AreaVector ammut3 = ammut2.mutable().divideBy(0.0);
109                 for (int index = 0; index < ammut3.size(); index++)
110                 {
111                     if (ammut2.getSI(index) == 0)
112                     {
113                         assertTrue(Double.isNaN(ammut3.getSI(index)), "Value should be NaN");
114 
115                     }
116                     else
117                     {
118                         assertTrue(Double.isInfinite(ammut3.getSI(index)), "Value should be Infinite");
119                     }
120                 }
121 
122                 // ZSUM and CARDINALITY
123                 Area zSum = am.zSum();
124                 double sum = 0;
125                 int card = 0;
126                 for (int index = 0; index < testData.length; index++)
127                 {
128                     sum += testData[index];
129                     card += testData[index] == 0.0d ? 0 : 1;
130                 }
131                 assertEquals(sum, zSum.getInUnit(), 0.1, "zSum");
132                 assertEquals(card, am.cardinality(), "cardinality");
133 
134                 // INCREMENTBY(SCALAR) and DECREMENTBY(SCALAR)
135                 AreaVector amold = am.clone();
136                 Area fa = Area.of(10.0d, "m^2");
137                 AreaVector aminc = am.mutable().incrementBy(fa).immutable();
138                 AreaVector amdec = am.mutable().decrementBy(fa).immutable();
139                 AreaVector amid = aminc.mutable().decrementBy(fa);
140                 assertEquals(am, amold, "immutable vector should not change when converted to mutable");
141                 for (int index = 0; index < testData.length; index++)
142                 {
143                     assertEquals(am.getSI(index), amid.getSI(index), 0.1,
144                             "increment and decrement with scalar should result in same vector");
145                     assertEquals(au.getScale().toStandardUnit(testData[index]) + 10.0, aminc.getSI(index), 0.1,
146                             "m + s = (m+s)");
147                     assertEquals(au.getScale().toStandardUnit(testData[index]) - 10.0, amdec.getSI(index), 0.1,
148                             "m - s = (m-s)");
149                 }
150 
151                 // MULTIPLYBY() and DIVIDEBY(), TIMES(), DIVIDE()
152                 AreaVector amt5 = am.mutable().multiplyBy(5.0d).immutable();
153                 AreaVector amd5 = am.mutable().divideBy(5.0d).immutable();
154                 AreaVector amtd = amt5.mutable().divideBy(5.0d);
155                 AreaVector amtimD = am.times(5.0d);
156                 AreaVector amtimF = am.times(5.0f);
157                 AreaVector amdivD = am.divide(5.0d);
158                 AreaVector amdivF = am.divide(5.0f);
159                 for (int index = 0; index < testData.length; index++)
160                 {
161                     assertEquals(am.getSI(index), amtd.getSI(index), 0.1,
162                             "times followed by divide with constant should result in same vector");
163                     assertEquals(au.getScale().toStandardUnit(testData[index]) * 5.0d, amt5.getSI(index), 0.1,
164                             "m * 5.0 = (m*5.0)");
165                     assertEquals(au.getScale().toStandardUnit(testData[index]) / 5.0d, amd5.getSI(index), 0.1,
166                             "m / 5.0 = (m/5.0)");
167                     assertEquals(amt5.getSI(index), amtimD.getSI(index), 0.1d, "amtimD");
168                     assertEquals(amt5.getSI(index), amtimF.getSI(index), 0.1d, "amtimF");
169                     assertEquals(amd5.getSI(index), amdivD.getSI(index), 0.01d, "amdivD");
170                     assertEquals(amd5.getSI(index), amdivF.getSI(index), 0.01d, "amdivD");
171                 }
172 
173                 // GET(), GETINUNIT()
174                 assertEquals(new Area(testData[2], au), am.get(2), "get()");
175                 assertEquals(au.getScale().toStandardUnit(testData[2]), am.getSI(2), 0.1, "getSI()");
176                 assertEquals(testData[2], am.getInUnit(2), 0.1, "getInUnit()");
177                 assertEquals(AreaUnit.SQUARE_YARD.getScale().fromStandardUnit(au.getScale().toStandardUnit(testData[2])),
178                         am.getInUnit(2, AreaUnit.SQUARE_YARD), 0.1, "getInUnit(unit)");
179 
180                 // SET(), SETINUNIT()
181                 Area fasqft = new Area(10.5d, AreaUnit.SQUARE_FOOT);
182                 AreaVector famChange = am.clone().mutable();
183                 famChange.set(2, fasqft);
184                 assertEquals(fasqft.si, famChange.get(2).si, 0.1d, "set()");
185                 famChange = am.clone().mutable();
186                 famChange.setSI(2, 123.4d);
187                 assertEquals(123.4d, famChange.get(2).si, 0.1d, "setSI()");
188                 famChange = am.clone().mutable();
189                 famChange.setInUnit(2, 1.2d);
190                 assertEquals(1.2d, famChange.getInUnit(2), 0.1d, "setInUnit()");
191                 famChange = am.clone().mutable();
192                 famChange.setInUnit(2, 1.5d, AreaUnit.HECTARE);
193                 assertEquals(15000.0d, famChange.get(2).si, 1.0d, "setInUnit(unit)");
194 
195                 // GETVALUES(), GETSCALARS()
196                 double[] valsi = am.getValuesSI();
197                 double[] valunit = am.getValuesInUnit();
198                 double[] valsqft = am.getValuesInUnit(AreaUnit.SQUARE_YARD);
199                 Area[] valscalars = am.getScalars();
200                 for (int index = 0; index < testData.length; index++)
201                 {
202                     assertEquals(au.getScale().toStandardUnit(testData[index]), valsi[index], 0.1, "getValuesSI()");
203                     assertEquals(testData[index], valunit[index], 0.1, "getValuesInUnit()");
204                     assertEquals(
205                             AreaUnit.SQUARE_YARD.getScale().fromStandardUnit(au.getScale().toStandardUnit(testData[index])),
206                             valsqft[index], 0.1, "getValuesInUnit(unit)");
207                     assertEquals(au.getScale().toStandardUnit(testData[index]), valscalars[index].si, 0.1,
208                             "getValuesInUnit(unit)");
209                 }
210 
211                 // ASSIGN FUNCTION ABS, CEIL, FLOOR, NEG, RINT
212                 AreaVector amdiv2 = am.divide(2.0d);
213                 assertEquals(am.getStorageType(), amdiv2.getStorageType());
214                 assertEquals(am.getDisplayUnit(), amdiv2.getDisplayUnit());
215                 AreaVector amAbs = amdiv2.mutable().abs().immutable();
216                 assertEquals(am.getStorageType(), amAbs.getStorageType());
217                 assertEquals(am.getDisplayUnit(), amAbs.getDisplayUnit());
218                 AreaVector amCeil = amdiv2.mutable().ceil().immutable();
219                 assertEquals(am.getStorageType(), amCeil.getStorageType());
220                 assertEquals(am.getDisplayUnit(), amCeil.getDisplayUnit());
221                 AreaVector amFloor = amdiv2.mutable().floor().immutable();
222                 assertEquals(am.getStorageType(), amFloor.getStorageType());
223                 assertEquals(am.getDisplayUnit(), amFloor.getDisplayUnit());
224                 AreaVector amNeg = amdiv2.mutable().neg().immutable();
225                 assertEquals(am.getStorageType(), amNeg.getStorageType());
226                 assertEquals(am.getDisplayUnit(), amNeg.getDisplayUnit());
227                 AreaVector amRint = amdiv2.mutable().rint().immutable();
228                 assertEquals(am.getStorageType(), amRint.getStorageType());
229                 assertEquals(am.getDisplayUnit(), amRint.getDisplayUnit());
230                 for (int index = 0; index < testData.length; index++)
231                 {
232                     // TODO: Should be rounded IN THE UNIT rather than BY SI VALUES
233                     assertEquals(au.getScale().toStandardUnit(testData[index]) / 2.0d, amdiv2.getSI(index), 0.1d, "div2");
234                     assertEquals(Math.abs(au.getScale().toStandardUnit(testData[index]) / 2.0d), amAbs.getSI(index), 0.1d,
235                             "abs");
236                     assertEquals(Math.ceil(au.getScale().toStandardUnit(testData[index]) / 2.0d), amCeil.getSI(index), 0.1d,
237                             "ceil");
238                     assertEquals(Math.floor(au.getScale().toStandardUnit(testData[index]) / 2.0d), amFloor.getSI(index), 0.1d,
239                             "floor");
240                     assertEquals(-au.getScale().toStandardUnit(testData[index]) / 2.0d, amNeg.getSI(index), 0.1d, "neg");
241                     assertEquals(Math.rint(au.getScale().toStandardUnit(testData[index]) / 2.0d), amRint.getSI(index), 0.1d,
242                             "rint");
243                 }
244 
245                 // TEST METHODS THAT INVOLVE TWO VECTOR INSTANCES
246 
247                 for (StorageType storageType2 : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
248                 {
249                     double[] testData2 = storageType2.equals(StorageType.DENSE) ? denseTestData : reverseSparseTestData;
250                     for (AreaUnit au2 : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
251                     {
252                         // PLUS and INCREMENTBY(VECTOR)
253                         AreaVector am2 =
254                                 new AreaVector(DoubleVectorData.instantiate(testData2, au2.getScale(), storageType2), au2);
255                         AreaVector amSum1 = am.plus(am2);
256                         AreaVector amSum2 = am2.plus(am);
257                         AreaVector amSum3 = am.mutable().incrementBy(am2).immutable();
258                         // different order of running out of nonzero values
259                         AreaVector amSum4 = am2.mutable().incrementBy(am).immutable();
260                         assertEquals(amSum1, amSum2, "a+b == b+a");
261                         assertEquals(amSum1, amSum3, "a+b == b+a");
262                         assertEquals(amSum1, amSum4, "a+c == c+a");
263                         for (int index = 0; index < testData.length; index++)
264                         {
265                             double tolerance =
266                                     Double.isFinite(amSum1.getSI(index)) ? Math.abs(amSum1.getSI(index) / 10000.0d) : 0.1d;
267                             assertEquals(
268                                     au.getScale().toStandardUnit(testData[index])
269                                             + au2.getScale().toStandardUnit(testData2[index]),
270                                     amSum1.getSI(index), tolerance, "value in vector matches");
271                         }
272 
273                         // MINUS and DECREMENTBY(VECTOR)
274                         AreaVector amDiff1 = am.minus(am2);
275                         AreaVector amDiff2 = am2.minus(am).mutable().neg();
276                         AreaVector amDiff3 = am.mutable().decrementBy(am2).immutable();
277                         // different order of running out of nonzero values
278                         AreaVector amDiff4 = am2.mutable().decrementBy(am).neg().immutable();
279                         assertEquals(amDiff1, amDiff2, "a-b == -(b-a)");
280                         assertEquals(amDiff1, amDiff3, "a-b == -(b-a)");
281                         assertEquals(amDiff1, amDiff4, "a-c == -(c-a)");
282                         for (int index = 0; index < testData.length; index++)
283                         {
284                             double tolerance =
285                                     Double.isFinite(amDiff1.getSI(index)) ? Math.abs(amDiff1.getSI(index) / 10000.0d) : 0.1d;
286                             assertEquals(
287                                     au.getScale().toStandardUnit(testData[index])
288                                             - au2.getScale().toStandardUnit(testData2[index]),
289                                     amDiff1.getSI(index), tolerance, "value in vector matches");
290                         }
291 
292                         // TIMES(VECTOR) and DIVIDE(VECTOR)
293                         SIVector amTim = am.times(am2);
294                         SIVector amDiv = am.divide(am2);
295                         assertEquals("m4", amTim.getDisplayUnit().getQuantity().getSiDimensions().toString(false, false, false),
296                                 "unit of m2 * m2 should be m4");
297                         assertEquals("", amDiv.getDisplayUnit().getQuantity().getSiDimensions().toString(false, false, false),
298                                 "unit of m2 / m2 should be empty string");
299                         for (int index = 0; index < testData.length; index++)
300                         {
301                             double tolerance =
302                                     Double.isFinite(amTim.getSI(index)) ? Math.abs(amTim.getSI(index) / 10000.0d) : 0.1d;
303                             assertEquals(
304                                     au.getScale().toStandardUnit(testData[index])
305                                             * au2.getScale().toStandardUnit(testData2[index]),
306                                     amTim.getSI(index), tolerance, "value in m2 * m2 matches");
307                             tolerance = Double.isFinite(amDiv.getSI(index)) ? Math.abs(amDiv.getSI(index) / 10000.0d) : 0.1d;
308                             assertEquals(
309                                     au.getScale().toStandardUnit(testData[index])
310                                             / au2.getScale().toStandardUnit(testData2[index]),
311                                     amDiv.getSI(index), tolerance, "value in m2 / m2 matches (could be NaN)");
312                         }
313                         // This does not compile: SIVector amTim2 = am.immutable().multiplyBy(am2).immutable();
314                     }
315                 }
316             }
317         }
318     }
319 
320     /**
321      * Test if mutable methods give an error in case the vector is immutable.
322      */
323     @Test
324     public void testImmutableVector()
325     {
326         double[] denseTestData = DOUBLEVECTOR.denseArray(105);
327         double[] sparseTestData = DOUBLEVECTOR.sparseArray(105);
328 
329         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
330         {
331             for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
332             {
333                 double[] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
334                 AreaVector am = new AreaVector(DoubleVectorData.instantiate(testData, au.getScale(), storageType), au);
335                 am = am.immutable();
336                 final AreaVector amPtr = am;
337                 Area fa = Area.of(10.0d, "m^2");
338                 Try.testFail(() -> amPtr.assign(DoubleMathFunctions.ABS), "ImmutableVector.assign(...) should throw error");
339                 Try.testFail(() -> amPtr.decrementBy(fa), "ImmutableVector.decrementBy(scalar) should throw error");
340                 Try.testFail(() -> amPtr.decrementBy(amPtr), "ImmutableVector.decrementBy(vector) should throw error");
341                 Try.testFail(() -> amPtr.incrementBy(fa), "ImmutableVector.incrementBy(scalar) should throw error");
342                 Try.testFail(() -> amPtr.incrementBy(amPtr), "ImmutableVector.incrementBy(vector) should throw error");
343                 Try.testFail(() -> amPtr.divideBy(2.0d), "ImmutableVector.divideBy(factor) should throw error");
344                 Try.testFail(() -> amPtr.multiplyBy(2.0d), "ImmutableVector.multiplyBy(factor) should throw error");
345                 Try.testFail(() -> amPtr.set(1, fa), "ImmutableVector.set() should throw error");
346                 Try.testFail(() -> amPtr.setSI(1, 20.1d), "ImmutableVector.setSI() should throw error");
347                 Try.testFail(() -> amPtr.setInUnit(1, 15.2d), "ImmutableVector.setInUnit(f) should throw error");
348                 Try.testFail(() -> amPtr.setInUnit(1, 15.2d, AreaUnit.ARE),
349                         "ImmutableVector.setInUnit(f, u) should throw error");
350                 Try.testFail(() -> amPtr.abs(), "ImmutableVector.abs() should throw error");
351                 Try.testFail(() -> amPtr.ceil(), "ImmutableVector.ceil() should throw error");
352                 Try.testFail(() -> amPtr.floor(), "ImmutableVector.floor() should throw error");
353                 Try.testFail(() -> amPtr.neg(), "ImmutableVector.neg() should throw error");
354                 Try.testFail(() -> amPtr.rint(), "ImmutableVector.rint() should throw error");
355             }
356         }
357     }
358 
359     /**
360      * Test toString() methods. TODO: expand?
361      */
362     @Test
363     public void testVectorToString()
364     {
365         double[] denseTestData = DOUBLEVECTOR.denseArray(105);
366         double[] sparseTestData = DOUBLEVECTOR.sparseArray(105);
367 
368         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
369         {
370             for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
371             {
372                 double[] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
373                 AreaVector am = new AreaVector(DoubleVectorData.instantiate(testData, au.getScale(), storageType), au);
374                 String s1 = am.toString(); // non-verbose with unit
375                 assertTrue(s1.contains(au.getDefaultTextualAbbreviation()));
376                 String s2 = am.toString(AreaUnit.SQUARE_INCH); // non-verbose with unit
377                 assertTrue(s2.contains(AreaUnit.SQUARE_INCH.getDefaultTextualAbbreviation()));
378                 String s3 = am.toString(AreaUnit.SQUARE_INCH, true, true); // verbose with unit
379                 assertTrue(s3.contains(AreaUnit.SQUARE_INCH.getDefaultTextualAbbreviation()));
380                 if (storageType.equals(StorageType.DENSE))
381                 {
382                     assertTrue(s3.contains("Dense"));
383                     assertFalse(s3.contains("Sparse"));
384                 }
385                 else
386                 {
387                     assertFalse(s3.contains("Dense"));
388                     assertTrue(s3.contains("Sparse"));
389                 }
390                 assertTrue(s3.contains("Rel"));
391                 assertFalse(s3.contains("Abs"));
392                 assertTrue(s3.contains("Immutable"));
393                 assertFalse(s3.contains("Mutable"));
394                 AreaVector ammut = am.mutable();
395                 String smut = ammut.toString(AreaUnit.SQUARE_INCH, true, true); // verbose with unit
396                 assertFalse(smut.contains("Immutable"));
397                 assertTrue(smut.contains("Mutable"));
398                 String sNotVerbose = ammut.toString(false, false);
399                 assertFalse(sNotVerbose.contains("Rel"));
400                 assertFalse(sNotVerbose.contains("Abs"));
401                 assertFalse(sNotVerbose.contains("Immutable"));
402                 assertFalse(sNotVerbose.contains("Mutable"));
403                 assertFalse(sNotVerbose.contains(au.getDefaultTextualAbbreviation()));
404             }
405         }
406         TimeVector tm = new TimeVector(
407                 DoubleVectorData.instantiate(denseTestData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE), TimeUnit.DEFAULT);
408         String st = tm.toString(TimeUnit.DEFAULT, true, true); // verbose with unit
409         assertFalse(st.contains("Rel"));
410         assertTrue(st.contains("Abs"));
411         LengthVector lm = new LengthVector(
412                 DoubleVectorData.instantiate(denseTestData, LengthUnit.SI.getScale(), StorageType.DENSE), LengthUnit.SI);
413         String sl = lm.toString(LengthUnit.SI, true, true); // verbose with unit
414         assertTrue(sl.contains("Rel"));
415         assertFalse(sl.contains("Abs"));
416     }
417 
418     /**
419      * Test the extra methods that Absolute and Relative with Absolute matrices implement.
420      */
421     @Test
422     public void testSpecialVectorMethodsRelWithAbs()
423     {
424         double[] denseTestData = DOUBLEVECTOR.denseArray(105);
425         TimeVector tm = new TimeVector(
426                 DoubleVectorData.instantiate(denseTestData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE), TimeUnit.DEFAULT);
427         DurationVector dm = new DurationVector(
428                 DoubleVectorData.instantiate(denseTestData, DurationUnit.MINUTE.getScale(), StorageType.DENSE),
429                 DurationUnit.SECOND);
430         assertTrue(tm.isAbsolute());
431         assertFalse(dm.isAbsolute());
432         assertFalse(tm.isRelative());
433         assertTrue(dm.isRelative());
434 
435         TimeVector absPlusRel = tm.plus(dm);
436         TimeVector absMinusRel = tm.minus(dm);
437         double[] halfDenseData = DOUBLEVECTOR.denseArray(105);
438         for (int index = 0; index < halfDenseData.length; index++)
439         {
440             halfDenseData[index] *= 0.5;
441         }
442         TimeVector halfTimeVector = new TimeVector(
443                 DoubleVectorData.instantiate(halfDenseData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE), TimeUnit.DEFAULT);
444         DurationVector absMinusAbs = tm.minus(halfTimeVector);
445         TimeVector absDecByRelS = tm.mutable().decrementBy(Duration.of(1.0d, "min"));
446         TimeVector absDecByRelM = tm.mutable().decrementBy(dm.divide(2.0d));
447         TimeVector relPlusAbs = dm.plus(tm);
448         for (int index = 0; index < denseTestData.length; index++)
449         {
450             assertEquals(61.0 * denseTestData[index], absPlusRel.getSI(index), 0.01, "absPlusRel");
451             assertEquals(-59.0 * denseTestData[index], absMinusRel.getSI(index), 0.01, "absMinusRel");
452             assertEquals(denseTestData[index] / 2.0, absMinusAbs.getSI(index), 0.01, "absMinusAbs");
453             assertEquals(denseTestData[index] - 60.0, absDecByRelS.getSI(index), 0.01, "absDecByRelS");
454             assertEquals(-29.0 * denseTestData[index], absDecByRelM.getSI(index), 0.01, "absDecByRelM");
455             assertEquals(61.0 * denseTestData[index], relPlusAbs.getSI(index), 0.01, "relPlusAbs");
456         }
457         for (int dLength : new int[] {-1, 1})
458         {
459             double[] other = DOUBLEVECTOR.denseArray(denseTestData.length + dLength);
460             TimeVector wrongTimeVector = new TimeVector(
461                     DoubleVectorData.instantiate(other, TimeUnit.DEFAULT.getScale(), StorageType.DENSE), TimeUnit.DEFAULT);
462             try
463             {
464                 tm.mutable().minus(wrongTimeVector);
465                 fail("Mismatching size should have thrown a ValueRuntimeException");
466             }
467             catch (ValueRuntimeException vre)
468             {
469                 // Ignore expected exception
470             }
471         }
472         assertTrue(DoubleVectorData.instantiate(denseTestData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE).toString()
473                 .startsWith("DoubleVectorData"), "toString returns something informative");
474     }
475 
476     /**
477      * Test the instantiateAbs method and instantiateScalarAbsSI method.
478      */
479     @Test
480     public void testInstantiateAbs()
481     {
482         double[] denseTestData = DOUBLEVECTOR.denseArray(105);
483         TimeVector timeVector = new TimeVector(
484                 DoubleVectorData.instantiate(denseTestData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE), TimeUnit.DEFAULT);
485         DurationVector durationVector = new DurationVector(
486                 DoubleVectorData.instantiate(denseTestData, DurationUnit.MINUTE.getScale(), StorageType.DENSE),
487                 DurationUnit.SECOND);
488 
489         double[] halfDenseData = DOUBLEVECTOR.denseArray(105);
490         for (int index = 0; index < halfDenseData.length; index++)
491         {
492             halfDenseData[index] *= 0.5;
493         }
494         TimeVector relPlusAbsTime = durationVector.plus(timeVector);
495         for (int index = 0; index < denseTestData.length; index++)
496         {
497             assertEquals(61.0 * denseTestData[index], relPlusAbsTime.getSI(index), 0.01, "relPlusAbsTime");
498         }
499         Time time = durationVector.instantiateScalarAbsSI(123.456f, TimeUnit.EPOCH_DAY);
500         assertEquals(TimeUnit.EPOCH_DAY, time.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
501         assertEquals(123.456f, time.si, 0.1, "Value of instantiateScalarAbsSI matches");
502 
503         AngleVector angleVector = new AngleVector(
504                 DoubleVectorData.instantiate(denseTestData, AngleUnit.DEGREE.getScale(), StorageType.DENSE), AngleUnit.DEGREE);
505         DirectionVector directionVector = new DirectionVector(
506                 DoubleVectorData.instantiate(denseTestData, DirectionUnit.EAST_DEGREE.getScale(), StorageType.DENSE),
507                 DirectionUnit.EAST_DEGREE);
508 
509         DirectionVector relPlusAbsDirection = angleVector.plus(directionVector);
510         for (int index = 0; index < denseTestData.length; index++)
511         {
512             assertEquals(2.0 / 180 * Math.PI * denseTestData[index], relPlusAbsDirection.getSI(index), 0.01,
513                     "relPlusAbsDirection");
514         }
515         Direction direction = angleVector.instantiateScalarAbsSI(123.456f, DirectionUnit.NORTH_RADIAN);
516         assertEquals(DirectionUnit.NORTH_RADIAN, direction.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
517         assertEquals(123.456f, direction.si, 0.1, "Value of instantiateScalarAbsSI matches");
518 
519         TemperatureVector temperatureVector = new TemperatureVector(
520                 DoubleVectorData.instantiate(denseTestData, TemperatureUnit.DEGREE_FAHRENHEIT.getScale(), StorageType.DENSE),
521                 TemperatureUnit.DEGREE_FAHRENHEIT);
522         AbsoluteTemperatureVector absoluteTemperatureVector = new AbsoluteTemperatureVector(
523                 DoubleVectorData.instantiate(denseTestData, AbsoluteTemperatureUnit.KELVIN.getScale(), StorageType.DENSE),
524                 AbsoluteTemperatureUnit.KELVIN);
525 
526         AbsoluteTemperatureVector relPlusAbsTemperature = temperatureVector.plus(absoluteTemperatureVector);
527         for (int index = 0; index < denseTestData.length; index++)
528         {
529             assertEquals((1.0 + 5.0 / 9.0) * denseTestData[index], relPlusAbsTemperature.getSI(index), 0.01,
530                     "relPlusAbsTemperature");
531         }
532         AbsoluteTemperature absoluteTemperature =
533                 temperatureVector.instantiateScalarAbsSI(123.456f, AbsoluteTemperatureUnit.DEGREE_FAHRENHEIT);
534         assertEquals(AbsoluteTemperatureUnit.DEGREE_FAHRENHEIT, absoluteTemperature.getDisplayUnit(),
535                 "Unit of instantiateScalarAbsSI matches");
536         assertEquals(123.456f, absoluteTemperature.si, 0.1, "Value of instantiateScalarAbsSI matches");
537 
538         LengthVector lengthVector = new LengthVector(
539                 DoubleVectorData.instantiate(denseTestData, LengthUnit.MILE.getScale(), StorageType.DENSE), LengthUnit.MILE);
540         PositionVector positionVector = new PositionVector(
541                 DoubleVectorData.instantiate(denseTestData, PositionUnit.KILOMETER.getScale(), StorageType.DENSE),
542                 PositionUnit.KILOMETER);
543 
544         PositionVector relPlusAbsPosition = lengthVector.plus(positionVector);
545         for (int index = 0; index < denseTestData.length; index++)
546         {
547             assertEquals(2609.344 * denseTestData[index], relPlusAbsPosition.getSI(index), 1, "relPlusAbsPosition");
548         }
549         Position position = lengthVector.instantiateScalarAbsSI(123.456f, PositionUnit.ANGSTROM);
550         assertEquals(PositionUnit.ANGSTROM, position.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
551         assertEquals(123.456f, position.si, 0.1, "Value of instantiateScalarAbsSI matches");
552     }
553 
554     /**
555      * Test the equals method.
556      */
557     @SuppressWarnings("unlikely-arg-type")
558     @Test
559     public void testEquals()
560     {
561         double[] testData = DOUBLEVECTOR.denseArray(123);
562         testData[2] = 0;
563         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
564         {
565             DoubleVectorData dvd = DoubleVectorData.instantiate(testData, TemperatureUnit.KELVIN.getScale(), storageType);
566             assertTrue(dvd.equals(dvd), "Double vector data is equal to itself");
567             assertFalse(dvd.equals(null), "Double vector data is not equal to null");
568             assertFalse(dvd.equals("some string"), "Double vector data is not equal to some string");
569             assertTrue(dvd.equals(dvd.toSparse()), "Double vector is equal to sparse version of itself");
570             assertTrue(dvd.equals(dvd.toDense()), "Double vector is equal to dense version of itself");
571             for (StorageType storageType2 : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
572             {
573                 DoubleVectorData dvd2 = DoubleVectorData.instantiate(testData, TemperatureUnit.KELVIN.getScale(), storageType2);
574                 assertEquals(dvd, dvd2,
575                         "Double vector data is equal to other double vector containing same values regardless of storage type");
576                 double[] testData2 = DOUBLEVECTOR.denseArray(122);
577                 testData2[2] = 0;
578                 dvd2 = DoubleVectorData.instantiate(testData2, TemperatureUnit.KELVIN.getScale(), storageType2);
579                 assertFalse(dvd.equals(dvd2),
580                         "Double vector data is not equal to other double vector containing same values except last one");
581                 testData2 = DOUBLEVECTOR.denseArray(123);
582                 dvd2 = DoubleVectorData.instantiate(testData2, TemperatureUnit.KELVIN.getScale(), storageType2);
583                 assertFalse(dvd.equals(dvd2),
584                         "Double vector data is not equal to other double vector containing same values except for one zero");
585             }
586         }
587     }
588 
589     /**
590      * Test the plus and similar methods.
591      */
592     @Test
593     public void operationTest()
594     {
595         double[] testValues = new double[] {0, 123.456d, 0, -273.15, -273.15, 0, -273.15, 234.567d, 0, 0};
596         double[] testValues2 = new double[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
597         for (AbsoluteTemperatureUnit temperatureUnit : new AbsoluteTemperatureUnit[] {AbsoluteTemperatureUnit.KELVIN,
598                 AbsoluteTemperatureUnit.DEGREE_CELSIUS, AbsoluteTemperatureUnit.DEGREE_FAHRENHEIT})
599         {
600             for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
601             {
602                 AbsoluteTemperatureVector atv = new AbsoluteTemperatureVector(testValues, temperatureUnit, storageType);
603                 for (TemperatureUnit relativeTemperatureUnit : new TemperatureUnit[] {TemperatureUnit.KELVIN,
604                         TemperatureUnit.DEGREE_CELSIUS, TemperatureUnit.DEGREE_FAHRENHEIT})
605                 {
606                     for (StorageType storageType2 : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
607                     {
608                         TemperatureVector rtv = new TemperatureVector(testValues2, relativeTemperatureUnit, storageType2);
609                         AbsoluteTemperatureVector sumtv = atv.plus(rtv);
610                         compareSum(atv.getValuesInUnit(AbsoluteTemperatureUnit.KELVIN),
611                                 rtv.getValuesInUnit(TemperatureUnit.KELVIN),
612                                 sumtv.getValuesInUnit(AbsoluteTemperatureUnit.KELVIN));
613                         AbsoluteTemperatureVector difftv = atv.minus(rtv);
614                         compareSum(rtv.getValuesInUnit(TemperatureUnit.KELVIN),
615                                 difftv.getValuesInUnit(AbsoluteTemperatureUnit.KELVIN),
616                                 atv.getValuesInUnit(AbsoluteTemperatureUnit.KELVIN));
617 
618                         String s = atv.toString(temperatureUnit);
619                         assertTrue(s.startsWith("["), "toString returns something sensible");
620                         assertTrue(s.endsWith("] " + temperatureUnit.toString()), "toString returns something sensible");
621                         // System.out.println(atv.toString(true, true));
622                         s = atv.toString(true, true);
623                         assertTrue(s.contains("Immutable"), "toString includes Immutable");
624                         assertTrue(s.contains("Abs"), "toString includes Abs");
625                         assertTrue(s.contains(atv.isDense() ? "Dense" : "Sparse"), "toString includes Dense or Sparse");
626                         assertTrue(s.endsWith("] " + temperatureUnit.toString()), "toString returns something sensible");
627                         s = atv.mutable().toString(true, true);
628                         assertTrue(s.contains("Mutable"), "toString includes Mutable");
629 
630                         s = rtv.toString();
631                         assertTrue(s.startsWith("["), "toString returns something sensible");
632                         assertTrue(s.endsWith("] " + relativeTemperatureUnit.toString()),
633                                 "toString returns something sensible");
634                         s = rtv.toString(true, true);
635                         assertTrue(s.contains("Immutable"), "toString includes Immutable");
636                         assertTrue(s.contains("Rel"), "toString includes Rel");
637                         assertTrue(s.contains(rtv.isDense() ? "Dense" : "Sparse"), "toString includes Dense or Sparse");
638                         assertTrue(s.endsWith("] " + relativeTemperatureUnit.toString()),
639                                 "toString returns something sensible");
640                         s = rtv.mutable().toString(true, true);
641                         assertTrue(s.contains("Mutable"), "toString includes Mutable");
642 
643                     }
644                 }
645             }
646         }
647     }
648 
649     /**
650      * Check that two arrays and a sum array match.
651      * @param left double[]; the left array
652      * @param right double[]; the right array
653      * @param sum double[]; the sum array
654      */
655     public void compareSum(final double[] left, final double[] right, final double[] sum)
656     {
657         assertEquals(left.length, sum.length, "length of left must equal length of sum");
658         assertEquals(right.length, sum.length, "length of right must equal length of sum");
659         for (int i = 0; i < sum.length; i++)
660         {
661             assertEquals(left[i] + right[i], sum[i], 0.001, "left plus right is sum");
662         }
663     }
664 
665 }