Published
Edited
Jul 25, 2020
1 star
Insert cell
Insert cell
class Ex {
constructor(ex) {
if(ex instanceof Ex) {
this.ex = ex.ex;
return;
}
if(typeof(ex)==='object') {
this.ex = ex;
return;
}
this.ex = math.simplify(math.parse(ex));
}
add(ex) {
if(Number.isInteger(ex)) {
ex = new Ex(ex);
}
const r = new math.OperatorNode('+', 'add', [this.ex, ex.ex]);
return new Ex(math.simplify(r));
}
sub(ex) {
if(Number.isInteger(ex)) {
ex = new Ex(ex);
}
const r = new math.OperatorNode('-', 'subtract', [this.ex, ex.ex]);
return new Ex(math.simplify(r));
}
mul(ex) {
if(Number.isInteger(ex)) {
ex = new Ex(ex);
}
const r = new math.OperatorNode('*', 'multiply', [this.ex, ex.ex]);
return new Ex(math.simplify(r));
}
div(ex) {
if(Number.isInteger(ex)) {
ex = new Ex(ex);
}
const r = new math.OperatorNode('/', 'divide', [this.ex, ex.ex]);
return new Ex(math.simplify(r));
}
isZero() {
if(this.ex instanceof math.ConstantNode) {
return this.ex.value == 0;
}
return false;
}
tex() {
function customLaTeX(node, options) {
if ((node.type === 'OperatorNode') && (node.fn === 'divide')) {
//don't forget to pass the options to the toTex functions
const num = node.args[0];
const den = node.args[1];
if ((node.type === 'OperatorNode') && (num.fn === 'unaryMinus')) {
return '-\\frac{'+num.args[0].toTex(options)+'}{'+den.toTex(options)+'}';
}
}
}
return this.ex.toTex({handler: customLaTeX});
}
}
Insert cell
Insert cell
class Mat {
constructor(m, n) {
if(Number.isInteger(m)) {
if(n == undefined) n = m;
this.m = m;
this.n = n;
this.rows = [];
for(let i = 1; i<=m; ++i) {
const columns = [];
for(let j = 1; j<=n; ++j) {
columns.push(new Ex(0));
}
this.rows.push(columns);
}
}
if(Array.isArray(m)) {
this.rows = m;
this.m = m.length;
for(let i=0; i<this.m;++i) {
if(!Array.isArray(this.rows[i])) {
this.rows[i] = [this.rows[i]];
}
this.n = this.rows[i].length;
}
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=this.n; ++j) {
this.set(i, j, new Ex(this.get(i, j)));
}
}
}
}
get(i, j) {
return this.rows[i-1][j-1];
}
set(i, j, v) {
this.rows[i-1][j-1] = v;
}
copy() {
const r = new Mat(this.m, this.n);
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=this.n; ++j) {
r.set(i, j, this.get(i, j));
}
}
return r;
}
add(m) {
const r = new Mat(this.m, this.n);
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=this.n; ++j) {
r.set(i, j, this.get(i, j).add(m.get(i, j)));
}
}
return r;
}
mul(m) {
const r = new Mat(this.m, m.n);
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=m.n; ++j) {
let v = new Ex(0);
for(let k=1; k<= this.n; ++k) {
v = v.add(this.get(i,k).mul(m.get(k,j)));
}
r.set(i, j, v);
}
}
return r;
}
tex() {
let r = '';
r = r + '\\left[\\begin{matrix}';
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=this.n; ++j) {
r = r + this.get(i, j).tex();
if(j!=this.n) r = r + '&';
}
if(i!=this.m) r = r + '\\\\';
}
r = r + '\\end{matrix}\\right]';
return r;
}
del(ii, jj) {
const r = new Mat(this.m-1, this.n-1);
for(let i = 1; i<=this.m; ++i) {
if(i == ii) continue;
for(let j = 1; j<=this.n; ++j) {
if(j == jj) continue;
r.set(i-(i>ii? 1 : 0), j-(j>jj? 1 : 0), this.get(i, j));
}
}
return r;
}
det() {
if(this.m == this.n) {
if(this.m == 1) return this.get(1,1);
let r = new Ex(0);
const ra = Array.from(Array(this.m), (_, i) => i + 1);
for(let p of permutations(ra)) {
let s = new Ex(sign(p));
for(let i = 1;i<=this.m;++i) {
s = s.mul(this.get(i, p[i-1]));
}
r = r.add(s);
}
return r;
}
}
transpose() {
const r = new Mat(this.n, this.m);
for(let i = 1; i<=this.m; ++i) {
for(let j = 1; j<=this.n; ++j) {
r.set(j, i, this.get(i, j));
}
}
return r;
}
rswap(i1, i2) {
const r = this.rows[i1-1];
this.rows[i1-1] = this.rows[i2-1];
this.rows[i2-1] = r;
}
rscale(i, k) {
for(let j = 1; j<=this.n; ++j) {
this.set(i, j, k.mul(this.get(i, j)));
}
}
radd(i1, i2, k) {
for(let j = 1; j<=this.n; ++j) {
this.set(i1, j, this.get(i1, j).add(k.mul(this.get(i2, j))));
}
}
}
Insert cell
function systemTex(A) {
let r = '';
let vars;
if(A.n<=5) {
vars = ['x','y','z','w'];
} else {
vars = [];
for(let j = 1;j<A.n; ++j) {
vars.push(`x_{${j}}`);
}
}
r = r + '\\left\\{\\begin{alignedat}{100}';
for(let i = 1; i<=A.m; ++i) {
let l = '';
let first = true;
let zero = true;
for(let j = 1; j<=A.n; ++j) {
let k = A.get(i, j);
if(j==A.n) {
if(zero && k.isZero()) {
l = '';
break;
}
if(zero) l = l + '0';
l = l + ' = &' + k.tex();
l = l + '\\\\';
} else {
if(k.isZero()) {
l = l + '& & &';
} else {
zero = false;
let kt = k.tex();
let s;
if(kt.charAt(0)==='-') {
s = '-';
kt = kt.slice(1);
} else {
if(j==1||first) s = '';
else s = '+';
}
if(kt==='1') kt='';
l = l + '&' + s + '&'+kt+'&'+vars[j-1];
first = false;
}
}
}
r = r + l;
}
r = r + '\\end{alignedat}\\right.';
return r;
}
Insert cell
{
const r = systemTex(new Mat([[1,1,'-4/3',3],[-4,0,'1/2',-16],[0,0,0,1]]));
return tex`${r}`;
}
Insert cell
function* rowEchelonSteps(A) {
const m = A.m;
const n = A.n;
yield({A: A, op: 'init', text: 'init'});
let ii = 1;
for(let j=1; j<=n; ++j) {
let i;
for(i=ii; i<=m; ++i) {
if(!A.get(i, j).isZero()) break;
}
if(i > m) continue;
if(i != ii) {
A.rswap(i, ii);
yield({A: A, op: 'rswap', i: i, ip:ii, text: `L_{${i}}\\leftrightarrow L_{${ii}}`});
}
yield({op:'pivot', pivot: [ii, j]});
for(i=ii+1; i<=m; ++i) {
const k = A.get(i, j).div(A.get(ii, j)).mul(-1);
if(k.isZero()) continue;
A.radd(i, ii, k);
let kt = k.tex();
if(kt.charAt(0)!='-') kt = '+'+kt;
if(kt === '+1' || kt === '-1') kt = kt.slice(0, -1);
yield({A: A, op: 'radd', i: i, ip:ii, k: k, text: `L_{${i}}\\leftarrow L_{${i}} ${kt}L_{${ii}}`});
}
if(ii == m) break;
ii = ii+1;
}
}
Insert cell
function* reducedRowEchelonSteps(A) {
const m = A.m;
const n = A.n;
const one = new Ex(1);
yield({A: A, op: 'init', text: 'init'});
let ii = 1;
for(let j=1; j<=n; ++j) {
let i;
for(i=ii; i<=m; ++i) {
if(!A.get(i, j).isZero()) break;
}
if(i > m) continue;
if(i != ii) {
A.rswap(i, ii);
yield({A: A, op: 'rswap', i: i, ip:ii, text: `L_{${i}}\\leftrightarrow L_{${ii}}` });
}
yield({op:'pivot', pivot: [ii, j]});
{
const k = one.div(A.get(ii, j));
let kt = k.tex();
if(kt === '1') break;
if(kt === '-1') kt = kt.slice(0, -1);
A.rscale(ii, k);
yield({A: A, op: 'rscale', i: ii, ip: ii, k: k, text: `L_{${ii}}\\leftarrow ${kt}L_{${ii}}`});
}
for(i=ii-1; i>=1; --i) {
const k = A.get(i, j).div(A.get(ii, j)).mul(-1);
if(k.isZero()) continue;
A.radd(i, ii, k);
let kt = k.tex();
if(kt.charAt(0)!='-') kt = '+'+kt;
if(kt === '+1' || kt === '-1') kt = kt.slice(0, -1);
yield({A: A, op: 'radd', i: i, ip:ii, k: k, text: `L_{${i}}\\leftarrow L_{${i}} ${kt}L_{${ii}}`});
}
for(i=ii+1; i<=m; ++i) {
const k = A.get(i, j).div(A.get(ii, j)).mul(-1);
if(k.isZero()) continue;
A.radd(i, ii, k);
let kt = k.tex();
if(kt.charAt(0)!='-') kt = '+'+kt;
if(kt === '+1' || kt === '-1') kt = kt.slice(0, -1);
yield({A: A, op: 'radd', i: i, ip:ii, k: k, text: `L_{${i}}\\leftarrow L_{${i}} ${kt}L_{${ii}}`});
}
if(ii == m) break;
ii = ii+1;
}
}
Insert cell
function inverse(A) {
let AI = new Mat(A.n, 2*A.n);
for(let i=1; i<=A.n; ++i) {
for(let j=1; j<=A.n; ++j) {
AI.set(i, j, A.get(i, j));
AI.set(i, j+A.n, new Ex((i==j)? 1 : 0));
}
}
for(let s of reducedRowEchelonSteps(AI)) {
}
const r = new Mat(A.n, A.n);
for(let i=1; i<=A.n; ++i) {
for(let j=1; j<=A.n; ++j) {
r.set(i, j, AI.get(i, j+A.n));
}
}
return r;
}
Insert cell
function* permutations(elements) {
if (elements.length === 1) {
yield elements;
} else {
let [first, ...rest] = elements;
for (let perm of permutations(rest)) {
for (let j = 0; j < elements.length; j++) {
let start = perm.slice(0, j);
let rest = perm.slice(j);
yield [...start, first, ...rest];
}
}
}
}
Insert cell
function sign(perm) {
let s = 1;
for(let i=0; i<perm.length; ++i) {
for(let j=i+1; j<perm.length; ++j) {
if(perm[i]>perm[j]) s = -s;
}
}
return s;
}
Insert cell
function gcd(a, b) {
return (b==0) ? a : gcd(b, a % b);
}
Insert cell
function collapsible(options={}) {
let {
showText = 'Mostrar',
hideText = 'Ocultar',
} = options;
const text = html`<div/>`;
const button = html`<button>${showText}</button>`;
text.appendChild(button);
const div = html`<div style='display: none;'/>`;
button.addEventListener("click", () => {
if(div.style.display == 'block') {
button.textContent = showText;
div.style.display = 'none';
} else {
button.textContent = hideText;
div.style.display = 'block';
}
});
text.appendChild(div);
return [text, div];
}
Insert cell
function appendEchelonSteps(A, div, options = {}) {
let {
method = rowEchelonSteps,
format = (m)=>m.tex()
} = options;
let last;
for(let s of method(A)) {
if(s.op === 'pivot') {
continue;
}
if(s.op === 'init') {
last = format(s.A);
continue;
}
const ta = format(s.A);
const tt = s.text;
div.appendChild(tex.block`${last}\;\underrightarrow{${tt}}\;${ta}`);
last = ta;
}
}
Insert cell
function appendBackSubst(U, div, options) {
const m = U.m;
const n = U.n;
const vars = ['x', 'y', 'z', 'w'];
const sol = new Mat(1, m);
const one = new Ex(1);
const zero = new Ex(0);
let r = '\\begin{aligned}';
for(let i=m;i>=1;--i) {
let l = '';
l = l + vars[i-1] + '& = '+ one.div(U.get(i, i)).tex()+
'\\left('+U.get(i, n).tex();
let s = U.get(i, n);
for(let j=i+1;j<=m;++j) {
l = l +'-'+U.get(i, j)+'\\left('+sol.get(1,i+1).tex()+'\\right)';
s = s.sub(U.get(i, j).mul(sol.get(1,i+1)));
}
sol.set(1, i, one.div(U.get(i, i)).mul(s));
l = l +'\\right) = '+sol.get(1, i).tex();
r = r + l + '\\\\';
}
r = r + '\\end{aligned}';
div.appendChild(tex.block`${r}`);
return sol;
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more