001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.math4.linear;
019
020import java.util.ArrayList;
021
022import org.apache.commons.math4.Field;
023import org.apache.commons.math4.FieldElement;
024import org.apache.commons.math4.exception.DimensionMismatchException;
025import org.apache.commons.math4.exception.NoDataException;
026import org.apache.commons.math4.exception.NotPositiveException;
027import org.apache.commons.math4.exception.NotStrictlyPositiveException;
028import org.apache.commons.math4.exception.NullArgumentException;
029import org.apache.commons.math4.exception.NumberIsTooSmallException;
030import org.apache.commons.math4.exception.OutOfRangeException;
031import org.apache.commons.math4.exception.util.LocalizedFormats;
032import org.apache.commons.math4.util.MathArrays;
033
034/**
035 * Basic implementation of {@link FieldMatrix} methods regardless of the underlying storage.
036 * <p>All the methods implemented here use {@link #getEntry(int, int)} to access
037 * matrix elements. Derived class can provide faster implementations. </p>
038 *
039 * @param <T> Type of the field elements.
040 *
041 * @since 2.0
042 */
043public abstract class AbstractFieldMatrix<T extends FieldElement<T>>
044    implements FieldMatrix<T> {
045    /** Field to which the elements belong. */
046    private final Field<T> field;
047
048    /**
049     * Constructor for use with Serializable
050     */
051    protected AbstractFieldMatrix() {
052        field = null;
053    }
054
055    /**
056     * Creates a matrix with no data
057     * @param field field to which the elements belong
058     */
059    protected AbstractFieldMatrix(final Field<T> field) {
060        this.field = field;
061    }
062
063    /**
064     * Create a new {@code FieldMatrix<T>} with the supplied row and column dimensions.
065     *
066     * @param field Field to which the elements belong.
067     * @param rowDimension Number of rows in the new matrix.
068     * @param columnDimension Number of columns in the new matrix.
069     * @throws NotStrictlyPositiveException if row or column dimension is not
070     * positive.
071     */
072    protected AbstractFieldMatrix(final Field<T> field,
073                                  final int rowDimension,
074                                  final int columnDimension)
075        throws NotStrictlyPositiveException {
076        if (rowDimension <= 0) {
077            throw new NotStrictlyPositiveException(LocalizedFormats.DIMENSION,
078                                                   rowDimension);
079        }
080        if (columnDimension <= 0) {
081            throw new NotStrictlyPositiveException(LocalizedFormats.DIMENSION,
082                                                   columnDimension);
083        }
084        this.field = field;
085    }
086
087    /**
088     * Get the elements type from an array.
089     *
090     * @param <T> Type of the field elements.
091     * @param d Data array.
092     * @return the field to which the array elements belong.
093     * @throws NullArgumentException if the array is {@code null}.
094     * @throws NoDataException if the array is empty.
095     */
096    protected static <T extends FieldElement<T>> Field<T> extractField(final T[][] d)
097        throws NoDataException, NullArgumentException {
098        if (d == null) {
099            throw new NullArgumentException();
100        }
101        if (d.length == 0) {
102            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
103        }
104        if (d[0].length == 0) {
105            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
106        }
107        return d[0][0].getField();
108    }
109
110    /**
111     * Get the elements type from an array.
112     *
113     * @param <T> Type of the field elements.
114     * @param d Data array.
115     * @return the field to which the array elements belong.
116     * @throws NoDataException if array is empty.
117     */
118    protected static <T extends FieldElement<T>> Field<T> extractField(final T[] d)
119        throws NoDataException {
120        if (d.length == 0) {
121            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
122        }
123        return d[0].getField();
124    }
125
126    /** {@inheritDoc} */
127    @Override
128    public Field<T> getField() {
129        return field;
130    }
131
132    /** {@inheritDoc} */
133    @Override
134    public abstract FieldMatrix<T> createMatrix(final int rowDimension,
135                                                final int columnDimension)
136        throws NotStrictlyPositiveException;
137
138    /** {@inheritDoc} */
139    @Override
140    public abstract FieldMatrix<T> copy();
141
142    /** {@inheritDoc} */
143    @Override
144    public FieldMatrix<T> add(FieldMatrix<T> m)
145        throws MatrixDimensionMismatchException {
146        // safety check
147        checkAdd(m);
148
149        final int rowCount    = getRowDimension();
150        final int columnCount = getColumnDimension();
151        final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
152        for (int row = 0; row < rowCount; ++row) {
153            for (int col = 0; col < columnCount; ++col) {
154                out.setEntry(row, col, getEntry(row, col).add(m.getEntry(row, col)));
155            }
156        }
157
158        return out;
159    }
160
161    /** {@inheritDoc} */
162    @Override
163    public FieldMatrix<T> subtract(final FieldMatrix<T> m)
164        throws MatrixDimensionMismatchException {
165        // safety check
166        checkAdd(m);
167
168        final int rowCount    = getRowDimension();
169        final int columnCount = getColumnDimension();
170        final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
171        for (int row = 0; row < rowCount; ++row) {
172            for (int col = 0; col < columnCount; ++col) {
173                out.setEntry(row, col, getEntry(row, col).subtract(m.getEntry(row, col)));
174            }
175        }
176
177        return out;
178    }
179
180    /** {@inheritDoc} */
181    @Override
182    public FieldMatrix<T> scalarAdd(final T d) {
183
184        final int rowCount    = getRowDimension();
185        final int columnCount = getColumnDimension();
186        final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
187        for (int row = 0; row < rowCount; ++row) {
188            for (int col = 0; col < columnCount; ++col) {
189                out.setEntry(row, col, getEntry(row, col).add(d));
190            }
191        }
192
193        return out;
194    }
195
196    /** {@inheritDoc} */
197    @Override
198    public FieldMatrix<T> scalarMultiply(final T d) {
199        final int rowCount    = getRowDimension();
200        final int columnCount = getColumnDimension();
201        final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
202        for (int row = 0; row < rowCount; ++row) {
203            for (int col = 0; col < columnCount; ++col) {
204                out.setEntry(row, col, getEntry(row, col).multiply(d));
205            }
206        }
207
208        return out;
209    }
210
211    /** {@inheritDoc} */
212    @Override
213    public FieldMatrix<T> multiply(final FieldMatrix<T> m)
214        throws DimensionMismatchException {
215        // safety check
216        checkMultiply(m);
217
218        final int nRows = getRowDimension();
219        final int nCols = m.getColumnDimension();
220        final int nSum  = getColumnDimension();
221        final FieldMatrix<T> out = createMatrix(nRows, nCols);
222        for (int row = 0; row < nRows; ++row) {
223            for (int col = 0; col < nCols; ++col) {
224                T sum = field.getZero();
225                for (int i = 0; i < nSum; ++i) {
226                    sum = sum.add(getEntry(row, i).multiply(m.getEntry(i, col)));
227                }
228                out.setEntry(row, col, sum);
229            }
230        }
231
232        return out;
233    }
234
235    /** {@inheritDoc} */
236    @Override
237    public FieldMatrix<T> preMultiply(final FieldMatrix<T> m)
238        throws DimensionMismatchException {
239        return m.multiply(this);
240    }
241
242    /** {@inheritDoc} */
243    @Override
244    public FieldMatrix<T> power(final int p) throws NonSquareMatrixException,
245    NotPositiveException {
246        if (p < 0) {
247            throw new NotPositiveException(p);
248        }
249
250        if (!isSquare()) {
251            throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
252        }
253
254        if (p == 0) {
255            return MatrixUtils.createFieldIdentityMatrix(this.getField(), this.getRowDimension());
256        }
257
258        if (p == 1) {
259            return this.copy();
260        }
261
262        final int power = p - 1;
263
264        /*
265         * Only log_2(p) operations is used by doing as follows:
266         * 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
267         *
268         * In general, the same approach is used for A^p.
269         */
270
271        final char[] binaryRepresentation = Integer.toBinaryString(power)
272                .toCharArray();
273        final ArrayList<Integer> nonZeroPositions = new ArrayList<>();
274
275        for (int i = 0; i < binaryRepresentation.length; ++i) {
276            if (binaryRepresentation[i] == '1') {
277                final int pos = binaryRepresentation.length - i - 1;
278                nonZeroPositions.add(pos);
279            }
280        }
281
282        ArrayList<FieldMatrix<T>> results = new ArrayList<>(
283                binaryRepresentation.length);
284
285        results.add(0, this.copy());
286
287        for (int i = 1; i < binaryRepresentation.length; ++i) {
288            final FieldMatrix<T> s = results.get(i - 1);
289            final FieldMatrix<T> r = s.multiply(s);
290            results.add(i, r);
291        }
292
293        FieldMatrix<T> result = this.copy();
294
295        for (Integer i : nonZeroPositions) {
296            result = result.multiply(results.get(i));
297        }
298
299        return result;
300    }
301
302    /** {@inheritDoc} */
303    @Override
304    public T[][] getData() {
305        final T[][] data = MathArrays.buildArray(field, getRowDimension(), getColumnDimension());
306
307        for (int i = 0; i < data.length; ++i) {
308            final T[] dataI = data[i];
309            for (int j = 0; j < dataI.length; ++j) {
310                dataI[j] = getEntry(i, j);
311            }
312        }
313
314        return data;
315    }
316
317    /** {@inheritDoc} */
318    @Override
319    public FieldMatrix<T> getSubMatrix(final int startRow, final int endRow,
320                                       final int startColumn, final int endColumn)
321        throws NumberIsTooSmallException, OutOfRangeException {
322        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
323
324        final FieldMatrix<T> subMatrix =
325            createMatrix(endRow - startRow + 1, endColumn - startColumn + 1);
326        for (int i = startRow; i <= endRow; ++i) {
327            for (int j = startColumn; j <= endColumn; ++j) {
328                subMatrix.setEntry(i - startRow, j - startColumn, getEntry(i, j));
329            }
330        }
331
332        return subMatrix;
333
334    }
335
336    /** {@inheritDoc} */
337    @Override
338    public FieldMatrix<T> getSubMatrix(final int[] selectedRows,
339                                       final int[] selectedColumns)
340    throws NoDataException, NullArgumentException, OutOfRangeException {
341
342        // safety checks
343        checkSubMatrixIndex(selectedRows, selectedColumns);
344
345        // copy entries
346        final FieldMatrix<T> subMatrix =
347            createMatrix(selectedRows.length, selectedColumns.length);
348        subMatrix.walkInOptimizedOrder(new DefaultFieldMatrixChangingVisitor<T>(field.getZero()) {
349
350            /** {@inheritDoc} */
351            @Override
352            public T visit(final int row, final int column, final T value) {
353                return getEntry(selectedRows[row], selectedColumns[column]);
354            }
355
356        });
357
358        return subMatrix;
359
360    }
361
362    /** {@inheritDoc} */
363    @Override
364    public void copySubMatrix(final int startRow, final int endRow,
365                              final int startColumn, final int endColumn,
366                              final T[][] destination)
367    throws MatrixDimensionMismatchException, NumberIsTooSmallException,
368    OutOfRangeException{
369        // safety checks
370        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
371        final int rowsCount    = endRow + 1 - startRow;
372        final int columnsCount = endColumn + 1 - startColumn;
373        if ((destination.length < rowsCount) || (destination[0].length < columnsCount)) {
374            throw new MatrixDimensionMismatchException(destination.length,
375                                                       destination[0].length,
376                                                       rowsCount,
377                                                       columnsCount);
378        }
379
380        // copy entries
381        walkInOptimizedOrder(new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
382
383            /** Initial row index. */
384            private int startRow;
385
386            /** Initial column index. */
387            private int startColumn;
388
389            /** {@inheritDoc} */
390            @Override
391            public void start(final int rows, final int columns,
392                              final int startRow, final int endRow,
393                              final int startColumn, final int endColumn) {
394                this.startRow    = startRow;
395                this.startColumn = startColumn;
396            }
397
398            /** {@inheritDoc} */
399            @Override
400            public void visit(final int row, final int column, final T value) {
401                destination[row - startRow][column - startColumn] = value;
402            }
403
404        }, startRow, endRow, startColumn, endColumn);
405
406    }
407
408    /** {@inheritDoc} */
409    @Override
410    public void copySubMatrix(int[] selectedRows, int[] selectedColumns, T[][] destination)
411        throws MatrixDimensionMismatchException, NoDataException,
412        NullArgumentException, OutOfRangeException {
413        // safety checks
414        checkSubMatrixIndex(selectedRows, selectedColumns);
415        if ((destination.length < selectedRows.length) ||
416            (destination[0].length < selectedColumns.length)) {
417            throw new MatrixDimensionMismatchException(destination.length,
418                                                       destination[0].length,
419                                                       selectedRows.length,
420                                                       selectedColumns.length);
421        }
422
423        // copy entries
424        for (int i = 0; i < selectedRows.length; i++) {
425            final T[] destinationI = destination[i];
426            for (int j = 0; j < selectedColumns.length; j++) {
427                destinationI[j] = getEntry(selectedRows[i], selectedColumns[j]);
428            }
429        }
430
431    }
432
433    /** {@inheritDoc} */
434    @Override
435    public void setSubMatrix(final T[][] subMatrix, final int row,
436                             final int column)
437        throws DimensionMismatchException, OutOfRangeException,
438        NoDataException, NullArgumentException {
439        if (subMatrix == null) {
440            throw new NullArgumentException();
441        }
442        final int nRows = subMatrix.length;
443        if (nRows == 0) {
444            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
445        }
446
447        final int nCols = subMatrix[0].length;
448        if (nCols == 0) {
449            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
450        }
451
452        for (int r = 1; r < nRows; ++r) {
453            if (subMatrix[r].length != nCols) {
454                throw new DimensionMismatchException(nCols, subMatrix[r].length);
455            }
456        }
457
458        checkRowIndex(row);
459        checkColumnIndex(column);
460        checkRowIndex(nRows + row - 1);
461        checkColumnIndex(nCols + column - 1);
462
463        for (int i = 0; i < nRows; ++i) {
464            for (int j = 0; j < nCols; ++j) {
465                setEntry(row + i, column + j, subMatrix[i][j]);
466            }
467        }
468    }
469
470    /** {@inheritDoc} */
471    @Override
472    public FieldMatrix<T> getRowMatrix(final int row) throws OutOfRangeException {
473        checkRowIndex(row);
474        final int nCols = getColumnDimension();
475        final FieldMatrix<T> out = createMatrix(1, nCols);
476        for (int i = 0; i < nCols; ++i) {
477            out.setEntry(0, i, getEntry(row, i));
478        }
479
480        return out;
481
482    }
483
484    /** {@inheritDoc} */
485    @Override
486    public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
487        throws OutOfRangeException, MatrixDimensionMismatchException {
488        checkRowIndex(row);
489        final int nCols = getColumnDimension();
490        if ((matrix.getRowDimension() != 1) ||
491            (matrix.getColumnDimension() != nCols)) {
492            throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
493                                                       matrix.getColumnDimension(),
494                                                       1, nCols);
495        }
496        for (int i = 0; i < nCols; ++i) {
497            setEntry(row, i, matrix.getEntry(0, i));
498        }
499
500    }
501
502    /** {@inheritDoc} */
503    @Override
504    public FieldMatrix<T> getColumnMatrix(final int column)
505    throws OutOfRangeException {
506
507        checkColumnIndex(column);
508        final int nRows = getRowDimension();
509        final FieldMatrix<T> out = createMatrix(nRows, 1);
510        for (int i = 0; i < nRows; ++i) {
511            out.setEntry(i, 0, getEntry(i, column));
512        }
513
514        return out;
515
516    }
517
518    /** {@inheritDoc} */
519    @Override
520    public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
521        throws OutOfRangeException, MatrixDimensionMismatchException {
522        checkColumnIndex(column);
523        final int nRows = getRowDimension();
524        if ((matrix.getRowDimension() != nRows) ||
525            (matrix.getColumnDimension() != 1)) {
526            throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
527                                                       matrix.getColumnDimension(),
528                                                       nRows, 1);
529        }
530        for (int i = 0; i < nRows; ++i) {
531            setEntry(i, column, matrix.getEntry(i, 0));
532        }
533
534    }
535
536    /** {@inheritDoc} */
537    @Override
538    public FieldVector<T> getRowVector(final int row)
539        throws OutOfRangeException {
540        return new ArrayFieldVector<>(field, getRow(row), false);
541    }
542
543    /** {@inheritDoc} */
544    @Override
545    public void setRowVector(final int row, final FieldVector<T> vector)
546        throws OutOfRangeException, MatrixDimensionMismatchException {
547        checkRowIndex(row);
548        final int nCols = getColumnDimension();
549        if (vector.getDimension() != nCols) {
550            throw new MatrixDimensionMismatchException(1, vector.getDimension(),
551                                                       1, nCols);
552        }
553        for (int i = 0; i < nCols; ++i) {
554            setEntry(row, i, vector.getEntry(i));
555        }
556
557    }
558
559    /** {@inheritDoc} */
560    @Override
561    public FieldVector<T> getColumnVector(final int column)
562        throws OutOfRangeException {
563        return new ArrayFieldVector<>(field, getColumn(column), false);
564    }
565
566    /** {@inheritDoc} */
567    @Override
568    public void setColumnVector(final int column, final FieldVector<T> vector)
569        throws OutOfRangeException, MatrixDimensionMismatchException {
570
571        checkColumnIndex(column);
572        final int nRows = getRowDimension();
573        if (vector.getDimension() != nRows) {
574            throw new MatrixDimensionMismatchException(vector.getDimension(), 1,
575                                                       nRows, 1);
576        }
577        for (int i = 0; i < nRows; ++i) {
578            setEntry(i, column, vector.getEntry(i));
579        }
580
581    }
582
583    /** {@inheritDoc} */
584    @Override
585    public T[] getRow(final int row) throws OutOfRangeException {
586        checkRowIndex(row);
587        final int nCols = getColumnDimension();
588        final T[] out = MathArrays.buildArray(field, nCols);
589        for (int i = 0; i < nCols; ++i) {
590            out[i] = getEntry(row, i);
591        }
592
593        return out;
594
595    }
596
597    /** {@inheritDoc} */
598    @Override
599    public void setRow(final int row, final T[] array)
600        throws OutOfRangeException, MatrixDimensionMismatchException {
601        checkRowIndex(row);
602        final int nCols = getColumnDimension();
603        if (array.length != nCols) {
604            throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
605        }
606        for (int i = 0; i < nCols; ++i) {
607            setEntry(row, i, array[i]);
608        }
609
610    }
611
612    /** {@inheritDoc} */
613    @Override
614    public T[] getColumn(final int column) throws OutOfRangeException {
615        checkColumnIndex(column);
616        final int nRows = getRowDimension();
617        final T[] out = MathArrays.buildArray(field, nRows);
618        for (int i = 0; i < nRows; ++i) {
619            out[i] = getEntry(i, column);
620        }
621
622        return out;
623
624    }
625
626    /** {@inheritDoc} */
627    @Override
628    public void setColumn(final int column, final T[] array)
629        throws OutOfRangeException, MatrixDimensionMismatchException {
630        checkColumnIndex(column);
631        final int nRows = getRowDimension();
632        if (array.length != nRows) {
633            throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
634        }
635        for (int i = 0; i < nRows; ++i) {
636            setEntry(i, column, array[i]);
637        }
638    }
639
640    /** {@inheritDoc} */
641    @Override
642    public abstract T getEntry(int row, int column) throws OutOfRangeException;
643
644    /** {@inheritDoc} */
645    @Override
646    public abstract void setEntry(int row, int column, T value) throws OutOfRangeException;
647
648    /** {@inheritDoc} */
649    @Override
650    public abstract void addToEntry(int row, int column, T increment) throws OutOfRangeException;
651
652    /** {@inheritDoc} */
653    @Override
654    public abstract void multiplyEntry(int row, int column, T factor) throws OutOfRangeException;
655
656    /** {@inheritDoc} */
657    @Override
658    public FieldMatrix<T> transpose() {
659        final int nRows = getRowDimension();
660        final int nCols = getColumnDimension();
661        final FieldMatrix<T> out = createMatrix(nCols, nRows);
662        walkInOptimizedOrder(new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
663            /** {@inheritDoc} */
664            @Override
665            public void visit(final int row, final int column, final T value) {
666                out.setEntry(column, row, value);
667            }
668        });
669
670        return out;
671    }
672
673    /** {@inheritDoc} */
674    @Override
675    public T getTrace() throws NonSquareMatrixException {
676        final int nRows = getRowDimension();
677        final int nCols = getColumnDimension();
678        if (nRows != nCols) {
679            throw new NonSquareMatrixException(nRows, nCols);
680       }
681        T trace = field.getZero();
682        for (int i = 0; i < nRows; ++i) {
683            trace = trace.add(getEntry(i, i));
684        }
685        return trace;
686    }
687
688    /** {@inheritDoc} */
689    @Override
690    public T[] operate(final T[] v) throws DimensionMismatchException {
691
692        final int nRows = getRowDimension();
693        final int nCols = getColumnDimension();
694        if (v.length != nCols) {
695            throw new DimensionMismatchException(v.length, nCols);
696        }
697
698        final T[] out = MathArrays.buildArray(field, nRows);
699        for (int row = 0; row < nRows; ++row) {
700            T sum = field.getZero();
701            for (int i = 0; i < nCols; ++i) {
702                sum = sum.add(getEntry(row, i).multiply(v[i]));
703            }
704            out[row] = sum;
705        }
706
707        return out;
708    }
709
710    /** {@inheritDoc} */
711    @Override
712    public FieldVector<T> operate(final FieldVector<T> v)
713        throws DimensionMismatchException {
714        if (v instanceof ArrayFieldVector) {
715            return new ArrayFieldVector<>(field, operate(((ArrayFieldVector<T>) v).getDataRef()), false);
716        }
717
718        final int nRows = getRowDimension();
719        final int nCols = getColumnDimension();
720        if (v.getDimension() != nCols) {
721            throw new DimensionMismatchException(v.getDimension(), nCols);
722        }
723
724        final T[] out = MathArrays.buildArray(field, nRows);
725        for (int row = 0; row < nRows; ++row) {
726            T sum = field.getZero();
727            for (int i = 0; i < nCols; ++i) {
728                sum = sum.add(getEntry(row, i).multiply(v.getEntry(i)));
729            }
730            out[row] = sum;
731        }
732
733        return new ArrayFieldVector<>(field, out, false);
734    }
735
736    /** {@inheritDoc} */
737    @Override
738    public T[] preMultiply(final T[] v) throws DimensionMismatchException {
739
740        final int nRows = getRowDimension();
741        final int nCols = getColumnDimension();
742        if (v.length != nRows) {
743            throw new DimensionMismatchException(v.length, nRows);
744        }
745
746        final T[] out = MathArrays.buildArray(field, nCols);
747        for (int col = 0; col < nCols; ++col) {
748            T sum = field.getZero();
749            for (int i = 0; i < nRows; ++i) {
750                sum = sum.add(getEntry(i, col).multiply(v[i]));
751            }
752            out[col] = sum;
753        }
754
755        return out;
756    }
757
758    /** {@inheritDoc} */
759    @Override
760    public FieldVector<T> preMultiply(final FieldVector<T> v)
761        throws DimensionMismatchException {
762        if (v instanceof ArrayFieldVector) {
763            return new ArrayFieldVector<>(field, preMultiply(((ArrayFieldVector<T>) v).getDataRef()), false);
764        }
765
766        final int nRows = getRowDimension();
767        final int nCols = getColumnDimension();
768        if (v.getDimension() != nRows) {
769            throw new DimensionMismatchException(v.getDimension(), nRows);
770        }
771
772        final T[] out = MathArrays.buildArray(field, nCols);
773        for (int col = 0; col < nCols; ++col) {
774            T sum = field.getZero();
775            for (int i = 0; i < nRows; ++i) {
776                sum = sum.add(getEntry(i, col).multiply(v.getEntry(i)));
777            }
778            out[col] = sum;
779        }
780
781        return new ArrayFieldVector<>(field, out, false);
782    }
783
784    /** {@inheritDoc} */
785    @Override
786    public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
787        final int rows    = getRowDimension();
788        final int columns = getColumnDimension();
789        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
790        for (int row = 0; row < rows; ++row) {
791            for (int column = 0; column < columns; ++column) {
792                final T oldValue = getEntry(row, column);
793                final T newValue = visitor.visit(row, column, oldValue);
794                setEntry(row, column, newValue);
795            }
796        }
797        return visitor.end();
798    }
799
800    /** {@inheritDoc} */
801    @Override
802    public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
803        final int rows    = getRowDimension();
804        final int columns = getColumnDimension();
805        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
806        for (int row = 0; row < rows; ++row) {
807            for (int column = 0; column < columns; ++column) {
808                visitor.visit(row, column, getEntry(row, column));
809            }
810        }
811        return visitor.end();
812    }
813
814    /** {@inheritDoc} */
815    @Override
816    public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
817                            final int startRow, final int endRow,
818                            final int startColumn, final int endColumn)
819        throws NumberIsTooSmallException, OutOfRangeException {
820        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
821        visitor.start(getRowDimension(), getColumnDimension(),
822                      startRow, endRow, startColumn, endColumn);
823        for (int row = startRow; row <= endRow; ++row) {
824            for (int column = startColumn; column <= endColumn; ++column) {
825                final T oldValue = getEntry(row, column);
826                final T newValue = visitor.visit(row, column, oldValue);
827                setEntry(row, column, newValue);
828            }
829        }
830        return visitor.end();
831    }
832
833    /** {@inheritDoc} */
834    @Override
835    public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
836                            final int startRow, final int endRow,
837                            final int startColumn, final int endColumn)
838        throws NumberIsTooSmallException, OutOfRangeException {
839        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
840        visitor.start(getRowDimension(), getColumnDimension(),
841                      startRow, endRow, startColumn, endColumn);
842        for (int row = startRow; row <= endRow; ++row) {
843            for (int column = startColumn; column <= endColumn; ++column) {
844                visitor.visit(row, column, getEntry(row, column));
845            }
846        }
847        return visitor.end();
848    }
849
850    /** {@inheritDoc} */
851    @Override
852    public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor) {
853        final int rows    = getRowDimension();
854        final int columns = getColumnDimension();
855        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
856        for (int column = 0; column < columns; ++column) {
857            for (int row = 0; row < rows; ++row) {
858                final T oldValue = getEntry(row, column);
859                final T newValue = visitor.visit(row, column, oldValue);
860                setEntry(row, column, newValue);
861            }
862        }
863        return visitor.end();
864    }
865
866    /** {@inheritDoc} */
867    @Override
868    public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor) {
869        final int rows    = getRowDimension();
870        final int columns = getColumnDimension();
871        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
872        for (int column = 0; column < columns; ++column) {
873            for (int row = 0; row < rows; ++row) {
874                visitor.visit(row, column, getEntry(row, column));
875            }
876        }
877        return visitor.end();
878    }
879
880    /** {@inheritDoc} */
881    @Override
882    public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor,
883                               final int startRow, final int endRow,
884                               final int startColumn, final int endColumn)
885    throws NumberIsTooSmallException, OutOfRangeException {
886        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
887        visitor.start(getRowDimension(), getColumnDimension(),
888                      startRow, endRow, startColumn, endColumn);
889        for (int column = startColumn; column <= endColumn; ++column) {
890            for (int row = startRow; row <= endRow; ++row) {
891                final T oldValue = getEntry(row, column);
892                final T newValue = visitor.visit(row, column, oldValue);
893                setEntry(row, column, newValue);
894            }
895        }
896        return visitor.end();
897    }
898
899    /** {@inheritDoc} */
900    @Override
901    public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor,
902                               final int startRow, final int endRow,
903                               final int startColumn, final int endColumn)
904    throws NumberIsTooSmallException, OutOfRangeException{
905        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
906        visitor.start(getRowDimension(), getColumnDimension(),
907                      startRow, endRow, startColumn, endColumn);
908        for (int column = startColumn; column <= endColumn; ++column) {
909            for (int row = startRow; row <= endRow; ++row) {
910                visitor.visit(row, column, getEntry(row, column));
911            }
912        }
913        return visitor.end();
914    }
915
916    /** {@inheritDoc} */
917    @Override
918    public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
919        return walkInRowOrder(visitor);
920    }
921
922    /** {@inheritDoc} */
923    @Override
924    public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
925        return walkInRowOrder(visitor);
926    }
927
928    /** {@inheritDoc} */
929    @Override
930    public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor,
931                                  final int startRow, final int endRow,
932                                  final int startColumn, final int endColumn)
933        throws NumberIsTooSmallException, OutOfRangeException {
934        return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
935    }
936
937    /** {@inheritDoc} */
938    @Override
939    public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor,
940                                  final int startRow, final int endRow,
941                                  final int startColumn, final int endColumn)
942        throws NumberIsTooSmallException, OutOfRangeException {
943        return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
944    }
945
946    /**
947     * Get a string representation for this matrix.
948     * @return a string representation for this matrix
949     */
950    @Override
951    public String toString() {
952        final int nRows = getRowDimension();
953        final int nCols = getColumnDimension();
954        final StringBuffer res = new StringBuffer();
955        String fullClassName = getClass().getName();
956        String shortClassName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
957        res.append(shortClassName).append("{");
958
959        for (int i = 0; i < nRows; ++i) {
960            if (i > 0) {
961                res.append(",");
962            }
963            res.append("{");
964            for (int j = 0; j < nCols; ++j) {
965                if (j > 0) {
966                    res.append(",");
967                }
968                res.append(getEntry(i, j));
969            }
970            res.append("}");
971        }
972
973        res.append("}");
974        return res.toString();
975    }
976
977    /**
978     * Returns true iff <code>object</code> is a
979     * <code>FieldMatrix</code> instance with the same dimensions as this
980     * and all corresponding matrix entries are equal.
981     *
982     * @param object the object to test equality against.
983     * @return true if object equals this
984     */
985    @Override
986    public boolean equals(final Object object) {
987        if (object == this ) {
988            return true;
989        }
990        if (object instanceof FieldMatrix<?> == false) {
991            return false;
992        }
993        FieldMatrix<?> m = (FieldMatrix<?>) object;
994        final int nRows = getRowDimension();
995        final int nCols = getColumnDimension();
996        if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) {
997            return false;
998        }
999        for (int row = 0; row < nRows; ++row) {
1000            for (int col = 0; col < nCols; ++col) {
1001                if (!getEntry(row, col).equals(m.getEntry(row, col))) {
1002                    return false;
1003                }
1004            }
1005        }
1006        return true;
1007    }
1008
1009    /**
1010     * Computes a hashcode for the matrix.
1011     *
1012     * @return hashcode for matrix
1013     */
1014    @Override
1015    public int hashCode() {
1016        int ret = 322562;
1017        final int nRows = getRowDimension();
1018        final int nCols = getColumnDimension();
1019        ret = ret * 31 + nRows;
1020        ret = ret * 31 + nCols;
1021        for (int row = 0; row < nRows; ++row) {
1022            for (int col = 0; col < nCols; ++col) {
1023               ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) * getEntry(row, col).hashCode();
1024           }
1025        }
1026        return ret;
1027    }
1028
1029    /**
1030     * Check if a row index is valid.
1031     *
1032     * @param row Row index to check.
1033     * @throws OutOfRangeException if {@code index} is not valid.
1034     */
1035    protected void checkRowIndex(final int row) throws OutOfRangeException {
1036        if (row < 0 || row >= getRowDimension()) {
1037            throw new OutOfRangeException(LocalizedFormats.ROW_INDEX,
1038                                          row, 0, getRowDimension() - 1);
1039        }
1040    }
1041
1042    /**
1043     * Check if a column index is valid.
1044     *
1045     * @param column Column index to check.
1046     * @throws OutOfRangeException if {@code index} is not valid.
1047     */
1048    protected void checkColumnIndex(final int column)
1049        throws OutOfRangeException {
1050        if (column < 0 || column >= getColumnDimension()) {
1051            throw new OutOfRangeException(LocalizedFormats.COLUMN_INDEX,
1052                                          column, 0, getColumnDimension() - 1);
1053        }
1054    }
1055
1056    /**
1057     * Check if submatrix ranges indices are valid.
1058     * Rows and columns are indicated counting from 0 to n-1.
1059     *
1060     * @param startRow Initial row index.
1061     * @param endRow Final row index.
1062     * @param startColumn Initial column index.
1063     * @param endColumn Final column index.
1064     * @throws OutOfRangeException if the indices are not valid.
1065     * @throws NumberIsTooSmallException if {@code endRow < startRow} or
1066     * {@code endColumn < startColumn}.
1067     */
1068    protected void checkSubMatrixIndex(final int startRow, final int endRow,
1069                                       final int startColumn, final int endColumn)
1070        throws NumberIsTooSmallException, OutOfRangeException {
1071        checkRowIndex(startRow);
1072        checkRowIndex(endRow);
1073        if (endRow < startRow) {
1074            throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
1075                                                endRow, startRow, true);
1076        }
1077
1078        checkColumnIndex(startColumn);
1079        checkColumnIndex(endColumn);
1080        if (endColumn < startColumn) {
1081            throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
1082                                                endColumn, startColumn, true);
1083        }
1084    }
1085
1086    /**
1087     * Check if submatrix ranges indices are valid.
1088     * Rows and columns are indicated counting from 0 to n-1.
1089     *
1090     * @param selectedRows Array of row indices.
1091     * @param selectedColumns Array of column indices.
1092     * @throws NullArgumentException if the arrays are {@code null}.
1093     * @throws NoDataException if the arrays have zero length.
1094     * @throws OutOfRangeException if row or column selections are not valid.
1095     */
1096    protected void checkSubMatrixIndex(final int[] selectedRows, final int[] selectedColumns)
1097        throws NoDataException, NullArgumentException, OutOfRangeException {
1098        if (selectedRows == null ||
1099            selectedColumns == null) {
1100            throw new NullArgumentException();
1101        }
1102        if (selectedRows.length == 0 ||
1103            selectedColumns.length == 0) {
1104            throw new NoDataException();
1105        }
1106
1107        for (final int row : selectedRows) {
1108            checkRowIndex(row);
1109        }
1110        for (final int column : selectedColumns) {
1111            checkColumnIndex(column);
1112        }
1113    }
1114}