View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math.linear;
19  import java.io.Serializable;
20  import java.math.BigDecimal;
21  
22  import org.apache.commons.math.MathRuntimeException;
23  import org.apache.commons.math.exception.util.LocalizedFormats;
24  
25  /**
26   * Implementation of {@link BigMatrix} using a BigDecimal[][] array to store entries
27   * and <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
28   * LU decompostion</a> to support linear system
29   * solution and inverse.
30   * <p>
31   * The LU decompostion is performed as needed, to support the following operations: <ul>
32   * <li>solve</li>
33   * <li>isSingular</li>
34   * <li>getDeterminant</li>
35   * <li>inverse</li> </ul></p>
36   * <p>
37  * <strong>Usage notes</strong>:<br>
38   * <ul><li>
39   * The LU decomposition is stored and reused on subsequent calls.  If matrix
40   * data are modified using any of the public setXxx methods, the saved
41   * decomposition is discarded.  If data are modified via references to the
42   * underlying array obtained using <code>getDataRef()</code>, then the stored
43   * LU decomposition will not be discarded.  In this case, you need to
44   * explicitly invoke <code>LUDecompose()</code> to recompute the decomposition
45   * before using any of the methods above.</li>
46   * <li>
47   * As specified in the {@link BigMatrix} interface, matrix element indexing
48   * is 0-based -- e.g., <code>getEntry(0, 0)</code>
49   * returns the element in the first row, first column of the matrix.</li></ul></p>
50   *
51   * @deprecated as of 2.0, replaced by {@link Array2DRowFieldMatrix} with a {@link
52   * org.apache.commons.math.util.BigReal} parameter
53   * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $
54   */
55  @Deprecated
56  public class BigMatrixImpl implements BigMatrix, Serializable {
57  
58      /** BigDecimal 0 */
59      static final BigDecimal ZERO = new BigDecimal(0);
60  
61      /** BigDecimal 1 */
62      static final BigDecimal ONE = new BigDecimal(1);
63  
64      /** Bound to determine effective singularity in LU decomposition */
65      private static final BigDecimal TOO_SMALL = new BigDecimal(10E-12);
66  
67      /** Serialization id */
68      private static final long serialVersionUID = -1011428905656140431L;
69  
70      /** Entries of the matrix */
71      protected BigDecimal data[][] = null;
72  
73      /** Entries of cached LU decomposition.
74       *  All updates to data (other than luDecompose()) *must* set this to null
75       */
76      protected BigDecimal lu[][] = null;
77  
78      /** Permutation associated with LU decomposition */
79      protected int[] permutation = null;
80  
81      /** Parity of the permutation associated with the LU decomposition */
82      protected int parity = 1;
83  
84      /** Rounding mode for divisions **/
85      private int roundingMode = BigDecimal.ROUND_HALF_UP;
86  
87      /*** BigDecimal scale ***/
88      private int scale = 64;
89  
90      /**
91       * Creates a matrix with no data
92       */
93      public BigMatrixImpl() {
94      }
95  
96      /**
97       * Create a new BigMatrix with the supplied row and column dimensions.
98       *
99       * @param rowDimension      the number of rows in the new matrix
100      * @param columnDimension   the number of columns in the new matrix
101      * @throws IllegalArgumentException if row or column dimension is not
102      *  positive
103      */
104     public BigMatrixImpl(int rowDimension, int columnDimension) {
105         if (rowDimension < 1 ) {
106             throw MathRuntimeException.createIllegalArgumentException(
107                     LocalizedFormats.INSUFFICIENT_DIMENSION, rowDimension, 1);
108         }
109         if (columnDimension < 1) {
110             throw MathRuntimeException.createIllegalArgumentException(
111                     LocalizedFormats.INSUFFICIENT_DIMENSION, columnDimension, 1);
112         }
113         data = new BigDecimal[rowDimension][columnDimension];
114         lu = null;
115     }
116 
117     /**
118      * Create a new BigMatrix using <code>d</code> as the underlying
119      * data array.
120      * <p>The input array is copied, not referenced. This constructor has
121      * the same effect as calling {@link #BigMatrixImpl(BigDecimal[][], boolean)}
122      * with the second argument set to <code>true</code>.</p>
123      *
124      * @param d data for new matrix
125      * @throws IllegalArgumentException if <code>d</code> is not rectangular
126      *  (not all rows have the same length) or empty
127      * @throws NullPointerException if <code>d</code> is null
128      */
129     public BigMatrixImpl(BigDecimal[][] d) {
130         this.copyIn(d);
131         lu = null;
132     }
133 
134     /**
135      * Create a new BigMatrix using the input array as the underlying
136      * data array.
137      * <p>If an array is built specially in order to be embedded in a
138      * BigMatrix and not used directly, the <code>copyArray</code> may be
139      * set to <code>false</code. This will prevent the copying and improve
140      * performance as no new array will be built and no data will be copied.</p>
141      * @param d data for new matrix
142      * @param copyArray if true, the input array will be copied, otherwise
143      * it will be referenced
144      * @throws IllegalArgumentException if <code>d</code> is not rectangular
145      *  (not all rows have the same length) or empty
146      * @throws NullPointerException if <code>d</code> is null
147      * @see #BigMatrixImpl(BigDecimal[][])
148      */
149     public BigMatrixImpl(BigDecimal[][] d, boolean copyArray) {
150         if (copyArray) {
151             copyIn(d);
152         } else {
153             if (d == null) {
154                 throw new NullPointerException();
155             }
156             final int nRows = d.length;
157             if (nRows == 0) {
158                 throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
159             }
160 
161             final int nCols = d[0].length;
162             if (nCols == 0) {
163                 throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
164             }
165             for (int r = 1; r < nRows; r++) {
166                 if (d[r].length != nCols) {
167                     throw MathRuntimeException.createIllegalArgumentException(
168                           LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
169                           nCols, d[r].length);
170                 }
171             }
172             data = d;
173         }
174         lu = null;
175     }
176 
177     /**
178      * Create a new BigMatrix using <code>d</code> as the underlying
179      * data array.
180      * <p>Since the underlying array will hold <code>BigDecimal</code>
181      * instances, it will be created.</p>
182      *
183      * @param d data for new matrix
184      * @throws IllegalArgumentException if <code>d</code> is not rectangular
185      *  (not all rows have the same length) or empty
186      * @throws NullPointerException if <code>d</code> is null
187      */
188     public BigMatrixImpl(double[][] d) {
189         final int nRows = d.length;
190         if (nRows == 0) {
191             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
192         }
193 
194         final int nCols = d[0].length;
195         if (nCols == 0) {
196             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
197         }
198         for (int row = 1; row < nRows; row++) {
199             if (d[row].length != nCols) {
200                 throw MathRuntimeException.createIllegalArgumentException(
201                       LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
202                       nCols, d[row].length);
203             }
204         }
205         this.copyIn(d);
206         lu = null;
207     }
208 
209     /**
210      * Create a new BigMatrix using the values represented by the strings in
211      * <code>d</code> as the underlying data array.
212      *
213      * @param d data for new matrix
214      * @throws IllegalArgumentException if <code>d</code> is not rectangular
215      *  (not all rows have the same length) or empty
216      * @throws NullPointerException if <code>d</code> is null
217      */
218     public BigMatrixImpl(String[][] d) {
219         final int nRows = d.length;
220         if (nRows == 0) {
221             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
222         }
223 
224         final int nCols = d[0].length;
225         if (nCols == 0) {
226             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
227         }
228         for (int row = 1; row < nRows; row++) {
229             if (d[row].length != nCols) {
230                 throw MathRuntimeException.createIllegalArgumentException(
231                       LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
232                       nCols, d[row].length);
233             }
234         }
235         this.copyIn(d);
236         lu = null;
237     }
238 
239     /**
240      * Create a new (column) BigMatrix using <code>v</code> as the
241      * data for the unique column of the <code>v.length x 1</code> matrix
242      * created.
243      * <p>
244      * The input array is copied, not referenced.</p>
245      *
246      * @param v column vector holding data for new matrix
247      */
248     public BigMatrixImpl(BigDecimal[] v) {
249         final int nRows = v.length;
250         data = new BigDecimal[nRows][1];
251         for (int row = 0; row < nRows; row++) {
252             data[row][0] = v[row];
253         }
254     }
255 
256     /**
257      * Create a new BigMatrix which is a copy of this.
258      *
259      * @return  the cloned matrix
260      */
261     public BigMatrix copy() {
262         return new BigMatrixImpl(this.copyOut(), false);
263     }
264 
265     /**
266      * Compute the sum of this and <code>m</code>.
267      *
268      * @param m    matrix to be added
269      * @return     this + m
270      * @throws  IllegalArgumentException if m is not the same size as this
271      */
272     public BigMatrix add(BigMatrix m) throws IllegalArgumentException {
273         try {
274             return add((BigMatrixImpl) m);
275         } catch (ClassCastException cce) {
276 
277             // safety check
278             MatrixUtils.checkAdditionCompatible(this, m);
279 
280             final int rowCount    = getRowDimension();
281             final int columnCount = getColumnDimension();
282             final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
283             for (int row = 0; row < rowCount; row++) {
284                 final BigDecimal[] dataRow    = data[row];
285                 final BigDecimal[] outDataRow = outData[row];
286                 for (int col = 0; col < columnCount; col++) {
287                     outDataRow[col] = dataRow[col].add(m.getEntry(row, col));
288                 }
289             }
290             return new BigMatrixImpl(outData, false);
291         }
292     }
293 
294     /**
295      * Compute the sum of this and <code>m</code>.
296      *
297      * @param m    matrix to be added
298      * @return     this + m
299      * @throws  IllegalArgumentException if m is not the same size as this
300      */
301     public BigMatrixImpl add(BigMatrixImpl m) throws IllegalArgumentException {
302 
303         // safety check
304         MatrixUtils.checkAdditionCompatible(this, m);
305 
306         final int rowCount    = getRowDimension();
307         final int columnCount = getColumnDimension();
308         final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
309         for (int row = 0; row < rowCount; row++) {
310             final BigDecimal[] dataRow    = data[row];
311             final BigDecimal[] mRow       = m.data[row];
312             final BigDecimal[] outDataRow = outData[row];
313             for (int col = 0; col < columnCount; col++) {
314                 outDataRow[col] = dataRow[col].add(mRow[col]);
315             }
316         }
317         return new BigMatrixImpl(outData, false);
318     }
319 
320     /**
321      * Compute  this minus <code>m</code>.
322      *
323      * @param m    matrix to be subtracted
324      * @return     this + m
325      * @throws  IllegalArgumentException if m is not the same size as this
326      */
327     public BigMatrix subtract(BigMatrix m) throws IllegalArgumentException {
328         try {
329             return subtract((BigMatrixImpl) m);
330         } catch (ClassCastException cce) {
331 
332             // safety check
333             MatrixUtils.checkSubtractionCompatible(this, m);
334 
335             final int rowCount    = getRowDimension();
336             final int columnCount = getColumnDimension();
337             final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
338             for (int row = 0; row < rowCount; row++) {
339                 final BigDecimal[] dataRow    = data[row];
340                 final BigDecimal[] outDataRow = outData[row];
341                 for (int col = 0; col < columnCount; col++) {
342                     outDataRow[col] = dataRow[col].subtract(getEntry(row, col));
343                 }
344             }
345             return new BigMatrixImpl(outData, false);
346         }
347     }
348 
349     /**
350      * Compute  this minus <code>m</code>.
351      *
352      * @param m    matrix to be subtracted
353      * @return     this + m
354      * @throws  IllegalArgumentException if m is not the same size as this
355      */
356     public BigMatrixImpl subtract(BigMatrixImpl m) throws IllegalArgumentException {
357 
358         // safety check
359         MatrixUtils.checkSubtractionCompatible(this, m);
360 
361         final int rowCount    = getRowDimension();
362         final int columnCount = getColumnDimension();
363         final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
364         for (int row = 0; row < rowCount; row++) {
365             final BigDecimal[] dataRow    = data[row];
366             final BigDecimal[] mRow       = m.data[row];
367             final BigDecimal[] outDataRow = outData[row];
368             for (int col = 0; col < columnCount; col++) {
369                 outDataRow[col] = dataRow[col].subtract(mRow[col]);
370             }
371         }
372         return new BigMatrixImpl(outData, false);
373     }
374 
375     /**
376      * Returns the result of adding d to each entry of this.
377      *
378      * @param d    value to be added to each entry
379      * @return     d + this
380      */
381     public BigMatrix scalarAdd(BigDecimal d) {
382         final int rowCount    = getRowDimension();
383         final int columnCount = getColumnDimension();
384         final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
385         for (int row = 0; row < rowCount; row++) {
386             final BigDecimal[] dataRow    = data[row];
387             final BigDecimal[] outDataRow = outData[row];
388             for (int col = 0; col < columnCount; col++) {
389                 outDataRow[col] = dataRow[col].add(d);
390             }
391         }
392         return new BigMatrixImpl(outData, false);
393     }
394 
395     /**
396      * Returns the result of multiplying each entry of this by <code>d</code>
397      * @param d  value to multiply all entries by
398      * @return d * this
399      */
400     public BigMatrix scalarMultiply(BigDecimal d) {
401         final int rowCount    = getRowDimension();
402         final int columnCount = getColumnDimension();
403         final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount];
404         for (int row = 0; row < rowCount; row++) {
405             final BigDecimal[] dataRow    = data[row];
406             final BigDecimal[] outDataRow = outData[row];
407             for (int col = 0; col < columnCount; col++) {
408                 outDataRow[col] = dataRow[col].multiply(d);
409             }
410         }
411         return new BigMatrixImpl(outData, false);
412     }
413 
414     /**
415      * Returns the result of postmultiplying this by <code>m</code>.
416      * @param m    matrix to postmultiply by
417      * @return     this*m
418      * @throws     IllegalArgumentException
419      *             if columnDimension(this) != rowDimension(m)
420      */
421     public BigMatrix multiply(BigMatrix m) throws IllegalArgumentException {
422         try {
423             return multiply((BigMatrixImpl) m);
424         } catch (ClassCastException cce) {
425 
426             // safety check
427             MatrixUtils.checkMultiplicationCompatible(this, m);
428 
429             final int nRows = this.getRowDimension();
430             final int nCols = m.getColumnDimension();
431             final int nSum = this.getColumnDimension();
432             final BigDecimal[][] outData = new BigDecimal[nRows][nCols];
433             for (int row = 0; row < nRows; row++) {
434                 final BigDecimal[] dataRow    = data[row];
435                 final BigDecimal[] outDataRow = outData[row];
436                 for (int col = 0; col < nCols; col++) {
437                     BigDecimal sum = ZERO;
438                     for (int i = 0; i < nSum; i++) {
439                         sum = sum.add(dataRow[i].multiply(m.getEntry(i, col)));
440                     }
441                     outDataRow[col] = sum;
442                 }
443             }
444             return new BigMatrixImpl(outData, false);
445         }
446     }
447 
448     /**
449      * Returns the result of postmultiplying this by <code>m</code>.
450      * @param m    matrix to postmultiply by
451      * @return     this*m
452      * @throws     IllegalArgumentException
453      *             if columnDimension(this) != rowDimension(m)
454      */
455     public BigMatrixImpl multiply(BigMatrixImpl m) throws IllegalArgumentException {
456 
457         // safety check
458         MatrixUtils.checkMultiplicationCompatible(this, m);
459 
460         final int nRows = this.getRowDimension();
461         final int nCols = m.getColumnDimension();
462         final int nSum = this.getColumnDimension();
463         final BigDecimal[][] outData = new BigDecimal[nRows][nCols];
464         for (int row = 0; row < nRows; row++) {
465             final BigDecimal[] dataRow    = data[row];
466             final BigDecimal[] outDataRow = outData[row];
467             for (int col = 0; col < nCols; col++) {
468                 BigDecimal sum = ZERO;
469                 for (int i = 0; i < nSum; i++) {
470                     sum = sum.add(dataRow[i].multiply(m.data[i][col]));
471                 }
472                 outDataRow[col] = sum;
473             }
474         }
475         return new BigMatrixImpl(outData, false);
476     }
477 
478     /**
479      * Returns the result premultiplying this by <code>m</code>.
480      * @param m    matrix to premultiply by
481      * @return     m * this
482      * @throws     IllegalArgumentException
483      *             if rowDimension(this) != columnDimension(m)
484      */
485     public BigMatrix preMultiply(BigMatrix m) throws IllegalArgumentException {
486         return m.multiply(this);
487     }
488 
489     /**
490      * Returns matrix entries as a two-dimensional array.
491      * <p>
492      * Makes a fresh copy of the underlying data.</p>
493      *
494      * @return    2-dimensional array of entries
495      */
496     public BigDecimal[][] getData() {
497         return copyOut();
498     }
499 
500     /**
501      * Returns matrix entries as a two-dimensional array.
502      * <p>
503      * Makes a fresh copy of the underlying data converted to
504      * <code>double</code> values.</p>
505      *
506      * @return    2-dimensional array of entries
507      */
508     public double[][] getDataAsDoubleArray() {
509         final int nRows = getRowDimension();
510         final int nCols = getColumnDimension();
511         final double d[][] = new double[nRows][nCols];
512         for (int i = 0; i < nRows; i++) {
513             for (int j = 0; j < nCols; j++) {
514                 d[i][j] = data[i][j].doubleValue();
515             }
516         }
517         return d;
518     }
519 
520     /**
521      * Returns a reference to the underlying data array.
522      * <p>
523      * Does not make a fresh copy of the underlying data.</p>
524      *
525      * @return 2-dimensional array of entries
526      */
527     public BigDecimal[][] getDataRef() {
528         return data;
529     }
530 
531     /***
532      * Gets the rounding mode for division operations
533      * The default is {@link java.math.BigDecimal#ROUND_HALF_UP}
534      * @see BigDecimal
535      * @return the rounding mode.
536      */
537     public int getRoundingMode() {
538         return roundingMode;
539     }
540 
541     /***
542      * Sets the rounding mode for decimal divisions.
543      * @see BigDecimal
544      * @param roundingMode rounding mode for decimal divisions
545      */
546     public void setRoundingMode(int roundingMode) {
547         this.roundingMode = roundingMode;
548     }
549 
550     /***
551      * Sets the scale for division operations.
552      * The default is 64
553      * @see BigDecimal
554      * @return the scale
555      */
556     public int getScale() {
557         return scale;
558     }
559 
560     /***
561      * Sets the scale for division operations.
562      * @see BigDecimal
563      * @param scale scale for division operations
564      */
565     public void setScale(int scale) {
566         this.scale = scale;
567     }
568 
569     /**
570      * Returns the <a href="http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html">
571      * maximum absolute row sum norm</a> of the matrix.
572      *
573      * @return norm
574      */
575     public BigDecimal getNorm() {
576         BigDecimal maxColSum = ZERO;
577         for (int col = 0; col < this.getColumnDimension(); col++) {
578             BigDecimal sum = ZERO;
579             for (int row = 0; row < this.getRowDimension(); row++) {
580                 sum = sum.add(data[row][col].abs());
581             }
582             maxColSum = maxColSum.max(sum);
583         }
584         return maxColSum;
585     }
586 
587     /**
588      * Gets a submatrix. Rows and columns are indicated
589      * counting from 0 to n-1.
590      *
591      * @param startRow Initial row index
592      * @param endRow Final row index
593      * @param startColumn Initial column index
594      * @param endColumn Final column index
595      * @return The subMatrix containing the data of the
596      *         specified rows and columns
597      * @exception MatrixIndexException if row or column selections are not valid
598      */
599     public BigMatrix getSubMatrix(int startRow, int endRow,
600                                   int startColumn, int endColumn)
601         throws MatrixIndexException {
602 
603         MatrixUtils.checkRowIndex(this, startRow);
604         MatrixUtils.checkRowIndex(this, endRow);
605         if (startRow > endRow) {
606             throw new MatrixIndexException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
607                                            startRow, endRow);
608         }
609 
610         MatrixUtils.checkColumnIndex(this, startColumn);
611         MatrixUtils.checkColumnIndex(this, endColumn);
612         if (startColumn > endColumn) {
613             throw new MatrixIndexException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
614                                            startColumn, endColumn);
615         }
616 
617         final BigDecimal[][] subMatrixData =
618             new BigDecimal[endRow - startRow + 1][endColumn - startColumn + 1];
619         for (int i = startRow; i <= endRow; i++) {
620             System.arraycopy(data[i], startColumn,
621                              subMatrixData[i - startRow], 0,
622                              endColumn - startColumn + 1);
623         }
624 
625         return new BigMatrixImpl(subMatrixData, false);
626 
627     }
628 
629     /**
630      * Gets a submatrix. Rows and columns are indicated
631      * counting from 0 to n-1.
632      *
633      * @param selectedRows Array of row indices must be non-empty
634      * @param selectedColumns Array of column indices must be non-empty
635      * @return The subMatrix containing the data in the
636      *     specified rows and columns
637      * @exception MatrixIndexException  if supplied row or column index arrays
638      *     are not valid
639      */
640     public BigMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
641         throws MatrixIndexException {
642 
643         if (selectedRows.length * selectedColumns.length == 0) {
644             if (selectedRows.length == 0) {
645                 throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
646             }
647             throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY);
648         }
649 
650         final BigDecimal[][] subMatrixData =
651             new BigDecimal[selectedRows.length][selectedColumns.length];
652         try  {
653             for (int i = 0; i < selectedRows.length; i++) {
654                 final BigDecimal[] subI = subMatrixData[i];
655                 final BigDecimal[] dataSelectedI = data[selectedRows[i]];
656                 for (int j = 0; j < selectedColumns.length; j++) {
657                     subI[j] = dataSelectedI[selectedColumns[j]];
658                 }
659             }
660         } catch (ArrayIndexOutOfBoundsException e) {
661             // we redo the loop with checks enabled
662             // in order to generate an appropriate message
663             for (final int row : selectedRows) {
664                 MatrixUtils.checkRowIndex(this, row);
665             }
666             for (final int column : selectedColumns) {
667                 MatrixUtils.checkColumnIndex(this, column);
668             }
669         }
670         return new BigMatrixImpl(subMatrixData, false);
671     }
672 
673     /**
674      * Replace the submatrix starting at <code>row, column</code> using data in
675      * the input <code>subMatrix</code> array. Indexes are 0-based.
676      * <p>
677      * Example:<br>
678      * Starting with <pre>
679      * 1  2  3  4
680      * 5  6  7  8
681      * 9  0  1  2
682      * </pre>
683      * and <code>subMatrix = {{3, 4} {5,6}}</code>, invoking
684      * <code>setSubMatrix(subMatrix,1,1))</code> will result in <pre>
685      * 1  2  3  4
686      * 5  3  4  8
687      * 9  5  6  2
688      * </pre></p>
689      *
690      * @param subMatrix  array containing the submatrix replacement data
691      * @param row  row coordinate of the top, left element to be replaced
692      * @param column  column coordinate of the top, left element to be replaced
693      * @throws MatrixIndexException  if subMatrix does not fit into this
694      *    matrix from element in (row, column)
695      * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular
696      *  (not all rows have the same length) or empty
697      * @throws NullPointerException if <code>subMatrix</code> is null
698      * @since 1.1
699      */
700     public void setSubMatrix(BigDecimal[][] subMatrix, int row, int column)
701     throws MatrixIndexException {
702 
703         final int nRows = subMatrix.length;
704         if (nRows == 0) {
705             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW);
706         }
707 
708         final int nCols = subMatrix[0].length;
709         if (nCols == 0) {
710             throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
711         }
712 
713         for (int r = 1; r < nRows; r++) {
714             if (subMatrix[r].length != nCols) {
715                 throw MathRuntimeException.createIllegalArgumentException(
716                       LocalizedFormats.DIFFERENT_ROWS_LENGTHS,
717                       nCols, subMatrix[r].length);
718             }
719         }
720 
721         if (data == null) {
722             if (row > 0) {
723                 throw MathRuntimeException.createIllegalStateException(
724                         LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET,
725                         row);
726             }
727             if (column > 0) {
728                 throw MathRuntimeException.createIllegalStateException(
729                         LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET,
730                         column);
731             }
732             data = new BigDecimal[nRows][nCols];
733             System.arraycopy(subMatrix, 0, data, 0, subMatrix.length);
734         } else {
735             MatrixUtils.checkRowIndex(this, row);
736             MatrixUtils.checkColumnIndex(this, column);
737             MatrixUtils.checkRowIndex(this, nRows + row - 1);
738             MatrixUtils.checkColumnIndex(this, nCols + column - 1);
739         }
740         for (int i = 0; i < nRows; i++) {
741             System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols);
742         }
743 
744         lu = null;
745 
746     }
747 
748     /**
749      * Returns the entries in row number <code>row</code>
750      * as a row matrix.  Row indices start at 0.
751      *
752      * @param row the row to be fetched
753      * @return row matrix
754      * @throws MatrixIndexException if the specified row index is invalid
755      */
756     public BigMatrix getRowMatrix(int row) throws MatrixIndexException {
757         MatrixUtils.checkRowIndex(this, row);
758         final int ncols = this.getColumnDimension();
759         final BigDecimal[][] out = new BigDecimal[1][ncols];
760         System.arraycopy(data[row], 0, out[0], 0, ncols);
761         return new BigMatrixImpl(out, false);
762     }
763 
764     /**
765      * Returns the entries in column number <code>column</code>
766      * as a column matrix.  Column indices start at 0.
767      *
768      * @param column the column to be fetched
769      * @return column matrix
770      * @throws MatrixIndexException if the specified column index is invalid
771      */
772     public BigMatrix getColumnMatrix(int column) throws MatrixIndexException {
773         MatrixUtils.checkColumnIndex(this, column);
774         final int nRows = this.getRowDimension();
775         final BigDecimal[][] out = new BigDecimal[nRows][1];
776         for (int row = 0; row < nRows; row++) {
777             out[row][0] = data[row][column];
778         }
779         return new BigMatrixImpl(out, false);
780     }
781 
782     /**
783      * Returns the entries in row number <code>row</code> as an array.
784      * <p>
785      * Row indices start at 0.  A <code>MatrixIndexException</code> is thrown
786      * unless <code>0 <= row < rowDimension.</code></p>
787      *
788      * @param row the row to be fetched
789      * @return array of entries in the row
790      * @throws MatrixIndexException if the specified row index is not valid
791      */
792     public BigDecimal[] getRow(int row) throws MatrixIndexException {
793         MatrixUtils.checkRowIndex(this, row);
794         final int ncols = this.getColumnDimension();
795         final BigDecimal[] out = new BigDecimal[ncols];
796         System.arraycopy(data[row], 0, out, 0, ncols);
797         return out;
798     }
799 
800      /**
801      * Returns the entries in row number <code>row</code> as an array
802      * of double values.
803      * <p>
804      * Row indices start at 0.  A <code>MatrixIndexException</code> is thrown
805      * unless <code>0 <= row < rowDimension.</code></p>
806      *
807      * @param row the row to be fetched
808      * @return array of entries in the row
809      * @throws MatrixIndexException if the specified row index is not valid
810      */
811     public double[] getRowAsDoubleArray(int row) throws MatrixIndexException {
812         MatrixUtils.checkRowIndex(this, row);
813         final int ncols = this.getColumnDimension();
814         final double[] out = new double[ncols];
815         for (int i=0;i<ncols;i++) {
816             out[i] = data[row][i].doubleValue();
817         }
818         return out;
819     }
820 
821      /**
822      * Returns the entries in column number <code>col</code> as an array.
823      * <p>
824      * Column indices start at 0.  A <code>MatrixIndexException</code> is thrown
825      * unless <code>0 <= column < columnDimension.</code></p>
826      *
827      * @param col the column to be fetched
828      * @return array of entries in the column
829      * @throws MatrixIndexException if the specified column index is not valid
830      */
831     public BigDecimal[] getColumn(int col) throws MatrixIndexException {
832         MatrixUtils.checkColumnIndex(this, col);
833         final int nRows = this.getRowDimension();
834         final BigDecimal[] out = new BigDecimal[nRows];
835         for (int i = 0; i < nRows; i++) {
836             out[i] = data[i][col];
837         }
838         return out;
839     }
840 
841     /**
842      * Returns the entries in column number <code>col</code> as an array
843      * of double values.
844      * <p>
845      * Column indices start at 0.  A <code>MatrixIndexException</code> is thrown
846      * unless <code>0 <= column < columnDimension.</code></p>
847      *
848      * @param col the column to be fetched
849      * @return array of entries in the column
850      * @throws MatrixIndexException if the specified column index is not valid
851      */
852     public double[] getColumnAsDoubleArray(int col) throws MatrixIndexException {
853         MatrixUtils.checkColumnIndex(this, col);
854         final int nrows = this.getRowDimension();
855         final double[] out = new double[nrows];
856         for (int i=0;i<nrows;i++) {
857             out[i] = data[i][col].doubleValue();
858         }
859         return out;
860     }
861 
862      /**
863      * Returns the entry in the specified row and column.
864      * <p>
865      * Row and column indices start at 0 and must satisfy
866      * <ul>
867      * <li><code>0 <= row < rowDimension</code></li>
868      * <li><code> 0 <= column < columnDimension</code></li>
869      * </ul>
870      * otherwise a <code>MatrixIndexException</code> is thrown.</p>
871      *
872      * @param row  row location of entry to be fetched
873      * @param column  column location of entry to be fetched
874      * @return matrix entry in row,column
875      * @throws MatrixIndexException if the row or column index is not valid
876      */
877     public BigDecimal getEntry(int row, int column)
878     throws MatrixIndexException {
879         try {
880             return data[row][column];
881         } catch (ArrayIndexOutOfBoundsException e) {
882             throw new MatrixIndexException(
883                     LocalizedFormats.NO_SUCH_MATRIX_ENTRY,
884                     row, column, getRowDimension(), getColumnDimension());
885         }
886     }
887 
888     /**
889      * Returns the entry in the specified row and column as a double.
890      * <p>
891      * Row and column indices start at 0 and must satisfy
892      * <ul>
893      * <li><code>0 <= row < rowDimension</code></li>
894      * <li><code> 0 <= column < columnDimension</code></li>
895      * </ul>
896      * otherwise a <code>MatrixIndexException</code> is thrown.</p>
897      *
898      * @param row  row location of entry to be fetched
899      * @param column  column location of entry to be fetched
900      * @return matrix entry in row,column
901      * @throws MatrixIndexException if the row
902      * or column index is not valid
903      */
904     public double getEntryAsDouble(int row, int column) throws MatrixIndexException {
905         return getEntry(row,column).doubleValue();
906     }
907 
908     /**
909      * Returns the transpose matrix.
910      *
911      * @return transpose matrix
912      */
913     public BigMatrix transpose() {
914         final int nRows = this.getRowDimension();
915         final int nCols = this.getColumnDimension();
916         final BigDecimal[][] outData = new BigDecimal[nCols][nRows];
917         for (int row = 0; row < nRows; row++) {
918             final BigDecimal[] dataRow = data[row];
919             for (int col = 0; col < nCols; col++) {
920                 outData[col][row] = dataRow[col];
921             }
922         }
923         return new BigMatrixImpl(outData, false);
924     }
925 
926     /**
927      * Returns the inverse matrix if this matrix is invertible.
928      *
929      * @return inverse matrix
930      * @throws InvalidMatrixException if this is not invertible
931      */
932     public BigMatrix inverse() throws InvalidMatrixException {
933         return solve(MatrixUtils.createBigIdentityMatrix(getRowDimension()));
934     }
935 
936     /**
937      * Returns the determinant of this matrix.
938      *
939      * @return determinant
940      * @throws InvalidMatrixException if matrix is not square
941      */
942     public BigDecimal getDeterminant() throws InvalidMatrixException {
943         if (!isSquare()) {
944             throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
945         }
946         if (isSingular()) {   // note: this has side effect of attempting LU decomp if lu == null
947             return ZERO;
948         } else {
949             BigDecimal det = (parity == 1) ? ONE : ONE.negate();
950             for (int i = 0; i < getRowDimension(); i++) {
951                 det = det.multiply(lu[i][i]);
952             }
953             return det;
954         }
955     }
956 
957      /**
958      * Is this a square matrix?
959      * @return true if the matrix is square (rowDimension = columnDimension)
960      */
961     public boolean isSquare() {
962         return getColumnDimension() == getRowDimension();
963     }
964 
965     /**
966      * Is this a singular matrix?
967      * @return true if the matrix is singular
968      */
969     public boolean isSingular() {
970         if (lu == null) {
971             try {
972                 luDecompose();
973                 return false;
974             } catch (InvalidMatrixException ex) {
975                 return true;
976             }
977         } else { // LU decomp must have been successfully performed
978             return false; // so the matrix is not singular
979         }
980     }
981 
982     /**
983      * Returns the number of rows in the matrix.
984      *
985      * @return rowDimension
986      */
987     public int getRowDimension() {
988         return data.length;
989     }
990 
991     /**
992      * Returns the number of columns in the matrix.
993      *
994      * @return columnDimension
995      */
996     public int getColumnDimension() {
997         return data[0].length;
998     }
999 
1000      /**
1001      * Returns the <a href="http://mathworld.wolfram.com/MatrixTrace.html">
1002      * trace</a> of the matrix (the sum of the elements on the main diagonal).
1003      *
1004      * @return trace
1005      *
1006      * @throws IllegalArgumentException if this matrix is not square.
1007      */
1008     public BigDecimal getTrace() throws IllegalArgumentException {
1009         if (!isSquare()) {
1010             throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
1011         }
1012         BigDecimal trace = data[0][0];
1013         for (int i = 1; i < this.getRowDimension(); i++) {
1014             trace = trace.add(data[i][i]);
1015         }
1016         return trace;
1017     }
1018 
1019     /**
1020      * Returns the result of multiplying this by the vector <code>v</code>.
1021      *
1022      * @param v the vector to operate on
1023      * @return this*v
1024      * @throws IllegalArgumentException if columnDimension != v.size()
1025      */
1026     public BigDecimal[] operate(BigDecimal[] v) throws IllegalArgumentException {
1027         if (v.length != getColumnDimension()) {
1028             throw MathRuntimeException.createIllegalArgumentException(
1029                     LocalizedFormats.VECTOR_LENGTH_MISMATCH,
1030                     v.length, getColumnDimension() );
1031         }
1032         final int nRows = this.getRowDimension();
1033         final int nCols = this.getColumnDimension();
1034         final BigDecimal[] out = new BigDecimal[nRows];
1035         for (int row = 0; row < nRows; row++) {
1036             BigDecimal sum = ZERO;
1037             for (int i = 0; i < nCols; i++) {
1038                 sum = sum.add(data[row][i].multiply(v[i]));
1039             }
1040             out[row] = sum;
1041         }
1042         return out;
1043     }
1044 
1045     /**
1046      * Returns the result of multiplying this by the vector <code>v</code>.
1047      *
1048      * @param v the vector to operate on
1049      * @return this*v
1050      * @throws IllegalArgumentException if columnDimension != v.size()
1051      */
1052     public BigDecimal[] operate(double[] v) throws IllegalArgumentException {
1053         final BigDecimal bd[] = new BigDecimal[v.length];
1054         for (int i = 0; i < bd.length; i++) {
1055             bd[i] = new BigDecimal(v[i]);
1056         }
1057         return operate(bd);
1058     }
1059 
1060     /**
1061      * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
1062      *
1063      * @param v the row vector to premultiply by
1064      * @return v*this
1065      * @throws IllegalArgumentException if rowDimension != v.size()
1066      */
1067     public BigDecimal[] preMultiply(BigDecimal[] v) throws IllegalArgumentException {
1068         final int nRows = this.getRowDimension();
1069         if (v.length != nRows) {
1070             throw MathRuntimeException.createIllegalArgumentException(
1071                     LocalizedFormats.VECTOR_LENGTH_MISMATCH,
1072                     v.length, nRows );
1073         }
1074         final int nCols = this.getColumnDimension();
1075         final BigDecimal[] out = new BigDecimal[nCols];
1076         for (int col = 0; col < nCols; col++) {
1077             BigDecimal sum = ZERO;
1078             for (int i = 0; i < nRows; i++) {
1079                 sum = sum.add(data[i][col].multiply(v[i]));
1080             }
1081             out[col] = sum;
1082         }
1083         return out;
1084     }
1085 
1086     /**
1087      * Returns a matrix of (column) solution vectors for linear systems with
1088      * coefficient matrix = this and constant vectors = columns of
1089      * <code>b</code>.
1090      *
1091      * @param b  array of constants forming RHS of linear systems to
1092      * to solve
1093      * @return solution array
1094      * @throws IllegalArgumentException if this.rowDimension != row dimension
1095      * @throws InvalidMatrixException if this matrix is not square or is singular
1096      */
1097     public BigDecimal[] solve(BigDecimal[] b) throws IllegalArgumentException, InvalidMatrixException {
1098         final int nRows = this.getRowDimension();
1099         if (b.length != nRows) {
1100             throw MathRuntimeException.createIllegalArgumentException(
1101                     LocalizedFormats.VECTOR_LENGTH_MISMATCH,
1102                     b.length, nRows);
1103         }
1104         final BigMatrix bMatrix = new BigMatrixImpl(b);
1105         final BigDecimal[][] solution = ((BigMatrixImpl) (solve(bMatrix))).getDataRef();
1106         final BigDecimal[] out = new BigDecimal[nRows];
1107         for (int row = 0; row < nRows; row++) {
1108             out[row] = solution[row][0];
1109         }
1110         return out;
1111     }
1112 
1113     /**
1114      * Returns a matrix of (column) solution vectors for linear systems with
1115      * coefficient matrix = this and constant vectors = columns of
1116      * <code>b</code>.
1117      *
1118      * @param b  array of constants forming RHS of linear systems to
1119      * to solve
1120      * @return solution array
1121      * @throws IllegalArgumentException if this.rowDimension != row dimension
1122      * @throws InvalidMatrixException if this matrix is not square or is singular
1123      */
1124     public BigDecimal[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException {
1125         final BigDecimal bd[] = new BigDecimal[b.length];
1126         for (int i = 0; i < bd.length; i++) {
1127             bd[i] = new BigDecimal(b[i]);
1128         }
1129         return solve(bd);
1130     }
1131 
1132     /**
1133      * Returns a matrix of (column) solution vectors for linear systems with
1134      * coefficient matrix = this and constant vectors = columns of
1135      * <code>b</code>.
1136      *
1137      * @param b  matrix of constant vectors forming RHS of linear systems to
1138      * to solve
1139      * @return matrix of solution vectors
1140      * @throws IllegalArgumentException if this.rowDimension != row dimension
1141      * @throws InvalidMatrixException if this matrix is not square or is singular
1142      */
1143     public BigMatrix solve(BigMatrix b) throws IllegalArgumentException, InvalidMatrixException  {
1144         if (b.getRowDimension() != getRowDimension()) {
1145             throw MathRuntimeException.createIllegalArgumentException(
1146                     LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
1147                     b.getRowDimension(), b.getColumnDimension(), getRowDimension(), "n");
1148         }
1149         if (!isSquare()) {
1150             throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
1151         }
1152         if (this.isSingular()) { // side effect: compute LU decomp
1153             throw new SingularMatrixException();
1154         }
1155 
1156         final int nCol = this.getColumnDimension();
1157         final int nColB = b.getColumnDimension();
1158         final int nRowB = b.getRowDimension();
1159 
1160         // Apply permutations to b
1161         final BigDecimal[][] bp = new BigDecimal[nRowB][nColB];
1162         for (int row = 0; row < nRowB; row++) {
1163             final BigDecimal[] bpRow = bp[row];
1164             for (int col = 0; col < nColB; col++) {
1165                 bpRow[col] = b.getEntry(permutation[row], col);
1166             }
1167         }
1168 
1169         // Solve LY = b
1170         for (int col = 0; col < nCol; col++) {
1171             for (int i = col + 1; i < nCol; i++) {
1172                 final BigDecimal[] bpI = bp[i];
1173                 final BigDecimal[] luI = lu[i];
1174                 for (int j = 0; j < nColB; j++) {
1175                     bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col]));
1176                 }
1177             }
1178         }
1179 
1180         // Solve UX = Y
1181         for (int col = nCol - 1; col >= 0; col--) {
1182             final BigDecimal[] bpCol = bp[col];
1183             final BigDecimal luDiag = lu[col][col];
1184             for (int j = 0; j < nColB; j++) {
1185                 bpCol[j] = bpCol[j].divide(luDiag, scale, roundingMode);
1186             }
1187             for (int i = 0; i < col; i++) {
1188                 final BigDecimal[] bpI = bp[i];
1189                 final BigDecimal[] luI = lu[i];
1190                 for (int j = 0; j < nColB; j++) {
1191                     bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col]));
1192                 }
1193             }
1194         }
1195 
1196         return new BigMatrixImpl(bp, false);
1197 
1198     }
1199 
1200     /**
1201      * Computes a new
1202      * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
1203      * LU decompostion</a> for this matrix, storing the result for use by other methods.
1204      * <p>
1205      * <strong>Implementation Note</strong>:<br>
1206      * Uses <a href="http://www.damtp.cam.ac.uk/user/fdl/people/sd/lectures/nummeth98/linear.htm">
1207      * Crout's algortithm</a>, with partial pivoting.</p>
1208      * <p>
1209      * <strong>Usage Note</strong>:<br>
1210      * This method should rarely be invoked directly. Its only use is
1211      * to force recomputation of the LU decomposition when changes have been
1212      * made to the underlying data using direct array references. Changes
1213      * made using setXxx methods will trigger recomputation when needed
1214      * automatically.</p>
1215      *
1216      * @throws InvalidMatrixException if the matrix is non-square or singular.
1217      */
1218     public void luDecompose() throws InvalidMatrixException {
1219 
1220         final int nRows = this.getRowDimension();
1221         final int nCols = this.getColumnDimension();
1222         if (nRows != nCols) {
1223             throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
1224         }
1225         lu = this.getData();
1226 
1227         // Initialize permutation array and parity
1228         permutation = new int[nRows];
1229         for (int row = 0; row < nRows; row++) {
1230             permutation[row] = row;
1231         }
1232         parity = 1;
1233 
1234         // Loop over columns
1235         for (int col = 0; col < nCols; col++) {
1236 
1237             BigDecimal sum = ZERO;
1238 
1239             // upper
1240             for (int row = 0; row < col; row++) {
1241                 final BigDecimal[] luRow = lu[row];
1242                 sum = luRow[col];
1243                 for (int i = 0; i < row; i++) {
1244                     sum = sum.subtract(luRow[i].multiply(lu[i][col]));
1245                 }
1246                 luRow[col] = sum;
1247             }
1248 
1249             // lower
1250             int max = col; // permutation row
1251             BigDecimal largest = ZERO;
1252             for (int row = col; row < nRows; row++) {
1253                 final BigDecimal[] luRow = lu[row];
1254                 sum = luRow[col];
1255                 for (int i = 0; i < col; i++) {
1256                     sum = sum.subtract(luRow[i].multiply(lu[i][col]));
1257                 }
1258                 luRow[col] = sum;
1259 
1260                 // maintain best permutation choice
1261                 if (sum.abs().compareTo(largest) == 1) {
1262                     largest = sum.abs();
1263                     max = row;
1264                 }
1265             }
1266 
1267             // Singularity check
1268             if (lu[max][col].abs().compareTo(TOO_SMALL) <= 0) {
1269                 lu = null;
1270                 throw new SingularMatrixException();
1271             }
1272 
1273             // Pivot if necessary
1274             if (max != col) {
1275                 BigDecimal tmp = ZERO;
1276                 for (int i = 0; i < nCols; i++) {
1277                     tmp = lu[max][i];
1278                     lu[max][i] = lu[col][i];
1279                     lu[col][i] = tmp;
1280                 }
1281                 int temp = permutation[max];
1282                 permutation[max] = permutation[col];
1283                 permutation[col] = temp;
1284                 parity = -parity;
1285             }
1286 
1287             // Divide the lower elements by the "winning" diagonal elt.
1288             final BigDecimal luDiag = lu[col][col];
1289             for (int row = col + 1; row < nRows; row++) {
1290                 final BigDecimal[] luRow = lu[row];
1291                 luRow[col] = luRow[col].divide(luDiag, scale, roundingMode);
1292             }
1293 
1294         }
1295 
1296     }
1297 
1298     /**
1299      * Get a string representation for this matrix.
1300      * @return a string representation for this matrix
1301      */
1302     @Override
1303     public String toString() {
1304         StringBuilder res = new StringBuilder();
1305         res.append("BigMatrixImpl{");
1306         if (data != null) {
1307             for (int i = 0; i < data.length; i++) {
1308                 if (i > 0) {
1309                     res.append(",");
1310                 }
1311                 res.append("{");
1312                 for (int j = 0; j < data[0].length; j++) {
1313                     if (j > 0) {
1314                         res.append(",");
1315                     }
1316                     res.append(data[i][j]);
1317                 }
1318                 res.append("}");
1319             }
1320         }
1321         res.append("}");
1322         return res.toString();
1323     }
1324 
1325     /**
1326      * Returns true iff <code>object</code> is a
1327      * <code>BigMatrixImpl</code> instance with the same dimensions as this
1328      * and all corresponding matrix entries are equal.  BigDecimal.equals
1329      * is used to compare corresponding entries.
1330      *
1331      * @param object the object to test equality against.
1332      * @return true if object equals this
1333      */
1334     @Override
1335     public boolean equals(Object object) {
1336         if (object == this ) {
1337             return true;
1338         }
1339         if (object instanceof BigMatrixImpl == false) {
1340             return false;
1341         }
1342         final BigMatrix m = (BigMatrix) object;
1343         final int nRows = getRowDimension();
1344         final int nCols = getColumnDimension();
1345         if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) {
1346             return false;
1347         }
1348         for (int row = 0; row < nRows; row++) {
1349             final BigDecimal[] dataRow = data[row];
1350             for (int col = 0; col < nCols; col++) {
1351                 if (!dataRow[col].equals(m.getEntry(row, col))) {
1352                     return false;
1353                 }
1354             }
1355         }
1356         return true;
1357     }
1358 
1359     /**
1360      * Computes a hashcode for the matrix.
1361      *
1362      * @return hashcode for matrix
1363      */
1364     @Override
1365     public int hashCode() {
1366         int ret = 7;
1367         final int nRows = getRowDimension();
1368         final int nCols = getColumnDimension();
1369         ret = ret * 31 + nRows;
1370         ret = ret * 31 + nCols;
1371         for (int row = 0; row < nRows; row++) {
1372             final BigDecimal[] dataRow = data[row];
1373             for (int col = 0; col < nCols; col++) {
1374                 ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) *
1375                 dataRow[col].hashCode();
1376             }
1377         }
1378         return ret;
1379     }
1380 
1381     //------------------------ Protected methods
1382 
1383     /**
1384      *  Returns the LU decomposition as a BigMatrix.
1385      *  Returns a fresh copy of the cached LU matrix if this has been computed;
1386      *  otherwise the composition is computed and cached for use by other methods.
1387      *  Since a copy is returned in either case, changes to the returned matrix do not
1388      *  affect the LU decomposition property.
1389      * <p>
1390      * The matrix returned is a compact representation of the LU decomposition.
1391      * Elements below the main diagonal correspond to entries of the "L" matrix;
1392      * elements on and above the main diagonal correspond to entries of the "U"
1393      * matrix.</p>
1394      * <p>
1395      * Example: <pre>
1396      *
1397      *     Returned matrix                L                  U
1398      *         2  3  1                   1  0  0            2  3  1
1399      *         5  4  6                   5  1  0            0  4  6
1400      *         1  7  8                   1  7  1            0  0  8
1401      * </pre>
1402      *
1403      * The L and U matrices satisfy the matrix equation LU = permuteRows(this), <br>
1404      *  where permuteRows reorders the rows of the matrix to follow the order determined
1405      *  by the <a href=#getPermutation()>permutation</a> property.</p>
1406      *
1407      * @return LU decomposition matrix
1408      * @throws InvalidMatrixException if the matrix is non-square or singular.
1409      */
1410     protected BigMatrix getLUMatrix() throws InvalidMatrixException {
1411         if (lu == null) {
1412             luDecompose();
1413         }
1414         return new BigMatrixImpl(lu);
1415     }
1416 
1417     /**
1418      * Returns the permutation associated with the lu decomposition.
1419      * The entries of the array represent a permutation of the numbers 0, ... , nRows - 1.
1420      * <p>
1421      * Example:
1422      * permutation = [1, 2, 0] means current 2nd row is first, current third row is second
1423      * and current first row is last.</p>
1424      * <p>
1425      * Returns a fresh copy of the array.</p>
1426      *
1427      * @return the permutation
1428      */
1429     protected int[] getPermutation() {
1430         final int[] out = new int[permutation.length];
1431         System.arraycopy(permutation, 0, out, 0, permutation.length);
1432         return out;
1433     }
1434 
1435     //------------------------ Private methods
1436 
1437     /**
1438      * Returns a fresh copy of the underlying data array.
1439      *
1440      * @return a copy of the underlying data array.
1441      */
1442     private BigDecimal[][] copyOut() {
1443         final int nRows = this.getRowDimension();
1444         final BigDecimal[][] out = new BigDecimal[nRows][this.getColumnDimension()];
1445         // can't copy 2-d array in one shot, otherwise get row references
1446         for (int i = 0; i < nRows; i++) {
1447             System.arraycopy(data[i], 0, out[i], 0, data[i].length);
1448         }
1449         return out;
1450     }
1451 
1452     /**
1453      * Replaces data with a fresh copy of the input array.
1454      * <p>
1455      * Verifies that the input array is rectangular and non-empty.</p>
1456      *
1457      * @param in data to copy in
1458      * @throws IllegalArgumentException if input array is emtpy or not
1459      *    rectangular
1460      * @throws NullPointerException if input array is null
1461      */
1462     private void copyIn(BigDecimal[][] in) {
1463         setSubMatrix(in,0,0);
1464     }
1465 
1466     /**
1467      * Replaces data with a fresh copy of the input array.
1468      *
1469      * @param in data to copy in
1470      */
1471     private void copyIn(double[][] in) {
1472         final int nRows = in.length;
1473         final int nCols = in[0].length;
1474         data = new BigDecimal[nRows][nCols];
1475         for (int i = 0; i < nRows; i++) {
1476             final BigDecimal[] dataI = data[i];
1477             final double[] inI = in[i];
1478             for (int j = 0; j < nCols; j++) {
1479                 dataI[j] = new BigDecimal(inI[j]);
1480             }
1481         }
1482         lu = null;
1483     }
1484 
1485     /**
1486      * Replaces data with BigDecimals represented by the strings in the input
1487      * array.
1488      *
1489      * @param in data to copy in
1490      */
1491     private void copyIn(String[][] in) {
1492         final int nRows = in.length;
1493         final int nCols = in[0].length;
1494         data = new BigDecimal[nRows][nCols];
1495         for (int i = 0; i < nRows; i++) {
1496             final BigDecimal[] dataI = data[i];
1497             final String[] inI = in[i];
1498             for (int j = 0; j < nCols; j++) {
1499                 dataI[j] = new BigDecimal(inI[j]);
1500             }
1501         }
1502         lu = null;
1503     }
1504 
1505 }