View Javadoc
1   /*
2    * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.algebra;
17  
18  import com.irurueta.statistics.GaussianRandomizer;
19  import com.irurueta.statistics.UniformRandomizer;
20  
21  import java.io.Serializable;
22  import java.util.Arrays;
23  import java.util.Objects;
24  import java.util.Random;
25  
26  /**
27   * Defines a matrix of numerical data.
28   * Values of a matrix are stored inside an internal array of data.
29   */
30  @SuppressWarnings("DuplicatedCode")
31  public class Matrix implements Serializable, Cloneable {
32      /**
33       * Constant defining the default order in which values are stored in a
34       * matrix. This can be useful when copying data to and from an array.
35       */
36      public static final boolean DEFAULT_USE_COLUMN_ORDER = true;
37  
38      /**
39       * Number of matrix rows.
40       */
41      private int rows;
42  
43      /**
44       * Number of matrix columns.
45       */
46      private int columns;
47  
48      /**
49       * Array containing data of matrix. Data is stored linearly in memory
50       * using column order.
51       */
52      private double[] buffer;
53  
54      /**
55       * Array used for indexing column start positions within buffer. This
56       * is used for faster access to matrix elements.
57       */
58      private int[] columnIndex;
59  
60      /**
61       * Constructor of this class.
62       * By default, when instantiating a matrix its values are undefined.
63       *
64       * @param rows    Defines number of rows in matrix.
65       * @param columns Defines number of columns in matrix.
66       * @throws WrongSizeException Exception thrown if matrix must be
67       *                            empty with provided size. In other words, matrices must have at least
68       *                            one row and one column. Leaving number of rows or columns to zero will
69       *                            raise this exception.
70       */
71      public Matrix(final int rows, final int columns) throws WrongSizeException {
72          internalResize(rows, columns);
73      }
74  
75      /**
76       * Copy constructor.
77       *
78       * @param m matrix to copy from.
79       */
80      public Matrix(final Matrix m) {
81          try {
82              internalResize(m.getRows(), m.getColumns());
83          } catch (final WrongSizeException ignore) {
84              // never happens
85          }
86  
87          System.arraycopy(m.getBuffer(), 0, buffer, 0, buffer.length);
88      }
89  
90      /**
91       * Returns number of rows in matrix.
92       *
93       * @return Number of rows in matrix.
94       */
95      public int getRows() {
96          return rows;
97      }
98  
99      /**
100      * Returns number of columns in matrix.
101      *
102      * @return Number of columns in matrix.
103      */
104     public int getColumns() {
105         return columns;
106     }
107 
108     /**
109      * Obtains element in matrix located at position (row and column).
110      * Notice that row and column position are zero-indexed.
111      *
112      * @param row    Row to be used for element location.
113      * @param column Column to be used for element location.
114      * @return Value of element in matrix located at provided position.
115      * @throws ArrayIndexOutOfBoundsException Exception raised if attempting
116      *                                        to access a location that lies outside the boundaries of the internal
117      *                                        array containing matrix data. Notice that internal data is stored in
118      *                                        column order, hence, if row position exceeds the number of rows in the
119      *                                        matrix, this exception might not be raised depending on column position,
120      *                                        however, if the column value exceeds the number of columns, it will
121      *                                        always raise the exception.
122      */
123     public double getElementAt(final int row, final int column) {
124         return buffer[columnIndex[column] + row];
125     }
126 
127     /**
128      * Returns index within internal buffer corresponding to provided row
129      * and column positions.
130      *
131      * @param row    row to be used for element location.
132      * @param column column to be used for element location.
133      * @return index within internal buffer.
134      */
135     public int getIndex(final int row, final int column) {
136         return columnIndex[column] + row;
137     }
138 
139     /**
140      * Obtains element in matrix located at provided index value using the
141      * order specified by DEFAULT_USE_COLUMN_ORDER.
142      * Note: an index indicates linear position within matrix, in layman terms,
143      * if using column order, then position (i, j), where i stands for row
144      * index and j for column index, becomes index = j * rows + 1. On the other
145      * hand, if using row order then index becomes index = i * columns + j.
146      *
147      * @param index Linear position.
148      * @return Value of element contained at provided position.
149      * @throws ArrayIndexOutOfBoundsException Exception raised if index lays
150      *                                        outside valid values, which range from zero (inclusive) to
151      *                                        rows * columns (exclusive)
152      */
153     public double getElementAtIndex(final int index) {
154         return getElementAtIndex(index, DEFAULT_USE_COLUMN_ORDER);
155     }
156 
157     /**
158      * Obtain element in matrix located at provided index value using provided
159      * order (either column or row order).
160      *
161      * @param index         Linear position.
162      * @param isColumnOrder if true indicates that values are retrieved
163      *                      assuming that are stored in column order.
164      * @return Value of element contained at provided position.
165      * @throws ArrayIndexOutOfBoundsException Exception raised if index lays
166      *                                        outside valid values, which range from zero (inclusive) to
167      *                                        rows * columns (exclusive)
168      */
169     public double getElementAtIndex(final int index, final boolean isColumnOrder) {
170 
171         if (isColumnOrder) {
172             return buffer[index];
173         } else {
174             final var row = index / columns;
175             final var column = index % columns;
176             return buffer[columnIndex[column] + row];
177         }
178     }
179 
180     /**
181      * Sets element in matrix located at provided position (row and column).
182      *
183      * @param row    Row to be used for element location to be set.
184      * @param column Column to be used for element location to be set.
185      * @param value  Value to be set at provided position.
186      * @throws ArrayIndexOutOfBoundsException Exception raised if attempting
187      *                                        to access a location that lies outside the boundaries of the internal
188      *                                        array containing matrix data. Notice that internal data is stored in
189      *                                        column order, hence, if row position exceeds the number of rows in the
190      *                                        matrix, this exception might not be raised depending on column position,
191      *                                        however, if the column value exceeds the number of columns, it will
192      *                                        always raise the exception.
193      */
194     public void setElementAt(final int row, final int column, final double value) {
195         buffer[columnIndex[column] + row] = value;
196     }
197 
198     /**
199      * Sets element in matrix located at provided index using the order
200      * specified by DEFAULT_USE_COLUMN_ORDER.
201      * Note: an index indicates linear position within matrix, in layman terms,
202      * if using column order, then position (i, j), where i stands for row
203      * index and j for column index, becomes index = j * rows + 1. On the other
204      * hand, if using row order then index becomes index = i * columns + j.
205      *
206      * @param index Linear position.
207      * @param value Value of element contained at provided position.
208      * @throws ArrayIndexOutOfBoundsException Exception raised if index lays
209      *                                        outside valid values, which range from zero (inclusive) to
210      *                                        rows * columns (exclusive)
211      */
212     public void setElementAtIndex(final int index, final double value) {
213         setElementAtIndex(index, value, DEFAULT_USE_COLUMN_ORDER);
214     }
215 
216     /**
217      * Sets element in matrix located at provided index using provided order
218      * (either column or row order).
219      *
220      * @param index         Linear position.
221      * @param value         Value of element to be set at provided position.
222      * @param isColumnOrder if true indicates that values are retrieved
223      *                      assuming that are stored in column order.
224      * @throws ArrayIndexOutOfBoundsException Exception raised if index lays
225      *                                        outside valid values, which range from zero (inclusive) to
226      *                                        rows * columns (exclusive).
227      */
228     public void setElementAtIndex(final int index, final double value, final boolean isColumnOrder) {
229         if (isColumnOrder) {
230             buffer[index] = value;
231         } else {
232             final var row = index / columns;
233             final var column = index % columns;
234             buffer[columnIndex[column] + row] = value;
235         }
236     }
237 
238     /**
239      * Returns a new matrix instance containing the same data as this instance.
240      *
241      * @return A copy of this matrix instance.
242      * @throws CloneNotSupportedException if clone fails.
243      */
244     @Override
245     public Matrix clone() throws CloneNotSupportedException {
246         var out = (Matrix) super.clone();
247         out.copyFrom(this);
248         return out;
249     }
250 
251     /**
252      * Copies this matrix data into provided matrix. Provided output matrix will
253      * be resized if needed.
254      *
255      * @param output Destination matrix where data will be copied to.
256      * @throws NullPointerException Exception raised if provided output matrix
257      *                              is null.
258      */
259     public void copyTo(final Matrix output) {
260         // reset output size if not equal, otherwise reuse buffer and column
261         // index
262         if (output.getRows() != rows || output.getColumns() != columns) {
263             // resets size and column index
264             try {
265                 output.resize(rows, columns);
266             } catch (final WrongSizeException ignore) {
267                 // never happens
268             }
269         }
270         // copies content
271         System.arraycopy(buffer, 0, output.buffer, 0, buffer.length);
272     }
273 
274     /**
275      * Copies the contents of provided matrix into this instance. This instance
276      * will be resized if needed.
277      *
278      * @param input Input matrix where data will be copied from.
279      * @throws NullPointerException Exception raised if provided input matrix is
280      *                              null.
281      */
282     public void copyFrom(final Matrix input) {
283         // reset size if not equal, otherwise reuse buffer and column index
284         if (input.getRows() != rows || input.getColumns() != columns) {
285             // resets size and column index
286             try {
287                 resize(input.getRows(), input.getColumns());
288             } catch (final WrongSizeException ignore) {
289                 // never happens
290             }
291         }
292         // copies content
293         System.arraycopy(input.buffer, 0, buffer, 0, buffer.length);
294     }
295 
296     /**
297      * Adds another matrix to this matrix instance and stores the result in
298      * provided result matrix. If provided result matrix doesn't have proper
299      * size, it will be resized.
300      *
301      * @param other  Matrix to be added to current instance.
302      * @param result Matrix where result of summation is stored.
303      * @throws WrongSizeException   Exception thrown if provided matrix to be
304      *                              added (i.e. other) does not have the same size as this matrix.
305      * @throws NullPointerException Exception raised if provided matrices are
306      *                              null.
307      */
308     public void add(final Matrix other, final Matrix result) throws WrongSizeException {
309         if (other.getRows() != rows || other.getColumns() != columns) {
310             throw new WrongSizeException();
311         }
312 
313         // resize result if needed
314         if (result.getRows() != rows || result.getColumns() != columns) {
315             result.resize(rows, columns);
316         }
317 
318         internalAdd(other, result);
319     }
320 
321     /**
322      * Adds provided matrix to this instance and returns the result as a new
323      * matrix instance.
324      *
325      * @param other Matrix to be added.
326      * @return Returns a new matrix containing the sum of this matrix with
327      * provided matrix.
328      * @throws WrongSizeException   Exception raised if provided matrix does
329      *                              not have the same size as this matrix.
330      * @throws NullPointerException Exception raised if provided matrix is null.
331      */
332     public Matrix addAndReturnNew(final Matrix other) throws WrongSizeException {
333         if (other.getRows() != rows || other.getColumns() != columns) {
334             throw new WrongSizeException();
335         }
336 
337         final var out = new Matrix(rows, columns);
338         internalAdd(other, out);
339         return out;
340     }
341 
342     /**
343      * Adds provided matrix to this instance.
344      *
345      * @param other Matrix to be added.
346      * @throws WrongSizeException   Exception raised if provided matrix does
347      *                              not have the same size as this matrix.
348      * @throws NullPointerException Exception raised if provided matrix is null.
349      */
350     public void add(final Matrix other) throws WrongSizeException {
351         if (other.getRows() != rows || other.getColumns() != columns) {
352             throw new WrongSizeException();
353         }
354 
355         internalAdd(other, this);
356     }
357 
358     /**
359      * Subtracts another matrix from this matrix instance and stores the result
360      * in provided result matrix. If provided result matrix doesn't have proper
361      * size, it will be resized.
362      *
363      * @param other  Matrix to be added to current instance.
364      * @param result Matrix where result of subtraction is stored.
365      * @throws WrongSizeException   Exception thrown if provided matrix to be
366      *                              subtracted (i.e. other) does not have the same size as this matrix.
367      * @throws NullPointerException Exception raised if provided matrices are
368      *                              null.
369      */
370     public void subtract(final Matrix other, final Matrix result) throws WrongSizeException {
371         if (other.getRows() != rows || other.getColumns() != columns) {
372             throw new WrongSizeException();
373         }
374 
375         // resize result if needed
376         if (result.getRows() != rows || result.getColumns() != columns) {
377             result.resize(rows, columns);
378         }
379 
380         internalSubtract(other, result);
381     }
382 
383     /**
384      * Subtracts provided matrix from this instance and returns the result as a
385      * new matrix instance.
386      *
387      * @param other Matrix to be subtracted from.
388      * @return Returns a new matrix containing the subtraction of provided
389      * matrix from this matrix.
390      * @throws WrongSizeException   Exception raised if provided matrix does
391      *                              not have the same size as this matrix.
392      * @throws NullPointerException Exception raised if provided matrix is null.
393      */
394     public Matrix subtractAndReturnNew(final Matrix other) throws WrongSizeException {
395         if (other.getRows() != rows || other.getColumns() != columns) {
396             throw new WrongSizeException();
397         }
398 
399         final var out = new Matrix(rows, columns);
400         internalSubtract(other, out);
401         return out;
402     }
403 
404     /**
405      * Subtracts provided matrix from this instance.
406      *
407      * @param other Matrix to be subtracted from.
408      * @throws WrongSizeException   Exception raised if provided matrix does
409      *                              not have the same size as this matrix.
410      * @throws NullPointerException Exception raised if provided matrix is null.
411      */
412     public void subtract(final Matrix other) throws WrongSizeException {
413         if (other.getRows() != rows || other.getColumns() != columns) {
414             throw new WrongSizeException();
415         }
416 
417         internalSubtract(other, this);
418     }
419 
420     /**
421      * Multiplies another matrix to this matrix instance and stores the result
422      * in provided result matrix. If provided result matrix doesn't have proper
423      * size, it will be resized.
424      *
425      * @param other  Matrix to be multiplied to current instance.
426      * @param result Matrix where result of product is stored.
427      * @throws WrongSizeException   Exception thrown when current and provided
428      *                              matrix (i.e. other) has incompatible size for product computation.
429      * @throws NullPointerException Exception raised if provided matrices are
430      *                              null.
431      */
432     public void multiply(final Matrix other, final Matrix result) throws WrongSizeException {
433         if (columns != other.rows) {
434             throw new WrongSizeException();
435         }
436 
437         // resize result if needed
438         if (result.rows != rows || result.columns != other.columns) {
439             result.resize(rows, other.columns);
440         }
441 
442         internalMultiply(other, result);
443     }
444 
445     /**
446      * Multiplies this matrix with provided matrix and returns the result as
447      * a new instance.
448      * If this matrix m1 has size m x n and provided matrix m2 has size p x q,
449      * then n must be equal to p so that product m1 * m2 can be correctly
450      * computed obtaining a matrix of size m x q, otherwise an
451      * IllegalArgumentException will be raised.
452      *
453      * @param other Right operand of matrix product
454      * @return Matrix containing result of multiplication
455      * @throws WrongSizeException   Exception thrown when current and
456      *                              provided matrices have incompatible sizes for product computation.
457      * @throws NullPointerException Exception thrown if provided matrix is null
458      */
459     public Matrix multiplyAndReturnNew(final Matrix other) throws WrongSizeException {
460 
461         if (columns != other.rows) {
462             throw new WrongSizeException();
463         }
464 
465         final var out = new Matrix(rows, other.columns);
466         internalMultiply(other, out);
467         return out;
468     }
469 
470     /**
471      * Multiplies this matrix with provided matrix.
472      * If this matrix m1 has size m x n and provided matrix m2 has size p x q,
473      * then n must be equal to p so that product m1 * m2 can be correctly
474      * computed resizing this matrix to a new one having size m x q, otherwise
475      * an IllegalArgumentException will be raised.
476      *
477      * @param other Right operand of matrix product
478      * @throws WrongSizeException   Exception thrown when current and
479      *                              provided matrices have incompatible sizes for product computation.
480      * @throws NullPointerException Exception thrown if provided matrix is null
481      */
482     public void multiply(final Matrix other) throws WrongSizeException {
483 
484         if (columns != other.rows) {
485             throw new WrongSizeException();
486         }
487 
488         // instantiate new buffer and column index
489         final var resultBuffer = new double[rows * other.columns];
490         final var resultColumnIndex = new int[other.columns];
491         var counter = 0;
492         for (int i = 0; i < other.columns; i++) {
493             resultColumnIndex[i] = counter;
494             counter += rows;
495         }
496 
497         internalMultiply(other, resultBuffer, resultColumnIndex);
498         // update matrix data
499         columns = other.columns;
500         columnIndex = resultColumnIndex;
501         buffer = resultBuffer;
502     }
503 
504     /**
505      * Computes the Kronecker product with provided matrix and stores the
506      * result in provided result matrix. If provided result matrix doesn't
507      * have proper size, it will be resized.
508      *
509      * @param other  other matrix to be Kronecker multiplied to current matrix.
510      * @param result matrix where result will be stored.
511      */
512     public void multiplyKronecker(final Matrix other, final Matrix result) {
513         // resize result if needed
514         final var resultRows = rows * other.rows;
515         final var resultCols = columns * other.columns;
516         if (result.rows != resultRows || result.columns != resultCols) {
517             try {
518                 result.resize(resultRows, resultCols);
519             } catch (final WrongSizeException ignore) {
520                 // never thrown
521             }
522         }
523 
524         internalMultiplyKronecker(other, result);
525     }
526 
527     /**
528      * Computes the Kronecker product with provided matrix and returns the
529      * result as a new instance.
530      * If this matrix m1 has size mxn and provided matrix m2 has size pxq,
531      * the resulting matrix will be m*pxn*q.
532      *
533      * @param other other matrix to be Kronecker multiplied to current matrix.
534      * @return matrix containing result of Kronecker multiplication.
535      */
536     public Matrix multiplyKroneckerAndReturnNew(final Matrix other) {
537         Matrix out = null;
538         try {
539             out = new Matrix(rows * other.rows, columns * other.columns);
540             internalMultiplyKronecker(other, out);
541         } catch (final WrongSizeException ignore) {
542             // never thrown
543         }
544         return out;
545     }
546 
547     /**
548      * Computes the Kronecker product of this matrix with provided matrix and
549      * updates this matrix with the result of the multiplication.
550      * If this matrix m1 has size mxn and provided matrix m2 has size pxq,
551      * the resulting matrix will be resized to m*pxn*q.
552      *
553      * @param other other matrix to be Kronecker multiplied to current matrix.
554      */
555     public void multiplyKronecker(final Matrix other) {
556 
557         // instantiate new buffer and column index
558         final var resultRows = rows * other.rows;
559         final var resultCols = columns * other.columns;
560         final var resultBuffer = new double[resultRows * resultCols];
561         final var resultColumnIndex = new int[resultCols];
562         var counter = 0;
563         for (int i = 0; i < resultCols; i++) {
564             resultColumnIndex[i] = counter;
565             counter += resultRows;
566         }
567 
568         internalMultiplyKronecker(other, resultBuffer, resultColumnIndex);
569         // update matrix data
570         rows = resultRows;
571         columns = resultCols;
572         columnIndex = resultColumnIndex;
573         buffer = resultBuffer;
574     }
575 
576     /**
577      * Computes product by scalar of this instance multiplying all its elements
578      * by provided scalar value and storing the results in provided result
579      * matrix. If provided result matrix doesn't have proper size, it will be
580      * automatically resized.
581      *
582      * @param scalar Scalar amount that current matrix will be multiplied by.
583      * @param result Matrix where result of operation is stored.
584      */
585     private void multiplyByScalar(final double scalar, final Matrix result) {
586         final var length = rows * columns;
587         for (int i = 0; i < length; i++) {
588             result.buffer[i] = scalar * buffer[i];
589         }
590     }
591 
592     /**
593      * Computes product by scalar of this instance multiplying all its elements
594      * by provided scalar value and returning the result as a new instance.
595      *
596      * @param scalar Scalar amount that current matrix will be multiplied by.
597      * @return Returns a new matrix instance that contains result of product by
598      * scalar.
599      */
600     public Matrix multiplyByScalarAndReturnNew(final double scalar) {
601         Matrix out = null;
602         try {
603             out = new Matrix(rows, columns);
604             multiplyByScalar(scalar, out);
605         } catch (final WrongSizeException ignore) {
606             // never happens
607         }
608         return out;
609     }
610 
611     /**
612      * Computes product by scalar of this instance multiplying all its elements
613      * by provided scalar value and returning the result as a new instance.
614      *
615      * @param scalar Scalar amount that current matrix will be multiplied by.
616      */
617     public void multiplyByScalar(final double scalar) {
618         multiplyByScalar(scalar, this);
619     }
620 
621     /**
622      * Checks if provided object is a Matrix instance having exactly the same
623      * contents as this matrix instance.
624      *
625      * @param obj Object to be compared
626      * @return Returns true if both objects are considered to be equal.
627      */
628     @Override
629     public boolean equals(final Object obj) {
630         if (obj == null) {
631             return false;
632         }
633         if (obj == this) {
634             return true;
635         }
636         if (!(obj instanceof Matrix other)) {
637             return false;
638         }
639 
640         return equals(other);
641     }
642 
643     /**
644      * Computes and returns hash code for this instance. Hash codes are almost
645      * unique values that are useful for fast classification and storage of
646      * objects in collections.
647      *
648      * @return Hash code
649      */
650     @Override
651     public int hashCode() {
652         return Objects.hash(this.rows, this.columns, Arrays.hashCode(this.buffer), Arrays.hashCode(this.columnIndex));
653     }
654 
655     /**
656      * Checks if provided matrix has exactly the same contents as this matrix
657      * instance.
658      *
659      * @param other Matrix to be compared.
660      * @return Returns true if both objects are considered to be equal (same
661      * content and size)
662      */
663     public boolean equals(final Matrix other) {
664         return equals(other, 0.0);
665     }
666 
667     /**
668      * Checks if provided matrix has contents similar to this matrix by checking
669      * that all values have a maximum difference equal to provided threshold and
670      * same size.
671      *
672      * @param other     Matrix to be compared
673      * @param threshold Maximum difference allowed between values on same
674      *                  position to determine that matrices are equal
675      * @return True if matrices are considered to be equal (almost equal content
676      * and same size)
677      */
678     public boolean equals(final Matrix other, final double threshold) {
679         if (other == null) {
680             return false;
681         }
682         if (other.getRows() != rows) {
683             return false;
684         }
685         if (other.getColumns() != columns) {
686             return false;
687         }
688 
689         // check contents
690         final var length = rows * columns;
691         for (int i = 0; i < length; i++) {
692             if (Math.abs(buffer[i] - other.buffer[i]) > threshold) {
693                 return false;
694             }
695         }
696         return true;
697     }
698 
699     /**
700      * Computes element by element product (i.e. Hadamard product) between
701      * current and provided (i.e. other) matrix, and stores the result in
702      * provided result matrix.
703      *
704      * @param other  Instance that will be used for element by element product
705      *               with current instance.
706      * @param result Matrix where result of operation is stored.
707      * @throws WrongSizeException   Exception raised if attempting to perform
708      *                              element by element product on matrices of different size.
709      * @throws NullPointerException Exception raised if provided matrix is null
710      */
711     public void elementByElementProduct(final Matrix other, final Matrix result) throws WrongSizeException {
712         if (other.getRows() != rows || other.getColumns() != columns) {
713             throw new WrongSizeException();
714         }
715 
716         // resize result if needed
717         if (result.getRows() != rows || result.getColumns() != columns) {
718             result.resize(rows, columns);
719         }
720 
721         internalElementByElementProduct(other, result);
722     }
723 
724     /**
725      * Computes element by element product (i.e. Hadamard product) between
726      * current and provided instances and returns the result as a new instance.
727      * Example:
728      * Having matrices
729      * [1, 2, 3]		 [10, 11, 12]
730      * m1 =	[4, 5, 6]	m2 = [13, 14, 15]
731      * [7, 8, 9]		 [16, 17, 18]
732      * Then their element by element product will be
733      * [10,  22,  36 ]
734      * m3 =    [52,  70,  90 ]
735      * [112, 136, 162]
736      * <p>
737      * Note: Attempting to perform element product on matrices of different size
738      * will raise an IllegalArgumentException
739      *
740      * @param other Instance that will be used for element by element product
741      *              with current instance.
742      * @return A new Matrix containing element by element product result
743      * @throws WrongSizeException   Exception raised if attempting to
744      *                              perform element by element product on matrices of different size.
745      * @throws NullPointerException Exception raised if provided matrix is null
746      */
747     public Matrix elementByElementProductAndReturnNew(final Matrix other) throws WrongSizeException {
748         if (other.getRows() != rows || other.getColumns() != columns) {
749             throw new WrongSizeException();
750         }
751 
752         final var out = new Matrix(rows, columns);
753         internalElementByElementProduct(other, out);
754         return out;
755     }
756 
757     /**
758      * Computes element by element product (i.e. Hadamard product) between
759      * current and provided instances.
760      * Example:
761      * Having matrices
762      * [1, 2, 3]		 [10, 11, 12]
763      * m1 =	[4, 5, 6]	m2 = [13, 14, 15]
764      * [7, 8, 9]		 [16, 17, 18]
765      * Then their element by element product will be
766      * [10,  22,  36 ]
767      * m3 =    [52,  70,  90 ]
768      * [112, 136, 162]
769      * <p>
770      * Note: Attempting to perform element product on matrices of different size
771      * will raise an IllegalArgumentException
772      *
773      * @param other Instance that will be used for element by element product
774      *              with current instance.
775      * @throws WrongSizeException   Exception raised if attempting to
776      *                              perform element by element product on matrices of different size.
777      * @throws NullPointerException Exception raised if provided matrix is null
778      */
779     public void elementByElementProduct(final Matrix other) throws WrongSizeException {
780         if (other.getRows() != rows || other.getColumns() != columns) {
781             throw new WrongSizeException();
782         }
783 
784         internalElementByElementProduct(other, this);
785     }
786 
787     /**
788      * Transposes current matrix and stores result in provided matrix. If
789      * provided matrix doesn't have proper size, it will be resized.
790      *
791      * @param result Instance where transposed matrix is stored.
792      */
793     public void transpose(final Matrix result) {
794         // resize result if needed
795         if (result.getRows() != columns || result.getColumns() != rows) {
796             try {
797                 result.resize(columns, rows);
798             } catch (final WrongSizeException ignore) {
799                 // never happens
800             }
801         }
802         internalTranspose(result);
803     }
804 
805     /**
806      * Transposes current matrix and returns result as a new instance.
807      * Transposition of a matrix is done by exchanging rows and columns, in
808      * Layman terms, given a matrix m1 with elements located at m1(i,j), where i
809      * is the row index and j is the column index, then it follows that its
810      * transposed matrix m2 has the following property m2(i,j) = m1(j, i).
811      *
812      * @return A new Matrix instance containing transposed matrix.
813      */
814     public Matrix transposeAndReturnNew() {
815         Matrix out = null;
816         try {
817             out = new Matrix(columns, rows);
818             internalTranspose(out);
819         } catch (final WrongSizeException ignore) {
820             // never happens
821         }
822         return out;
823     }
824 
825     /**
826      * Transposes current matrix.
827      * Transposition of a matrix is done by exchanging rows and columns, in
828      * Layman terms, given a matrix m1 with elements located at m1(i,j), where i
829      * is the row index and j is the column index, then it follows that its
830      * transposed matrix m2 has the following property m2(i,j) = m1(j, i).
831      */
832     public void transpose() {
833 
834         final var newBuffer = new double[rows * columns];
835         final var newColumnIndex = new int[rows];
836         var counter = 0;
837         for (var i = 0; i < rows; i++) {
838             newColumnIndex[i] = counter;
839             counter += columns;
840         }
841         internalTranspose(newBuffer, newColumnIndex);
842 
843         // update matrix data
844 
845         // swap rows and columns
846         final var tmp = rows;
847         rows = columns;
848         columns = tmp;
849 
850         columnIndex = newColumnIndex;
851         buffer = newBuffer;
852     }
853 
854     /**
855      * Sets the contents of this matrix to provided value in all of its elements
856      *
857      * @param initValue Value to be set on all the elements of this matrix.
858      */
859     public void initialize(final double initValue) {
860         // initialize buffer array to provided value
861         Arrays.fill(buffer, initValue);
862     }
863 
864     /**
865      * Resizes current instance by removing its contents and resizing it to
866      * provided size.
867      *
868      * @param rows    Number of rows to be set
869      * @param columns Number of columns to be set
870      * @throws WrongSizeException Exception raised if either rows or
871      *                            columns is zero.
872      */
873     public void resize(final int rows, final int columns) throws WrongSizeException {
874         internalResize(rows, columns);
875     }
876 
877     /**
878      * Resets current instance by removing its contents, resizing it to provided
879      * size and setting all its elements to provided value.
880      *
881      * @param rows      Number of rows to be set
882      * @param columns   Number of columns to be set
883      * @param initValue Value to be set in all of its elements
884      * @throws WrongSizeException Exception raised if either rows or
885      *                            columns is zero.
886      */
887     public void reset(final int rows, final int columns, final double initValue) throws WrongSizeException {
888         internalResize(rows, columns);
889         initialize(initValue);
890     }
891 
892     /**
893      * Returns the contents of the matrix as an array of values using
894      * DEFAULT_USE_COLUMN_ORDER to pick elements.
895      *
896      * @return Contents of matrix as an array
897      */
898     public double[] toArray() {
899         return toArray(DEFAULT_USE_COLUMN_ORDER);
900     }
901 
902     /**
903      * Returns the contents of the matrix as an array of values using provided
904      * order to pick elements.
905      *
906      * @param isColumnOrder If true, picks elements from matrix using column
907      *                      order, otherwise row order is used.
908      * @return Contents of matrix as an array,
909      */
910     public double[] toArray(final boolean isColumnOrder) {
911         final var length = rows * columns;
912 
913         if (isColumnOrder) {
914             return Arrays.copyOf(buffer, length);
915         } else {
916             final var out = new double[length];
917             double value;
918             var counter = 0;
919 
920             for (var j = 0; j < rows; j++) {
921                 for (var i = 0; i < columns; i++) {
922                     value = buffer[columnIndex[i] + j];
923                     out[counter] = value;
924                     counter++;
925                 }
926             }
927 
928             return out;
929         }
930     }
931 
932     /**
933      * Copies the contents of the matrix to an array of values using column
934      * order.
935      *
936      * @param result array where values will be copied to.
937      * @throws WrongSizeException if provided result array does not have the
938      *                            same number of elements as the matrix (i.e. rows x columns).
939      */
940     public void toArray(final double[] result) throws WrongSizeException {
941         toArray(result, DEFAULT_USE_COLUMN_ORDER);
942     }
943 
944     /**
945      * Copies the contents of the matrix to an array of values using provided
946      * order to pick elements.
947      *
948      * @param result        array where values will be copied to.
949      * @param isColumnOrder if true, picks elements from matrix using column
950      *                      order, otherwise row order is used.
951      * @throws WrongSizeException if provided result array does not have the
952      *                            same number of elements as the matrix (i.e. rows x columns).
953      */
954     public void toArray(final double[] result, final boolean isColumnOrder) throws WrongSizeException {
955         if (result.length != buffer.length) {
956             throw new WrongSizeException("result array must be equal to rows x columns");
957         }
958 
959         if (isColumnOrder) {
960             System.arraycopy(buffer, 0, result, 0, buffer.length);
961         } else {
962             double value;
963             var counter = 0;
964 
965             for (var j = 0; j < rows; j++) {
966                 for (var i = 0; i < columns; i++) {
967                     value = buffer[columnIndex[i] + j];
968                     result[counter] = value;
969                     counter++;
970                 }
971             }
972         }
973     }
974 
975     /**
976      * Returns current matrix internal buffer of data.
977      *
978      * @return Internal buffer of data.
979      */
980     public double[] getBuffer() {
981         return buffer;
982     }
983 
984     /**
985      * Obtains a sub-matrix of current matrix instance. Sub-matrix is obtained by
986      * copying all elements contained within provided coordinates (both top-left
987      * and bottom-right points are included within sub-matrix).
988      *
989      * @param topLeftRow        Top-left row index where sub-matrix starts.
990      * @param topLeftColumn     Top-left column index where sub-matrix starts.
991      * @param bottomRightRow    Bottom-right row index where sub-matrix ends.
992      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
993      * @param result            Instance where sub-matrix data is stored.
994      * @throws IllegalArgumentException Exception raised whenever top-left or
995      *                                  bottom-right corners lie outside current matrix instance, or if top-left
996      *                                  corner is indeed located below or at right side of bottom-right corner.
997      * @throws NullPointerException     If provided result matrix is null.
998      */
999     public void getSubmatrix(final int topLeftRow, final int topLeftColumn, final int bottomRightRow,
1000                              final int bottomRightColumn, final Matrix result) {
1001         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns
1002                 || bottomRightRow < 0 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1003                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1004             throw new IllegalArgumentException();
1005         }
1006 
1007         final var subRows = bottomRightRow - topLeftRow + 1;
1008         final var subCols = bottomRightColumn - topLeftColumn + 1;
1009         if (result.getRows() != subRows || result.getColumns() != subCols) {
1010             // resize result
1011             try {
1012                 result.resize(subRows, subCols);
1013             } catch (final WrongSizeException ignore) {
1014                 // never happens
1015             }
1016         }
1017         internalGetSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, result);
1018     }
1019 
1020     /**
1021      * Obtains a sub-matrix of current matrix instance. Sub-matrix is obtained by
1022      * copying all elements contained within provided coordinates (both top-left
1023      * and bottom-right points are included within sub-matrix).
1024      *
1025      * @param topLeftRow        Top-left row index where sub-matrix starts.
1026      * @param topLeftColumn     Top-left column index where sub-matrix starts.
1027      * @param bottomRightRow    Bottom-right row index where sub-matrix ends.
1028      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1029      * @return A new instance containing selected sub-matrix.
1030      * @throws IllegalArgumentException Exception raised whenever top-left or
1031      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1032      *                                  corner is indeed located belo or at right side of bottom-right corner.
1033      */
1034     public Matrix getSubmatrix(final int topLeftRow, final int topLeftColumn,
1035                                final int bottomRightRow, final int bottomRightColumn) {
1036         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns
1037                 || bottomRightRow < 0 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1038                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1039             throw new IllegalArgumentException();
1040         }
1041 
1042         Matrix out;
1043         try {
1044             out = new Matrix(bottomRightRow - topLeftRow + 1, bottomRightColumn - topLeftColumn + 1);
1045         } catch (final WrongSizeException e) {
1046             throw new IllegalArgumentException(e);
1047         }
1048         internalGetSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, out);
1049         return out;
1050     }
1051 
1052     /**
1053      * Retrieves a sub-matrix of current matrix instance as an array of values
1054      * using column order and storing the result in provided array.
1055      * Sub-matrix is obtained by copying all elements contained within provided
1056      * coordinates (both top-left and bottom-right points are included within
1057      * sub-matrix).
1058      *
1059      * @param topLeftRow        Top-left row index where sub-matrix starts
1060      * @param topLeftColumn     Top-left column index where sub-matrix starts
1061      * @param bottomRightRow    Bottom-right row index where sub-matrix ends
1062      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1063      * @param array             Array where sub-matrix data is stored.
1064      * @throws IllegalArgumentException Thrown if provided coordinates lie
1065      *                                  outside of matrix boundaries or if top-left corner is at the bottom or
1066      *                                  right side of bottom-right corner
1067      * @throws WrongSizeException       thrown if length of provided array does not
1068      *                                  match the number of elements to be extracted from this matrix
1069      */
1070     public void getSubmatrixAsArray(final int topLeftRow, final int topLeftColumn,
1071                                     final int bottomRightRow, final int bottomRightColumn,
1072                                     final double[] array) throws WrongSizeException {
1073         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1074                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1075                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1076             throw new IllegalArgumentException();
1077         }
1078 
1079         final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1);
1080         if (array.length != length) {
1081             throw new WrongSizeException();
1082         }
1083 
1084         getSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, DEFAULT_USE_COLUMN_ORDER,
1085                 array);
1086     }
1087 
1088     /**
1089      * Retrieves a sub-matrix of current matrix instance as an array of values
1090      * using provided column order and storing the result in provided array.
1091      * Sub-matrix is obtained by copying all elements contained within provided
1092      * coordinates (both top-left and bottom-right points are included within
1093      * sub-matrix).
1094      *
1095      * @param topLeftRow        Top-left row index where sub-matrix starts
1096      * @param topLeftColumn     Top-left column index where sub-matrix starts
1097      * @param bottomRightRow    Bottom-right row index where sub-matrix ends
1098      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1099      * @param isColumnOrder     If true, picks elements from matrix using column
1100      *                          order, otherwise row order is used.
1101      * @param array             Array where sub-matrix data is stored.
1102      * @throws IllegalArgumentException Exception raised whenever top-left or
1103      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1104      *                                  corner is indeed located below or at right side of bottom-right corner.
1105      * @throws WrongSizeException       If provided array doesn't have proper length,
1106      *                                  which must be equal to the amount of elements in desired sub-matrix.
1107      */
1108     public void getSubmatrixAsArray(
1109             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1110             final boolean isColumnOrder, final double[] array) throws WrongSizeException {
1111         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1112                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1113                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1114             throw new IllegalArgumentException();
1115         }
1116 
1117         final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1);
1118         if (array.length != length) {
1119             throw new WrongSizeException();
1120         }
1121 
1122         internalGetSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, isColumnOrder, array);
1123     }
1124 
1125     /**
1126      * Obtains sub-matrix of current matrix instance as an array of values using
1127      * DEFAULT_USE_COLUMN_ORDER. Array is obtained by copying all elements
1128      * contained within provided coordinates (both top-left
1129      * and bottom-right points are included within sub-matrix).
1130      *
1131      * @param topLeftRow        Top-left row index where sub-matrix starts
1132      * @param topLeftColumn     Top-left column index where sub-matrix starts
1133      * @param bottomRightRow    Bottom-right row index where sub-matrix ends
1134      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1135      * @return An array containing sub-matrix elements
1136      * @throws IllegalArgumentException Exception raised whenever top-left or
1137      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1138      *                                  corner is indeed located belo or at right side of bottom-right corner.
1139      */
1140     public double[] getSubmatrixAsArray(
1141             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn) {
1142         return getSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn,
1143                 DEFAULT_USE_COLUMN_ORDER);
1144     }
1145 
1146     /**
1147      * Obtains sub-matrix of current matrix instance as an array of values using
1148      * provided order. Array is obtained by copying all elements
1149      * contained within provided coordinates (both top-left
1150      * and bottom-right points are included within sub-matrix).
1151      *
1152      * @param topLeftRow        Top-left row index where sub-matrix starts
1153      * @param topLeftColumn     Top-left column index where sub-matrix starts
1154      * @param bottomRightRow    Bottom-right row index where sub-matrix ends
1155      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1156      * @param isColumnOrder     If true, picks elements from matrix using column
1157      *                          order, otherwise row order is used.
1158      * @return An array containing sub-matrix elements
1159      * @throws IllegalArgumentException Exception raised whenever top-left or
1160      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1161      *                                  corner is indeed located below or at right side of bottom-right corner.
1162      */
1163     public double[] getSubmatrixAsArray(
1164             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1165             final boolean isColumnOrder) {
1166         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1167                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1168                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1169             throw new IllegalArgumentException();
1170         }
1171 
1172         final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1);
1173 
1174         final var out = new double[length];
1175         internalGetSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, isColumnOrder, out);
1176         return out;
1177     }
1178 
1179     /**
1180      * Copies elements from provided sub-matrix into this matrix at provided
1181      * location.
1182      *
1183      * @param topLeftRow        Top-left row index where sub-matrix copy starts
1184      * @param topLeftColumn     Top-left column index where sub-matrix copy starts
1185      * @param bottomRightRow    Bottom-right row index where sub-matrix copy ends
1186      * @param bottomRightColumn Bottom-right column index where sub-matrix copy
1187      *                          ends.
1188      * @param submatrix         Sub-matrix to be copied
1189      * @throws IllegalArgumentException Exception raised whenever top-left or
1190      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1191      *                                  corner is indeed located below or at right side of bottom-right corner.
1192      */
1193     public void setSubmatrix(
1194             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1195             final Matrix submatrix) {
1196         setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, submatrix, 0,
1197                 0, submatrix.getRows() - 1,
1198                 submatrix.getColumns() - 1);
1199     }
1200 
1201     /**
1202      * Copies elements from provided sub-matrix into this matrix at provided
1203      * location
1204      *
1205      * @param topLeftRow                 Top-left row index where sub-matrix copy starts
1206      * @param topLeftColumn              Top-left column index where sub-matrix copy starts
1207      * @param bottomRightRow             Bottom-right row index where sub-matrix copy ends
1208      * @param bottomRightColumn          Bottom-right column index where sub-matrix copy
1209      *                                   ends.
1210      * @param submatrix                  Sub-matrix to be copied
1211      * @param submatrixTopLeftRow        Top-left row index of sub-matrix where copy
1212      *                                   starts
1213      * @param submatrixTopLeftColumn     Top-left column index of sub-matrix where
1214      *                                   copy starts
1215      * @param submatrixBottomRightRow    Bottom-right row index of sub-matrix where
1216      *                                   copy ends
1217      * @param submatrixBottomRightColumn Bottom-right column index of sub-matrix
1218      *                                   where copy ends
1219      * @throws IllegalArgumentException Exception raised whenever top-left or
1220      *                                  bottom-right corners lie outside current or provided matrices, or if
1221      *                                  top-left corners are indeed located below or at right side of
1222      *                                  bottom-right corners.
1223      */
1224     public void setSubmatrix(
1225             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1226             final Matrix submatrix, final int submatrixTopLeftRow, final int submatrixTopLeftColumn,
1227             final int submatrixBottomRightRow, final int submatrixBottomRightColumn) {
1228         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1229                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1230                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1231             throw new IllegalArgumentException();
1232         }
1233 
1234         if (submatrixTopLeftRow < 0 || submatrixTopLeftColumn < 0 || submatrixBottomRightRow < 0
1235                 || submatrixBottomRightRow >= submatrix.rows || submatrixBottomRightColumn < 0
1236                 || submatrixBottomRightColumn >= submatrix.columns || submatrixTopLeftRow > submatrixBottomRightRow
1237                 || submatrixTopLeftColumn > submatrixBottomRightColumn) {
1238             throw new IllegalArgumentException();
1239         }
1240 
1241         final var matrixRows = bottomRightRow - topLeftRow + 1;
1242         final var matrixColumns = bottomRightColumn - topLeftColumn + 1;
1243         final var submatrixRows = submatrixBottomRightRow - submatrixTopLeftRow + 1;
1244         final var submatrixColumns = submatrixBottomRightColumn - submatrixTopLeftColumn + 1;
1245         if (matrixRows != submatrixRows || matrixColumns != submatrixColumns) {
1246             throw new IllegalArgumentException();
1247         }
1248 
1249         var j2 = submatrixTopLeftColumn;
1250         int destPos;
1251         int sourcePos;
1252         for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
1253             destPos = columnIndex[j] + topLeftRow;
1254             sourcePos = submatrix.columnIndex[j2] + submatrixTopLeftRow;
1255             for (var i = topLeftRow; i <= bottomRightRow; i++) {
1256                 // Lines below are equivalent to commented code
1257                 buffer[destPos] = submatrix.buffer[sourcePos];
1258                 destPos++;
1259                 sourcePos++;
1260             }
1261             j2++;
1262         }
1263     }
1264 
1265     /**
1266      * Sets elements in provided region to provided value.
1267      *
1268      * @param topLeftRow        Top-left row index of region (inclusive).
1269      * @param topLeftColumn     Top-left column index of region (inclusive).
1270      * @param bottomRightRow    Bottom-right row index of region (inclusive).
1271      * @param bottomRightColumn Bottom-right column index of region (inclusive).
1272      * @param value             Value to be set.
1273      * @throws IllegalArgumentException Exception raised whenever top-left or
1274      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1275      *                                  corner is indeed located below or at right side of bottom-right corner.
1276      */
1277     public void setSubmatrix(
1278             int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn, double value) {
1279         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1280                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1281                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1282             throw new IllegalArgumentException();
1283         }
1284 
1285         for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
1286             for (var i = topLeftRow; i <= bottomRightRow; i++) {
1287                 buffer[columnIndex[j] + i] = value;
1288             }
1289         }
1290     }
1291 
1292     /**
1293      * Copies elements from provided array into this matrix at provided
1294      * location. Elements in array are copied into this matrix considering
1295      * DEFAULT_USE_COLUMN_ORDER.
1296      *
1297      * @param topLeftRow        Top-left row index where copy starts.
1298      * @param topLeftColumn     Top-left column index where copy starts.
1299      * @param bottomRightRow    Bottom-right row index where copy ends.
1300      * @param bottomRightColumn Bottom-right column index where copy
1301      *                          ends.
1302      * @param values            Array to be copied.
1303      * @throws IllegalArgumentException Exception raised whenever top-left or
1304      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1305      *                                  corner is indeed located below or at right side of bottom-right corner.
1306      */
1307     public void setSubmatrix(
1308             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1309             final double[] values) {
1310         setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, DEFAULT_USE_COLUMN_ORDER);
1311     }
1312 
1313     /**
1314      * Copies elements from provided array into this matrix at provided
1315      * location. Elements in array are copied into this matrix following
1316      * provided order (either row or column order).
1317      *
1318      * @param topLeftRow        Top-left row index where copy starts.
1319      * @param topLeftColumn     Top-left column index where copy ends.
1320      * @param bottomRightRow    Bottom-right row index where copy ends.
1321      * @param bottomRightColumn Bottom-right column index where copy ends.
1322      * @param values            Array to be copied.
1323      * @param isColumnOrder     If true values are copied consecutively from array
1324      *                          following column order on the destination matrix, otherwise row order is
1325      *                          used.
1326      * @throws IllegalArgumentException Exception raised whenever top-left or
1327      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1328      *                                  corner is indeed located below or at right side of bottom-right corner.
1329      */
1330     public void setSubmatrix(
1331             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1332             final double[] values, final boolean isColumnOrder) {
1333         setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, 0,
1334                 values.length - 1, isColumnOrder);
1335     }
1336 
1337     /**
1338      * Copies elements from provided array into this matrix at provided
1339      * location. Elements in array are copied into this matrix considering
1340      * DEFAULT_USE_COLUMN_ORDER starting at provided location until end provided
1341      * position.
1342      *
1343      * @param topLeftRow        Top-left row index where copy starts (inclusive).
1344      * @param topLeftColumn     Top-left column index where copy starts (inclusive).
1345      * @param bottomRightRow    Bottom-right row index where copy ends (inclusive).
1346      * @param bottomRightColumn Bottom-right column index where copy
1347      *                          ends. (inclusive)
1348      * @param values            Array to be copied.
1349      * @param valuesStart       Position where copy from array will start (inclusive).
1350      * @param valuesEnd         Position where copy from array will finish (inclusive).
1351      * @throws IllegalArgumentException Exception raised whenever top-left or
1352      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1353      *                                  corner is indeed located below or at right side of bottom-right corner,
1354      *                                  or if valuesStart and valuesEnd lie outside valid array positions or
1355      *                                  start is greater than end position.
1356      */
1357     public void setSubmatrix(
1358             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1359             final double[] values, final int valuesStart, final int valuesEnd) {
1360         setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, valuesStart, valuesEnd,
1361                 DEFAULT_USE_COLUMN_ORDER);
1362     }
1363 
1364     /**
1365      * Copies elements from provided array into this matrix at provided
1366      * location. Elements in array are copied into this matrix following
1367      * provided order (either row or column order) starting at provided location
1368      * until end provided position.
1369      *
1370      * @param topLeftRow        Top-left row index where copy starts (inclusive)
1371      * @param topLeftColumn     Top-left column index where copy starts (inclusive)
1372      * @param bottomRightRow    Bottom-right row index where copy ends (inclusive)
1373      * @param bottomRightColumn Bottom-right column index where copy
1374      *                          ends. (inclusive)
1375      * @param values            Array to be copied.
1376      * @param valuesStart       Position where copy from array will start (inclusive).
1377      * @param valuesEnd         Position where copy from array will finish (inclusive).
1378      * @param isColumnOrder     If true values are copied consecutively from array
1379      *                          following column order on the destination matrix, otherwise row order is
1380      *                          used.
1381      * @throws IllegalArgumentException Exception raised whenever top-left or
1382      *                                  bottom-right corners lie outside current matrix instance, or if top-left
1383      *                                  corner is indeed located below or at right side of bottom-right corner,
1384      *                                  or if valuesStart and valuesEnd lie outside valid array positions or
1385      *                                  start is greater than end position.
1386      */
1387     public void setSubmatrix(
1388             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1389             final double[] values, final int valuesStart, final int valuesEnd, final boolean isColumnOrder) {
1390 
1391         if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0
1392                 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns
1393                 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) {
1394             throw new IllegalArgumentException();
1395         }
1396 
1397         if (valuesStart < 0 || valuesEnd < 0 || valuesEnd >= values.length || valuesStart > valuesEnd) {
1398             throw new IllegalArgumentException();
1399         }
1400 
1401         final var matrixRows = bottomRightRow - topLeftRow + 1;
1402         final var matrixColumns = bottomRightColumn - topLeftColumn + 1;
1403         final var matrixLength = matrixRows * matrixColumns;
1404         final var valuesLength = valuesEnd - valuesStart + 1;
1405 
1406         if (matrixLength != valuesLength) {
1407             throw new IllegalArgumentException();
1408         }
1409 
1410         var counter = valuesStart;
1411         if (isColumnOrder) {
1412             int destPos;
1413             for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
1414                 destPos = columnIndex[j] + topLeftRow;
1415                 for (var i = topLeftRow; i <= bottomRightRow; i++) {
1416                     // Two Lines below are equivalent to:
1417                     // buffer[columnIndex[j] + i] = values[counter]
1418                     buffer[destPos] = values[counter];
1419                     destPos++;
1420                     counter++;
1421                 }
1422             }
1423 
1424         } else {
1425             for (var i = topLeftRow; i <= bottomRightRow; i++) {
1426                 for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
1427                     buffer[columnIndex[j] + i] = values[counter];
1428                     counter++;
1429                 }
1430             }
1431         }
1432     }
1433 
1434     /**
1435      * Sets values into provided matrix to make it an identity matrix (all
1436      * elements in the diagonal equal to one, and remaining elements to zero).
1437      *
1438      * @param m Matrix where identity values are set.
1439      */
1440     public static void identity(final Matrix m) {
1441         m.initialize(0.0);
1442 
1443         final var minSize = Math.min(m.rows, m.columns);
1444         for (var i = 0; i < minSize; i++) {
1445             m.buffer[m.columnIndex[i] + i] = 1.0;
1446         }
1447     }
1448 
1449     /**
1450      * Creates and returns a new matrix instance having all the elements on
1451      * the diagonal equal to one and the remaining ones equal to zero.
1452      *
1453      * @param rows    Number of rows of created instance
1454      * @param columns Number of columns of created instance
1455      * @return An identity matrix
1456      * @throws WrongSizeException Raised if either rows or columns is
1457      *                            equal to zero
1458      */
1459     public static Matrix identity(final int rows, final int columns) throws WrongSizeException {
1460         final var out = new Matrix(rows, columns);
1461         identity(out);
1462         return out;
1463     }
1464 
1465     /**
1466      * Fills provided matrix with random uniform values ranging from minValue to
1467      * maxValue.
1468      *
1469      * @param minValue Minimum value of uniform random generated values
1470      * @param maxValue Maximum value of uniform random generated values
1471      * @param random   Random generator
1472      * @param result   Matrix where random values are stored.
1473      * @throws IllegalArgumentException if minValue &lt;= maxValue
1474      * @throws NullPointerException     if either provided random or result
1475      *                                  instances are null
1476      */
1477     public static void fillWithUniformRandomValues(
1478             final double minValue, final double maxValue, final Random random, final Matrix result) {
1479         final var randomizer = new UniformRandomizer(random);
1480 
1481         final var length = result.rows * result.columns;
1482 
1483         for (int i = 0; i < length; i++) {
1484             result.buffer[i] = randomizer.nextDouble(minValue, maxValue);
1485         }
1486     }
1487 
1488     /**
1489      * Fills provided matrix with random uniform values ranging from minValue to
1490      * maxValue.
1491      *
1492      * @param minValue Minimum value of uniform random generated values
1493      * @param maxValue Maximum value of uniform random generated values
1494      * @param result   Matrix where random values are stored
1495      * @throws IllegalArgumentException if minValue &lt;= maxValue
1496      * @throws NullPointerException     if provided result matrix is null
1497      */
1498     public static void fillWithUniformRandomValues(final double minValue, final double maxValue, final Matrix result) {
1499         fillWithUniformRandomValues(minValue, maxValue, new Random(), result);
1500     }
1501 
1502     /**
1503      * Creates new matrix instance using provided size and containing uniformly
1504      * distributed random values with provided range.
1505      *
1506      * @param rows     Number of rows of instantiated matrix
1507      * @param columns  Number of columns of instantiated matrix
1508      * @param minValue Minimum value of uniform random generated values
1509      * @param maxValue Maximum value of uniform random generated values
1510      * @return A new matrix containing uniform random values
1511      * @throws WrongSizeException       Exception thrown if either rows or
1512      *                                  columns is zero, or if the minimum random value is greater or equal
1513      *                                  than the maximum random value
1514      * @throws IllegalArgumentException if minValue &lt;= maxValue
1515      */
1516     public static Matrix createWithUniformRandomValues(
1517             final int rows, final int columns, final double minValue, final double maxValue) throws WrongSizeException {
1518         return createWithUniformRandomValues(rows, columns, minValue, maxValue, new Random());
1519     }
1520 
1521     /**
1522      * Creates new matrix instance using provided size and containing uniformly
1523      * distributed random values with provided range and using provided random
1524      * generator
1525      *
1526      * @param rows     Number of rows of instantiated matrix
1527      * @param columns  Number of columns of instantiated matrix
1528      * @param minValue Minimum value of uniform random generated values
1529      * @param maxValue Maximum value of uniform random generated values
1530      * @param random   A random generator.
1531      * @return A new matrix containing uniform random values
1532      * @throws WrongSizeException       Exception thrown if either rows or
1533      *                                  columns is zero, or if the minimum random value is greater or equal
1534      *                                  than the maximum random value
1535      * @throws IllegalArgumentException if minValue &lt;= maxValue
1536      */
1537     public static Matrix createWithUniformRandomValues(
1538             final int rows, final int columns, final double minValue, final double maxValue, final Random random)
1539             throws WrongSizeException {
1540 
1541         final var out = new Matrix(rows, columns);
1542         fillWithUniformRandomValues(minValue, maxValue, random, out);
1543         return out;
1544     }
1545 
1546     /**
1547      * Fills provided matrix with random Gaussian values with provided mean and
1548      * standard deviation.
1549      *
1550      * @param mean              Mean value of generated random values
1551      * @param standardDeviation Standard deviation of generated random values
1552      * @param random            Random generator
1553      * @param result            Matrix where random values are stored
1554      * @throws IllegalArgumentException if standard deviation is negative or
1555      *                                  zero
1556      * @throws NullPointerException     if provided result matrix is null
1557      */
1558     public static void fillWithGaussianRandomValues(
1559             final double mean, final double standardDeviation, final Random random, final Matrix result) {
1560 
1561         final var randomizer = new GaussianRandomizer(random, mean, standardDeviation);
1562 
1563         final var length = result.rows * result.columns;
1564 
1565         for (var i = 0; i < length; i++) {
1566             result.buffer[i] = randomizer.nextDouble();
1567         }
1568     }
1569 
1570     /**
1571      * Fills provided matrix with random Gaussian values with provided mean and
1572      * standard deviation
1573      *
1574      * @param mean              Mean value of generated random values
1575      * @param standardDeviation Standard deviation of generated random values
1576      * @param result            Matrix where random values are stored
1577      * @throws IllegalArgumentException if standard deviation is negative or
1578      *                                  zero
1579      * @throws NullPointerException     if provided result matrix is null
1580      */
1581     public static void fillWithGaussianRandomValues(
1582             final double mean, final double standardDeviation, final Matrix result) {
1583         fillWithGaussianRandomValues(mean, standardDeviation, new Random(), result);
1584     }
1585 
1586     /**
1587      * Creates new matrix instance using provided size and containing
1588      * gaussian/normal distributed random values with provided median and
1589      * standard deviation.
1590      *
1591      * @param rows              Number of rows of instantiated matrix
1592      * @param columns           Number of columns of instantiated matrix
1593      * @param mean              Mean value of gaussian random generated values
1594      * @param standardDeviation Standard deviation of gaussian random generated
1595      *                          values
1596      * @return A new matrix containing gaussian random values
1597      * @throws WrongSizeException       Exception thrown if either rows or
1598      *                                  columns is zero, or if the standard deviation is negative or zero.
1599      * @throws IllegalArgumentException thrown if provided standard deviation
1600      *                                  is negative or zero.
1601      */
1602     public static Matrix createWithGaussianRandomValues(
1603             final int rows, final int columns, final double mean, final double standardDeviation)
1604             throws WrongSizeException {
1605         return createWithGaussianRandomValues(rows, columns, mean, standardDeviation, new Random());
1606     }
1607 
1608     /**
1609      * Creates new matrix instance using provided size and containing
1610      * gaussian/normal distributed random values with provided median and
1611      * standard deviation and using provided random generator
1612      *
1613      * @param rows              Number of rows of instantiated matrix
1614      * @param columns           Number of columns of instantiated matrix
1615      * @param mean              Mean value of gaussian random generated values
1616      * @param standardDeviation Standard deviation of gaussian random generated
1617      *                          values
1618      * @param random            A random generator.
1619      * @return A new matrix containing gaussian random values
1620      * @throws WrongSizeException       Exception thrown if either rows or
1621      *                                  columns is zero, or if the standard deviation is negative or zero.
1622      * @throws IllegalArgumentException thrown if provided standard deviation
1623      *                                  is negative or zero.
1624      */
1625     public static Matrix createWithGaussianRandomValues(
1626             final int rows, final int columns, final double mean, final double standardDeviation, final Random random)
1627             throws WrongSizeException {
1628 
1629         final var out = new Matrix(rows, columns);
1630         fillWithGaussianRandomValues(mean, standardDeviation, random, out);
1631         return out;
1632     }
1633 
1634     /**
1635      * Makes provided result matrix a diagonal matrix containing provided
1636      * elements in the diagonal. Elements outside the diagonal will be set to
1637      * zero
1638      *
1639      * @param diagonal Array containing the elements to be set on the diagonal
1640      * @param result   Matrix where values are stored
1641      * @throws NullPointerException Exception thrown if provided parameters are
1642      *                              null
1643      */
1644     public static void diagonal(final double[] diagonal, final Matrix result) {
1645 
1646         result.initialize(0.0);
1647         // set diagonal elements
1648         //noinspection ManualArrayCopy
1649         for (var i = 0; i < diagonal.length; i++) {
1650             result.buffer[result.columnIndex[i] + i] = diagonal[i];
1651         }
1652     }
1653 
1654     /**
1655      * Creates a diagonal matrix having all the elements in provided array in
1656      * its diagonal and the remaining elements equal to zero.
1657      * Returned matrix will have size n x n, where n is the length of the array
1658      *
1659      * @param diagonal Array containing the elements to be set on the diagonal
1660      * @return A diagonal matrix
1661      * @throws NullPointerException Raised if provided diagonal array is null
1662      */
1663     public static Matrix diagonal(final double[] diagonal) {
1664 
1665         Matrix out = null;
1666         try {
1667             out = new Matrix(diagonal.length, diagonal.length);
1668             diagonal(diagonal, out);
1669         } catch (final WrongSizeException ignore) {
1670             // never happens
1671         }
1672 
1673         return out;
1674     }
1675 
1676     /**
1677      * Instantiates new matrix from array using DEFAULT_USE_COLUMN_ORDER
1678      *
1679      * @param array Array used as source to copy values from
1680      * @return Returns matrix created from array
1681      */
1682     public static Matrix newFromArray(final double[] array) {
1683         return newFromArray(array, DEFAULT_USE_COLUMN_ORDER);
1684     }
1685 
1686     /**
1687      * Instantiates new matrix from array using either column or row order
1688      *
1689      * @param array         Array used as source to copy values from
1690      * @param isColumnOrder True if column order must be used, false otherwise
1691      * @return Returns matrix created from array
1692      */
1693     public static Matrix newFromArray(final double[] array, final boolean isColumnOrder) {
1694         Matrix m = null;
1695         try {
1696             if (isColumnOrder) {
1697                 m = new Matrix(array.length, 1);
1698                 m.setSubmatrix(0, 0, array.length - 1, 0, array);
1699             } else {
1700                 m = new Matrix(1, array.length);
1701                 m.setSubmatrix(0, 0, 0, array.length - 1, array);
1702             }
1703         } catch (final WrongSizeException ignore) {
1704             // never happens
1705         }
1706         return m;
1707     }
1708 
1709     /**
1710      * Copies elements of array into this instance using column order.
1711      *
1712      * @param array array to copy values from.
1713      * @throws WrongSizeException if provided array length is not equal to the
1714      *                            number of rows multiplied per the number of columns of this instance.
1715      */
1716     public void fromArray(final double[] array) throws WrongSizeException {
1717         fromArray(array, DEFAULT_USE_COLUMN_ORDER);
1718     }
1719 
1720     /**
1721      * Copies elements of array into this instance using provided order.
1722      *
1723      * @param array         array to copy values from.
1724      * @param isColumnOrder true to use column order, false otherwise.
1725      * @throws WrongSizeException if provided array length is not equal to the
1726      *                            number of rows multiplied per the number of columns of this instance.
1727      */
1728     public void fromArray(final double[] array, final boolean isColumnOrder) throws WrongSizeException {
1729         if (array.length != buffer.length) {
1730             throw new WrongSizeException("array length must be equal to rows x columns");
1731         }
1732 
1733         if (isColumnOrder) {
1734             System.arraycopy(array, 0, buffer, 0, array.length);
1735         } else {
1736             var counter = 0;
1737 
1738             for (var j = 0; j < rows; j++) {
1739                 for (var i = 0; i < columns; i++) {
1740                     buffer[columnIndex[i] + j] = array[counter];
1741                     counter++;
1742                 }
1743             }
1744         }
1745     }
1746 
1747     /**
1748      * Symmetrizes this instance and stores the result into provided instance.
1749      * Symmetrization is done by averaging this instance with its transpose
1750      * (i.e. S = (M+M')/2
1751      *
1752      * @param result instance where symmetrized version of this instance will be
1753      *               stored.
1754      * @throws WrongSizeException if this instance is not square or provided
1755      *                            result instance doesn't have the same size as this instance.
1756      */
1757     public void symmetrize(final Matrix result) throws WrongSizeException {
1758         if (rows != columns) {
1759             throw new WrongSizeException("matrix must be square");
1760         }
1761         if (result.getRows() != rows || result.getColumns() != columns) {
1762             throw new WrongSizeException("result matrix must have the size of this instance");
1763         }
1764 
1765         // S = (M+M')/2
1766         double value1;
1767         double value2;
1768         double avg;
1769         int pos1;
1770         int pos2;
1771         for (var i = 0; i < columns; i++) {
1772             for (var j = i; j < rows; j++) {
1773                 // value at (i, j)
1774                 pos1 = columnIndex[i] + j;
1775                 value1 = buffer[pos1];
1776                 // transposed value (i.e. value at (j,i))
1777                 pos2 = columnIndex[j] + i;
1778                 value2 = buffer[pos2];
1779 
1780                 avg = 0.5 * (value1 + value2);
1781                 result.buffer[pos1] = avg;
1782                 result.buffer[pos2] = avg;
1783             }
1784         }
1785     }
1786 
1787     /**
1788      * Symmetrizes this instance and returns the result as a new matrix
1789      * instance.
1790      * Symmetrization is done by averaging this instance with its transpose
1791      * (i.e. S = (M+M')/2
1792      *
1793      * @return a new symmetrizes version of this instance.
1794      * @throws WrongSizeException if this instance is not square.
1795      */
1796     public Matrix symmetrizeAndReturnNew() throws WrongSizeException {
1797         final var m = new Matrix(rows, columns);
1798         symmetrize(m);
1799         return m;
1800     }
1801 
1802     /**
1803      * Symmetrizes this instance and updates it with computed value.
1804      * Symmetrization is done by averaging this instance with its transpose
1805      * (i.e. S = (M+M')/2
1806      *
1807      * @throws WrongSizeException if this instance is not square.
1808      */
1809     public void symmetrize() throws WrongSizeException {
1810         symmetrize(this);
1811     }
1812 
1813     /**
1814      * Method to internally add two matrices.
1815      *
1816      * @param other  Matrix to be added to current matrix
1817      * @param result Matrix where result will be stored.
1818      */
1819     private void internalAdd(final Matrix other, final Matrix result) {
1820         final var length = rows * columns;
1821         for (var i = 0; i < length; i++) {
1822             result.buffer[i] = buffer[i] + other.buffer[i];
1823         }
1824     }
1825 
1826     /**
1827      * Method to internally subtract two matrices.
1828      *
1829      * @param other  Matrix to be subtracted from current matrix.
1830      * @param result Matrix where result will be stored.
1831      */
1832     private void internalSubtract(final Matrix other, final Matrix result) {
1833         final var length = rows * columns;
1834         for (var i = 0; i < length; i++) {
1835             result.buffer[i] = buffer[i] - other.buffer[i];
1836         }
1837     }
1838 
1839     /**
1840      * Method to internally multiply two matrices.
1841      *
1842      * @param other             Matrix to be multiplied to current matrix
1843      * @param resultBuffer      Matrix buffer of data where result will be stored.
1844      * @param resultColumnIndex Array of matrix column indices where result will
1845      *                          be stored.
1846      */
1847     private void internalMultiply(
1848             final Matrix other, final double[] resultBuffer, final int[] resultColumnIndex) {
1849         final var columns2 = other.columns;
1850         double value;
1851         for (var k = 0; k < columns2; k++) {
1852             for (var j = 0; j < rows; j++) {
1853                 value = 0.0;
1854                 for (var i = 0; i < columns; i++) {
1855                     value += buffer[columnIndex[i] + j] *
1856                             other.buffer[other.columnIndex[k] + i];
1857                 }
1858                 resultBuffer[resultColumnIndex[k] + j] = value;
1859             }
1860         }
1861     }
1862 
1863     /**
1864      * Method to internally multiply two matrices.
1865      *
1866      * @param other  Matrix to be multiplied to current matrix.
1867      * @param result Matrix where result will be stored.
1868      */
1869     private void internalMultiply(final Matrix other, final Matrix result) {
1870         internalMultiply(other, result.buffer, result.columnIndex);
1871     }
1872 
1873     /**
1874      * Method to internally compute the Kronecker product between two matrices.
1875      *
1876      * @param other             other matrix to be Kronecker multiplied to current matrix.
1877      * @param resultBuffer      matrix buffer of data where result will be stored.
1878      * @param resultColumnIndex array of matrix column indices where result will
1879      *                          be stored.
1880      */
1881     private void internalMultiplyKronecker(
1882             final Matrix other, final double[] resultBuffer, final int[] resultColumnIndex) {
1883         final var rows2 = other.rows;
1884         final var columns2 = other.columns;
1885 
1886         for (var j1 = 0; j1 < rows; j1++) {
1887             final var startJ3 = j1 * other.rows;
1888             for (var i1 = 0; i1 < columns; i1++) {
1889                 final var startI3 = i1 * other.columns;
1890                 final var value1 = buffer[columnIndex[i1] + j1];
1891 
1892                 for (var j2 = 0; j2 < rows2; j2++) {
1893                     final var j3 = startJ3 + j2;
1894                     for (var i2 = 0; i2 < columns2; i2++) {
1895                         final var i3 = startI3 + i2;
1896                         final var value2 = other.buffer[other.columnIndex[i2] + j2];
1897 
1898                         final var value3 = value1 * value2;
1899                         resultBuffer[resultColumnIndex[i3] + j3] = value3;
1900                     }
1901                 }
1902             }
1903         }
1904     }
1905 
1906     /**
1907      * Method to internally compute the Kronecker product between two matrices.
1908      *
1909      * @param other  other matrix to be Kronecker multiplied to current matrix.
1910      * @param result matrix where result will be stored.
1911      */
1912     private void internalMultiplyKronecker(final Matrix other, final Matrix result) {
1913         internalMultiplyKronecker(other, result.buffer, result.columnIndex);
1914     }
1915 
1916     /**
1917      * Method to internally compute element by element product of two matrices.
1918      *
1919      * @param other  Matrix to be element by element multiplied to current matrix
1920      * @param result Matrix where result will be stored.
1921      */
1922     private void internalElementByElementProduct(final Matrix other, final Matrix result) {
1923         final var length = rows * columns;
1924         for (var i = 0; i < length; i++) {
1925             result.buffer[i] = buffer[i] * other.buffer[i];
1926         }
1927     }
1928 
1929     /**
1930      * Method to internally compute matrix transposition.
1931      *
1932      * @param resultBuffer      Buffer where transposed matrix data is stored.
1933      * @param resultColumnIndex Buffer where indices of transposed matrix data
1934      *                          is stored.
1935      */
1936     private void internalTranspose(final double[] resultBuffer, final int[] resultColumnIndex) {
1937         for (var j = 0; j < rows; j++) {
1938             for (var i = 0; i < columns; i++) {
1939                 resultBuffer[resultColumnIndex[j] + i] = buffer[columnIndex[i] + j];
1940             }
1941         }
1942     }
1943 
1944     /**
1945      * Method to internally compute matrix transposition.
1946      *
1947      * @param result Matrix where transposed data is stored.
1948      */
1949     private void internalTranspose(final Matrix result) {
1950         internalTranspose(result.buffer, result.columnIndex);
1951     }
1952 
1953     /**
1954      * Method used internally to remove matrix contents and resizing it.
1955      *
1956      * @param rows    Number of rows to be set
1957      * @param columns Number of columns to be set.
1958      * @throws WrongSizeException Exception raised if either rows or
1959      *                            columns is zero.
1960      */
1961     private void internalResize(final int rows, final int columns) throws WrongSizeException {
1962         if (rows == 0 || columns == 0) {
1963             throw new WrongSizeException();
1964         }
1965 
1966         this.rows = rows;
1967         this.columns = columns;
1968 
1969         // instantiate buffers of data
1970         buffer = new double[rows * columns];
1971         columnIndex = new int[columns];
1972 
1973         // initialize column index
1974         var counter = 0;
1975         for (var i = 0; i < columns; i++) {
1976             columnIndex[i] = counter;
1977             counter += rows;
1978         }
1979     }
1980 
1981     /**
1982      * Internal method to retrieve a sub-matrix of current matrix instance.
1983      * Sub-matrix is obtained by copying all elements contained within provided
1984      * coordinates (both top-left and bottom-right points are included within
1985      * sub-matrix).
1986      *
1987      * @param topLeftRow        Top-left row index where sub-matrix starts.
1988      * @param topLeftColumn     Top-left column index where sub-matrix starts.
1989      * @param bottomRightRow    Bottom-right row index where sub-matrix ends.
1990      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
1991      * @param result            Instance where sub-matrix data is stored.
1992      */
1993     private void internalGetSubmatrix(
1994             final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn,
1995             final Matrix result) {
1996         var i2 = 0;
1997         var j2 = 0;
1998         for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
1999             for (var i = topLeftRow; i <= bottomRightRow; i++) {
2000                 result.buffer[result.columnIndex[j2] + i2] = buffer[columnIndex[j] + i];
2001                 i2++;
2002             }
2003             i2 = 0;
2004             j2++;
2005         }
2006     }
2007 
2008     /**
2009      * Internal method to retrieve a sub-matrix of current matrix instance as an
2010      * array of values using provided column order and storing the result in
2011      * provided array.
2012      * Sub-matrix is obtained by copying all elements contained within provided
2013      * coordinates (both top-left and bottom-right points are included within
2014      * sub-matrix).
2015      *
2016      * @param topLeftRow        Top-left row index where sub-matrix starts
2017      * @param topLeftColumn     Top-left column index where sub-matrix starts
2018      * @param bottomRightRow    Bottom-right row index where sub-matrix ends
2019      * @param bottomRightColumn Bottom-right column index where sub-matrix ends.
2020      * @param isColumnOrder     If true, picks elements from matrix using column
2021      *                          order, otherwise row order is used.
2022      * @param result            Array where sub-matrix data is stored.
2023      */
2024     private void internalGetSubmatrixAsArray(
2025             final int topLeftRow, final int topLeftColumn, final int bottomRightRow,
2026             final int bottomRightColumn, final boolean isColumnOrder, final double[] result) {
2027         var counter = 0;
2028         if (isColumnOrder) {
2029             for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
2030                 for (var i = topLeftRow; i <= bottomRightRow; i++) {
2031                     result[counter] = buffer[columnIndex[j] + i];
2032                     counter++;
2033                 }
2034             }
2035         } else {
2036             for (var i = topLeftRow; i <= bottomRightRow; i++) {
2037                 for (var j = topLeftColumn; j <= bottomRightColumn; j++) {
2038                     result[counter] = buffer[columnIndex[j] + i];
2039                     counter++;
2040                 }
2041             }
2042         }
2043     }
2044 }