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 */
017package org.apache.commons.math4.legacy.field.linalg;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.ArrayList;
022import org.apache.commons.numbers.field.Field;
023import org.apache.commons.math4.legacy.linear.AnyMatrix;
024
025/**
026 * Square matrix whose elements define a {@link Field}.
027 *
028 * @param <T> Type of the field elements.
029 *
030 * @since 4.0
031 */
032public final class FieldDenseMatrix<T>
033    implements AnyMatrix {
034    /** Field. */
035    private final Field<T> field;
036    /** Number of rows. */
037    private final int rows;
038    /** Number of columns. */
039    private final int columns;
040    /**
041     * Data storage (in row-major order).
042     *
043     * <p>Note: This is an Object[] that has been cast to T[] for convenience. It should not be
044     * exposed to avoid heap pollution. It is expected all entries stored in the array are
045     * instances of T.
046     */
047    private final T[] data;
048
049    /**
050     * @param f Field.
051     * @param r Number of rows.
052     * @param c Number of columns.
053     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
054     */
055    @SuppressWarnings("unchecked")
056    private FieldDenseMatrix(Field<T> f,
057                             int r,
058                             int c) {
059        if (r <= 0 ||
060            c <= 0) {
061            throw new IllegalArgumentException("Negative size");
062        }
063
064        field = f;
065        rows = r;
066        columns = c;
067        data = (T[]) new Object[r * c];
068    }
069
070    /**
071     * Factory method.
072     *
073     * @param <T> Type of the field elements.
074     * @param f Field.
075     * @param r Number of rows.
076     * @param c Number of columns.
077     * @return a new instance.
078     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
079     */
080    public static <T> FieldDenseMatrix<T> create(Field<T> f,
081                                                 int r,
082                                                 int c) {
083        return new FieldDenseMatrix<>(f, r, c);
084    }
085
086    /**
087     * Factory method.
088     *
089     * @param <T> Type of the field elements.
090     * @param f Field.
091     * @param r Number of rows.
092     * @param c Number of columns.
093     * @return a matrix with elements zet to {@link Field#zero() zero}.
094     * @throws IllegalArgumentException if {@code r <= 0} or {@code c <= 0}.
095     */
096    public static <T> FieldDenseMatrix<T> zero(Field<T> f,
097                                               int r,
098                                               int c) {
099        return create(f, r, c).fill(f.zero());
100    }
101
102    /**
103     * Factory method.
104     *
105     * @param <T> Type of the field elements.
106     * @param f Field.
107     * @param n Dimension of the matrix.
108     * @throws IllegalArgumentException if {@code n <= 0}.
109     * @return the identity matrix.
110     */
111    public static <T> FieldDenseMatrix<T> identity(Field<T> f,
112                                                   int n) {
113        final FieldDenseMatrix<T> r = zero(f, n, n);
114
115        for (int i = 0; i < n; i++) {
116            r.set(i, i, f.one());
117        }
118
119        return r;
120    }
121
122    /** {@inheritDoc} */
123    @Override
124    public boolean equals(Object other) {
125        if (this == other) {
126            return true;
127        } else {
128            if (other instanceof FieldDenseMatrix) {
129                final FieldDenseMatrix<?> m = (FieldDenseMatrix<?>) other;
130                return field.equals(m.field) &&
131                    rows == m.rows &&
132                    columns == m.columns &&
133                    Arrays.equals(data, m.data);
134            } else {
135                return false;
136            }
137        }
138    }
139
140    /**
141     * Copies this matrix.
142     *
143     * @return a new instance.
144     */
145    public FieldDenseMatrix<T> copy() {
146        final FieldDenseMatrix<T> r = create(field, rows, columns);
147        System.arraycopy(data, 0, r.data, 0, data.length);
148        return r;
149    }
150
151    /**
152     * Transposes this matrix.
153     *
154     * @return a new instance.
155     */
156    public FieldDenseMatrix<T> transpose() {
157        final FieldDenseMatrix<T> r = create(field, columns, rows);
158
159        for (int i = 0; i < rows; i++) {
160            for (int j = 0; j < columns; j++) {
161                r.set(j, i, get(i, j));
162            }
163        }
164
165        return r;
166    }
167
168    /** {@inheritDoc} */
169    @Override
170    public int getRowDimension() {
171        return rows;
172    }
173
174    /** {@inheritDoc} */
175    @Override
176    public int getColumnDimension() {
177        return columns;
178    }
179
180    /**
181     * @return the field associated with the matrix entries.
182     */
183    public Field<T> getField() {
184        return field;
185    }
186
187    /**
188     * Sets all elements to the given value.
189     *
190     * @param value Value of the elements of the matrix.
191     * @return {@code this}.
192     */
193    public FieldDenseMatrix<T> fill(T value) {
194        Arrays.fill(data, value);
195        return this;
196    }
197
198    /**
199     * Gets an element.
200     *
201     * @param i Row.
202     * @param j Column.
203     * @return the element at (i, j).
204     */
205    public T get(int i,
206                 int j) {
207        return data[i * columns + j];
208    }
209
210    /**
211     * Sets an element.
212     *
213     * @param i Row.
214     * @param j Column.
215     * @param value Value.
216     */
217    public void set(int i,
218                    int j,
219                    T value) {
220        data[i * columns + j] = value;
221    }
222
223    /**
224     * Addition.
225     *
226     * @param other Matrix to add.
227     * @return a new instance with the result of the addition.
228     * @throws IllegalArgumentException if the dimensions do not match.
229     */
230    public FieldDenseMatrix<T> add(FieldDenseMatrix<T> other) {
231        checkAdd(other);
232        final FieldDenseMatrix<T> r = create(field, rows, columns);
233
234        for (int i = 0; i < data.length; i++) {
235            r.data[i] = field.add(data[i], other.data[i]);
236        }
237
238        return r;
239    }
240
241    /**
242     * Subtraction.
243     *
244     * @param other Matrix to subtract.
245     * @return a new instance with the result of the subtraction.
246     * @throws IllegalArgumentException if the dimensions do not match.
247     */
248    public FieldDenseMatrix<T> subtract(FieldDenseMatrix<T> other) {
249        checkAdd(other);
250        final FieldDenseMatrix<T> r = create(field, rows, columns);
251
252        for (int i = 0; i < data.length; i++) {
253            r.data[i] = field.subtract(data[i], other.data[i]);
254        }
255
256        return r;
257    }
258
259    /**
260     * Negate.
261     *
262     * @return a new instance with the opposite matrix.
263     */
264    public FieldDenseMatrix<T> negate() {
265        final FieldDenseMatrix<T> r = create(field, rows, columns);
266
267        for (int i = 0; i < data.length; i++) {
268            r.data[i] = field.negate(data[i]);
269        }
270
271        return r;
272    }
273
274    /**
275     * Multiplication.
276     *
277     * @param other Matrix to multiply with.
278     * @return a new instance with the result of the multiplication.
279     * @throws IllegalArgumentException if the dimensions do not match.
280     */
281    public FieldDenseMatrix<T> multiply(FieldDenseMatrix<T> other) {
282        checkMultiply(other);
283        final FieldDenseMatrix<T> r = zero(field, rows, other.columns);
284
285        for (int i = 0; i < rows; i++) {
286            final int o1 = i * columns;
287            final int o2 = i * r.columns;
288            for (int j = 0; j < other.columns; j++) {
289                final int o3 = o2 + j;
290                for (int k = 0; k < columns; k++) {
291                    r.data[o3] = field.add(r.data[o3],
292                                           field.multiply(data[o1 + k],
293                                                          other.data[k * other.columns + j]));
294                }
295            }
296        }
297
298        return r;
299    }
300
301    /**
302     * Multiplies the matrix with itself {@code p} times.
303     *
304     * @param p Exponent.
305     * @return a new instance.
306     * @throws IllegalArgumentException if {@code p < 0}.
307     */
308    public FieldDenseMatrix<T> pow(int p) {
309        checkMultiply(this);
310
311        if (p < 0) {
312            throw new IllegalArgumentException("Negative exponent: " + p);
313        }
314
315        if (p == 0) {
316            return identity(field, rows);
317        }
318
319        if (p == 1) {
320            return copy();
321        }
322
323        final int power = p - 1;
324
325        // Only log_2(p) operations are necessary by doing as follows:
326        //    5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
327        // The same approach is used for A^p.
328
329        final char[] binary = Integer.toBinaryString(power).toCharArray();
330        final ArrayList<Integer> nonZeroPositions = new ArrayList<>();
331
332        for (int i = 0; i < binary.length; i++) {
333            if (binary[i] == '1') {
334                final int pos = binary.length - i - 1;
335                nonZeroPositions.add(pos);
336            }
337        }
338
339        final List<FieldDenseMatrix<T>> results = new ArrayList<>(binary.length);
340        results.add(this);
341        for (int i = 1; i < binary.length; i++) {
342            final FieldDenseMatrix<T> s = results.get(i - 1);
343            final FieldDenseMatrix<T> r = s.multiply(s);
344            results.add(r);
345        }
346
347        FieldDenseMatrix<T> r = this;
348        for (Integer i : nonZeroPositions) {
349            r = r.multiply(results.get(i));
350        }
351
352        return r;
353    }
354
355    /** {@inheritDoc} */
356    @Override
357    public int hashCode() {
358        assert false : "hashCode not designed";
359        return 42; // Any arbitrary constant will do.
360    }
361}