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