/*
Chalkboard - Matrix Namespace
Version 3.0.2 Euler
Released April 13th, 2026
*/
/*
This Source Code Form is subject to the terms of the
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
///
namespace Chalkboard {
/**
* The matrix namespace.
* @namespace
*/
export namespace matr {
/** @ignore */
const $ = (input: ChalkboardVector): ChalkboardVector => {
const v = input as { x: number, y: number, z?: number, w?: number };
if (v && typeof v.x === "number" && typeof v.y === "number") {
return input as ChalkboardVector;
}
if (Array.isArray(input)) {
if (input.length > 0 && Array.isArray(input[0])) {
const matr = input as ChalkboardMatrix;
const rows = Chalkboard.matr.rows(matr);
const cols = Chalkboard.matr.cols(matr);
if (cols === 1) {
if (rows === 2) return Chalkboard.vect.init(matr[0][0], matr[1][0]);
if (rows === 3) return Chalkboard.vect.init(matr[0][0], matr[1][0], matr[2][0]);
if (rows === 4) return Chalkboard.vect.init(matr[0][0], matr[1][0], matr[2][0], matr[3][0]);
} else if (rows === 1) {
if (cols === 2) return Chalkboard.vect.init(matr[0][0], matr[0][1]);
if (cols === 3) return Chalkboard.vect.init(matr[0][0], matr[0][1], matr[0][2]);
if (cols === 4) return Chalkboard.vect.init(matr[0][0], matr[0][1], matr[0][2], matr[0][3]);
}
} else {
const arr = input as number[];
if (arr.length === 2) return Chalkboard.vect.init(arr[0], arr[1]);
if (arr.length === 3) return Chalkboard.vect.init(arr[0], arr[1], arr[2]);
if (arr.length === 4) return Chalkboard.vect.init(arr[0], arr[1], arr[2], arr[3]);
}
}
if (input instanceof Float32Array || input instanceof Float64Array) {
const arr = input as Float32Array | Float64Array;
if (arr.length === 2) return Chalkboard.vect.init(arr[0], arr[1]);
if (arr.length === 3) return Chalkboard.vect.init(arr[0], arr[1], arr[2]);
if (arr.length === 4) return Chalkboard.vect.init(arr[0], arr[1], arr[2], arr[3]);
}
if (typeof input === "string") {
try {
const parsed = JSON.parse(input as string);
if (parsed && typeof parsed === "object" && typeof parsed.x === "number" && typeof parsed.y === "number") {
return Chalkboard.vect.init(parsed.x, parsed.y, parsed.z !== undefined ? parsed.z : undefined, parsed.w !== undefined ? parsed.w : undefined);
}
} catch (e) {
const str = (input as string).trim();
if (str.startsWith("(") && str.endsWith(")")) {
const content = str.substring(1, str.length - 1);
const components = content.split(",").map(part => parseFloat(part.trim()));
if (components.length >= 2 && components.every(p => !isNaN(p))) {
if (components.length === 2) return Chalkboard.vect.init(components[0], components[1]);
if (components.length === 3) return Chalkboard.vect.init(components[0], components[1], components[2]);
if (components.length === 4) return Chalkboard.vect.init(components[0], components[1], components[2], components[3]);
}
}
}
}
throw new TypeError(`Invalid ChalkboardVector input: ${JSON.stringify(input)}`);
};
/**
* Calculates the absolute value of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const absolute = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([Math.abs(matr[0][0]), Math.abs(matr[0][1])], [Math.abs(matr[1][0]), Math.abs(matr[1][1])]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([Math.abs(matr[0][0]), Math.abs(matr[0][1]), Math.abs(matr[0][2])], [Math.abs(matr[1][0]), Math.abs(matr[1][1]), Math.abs(matr[1][2])], [Math.abs(matr[2][0]), Math.abs(matr[2][1]), Math.abs(matr[2][2])]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([Math.abs(matr[0][0]), Math.abs(matr[0][1]), Math.abs(matr[0][2]), Math.abs(matr[0][3])], [Math.abs(matr[1][0]), Math.abs(matr[1][1]), Math.abs(matr[1][2]), Math.abs(matr[1][3])], [Math.abs(matr[2][0]), Math.abs(matr[2][1]), Math.abs(matr[2][2]), Math.abs(matr[2][3])], [Math.abs(matr[3][0]), Math.abs(matr[3][1]), Math.abs(matr[3][2]), Math.abs(matr[3][3])]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = Math.abs(matr[i][j]);
}
}
return result;
}
};
/**
* Calculates the addition of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {ChalkboardMatrix}
*/
export const add = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeEqual(matr1, matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2)) {
return Chalkboard.matr.init(
[matr1[0][0] + matr2[0][0], matr1[0][1] + matr2[0][1]],
[matr1[1][0] + matr2[1][0], matr1[1][1] + matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3)) {
return Chalkboard.matr.init(
[matr1[0][0] + matr2[0][0], matr1[0][1] + matr2[0][1], matr1[0][2] + matr2[0][2]],
[matr1[1][0] + matr2[1][0], matr1[1][1] + matr2[1][1], matr1[1][2] + matr2[1][2]],
[matr1[2][0] + matr2[2][0], matr1[2][1] + matr2[2][1], matr1[2][2] + matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4)) {
return Chalkboard.matr.init(
[matr1[0][0] + matr2[0][0], matr1[0][1] + matr2[0][1], matr1[0][2] + matr2[0][2], matr1[0][3] + matr2[0][3]],
[matr1[1][0] + matr2[1][0], matr1[1][1] + matr2[1][1], matr1[1][2] + matr2[1][2], matr1[1][3] + matr2[1][3]],
[matr1[2][0] + matr2[2][0], matr1[2][1] + matr2[2][1], matr1[2][2] + matr2[2][2], matr1[2][3] + matr2[2][3]],
[matr1[3][0] + matr2[3][0], matr1[3][1] + matr2[3][1], matr1[3][2] + matr2[3][2], matr1[3][3] + matr2[3][3]]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr1); j++) {
result[i][j] = matr1[i][j] + matr2[i][j];
}
}
return result;
}
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" with equivalent numbers of rows and columns.');
}
};
/**
* Calculates the Kronecker addition of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {ChalkboardMatrix}
*/
export const addKronecker = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSquare(matr1) && Chalkboard.matr.isSquare(matr2)) {
return Chalkboard.matr.add(
Chalkboard.matr.mulKronecker(matr1, Chalkboard.matr.identity(Chalkboard.matr.rows(matr1))),
Chalkboard.matr.mulKronecker(Chalkboard.matr.identity(Chalkboard.matr.rows(matr2)), matr2)
);
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" that are square.');
}
};
/**
* Calculates the adjugate matrix of a matrix at a row and column.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} row - The row
* @param {number} col - The column
* @returns {ChalkboardMatrix}
*/
export const adjugate = (matr: ChalkboardMatrix, row: number, col: number): ChalkboardMatrix => {
return Chalkboard.matr.transpose(Chalkboard.matr.cofactor(matr, row, col));
};
/**
* Calculates the Cholesky decomposition of a symmetric positive definite matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {{ L: ChalkboardMatrix, U: ChalkboardMatrix }}
*/
export const Cholesky = (matr: ChalkboardMatrix): { L: ChalkboardMatrix; U: ChalkboardMatrix } => {
if (!Chalkboard.matr.isSquare(matr)) throw new TypeError('Chalkboard.matr.Cholesky: Parameter "matr" must be a square matrix.');
if (!Chalkboard.matr.isSymmetric(matr)) throw new TypeError('Chalkboard.matr.Cholesky: Parameter "matr" must be symmetric.');
const n = Chalkboard.matr.rows(matr);
const L = Chalkboard.matr.fill(0, n);
for (let i = 0; i < n; i++) {
for (let j = 0; j <= i; j++) {
let sum = matr[i][j];
for (let k = 0; k < j; k++) {
sum -= L[i][k] * L[j][k];
}
if (i === j) {
if (sum <= 0) throw new RangeError('Chalkboard.matr.Cholesky: Matrix is not positive definite.');
L[i][j] = Chalkboard.real.sqrt(sum);
} else {
L[i][j] = sum / L[j][j];
}
}
}
return { L: L, U: Chalkboard.matr.transpose(L) };
};
/**
* Calculates the cofactor matrix of a matrix at a row and column.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} row - The row
* @param {number} col - The column
* @returns {ChalkboardMatrix}
*/
export const cofactor = (matr: ChalkboardMatrix, row: number, col: number): ChalkboardMatrix => {
return matr.slice(0, row).concat(matr.slice(row + 1)).map((row) => row.slice(0, col).concat(row.slice(col + 1)));
};
/**
* Returns the number of columns in a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const cols = (matr: ChalkboardMatrix): number => {
return matr[0].length;
};
/**
* Calculates the column space of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const colspace = (matr: ChalkboardMatrix): ChalkboardMatrix => {
return Chalkboard.matr.transpose(Chalkboard.matr.rowspace(Chalkboard.matr.transpose(matr)));
};
/**
* Calculates the concatentation of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @param {number} [axis=0] - The axis to concatenate over, which is 0 for the rows or 1 for the columns
* @returns {ChalkboardMatrix}
*/
export const concat = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix, axis: 0 | 1 = 0): ChalkboardMatrix => {
if (axis === 0) {
if (Chalkboard.matr.cols(matr1) === Chalkboard.matr.cols(matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2) && Chalkboard.matr.rows(matr2) === 2) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1]], [matr1[1][0], matr1[1][1]],
[matr2[0][0], matr2[0][1]], [matr2[1][0], matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3) && Chalkboard.matr.rows(matr2) === 3) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1], matr1[0][2]],
[matr1[1][0], matr1[1][1], matr1[1][2]],
[matr1[2][0], matr1[2][1], matr1[2][2]],
[matr2[0][0], matr2[0][1], matr2[0][2]],
[matr2[1][0], matr2[1][1], matr2[1][2]],
[matr2[2][0], matr2[2][1], matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4) && Chalkboard.matr.rows(matr2) === 4) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1], matr1[0][2], matr1[0][3]],
[matr1[1][0], matr1[1][1], matr1[1][2], matr1[1][3]],
[matr1[2][0], matr1[2][1], matr1[2][2], matr1[2][3]],
[matr1[3][0], matr1[3][1], matr1[3][2], matr1[3][3]],
[matr2[0][0], matr2[0][1], matr2[0][2], matr2[0][3]],
[matr2[1][0], matr2[1][1], matr2[1][2], matr2[1][3]],
[matr2[2][0], matr2[2][1], matr2[2][2], matr2[2][3]],
[matr2[3][0], matr2[3][1], matr2[3][2], matr2[3][3]]
);
} else {
return Chalkboard.matr.init(matr1.concat(matr2));
}
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" with equivalent numbers of columns.');
}
} else if (axis === 1) {
if (Chalkboard.matr.rows(matr1) === Chalkboard.matr.rows(matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2) && Chalkboard.matr.cols(matr2) === 2) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1], matr2[0][0], matr2[0][1]],
[matr1[1][0], matr1[1][1], matr2[1][0], matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3) && Chalkboard.matr.cols(matr2) === 3) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1], matr1[0][2], matr2[0][0], matr2[0][1], matr2[0][2]],
[matr1[1][0], matr1[1][1], matr1[1][2], matr2[1][0], matr2[1][1], matr2[1][2]],
[matr1[2][0], matr1[2][1], matr1[2][2], matr2[2][0], matr2[2][1], matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4) && Chalkboard.matr.cols(matr2) === 4) {
return Chalkboard.matr.init(
[matr1[0][0], matr1[0][1], matr1[0][2], matr1[0][3], matr2[0][0], matr2[0][1], matr2[0][2], matr2[0][3]],
[matr1[1][0], matr1[1][1], matr1[1][2], matr1[1][3], matr2[1][0], matr2[1][1], matr2[1][2], matr2[1][3]],
[matr1[2][0], matr1[2][1], matr1[2][2], matr1[2][3], matr2[2][0], matr2[2][1], matr2[2][2], matr2[2][3]],
[matr1[3][0], matr1[3][1], matr1[3][2], matr1[3][3], matr2[3][0], matr2[3][1], matr2[3][2], matr2[3][3]]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
result.push(matr1[i].concat(matr2[i]));
}
return result;
}
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" with equivalent numbers of rows.');
}
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
};
/**
* Calculates a matrix constrained within a range.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number[]} [range=[0, 1]] - The range
* @returns {ChalkboardMatrix}
*/
export const constrain = (matr: ChalkboardMatrix, range: [number, number] = [0, 1]): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init(
[Chalkboard.numb.constrain(matr[0][0], range), Chalkboard.numb.constrain(matr[0][1], range)],
[Chalkboard.numb.constrain(matr[1][0], range), Chalkboard.numb.constrain(matr[1][1], range)]
);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init(
[Chalkboard.numb.constrain(matr[0][0], range), Chalkboard.numb.constrain(matr[0][1], range), Chalkboard.numb.constrain(matr[0][2], range)],
[Chalkboard.numb.constrain(matr[1][0], range), Chalkboard.numb.constrain(matr[1][1], range), Chalkboard.numb.constrain(matr[1][2], range)],
[Chalkboard.numb.constrain(matr[2][0], range), Chalkboard.numb.constrain(matr[2][1], range), Chalkboard.numb.constrain(matr[2][2], range)]
);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init(
[Chalkboard.numb.constrain(matr[0][0], range), Chalkboard.numb.constrain(matr[0][1], range), Chalkboard.numb.constrain(matr[0][2], range), Chalkboard.numb.constrain(matr[0][3], range)],
[Chalkboard.numb.constrain(matr[1][0], range), Chalkboard.numb.constrain(matr[1][1], range), Chalkboard.numb.constrain(matr[1][2], range), Chalkboard.numb.constrain(matr[1][3], range)],
[Chalkboard.numb.constrain(matr[2][0], range), Chalkboard.numb.constrain(matr[2][1], range), Chalkboard.numb.constrain(matr[2][2], range), Chalkboard.numb.constrain(matr[2][3], range)],
[Chalkboard.numb.constrain(matr[3][0], range), Chalkboard.numb.constrain(matr[3][1], range), Chalkboard.numb.constrain(matr[3][2], range), Chalkboard.numb.constrain(matr[3][3], range)]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = Chalkboard.numb.constrain(matr[i][j], range);
}
}
return result;
}
};
/**
* Copies a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const copy = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([matr[0][0], matr[0][1]], [matr[1][0], matr[1][1]]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([matr[0][0], matr[0][1], matr[0][2]], [matr[1][0], matr[1][1], matr[1][2]], [matr[2][0], matr[2][1], matr[2][2]]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([matr[0][0], matr[0][1], matr[0][2], matr[0][3]], [matr[1][0], matr[1][1], matr[1][2], matr[1][3]], [matr[2][0], matr[2][1], matr[2][2], matr[2][3]], [matr[3][0], matr[3][1], matr[3][2], matr[3][3]]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result.push([]);
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i].push(matr[i][j]);
}
}
return result;
}
};
/**
* Calculates the determinant of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const det = (matr: ChalkboardMatrix): number => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.rows(matr) === 1) {
return matr[0][0];
} else if (Chalkboard.matr.rows(matr) === 2) {
return matr[0][0] * matr[1][1] - matr[0][1] * matr[1][0];
} else if (Chalkboard.matr.rows(matr) === 3) {
return matr[0][0] * (matr[1][1] * matr[2][2] - matr[1][2] * matr[2][1]) - matr[0][1] * (matr[1][0] * matr[2][2] - matr[1][2] * matr[2][0]) + matr[0][2] * (matr[1][0] * matr[2][1] - matr[1][1] * matr[2][0]);
} else if (Chalkboard.matr.rows(matr) === 4) {
return matr[0][0] * (matr[1][1] * (matr[2][2] * matr[3][3] - matr[2][3] * matr[3][2]) - matr[1][2] * (matr[2][1] * matr[3][3] - matr[2][3] * matr[3][1]) + matr[1][3] * (matr[2][1] * matr[3][2] - matr[2][2] * matr[3][1])) - matr[0][1] * (matr[1][0] * (matr[2][2] * matr[3][3] - matr[2][3] * matr[3][2]) - matr[1][2] * (matr[2][0] * matr[3][3] - matr[2][3] * matr[3][0]) + matr[1][3] * (matr[2][0] * matr[3][2] - matr[2][2] * matr[3][0])) + matr[0][2] * (matr[1][0] * (matr[2][1] * matr[3][3] - matr[2][3] * matr[3][1]) - matr[1][1] * (matr[2][0] * matr[3][3] - matr[2][3] * matr[3][0]) + matr[1][3] * (matr[2][0] * matr[3][1] - matr[2][1] * matr[3][0])) - matr[0][3] * (matr[1][0] * (matr[2][1] * matr[3][2] - matr[2][2] * matr[3][1]) - matr[1][1] * (matr[2][0] * matr[3][2] - matr[2][2] * matr[3][0]) + matr[1][2] * (matr[2][0] * matr[3][1] - matr[2][1] * matr[3][0]));
} else {
let result = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
const cofactor = matr[0][i] * Chalkboard.matr.det(Chalkboard.matr.cofactor(matr, 0, i));
result += i % 2 === 0 ? cofactor : -cofactor;
}
return result;
}
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Initializes a diagonal matrix.
* @param {number} size - The number of rows or columns
* @param {number[]} elements - The elements on the main diagonal
* @returns {ChalkboardMatrix}
*/
export const diagonal = (size: number, ...elements: number[]): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([elements[0] || 0, 0], [0, elements[1] || 0]);
} else if (size === 3) {
return Chalkboard.matr.init([elements[0] || 0, 0, 0], [0, elements[1] || 0, 0], [0, 0, elements[2] || 0]);
} else if (size === 4) {
return Chalkboard.matr.init([elements[0] || 0, 0, 0, 0], [0, elements[1] || 0, 0, 0], [0, 0, elements[2] || 0, 0], [0, 0, 0, elements[3] || 0]);
} else {
elements = Array.isArray(elements[0]) ? elements[0] : elements;
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push(Array(size).fill(0));
result[i][i] = elements[i] || 0;
}
return result;
}
};
/**
* Calculates the dominant eigenvalue of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} [maxIterations=100] - The number of iterations the algorithm runs
* @returns {number}
*/
export const eigenvalue = (matr: ChalkboardMatrix, maxIterations: number = 100): number => {
let v = Chalkboard.matr.fill(1, Chalkboard.matr.rows(matr), 1);
for (let i = 0; i < maxIterations; i++) {
const matrv = Chalkboard.matr.mul(matr, v);
const max = Chalkboard.stat.max(Chalkboard.matr.toArray(Chalkboard.matr.absolute(matrv)));
v = Chalkboard.stat.toMatrix(
Chalkboard.matr.toArray(matrv).map((i) => i / max),
Chalkboard.matr.rows(matr),
1
);
}
const dot = function (v1: number[], v2: number[]): number {
let result = 0;
for (let i = 0; i < v1.length; i++) {
result += v1[i] * v2[i];
}
return result;
};
return (
dot(Chalkboard.matr.toArray(Chalkboard.matr.transpose(v)), Chalkboard.matr.toArray(Chalkboard.matr.mul(matr, v))) /
dot(Chalkboard.matr.toArray(Chalkboard.matr.transpose(v)), Chalkboard.matr.toArray(v))
);
};
/**
* Calculates the dominant eigenvector of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} [maxIterations=100] - The number of iterations the algorithm runs
* @returns {number[]}
*/
export const eigenvector = (matr: ChalkboardMatrix, maxIterations: number = 100): number[] => {
let v = Chalkboard.matr.fill(1, Chalkboard.matr.rows(matr), 1);
for (let i = 0; i < maxIterations; i++) {
const matrv = Chalkboard.matr.mul(matr, v);
const max = Chalkboard.stat.max(Chalkboard.matr.toArray(Chalkboard.matr.absolute(matrv)));
v = Chalkboard.stat.toMatrix(Chalkboard.matr.toArray(matrv).map((i) => i / max), Chalkboard.matr.rows(matr), 1 );
}
const result = Chalkboard.matr.toArray(v);
return result;
};
/**
* Initializes an empty matrix.
* @param {number} rows - The number of rows or (if the cols parameter is blank) the number of rows or columns (the size)
* @param {number} [cols=rows] - The number of columns
* @returns {ChalkboardMatrix}
*/
export const empty = (rows: number, cols: number = rows): ChalkboardMatrix => {
const _null = null as unknown as number;
if (rows === 2 && cols === 2) {
return Chalkboard.matr.init([_null, _null], [_null, _null]);
} else if (rows === 3 && cols === 3) {
return Chalkboard.matr.init([_null, _null, _null], [_null, _null, _null], [_null, _null, _null]);
} else if (rows === 4 && cols === 4) {
return Chalkboard.matr.init([_null, _null, _null, _null], [_null, _null, _null, _null], [_null, _null, _null, _null], [_null, _null, _null, _null]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < rows; i++) {
result.push([]);
for (let j = 0; j < cols; j++) {
result[i].push(_null);
}
}
return result;
}
};
/**
* Initializes an exchange matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const exchange = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([0, 1], [1, 0]);
} else if (size === 3) {
return Chalkboard.matr.init([0, 0, 1], [0, 1, 0], [1, 0, 0]);
} else if (size === 4) {
return Chalkboard.matr.init([0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]);
} else {
const result = Chalkboard.matr.fill(0, size, size);
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
if (i + j === size - 1) {
result[i][j] = 1;
}
}
}
return result;
}
};
/**
* Initializes a matrix filled with one number.
* @param {number} element - The number to fill the elements with
* @param {number} rows - The number of rows or (if the cols parameter is blank) the number of rows or columns (the size)
* @param {number} [cols=rows] - The number of columns
* @returns {ChalkboardMatrix}
*/
export const fill = (element: number, rows: number, cols: number = rows): ChalkboardMatrix => {
if (rows === 2 && cols === 2) {
return Chalkboard.matr.init([element, element], [element, element]);
} else if (rows === 3 && cols === 3) {
return Chalkboard.matr.init([element, element, element], [element, element, element], [element, element, element]);
} else if (rows === 4 && cols === 4) {
return Chalkboard.matr.init([element, element, element, element], [element, element, element, element], [element, element, element, element], [element, element, element, element]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < rows; i++) {
result.push([]);
for (let j = 0; j < cols; j++) {
result[i].push(element);
}
}
return result;
}
};
/**
* Calculates the row echelon form of a matrix (performs Gaussian elimination on it).
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const Gaussian = (matr: ChalkboardMatrix): ChalkboardMatrix => {
let lead = 0;
for (let row = 0; row < Chalkboard.matr.rows(matr); row++) {
if (lead >= Chalkboard.matr.cols(matr)) {
break;
}
let i = row;
while (matr[i][lead] === 0) {
i++;
if (i === Chalkboard.matr.rows(matr)) {
i = row;
lead++;
if (Chalkboard.matr.cols(matr) === lead) {
return matr;
}
}
}
const temp = matr[i];
matr[i] = matr[row];
matr[row] = temp;
const scl = matr[row][lead];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
matr[row][j] /= scl;
}
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
if (i !== row) {
const coeff = matr[i][lead];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
matr[i][j] -= coeff * matr[row][j];
}
}
}
lead++;
}
return matr;
};
/**
* Initializes a Hilbert matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const Hilbert = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1 / 1, 1 / 2], [1 / 2, 1 / 3]);
} else if (size === 3) {
return Chalkboard.matr.init([1 / 1, 1 / 2, 1 / 3], [1 / 2, 1 / 3, 1 / 4], [1 / 3, 1 / 4, 1 / 5]);
} else if (size === 4) {
return Chalkboard.matr.init([1 / 1, 1 / 2, 1 / 3, 1 / 4], [1 / 2, 1 / 3, 1 / 4, 1 / 5], [1 / 3, 1 / 4, 1 / 5, 1 / 6], [1 / 4, 1 / 5, 1 / 6, 1 / 7]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push([]);
for (let j = 0; j < size; j++) {
result[i].push(1 / (i + j + 1));
}
}
return result;
}
};
/**
* Initializes an identity matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const identity = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1, 0], [0, 1]);
} else if (size === 3) {
return Chalkboard.matr.init([1, 0, 0], [0, 1, 0], [0, 0, 1]);
} else if (size === 4) {
return Chalkboard.matr.init([1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push(Array(size).fill(0));
result[i][i] = 1;
}
return result;
}
};
/**
* Initializes a new matrix.
* @param {number[][] | number[][][]} matrix - Either a sequence of 1D arrays to be used as rows for the matrix or one 2D array to be used as the matrix
* @returns {ChalkboardMatrix}
* @example
* // Defines a 3x3 matrix [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
* let A = Chalkboard.matr.init([1, 2, 3],
* [4, 5, 6],
* [7, 8, 9]);
* let B = Chalkboard.matr.init([[1, 2, 3],
* [4, 5, 6],
* [7, 8, 9]]);
*/
export const init = (...matrix: number[][] | number[][][]): ChalkboardMatrix => {
if (matrix.length === 0) {
return [];
} else if (Array.isArray(matrix[0]) && Array.isArray(matrix[0][0])) {
return (matrix as number[][][])[0];
} else {
return matrix as number[][];
}
};
/**
* Calculates the inverse of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const invert = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isInvertible(matr)) {
if (Chalkboard.matr.rows(matr) === 2) {
const det = Chalkboard.matr.det(matr);
return Chalkboard.matr.init(
[matr[1][1] / det, -matr[0][1] / det],
[-matr[1][0] / det, matr[0][0] / det]
);
} else if (Chalkboard.matr.rows(matr) === 3) {
const det = Chalkboard.matr.det(matr);
return Chalkboard.matr.init(
[(matr[1][1] * matr[2][2] - matr[1][2] * matr[2][1]) / det, (matr[0][2] * matr[2][1] - matr[0][1] * matr[2][2]) / det, (matr[0][1] * matr[1][2] - matr[0][2] * matr[1][1]) / det],
[(matr[1][2] * matr[2][0] - matr[1][0] * matr[2][2]) / det, (matr[0][0] * matr[2][2] - matr[0][2] * matr[2][0]) / det, (matr[0][2] * matr[1][0] - matr[0][0] * matr[1][2]) / det],
[(matr[1][0] * matr[2][1] - matr[1][1] * matr[2][0]) / det, (matr[0][1] * matr[2][0] - matr[0][0] * matr[2][1]) / det, (matr[0][0] * matr[1][1] - matr[0][1] * matr[1][0]) / det]
);
} else if (Chalkboard.matr.rows(matr) === 4) {
const det = Chalkboard.matr.det(matr);
const adj00 = matr[0][0] * matr[1][1] - matr[0][1] * matr[1][0],
adj01 = matr[0][0] * matr[1][2] - matr[0][2] * matr[1][0],
adj02 = matr[0][0] * matr[1][3] - matr[0][3] * matr[1][0],
adj03 = matr[0][1] * matr[1][2] - matr[0][2] * matr[1][1],
adj04 = matr[0][1] * matr[1][3] - matr[0][3] * matr[1][1],
adj05 = matr[0][2] * matr[1][3] - matr[0][3] * matr[1][2],
adj06 = matr[2][0] * matr[3][1] - matr[2][1] * matr[3][0],
adj07 = matr[2][0] * matr[3][2] - matr[2][2] * matr[3][0],
adj08 = matr[2][0] * matr[3][3] - matr[2][3] * matr[3][0],
adj09 = matr[2][1] * matr[3][2] - matr[2][2] * matr[3][1],
adj10 = matr[2][1] * matr[3][3] - matr[2][3] * matr[3][1],
adj11 = matr[2][2] * matr[3][3] - matr[2][3] * matr[3][2];
return Chalkboard.matr.init(
[(matr[1][1] * adj11 - matr[1][2] * adj10 + matr[1][3] * adj09) / det, (matr[0][2] * adj10 - matr[0][1] * adj11 - matr[0][3] * adj09) / det, (matr[3][1] * adj05 - matr[3][2] * adj04 + matr[3][3] * adj03) / det, (matr[2][2] * adj04 - matr[2][1] * adj05 - matr[2][3] * adj03) / det],
[(matr[1][2] * adj08 - matr[1][0] * adj11 - matr[1][3] * adj07) / det, (matr[0][0] * adj11 - matr[0][2] * adj08 + matr[0][3] * adj07) / det, (matr[3][2] * adj02 - matr[3][0] * adj05 - matr[3][3] * adj01) / det, (matr[2][0] * adj05 - matr[2][2] * adj02 + matr[2][3] * adj01) / det],
[(matr[1][0] * adj10 - matr[1][1] * adj08 + matr[1][3] * adj06) / det, (matr[0][1] * adj08 - matr[0][0] * adj10 - matr[0][3] * adj06) / det, (matr[3][0] * adj04 - matr[3][1] * adj02 + matr[3][3] * adj00) / det, (matr[2][1] * adj02 - matr[2][0] * adj04 - matr[2][3] * adj00) / det],
[(matr[1][1] * adj07 - matr[1][0] * adj09 - matr[1][2] * adj06) / det, (matr[0][0] * adj09 - matr[0][1] * adj07 + matr[0][2] * adj06) / det, (matr[3][1] * adj01 - matr[3][0] * adj03 - matr[3][2] * adj00) / det, (matr[2][0] * adj03 - matr[2][1] * adj01 + matr[2][2] * adj00) / det]
);
} else {
const result = Chalkboard.matr.init();
const augmented = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
augmented.push(matr[i].concat(Array(Chalkboard.matr.rows(matr)).fill(0)));
augmented[i][Chalkboard.matr.cols(matr) + i] = 1;
}
for (let row = 0; row < Chalkboard.matr.rows(matr); row++) {
let diagonal = augmented[row][row];
if (diagonal === 0) {
let max = row;
for (let i = row + 1; i < Chalkboard.matr.rows(matr); i++) {
if (Math.abs(augmented[i][row]) > Math.abs(augmented[max][row])) {
max = i;
}
}
const temp = augmented[row];
augmented[row] = augmented[max];
augmented[max] = temp;
diagonal = augmented[row][row];
}
for (let col = 0; col < 2 * Chalkboard.matr.cols(matr); col++) {
augmented[row][col] /= diagonal;
}
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
if (i !== row) {
const coeff = augmented[i][row];
for (let j = 0; j < 2 * Chalkboard.matr.cols(matr); j++) {
augmented[i][j] -= coeff * augmented[row][j];
}
}
}
}
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result.push(augmented[i].slice(Chalkboard.matr.cols(matr), 2 * Chalkboard.matr.cols(matr)));
}
return result;
}
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square and has a non-zero determinant.');
}
};
/**
* Checks if two matrices are approximately equal to each other within a particular precision.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @param {number} [precision=0.000001] - The precision to check
* @returns {boolean}
*/
export const isApproxEqual = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix, precision: number = 0.000001): boolean => {
if (Chalkboard.matr.isSizeEqual(matr1, matr2)) {
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
for (let j = 0; j < Chalkboard.matr.cols(matr1); j++) {
if (!Chalkboard.numb.isApproxEqual(matr1[i][j], matr2[i][j], precision)) {
return false;
}
}
}
return true;
} else {
return false;
}
};
/**
* Checks if a matrix is a diagonal matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isDiagonal = (matr: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.numb.isApproxEqual(matr[0][1], 0) && Chalkboard.numb.isApproxEqual(matr[1][0], 0);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.numb.isApproxEqual(matr[0][1], 0) && Chalkboard.numb.isApproxEqual(matr[0][2], 0) && Chalkboard.numb.isApproxEqual(matr[1][0], 0) && Chalkboard.numb.isApproxEqual(matr[1][2], 0) && Chalkboard.numb.isApproxEqual(matr[2][0], 0) && Chalkboard.numb.isApproxEqual(matr[2][1], 0);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.numb.isApproxEqual(matr[0][1], 0) && Chalkboard.numb.isApproxEqual(matr[0][2], 0) && Chalkboard.numb.isApproxEqual(matr[0][3], 0) && Chalkboard.numb.isApproxEqual(matr[1][0], 0) && Chalkboard.numb.isApproxEqual(matr[1][2], 0) && Chalkboard.numb.isApproxEqual(matr[1][3], 0) && Chalkboard.numb.isApproxEqual(matr[2][0], 0) && Chalkboard.numb.isApproxEqual(matr[2][1], 0) && Chalkboard.numb.isApproxEqual(matr[2][3], 0) && Chalkboard.numb.isApproxEqual(matr[3][0], 0) && Chalkboard.numb.isApproxEqual(matr[3][1], 0) && Chalkboard.numb.isApproxEqual(matr[3][2], 0);
} else {
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
if (i !== j && !Chalkboard.numb.isApproxEqual(matr[i][j], 0)) return false;
}
}
return true;
}
} else {
return false;
}
};
/**
* Checks if two matrices are equal.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {boolean}
*/
export const isEqual = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isSizeEqual(matr1, matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2)) {
return matr1[0][0] === matr2[0][0] && matr1[0][1] === matr2[0][1] && matr1[1][0] === matr2[1][0] && matr1[1][1] === matr2[1][1];
} else if (Chalkboard.matr.isSizeOf(matr1, 3)) {
return matr1[0][0] === matr2[0][0] && matr1[0][1] === matr2[0][1] && matr1[0][2] === matr2[0][2] && matr1[1][0] === matr2[1][0] && matr1[1][1] === matr2[1][1] && matr1[1][2] === matr2[1][2] && matr1[2][0] === matr2[2][0] && matr1[2][1] === matr2[2][1] && matr1[2][2] === matr2[2][2];
} else if (Chalkboard.matr.isSizeOf(matr1, 4)) {
return matr1[0][0] === matr2[0][0] && matr1[0][1] === matr2[0][1] && matr1[0][2] === matr2[0][2] && matr1[0][3] === matr2[0][3] && matr1[1][0] === matr2[1][0] && matr1[1][1] === matr2[1][1] && matr1[1][2] === matr2[1][2] && matr1[1][3] === matr2[1][3] && matr1[2][0] === matr2[2][0] && matr1[2][1] === matr2[2][1] && matr1[2][2] === matr2[2][2] && matr1[2][3] === matr2[2][3] && matr1[3][0] === matr2[3][0] && matr1[3][1] === matr2[3][1] && matr1[3][2] === matr2[3][2] && matr1[3][3] === matr2[3][3];
} else {
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
for (let j = 0; j < Chalkboard.matr.cols(matr2); j++) {
if (!(matr1[i][j] === matr2[i][j])) return false;
}
}
return true;
}
} else {
return false;
}
};
/**
* Checks if a matrix is an identity matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isIdentity = (matr: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isDiagonal(matr)) {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.identity(2));
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.identity(3));
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.identity(4));
} else {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.identity(Chalkboard.matr.rows(matr)));
}
} else {
return false;
}
};
/**
* Checks if a matrix is invertible.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isInvertible = (matr: ChalkboardMatrix): boolean => {
return Chalkboard.matr.isSquare(matr) && Chalkboard.matr.det(matr) !== 0;
};
/**
* Checks if a matrix is a lower triangular matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isLowerTriangular = (matr: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], 0], [matr[1][0], matr[1][1]]));
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], 0, 0], [matr[1][0], matr[1][1], 0], [matr[2][0], matr[2][1], matr[2][2]]));
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], 0, 0, 0], [matr[1][0], matr[1][1], 0, 0], [matr[2][0], matr[2][1], matr[2][2], 0], [matr[3][0], matr[3][1], matr[3][2], matr[3][3]]));
} else {
let score = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
for (let j = i + 1; j < Chalkboard.matr.cols(matr); j++) {
if (matr[i][j] !== 0) score++;
}
}
return score === 0;
}
} else {
return false;
}
};
/**
* Checks if a matrix is orthogonal.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isOrthogonal = (matr: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isInvertible(matr)) {
return Chalkboard.matr.isApproxEqual(Chalkboard.matr.transpose(matr), Chalkboard.matr.invert(matr));
} else {
return false;
}
};
/**
* Checks if two matrices have equal sizes.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {boolean}
*/
export const isSizeEqual = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): boolean => {
return Chalkboard.matr.rows(matr1) === Chalkboard.matr.rows(matr2) && Chalkboard.matr.cols(matr1) === Chalkboard.matr.cols(matr2);
};
/**
* Checks if a matrix is of a particular size.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} rows - The number of rows or (if the cols parameter is blank) the number of rows or columns (the size)
* @param {number} [cols=rows] - The number of columns
*/
export const isSizeOf = (matr: ChalkboardMatrix, rows: number, cols: number = rows): boolean => {
return Chalkboard.matr.rows(matr) === rows && Chalkboard.matr.cols(matr) === cols;
};
/**
* Checks if a matrix is skew-symmetric.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isSkewSymmetric = (matr: ChalkboardMatrix): boolean => {
return Chalkboard.matr.isEqual(Chalkboard.matr.transpose(matr), Chalkboard.matr.negate(matr));
};
/**
* Checks if a matrix is square.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isSquare = (matr: ChalkboardMatrix): boolean => {
return Chalkboard.matr.rows(matr) === Chalkboard.matr.cols(matr);
};
/**
* Checks if a matrix is symmetric.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isSymmetric = (matr: ChalkboardMatrix): boolean => {
return Chalkboard.matr.isEqual(matr, Chalkboard.matr.transpose(matr));
};
/**
* Checks if a matrix is an upper triangular matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isUpperTriangular = (matr: ChalkboardMatrix): boolean => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], matr[0][1]], [0, matr[1][1]]));
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], matr[0][1], matr[0][2]], [0, matr[1][1], matr[1][2]], [0, 0, matr[2][2]]));
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.init([matr[0][0], matr[0][1], matr[0][2], matr[0][3]], [0, matr[1][1], matr[1][2], matr[1][3]], [0, 0, matr[2][2], matr[2][3]], [0, 0, 0, matr[3][3]]));
} else {
let score = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
for (let j = 0; j < i; j++) {
if (matr[i][j] !== 0) score++;
}
}
return score === 0;
}
} else {
return false;
}
};
/**
* Checks if a matrix is a zero matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {boolean}
*/
export const isZero = (matr: ChalkboardMatrix): boolean => {
return Chalkboard.matr.isApproxEqual(matr, Chalkboard.matr.zero(Chalkboard.matr.rows(matr), Chalkboard.matr.cols(matr)));
};
/**
* Initializes a Lehmer matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const Lehmer = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1 / 1, 1 / 2], [1 / 2, 1 / 1]);
} else if (size === 3) {
return Chalkboard.matr.init([1 / 1, 1 / 2, 1 / 3], [1 / 2, 1 / 1, 2 / 3], [1 / 3, 2 / 3, 1 / 1]);
} else if (size === 4) {
return Chalkboard.matr.init([1 / 1, 1 / 2, 1 / 3, 1 / 4], [1 / 2, 1 / 1, 2 / 3, 1 / 2], [1 / 3, 2 / 3, 1 / 1, 3 / 4], [1 / 4, 1 / 1, 3 / 4, 1 / 1]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push([]);
for (let j = 0; j < size; j++) {
result[i].push(Math.min(i + 1, j + 1) / Math.max(i + 1, j + 1));
}
}
return result;
}
};
/**
* Initializes a lower binomial matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const lowerBinomial = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1, 0], [1, 1]);
} else if (size === 3) {
return Chalkboard.matr.init([1, 0, 0], [1, 1, 0], [1, 2, 1]);
} else if (size === 4) {
return Chalkboard.matr.init([1, 0, 0, 0], [1, 1, 0, 0], [1, 2, 1, 0], [1, 3, 3, 1]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push([]);
for (let j = 0; j < size; j++) {
result[i].push(Chalkboard.numb.binomial(i, j));
}
}
return result;
}
};
/**
* Initializes a lower shift matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const lowerShift = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([0, 0], [1, 0]);
} else if (size === 3) {
return Chalkboard.matr.init([0, 0, 0], [1, 0, 0], [0, 1, 0]);
} else if (size === 4) {
return Chalkboard.matr.init([0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result[i] = [];
for (let j = 0; j < size; j++) {
result[i][j] = Chalkboard.numb.Kronecker(i, j + 1);
}
}
return result;
}
};
/**
* Initializes a lower triangular matrix.
* @param {number} size - The number of rows or columns
* @param {number[]} elements - The elements on and below the main diagonal
* @returns {ChalkboardMatrix}
*/
export const lowerTriangular = (size: number, ...elements: number[]): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([elements[0] || 0, 0], [elements[1] || 0, elements[2] || 0]);
} else if (size === 3) {
return Chalkboard.matr.init([elements[0] || 0, 0, 0], [elements[1] || 0, elements[2] || 0, 0], [elements[3] || 0, elements[4] || 0, elements[5] || 0]);
} else if (size === 4) {
return Chalkboard.matr.init(
[elements[0] || 0, 0, 0, 0],
[elements[1] || 0, elements[2] || 0, 0, 0],
[elements[3] || 0, elements[4] || 0, elements[5] || 0, 0],
[elements[6] || 0, elements[7] || 0, elements[8] || 0, elements[9] || 0]
);
} else {
elements = Array.isArray(elements[0]) ? elements[0] : elements;
const result = Chalkboard.matr.init();
let index = 0;
for (let i = 0; i < size; i++) {
result[i] = [];
for (let j = 0; j < size; j++) {
result[i][j] = j <= i ? elements[index++] || 0 : 0;
}
}
return result;
}
};
/**
* Calculates the LU decomposition of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {{L: ChalkboardMatrix, U: ChalkboardMatrix}}
*/
export const LUdecomp = (matr: ChalkboardMatrix): { L: ChalkboardMatrix; U: ChalkboardMatrix } => {
if (Chalkboard.matr.isSquare(matr)) {
const L = Chalkboard.matr.identity(Chalkboard.matr.rows(matr)),
U = Chalkboard.matr.fill(0, Chalkboard.matr.rows(matr));
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
for (let i = 0; i <= j; i++) {
let sum = 0;
for (let k = 0; k < i; k++) {
sum += L[i][k] * U[k][j];
}
U[i][j] = matr[i][j] - sum;
}
for (let i = j + 1; i < Chalkboard.matr.rows(matr); i++) {
let sum = 0;
for (let k = 0; k < j; k++) {
sum += L[i][k] * U[k][j];
}
L[i][j] = (matr[i][j] - sum) / U[j][j];
}
}
return { L: L, U: U };
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Calculates the multiplication of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {ChalkboardMatrix}
*/
export const mul = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.cols(matr1) === Chalkboard.matr.rows(matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2) && Chalkboard.matr.isSizeOf(matr2, 2, 1)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 2) && Chalkboard.matr.isSizeOf(matr2, 2)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0], matr1[0][0] * matr2[0][1] + matr1[0][1] * matr2[1][1]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0], matr1[1][0] * matr2[0][1] + matr1[1][1] * matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3) && Chalkboard.matr.isSizeOf(matr2, 3, 1)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0] + matr1[0][2] * matr2[2][0]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0] + matr1[1][2] * matr2[2][0]],
[matr1[2][0] * matr2[0][0] + matr1[2][1] * matr2[1][0] + matr1[2][2] * matr2[2][0]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3) && Chalkboard.matr.isSizeOf(matr2, 3)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0] + matr1[0][2] * matr2[2][0], matr1[0][0] * matr2[0][1] + matr1[0][1] * matr2[1][1] + matr1[0][2] * matr2[2][1], matr1[0][0] * matr2[0][2] + matr1[0][1] * matr2[1][2] + matr1[0][2] * matr2[2][2]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0] + matr1[1][2] * matr2[2][0], matr1[1][0] * matr2[0][1] + matr1[1][1] * matr2[1][1] + matr1[1][2] * matr2[2][1], matr1[1][0] * matr2[0][2] + matr1[1][1] * matr2[1][2] + matr1[1][2] * matr2[2][2]],
[matr1[2][0] * matr2[0][0] + matr1[2][1] * matr2[1][0] + matr1[2][2] * matr2[2][0], matr1[2][0] * matr2[0][1] + matr1[2][1] * matr2[1][1] + matr1[2][2] * matr2[2][1], matr1[2][0] * matr2[0][2] + matr1[2][1] * matr2[1][2] + matr1[2][2] * matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4) && Chalkboard.matr.isSizeOf(matr2, 4, 1)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0] + matr1[0][2] * matr2[2][0] + matr1[0][3] * matr2[3][0]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0] + matr1[1][2] * matr2[2][0] + matr1[1][3] * matr2[3][0]],
[matr1[2][0] * matr2[0][0] + matr1[2][1] * matr2[1][0] + matr1[2][2] * matr2[2][0] + matr1[2][3] * matr2[3][0]],
[matr1[3][0] * matr2[0][0] + matr1[3][1] * matr2[1][0] + matr1[3][2] * matr2[2][0] + matr1[3][3] * matr2[3][0]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4) && Chalkboard.matr.isSizeOf(matr2, 4)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0] + matr1[0][1] * matr2[1][0] + matr1[0][2] * matr2[2][0] + matr1[0][3] * matr2[3][0], matr1[0][0] * matr2[0][1] + matr1[0][1] * matr2[1][1] + matr1[0][2] * matr2[2][1] + matr1[0][3] * matr2[3][1], matr1[0][0] * matr2[0][2] + matr1[0][1] * matr2[1][2] + matr1[0][2] * matr2[2][2] + matr1[0][3] * matr2[3][2], matr1[0][0] * matr2[0][3] + matr1[0][1] * matr2[1][3] + matr1[0][2] * matr2[2][3] + matr1[0][3] * matr2[3][3]],
[matr1[1][0] * matr2[0][0] + matr1[1][1] * matr2[1][0] + matr1[1][2] * matr2[2][0] + matr1[1][3] * matr2[3][0], matr1[1][0] * matr2[0][1] + matr1[1][1] * matr2[1][1] + matr1[1][2] * matr2[2][1] + matr1[1][3] * matr2[3][1], matr1[1][0] * matr2[0][2] + matr1[1][1] * matr2[1][2] + matr1[1][2] * matr2[2][2] + matr1[1][3] * matr2[3][2], matr1[1][0] * matr2[0][3] + matr1[1][1] * matr2[1][3] + matr1[1][2] * matr2[2][3] + matr1[1][3] * matr2[3][3]],
[matr1[2][0] * matr2[0][0] + matr1[2][1] * matr2[1][0] + matr1[2][2] * matr2[2][0] + matr1[2][3] * matr2[3][0], matr1[2][0] * matr2[0][1] + matr1[2][1] * matr2[1][1] + matr1[2][2] * matr2[2][1] + matr1[2][3] * matr2[3][1], matr1[2][0] * matr2[0][2] + matr1[2][1] * matr2[1][2] + matr1[2][2] * matr2[2][2] + matr1[2][3] * matr2[3][2], matr1[2][0] * matr2[0][3] + matr1[2][1] * matr2[1][3] + matr1[2][2] * matr2[2][3] + matr1[2][3] * matr2[3][3]],
[matr1[3][0] * matr2[0][0] + matr1[3][1] * matr2[1][0] + matr1[3][2] * matr2[2][0] + matr1[3][3] * matr2[3][0], matr1[3][0] * matr2[0][1] + matr1[3][1] * matr2[1][1] + matr1[3][2] * matr2[2][1] + matr1[3][3] * matr2[3][1], matr1[3][0] * matr2[0][2] + matr1[3][1] * matr2[1][2] + matr1[3][2] * matr2[2][2] + matr1[3][3] * matr2[3][2], matr1[3][0] * matr2[0][3] + matr1[3][1] * matr2[1][3] + matr1[3][2] * matr2[2][3] + matr1[3][3] * matr2[3][3]]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr2); j++) {
result[i][j] = 0;
for (let k = 0; k < Chalkboard.matr.cols(matr1); k++) {
result[i][j] += matr1[i][k] * matr2[k][j];
}
}
}
return result;
}
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" where the numbers of columns of "matr1" must be equivalent to the number of rows of "matr2".');
}
};
/**
* Calculates the Kronecker multiplication of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {ChalkboardMatrix}
*/
export const mulKronecker = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr1, 2) && Chalkboard.matr.isSizeOf(matr2, 2)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0], matr1[0][0] * matr2[0][1], matr1[0][1] * matr2[0][0], matr1[0][1] * matr2[0][1]],
[matr1[0][0] * matr2[1][0], matr1[0][0] * matr2[1][1], matr1[0][1] * matr2[1][0], matr1[0][1] * matr2[1][1]],
[matr1[1][0] * matr2[0][0], matr1[1][0] * matr2[0][1], matr1[1][1] * matr2[0][0], matr1[1][1] * matr2[0][1]],
[matr1[1][0] * matr2[1][0], matr1[1][0] * matr2[1][1], matr1[1][1] * matr2[1][0], matr1[1][1] * matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3) && Chalkboard.matr.isSizeOf(matr2, 3)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0], matr1[0][0] * matr2[0][1], matr1[0][0] * matr2[0][2], matr1[0][1] * matr2[0][0], matr1[0][1] * matr2[0][1], matr1[0][1] * matr2[0][2], matr1[0][2] * matr2[0][0], matr1[0][2] * matr2[0][1], matr1[0][2] * matr2[0][2]],
[matr1[0][0] * matr2[1][0], matr1[0][0] * matr2[1][1], matr1[0][0] * matr2[1][2], matr1[0][1] * matr2[1][0], matr1[0][1] * matr2[1][1], matr1[0][1] * matr2[1][2], matr1[0][2] * matr2[1][0], matr1[0][2] * matr2[1][1], matr1[0][2] * matr2[1][2]],
[matr1[0][0] * matr2[2][0], matr1[0][0] * matr2[2][1], matr1[0][0] * matr2[2][2], matr1[0][1] * matr2[2][0], matr1[0][1] * matr2[2][1], matr1[0][1] * matr2[2][2], matr1[0][2] * matr2[2][0], matr1[0][2] * matr2[2][1], matr1[0][2] * matr2[2][2]],
[matr1[1][0] * matr2[0][0], matr1[1][0] * matr2[0][1], matr1[1][0] * matr2[0][2], matr1[1][1] * matr2[0][0], matr1[1][1] * matr2[0][1], matr1[1][1] * matr2[0][2], matr1[1][2] * matr2[0][0], matr1[1][2] * matr2[0][1], matr1[1][2] * matr2[0][2]],
[matr1[1][0] * matr2[1][0], matr1[1][0] * matr2[1][1], matr1[1][0] * matr2[1][2], matr1[1][1] * matr2[1][0], matr1[1][1] * matr2[1][1], matr1[1][1] * matr2[1][2], matr1[1][2] * matr2[1][0], matr1[1][2] * matr2[1][1], matr1[1][2] * matr2[1][2]],
[matr1[1][0] * matr2[2][0], matr1[1][0] * matr2[2][1], matr1[1][0] * matr2[2][2], matr1[1][1] * matr2[2][0], matr1[1][1] * matr2[2][1], matr1[1][1] * matr2[2][2], matr1[1][2] * matr2[2][0], matr1[1][2] * matr2[2][1], matr1[1][2] * matr2[2][2]],
[matr1[2][0] * matr2[0][0], matr1[2][0] * matr2[0][1], matr1[2][0] * matr2[0][2], matr1[2][1] * matr2[0][0], matr1[2][1] * matr2[0][1], matr1[2][1] * matr2[0][2], matr1[2][2] * matr2[0][0], matr1[2][2] * matr2[0][1], matr1[2][2] * matr2[0][2]],
[matr1[2][0] * matr2[1][0], matr1[2][0] * matr2[1][1], matr1[2][0] * matr2[1][2], matr1[2][1] * matr2[1][0], matr1[2][1] * matr2[1][1], matr1[2][1] * matr2[1][2], matr1[2][2] * matr2[1][0], matr1[2][2] * matr2[1][1], matr1[2][2] * matr2[1][2]],
[matr1[2][0] * matr2[2][0], matr1[2][0] * matr2[2][1], matr1[2][0] * matr2[2][2], matr1[2][1] * matr2[2][0], matr1[2][1] * matr2[2][1], matr1[2][1] * matr2[2][2], matr1[2][2] * matr2[2][0], matr1[2][2] * matr2[2][1], matr1[2][2] * matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4) && Chalkboard.matr.isSizeOf(matr2, 4)) {
return Chalkboard.matr.init(
[matr1[0][0] * matr2[0][0], matr1[0][0] * matr2[0][1], matr1[0][0] * matr2[0][2], matr1[0][0] * matr2[0][3], matr1[0][1] * matr2[0][0], matr1[0][1] * matr2[0][1], matr1[0][1] * matr2[0][2], matr1[0][1] * matr2[0][3], matr1[0][2] * matr2[0][0], matr1[0][2] * matr2[0][1], matr1[0][2] * matr2[0][2], matr1[0][2] * matr2[0][3], matr1[0][3] * matr2[0][0], matr1[0][3] * matr2[0][1], matr1[0][3] * matr2[0][2], matr1[0][3] * matr2[0][3]],
[matr1[0][0] * matr2[1][0], matr1[0][0] * matr2[1][1], matr1[0][0] * matr2[1][2], matr1[0][0] * matr2[1][3], matr1[0][1] * matr2[1][0], matr1[0][1] * matr2[1][1], matr1[0][1] * matr2[1][2], matr1[0][1] * matr2[1][3], matr1[0][2] * matr2[1][0], matr1[0][2] * matr2[1][1], matr1[0][2] * matr2[1][2], matr1[0][2] * matr2[1][3], matr1[0][3] * matr2[1][0], matr1[0][3] * matr2[1][1], matr1[0][3] * matr2[1][2], matr1[0][3] * matr2[1][3]],
[matr1[0][0] * matr2[2][0], matr1[0][0] * matr2[2][1], matr1[0][0] * matr2[2][2], matr1[0][0] * matr2[2][3], matr1[0][1] * matr2[2][0], matr1[0][1] * matr2[2][1], matr1[0][1] * matr2[2][2], matr1[0][1] * matr2[2][3], matr1[0][2] * matr2[2][0], matr1[0][2] * matr2[2][1], matr1[0][2] * matr2[2][2], matr1[0][2] * matr2[2][3], matr1[0][3] * matr2[2][0], matr1[0][3] * matr2[2][1], matr1[0][3] * matr2[2][2], matr1[0][3] * matr2[2][3]],
[matr1[0][0] * matr2[3][0], matr1[0][0] * matr2[3][1], matr1[0][0] * matr2[3][2], matr1[0][0] * matr2[3][3], matr1[0][1] * matr2[3][0], matr1[0][1] * matr2[3][1], matr1[0][1] * matr2[3][2], matr1[0][1] * matr2[3][3], matr1[0][2] * matr2[3][0], matr1[0][2] * matr2[3][1], matr1[0][2] * matr2[3][2], matr1[0][2] * matr2[3][3], matr1[0][3] * matr2[3][0], matr1[0][3] * matr2[3][1], matr1[0][3] * matr2[3][2], matr1[0][3] * matr2[3][3]],
[matr1[1][0] * matr2[0][0], matr1[1][0] * matr2[0][1], matr1[1][0] * matr2[0][2], matr1[1][0] * matr2[0][3], matr1[1][1] * matr2[0][0], matr1[1][1] * matr2[0][1], matr1[1][1] * matr2[0][2], matr1[1][1] * matr2[0][3], matr1[1][2] * matr2[0][0], matr1[1][2] * matr2[0][1], matr1[1][2] * matr2[0][2], matr1[1][2] * matr2[0][3], matr1[1][3] * matr2[0][0], matr1[1][3] * matr2[0][1], matr1[1][3] * matr2[0][2], matr1[1][3] * matr2[0][3]],
[matr1[1][0] * matr2[1][0], matr1[1][0] * matr2[1][1], matr1[1][0] * matr2[1][2], matr1[1][0] * matr2[1][3], matr1[1][1] * matr2[1][0], matr1[1][1] * matr2[1][1], matr1[1][1] * matr2[1][2], matr1[1][1] * matr2[1][3], matr1[1][2] * matr2[1][0], matr1[1][2] * matr2[1][1], matr1[1][2] * matr2[1][2], matr1[1][2] * matr2[1][3], matr1[1][3] * matr2[1][0], matr1[1][3] * matr2[1][1], matr1[1][3] * matr2[1][2], matr1[1][3] * matr2[1][3]],
[matr1[1][0] * matr2[2][0], matr1[1][0] * matr2[2][1], matr1[1][0] * matr2[2][2], matr1[1][0] * matr2[2][3], matr1[1][1] * matr2[2][0], matr1[1][1] * matr2[2][1], matr1[1][1] * matr2[2][2], matr1[1][1] * matr2[2][3], matr1[1][2] * matr2[2][0], matr1[1][2] * matr2[2][1], matr1[1][2] * matr2[2][2], matr1[1][2] * matr2[2][3], matr1[1][3] * matr2[2][0], matr1[1][3] * matr2[2][1], matr1[1][3] * matr2[2][2], matr1[1][3] * matr2[2][3]],
[matr1[1][0] * matr2[3][0], matr1[1][0] * matr2[3][1], matr1[1][0] * matr2[3][2], matr1[1][0] * matr2[3][3], matr1[1][1] * matr2[3][0], matr1[1][1] * matr2[3][1], matr1[1][1] * matr2[3][2], matr1[1][1] * matr2[3][3], matr1[1][2] * matr2[3][0], matr1[1][2] * matr2[3][1], matr1[1][2] * matr2[3][2], matr1[1][2] * matr2[3][3], matr1[1][3] * matr2[3][0], matr1[1][3] * matr2[3][1], matr1[1][3] * matr2[3][2], matr1[1][3] * matr2[3][3]],
[matr1[2][0] * matr2[0][0], matr1[2][0] * matr2[0][1], matr1[2][0] * matr2[0][2], matr1[2][0] * matr2[0][3], matr1[2][1] * matr2[0][0], matr1[2][1] * matr2[0][1], matr1[2][1] * matr2[0][2], matr1[2][1] * matr2[0][3], matr1[2][2] * matr2[0][0], matr1[2][2] * matr2[0][1], matr1[2][2] * matr2[0][2], matr1[2][2] * matr2[0][3], matr1[2][3] * matr2[0][0], matr1[2][3] * matr2[0][1], matr1[2][3] * matr2[0][2], matr1[2][3] * matr2[0][3]],
[matr1[2][0] * matr2[1][0], matr1[2][0] * matr2[1][1], matr1[2][0] * matr2[1][2], matr1[2][0] * matr2[1][3], matr1[2][1] * matr2[1][0], matr1[2][1] * matr2[1][1], matr1[2][1] * matr2[1][2], matr1[2][1] * matr2[1][3], matr1[2][2] * matr2[1][0], matr1[2][2] * matr2[1][1], matr1[2][2] * matr2[1][2], matr1[2][2] * matr2[1][3], matr1[2][3] * matr2[1][0], matr1[2][3] * matr2[1][1], matr1[2][3] * matr2[1][2], matr1[2][3] * matr2[1][3]],
[matr1[2][0] * matr2[2][0], matr1[2][0] * matr2[2][1], matr1[2][0] * matr2[2][2], matr1[2][0] * matr2[2][3], matr1[2][1] * matr2[2][0], matr1[2][1] * matr2[2][1], matr1[2][1] * matr2[2][2], matr1[2][1] * matr2[2][3], matr1[2][2] * matr2[2][0], matr1[2][2] * matr2[2][1], matr1[2][2] * matr2[2][2], matr1[2][2] * matr2[2][3], matr1[2][3] * matr2[2][0], matr1[2][3] * matr2[2][1], matr1[2][3] * matr2[2][2], matr1[2][3] * matr2[2][3]],
[matr1[2][0] * matr2[3][0], matr1[2][0] * matr2[3][1], matr1[2][0] * matr2[3][2], matr1[2][0] * matr2[3][3], matr1[2][1] * matr2[3][0], matr1[2][1] * matr2[3][1], matr1[2][1] * matr2[3][2], matr1[2][1] * matr2[3][3], matr1[2][2] * matr2[3][0], matr1[2][2] * matr2[3][1], matr1[2][2] * matr2[3][2], matr1[2][2] * matr2[3][3], matr1[2][3] * matr2[3][0], matr1[2][3] * matr2[3][1], matr1[2][3] * matr2[3][2], matr1[2][3] * matr2[3][3]],
[matr1[3][0] * matr2[0][0], matr1[3][0] * matr2[0][1], matr1[3][0] * matr2[0][2], matr1[3][0] * matr2[0][3], matr1[3][1] * matr2[0][0], matr1[3][1] * matr2[0][1], matr1[3][1] * matr2[0][2], matr1[3][1] * matr2[0][3], matr1[3][2] * matr2[0][0], matr1[3][2] * matr2[0][1], matr1[3][2] * matr2[0][2], matr1[3][2] * matr2[0][3], matr1[3][3] * matr2[0][0], matr1[3][3] * matr2[0][1], matr1[3][3] * matr2[0][2], matr1[3][3] * matr2[0][3]],
[matr1[3][0] * matr2[1][0], matr1[3][0] * matr2[1][1], matr1[3][0] * matr2[1][2], matr1[3][0] * matr2[1][3], matr1[3][1] * matr2[1][0], matr1[3][1] * matr2[1][1], matr1[3][1] * matr2[1][2], matr1[3][1] * matr2[1][3], matr1[3][2] * matr2[1][0], matr1[3][2] * matr2[1][1], matr1[3][2] * matr2[1][2], matr1[3][2] * matr2[1][3], matr1[3][3] * matr2[1][0], matr1[3][3] * matr2[1][1], matr1[3][3] * matr2[1][2], matr1[3][3] * matr2[1][3]],
[matr1[3][0] * matr2[2][0], matr1[3][0] * matr2[2][1], matr1[3][0] * matr2[2][2], matr1[3][0] * matr2[2][3], matr1[3][1] * matr2[2][0], matr1[3][1] * matr2[2][1], matr1[3][1] * matr2[2][2], matr1[3][1] * matr2[2][3], matr1[3][2] * matr2[2][0], matr1[3][2] * matr2[2][1], matr1[3][2] * matr2[2][2], matr1[3][2] * matr2[2][3], matr1[3][3] * matr2[2][0], matr1[3][3] * matr2[2][1], matr1[3][3] * matr2[2][2], matr1[3][3] * matr2[2][3]],
[matr1[3][0] * matr2[3][0], matr1[3][0] * matr2[3][1], matr1[3][0] * matr2[3][2], matr1[3][0] * matr2[3][3], matr1[3][1] * matr2[3][0], matr1[3][1] * matr2[3][1], matr1[3][1] * matr2[3][2], matr1[3][1] * matr2[3][3], matr1[3][2] * matr2[3][0], matr1[3][2] * matr2[3][1], matr1[3][2] * matr2[3][2], matr1[3][2] * matr2[3][3], matr1[3][3] * matr2[3][0], matr1[3][3] * matr2[3][1], matr1[3][3] * matr2[3][2], matr1[3][3] * matr2[3][3]]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
for (let j = 0; j < Chalkboard.matr.cols(matr1); j++) {
for (let k = 0; k < Chalkboard.matr.rows(matr2); k++) {
for (let l = 0; l < Chalkboard.matr.cols(matr2); l++) {
if (!result[i * Chalkboard.matr.rows(matr2) + k]) {
result[i * Chalkboard.matr.rows(matr2) + k] = [];
}
result[i * Chalkboard.matr.rows(matr2) + k][j * Chalkboard.matr.cols(matr2) + l] = matr1[i][j] * matr2[k][l];
}
}
}
}
return result;
}
};
/**
* Calculates the multiplication of a matrix with a vector.
* @param {ChalkboardMatrix} matr - The matrix
* @param {ChalkboardVector} vect - The vector
* @returns {ChalkboardMatrix | ChalkboardVector}
*/
export const mulVector = (matr: ChalkboardMatrix, vect: ChalkboardVector): ChalkboardMatrix | ChalkboardVector => {
vect = $(vect) as { x: number, y: number, z?: number, w?: number };
if (Chalkboard.vect.isDimensionOf(vect, 2)) {
if (Chalkboard.matr.rows(matr) === 2) {
return Chalkboard.matr.toVector(Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect)), 2);
} else {
return Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect));
}
} else if (Chalkboard.vect.isDimensionOf(vect, 3)) {
if (Chalkboard.matr.rows(matr) === 3) {
return Chalkboard.matr.toVector(Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect)), 3);
} else {
return Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect));
}
} else if (Chalkboard.vect.isDimensionOf(vect, 4)) {
if (Chalkboard.matr.rows(matr) === 4) {
return Chalkboard.matr.toVector(Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect)), 4);
} else {
return Chalkboard.matr.mul(matr, Chalkboard.vect.toMatrix(vect));
}
} else {
throw new TypeError('Parameter "vect" must be of type "ChalkboardVector" with 2, 3, or 4 dimensions.');
}
};
/**
* Calculates the negation of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const negate = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([-matr[0][0], -matr[0][1]], [-matr[1][0], -matr[1][1]]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([-matr[0][0], -matr[0][1], -matr[0][2]], [-matr[1][0], -matr[1][1], -matr[1][2]], [-matr[2][0], -matr[2][1], -matr[2][2]]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([-matr[0][0], -matr[0][1], -matr[0][2], -matr[0][3]], [-matr[1][0], -matr[1][1], -matr[1][2], -matr[1][3]], [-matr[2][0], -matr[2][1], -matr[2][2], -matr[2][3]], [-matr[3][0], -matr[3][1], -matr[3][2], -matr[3][3]]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = -matr[i][j];
}
}
return result;
}
};
/**
* Calculates the element-wise L_(p,q) norm of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} [p=2] - The exponent of each element and the denominator of the exponent of the sum of the rows
* @param {number} [q=2] - The numerator of the exponent of the sum of the rows and the denominator of the exponent of the sum of the summed rows
* @returns {number}
*/
export const norm = (matr: ChalkboardMatrix, p: number = 2, q: number = 2): number => {
if (Chalkboard.matr.isSizeOf(matr, 2) && p === 2 && q === 2) {
return Chalkboard.real.sqrt(matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1]);
} else if (Chalkboard.matr.isSizeOf(matr, 3) && p === 2 && q === 2) {
return Chalkboard.real.sqrt(matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[0][2] * matr[0][2] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1] + matr[1][2] * matr[1][2] + matr[2][0] * matr[2][0] + matr[2][1] * matr[2][1] + matr[2][2] * matr[2][2]);
} else if (Chalkboard.matr.isSizeOf(matr, 4) && p === 2 && q === 2) {
return Chalkboard.real.sqrt(matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[0][2] * matr[0][2] + matr[0][3] * matr[0][3] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1] + matr[1][2] * matr[1][2] + matr[1][3] * matr[1][3] + matr[2][0] * matr[2][0] + matr[2][1] * matr[2][1] + matr[2][2] * matr[2][2] + matr[2][3] * matr[2][3] + matr[3][0] * matr[3][0] + matr[3][1] * matr[3][1] + matr[3][2] * matr[3][2] + matr[3][3] * matr[3][3]);
} else {
let result = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
let rowResult = 0;
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
rowResult += Chalkboard.real.pow(matr[i][j], p) as number;
}
result += Chalkboard.real.pow(rowResult, q / p) as number;
}
return Chalkboard.real.pow(result, 1 / q) as number;
}
};
/**
* Calculates the normalization of a matrix with the element-wise L_(p, q) norm.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} [p=2] - The exponent of each element and the denominator of the exponent of the sum of the rows
* @param {number} [q=2] - The numerator of the exponent of the sum of the rows and the denominator of the exponent of the sum of the summed rows
* @returns {ChalkboardMatrix}
*/
export const normalize = (matr: ChalkboardMatrix, p: number = 2, q: number = 2): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init(
[matr[0][0] / Chalkboard.matr.norm(matr, p, q), matr[0][1] / Chalkboard.matr.norm(matr, p, q)],
[matr[1][0] / Chalkboard.matr.norm(matr, p, q), matr[1][1] / Chalkboard.matr.norm(matr, p, q)]
);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init(
[matr[0][0] / Chalkboard.matr.norm(matr, p, q), matr[0][1] / Chalkboard.matr.norm(matr, p, q), matr[0][2] / Chalkboard.matr.norm(matr, p, q)],
[matr[1][0] / Chalkboard.matr.norm(matr, p, q), matr[1][1] / Chalkboard.matr.norm(matr, p, q), matr[1][2] / Chalkboard.matr.norm(matr, p, q)],
[matr[2][0] / Chalkboard.matr.norm(matr, p, q), matr[2][1] / Chalkboard.matr.norm(matr, p, q), matr[2][2] / Chalkboard.matr.norm(matr, p, q)]
);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init(
[matr[0][0] / Chalkboard.matr.norm(matr, p, q), matr[0][1] / Chalkboard.matr.norm(matr, p, q), matr[0][2] / Chalkboard.matr.norm(matr, p, q), matr[0][3] / Chalkboard.matr.norm(matr, p, q)],
[matr[1][0] / Chalkboard.matr.norm(matr, p, q), matr[1][1] / Chalkboard.matr.norm(matr, p, q), matr[1][2] / Chalkboard.matr.norm(matr, p, q), matr[1][3] / Chalkboard.matr.norm(matr, p, q)],
[matr[2][0] / Chalkboard.matr.norm(matr, p, q), matr[2][1] / Chalkboard.matr.norm(matr, p, q), matr[2][2] / Chalkboard.matr.norm(matr, p, q), matr[2][3] / Chalkboard.matr.norm(matr, p, q)],
[matr[3][0] / Chalkboard.matr.norm(matr, p, q), matr[3][1] / Chalkboard.matr.norm(matr, p, q), matr[3][2] / Chalkboard.matr.norm(matr, p, q), matr[3][3] / Chalkboard.matr.norm(matr, p, q)]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = matr[i][j] / Chalkboard.matr.norm(matr, p, q);
}
}
return result;
}
};
/**
* Calculates the element-wise L_(p,q) norm squared of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} [p=2] - The exponent of each element and the denominator of the exponent of the sum of the rows
* @param {number} [q=2] - The numerator of the exponent of the sum of the rows
* @returns {number}
*/
export const normsq = (matr: ChalkboardMatrix, p: number = 2, q: number = 2): number => {
if (Chalkboard.matr.isSizeOf(matr, 2) && p === 2 && q === 2) {
return matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1];
} else if (Chalkboard.matr.isSizeOf(matr, 3) && p === 2 && q === 2) {
return matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[0][2] * matr[0][2] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1] + matr[1][2] * matr[1][2] + matr[2][0] * matr[2][0] + matr[2][1] * matr[2][1] + matr[2][2] * matr[2][2];
} else if (Chalkboard.matr.isSizeOf(matr, 4) && p === 2 && q === 2) {
return matr[0][0] * matr[0][0] + matr[0][1] * matr[0][1] + matr[0][2] * matr[0][2] + matr[0][3] * matr[0][3] + matr[1][0] * matr[1][0] + matr[1][1] * matr[1][1] + matr[1][2] * matr[1][2] + matr[1][3] * matr[1][3] + matr[2][0] * matr[2][0] + matr[2][1] * matr[2][1] + matr[2][2] * matr[2][2] + matr[2][3] * matr[2][3] + matr[3][0] * matr[3][0] + matr[3][1] * matr[3][1] + matr[3][2] * matr[3][2] + matr[3][3] * matr[3][3];
} else {
let result = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
let rowResult = 0;
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
rowResult += Chalkboard.real.pow(matr[i][j], p) as number;
}
result += Chalkboard.real.pow(rowResult, q / p) as number;
}
return result;
}
};
/**
* Calculates the null space of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const nullspace = (matr: ChalkboardMatrix): ChalkboardMatrix => {
const augmented = matr.map((row) => row.slice().concat(Array(Chalkboard.matr.rows(matr)).fill(0)));
const rowEchelonForm = Chalkboard.matr.Gaussian(augmented);
return rowEchelonForm.filter((row: number[]) => row.slice(0, Chalkboard.matr.rows(matr)).every((element) => element === 0)).map((row: number[]) => row.slice(Chalkboard.matr.rows(matr)));
};
/**
* Calculates the permanent of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const perm = (matr: ChalkboardMatrix): number => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.rows(matr) === 1) {
return matr[0][0];
} else if (Chalkboard.matr.rows(matr) === 2) {
return matr[0][0] * matr[1][1] + matr[0][1] * matr[1][0];
} else if (Chalkboard.matr.rows(matr) === 3) {
return matr[0][0] * (matr[1][1] * matr[2][2] + matr[1][2] * matr[2][1]) + matr[0][1] * (matr[1][0] * matr[2][2] + matr[1][2] * matr[2][0]) + matr[0][2] * (matr[1][0] * matr[2][1] + matr[1][1] * matr[2][0]);
} else if (Chalkboard.matr.rows(matr) === 4) {
return matr[0][0] * (matr[1][1] * (matr[2][2] * matr[3][3] + matr[2][3] * matr[3][2]) + matr[1][2] * (matr[2][1] * matr[3][3] + matr[2][3] * matr[3][1]) + matr[1][3] * (matr[2][1] * matr[3][2] + matr[2][2] * matr[3][1])) + matr[0][1] * (matr[1][0] * (matr[2][2] * matr[3][3] + matr[2][3] * matr[3][2]) + matr[1][2] * (matr[2][0] * matr[3][3] + matr[2][3] * matr[3][0]) + matr[1][3] * (matr[2][0] * matr[3][2] + matr[2][2] * matr[3][0])) + matr[0][2] * (matr[1][0] * (matr[2][1] * matr[3][3] + matr[2][3] * matr[3][1]) + matr[1][1] * (matr[2][0] * matr[3][3] + matr[2][3] * matr[3][0]) + matr[1][3] * (matr[2][0] * matr[3][1] + matr[2][1] * matr[3][0])) + matr[0][3] * (matr[1][0] * (matr[2][1] * matr[3][2] + matr[2][2] * matr[3][1]) + matr[1][1] * (matr[2][0] * matr[3][2] + matr[2][2] * matr[3][0]) + matr[1][2] * (matr[2][0] * matr[3][1] + matr[2][1] * matr[3][0]));
} else {
let result = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
const cofactor = matr[0][i] * Chalkboard.matr.perm(Chalkboard.matr.cofactor(matr, 0, i));
result += Math.abs(cofactor);
}
return result;
}
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Calculates the exponentiation of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} num - The exponent
* @returns {ChalkboardMatrix}
*/
export const pow = (matr: ChalkboardMatrix, num: number): ChalkboardMatrix => {
if (Chalkboard.matr.isSquare(matr)) {
if (num === 0) {
return Chalkboard.matr.identity(Chalkboard.matr.rows(matr));
} else {
let result = matr;
for (let i = 1; i < num; i++) {
result = Chalkboard.matr.mul(matr, result);
}
return result;
}
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Prints a matrix in the console.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {void}
*/
export const print = (matr: ChalkboardMatrix): void => {
console.log(Chalkboard.matr.toString(matr));
};
/**
* Returns a matrix with a row or column removed (pulled out).
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} index - The index of the row or column to pull
* @param {number} axis - The axis to pull from, which is 0 for the rows or 1 for the columns
* @returns {ChalkboardMatrix}
*/
export const pull = (matr: ChalkboardMatrix, index: number, axis: 0 | 1): ChalkboardMatrix => {
if (axis === 0) {
matr.splice(index, 1);
return matr;
} else if (axis === 1) {
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
matr[i].splice(index, 1);
}
return matr;
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
};
/**
* Returns a matrix with a row or column added (pushed in).
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} index - The index of the row or column to push
* @param {number} axis - The axis to push to, which is 0 for the rows or 1 for the columns
* @param {number[]} elements - The elements to push
* @returns {ChalkboardMatrix}
*/
export const push = (matr: ChalkboardMatrix, index: number, axis: 0 | 1, elements: number[]): ChalkboardMatrix => {
if (axis === 0) {
matr.splice(index, 0, elements);
return matr;
} else if (axis === 1) {
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
matr[i].splice(index, 0, elements[i]);
}
return matr;
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
};
/**
* Calculates the QR decomposition of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {{Q: ChalkboardMatrix, R: ChalkboardMatrix}}
*/
export const QRdecomp = (matr: ChalkboardMatrix): { Q: ChalkboardMatrix; R: ChalkboardMatrix } => {
const Q = Chalkboard.matr.identity(Chalkboard.matr.rows(matr)),
R = Chalkboard.matr.copy(matr);
for (let j = 0; j < Math.min(Chalkboard.matr.rows(matr), Chalkboard.matr.cols(matr)) - (Chalkboard.matr.rows(matr) > Chalkboard.matr.cols(matr) ? 0 : 1); j++) {
let norm = 0;
for (let i = j; i < Chalkboard.matr.rows(matr); i++) {
norm += R[i][j] * R[i][j];
}
norm = Chalkboard.real.sqrt(norm);
const v = [];
v[0] = norm - R[j][j];
let normalizer = v[0] * v[0];
for (let i = 1; i < Chalkboard.matr.rows(matr) - j; i++) {
v[i] = -R[i + j][j];
normalizer += v[i] * v[i];
}
normalizer = 1 / Chalkboard.real.sqrt(normalizer);
for (let i = 0; i < v.length; i++) {
v[i] *= normalizer;
}
R[j][j] = norm;
for (let i = j + 1; i < Chalkboard.matr.rows(R); i++) {
R[i][j] = 0;
}
for (let k = j + 1; k < Chalkboard.matr.cols(R); k++) {
let dot = 0;
for (let i = 0; i < v.length; i++) {
dot += v[i] * R[i + j][k];
}
dot *= 2;
for (let i = 0; i < v.length; i++) {
R[i + j][k] -= dot * v[i];
}
}
for (let k = 0; k < Chalkboard.matr.cols(Q); k++) {
let dot = 0;
for (let i = 0; i < v.length; i++) {
dot += v[i] * Q[k][i + j];
}
dot *= 2;
for (let i = 0; i < v.length; i++) {
Q[k][i + j] -= dot * v[i];
}
}
}
return { Q: Q, R: R };
};
/**
* Initializes a random matrix.
* @param {number} rows - The number of rows or (if the cols parameter is blank) the number of rows or columns (the size)
* @param {number} [cols=rows] - The number of columns
* @param {number} [inf=0] - The lower bound
* @param {number} [sup=1] - The upper bound
* @returns {ChalkboardMatrix}
*/
export const random = (rows: number, cols: number = rows, inf: number = 0, sup: number = 1): ChalkboardMatrix => {
if (rows === 2 && cols === 2) {
return Chalkboard.matr.init([Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)]);
} else if (rows === 3 && cols === 3) {
return Chalkboard.matr.init([Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)]);
} else if (rows === 4 && cols === 4) {
return Chalkboard.matr.init([Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)], [Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup), Chalkboard.numb.random(inf, sup)]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < rows; i++) {
result.push([]);
for (let j = 0; j < cols; j++) {
result[i].push(Chalkboard.numb.random(inf, sup));
}
}
return result;
}
};
/**
* Calculates the rank of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const rank = (matr: ChalkboardMatrix): number => {
return Chalkboard.matr.Gaussian(matr).filter((row: number[]) => row.some((element) => element !== 0)).length;
};
/**
* Calculates the reciprocal of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const reciprocate = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([1 / matr[0][0], 1 / matr[0][1]], [1 / matr[1][0], 1 / matr[1][1]]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([1 / matr[0][0], 1 / matr[0][1], 1 / matr[0][2]], [1 / matr[1][0], 1 / matr[1][1], 1 / matr[1][2]], [1 / matr[2][0], 1 / matr[2][1], 1 / matr[2][2]]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([1 / matr[0][0], 1 / matr[0][1], 1 / matr[0][2], 1 / matr[0][3]], [1 / matr[1][0], 1 / matr[1][1], 1 / matr[1][2], 1 / matr[1][3]], [1 / matr[2][0], 1 / matr[2][1], 1 / matr[2][2], 1 / matr[2][3]], [1 / matr[3][0], 1 / matr[3][1], 1 / matr[3][2], 1 / matr[3][3]]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = 1 / matr[i][j];
}
}
return result;
}
};
/**
* Returns a matrix with the number of rows and columns changed.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} rows - The number of rows to change to or (if the cols parameter is blank) the number of rows or columns (the size) to change to
* @param {nmber} [cols=rows] - The number of columns to change to
* @returns {ChalkboardMatrix}
*/
export const resize = (matr: ChalkboardMatrix, rows: number, cols: number = rows): ChalkboardMatrix => {
const result = Chalkboard.matr.init();
const matrrows = Chalkboard.matr.rows(matr);
const matrcols = Chalkboard.matr.cols(matr);
for (let i = 0; i < rows; i++) {
result.push([]);
for (let j = 0; j < cols; j++) {
result[i].push(i < matrrows && j < matrcols ? matr[i][j] : 0);
}
}
return result;
};
/**
* Initializes a rotation matrix.
* @param {number} radx - The xy-rotation in radians (for 2D) or the x-rotation in radians (for 3D)
* @param {number} [rady] - The y-rotation in radians (for 3D)
* @param {number} [radz] - The z-rotation in radians (for 3D)
* @returns {ChalkboardMatrix}
*/
export const rotator = (radx: number, rady?: number, radz?: number): ChalkboardMatrix => {
if (rady === undefined && radz === undefined) {
return Chalkboard.matr.init([Math.cos(radx), -Math.sin(radx)], [Math.sin(radx), Math.cos(radx)]);
} else {
const matrx = Chalkboard.matr.init([1, 0, 0], [0, Math.cos(radx), -Math.sin(radx)], [0, Math.sin(radx), Math.cos(radx)]),
matry = Chalkboard.matr.init([Math.cos(rady!), 0, Math.sin(rady!)], [0, 1, 0], [-Math.sin(rady!), 0, Math.cos(rady!)]),
matrz = Chalkboard.matr.init([Math.cos(radz!), -Math.sin(radz!), 0], [Math.sin(radz!), Math.cos(radz!), 0], [0, 0, 1]);
return Chalkboard.matr.mul(Chalkboard.matr.mul(matrz, matry), matrx);
}
};
/**
* Calculates the rounding of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const round = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([Math.round(matr[0][0]), Math.round(matr[0][1])], [Math.round(matr[1][0]), Math.round(matr[1][1])]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([Math.round(matr[0][0]), Math.round(matr[0][1]), Math.round(matr[0][2])], [Math.round(matr[1][0]), Math.round(matr[1][1]), Math.round(matr[1][2])], [Math.round(matr[2][0]), Math.round(matr[2][1]), Math.round(matr[2][2])]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([Math.round(matr[0][0]), Math.round(matr[0][1]), Math.round(matr[0][2]), Math.round(matr[0][3])], [Math.round(matr[1][0]), Math.round(matr[1][1]), Math.round(matr[1][2]), Math.round(matr[1][3])], [Math.round(matr[2][0]), Math.round(matr[2][1]), Math.round(matr[2][2]), Math.round(matr[2][3])], [Math.round(matr[3][0]), Math.round(matr[3][1]), Math.round(matr[3][2]), Math.round(matr[3][3])]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = Math.round(matr[i][j]);
}
}
return result;
}
};
/**
* Returns the number of rows in a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const rows = (matr: ChalkboardMatrix): number => {
return matr.length;
};
/**
* Calculates the row space of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const rowspace = (matr: ChalkboardMatrix): ChalkboardMatrix => {
return Chalkboard.matr.Gaussian(matr).filter((row: number[]) => row.some((element: number) => element !== 0));
};
/**
* Initializes a scaling matrix.
* @param {ChalkboardVector} vect - The coordinates to use represented as a vector
* @returns {ChalkboardMatrix}
*/
export const scaler = (vect: ChalkboardVector): ChalkboardMatrix => {
vect = $(vect) as { x: number, y: number, z?: number, w?: number };
if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "undefined" && typeof vect.w === "undefined") {
return Chalkboard.matr.init([vect.x, 0], [0, vect.y]);
} else if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "number" && typeof vect.w === "undefined") {
return Chalkboard.matr.init([vect.x, 0, 0], [0, vect.y, 0], [0, 0, vect.z]);
} else if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "number" && typeof vect.w === "number") {
return Chalkboard.matr.init([vect.x, 0, 0, 0], [0, vect.y, 0, 0], [0, 0, vect.z, 0], [0, 0, 0, vect.w]);
} else {
throw new TypeError('Parameter "vect" must be of type "ChalkboardVector" with 2, 3, or 4 dimensions.');
}
};
/**
* Calculates the scalar multiplication of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} num - The number
* @returns {ChalkboardMatrix}
*/
export const scl = (matr: ChalkboardMatrix, num: number): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2, 1)) {
return Chalkboard.matr.init([matr[0][0] * num], [matr[1][0] * num]);
} else if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([matr[0][0] * num, matr[0][1] * num], [matr[1][0] * num, matr[1][1] * num]);
} else if (Chalkboard.matr.isSizeOf(matr, 3, 1)) {
return Chalkboard.matr.init([matr[0][0] * num], [matr[1][0] * num], [matr[2][0] * num]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([matr[0][0] * num, matr[0][1] * num, matr[0][2] * num], [matr[1][0] * num, matr[1][1] * num, matr[1][2] * num], [matr[2][0] * num, matr[2][1] * num, matr[2][2] * num]);
} else if (Chalkboard.matr.isSizeOf(matr, 4, 1)) {
return Chalkboard.matr.init([matr[0][0] * num], [matr[1][0] * num], [matr[2][0] * num], [matr[3][0] * num]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([matr[0][0] * num, matr[0][1] * num, matr[0][2] * num, matr[0][3] * num], [matr[1][0] * num, matr[1][1] * num, matr[1][2] * num, matr[1][3] * num], [matr[2][0] * num, matr[2][1] * num, matr[2][2] * num, matr[2][3] * num], [matr[3][0] * num, matr[3][1] * num, matr[3][2] * num, matr[3][3] * num]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result[i][j] = matr[i][j] * num;
}
}
return result;
}
};
/**
* Calculates the solution to a system of linear equations defined by a coefficients matrix and a constants matrix.
* @param {ChalkboardMatrix} matrA - The coefficients matrix
* @param {ChalkboardMatrix} matrB - The constants matrix
* @returns {ChalkboardMatrix}
*/
export const solve = (matrA: ChalkboardMatrix, matrB: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSquare(matrA)) {
if (Chalkboard.matr.rows(matrA) === Chalkboard.matr.rows(matrB)) {
if (Chalkboard.matr.det(matrA) !== 0) {
return Chalkboard.matr.mul(Chalkboard.matr.invert(matrA), matrB);
} else {
throw new TypeError('Parameter "matrA" must be of type "ChalkboardMatrix" that has a non-zero determinant.');
}
} else {
throw new TypeError('Parameters "matrA" and "matrB" must be of type "ChalkboardMatrix" with equivalent numbers of rows.');
}
} else {
throw new TypeError('Parameter "matrA" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Calculates the subtraction of two matrices.
* @param {ChalkboardMatrix} matr1 - The first matrix
* @param {ChalkboardMatrix} matr2 - The second matrix
* @returns {ChalkboardMatrix}
*/
export const sub = (matr1: ChalkboardMatrix, matr2: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeEqual(matr1, matr2)) {
if (Chalkboard.matr.isSizeOf(matr1, 2)) {
return Chalkboard.matr.init(
[matr1[0][0] - matr2[0][0], matr1[0][1] - matr2[0][1]],
[matr1[1][0] - matr2[1][0], matr1[1][1] - matr2[1][1]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 3)) {
return Chalkboard.matr.init(
[matr1[0][0] - matr2[0][0], matr1[0][1] - matr2[0][1], matr1[0][2] - matr2[0][2]],
[matr1[1][0] - matr2[1][0], matr1[1][1] - matr2[1][1], matr1[1][2] - matr2[1][2]],
[matr1[2][0] - matr2[2][0], matr1[2][1] - matr2[2][1], matr1[2][2] - matr2[2][2]]
);
} else if (Chalkboard.matr.isSizeOf(matr1, 4)) {
return Chalkboard.matr.init(
[matr1[0][0] - matr2[0][0], matr1[0][1] - matr2[0][1], matr1[0][2] - matr2[0][2], matr1[0][3] - matr2[0][3]],
[matr1[1][0] - matr2[1][0], matr1[1][1] - matr2[1][1], matr1[1][2] - matr2[1][2], matr1[1][3] - matr2[1][3]],
[matr1[2][0] - matr2[2][0], matr1[2][1] - matr2[2][1], matr1[2][2] - matr2[2][2], matr1[2][3] - matr2[2][3]],
[matr1[3][0] - matr2[3][0], matr1[3][1] - matr2[3][1], matr1[3][2] - matr2[3][2], matr1[3][3] - matr2[3][3]]
);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.rows(matr1); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.cols(matr1); j++) {
result[i][j] = matr1[i][j] - matr2[i][j];
}
}
return result;
}
} else {
throw new TypeError('Parameters "matr1" and "matr2" must be of type "ChalkboardMatrix" with equivalent numbers of rows and columns.');
}
};
/**
* Initializes a symmetric binomial matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const symmetricBinomial = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1, 1], [1, 2]);
} else if (size === 3) {
return Chalkboard.matr.init([1, 1, 1], [1, 2, 3], [1, 3, 6]);
} else if (size === 4) {
return Chalkboard.matr.init([1, 1, 1, 1], [1, 2, 3, 4], [1, 3, 6, 10], [1, 4, 10, 20]);
} else {
return Chalkboard.matr.mul(Chalkboard.matr.lowerBinomial(size), Chalkboard.matr.upperBinomial(size));
}
};
/**
* Converts a matrix to an array.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number[]}
*/
export const toArray = (matr: ChalkboardMatrix): number[] => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return [matr[0][0], matr[0][1], matr[1][0], matr[1][1]];
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return [matr[0][0], matr[0][1], matr[0][2], matr[1][0], matr[1][1], matr[1][2], matr[2][0], matr[2][1], matr[2][2]];
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return [matr[0][0], matr[0][1], matr[0][2], matr[0][3], matr[1][0], matr[1][1], matr[1][2], matr[1][3], matr[2][0], matr[2][1], matr[2][2], matr[2][3], matr[3][0], matr[3][1], matr[3][2], matr[3][3]];
} else {
const result = [];
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result.push(matr[i][j]);
}
}
return result;
}
};
/**
* Converts a matrix to an object.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {object}
*/
export const toObject = (matr: ChalkboardMatrix): object => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return {
i1: { j1: matr[0][0], j2: matr[0][1] },
i2: { j1: matr[1][0], j2: matr[1][1] }
};
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return {
i1: { j1: matr[0][0], j2: matr[0][1], j3: matr[0][2] },
i2: { j1: matr[1][0], j2: matr[1][1], j3: matr[1][2] },
i3: { j1: matr[2][0], j2: matr[2][1], j3: matr[2][2] }
};
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return {
i1: { j1: matr[0][0], j2: matr[0][1], j3: matr[0][2], j4: matr[0][3] },
i2: { j1: matr[1][0], j2: matr[1][1], j3: matr[1][2], j4: matr[1][3] },
i3: { j1: matr[2][0], j2: matr[2][1], j3: matr[2][2], j4: matr[2][3] },
i4: { j1: matr[3][0], j2: matr[3][1], j3: matr[3][2], j4: matr[3][3] }
};
} else {
const result: { [key: string]: { [key: string]: number } } = {};
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result["i" + (i + 1)] = {};
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result["i" + (i + 1)]["j" + (j + 1)] = matr[i][j];
}
}
return result;
}
};
/**
* Converts a matrix to a set.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardSet}
*/
export const toSet = (matr: ChalkboardMatrix): ChalkboardSet => {
return Chalkboard.abal.set(Chalkboard.matr.toArray(matr));
};
/**
* Converts a matrix to a string.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {string}
*/
export const toString = (matr: ChalkboardMatrix): string => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return (
"[ " + matr[0][0].toString() + " " + matr[0][1].toString() +
" ]\n[ " + matr[1][0].toString() + " " + matr[1][1].toString() + " ]"
);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return (
"[ " + matr[0][0].toString() + " " + matr[0][1].toString() + " " + matr[0][2].toString() +
" ]\n[ " + matr[1][0].toString() + " " + matr[1][1].toString() + " " + matr[1][2].toString() +
" ]\n[ " + matr[2][0].toString() + " " + matr[2][1].toString() + " " + matr[2][2].toString() + " ]"
);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return (
"[ " + matr[0][0].toString() + " " + matr[0][1].toString() + " " + matr[0][2].toString() + " " + matr[0][3].toString() +
" ]\n[ " + matr[1][0].toString() + " " + matr[1][1].toString() + " " + matr[1][2].toString() + " " + matr[1][3].toString() +
" ]\n[ " + matr[2][0].toString() + " " + matr[2][1].toString() + " " + matr[2][2].toString() + " " + matr[2][3].toString() +
" ]\n[ " + matr[3][0].toString() + " " + matr[3][1].toString() + " " + matr[3][2].toString() + " " + matr[3][3].toString() + " ]"
);
} else {
let result = "";
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result += "[ ";
for (let j = 0; j < Chalkboard.matr.cols(matr); j++) {
result += matr[i][j].toString() + " ";
}
result = result.trimEnd() + " ]\n";
}
return result;
}
};
/**
* Converts a matrix to a tensor.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number[]} size - The number of rows, columns, tubes, etc. of the tensor represented as a single array or a sequence of arguments
* @returns {ChalkboardTensor}
*/
export const toTensor = (matr: ChalkboardMatrix, ...size: number[]): ChalkboardTensor => {
size = Array.isArray(size[0]) ? size[0] : size;
return Chalkboard.tens.resize(matr, ...size);
};
/**
* Converts a matrix to a typed array.
* @param {ChalkboardMatrix} matr - The matrix
* @param {"int8" | "int16" | "int32" | "float32" | "float64" | "bigint64"} [type="float32"] - The type of the typed array, which can be "int8", "int16", "int32", "float32", "float64", or "bigint64" (optional, defaults to "float32")
* @returns {Int8Array | Int16Array | Int32Array | Float32Array | Float64Array | BigInt64Array}
*/
export const toTypedArray = (matr: ChalkboardMatrix, type: "int8" | "int16" | "int32" | "float32" | "float64" | "bigint64" = "float32"): Int8Array | Int16Array | Int32Array | Float32Array | Float64Array | BigInt64Array => {
const arr = Chalkboard.matr.toArray(matr);
if (type === "int8") {
return new Int8Array(arr);
} else if (type === "int16") {
return new Int16Array(arr);
} else if (type === "int32") {
return new Int32Array(arr);
} else if (type === "float32") {
return new Float32Array(arr);
} else if (type === "float64") {
return new Float64Array(arr);
} else if (type === "bigint64") {
return new BigInt64Array(arr.map((n) => BigInt(Math.floor(n))));
}
throw new TypeError('Parameter "type" must be "int8", "int16", "int32", "float32", "float64", or "bigint64".');
};
/**
* Converts a matrix to a vector.
* @param {ChalkboardMatrix} matr - The matrix
* @param {number} dimension - The dimension of the vector which can be either 2, 3, or 4
* @param {number} index - The index of the row or column of the matrix which is the first component of the converted vector
* @param {number} [axis=0] - The axis of the matrix to convert, which is 0 for the rows or 1 for the columns
* @returns {ChalkboardVector}
*/
export const toVector = (matr: ChalkboardMatrix, dimension: 2 | 3 | 4, index: number = 0, axis: 0 | 1 = 0): ChalkboardVector => {
if (dimension === 2) {
if (axis === 0) {
return Chalkboard.vect.init(matr[0][index], matr[1][index]);
} else if (axis === 1) {
return Chalkboard.vect.init(matr[index][0], matr[index][1]);
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
} else if (dimension === 3) {
if (axis === 0) {
return Chalkboard.vect.init(matr[0][index], matr[1][index], matr[2][index]);
} else if (axis === 1) {
return Chalkboard.vect.init(matr[index][0], matr[index][1], matr[index][2]);
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
} else if (dimension === 4) {
if (axis === 0) {
return Chalkboard.vect.init(matr[0][index], matr[1][index], matr[2][index], matr[3][index]);
} else if (axis === 1) {
return Chalkboard.vect.init(matr[index][0], matr[index][1], matr[index][2], matr[index][3]);
} else {
throw new TypeError('Parameter "axis" must be 0 or 1.');
}
} else {
throw new TypeError('Parameter "dimension" must be 2, 3, or 4.');
}
};
/**
* Calculates the trace of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {number}
*/
export const trace = (matr: ChalkboardMatrix): number => {
if (Chalkboard.matr.isSquare(matr)) {
if (Chalkboard.matr.rows(matr) === 2) {
return matr[0][0] + matr[1][1];
} else if (Chalkboard.matr.rows(matr) === 3) {
return matr[0][0] + matr[1][1] + matr[2][2];
} else if (Chalkboard.matr.rows(matr) === 4) {
return matr[0][0] + matr[1][1] + matr[2][2] + matr[3][3];
} else {
let result = 0;
for (let i = 0; i < Chalkboard.matr.rows(matr); i++) {
result += matr[i][i];
}
return result;
}
} else {
throw new TypeError('Parameter "matr" must be of type "ChalkboardMatrix" that is square.');
}
};
/**
* Calculates the transpose of a matrix.
* @param {ChalkboardMatrix} matr - The matrix
* @returns {ChalkboardMatrix}
*/
export const transpose = (matr: ChalkboardMatrix): ChalkboardMatrix => {
if (Chalkboard.matr.isSizeOf(matr, 2)) {
return Chalkboard.matr.init([matr[0][0], matr[1][0]], [matr[0][1], matr[1][1]]);
} else if (Chalkboard.matr.isSizeOf(matr, 3)) {
return Chalkboard.matr.init([matr[0][0], matr[1][0], matr[2][0]], [matr[0][1], matr[1][1], matr[2][1]], [matr[0][2], matr[1][2], matr[2][2]]);
} else if (Chalkboard.matr.isSizeOf(matr, 4)) {
return Chalkboard.matr.init([matr[0][0], matr[1][0], matr[2][0], matr[3][0]], [matr[0][1], matr[1][1], matr[2][1], matr[3][1]], [matr[0][2], matr[1][2], matr[2][2], matr[3][2]], [matr[0][3], matr[1][3], matr[2][3], matr[3][3]]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < Chalkboard.matr.cols(matr); i++) {
result[i] = [];
for (let j = 0; j < Chalkboard.matr.rows(matr); j++) {
result[i][j] = matr[j][i];
}
}
return result;
}
};
/**
* Initializes a translation matrix.
* @param {ChalkboardVector} vect - The coordinates to use represented as a vector
* @returns {ChalkboardMatrix}
*/
export const translator = (vect: ChalkboardVector): ChalkboardMatrix => {
vect = $(vect) as { x: number, y: number, z?: number, w?: number };
if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "undefined" && typeof vect.w === "undefined") {
return Chalkboard.matr.init([1, 0, vect.x], [0, 1, vect.y], [0, 0, 1]);
} else if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "number" && typeof vect.w === "undefined") {
return Chalkboard.matr.init([1, 0, 0, vect.x], [0, 1, 0, vect.y], [0, 0, 1, vect.z], [0, 0, 0, 1]);
} else if (typeof vect.x === "number" && typeof vect.y === "number" && typeof vect.z === "number" && typeof vect.w === "number") {
return Chalkboard.matr.init([1, 0, 0, 0, vect.x], [0, 1, 0, 0, vect.y], [0, 0, 1, 0, vect.z], [0, 0, 0, 1, vect.w], [0, 0, 0, 0, 1]);
} else {
throw new TypeError('Parameter "vect" must be of type "ChalkboardVector" with 2, 3, or 4 dimensions.');
}
};
/**
* Initializes an upper binomial matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const upperBinomial = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([1, 1], [0, 1]);
} else if (size === 3) {
return Chalkboard.matr.init([1, 2, 1], [0, 1, 1], [0, 0, 1]);
} else if (size === 4) {
return Chalkboard.matr.init([1, 3, 3, 1], [0, 1, 2, 1], [0, 0, 1, 1], [0, 0, 0, 1]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result.push([]);
for (let j = 0; j < size; j++) {
result[i].push(Chalkboard.numb.binomial(j, i));
}
}
return result;
}
};
/**
* Initializes an upper shift matrix.
* @param {number} size - The number of rows or columns of the matrix
* @returns {ChalkboardMatrix}
*/
export const upperShift = (size: number): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([0, 1], [0, 0]);
} else if (size === 3) {
return Chalkboard.matr.init([0, 1, 0], [0, 0, 1], [0, 0, 0]);
} else if (size === 4) {
return Chalkboard.matr.init([0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [0, 0, 0, 0]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < size; i++) {
result[i] = [];
for (let j = 0; j < size; j++) {
result[i][j] = Chalkboard.numb.Kronecker(i + 1, j);
}
}
return result;
}
};
/**
* Initializes an upper triangular matrix.
* @param {number} size - The number of rows or columns
* @param {number[]} elements - The elements on and above the main diagonal
* @returns {ChalkboardMatrix}
*/
export const upperTriangular = (size: number, ...elements: number[]): ChalkboardMatrix => {
if (size === 2) {
return Chalkboard.matr.init([elements[0] || 0, elements[1] || 0], [0, elements[2] || 0]);
} else if (size === 3) {
return Chalkboard.matr.init([elements[0] || 0, elements[1] || 0, elements[2] || 0], [0, elements[3] || 0, elements[4] || 0], [0, 0, elements[5] || 0]);
} else if (size === 4) {
return Chalkboard.matr.init([elements[0] || 0, elements[1] || 0, elements[2] || 0, elements[3] || 0], [0, elements[4] || 0, elements[5] || 0, elements[6] || 0], [0, 0, elements[7] || 0, elements[8] || 0], [0, 0, 0, elements[9] || 0]);
} else {
elements = Array.isArray(elements[0]) ? elements[0] : elements;
const result = Chalkboard.matr.init();
let index = 0;
for (let i = 0; i < size; i++) {
result[i] = [];
for (let j = 0; j < size; j++) {
result[i][j] = j >= i ? elements[index++] || 0 : 0;
}
}
return result;
}
};
/**
* Initializes a zero matrix.
* @param {number} rows - The number of rows or (if the cols parameter is blank) the number of rows or columns (the size)
* @param {number} [cols=rows] - The number of columns
* @returns {ChalkboardMatrix}
*/
export const zero = (rows: number, cols: number = rows): ChalkboardMatrix => {
if (rows === 2 && cols === 2) {
return Chalkboard.matr.init([0, 0], [0, 0]);
} else if (rows === 3 && cols === 3) {
return Chalkboard.matr.init([0, 0, 0], [0, 0, 0], [0, 0, 0]);
} else if (rows === 4 && cols === 4) {
return Chalkboard.matr.init([0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]);
} else {
const result = Chalkboard.matr.init();
for (let i = 0; i < rows; i++) {
result[i] = [];
for (let j = 0; j < cols; j++) {
result[i][j] = 0;
}
}
return result;
}
};
}
}