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.legacy.linear;
019
020import java.util.ArrayList;
021
022import org.apache.commons.math4.legacy.core.Field;
023import org.apache.commons.math4.legacy.core.FieldElement;
024import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
025import org.apache.commons.math4.legacy.exception.NoDataException;
026import org.apache.commons.math4.legacy.exception.NotPositiveException;
027import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
028import org.apache.commons.math4.legacy.exception.NullArgumentException;
029import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
030import org.apache.commons.math4.legacy.exception.OutOfRangeException;
031import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
032import org.apache.commons.math4.legacy.core.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(int rowDimension,
135                                                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    /** {@inheritDoc} */
336    @Override
337    public FieldMatrix<T> getSubMatrix(final int[] selectedRows,
338                                       final int[] selectedColumns)
339    throws NoDataException, NullArgumentException, OutOfRangeException {
340
341        // safety checks
342        checkSubMatrixIndex(selectedRows, selectedColumns);
343
344        // copy entries
345        final FieldMatrix<T> subMatrix =
346            createMatrix(selectedRows.length, selectedColumns.length);
347        subMatrix.walkInOptimizedOrder(new DefaultFieldMatrixChangingVisitor<T>(field.getZero()) {
348
349            /** {@inheritDoc} */
350            @Override
351            public T visit(final int row, final int column, final T value) {
352                return getEntry(selectedRows[row], selectedColumns[column]);
353            }
354        });
355
356        return subMatrix;
357    }
358
359    /** {@inheritDoc} */
360    @Override
361    public void copySubMatrix(final int startRow, final int endRow,
362                              final int startColumn, final int endColumn,
363                              final T[][] destination)
364    throws MatrixDimensionMismatchException, NumberIsTooSmallException,
365    OutOfRangeException{
366        // safety checks
367        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
368        final int rowsCount    = endRow + 1 - startRow;
369        final int columnsCount = endColumn + 1 - startColumn;
370        if (destination.length < rowsCount || destination[0].length < columnsCount) {
371            throw new MatrixDimensionMismatchException(destination.length,
372                                                       destination[0].length,
373                                                       rowsCount,
374                                                       columnsCount);
375        }
376
377        // copy entries
378        walkInOptimizedOrder(new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
379
380            /** Initial row index. */
381            private int startRow;
382
383            /** Initial column index. */
384            private int startColumn;
385
386            /** {@inheritDoc} */
387            @Override
388            public void start(final int rows, final int columns,
389                              final int startRow, final int endRow,
390                              final int startColumn, final int endColumn) {
391                this.startRow    = startRow;
392                this.startColumn = startColumn;
393            }
394
395            /** {@inheritDoc} */
396            @Override
397            public void visit(final int row, final int column, final T value) {
398                destination[row - startRow][column - startColumn] = value;
399            }
400        }, startRow, endRow, startColumn, endColumn);
401    }
402
403    /** {@inheritDoc} */
404    @Override
405    public void copySubMatrix(int[] selectedRows, int[] selectedColumns, T[][] destination)
406        throws MatrixDimensionMismatchException, NoDataException,
407        NullArgumentException, OutOfRangeException {
408        // safety checks
409        checkSubMatrixIndex(selectedRows, selectedColumns);
410        if (destination.length < selectedRows.length ||
411            destination[0].length < selectedColumns.length) {
412            throw new MatrixDimensionMismatchException(destination.length,
413                                                       destination[0].length,
414                                                       selectedRows.length,
415                                                       selectedColumns.length);
416        }
417
418        // copy entries
419        for (int i = 0; i < selectedRows.length; i++) {
420            final T[] destinationI = destination[i];
421            for (int j = 0; j < selectedColumns.length; j++) {
422                destinationI[j] = getEntry(selectedRows[i], selectedColumns[j]);
423            }
424        }
425    }
426
427    /** {@inheritDoc} */
428    @Override
429    public void setSubMatrix(final T[][] subMatrix, final int row,
430                             final int column)
431        throws DimensionMismatchException, OutOfRangeException,
432        NoDataException, NullArgumentException {
433        if (subMatrix == null) {
434            throw new NullArgumentException();
435        }
436        final int nRows = subMatrix.length;
437        if (nRows == 0) {
438            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
439        }
440
441        final int nCols = subMatrix[0].length;
442        if (nCols == 0) {
443            throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
444        }
445
446        for (int r = 1; r < nRows; ++r) {
447            if (subMatrix[r].length != nCols) {
448                throw new DimensionMismatchException(nCols, subMatrix[r].length);
449            }
450        }
451
452        checkRowIndex(row);
453        checkColumnIndex(column);
454        checkRowIndex(nRows + row - 1);
455        checkColumnIndex(nCols + column - 1);
456
457        for (int i = 0; i < nRows; ++i) {
458            for (int j = 0; j < nCols; ++j) {
459                setEntry(row + i, column + j, subMatrix[i][j]);
460            }
461        }
462    }
463
464    /** {@inheritDoc} */
465    @Override
466    public FieldMatrix<T> getRowMatrix(final int row) throws OutOfRangeException {
467        checkRowIndex(row);
468        final int nCols = getColumnDimension();
469        final FieldMatrix<T> out = createMatrix(1, nCols);
470        for (int i = 0; i < nCols; ++i) {
471            out.setEntry(0, i, getEntry(row, i));
472        }
473
474        return out;
475    }
476
477    /** {@inheritDoc} */
478    @Override
479    public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
480        throws OutOfRangeException, MatrixDimensionMismatchException {
481        checkRowIndex(row);
482        final int nCols = getColumnDimension();
483        if (matrix.getRowDimension() != 1 ||
484            matrix.getColumnDimension() != nCols) {
485            throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
486                                                       matrix.getColumnDimension(),
487                                                       1, nCols);
488        }
489        for (int i = 0; i < nCols; ++i) {
490            setEntry(row, i, matrix.getEntry(0, i));
491        }
492    }
493
494    /** {@inheritDoc} */
495    @Override
496    public FieldMatrix<T> getColumnMatrix(final int column)
497    throws OutOfRangeException {
498
499        checkColumnIndex(column);
500        final int nRows = getRowDimension();
501        final FieldMatrix<T> out = createMatrix(nRows, 1);
502        for (int i = 0; i < nRows; ++i) {
503            out.setEntry(i, 0, getEntry(i, column));
504        }
505
506        return out;
507    }
508
509    /** {@inheritDoc} */
510    @Override
511    public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
512        throws OutOfRangeException, MatrixDimensionMismatchException {
513        checkColumnIndex(column);
514        final int nRows = getRowDimension();
515        if (matrix.getRowDimension() != nRows ||
516            matrix.getColumnDimension() != 1) {
517            throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
518                                                       matrix.getColumnDimension(),
519                                                       nRows, 1);
520        }
521        for (int i = 0; i < nRows; ++i) {
522            setEntry(i, column, matrix.getEntry(i, 0));
523        }
524    }
525
526    /** {@inheritDoc} */
527    @Override
528    public FieldVector<T> getRowVector(final int row)
529        throws OutOfRangeException {
530        return new ArrayFieldVector<>(field, getRow(row), false);
531    }
532
533    /** {@inheritDoc} */
534    @Override
535    public void setRowVector(final int row, final FieldVector<T> vector)
536        throws OutOfRangeException, MatrixDimensionMismatchException {
537        checkRowIndex(row);
538        final int nCols = getColumnDimension();
539        if (vector.getDimension() != nCols) {
540            throw new MatrixDimensionMismatchException(1, vector.getDimension(),
541                                                       1, nCols);
542        }
543        for (int i = 0; i < nCols; ++i) {
544            setEntry(row, i, vector.getEntry(i));
545        }
546    }
547
548    /** {@inheritDoc} */
549    @Override
550    public FieldVector<T> getColumnVector(final int column)
551        throws OutOfRangeException {
552        return new ArrayFieldVector<>(field, getColumn(column), false);
553    }
554
555    /** {@inheritDoc} */
556    @Override
557    public void setColumnVector(final int column, final FieldVector<T> vector)
558        throws OutOfRangeException, MatrixDimensionMismatchException {
559
560        checkColumnIndex(column);
561        final int nRows = getRowDimension();
562        if (vector.getDimension() != nRows) {
563            throw new MatrixDimensionMismatchException(vector.getDimension(), 1,
564                                                       nRows, 1);
565        }
566        for (int i = 0; i < nRows; ++i) {
567            setEntry(i, column, vector.getEntry(i));
568        }
569    }
570
571    /** {@inheritDoc} */
572    @Override
573    public T[] getRow(final int row) throws OutOfRangeException {
574        checkRowIndex(row);
575        final int nCols = getColumnDimension();
576        final T[] out = MathArrays.buildArray(field, nCols);
577        for (int i = 0; i < nCols; ++i) {
578            out[i] = getEntry(row, i);
579        }
580
581        return out;
582    }
583
584    /** {@inheritDoc} */
585    @Override
586    public void setRow(final int row, final T[] array)
587        throws OutOfRangeException, MatrixDimensionMismatchException {
588        checkRowIndex(row);
589        final int nCols = getColumnDimension();
590        if (array.length != nCols) {
591            throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
592        }
593        for (int i = 0; i < nCols; ++i) {
594            setEntry(row, i, array[i]);
595        }
596    }
597
598    /** {@inheritDoc} */
599    @Override
600    public T[] getColumn(final int column) throws OutOfRangeException {
601        checkColumnIndex(column);
602        final int nRows = getRowDimension();
603        final T[] out = MathArrays.buildArray(field, nRows);
604        for (int i = 0; i < nRows; ++i) {
605            out[i] = getEntry(i, column);
606        }
607
608        return out;
609    }
610
611    /** {@inheritDoc} */
612    @Override
613    public void setColumn(final int column, final T[] array)
614        throws OutOfRangeException, MatrixDimensionMismatchException {
615        checkColumnIndex(column);
616        final int nRows = getRowDimension();
617        if (array.length != nRows) {
618            throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
619        }
620        for (int i = 0; i < nRows; ++i) {
621            setEntry(i, column, array[i]);
622        }
623    }
624
625    /** {@inheritDoc} */
626    @Override
627    public abstract T getEntry(int row, int column) throws OutOfRangeException;
628
629    /** {@inheritDoc} */
630    @Override
631    public abstract void setEntry(int row, int column, T value) throws OutOfRangeException;
632
633    /** {@inheritDoc} */
634    @Override
635    public abstract void addToEntry(int row, int column, T increment) throws OutOfRangeException;
636
637    /** {@inheritDoc} */
638    @Override
639    public abstract void multiplyEntry(int row, int column, T factor) throws OutOfRangeException;
640
641    /** {@inheritDoc} */
642    @Override
643    public FieldMatrix<T> transpose() {
644        final int nRows = getRowDimension();
645        final int nCols = getColumnDimension();
646        final FieldMatrix<T> out = createMatrix(nCols, nRows);
647        walkInOptimizedOrder(new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
648            /** {@inheritDoc} */
649            @Override
650            public void visit(final int row, final int column, final T value) {
651                out.setEntry(column, row, value);
652            }
653        });
654
655        return out;
656    }
657
658    /** {@inheritDoc} */
659    @Override
660    public T getTrace() throws NonSquareMatrixException {
661        final int nRows = getRowDimension();
662        final int nCols = getColumnDimension();
663        if (nRows != nCols) {
664            throw new NonSquareMatrixException(nRows, nCols);
665       }
666        T trace = field.getZero();
667        for (int i = 0; i < nRows; ++i) {
668            trace = trace.add(getEntry(i, i));
669        }
670        return trace;
671    }
672
673    /** {@inheritDoc} */
674    @Override
675    public T[] operate(final T[] v) throws DimensionMismatchException {
676
677        final int nRows = getRowDimension();
678        final int nCols = getColumnDimension();
679        if (v.length != nCols) {
680            throw new DimensionMismatchException(v.length, nCols);
681        }
682
683        final T[] out = MathArrays.buildArray(field, nRows);
684        for (int row = 0; row < nRows; ++row) {
685            T sum = field.getZero();
686            for (int i = 0; i < nCols; ++i) {
687                sum = sum.add(getEntry(row, i).multiply(v[i]));
688            }
689            out[row] = sum;
690        }
691
692        return out;
693    }
694
695    /** {@inheritDoc} */
696    @Override
697    public FieldVector<T> operate(final FieldVector<T> v)
698        throws DimensionMismatchException {
699        if (v instanceof ArrayFieldVector) {
700            return new ArrayFieldVector<>(field, operate(((ArrayFieldVector<T>) v).getDataRef()), false);
701        }
702
703        final int nRows = getRowDimension();
704        final int nCols = getColumnDimension();
705        if (v.getDimension() != nCols) {
706            throw new DimensionMismatchException(v.getDimension(), nCols);
707        }
708
709        final T[] out = MathArrays.buildArray(field, nRows);
710        for (int row = 0; row < nRows; ++row) {
711            T sum = field.getZero();
712            for (int i = 0; i < nCols; ++i) {
713                sum = sum.add(getEntry(row, i).multiply(v.getEntry(i)));
714            }
715            out[row] = sum;
716        }
717
718        return new ArrayFieldVector<>(field, out, false);
719    }
720
721    /** {@inheritDoc} */
722    @Override
723    public T[] preMultiply(final T[] v) throws DimensionMismatchException {
724
725        final int nRows = getRowDimension();
726        final int nCols = getColumnDimension();
727        if (v.length != nRows) {
728            throw new DimensionMismatchException(v.length, nRows);
729        }
730
731        final T[] out = MathArrays.buildArray(field, nCols);
732        for (int col = 0; col < nCols; ++col) {
733            T sum = field.getZero();
734            for (int i = 0; i < nRows; ++i) {
735                sum = sum.add(getEntry(i, col).multiply(v[i]));
736            }
737            out[col] = sum;
738        }
739
740        return out;
741    }
742
743    /** {@inheritDoc} */
744    @Override
745    public FieldVector<T> preMultiply(final FieldVector<T> v)
746        throws DimensionMismatchException {
747        if (v instanceof ArrayFieldVector) {
748            return new ArrayFieldVector<>(field, preMultiply(((ArrayFieldVector<T>) v).getDataRef()), false);
749        }
750
751        final int nRows = getRowDimension();
752        final int nCols = getColumnDimension();
753        if (v.getDimension() != nRows) {
754            throw new DimensionMismatchException(v.getDimension(), nRows);
755        }
756
757        final T[] out = MathArrays.buildArray(field, nCols);
758        for (int col = 0; col < nCols; ++col) {
759            T sum = field.getZero();
760            for (int i = 0; i < nRows; ++i) {
761                sum = sum.add(getEntry(i, col).multiply(v.getEntry(i)));
762            }
763            out[col] = sum;
764        }
765
766        return new ArrayFieldVector<>(field, out, false);
767    }
768
769    /** {@inheritDoc} */
770    @Override
771    public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
772        final int rows    = getRowDimension();
773        final int columns = getColumnDimension();
774        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
775        for (int row = 0; row < rows; ++row) {
776            for (int column = 0; column < columns; ++column) {
777                final T oldValue = getEntry(row, column);
778                final T newValue = visitor.visit(row, column, oldValue);
779                setEntry(row, column, newValue);
780            }
781        }
782        return visitor.end();
783    }
784
785    /** {@inheritDoc} */
786    @Override
787    public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
788        final int rows    = getRowDimension();
789        final int columns = getColumnDimension();
790        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
791        for (int row = 0; row < rows; ++row) {
792            for (int column = 0; column < columns; ++column) {
793                visitor.visit(row, column, getEntry(row, column));
794            }
795        }
796        return visitor.end();
797    }
798
799    /** {@inheritDoc} */
800    @Override
801    public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
802                            final int startRow, final int endRow,
803                            final int startColumn, final int endColumn)
804        throws NumberIsTooSmallException, OutOfRangeException {
805        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
806        visitor.start(getRowDimension(), getColumnDimension(),
807                      startRow, endRow, startColumn, endColumn);
808        for (int row = startRow; row <= endRow; ++row) {
809            for (int column = startColumn; column <= endColumn; ++column) {
810                final T oldValue = getEntry(row, column);
811                final T newValue = visitor.visit(row, column, oldValue);
812                setEntry(row, column, newValue);
813            }
814        }
815        return visitor.end();
816    }
817
818    /** {@inheritDoc} */
819    @Override
820    public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
821                            final int startRow, final int endRow,
822                            final int startColumn, final int endColumn)
823        throws NumberIsTooSmallException, OutOfRangeException {
824        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
825        visitor.start(getRowDimension(), getColumnDimension(),
826                      startRow, endRow, startColumn, endColumn);
827        for (int row = startRow; row <= endRow; ++row) {
828            for (int column = startColumn; column <= endColumn; ++column) {
829                visitor.visit(row, column, getEntry(row, column));
830            }
831        }
832        return visitor.end();
833    }
834
835    /** {@inheritDoc} */
836    @Override
837    public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor) {
838        final int rows    = getRowDimension();
839        final int columns = getColumnDimension();
840        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
841        for (int column = 0; column < columns; ++column) {
842            for (int row = 0; row < rows; ++row) {
843                final T oldValue = getEntry(row, column);
844                final T newValue = visitor.visit(row, column, oldValue);
845                setEntry(row, column, newValue);
846            }
847        }
848        return visitor.end();
849    }
850
851    /** {@inheritDoc} */
852    @Override
853    public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor) {
854        final int rows    = getRowDimension();
855        final int columns = getColumnDimension();
856        visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
857        for (int column = 0; column < columns; ++column) {
858            for (int row = 0; row < rows; ++row) {
859                visitor.visit(row, column, getEntry(row, column));
860            }
861        }
862        return visitor.end();
863    }
864
865    /** {@inheritDoc} */
866    @Override
867    public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor,
868                               final int startRow, final int endRow,
869                               final int startColumn, final int endColumn)
870    throws NumberIsTooSmallException, OutOfRangeException {
871        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
872        visitor.start(getRowDimension(), getColumnDimension(),
873                      startRow, endRow, startColumn, endColumn);
874        for (int column = startColumn; column <= endColumn; ++column) {
875            for (int row = startRow; row <= endRow; ++row) {
876                final T oldValue = getEntry(row, column);
877                final T newValue = visitor.visit(row, column, oldValue);
878                setEntry(row, column, newValue);
879            }
880        }
881        return visitor.end();
882    }
883
884    /** {@inheritDoc} */
885    @Override
886    public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor,
887                               final int startRow, final int endRow,
888                               final int startColumn, final int endColumn)
889    throws NumberIsTooSmallException, OutOfRangeException{
890        checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
891        visitor.start(getRowDimension(), getColumnDimension(),
892                      startRow, endRow, startColumn, endColumn);
893        for (int column = startColumn; column <= endColumn; ++column) {
894            for (int row = startRow; row <= endRow; ++row) {
895                visitor.visit(row, column, getEntry(row, column));
896            }
897        }
898        return visitor.end();
899    }
900
901    /** {@inheritDoc} */
902    @Override
903    public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
904        return walkInRowOrder(visitor);
905    }
906
907    /** {@inheritDoc} */
908    @Override
909    public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
910        return walkInRowOrder(visitor);
911    }
912
913    /** {@inheritDoc} */
914    @Override
915    public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor,
916                                  final int startRow, final int endRow,
917                                  final int startColumn, final int endColumn)
918        throws NumberIsTooSmallException, OutOfRangeException {
919        return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
920    }
921
922    /** {@inheritDoc} */
923    @Override
924    public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor,
925                                  final int startRow, final int endRow,
926                                  final int startColumn, final int endColumn)
927        throws NumberIsTooSmallException, OutOfRangeException {
928        return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
929    }
930
931    /**
932     * Get a string representation for this matrix.
933     * @return a string representation for this matrix
934     */
935    @Override
936    public String toString() {
937        final int nRows = getRowDimension();
938        final int nCols = getColumnDimension();
939        final StringBuffer res = new StringBuffer();
940        String fullClassName = getClass().getName();
941        String shortClassName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
942        res.append(shortClassName).append("{");
943
944        for (int i = 0; i < nRows; ++i) {
945            if (i > 0) {
946                res.append(",");
947            }
948            res.append("{");
949            for (int j = 0; j < nCols; ++j) {
950                if (j > 0) {
951                    res.append(",");
952                }
953                res.append(getEntry(i, j));
954            }
955            res.append("}");
956        }
957
958        res.append("}");
959        return res.toString();
960    }
961
962    /**
963     * Returns true iff <code>object</code> is a
964     * <code>FieldMatrix</code> instance with the same dimensions as this
965     * and all corresponding matrix entries are equal.
966     *
967     * @param object the object to test equality against.
968     * @return true if object equals this
969     */
970    @Override
971    public boolean equals(final Object object) {
972        if (object == this ) {
973            return true;
974        }
975        if (!(object instanceof FieldMatrix<?>)) {
976            return false;
977        }
978        FieldMatrix<?> m = (FieldMatrix<?>) object;
979        final int nRows = getRowDimension();
980        final int nCols = getColumnDimension();
981        if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) {
982            return false;
983        }
984        for (int row = 0; row < nRows; ++row) {
985            for (int col = 0; col < nCols; ++col) {
986                if (!getEntry(row, col).equals(m.getEntry(row, col))) {
987                    return false;
988                }
989            }
990        }
991        return true;
992    }
993
994    /**
995     * Computes a hashcode for the matrix.
996     *
997     * @return hashcode for matrix
998     */
999    @Override
1000    public int hashCode() {
1001        int ret = 322562;
1002        final int nRows = getRowDimension();
1003        final int nCols = getColumnDimension();
1004        ret = ret * 31 + nRows;
1005        ret = ret * 31 + nCols;
1006        for (int row = 0; row < nRows; ++row) {
1007            for (int col = 0; col < nCols; ++col) {
1008               ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) * getEntry(row, col).hashCode();
1009           }
1010        }
1011        return ret;
1012    }
1013
1014    /**
1015     * Check if a row index is valid.
1016     *
1017     * @param row Row index to check.
1018     * @throws OutOfRangeException if {@code index} is not valid.
1019     */
1020    protected void checkRowIndex(final int row) throws OutOfRangeException {
1021        if (row < 0 || row >= getRowDimension()) {
1022            throw new OutOfRangeException(LocalizedFormats.ROW_INDEX,
1023                                          row, 0, getRowDimension() - 1);
1024        }
1025    }
1026
1027    /**
1028     * Check if a column index is valid.
1029     *
1030     * @param column Column index to check.
1031     * @throws OutOfRangeException if {@code index} is not valid.
1032     */
1033    protected void checkColumnIndex(final int column)
1034        throws OutOfRangeException {
1035        if (column < 0 || column >= getColumnDimension()) {
1036            throw new OutOfRangeException(LocalizedFormats.COLUMN_INDEX,
1037                                          column, 0, getColumnDimension() - 1);
1038        }
1039    }
1040
1041    /**
1042     * Check if submatrix ranges indices are valid.
1043     * Rows and columns are indicated counting from 0 to n-1.
1044     *
1045     * @param startRow Initial row index.
1046     * @param endRow Final row index.
1047     * @param startColumn Initial column index.
1048     * @param endColumn Final column index.
1049     * @throws OutOfRangeException if the indices are not valid.
1050     * @throws NumberIsTooSmallException if {@code endRow < startRow} or
1051     * {@code endColumn < startColumn}.
1052     */
1053    protected void checkSubMatrixIndex(final int startRow, final int endRow,
1054                                       final int startColumn, final int endColumn)
1055        throws NumberIsTooSmallException, OutOfRangeException {
1056        checkRowIndex(startRow);
1057        checkRowIndex(endRow);
1058        if (endRow < startRow) {
1059            throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
1060                                                endRow, startRow, true);
1061        }
1062
1063        checkColumnIndex(startColumn);
1064        checkColumnIndex(endColumn);
1065        if (endColumn < startColumn) {
1066            throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
1067                                                endColumn, startColumn, true);
1068        }
1069    }
1070
1071    /**
1072     * Check if submatrix ranges indices are valid.
1073     * Rows and columns are indicated counting from 0 to n-1.
1074     *
1075     * @param selectedRows Array of row indices.
1076     * @param selectedColumns Array of column indices.
1077     * @throws NullArgumentException if the arrays are {@code null}.
1078     * @throws NoDataException if the arrays have zero length.
1079     * @throws OutOfRangeException if row or column selections are not valid.
1080     */
1081    protected void checkSubMatrixIndex(final int[] selectedRows, final int[] selectedColumns)
1082        throws NoDataException, NullArgumentException, OutOfRangeException {
1083        if (selectedRows == null ||
1084            selectedColumns == null) {
1085            throw new NullArgumentException();
1086        }
1087        if (selectedRows.length == 0 ||
1088            selectedColumns.length == 0) {
1089            throw new NoDataException();
1090        }
1091
1092        for (final int row : selectedRows) {
1093            checkRowIndex(row);
1094        }
1095        for (final int column : selectedColumns) {
1096            checkColumnIndex(column);
1097        }
1098    }
1099}