View Javadoc
1   package org.djunits.value.vdouble.matrix;
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.matrix.base.DoubleSparseValue;
23  import org.djunits.value.vdouble.matrix.data.DoubleMatrixData;
24  import org.djunits.value.vdouble.scalar.AbsoluteTemperature;
25  import org.djunits.value.vdouble.scalar.Area;
26  import org.djunits.value.vdouble.scalar.Direction;
27  import org.djunits.value.vdouble.scalar.Duration;
28  import org.djunits.value.vdouble.scalar.Length;
29  import org.djunits.value.vdouble.scalar.Position;
30  import org.djunits.value.vdouble.scalar.Time;
31  import org.djunits.value.vdouble.vector.AreaVector;
32  import org.djunits.value.vfloat.matrix.FloatAreaMatrix;
33  import org.djunits.value.vfloat.vector.FLOATVECTOR;
34  import org.djutils.exceptions.Try;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * ...
39   */
40  public class DoubleMatrixMethodTest
41  {
42  
43      /**
44       * Test the standard methods of all matrix classes.
45       * @throws UnitException on error
46       * @throws ValueRuntimeException on error
47       */
48      @Test
49      @SuppressWarnings("checkstyle:methodlength")
50      public void testMatrixMethods() throws ValueRuntimeException, UnitException
51      {
52          double[][] denseTestData = DOUBLEMATRIX.denseRectArrays(10, 20, false);
53          double[][] sparseTestData = DOUBLEMATRIX.sparseRectArrays(10, 20, false);
54          double[][] reverseSparseTestData = new double[sparseTestData.length][];
55          // sparseTestData and reverseSparseTestData should not "run out of values" at the same index
56          for (int index = 0; index < sparseTestData.length; index++)
57          {
58              reverseSparseTestData[reverseSparseTestData.length - 1 - index] = sparseTestData[index];
59          }
60          // Ensure that there are > 50% positions where both have a non-zero value
61          for (int row = 1; row < 8; row++)
62          {
63              for (int col = 2; col < 18; col++)
64              {
65                  sparseTestData[row][col] = 10000.456 + row + 100 * col;
66                  reverseSparseTestData[row][col] = 20000.567 + row + 100 * col;
67              }
68          }
69  
70          for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
71          {
72              for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
73              {
74                  double[][] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
75                  AreaMatrix am = new AreaMatrix(testData, au, storageType);
76  
77                  // INDEX CHECKING
78                  for (int row : new int[] {-1, 0, denseTestData.length - 1, denseTestData.length})
79                  {
80                      for (int col : new int[] {-1, 0, denseTestData[0].length - 1, denseTestData[0].length})
81                      {
82                          if (row < 0 || col < 0 || row >= denseTestData.length || col >= denseTestData[0].length)
83                          {
84                              try
85                              {
86                                  am.get(row, col);
87                                  fail("bad row or bad column value should have thrown an IndexOutOfBoundsException");
88                              }
89                              catch (IndexOutOfBoundsException vre)
90                              {
91                                  // Ignore expected exception
92                              }
93                          }
94                          else
95                          {
96                              am.get(row, col);
97                          }
98                      }
99                      if (row < 0 || row >= denseTestData.length)
100                     {
101                         try
102                         {
103                             am.getRow(row);
104                             fail("getRow with bad row value should have thrown an IndexOutOfBoundsException");
105                         }
106                         catch (IndexOutOfBoundsException vre)
107                         {
108                             // Ignore expected exception
109                         }
110                     }
111                 }
112                 for (int col : new int[] {-1, 0, denseTestData[0].length - 1, denseTestData[0].length})
113                 {
114                     if (col < 0 || col >= denseTestData[0].length)
115                     {
116                         try
117                         {
118                             am.getColumn(col);
119                             fail("getColumn with bad column value should have thrown an IndexOutOfBoundsException");
120                         }
121                         catch (IndexOutOfBoundsException vre)
122                         {
123                             // Ignore expected exception
124                         }
125                     }
126                     else
127                     {
128                         am.getColumn(col);
129                     }
130                 }
131 
132                 // SPARSE AND DENSE
133                 assertEquals(am, am.toSparse());
134                 assertEquals(am, am.toDense());
135                 assertEquals(am, am.toSparse().toDense());
136                 assertEquals(am, am.toDense().toSparse());
137                 assertEquals(am.hashCode(), am.toSparse().hashCode());
138                 assertEquals(am.hashCode(), am.toDense().hashCode());
139                 assertTrue(am.toDense().isDense());
140                 assertFalse(am.toDense().isSparse());
141                 assertTrue(am.toSparse().isSparse());
142                 assertFalse(am.toSparse().isDense());
143 
144                 // EQUALS
145                 assertEquals(am, am);
146                 assertNotEquals(am, new Object());
147                 assertNotEquals(am, null);
148                 assertNotEquals(am, new LengthMatrix(testData, LengthUnit.METER, storageType));
149                 assertNotEquals(am, am.divide(2.0d));
150 
151                 // MUTABLE
152                 assertFalse(am.isMutable());
153                 AreaMatrix ammut = am.mutable();
154                 assertTrue(ammut.isMutable());
155                 assertFalse(am.isMutable());
156                 AreaMatrix ammut2 = ammut.multiplyBy(1.0);
157                 assertEquals(am, ammut2);
158                 assertTrue(ammut.isMutable());
159                 assertFalse(am.isMutable());
160                 assertTrue(ammut2.isMutable());
161                 ammut2 = ammut2.mutable().divideBy(2.0);
162                 assertEquals(am, ammut);
163                 assertNotEquals(am, ammut2);
164                 AreaMatrix ammut3 = ammut2.mutable().divideBy(0.0);
165                 for (int row = 0; row < ammut3.rows(); row++)
166                 {
167                     for (int col = 0; col < ammut3.cols(); col++)
168                     {
169                         if (ammut2.getSI(row, col) == 0)
170                         {
171                             assertTrue(Double.isNaN(ammut3.getSI(row, col)), "Value should be NaN");
172 
173                         }
174                         else
175                         {
176                             assertTrue(Double.isInfinite(ammut3.getSI(row, col)), "Value should be Infinite");
177                         }
178                     }
179                 }
180 
181                 // ZSUM and CARDINALITY
182                 Area zSum = am.zSum();
183                 double sum = 0;
184                 int card = 0;
185                 for (int row = 0; row < testData.length; row++)
186                 {
187                     for (int col = 0; col < testData[0].length; col++)
188                     {
189                         sum += testData[row][col];
190                         card += testData[row][col] == 0.0d ? 0 : 1;
191                     }
192                 }
193                 assertEquals(sum, zSum.getInUnit(), 0.1, "zSum");
194                 assertEquals(card, am.cardinality(), "cardinality");
195                 AreaMatrix ammutZero = ammut.multiplyBy(0.0);
196                 assertEquals(0, ammutZero.cardinality(), "cardinality should be 0");
197                 assertEquals(0.0, ammutZero.zSum().getSI(), 0, "zSum should be 0");
198 
199                 // INCREMENTBY(SCALAR) and DECREMENTBY(SCALAR)
200                 AreaMatrix amold = am.clone();
201                 Area fa = Area.of(10.0d, "m^2");
202                 AreaMatrix aminc = am.mutable().incrementBy(fa).immutable();
203                 AreaMatrix amdec = am.mutable().decrementBy(fa).immutable();
204                 AreaMatrix amid = aminc.mutable().decrementBy(fa);
205                 assertEquals(am, amold, "immutable matrix should not change when converted to mutable");
206                 for (int row = 0; row < testData.length; row++)
207                 {
208                     for (int col = 0; col < testData[0].length; col++)
209                     {
210                         assertEquals(am.getSI(row, col), amid.getSI(row, col), 0.1,
211                                 "increment and decrement with scalar should result in same matrix");
212                         assertEquals(au.getScale().toStandardUnit(testData[row][col]) + 10.0, aminc.getSI(row, col), 0.1,
213                                 "m + s = (m+s)");
214                         assertEquals(au.getScale().toStandardUnit(testData[row][col]) - 10.0, amdec.getSI(row, col), 0.1,
215                                 "m - s = (m-s)");
216                     }
217                 }
218 
219                 // MULTIPLYBY() and DIVIDEBY(), TIMES(), DIVIDE()
220                 AreaMatrix amt5 = am.mutable().multiplyBy(5.0d).immutable();
221                 AreaMatrix amd5 = am.mutable().divideBy(5.0d).immutable();
222                 AreaMatrix amtd = amt5.mutable().divideBy(5.0d);
223                 AreaMatrix amtimD = am.times(5.0d);
224                 AreaMatrix amtimF = am.times(5.0f);
225                 AreaMatrix amdivD = am.divide(5.0d);
226                 AreaMatrix amdivF = am.divide(5.0f);
227                 for (int row = 0; row < testData.length; row++)
228                 {
229                     for (int col = 0; col < testData[0].length; col++)
230                     {
231                         assertEquals(am.getSI(row, col), amtd.getSI(row, col), 0.1,
232                                 "times followed by divide with constant should result in same matrix");
233                         assertEquals(au.getScale().toStandardUnit(testData[row][col]) * 5.0d, amt5.getSI(row, col), 0.1,
234                                 "m * 5.0 = (m*5.0)");
235                         assertEquals(au.getScale().toStandardUnit(testData[row][col]) / 5.0d, amd5.getSI(row, col), 0.1,
236                                 "m / 5.0 = (m/5.0)");
237                         assertEquals(amt5.getSI(row, col), amtimD.getSI(row, col), 0.1d, "amtimD");
238                         assertEquals(amt5.getSI(row, col), amtimF.getSI(row, col), 0.1d, "amtimF");
239                         assertEquals(amd5.getSI(row, col), amdivD.getSI(row, col), 0.01d, "amdivD");
240                         assertEquals(amd5.getSI(row, col), amdivF.getSI(row, col), 0.01d, "amdivD");
241                     }
242                 }
243 
244                 // GET(), GETINUNIT()
245                 assertEquals(new Area(testData[2][2], au), am.get(2, 2), "get()");
246                 assertEquals(au.getScale().toStandardUnit(testData[2][2]), am.getSI(2, 2), 0.1, "getSI()");
247                 assertEquals(testData[2][2], am.getInUnit(2, 2), 0.1, "getInUnit()");
248                 assertEquals(AreaUnit.SQUARE_YARD.getScale().fromStandardUnit(au.getScale().toStandardUnit(testData[2][2])),
249                         am.getInUnit(2, 2, AreaUnit.SQUARE_YARD), 0.1, "getInUnit(unit)");
250 
251                 // SET(), SETINUNIT()
252                 Area fasqft = new Area(10.5d, AreaUnit.SQUARE_FOOT);
253                 AreaMatrix famChange = am.clone().mutable();
254                 famChange.set(2, 2, fasqft);
255                 assertEquals(fasqft.si, famChange.get(2, 2).si, 0.1d, "set()");
256                 famChange = am.clone().mutable();
257                 famChange.setSI(2, 2, 123.4d);
258                 assertEquals(123.4d, famChange.get(2, 2).si, 0.1d, "setSI()");
259                 famChange = am.clone().mutable();
260                 famChange.setInUnit(2, 2, 1.2d);
261                 assertEquals(1.2d, famChange.getInUnit(2, 2), 0.1d, "setInUnit()");
262                 famChange = am.clone().mutable();
263                 famChange.setInUnit(2, 2, 1.5d, AreaUnit.HECTARE);
264                 assertEquals(15000.0d, famChange.get(2, 2).si, 1.0d, "setInUnit(unit)");
265 
266                 // GETROW(), GETCOLUMN(), GETDIAGONAL
267                 double[][] squareData = storageType.equals(StorageType.DENSE) ? DOUBLEMATRIX.denseRectArrays(12, 12, false)
268                         : DOUBLEMATRIX.sparseRectArrays(12, 12, false);
269                 AreaMatrix amSquare = new AreaMatrix(squareData, au, storageType);
270                 double[] row2si = am.getRowSI(2);
271                 double[] col2si = am.getColumnSI(2);
272                 double[] diagsi = amSquare.getDiagonalSI();
273                 AreaVector row2v = am.getRow(2);
274                 AreaVector col2v = am.getColumn(2);
275                 AreaVector diagv = amSquare.getDiagonal();
276                 Area[] row2scalar = am.getRowScalars(2);
277                 Area[] col2scalar = am.getColumnScalars(2);
278                 Area[] diagscalar = amSquare.getDiagonalScalars();
279                 for (int col = 0; col < testData[0].length; col++)
280                 {
281                     assertEquals(au.getScale().toStandardUnit(testData[2][col]), row2si[col], 0.1d, "row2si");
282                     assertEquals(au.getScale().toStandardUnit(testData[2][col]), row2v.getSI(col), 0.1d, "row2v");
283                     assertEquals(au.getScale().toStandardUnit(testData[2][col]), row2scalar[col].si, 0.1d, "row2scalar");
284                 }
285                 for (int row = 0; row < testData.length; row++)
286                 {
287                     assertEquals(au.getScale().toStandardUnit(testData[row][2]), col2si[row], 0.1d, "col2si");
288                     assertEquals(au.getScale().toStandardUnit(testData[row][2]), col2v.getSI(row), 0.1d, "col2v");
289                     assertEquals(au.getScale().toStandardUnit(testData[row][2]), col2scalar[row].si, 0.1d, "col2scalar");
290                 }
291                 for (int diag = 0; diag < amSquare.rows(); diag++)
292                 {
293                     assertEquals(au.getScale().toStandardUnit(squareData[diag][diag]), diagsi[diag], 0.1d, "diag2si");
294                     assertEquals(au.getScale().toStandardUnit(squareData[diag][diag]), diagv.getSI(diag), 0.1d, "diag2v");
295                     assertEquals(au.getScale().toStandardUnit(squareData[diag][diag]), diagscalar[diag].si, 0.1d,
296                             "diag2scalar");
297                 }
298 
299                 // GETVALUES(), GETSCALARS()
300                 double[][] valsi = am.getValuesSI();
301                 double[][] valunit = am.getValuesInUnit();
302                 double[][] valsqft = am.getValuesInUnit(AreaUnit.SQUARE_YARD);
303                 Area[][] valscalars = am.getScalars();
304                 for (int row = 0; row < testData.length; row++)
305                 {
306                     for (int col = 0; col < testData[0].length; col++)
307                     {
308                         assertEquals(au.getScale().toStandardUnit(testData[row][col]), valsi[row][col], 0.1, "getValuesSI()");
309                         assertEquals(testData[row][col], valunit[row][col], 0.1, "getValuesInUnit()");
310                         assertEquals(
311                                 AreaUnit.SQUARE_YARD.getScale()
312                                         .fromStandardUnit(au.getScale().toStandardUnit(testData[row][col])),
313                                 valsqft[row][col], 0.1, "getValuesInUnit(unit)");
314                         assertEquals(au.getScale().toStandardUnit(testData[row][col]), valscalars[row][col].si, 0.1,
315                                 "getValuesInUnit(unit)");
316                     }
317                 }
318 
319                 // ASSIGN FUNCTION ABS, CEIL, FLOOR, NEG, RINT
320                 AreaMatrix amdiv2 = am.divide(2.0d);
321                 assertEquals(am.getStorageType(), amdiv2.getStorageType());
322                 assertEquals(am.getDisplayUnit(), amdiv2.getDisplayUnit());
323                 AreaMatrix amAbs = amdiv2.mutable().abs().immutable();
324                 assertEquals(am.getStorageType(), amAbs.getStorageType());
325                 assertEquals(am.getDisplayUnit(), amAbs.getDisplayUnit());
326                 AreaMatrix amCeil = amdiv2.mutable().ceil().immutable();
327                 assertEquals(am.getStorageType(), amCeil.getStorageType());
328                 assertEquals(am.getDisplayUnit(), amCeil.getDisplayUnit());
329                 AreaMatrix amFloor = amdiv2.mutable().floor().immutable();
330                 assertEquals(am.getStorageType(), amFloor.getStorageType());
331                 assertEquals(am.getDisplayUnit(), amFloor.getDisplayUnit());
332                 AreaMatrix amNeg = amdiv2.mutable().neg().immutable();
333                 assertEquals(am.getStorageType(), amNeg.getStorageType());
334                 assertEquals(am.getDisplayUnit(), amNeg.getDisplayUnit());
335                 AreaMatrix amRint = amdiv2.mutable().rint().immutable();
336                 assertEquals(am.getStorageType(), amRint.getStorageType());
337                 assertEquals(am.getDisplayUnit(), amRint.getDisplayUnit());
338                 for (int row = 0; row < testData.length; row++)
339                 {
340                     for (int col = 0; col < testData[0].length; col++)
341                     {
342                         // TODO: Should be rounded IN THE UNIT rather than BY SI VALUES
343                         assertEquals(au.getScale().toStandardUnit(testData[row][col]) / 2.0d, amdiv2.getSI(row, col), 0.1d,
344                                 "div2");
345                         assertEquals(Math.abs(au.getScale().toStandardUnit(testData[row][col]) / 2.0d), amAbs.getSI(row, col),
346                                 0.1d, "abs");
347                         assertEquals(Math.ceil(au.getScale().toStandardUnit(testData[row][col]) / 2.0d), amCeil.getSI(row, col),
348                                 0.1d, "ceil");
349                         assertEquals(Math.floor(au.getScale().toStandardUnit(testData[row][col]) / 2.0d),
350                                 amFloor.getSI(row, col), 0.1d, "floor");
351                         assertEquals(-au.getScale().toStandardUnit(testData[row][col]) / 2.0d, amNeg.getSI(row, col), 0.1d,
352                                 "neg");
353                         assertEquals(Math.rint(au.getScale().toStandardUnit(testData[row][col]) / 2.0d), amRint.getSI(row, col),
354                                 0.1d, "rint");
355                     }
356                 }
357 
358                 double[][] testData4x4 = new double[][] {{2, 3, 5, 7}, {11, 13, 17, 19}, {23, 29, 31, 37}, {41, 43, 47, 49}};
359                 AreaMatrix am4x4 = new AreaMatrix(testData4x4, au, storageType);
360                 double det = am4x4.determinantSI();
361                 double detCalc = Determinant.det(am4x4.getValuesSI());
362                 double err = Math.max(det, detCalc) / 10000.0;
363                 assertEquals(detCalc, det, err, "Determinant of square matrix with unit " + au.getDefaultTextualAbbreviation()
364                         + ", storage = " + storageType + " = " + det + " but should have been " + detCalc);
365                 Try.testFail(() -> am.determinantSI(), "Determinant of non-square matrix should have thrown exception");
366 
367                 // TEST METHODS THAT INVOLVE TWO MATRIX INSTANCES
368 
369                 for (StorageType storageType2 : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
370                 {
371                     double[][] testData2 = storageType2.equals(StorageType.DENSE) ? denseTestData : reverseSparseTestData;
372                     for (AreaUnit au2 : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
373                     {
374 
375                         // PLUS and INCREMENTBY(MATRIX)
376                         AreaMatrix am2 = new AreaMatrix(testData2, au2, storageType2);
377                         AreaMatrix amSum1 = am.plus(am2);
378                         AreaMatrix amSum2 = am2.plus(am);
379                         AreaMatrix amSum3 = am.mutable().incrementBy(am2).immutable();
380                         AreaMatrix amSum4 = am2.mutable().incrementBy(am).immutable();
381                         assertEquals(amSum1, amSum2, "a+b == b+a");
382                         assertEquals(amSum1, amSum3, "a+b == b+a");
383                         assertEquals(amSum1, amSum4, "a+b == b+a");
384                         for (int row = 0; row < testData.length; row++)
385                         {
386                             for (int col = 0; col < testData[0].length; col++)
387                             {
388                                 double tolerance = Double.isFinite(amSum1.getSI(row, col))
389                                         ? Math.abs(amSum1.getSI(row, col) / 10000.0d) : 0.1d;
390                                 assertEquals(
391                                         au.getScale().toStandardUnit(testData[row][col])
392                                                 + au2.getScale().toStandardUnit(testData2[row][col]),
393                                         amSum1.getSI(row, col), tolerance, "value in matrix matches");
394                             }
395                         }
396 
397                         // MINUS and DECREMENTBY(MATRIX)
398                         AreaMatrix amDiff1 = am.minus(am2);
399                         AreaMatrix amDiff2 = am2.minus(am).mutable().neg();
400                         AreaMatrix amDiff3 = am.mutable().decrementBy(am2).immutable();
401                         assertEquals(amDiff1, amDiff2, "a-b == -(b-a)");
402                         assertEquals(amDiff1, amDiff3, "a-b == -(b-a)");
403                         for (int row = 0; row < testData.length; row++)
404                         {
405                             for (int col = 0; col < testData[0].length; col++)
406                             {
407                                 double tolerance = Double.isFinite(amDiff1.getSI(row, col))
408                                         ? Math.abs(amDiff1.getSI(row, col) / 10000.0d) : 0.1d;
409                                 assertEquals(
410                                         au.getScale().toStandardUnit(testData[row][col])
411                                                 - au2.getScale().toStandardUnit(testData2[row][col]),
412                                         amDiff1.getSI(row, col), tolerance, "value in matrix matches");
413                             }
414                         }
415 
416                         // TIMES(MATRIX) and DIVIDE(MATRIX)
417                         SIMatrix amTim = am.times(am2);
418                         SIMatrix amDiv = am.divide(am2);
419                         assertEquals("m4", amTim.getDisplayUnit().getQuantity().getSiDimensions().toString(false, false, false),
420                                 "unit of m2 * m2 should be m4");
421                         assertEquals("", amDiv.getDisplayUnit().getQuantity().getSiDimensions().toString(false, false, false),
422                                 "unit of m2 / m2 should be empty string");
423                         for (int row = 0; row < testData.length; row++)
424                         {
425                             for (int col = 0; col < testData[0].length; col++)
426                             {
427                                 double tolerance = Double.isFinite(amTim.getSI(row, col))
428                                         ? Math.abs(amTim.getSI(row, col) / 10000.0d) : 0.1d;
429                                 if (Math.abs(au.getScale().toStandardUnit(testData[row][col])
430                                         * au2.getScale().toStandardUnit(testData2[row][col])
431                                         - amTim.getSI(row, col)) > tolerance)
432                                 {
433                                     // system.out.println(
434                                     // "mismatch at row " + row + ", col " + col + ", got " + amTim.getSI(row, col)
435                                     // + ", expected " + au.getScale().toStandardUnit(testData[row][col])
436                                     // * au2.getScale().toStandardUnit(testData2[row][col]));
437                                     am.times(am2);
438                                 }
439                                 assertEquals(
440                                         au.getScale().toStandardUnit(testData[row][col])
441                                                 * au2.getScale().toStandardUnit(testData2[row][col]),
442                                         amTim.getSI(row, col), tolerance, "value in m2 * m2 matches");
443                                 tolerance = Double.isFinite(amDiv.getSI(row, col)) ? Math.abs(amDiv.getSI(row, col) / 10000.0d)
444                                         : 0.1d;
445                                 assertEquals(
446                                         au.getScale().toStandardUnit(testData[row][col])
447                                                 / au2.getScale().toStandardUnit(testData2[row][col]),
448                                         amDiv.getSI(row, col), tolerance, "value in m2 / m2 matches (could be NaN)");
449                             }
450                         }
451                     }
452                 }
453             }
454         }
455     }
456 
457     /**
458      * Test if mutable methods give an error in case the matrix is immutable.
459      */
460     @Test
461     public void testImmutableMatrix()
462     {
463         double[][] denseTestData = DOUBLEMATRIX.denseRectArrays(5, 10, false);
464         double[][] sparseTestData = DOUBLEMATRIX.sparseRectArrays(5, 10, false);
465 
466         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
467         {
468             for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
469             {
470                 double[][] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
471                 AreaMatrix am = new AreaMatrix(testData, au, storageType);
472                 am = am.immutable();
473                 final AreaMatrix amPtr = am;
474                 Area fa = Area.of(10.0d, "m^2");
475                 Try.testFail(() -> amPtr.assign(DoubleMathFunctions.ABS), "ImmutableMatrix.assign(...) should throw error");
476                 Try.testFail(() -> amPtr.decrementBy(fa), "ImmutableMatrix.decrementBy(scalar) should throw error");
477                 Try.testFail(() -> amPtr.decrementBy(amPtr), "ImmutableMatrix.decrementBy(matrix) should throw error");
478                 Try.testFail(() -> amPtr.incrementBy(fa), "ImmutableMatrix.incrementBy(scalar) should throw error");
479                 Try.testFail(() -> amPtr.incrementBy(amPtr), "ImmutableMatrix.incrementBy(matrix) should throw error");
480                 Try.testFail(() -> amPtr.divideBy(2.0d), "ImmutableMatrix.divideBy(factor) should throw error");
481                 Try.testFail(() -> amPtr.multiplyBy(2.0d), "ImmutableMatrix.multiplyBy(factor) should throw error");
482                 Try.testFail(() -> amPtr.set(1, 1, fa), "ImmutableMatrix.set() should throw error");
483                 Try.testFail(() -> amPtr.setSI(1, 1, 20.1d), "ImmutableMatrix.setSI() should throw error");
484                 Try.testFail(() -> amPtr.setInUnit(1, 1, 15.2d), "ImmutableMatrix.setInUnit(f) should throw error");
485                 Try.testFail(() -> amPtr.setInUnit(1, 1, 15.2d, AreaUnit.ARE),
486                         "ImmutableMatrix.setInUnit(f, u) should throw error");
487                 Try.testFail(() -> amPtr.abs(), "ImmutableMatrix.abs() should throw error");
488                 Try.testFail(() -> amPtr.ceil(), "ImmutableMatrix.ceil() should throw error");
489                 Try.testFail(() -> amPtr.floor(), "ImmutableMatrix.floor() should throw error");
490                 Try.testFail(() -> amPtr.neg(), "ImmutableMatrix.neg() should throw error");
491                 Try.testFail(() -> amPtr.rint(), "ImmutableMatrix.rint() should throw error");
492             }
493         }
494     }
495 
496     /**
497      * Test toString() methods. TODO: expand?
498      */
499     @Test
500     public void testMatrixToString()
501     {
502         double[][] denseTestData = DOUBLEMATRIX.denseRectArrays(5, 10, false);
503         double[][] sparseTestData = DOUBLEMATRIX.sparseRectArrays(5, 10, false);
504 
505         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
506         {
507             for (AreaUnit au : new AreaUnit[] {AreaUnit.SQUARE_METER, AreaUnit.ACRE})
508             {
509                 double[][] testData = storageType.equals(StorageType.DENSE) ? denseTestData : sparseTestData;
510                 AreaMatrix am = new AreaMatrix(testData, au, storageType);
511                 String s1 = am.toString(); // non-verbose with unit
512                 assertTrue(s1.contains(au.getDefaultTextualAbbreviation()));
513                 String s2 = am.toString(AreaUnit.SQUARE_INCH); // non-verbose with unit
514                 assertTrue(s2.contains(AreaUnit.SQUARE_INCH.getDefaultTextualAbbreviation()));
515                 String s3 = am.toString(AreaUnit.SQUARE_INCH, true, true); // verbose with unit
516                 assertTrue(s3.contains(AreaUnit.SQUARE_INCH.getDefaultTextualAbbreviation()));
517                 if (storageType.equals(StorageType.DENSE))
518                 {
519                     assertTrue(s3.contains("Dense"));
520                     assertFalse(s3.contains("Sparse"));
521                 }
522                 else
523                 {
524                     assertFalse(s3.contains("Dense"));
525                     assertTrue(s3.contains("Sparse"));
526                 }
527                 assertTrue(s3.contains("Rel"));
528                 assertFalse(s3.contains("Abs"));
529                 assertTrue(s3.contains("Immutable"));
530                 assertFalse(s3.contains("Mutable"));
531                 AreaMatrix ammut = am.mutable();
532                 String smut = ammut.toString(AreaUnit.SQUARE_INCH, true, true); // verbose with unit
533                 assertFalse(smut.contains("Immutable"));
534                 assertTrue(smut.contains("Mutable"));
535                 String sNotVerbose = ammut.toString(false, false);
536                 assertFalse(sNotVerbose.contains("Rel"));
537                 assertFalse(sNotVerbose.contains("Abs"));
538                 assertFalse(sNotVerbose.contains("Immutable"));
539                 assertFalse(sNotVerbose.contains("Mutable"));
540                 assertFalse(sNotVerbose.contains(au.getDefaultTextualAbbreviation()));
541             }
542         }
543         TimeMatrix tm = new TimeMatrix(denseTestData, TimeUnit.DEFAULT, StorageType.DENSE);
544         String st = tm.toString(TimeUnit.DEFAULT, true, true); // verbose with unit
545         assertFalse(st.contains("Rel"));
546         assertTrue(st.contains("Abs"));
547         LengthMatrix lm = new LengthMatrix(denseTestData, LengthUnit.SI, StorageType.DENSE);
548         String sl = lm.toString(LengthUnit.SI, true, true); // verbose with unit
549         assertTrue(sl.contains("Rel"));
550         assertFalse(sl.contains("Abs"));
551     }
552 
553     /**
554      * Test the extra methods that Absolute and Relative with Absolute matrices implement.
555      */
556     @Test
557     public void testSpecialMatrixMethodsRelWithAbs()
558     {
559         double[][] denseTestData = DOUBLEMATRIX.denseRectArrays(5, 10, false);
560         TimeMatrix tm = new TimeMatrix(denseTestData, TimeUnit.DEFAULT, StorageType.DENSE);
561         DurationMatrix dm = new DurationMatrix(denseTestData, DurationUnit.MINUTE, StorageType.DENSE);
562         assertTrue(tm.isAbsolute());
563         assertFalse(dm.isAbsolute());
564         assertFalse(tm.isRelative());
565         assertTrue(dm.isRelative());
566 
567         TimeMatrix absPlusRel = tm.plus(dm);
568         TimeMatrix absMinusRel = tm.minus(dm);
569         double[][] halfDenseData = DOUBLEMATRIX.denseRectArrays(5, 10, false);
570         for (int row = 0; row < halfDenseData.length; row++)
571         {
572             for (int col = 0; col < halfDenseData[row].length; col++)
573             {
574                 halfDenseData[row][col] *= 0.5;
575             }
576         }
577         TimeMatrix halfTimeMatrix = new TimeMatrix(halfDenseData, TimeUnit.DEFAULT, StorageType.DENSE);
578         DurationMatrix absMinusAbs = tm.minus(halfTimeMatrix);
579         TimeMatrix absDecByRelS = tm.mutable().decrementBy(Duration.of(1.0d, "min"));
580         TimeMatrix absDecByRelM = tm.mutable().decrementBy(dm.divide(2.0d));
581         TimeMatrix relPlusAbs = dm.plus(tm);
582         for (int row = 0; row < denseTestData.length; row++)
583         {
584             for (int col = 0; col < denseTestData[0].length; col++)
585             {
586                 assertEquals(61.0 * denseTestData[row][col], absPlusRel.getSI(row, col), 0.01, "absPlusRel");
587                 assertEquals(-59.0 * denseTestData[row][col], absMinusRel.getSI(row, col), 0.01, "absMinusRel");
588                 assertEquals(denseTestData[row][col] / 2.0, absMinusAbs.getSI(row, col), 0.01, "absMinusAbs");
589                 assertEquals(denseTestData[row][col] - 60.0, absDecByRelS.getSI(row, col), 0.01, "absDecByRelS");
590                 assertEquals(-29.0 * denseTestData[row][col], absDecByRelM.getSI(row, col), 0.01, "absDecByRelM");
591                 assertEquals(61.0 * denseTestData[row][col], relPlusAbs.getSI(row, col), 0.01, "relPlusAbs");
592             }
593         }
594         for (int dRows : new int[] {-1, 0, 1})
595         {
596             for (int dCols : new int[] {-1, 0, 1})
597             {
598                 if (dRows == 0 && dCols == 0)
599                 {
600                     continue;
601                 }
602                 double[][] other =
603                         DOUBLEMATRIX.denseRectArrays(denseTestData.length + dRows, denseTestData[0].length + dCols, false);
604                 TimeMatrix wrongTimeMatrix = new TimeMatrix(other, TimeUnit.DEFAULT, StorageType.DENSE);
605                 try
606                 {
607                     tm.mutable().minus(wrongTimeMatrix);
608                     fail("Mismatching size should have thrown a ValueRuntimeException");
609                 }
610                 catch (ValueRuntimeException vre)
611                 {
612                     // Ignore expected exception
613                 }
614             }
615         }
616         assertTrue(DoubleMatrixData.instantiate(denseTestData, TimeUnit.DEFAULT.getScale(), StorageType.DENSE).toString()
617                 .startsWith("DoubleMatrixData"), "toString returns something informative");
618     }
619 
620     /**
621      * Execute a mats++-like memory test on a sparse matrix. See
622      * <a href="http://www.eng.auburn.edu/~agrawvd/COURSE/E7250_05/REPORTS_TERM/Raghuraman_Mem.doc">Memory Test</a>.
623      */
624     @Test
625     public void memoryTest()
626     {
627         FloatAreaMatrix am = new FloatAreaMatrix(new float[5][10], AreaUnit.SI, StorageType.SPARSE);
628         am = am.mutable();
629         float nonZeroValue = 123.456f;
630         // Initially the array is filled with zero values; we can skip the initialization phase
631         for (int compoundIndex = 0; compoundIndex < am.cols() * am.rows(); compoundIndex++)
632         {
633             // Let the row count fastest
634             int row = compoundIndex % am.rows();
635             int col = compoundIndex / am.rows();
636             assertEquals(0f, am.getSI(row, col), 0.0001, "initial value is 0");
637             am.setSI(row, col, nonZeroValue);
638             assertEquals(nonZeroValue, am.getSI(row, col), 0.0001, "current value is nonZero");
639         }
640         for (int compoundIndex = am.cols() * am.rows(); --compoundIndex >= 0;)
641         {
642             // Let the row count fastest
643             int row = compoundIndex % am.rows();
644             int col = compoundIndex / am.rows();
645             assertEquals(nonZeroValue, am.getSI(row, col), 0.0001, "current value is nonZero");
646             am.setSI(row, col, 0f);
647             assertEquals(0f, am.getSI(row, col), 0.0001, "final value is 0");
648         }
649     }
650 
651     /**
652      * Test the instantiateAbs method and instantiateScalarAbsSI method.
653      */
654     @Test
655     public void testInstantiateAbs()
656     {
657         double[][] denseTestData = DOUBLEMATRIX.denseRectArrays(10, 20, false);
658         TimeMatrix timeMatrix = new TimeMatrix(denseTestData, TimeUnit.DEFAULT, StorageType.DENSE);
659         DurationMatrix durationMatrix = new DurationMatrix(denseTestData, DurationUnit.MINUTE, StorageType.DENSE);
660 
661         float[] halfDenseData = FLOATVECTOR.denseArray(105);
662         for (int index = 0; index < halfDenseData.length; index++)
663         {
664             halfDenseData[index] *= 0.5;
665         }
666         TimeMatrix relPlusAbsTime = durationMatrix.plus(timeMatrix);
667         for (int row = 0; row < denseTestData.length; row++)
668         {
669             for (int col = 0; col < denseTestData[0].length; col++)
670             {
671                 assertEquals(61.0 * denseTestData[row][col], relPlusAbsTime.getSI(row, col), 0.01, "relPlusAbsTime");
672             }
673         }
674         Time time = durationMatrix.instantiateScalarAbsSI(123.456f, TimeUnit.EPOCH_DAY);
675         assertEquals(TimeUnit.EPOCH_DAY, time.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
676         assertEquals(123.456f, time.si, 0.1, "Value of instantiateScalarAbsSI matches");
677 
678         AngleMatrix angleMatrix = new AngleMatrix(denseTestData, AngleUnit.DEGREE, StorageType.DENSE);
679         DirectionMatrix directionMatrix = new DirectionMatrix(denseTestData, DirectionUnit.EAST_DEGREE, StorageType.DENSE);
680 
681         DirectionMatrix relPlusAbsDirection = angleMatrix.plus(directionMatrix);
682         for (int row = 0; row < denseTestData.length; row++)
683         {
684             for (int col = 0; col < denseTestData[0].length; col++)
685             {
686                 assertEquals(2.0 / 180 * Math.PI * denseTestData[row][col], relPlusAbsDirection.getSI(row, col), 0.01,
687                         "relPlusAbsTime");
688             }
689         }
690         Direction direction = angleMatrix.instantiateScalarAbsSI(123.456f, DirectionUnit.NORTH_RADIAN);
691         assertEquals(DirectionUnit.NORTH_RADIAN, direction.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
692         assertEquals(123.456f, direction.si, 0.1, "Value of instantiateScalarAbsSI matches");
693 
694         TemperatureMatrix temperatureMatrix =
695                 new TemperatureMatrix(denseTestData, TemperatureUnit.DEGREE_FAHRENHEIT, StorageType.DENSE);
696         AbsoluteTemperatureMatrix absoluteTemperatureMatrix =
697                 new AbsoluteTemperatureMatrix(denseTestData, AbsoluteTemperatureUnit.KELVIN, StorageType.DENSE);
698 
699         AbsoluteTemperatureMatrix relPlusAbsTemperature = temperatureMatrix.plus(absoluteTemperatureMatrix);
700         for (int row = 0; row < denseTestData.length; row++)
701         {
702             for (int col = 0; col < denseTestData[0].length; col++)
703             {
704                 assertEquals((1.0 + 5.0 / 9.0) * denseTestData[row][col], relPlusAbsTemperature.getSI(row, col), 0.01,
705                         "relPlusAbsTime");
706             }
707         }
708         AbsoluteTemperature absoluteTemperature =
709                 temperatureMatrix.instantiateScalarAbsSI(123.456f, AbsoluteTemperatureUnit.DEGREE_FAHRENHEIT);
710         assertEquals(AbsoluteTemperatureUnit.DEGREE_FAHRENHEIT, absoluteTemperature.getDisplayUnit(),
711                 "Unit of instantiateScalarAbsSI matches");
712         assertEquals(123.456f, absoluteTemperature.si, 0.1, "Value of instantiateScalarAbsSI matches");
713 
714         LengthMatrix lengthMatrix = new LengthMatrix(denseTestData, LengthUnit.MILE, StorageType.DENSE);
715         PositionMatrix positionMatrix = new PositionMatrix(denseTestData, PositionUnit.KILOMETER, StorageType.DENSE);
716 
717         PositionMatrix relPlusAbsPosition = lengthMatrix.plus(positionMatrix);
718         for (int row = 0; row < denseTestData.length; row++)
719         {
720             for (int col = 0; col < denseTestData[0].length; col++)
721             {
722                 assertEquals(2609.344 * denseTestData[row][col], relPlusAbsPosition.getSI(row, col), 0.1, "relPlusAbsTime");
723             }
724         }
725         Position position = lengthMatrix.instantiateScalarAbsSI(123.456f, PositionUnit.ANGSTROM);
726         assertEquals(PositionUnit.ANGSTROM, position.getDisplayUnit(), "Unit of instantiateScalarAbsSI matches");
727         assertEquals(123.456f, position.si, 0.01, "Value of instantiateScalarAbsSI matches");
728     }
729 
730     /**
731      * Test the equals method.
732      */
733     @SuppressWarnings("unlikely-arg-type")
734     @Test
735     public void testEquals()
736     {
737         double[][] testData = DOUBLEMATRIX.denseRectArrays(12, 34, false);
738         testData[2][2] = 0;
739         for (StorageType storageType : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
740         {
741             DoubleMatrixData dmd = DoubleMatrixData.instantiate(testData, TemperatureUnit.KELVIN.getScale(), storageType);
742             assertTrue(dmd.equals(dmd), "Double matrix is equal to itself");
743             assertFalse(dmd.equals(null), "Double matrix is not equal to null");
744             assertFalse(dmd.equals("some string"), "Double matrix data is not equal to some string");
745             assertTrue(dmd.equals(dmd.toSparse()), "Double matrix is equal to sparse version of itself");
746             assertTrue(dmd.equals(dmd.toDense()), "Double matrix is equal to dense version of itself");
747             for (StorageType storageType2 : new StorageType[] {StorageType.DENSE, StorageType.SPARSE})
748             {
749                 DoubleMatrixData dvd2 = DoubleMatrixData.instantiate(testData, TemperatureUnit.KELVIN.getScale(), storageType2);
750                 assertEquals(dmd, dvd2,
751                         "Double matrix data is equal to other double vector containing same values regardless of storage type");
752                 double[][] testData2 = DOUBLEMATRIX.denseRectArrays(12, 33, false);
753                 testData2[2][2] = 0;
754                 dvd2 = DoubleMatrixData.instantiate(testData2, TemperatureUnit.KELVIN.getScale(), storageType2);
755                 assertFalse(dmd.equals(dvd2),
756                         "Double matrix data is not equal to other double vector containing same values except last one");
757                 testData2 = DOUBLEMATRIX.denseRectArrays(13, 34, false);
758                 testData2[2][2] = 0;
759                 dvd2 = DoubleMatrixData.instantiate(testData2, TemperatureUnit.KELVIN.getScale(), storageType2);
760                 assertFalse(dmd.equals(dvd2),
761                         "Double matrix data is not equal to other double vector containing same values except last one");
762                 testData2 = DOUBLEMATRIX.denseRectArrays(12, 34, false);
763                 dvd2 = DoubleMatrixData.instantiate(testData2, TemperatureUnit.KELVIN.getScale(), storageType2);
764                 assertFalse(dmd.equals(dvd2),
765                         "Double matrix data is not equal to other double vector containing same values except for one zero");
766             }
767         }
768     }
769 
770     /**
771      * Test the sparse value class.
772      */
773     @SuppressWarnings({"rawtypes", "unchecked"})
774     @Test
775     public void sparseValueTest()
776     {
777         try
778         {
779             new DoubleSparseValue(-1, 0, 123.456);
780             fail("Negative row should have caused a ValueRuntimeException");
781         }
782         catch (ValueRuntimeException vre)
783         {
784             // Ignore expected exception
785         }
786 
787         try
788         {
789             new DoubleSparseValue(0, -1, 123.456);
790             fail("Negative column should have caused a ValueRuntimeException");
791         }
792         catch (ValueRuntimeException vre)
793         {
794             // Ignore expected exception
795         }
796 
797         Length length = Length.valueOf("123 km");
798         DoubleSparseValue dsv = new DoubleSparseValue(2, 3, length);
799         assertEquals(2, dsv.getRow(), "row matches");
800         assertEquals(3, dsv.getColumn(), "column matches");
801         assertEquals(123000, dsv.getValueSI(), 0.1, "value matches");
802         dsv = new DoubleSparseValue(2, 3, 123.000, LengthUnit.KILOMETER);
803         assertEquals(2, dsv.getRow(), "row matches");
804         assertEquals(3, dsv.getColumn(), "column matches");
805         assertEquals(123000, dsv.getValueSI(), 0.1, "value matches");
806     }
807 
808 }