View Javadoc
1   package org.djunits.vecmat.storage;
2   
3   import java.util.Arrays;
4   import java.util.Objects;
5   
6   import org.djunits.quantity.def.Quantity;
7   import org.djunits.unit.UnitInterface;
8   import org.djutils.exceptions.Throw;
9   
10  /**
11   * DenseDoubleData implements a dense data grid for N x M matrices or N x 1 or 1 x M vectors with double values.
12   * <p>
13   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
14   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
15   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
16   * @author Alexander Verbraeck
17   */
18  public class DenseDoubleDataSi implements DataGridSi<DenseDoubleDataSi>
19  {
20      /** The data stored in row-major format. */
21      private final double[] data;
22  
23      /** The number of rows. */
24      private final int rows;
25  
26      /** the number of columns. */
27      private final int cols;
28  
29      /**
30       * Instantiate a data object with one array in row-major format. Note that NO safe copy of the data is stored.
31       * @param data the data in row-major format
32       * @param rows the number of rows
33       * @param cols the number of columns
34       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols, or when the number of rows
35       *             or columns is not positive
36       */
37      public DenseDoubleDataSi(final double[] data, final int rows, final int cols)
38      {
39          Throw.whenNull(data, "data");
40          Throw.when(rows <= 0, IllegalArgumentException.class, "Number of rows <= 0");
41          Throw.when(cols <= 0, IllegalArgumentException.class, "Number of columns <= 0");
42          Throw.when(data.length != rows * cols, IllegalArgumentException.class,
43                  "Data object length != rows * cols, %d != %d * %d", data.length, rows, cols);
44          this.data = data;
45          this.rows = rows;
46          this.cols = cols;
47      }
48  
49      /**
50       * Instantiate a data object with a double[rows][cols]. A safe copy of the data is stored.
51       * @param data the data in row-major format
52       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
53       */
54      @SuppressWarnings("checkstyle:needbraces")
55      public DenseDoubleDataSi(final double[][] data)
56      {
57          Throw.whenNull(data, "data");
58          Throw.when(data.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
59          this.rows = data.length;
60          this.cols = data[0].length;
61          this.data = new double[this.rows * this.cols];
62          for (int r = 0; r < this.rows; r++)
63          {
64              Throw.when(data[r].length != this.cols, IllegalArgumentException.class,
65                      "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, data[r].length,
66                      this.cols);
67              for (int c = 0; c < this.cols; c++)
68                  this.data[r * this.cols + c] = data[r][c];
69          }
70      }
71  
72      /**
73       * Instantiate a data object with a Q[rows][cols]. A safe copy of the data is stored.
74       * @param data the data in row-major format
75       * @throws IllegalArgumentException when the size of the data object is not equal to rows*cols
76       * @param <Q> the quantity type
77       * @param <U> the unit type
78       */
79      @SuppressWarnings("checkstyle:needbraces")
80      public <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> DenseDoubleDataSi(final Q[][] data)
81      {
82          Throw.whenNull(data, "data");
83          Throw.when(data.length == 0, IllegalArgumentException.class, "Number of rows in the data matrix = 0");
84          this.rows = data.length;
85          this.cols = data[0].length;
86          this.data = new double[this.rows * this.cols];
87          for (int r = 0; r < this.rows; r++)
88          {
89              Throw.when(data[r].length != this.cols, IllegalArgumentException.class,
90                      "Number of columns in row %d (%d) is not equal to number of columns in row 0 (%d)", r, data[r].length,
91                      this.cols);
92              for (int c = 0; c < this.cols; c++)
93                  this.data[r * this.cols + c] = data[r][c].si();
94          }
95      }
96  
97      @Override
98      public int rows()
99      {
100         return this.rows;
101     }
102 
103     @Override
104     public int cols()
105     {
106         return this.cols;
107     }
108 
109     @Override
110     public boolean isDense()
111     {
112         return true;
113     }
114 
115     @Override
116     public boolean isDouble()
117     {
118         return true;
119     }
120 
121     /**
122      * Check whether the row and column are within bounds.
123      * @param row the row number
124      * @param col the column number
125      * @throws IndexOutOfBoundsException when row &gt; rows() or col &gt; cols() or row &lt; 0 or col &lt; 0
126      */
127     private void checkRowCol(final int row, final int col) throws IndexOutOfBoundsException
128     {
129         Throw.when(row < 0 || row >= this.rows, IndexOutOfBoundsException.class, "row %d not in range 0..%d", row, this.rows);
130         Throw.when(col < 0 || col >= this.cols, IndexOutOfBoundsException.class, "column %d not in range 0..%d", col,
131                 this.cols);
132     }
133 
134     @Override
135     public double get(final int row, final int col)
136     {
137         checkRowCol(row, col);
138         return this.data[row * this.cols + col];
139     }
140 
141     @Override
142     public double[] getDataArray()
143     {
144         return this.data;
145     }
146 
147     @Override
148     public DenseDoubleDataSi copy()
149     {
150         return new DenseDoubleDataSi(this.data.clone(), rows(), cols());
151     }
152 
153     @SuppressWarnings("checkstyle:needbraces")
154     @Override
155     public int cardinality()
156     {
157         int result = 0;
158         for (int i = 0; i < this.data.length; i++)
159             result += this.data[i] == 0.0 ? 0 : 1;
160         return result;
161     }
162 
163     @Override
164     public DenseDoubleDataSi instantiateNew(final double[] newData)
165     {
166         Throw.when(newData.length != rows() * cols(), IllegalArgumentException.class,
167                 "Data object length != rows * cols, %d != %d * %d", newData.length, rows(), cols());
168         return new DenseDoubleDataSi(newData, rows(), cols());
169     }
170 
171     @Override
172     public DenseDoubleDataSi instantiateNew(final double[] newData, final int newRows, final int newCols)
173     {
174         Throw.when(newData.length != newRows * newCols, IllegalArgumentException.class,
175                 "Data object length != rows * cols, %d != %d * %d", newData.length, newRows, newCols);
176         return new DenseDoubleDataSi(newData, newRows, newCols);
177     }
178 
179     @Override
180     public int hashCode()
181     {
182         final int prime = 31;
183         int result = 1;
184         result = prime * result + Arrays.hashCode(this.data);
185         result = prime * result + Objects.hash(this.cols, this.rows);
186         return result;
187     }
188 
189     @SuppressWarnings("checkstyle:needbraces")
190     @Override
191     public boolean equals(final Object obj)
192     {
193         if (this == obj)
194             return true;
195         if (obj == null)
196             return false;
197         if (getClass() != obj.getClass())
198         {
199             if (obj instanceof DataGridSi dg)
200                 return this.cols == dg.cols()  && this.rows == dg.rows() && Arrays.equals(this.data, dg.getDataArray());
201             return false;
202         }
203         DenseDoubleDataSi other = (DenseDoubleDataSi) obj;
204         return this.cols == other.cols && this.rows == other.rows && Arrays.equals(this.data, other.data);
205     }
206 
207 }