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
18package org.apache.commons.math.linear;
19import java.io.Serializable;
20import java.math.BigDecimal;
21
22import org.apache.commons.math.MathRuntimeException;
23import 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
56public 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}
1506