View Javadoc
1   package org.djunits.value.vfloat.matrix.base;
2   
3   import java.lang.reflect.Array;
4   
5   import org.djunits.unit.Unit;
6   import org.djunits.value.Absolute;
7   import org.djunits.value.AbstractIndexedValue;
8   import org.djunits.value.ValueRuntimeException;
9   import org.djunits.value.formatter.Format;
10  import org.djunits.value.storage.StorageType;
11  import org.djunits.value.util.ValueUtil;
12  import org.djunits.value.vfloat.function.FloatFunction;
13  import org.djunits.value.vfloat.function.FloatMathFunctions;
14  import org.djunits.value.vfloat.matrix.data.FloatMatrixData;
15  import org.djunits.value.vfloat.scalar.base.AbstractFloatScalar;
16  import org.djunits.value.vfloat.scalar.base.FloatScalar;
17  import org.djunits.value.vfloat.vector.base.AbstractFloatVector;
18  import org.djunits.value.vfloat.vector.data.FloatVectorData;
19  import org.djutils.exceptions.Throw;
20  
21  /**
22   * The most basic abstract class for the FloatMatrix.
23   * <p>
24   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
25   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
26   * </p>
27   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
29   * @param <U> the unit
30   * @param <S> the scalar with unit U
31   * @param <V> the vector type belonging to the matrix type
32   * @param <M> the generic matrix type
33   */
34  public abstract class AbstractFloatMatrix<U extends Unit<U>, S extends AbstractFloatScalar<U, S>,
35          V extends AbstractFloatVector<U, S, V>, M extends AbstractFloatMatrix<U, S, V, M>>
36          extends AbstractIndexedValue<U, S, M, FloatMatrixData> implements FloatMatrixInterface<U, S, V, M>
37  {
38      /** */
39      private static final long serialVersionUID = 20161015L;
40  
41      /** The stored data as an object, can be sparse or dense. */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      protected FloatMatrixData data;
44  
45      /**
46       * Construct a new FloatMatrix.
47       * @param data FloatMatrixData; an internal data object
48       * @param unit U; the unit
49       */
50      AbstractFloatMatrix(final FloatMatrixData data, final U unit)
51      {
52          super(unit);
53          Throw.whenNull(data, "data cannot be null");
54          this.data = data;
55      }
56  
57      /** {@inheritDoc} */
58      @Override
59      protected final FloatMatrixData getData()
60      {
61          return this.data;
62      }
63  
64      /** {@inheritDoc} */
65      @Override
66      protected void setData(final FloatMatrixData data)
67      {
68          this.data = data;
69      }
70  
71      /** {@inheritDoc} */
72      @Override
73      public float getSI(final int row, final int column) throws ValueRuntimeException
74      {
75          checkIndex(row, column);
76          return this.data.getSI(row, column);
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public float getInUnit(final int row, final int column) throws ValueRuntimeException
82      {
83          checkIndex(row, column);
84          return (float) ValueUtil.expressAsUnit(this.data.getSI(row, column), getDisplayUnit());
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public float getInUnit(final int row, final int column, final U targetUnit) throws ValueRuntimeException
90      {
91          checkIndex(row, column);
92          return (float) ValueUtil.expressAsUnit(this.data.getSI(row, column), targetUnit);
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public void setSI(final int row, final int column, final float valueSI) throws ValueRuntimeException
98      {
99          checkIndex(row, column);
100         checkCopyOnWrite();
101         this.data.setSI(row, column, valueSI);
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public void setInUnit(final int row, final int column, final float valueInUnit) throws ValueRuntimeException
107     {
108         setSI(row, column, (float) ValueUtil.expressAsSIUnit(valueInUnit, getDisplayUnit()));
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public void setInUnit(final int row, final int column, final float valueInUnit, final U valueUnit)
114             throws ValueRuntimeException
115     {
116         setSI(row, column, (float) ValueUtil.expressAsSIUnit(valueInUnit, valueUnit));
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public void set(final int row, final int column, final S value) throws ValueRuntimeException
122     {
123         setSI(row, column, value.si);
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public float[] getRowSI(final int row) throws ValueRuntimeException
129     {
130         checkRowIndex(row);
131         float[] result = new float[this.data.cols()];
132         for (int col = 0; col < result.length; col++)
133         {
134             result[col] = this.data.getSI(row, col);
135         }
136         return result;
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public float[] getColumnSI(final int column) throws ValueRuntimeException
142     {
143         checkColumnIndex(column);
144         float[] result = new float[this.data.rows()];
145         for (int row = 0; row < result.length; row++)
146         {
147             result[row] = this.data.getSI(row, column);
148         }
149         return result;
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public float[] getDiagonalSI() throws ValueRuntimeException
155     {
156         checkSquare();
157         float[] result = new float[this.data.rows()];
158         for (int row = 0; row < result.length; row++)
159         {
160             result[row] = this.data.getSI(row, row);
161         }
162         return result;
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public final float[][] getValuesSI()
168     {
169         return this.data.getDenseMatrixSI();
170     }
171 
172     /** {@inheritDoc} */
173     @Override
174     public final float[][] getValuesInUnit()
175     {
176         return getValuesInUnit(getDisplayUnit());
177     }
178 
179     /** {@inheritDoc} */
180     @Override
181     public final float[][] getValuesInUnit(final U targetUnit)
182     {
183         float[][] values = getValuesSI();
184         for (int i = values.length; --i >= 0;)
185         {
186             for (int j = values[i].length; --j >= 0;)
187             {
188                 values[i][j] = (float) ValueUtil.expressAsUnit(values[i][j], targetUnit);
189             }
190         }
191         return values;
192     }
193 
194     /** {@inheritDoc} */
195     @Override
196     public int rows()
197     {
198         return this.data.rows();
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public int cols()
204     {
205         return this.data.cols();
206     }
207 
208     /** {@inheritDoc} */
209     @SuppressWarnings("unchecked")
210     @Override
211     public S[][] getScalars()
212     {
213         S[][] array = (S[][]) Array.newInstance(getScalarClass(), rows(), cols());
214         for (int i = 0; i < rows(); i++)
215         {
216             S[] row = (S[]) Array.newInstance(getScalarClass(), cols());
217             array[i] = row;
218             for (int j = 0; j < cols(); j++)
219             {
220                 row[j] = get(i, j);
221             }
222         }
223         return array;
224     }
225 
226     /** {@inheritDoc} */
227     @Override
228     public S get(final int row, final int column) throws ValueRuntimeException
229     {
230         checkIndex(row, column);
231         return FloatScalar.instantiateSI(getSI(row, column), getDisplayUnit());
232     }
233 
234     /** {@inheritDoc} */
235     @Override
236     public V getRow(final int row) throws ValueRuntimeException
237     {
238         checkRowIndex(row);
239         FloatVectorData dvd =
240                 FloatVectorData.instantiate(getRowSI(row), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
241         return instantiateVector(dvd, getDisplayUnit());
242     }
243 
244     /** {@inheritDoc} */
245     @Override
246     public V getColumn(final int column) throws ValueRuntimeException
247     {
248         checkColumnIndex(column);
249         FloatVectorData dvd = FloatVectorData.instantiate(getColumnSI(column), getDisplayUnit().getStandardUnit().getScale(),
250                 getStorageType());
251         return instantiateVector(dvd, getDisplayUnit());
252     }
253 
254     /** {@inheritDoc} */
255     @Override
256     public V getDiagonal() throws ValueRuntimeException
257     {
258         checkSquare();
259         FloatVectorData dvd =
260                 FloatVectorData.instantiate(getDiagonalSI(), getDisplayUnit().getStandardUnit().getScale(), getStorageType());
261         return instantiateVector(dvd, getDisplayUnit());
262 
263     }
264 
265     /** {@inheritDoc} */
266     @SuppressWarnings("unchecked")
267     @Override
268     public S[] getRowScalars(final int row) throws ValueRuntimeException
269     {
270         checkRowIndex(row);
271         S[] array = (S[]) Array.newInstance(getScalarClass(), cols());
272         for (int col = 0; col < cols(); col++)
273         {
274             array[col] = get(row, col);
275         }
276         return array;
277     }
278 
279     /** {@inheritDoc} */
280     @SuppressWarnings("unchecked")
281     @Override
282     public S[] getColumnScalars(final int col) throws ValueRuntimeException
283     {
284         checkColumnIndex(col);
285         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
286         for (int row = 0; row < rows(); row++)
287         {
288             array[row] = get(row, col);
289         }
290         return array;
291     }
292 
293     /** {@inheritDoc} */
294     @SuppressWarnings("unchecked")
295     @Override
296     public S[] getDiagonalScalars() throws ValueRuntimeException
297     {
298         checkSquare();
299         S[] array = (S[]) Array.newInstance(getScalarClass(), rows());
300         for (int row = 0; row < rows(); row++)
301         {
302             array[row] = get(row, row);
303         }
304         return array;
305     }
306 
307     /** {@inheritDoc} */
308     @SuppressWarnings("unchecked")
309     @Override
310     public M toSparse()
311     {
312         M result;
313         if (getStorageType().equals(StorageType.SPARSE))
314         {
315             result = (M) this;
316             result.setDisplayUnit(getDisplayUnit());
317         }
318         else
319         {
320             result = instantiateMatrix(this.data.toSparse(), getDisplayUnit());
321         }
322         result.setDisplayUnit(getDisplayUnit());
323         return result;
324     }
325 
326     /** {@inheritDoc} */
327     @SuppressWarnings("unchecked")
328     @Override
329     public M toDense()
330     {
331         M result;
332         if (getStorageType().equals(StorageType.DENSE))
333         {
334             result = (M) this;
335             result.setDisplayUnit(getDisplayUnit());
336         }
337         else
338         {
339             result = instantiateMatrix(this.data.toDense(), getDisplayUnit());
340         }
341         return result;
342     }
343 
344     /** {@inheritDoc} */
345     @SuppressWarnings("unchecked")
346     @Override
347     public final M assign(final FloatFunction floatFunction)
348     {
349         checkCopyOnWrite();
350         this.data.assign(floatFunction);
351         return (M) this;
352     }
353 
354     /** {@inheritDoc} */
355     @Override
356     public final M abs()
357     {
358         return assign(FloatMathFunctions.ABS);
359     }
360 
361     /** {@inheritDoc} */
362     @Override
363     public final M ceil()
364     {
365         return assign(FloatMathFunctions.CEIL);
366     }
367 
368     /** {@inheritDoc} */
369     @Override
370     public final M floor()
371     {
372         return assign(FloatMathFunctions.FLOOR);
373     }
374 
375     /** {@inheritDoc} */
376     @Override
377     public final M neg()
378     {
379         return assign(FloatMathFunctions.NEG);
380     }
381 
382     /** {@inheritDoc} */
383     @Override
384     public final M rint()
385     {
386         return assign(FloatMathFunctions.RINT);
387     }
388 
389     /** {@inheritDoc} */
390     @Override
391     public String toString()
392     {
393         return toString(getDisplayUnit(), false, true);
394     }
395 
396     /** {@inheritDoc} */
397     @Override
398     public String toString(final U displayUnit)
399     {
400         return toString(displayUnit, false, true);
401     }
402 
403     /** {@inheritDoc} */
404     @Override
405     public String toString(final boolean verbose, final boolean withUnit)
406     {
407         return toString(getDisplayUnit(), verbose, withUnit);
408     }
409 
410     /** {@inheritDoc} */
411     @Override
412     public String toString(final U displayUnit, final boolean verbose, final boolean withUnit)
413     {
414         StringBuffer buf = new StringBuffer();
415         if (verbose)
416         {
417             String ab = this instanceof Absolute ? "Abs " : "Rel ";
418             String ds = this.data.isDense() ? "Dense  " : this.data.isSparse() ? "Sparse " : "?????? ";
419             if (isMutable())
420             {
421                 buf.append("Mutable   " + ab + ds);
422             }
423             else
424             {
425                 buf.append("Immutable " + ab + ds);
426             }
427         }
428         for (int row = 0; row < rows(); row++)
429         {
430             buf.append("\r\n\t");
431             for (int col = 0; col < cols(); col++)
432             {
433                 try
434                 {
435                     float d = (float) ValueUtil.expressAsUnit(getSI(row, col), displayUnit);
436                     buf.append(" " + Format.format(d));
437                 }
438                 catch (ValueRuntimeException ve)
439                 {
440                     buf.append(" " + "********************".substring(0, Format.DEFAULTSIZE));
441                 }
442             }
443         }
444         buf.append("\n");
445         if (withUnit)
446         {
447             buf.append(displayUnit.getLocalizedDisplayAbbreviation());
448         }
449         return buf.toString();
450     }
451 
452     /**
453      * Check that provided row and column indices are valid.
454      * @param row int; the row value to check
455      * @param col int; the column value to check
456      * @throws ValueRuntimeException when row or column is invalid
457      */
458     protected final void checkIndex(final int row, final int col) throws ValueRuntimeException
459     {
460         if (row < 0 || row >= rows() || col < 0 || col >= cols())
461         {
462             throw new ValueRuntimeException("index out of range (valid range is 0.." + (rows() - 1) + ", 0.." + (cols() - 1)
463                     + ", got " + row + ", " + col + ")");
464         }
465     }
466 
467     /**
468      * Check that provided row index is valid.
469      * @param row int; the row value to check
470      * @throws ValueRuntimeException when row is invalid
471      */
472     protected final void checkRowIndex(final int row) throws ValueRuntimeException
473     {
474         if (row < 0 || row >= rows())
475         {
476             throw new ValueRuntimeException("row index out of range (valid range is 0.." + (rows() - 1) + ", got " + row + ")");
477         }
478     }
479 
480     /**
481      * Check that provided column index is valid.
482      * @param col int; the column value to check
483      * @throws ValueRuntimeException when row is invalid
484      */
485     protected final void checkColumnIndex(final int col) throws ValueRuntimeException
486     {
487         if (col < 0 || col >= cols())
488         {
489             throw new ValueRuntimeException(
490                     "column index out of range (valid range is 0.." + (cols() - 1) + ", got " + col + ")");
491         }
492     }
493 
494     /**
495      * Check that the matrix is square.
496      * @throws ValueRuntimeException when matrix is not square
497      */
498     protected final void checkSquare() throws ValueRuntimeException
499     {
500         Throw.when(rows() != cols(), ValueRuntimeException.class, "Matrix is not square, rows=%d, cols=%d", rows(), cols());
501     }
502 
503     /** {@inheritDoc} */
504     @Override
505     public final float determinantSI() throws ValueRuntimeException
506     {
507         checkSquare();
508         return det(getValuesSI());
509     }
510 
511     /**
512      * Calculate the determinant of an n x n matrix.
513      * @param mat the matrix
514      * @return the determinant using the co-factor formula
515      */
516     private static float det(final float[][] mat)
517     {
518         if (mat.length == 1)
519         {
520             return mat[0][0];
521         }
522         // det(A) = sum(j=1:n) (-1)^(i+j).a_ij.A_ij where A_ij is the matrix with row i and column j removed
523         float det = 0.0f;
524         // possible optimization: pick the row or column with most zeros; here: pick row 0
525         for (int col = 0; col < mat.length; col++)
526         {
527             float sgn = (col % 2 == 0) ? 1 : -1;
528             float aij = mat[0][col];
529             float[][] matAij = new float[mat.length - 1][];
530             int r = 0;
531             for (int row = 1; row < mat.length; row++)
532             {
533                 matAij[r] = new float[matAij.length];
534                 int c = 0;
535                 for (int j = 0; j < mat.length; j++)
536                 {
537                     if (j != col)
538                     {
539                         matAij[r][c++] = mat[row][j];
540                     }
541                 }
542                 r++;
543             }
544             det += sgn * aij * det(matAij);
545         }
546         return det;
547     }
548 
549     /** {@inheritDoc} */
550     @Override
551     @SuppressWarnings("checkstyle:designforextension")
552     public int hashCode()
553     {
554         final int prime = 31;
555         int result = getDisplayUnit().getStandardUnit().hashCode();
556         result = prime * result + ((this.data == null) ? 0 : this.data.hashCode());
557         return result;
558     }
559 
560     /** {@inheritDoc} */
561     @Override
562     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
563     public boolean equals(final Object obj)
564     {
565         if (this == obj)
566             return true;
567         if (obj == null)
568             return false;
569         if (getClass() != obj.getClass())
570             return false;
571         AbstractFloatMatrix<?, ?, ?, ?> other = (AbstractFloatMatrix<?, ?, ?, ?>) obj;
572         if (!getDisplayUnit().getStandardUnit().equals(other.getDisplayUnit().getStandardUnit()))
573             return false;
574         if (this.data == null)
575         {
576             if (other.data != null)
577                 return false;
578         }
579         else if (!this.data.equals(other.data))
580             return false;
581         return true;
582     }
583 }