UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

181 lines (162 loc) 4.88 kB
// Based on the implementation from kendo-spreadsheet-common/src/calc.js class Matrix { constructor() { this.height = 0; this.width = 0; this.data = []; } clone() { const m = new Matrix(); m.height = this.height; m.width = this.width; m.data = this.data.map(row => row.slice()); return m; } get(row, col) { const line = this.data[row]; const val = line ? line[col] : null; return val; } set(row, col, data) { let line = this.data[row]; if (line == null) { line = this.data[row] = []; } line[col] = data; if (row >= this.height) { this.height = row + 1; } if (col >= this.width) { this.width = col + 1; } } each(f, includeEmpty) { for (let row = 0; row < this.height; ++row) { for (let col = 0; col < this.width; ++col) { let val = this.get(row, col); if (includeEmpty || val != null) { val = f(val, row, col); if (val !== undefined) { return val; } } } } } map(f, includeEmpty) { const m = new Matrix(); this.each(function(el, row, col) { m.set(row, col, f(el, row, col)); }, includeEmpty); return m; } transpose() { const m = new Matrix(); this.each(function(el, row, col) { m.set(col, row, el); }); return m; } unit(n) { this.width = this.height = n; const a = this.data = new Array(n); for (let i = n; --i >= 0;) { const row = a[i] = new Array(n); for (let j = n; --j >= 0;) { row[j] = i === j ? 1 : 0; } } return this; } multiply(b) { const a = this; const m = new Matrix(); for (let row = 0; row < a.height; ++row) { for (let col = 0; col < b.width; ++col) { let s = 0; for (let i = 0; i < a.width; ++i) { const va = a.get(row, i); const vb = b.get(i, col); if (typeof va === "number" && typeof vb === "number") { s += va * vb; } } m.set(row, col, s); } } return m; } inverse() { const n = this.width; const m = this.augment(new Matrix().unit(n)); const a = m.data; // Gaussian elimination // https://en.wikipedia.org/wiki/Gaussian_elimination#Finding_the_inverse_of_a_matrix // 1. Get zeros below main diagonal for (let k = 0; k < n; ++k) { const imax = argmax(k, n, function(i) { return a[i][k]; }); if (!a[imax][k]) { return null; // singular matrix } if (k !== imax) { let tmp = a[k]; a[k] = a[imax]; a[imax] = tmp; } for (let i = k + 1; i < n; ++i) { for (let j = k + 1; j < 2 * n; ++j) { a[i][j] -= a[k][j] * a[i][k] / a[k][k]; } a[i][k] = 0; } } // 2. Get 1-s on main diagonal, dividing by pivot for (let i = 0; i < n; ++i) { for (let f = a[i][i], j = 0; j < 2 * n; ++j) { a[i][j] /= f; } } // 3. Get zeros above main diagonal. Actually, we only care to compute the right side // here (that will be the inverse), so in the inner loop below we go while j >= n, // instead of j >= k. for (let k = n; --k >= 0;) { for (let i = k; --i >= 0;) { if (a[i][k]) { for (let j = 2 * n; --j >= n;) { a[i][j] -= a[k][j] * a[i][k]; } } } } return m.slice(0, n, n, n); } augment(m) { const ret = this.clone(); const n = ret.width; m.each(function(val, row, col) { ret.set(row, col + n, val); }); return ret; } slice(row, col, height, width) { const m = new Matrix(); for (let i = 0; i < height; ++i) { for (let j = 0; j < width; ++j) { m.set(i, j, this.get(row + i, col + j)); } } return m; } } function argmax(start, end, f) { let max = f(start), pos = start; for (let i = start + 1; i < end; i++) { const v = f(start); if (v > max) { max = v; pos = start; } } return pos; } export default Matrix;