UNPKG

maths.ts

Version:

Math utilities library for TypeScript, JavaScript and Node.js

308 lines (307 loc) 10.1 kB
"use strict"; /** * @author Hector J. Vasquez <ipi.vasquez@gmail.com> * * @licence * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const Node_1 = require("../core/Node"); const $ = require("jquery"); /** * An implementation of a Matrix in mathematics. This matrix may be a matrix * of any kind of mathematical expression: number, expression... */ class Matrix { /** * Builds a matrix from the dimensions of the matrix or its representation * as an Array or as a Matrix object. * @param m May be a the representation of the matrix as an Array or as * a Matrix; or, it may be the number of rows for this Matrix. * @param n The number of columns of the matrix in case of the first * argument (m) is a number. */ constructor(m = 1, n = 1) { if (typeof m === 'number') { this.mxnConstructor(m, n); } else { this.matrixConstructor(m); } } /** * Gets the number of rows in this matrix. * @return The number of rows in matrix. */ get M() { return this.matrix.length; } /** * Get the number of columns in this matrix. * @return The number of columns in this matrix. */ get N() { return this.matrix[0].length; } /** * Creates a copy of the matrix this object represents as an array. * @return A two dimensional array of which this Matrix is representing. */ get nodeMatrix() { return this.matrix.map(row => row.map(Node_1.default.newNode)); } /** * Creates a copy of the matrix this object represents as a two * dimensional array of numbers. If this Matrix contains elements that * cannot be converted as numbers (such as expression with variables) it * will return an undefined in the element that it did not convert. * @return A two dimensional array of numbers of the matrix this object * represents. */ get numberMatrix() { return this.matrix.map(row => row.map(i => i.numberValue)); } /** * Creates a two dimensional array of strings representing each element * in this Matrix. * @return A two dimensional array of each representation of this * elements matrix. */ get stringMatrix() { return this.matrix.map(row => row.map(e => e + '')); } /** * Checks if this is a square matrix. * @return true if this is a square matrix, false otherwise. */ get isSquare() { return this.M === this.N; } /** * Generates the transpose of this matrix. * @return The transpose of this matrix. */ get transpose() { const mt = new Matrix(this.N, this.M); for (let i = 0; i < mt.M; i++) { for (let j = 0; j < mt.N; j++) { mt.matrix[i][j] = this.matrix[j][i].clone(); } } return mt; } /** * Calculates adj(this). * @return The adjucate matrix of this. */ get adjucate() { if (!this.isSquare) { throw new Error('Only a square matrix has adjucate'); } const adj = new Matrix(this.M, this.N); for (let i = 0; i < adj.M; i++) { for (let j = 0; j < adj.N; j++) { if ((i + j) & 1) { adj.matrix[i][j] = calcDeterminant(subMatrix(this.matrix, j, i)).negate(); } else { adj.matrix[i][j] = calcDeterminant(subMatrix(this.matrix, j, i)); } } } return adj; } /** * Generates the inverse of this matrix. * @return This matrix inverse. */ get inverse() { if (!this.isSquare) { throw new Error('Only a square matrix has inverse'); } const inv = this.adjucate; const det = this.determinant; for (const row of inv.matrix) { for (const element of row) { element.divideHere(det); } } return inv; } /** * Calculates the determinant of this Matrix recursively. */ get determinant() { if (!this.isSquare) { throw new Error('Determinant can only be calculated on a square ' + ' matrix'); } return calcDeterminant(this.matrix); } /** * Adds this to another matrix in a new Matrix. * @param m The matrix to add to this. * @return The result of this + m. */ add(m) { if (m.M !== this.M || m.N !== this.N) { throw new Error('The sizes of the matrix does not match to' + ' execute addition between them.'); } const result = new Matrix(this.M, this.N); for (let i = 0; i < this.M; i++) { for (let j = 0; j < this.N; j++) { result.matrix[i][j] = this.matrix[i][j].add(m.matrix[i][j]); } } return result; } /** * Multiplies this to another matrix in a new Matrix. * @param m The matrix to multiply to this. * @return The result of this * m. */ multiply(m) { if (this.N !== m.M) { throw new Error('The sizes of the matrix does not match to' + ' execute multiplication between them.'); } const result = new Matrix(this.M, m.N); for (let i = 0; i < result.M; i++) { for (let j = 0; j < result.N; j++) { result.matrix[i][j] = new Node_1.default(0); for (let k = 0; k < this.N; k++) { result.matrix[i][j] .addHere(this.matrix[i][k].multiply(m.matrix[k][j])); } } } return result; } /** * Considering A as this matrix, Ai as the i-th row and Aij as the * element on the j-th column of i-th row. Eliminates Aij from this * matrix, where i = [0, y)U(y, m) and j = x. * @param x Column to pivot. * @param y Row to pivot. */ pivoting(x, y) { let aux; for (let i = 0; i < this.matrix.length; i++) { if (i !== y) { aux = this.matrix[i][x].clone(); for (let j = 0; j < this.matrix[i].length; j++) { this.matrix[i][j] .subtractHere(this.matrix[y][j].multiply(aux)); } } } } /** * Generates a string for representing this matrix. * @return The representation of this as a string. */ toString() { return this.matrix.map(row => row.join('\t')).join('\n'); } /** * Generates an HTML table representative of this matrix. * @return This as a HTML table. * @throws Error if there is no window with a document. */ toHTMLElement() { const $table = $('<table>'); for (let i = 0; i < this.matrix.length; i++) { const $tr = $('<tr>').appendTo($table); for (let j = 0; j < this.matrix[i].length; j++) { $tr.append($('<td>', { html: this.matrix[i][j] + '' })); } } return $table[0]; } /** * Creates a copy of this matrix. * @return A clone of this. */ clone() { return new Matrix(this.matrix); } /** * Builds this as a MxN Matrix of NaNs. * @param m The number of rows. * @param n The number of columns. */ mxnConstructor(m, n) { this.matrix = []; for (let i = 0; i < m; i++) { this.matrix.push([]); for (let j = 0; j < n; j++) { this.matrix[i].push(new Node_1.default()); } } } /** * Builds this as a matrix by copying matrix. * @param matrix The matrix wanted to be represented as a two * dimensional array of numbers, strings or expressions. */ matrixConstructor(matrix) { if (matrix instanceof Matrix) { matrix = matrix.matrix.map(row => row.map(i => i.clone())); } this.matrix = matrix.map(row => row.map(Node_1.default.newNode)); } } exports.default = Matrix; /** * Calculates recursively the determinant of the given sub matrix until the * matrix received is a 2x2 matrix, then it applies returns the determinant: * m[0][0]*m[1][1] - m[1][0]*m[0][1]. * @param m The matrix wanted to get the determinant. * @return The determinant of m. */ function calcDeterminant(m) { if (m.length === 2) { return m[0][0].multiply(m[1][1]).subtract(m[0][1].multiply(m[1][0])); } else if (m.length === 1) { return m[0][0].clone(); } const det = new Node_1.default(0); for (let i = 0; i < m.length; i++) { if ((i & 1) === 0) { det.addHere(m[0][i].multiply(calcDeterminant(subMatrix(m, 0, i)))); } else { det.subtractHere(m[0][i].multiply(calcDeterminant(subMatrix(m, 0, i)))); } } return det; } /** * Generates a sub matrix of m without a given row and a given column. * @param m The matrix to extract the sub matrix. * @param y The row to ignore. * @param x The column to ignore. * @return A sub matrix without the y-th row and the x-th column. */ function subMatrix(m, y, x) { // TODO const sm = []; for (let i = 0; i < m.length; i++) { if (i !== y) { sm.push(m[i].slice(0, x).concat(m[i].slice(x + 1, m.length))); } } return sm; }