15b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass/*
25b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * Copyright (C) 2017 The Android Open Source Project
35b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass *
45b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * Licensed under the Apache License, Version 2.0 (the "License");
55b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * you may not use this file except in compliance with the License.
65b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * You may obtain a copy of the License at
75b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass *
85b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass *      http://www.apache.org/licenses/LICENSE-2.0
95b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass *
105b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * Unless required by applicable law or agreed to in writing, software
115b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * distributed under the License is distributed on an "AS IS" BASIS,
125b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * See the License for the specific language governing permissions and
145b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * limitations under the License.
155b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass */
165b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
175b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plasspackage com.android.server.wifi.util;
185b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
195b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass/**
205b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass * Utility for doing basic matix calculations
215b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass */
225b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plasspublic class Matrix {
235b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public final int n;
245b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public final int m;
255b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public final double[] mem;
265b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
275b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
285b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Creates a new matrix, initialized to zeros
295b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
305b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param rows - number of rows (n)
315b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param cols - number of columns (m)
325b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
335b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix(int rows, int cols) {
345b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        n = rows;
355b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        m = cols;
365b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        mem = new double[rows * cols];
375b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
385b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
395b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
405b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Creates a new matrix using the provided array of values
415b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * <p>
425b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Values are in row-major order.
435b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
445b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param stride is the number of columns.
455b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param values is the array of values.
465b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if length of values array not a multiple of stride
475b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
485b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix(int stride, double[] values) {
495b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        n = (values.length + stride - 1) / stride;
505b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        m = stride;
515b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        mem = values;
525b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (mem.length != n * m) throw new IllegalArgumentException();
535b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
545b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
555b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
565b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Creates a new matrix duplicating the given one
575b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
585b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that is the source Matrix.
595b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
605b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix(Matrix that) {
615b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        n = that.n;
625b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        m = that.m;
635b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        mem = new double[that.mem.length];
645b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
655b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            mem[i] = that.mem[i];
665b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
675b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
685b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
695b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
705b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Gets the matrix coefficient from row i, column j
715b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
725b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param i row number
735b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param j column number
745b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return Coefficient at i,j
755b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IndexOutOfBoundsException if an index is out of bounds
765b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
775b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public double get(int i, int j) {
785b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(0 <= i && i < n && 0 <= j && j < m)) throw new IndexOutOfBoundsException();
795b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return mem[i * m + j];
805b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
815b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
825b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
835b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Store a matrix coefficient in row i, column j
845b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
855b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param i row number
865b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param j column number
875b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param v Coefficient to store at i,j
885b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IndexOutOfBoundsException if an index is out of bounds
895b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
905b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public void put(int i, int j, double v) {
915b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(0 <= i && i < n && 0 <= j && j < m)) throw new IndexOutOfBoundsException();
925b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        mem[i * m + j] = v;
935b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
945b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
955b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
965b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the sum of two matrices, this and that
975b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
985b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that is the other matrix
995b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return newly allocated matrix representing the sum of this and that
1005b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes differ
1015b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
1025b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix plus(Matrix that) {
1035b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return plus(that, new Matrix(n, m));
1045b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1055b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
1065b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1075b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
1085b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the sum of two matrices, this and that
1095b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
1105b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that   is the other matrix
1115b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param result is space to hold the result
1125b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return result, filled with the matrix sum
1135b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes differ
1145b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
1155b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix plus(Matrix that, Matrix result) {
1165b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(this.n == that.n && this.m == that.m && this.n == result.n && this.m == result.m)) {
1175b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            throw new IllegalArgumentException();
1185b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
1195b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
1205b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            result.mem[i] = this.mem[i] + that.mem[i];
1215b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
1225b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return result;
1235b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
1245b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1255b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
1265b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the difference of two matrices, this and that
1275b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
1285b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that is the other matrix
1295b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return newly allocated matrix representing the difference of this and that
1305b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes differ
1315b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
1325b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix minus(Matrix that) {
1335b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return minus(that, new Matrix(n, m));
1345b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
1355b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1365b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
1375b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the difference of two matrices, this and that
1385b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
1395b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that   is the other matrix
1405b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param result is space to hold the result
1415b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return result, filled with the matrix difference
1425b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes differ
1435b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
1445b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix minus(Matrix that, Matrix result) {
1455b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(this.n == that.n && this.m == that.m && this.n == result.n && this.m == result.m)) {
1465b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            throw new IllegalArgumentException();
1475b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
1485b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
1495b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            result.mem[i] = this.mem[i] - that.mem[i];
1505b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
1515b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return result;
1525b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
1535b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1545b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
15551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * Forms a scalar product
15651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     *
15751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param scalar is the value to multiply by
15851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @return newly allocated matrix representing the product this and scalar
15951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     */
16051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    public Matrix times(double scalar) {
16151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        return times(scalar, new Matrix(n, m));
16251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    }
16351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass
16451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    /**
16551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * Forms a scalar product
16651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     *
16751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param scalar is the value to multiply by
16851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param result is space to hold the result
16951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @return result, filled with the matrix difference
17051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @throws IllegalArgumentException if shapes differ
17151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     */
17251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    public Matrix times(double scalar, Matrix result) {
17351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        if (!(this.n == result.n && this.m == result.m)) {
17451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass            throw new IllegalArgumentException();
17551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        }
17651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        for (int i = 0; i < mem.length; i++) {
17751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass            result.mem[i] = this.mem[i] * scalar;
17851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        }
17951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        return result;
18051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    }
18151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass
18251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    /**
1835b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the matrix product of two matrices, this and that
1845b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
1855b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that is the other matrix
1865b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return newly allocated matrix representing the matrix product of this and that
1875b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes are not conformant
1885b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
1895b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix dot(Matrix that) {
1905b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return dot(that, new Matrix(this.n, that.m));
1915b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
1925b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
1935b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
1945b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the matrix product of two matrices, this and that
1955b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * <p>
1965b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Caller supplies an object to contain the result, as well as scratch space
1975b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
1985b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param that   is the other matrix
1995b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param result is space to hold the result
2005b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return result, filled with the matrix product
2015b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shapes are not conformant
2025b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
2035b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix dot(Matrix that, Matrix result) {
2045b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(this.n == result.n && this.m == that.n && that.m == result.m)) {
2055b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            throw new IllegalArgumentException();
2065b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
2075b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < n; i++) {
2085b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int j = 0; j < that.m; j++) {
2095b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                double s = 0.0;
2105b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                for (int k = 0; k < m; k++) {
2115b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    s += this.get(i, k) * that.get(k, j);
2125b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                }
2135b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                result.put(i, j, s);
2145b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
2155b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
2165b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return result;
2175b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
2185b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2195b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
2205b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the matrix transpose
2215b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
2225b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return newly allocated transpose matrix
2235b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
2245b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix transpose() {
2255b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return transpose(new Matrix(m, n));
2265b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
2275b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2285b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
2295b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the matrix transpose
2305b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * <p>
2315b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Caller supplies an object to contain the result
2325b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
2335b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param result is space to hold the result
2345b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return result, filled with the matrix transpose
2355b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if result shape is wrong
2365b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
2375b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix transpose(Matrix result) {
2385b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(this.n == result.m && this.m == result.n)) throw new IllegalArgumentException();
2395b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < n; i++) {
2405b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int j = 0; j < m; j++) {
2415b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                result.put(j, i, get(i, j));
2425b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
2435b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
2445b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return result;
2455b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
2465b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2475b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
2485b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the inverse of a square matrix
2495b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
2505b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return newly allocated matrix representing the matrix inverse
2515b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws ArithmeticException if the matrix is not invertible
2525b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
2535b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix inverse() {
2545b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return inverse(new Matrix(n, m), new Matrix(n, 2 * m));
2555b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
2565b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2575b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
2585b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Forms the inverse of a square matrix
2595b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
2605b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param result  is space to hold the result
2615b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @param scratch is workspace of dimension n by 2*n
2625b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return result, filled with the matrix inverse
2635b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws ArithmeticException if the matrix is not invertible
2645b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @throws IllegalArgumentException if shape of scratch or result is wrong
2655b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
2665b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public Matrix inverse(Matrix result, Matrix scratch) {
2675b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(n == m && n == result.n && m == result.m && n == scratch.n && 2 * m == scratch.m)) {
2685b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            throw new IllegalArgumentException();
2695b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
2705b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2715b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < n; i++) {
2725b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int j = 0; j < m; j++) {
2735b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                scratch.put(i, j, get(i, j));
2745b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                scratch.put(i, m + j, i == j ? 1.0 : 0.0);
2755b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
2765b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
2775b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
2785b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < n; i++) {
2795b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            int ibest = i;
2805b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            double vbest = Math.abs(scratch.get(ibest, ibest));
2815b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int ii = i + 1; ii < n; ii++) {
2825b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                double v = Math.abs(scratch.get(ii, i));
2835b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                if (v > vbest) {
2845b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    ibest = ii;
2855b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    vbest = v;
2865b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                }
2875b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
2885b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            if (ibest != i) {
2895b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                for (int j = 0; j < scratch.m; j++) {
2905b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    double t = scratch.get(i, j);
2915b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    scratch.put(i, j, scratch.get(ibest, j));
2925b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    scratch.put(ibest, j, t);
2935b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                }
2945b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
2955b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            double d = scratch.get(i, i);
2965b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            if (d == 0.0) throw new ArithmeticException("Singular matrix");
2975b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int j = 0; j < scratch.m; j++) {
2985b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                scratch.put(i, j, scratch.get(i, j) / d);
2995b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
3005b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int ii = i + 1; ii < n; ii++) {
3015b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                d = scratch.get(ii, i);
3025b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                for (int j = 0; j < scratch.m; j++) {
3035b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    scratch.put(ii, j, scratch.get(ii, j) - d * scratch.get(i, j));
3045b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                }
3055b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
3065b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
3075b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = n - 1; i >= 0; i--) {
3085b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int ii = 0; ii < i; ii++) {
3095b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                double d = scratch.get(ii, i);
3105b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                for (int j = 0; j < scratch.m; j++) {
3115b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                    scratch.put(ii, j, scratch.get(ii, j) - d * scratch.get(i, j));
3125b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                }
3135b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
3145b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
3155b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < result.n; i++) {
3165b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            for (int j = 0; j < result.m; j++) {
3175b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass                result.put(i, j, scratch.get(i, m + j));
3185b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            }
3195b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
3205b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return result;
3215b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
32251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    /**
32351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * Forms the matrix product with the transpose of a second matrix
32451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     *
32551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param that is the other matrix
32651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @return newly allocated matrix representing the matrix product of this and that.transpose()
32751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @throws IllegalArgumentException if shapes are not conformant
32851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     */
32951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    public Matrix dotTranspose(Matrix that) {
33051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        return dotTranspose(that, new Matrix(this.n, that.n));
33151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    }
33251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass
33351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    /**
33451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * Forms the matrix product with the transpose of a second matrix
33551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * <p>
33651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * Caller supplies an object to contain the result, as well as scratch space
33751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     *
33851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param that is the other matrix
33951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @param result is space to hold the result
34051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @return result, filled with the matrix product of this and that.transpose()
34151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     * @throws IllegalArgumentException if shapes are not conformant
34251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass     */
34351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    public Matrix dotTranspose(Matrix that, Matrix result) {
34451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        if (!(this.n == result.n && this.m == that.m && that.n == result.m)) {
34551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass            throw new IllegalArgumentException();
34651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        }
34751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        for (int i = 0; i < n; i++) {
34851bb91940298ca771a98591f8682a02ebe4bca12Michael Plass            for (int j = 0; j < that.n; j++) {
34951bb91940298ca771a98591f8682a02ebe4bca12Michael Plass                double s = 0.0;
35051bb91940298ca771a98591f8682a02ebe4bca12Michael Plass                for (int k = 0; k < m; k++) {
35151bb91940298ca771a98591f8682a02ebe4bca12Michael Plass                    s += this.get(i, k) * that.get(j, k);
35251bb91940298ca771a98591f8682a02ebe4bca12Michael Plass                }
35351bb91940298ca771a98591f8682a02ebe4bca12Michael Plass                result.put(i, j, s);
35451bb91940298ca771a98591f8682a02ebe4bca12Michael Plass            }
35551bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        }
35651bb91940298ca771a98591f8682a02ebe4bca12Michael Plass        return result;
35751bb91940298ca771a98591f8682a02ebe4bca12Michael Plass    }
3585b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
3595b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
3605b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Tests for equality
3615b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
3625b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    @Override
3635b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public boolean equals(Object that) {
3645b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (this == that) return true;
3655b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (!(that instanceof Matrix)) return false;
3665b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        Matrix other = (Matrix) that;
3675b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (n != other.n) return false;
3685b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        if (m != other.m) return false;
3695b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
3705b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            if (mem[i] != other.mem[i]) return false;
3715b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
3725b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return true;
3735b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
3745b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
3755b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
3765b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Calculates a hash code
3775b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
3785b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    @Override
3795b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public int hashCode() {
3805b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        int h = n * 101 + m;
3815b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
3825b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            h = h * 37 + Double.hashCode(mem[i]);
3835b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
3845b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return h;
3855b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
3865b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
3875b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    /**
3885b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * Makes a string representation
3895b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     *
3905b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     * @return string like "[a, b; c, d]"
3915b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass     */
3925b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    @Override
3935b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    public String toString() {
3945b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        StringBuilder sb = new StringBuilder(n * m * 8);
3955b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        sb.append("[");
3965b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        for (int i = 0; i < mem.length; i++) {
3975b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            if (i > 0) sb.append(i % m == 0 ? "; " : ", ");
3985b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass            sb.append(mem[i]);
3995b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        }
4005b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        sb.append("]");
4015b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass        return sb.toString();
4025b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass    }
4035b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass
4045b0fa1e4851b9f4a8fd8efe7afa89b575be727bdMichael Plass}
405