DKL9 GitList
Repositories
DKL9 home
rtensor
Code
Commits
Branches
Tags
Search
Tree:
0133bd2
Branches
Tags
master
rtensor
rtensor_to_latex.js
Develop many cases in LaTeX converter
dkl9
commited
0133bd2
at 2023-203 16:57:06
rtensor_to_latex.js
Blame
History
Raw
"use strict"; import {AST} from "./maths_ast.js"; import {RTensorRender} from "./rtensor_to_html.js"; const LATEX_SYM = { "del": "\\nabla", "complexes": "\\mathbb{C}", "reals": "\\mathbb{R}", "rationals": "\\mathbb{Q}", "integers": "\\mathbb{Z}", "naturals": "\\mathbb{N}", }; [ "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", ].map(x => [x, x[0].toUpperCase() + x.slice(1)]). forEach(([x, y]) => (LATEX_SYM[x] = "\\" + x) + (LATEX_SYM[y] = "\\" + y)); [ "sin", "cos", "tan", "csc", "sec", "cot", "arcsin", "arccos", "arctan", "exp", "ln", "log", "partial", "pm", ].forEach(x => (LATEX_SYM[x] = "\\" + x)); RTensorRender.toLaTeX = function() { const lp = "\\left("; const rp = "\\right)"; switch (this.ts) { case 1: return this.v.split("_").map(s => LATEX_SYM[s] || s).join("_"); case 2: return `${this.v}`; case 3: const l = this.l.toLaTeX(); const r = this.r.toLaTeX(); const pl = this.l.shouldParenMul() ? `${lp}${l}${rp}` : l; const pr = this.r.shouldParenMul() ? `${lp}${r}${rp}` : r; switch (this.m) { case 0: if ((this.r.ts == 7 || this.r.ts == 8) && this.r.l.v == "pm") { return `${l} ${r}`; } return `${l} + ${r}`; case 1: return `${l} - ${r}`; case 2: const dot = this.l.ts == 2 && this.r.ts == 2; return `${pl}${dot ? " \\cdot " : ""}${pr}`; case 3: return `\\frac{${l}}{${r}}`; case 4: const spe = this.l.shouldParenExp(); return `${spe ? lp : ""}${l}${spe ? rp : ""}^{${r}}`; case 5: return `\\log_{${l}}{${pr}}`; case 6: case 11: return `${l} = ${r}`; case 7: return `\\sqrt[${l}]{${r}}`; // TODO: comma operator is confusing case 8: return `${l}, ${r}`; case 9: return `${l} < ${r}`; case 10: return `${l} \\mapsto ${r}`; default: return `${pl} ? ${pr}`; } break; case 4: return this.l ? `${this.m.toLaTeX()} = ${this.r.toLaTeX()}` : this.m.toLaTeX(); case 5: const m = this.m.toLaTeX(); const pm = this.m.shouldParenMul() ? `${lp}${m}${rp}` : m; switch (this.l) { case 0: return `+${pm}`; case 1: return `-${pm}`; case 3: return `\\frac{1}{${m}}`; case 4: return `e^{${m}}`; case 5: return `\\ln{${pm}}`; case 6: return `${m} = 0`; case 7: return `\\sqrt{${m}}`; case 9: return `${m} < 0`; default: return `? ${pm}`; } case 6: return `${this.l.toLaTeX()}[${this.m.toLaTeX()}]`; default: return "?"; } }; const thingy = { // fancy HTML version of the AST toHTML() { const lp = `<span class="big">(</span>`; const rp = `<span class="big">)</span>`; switch (this.ts) { case 1: // underscores indicate subscripts let sbu = this.v.replace(/'/g, ucFromNcr("prime")). split("_").map(s => RTensorRender.unicodify(s)). map(([s, b]) => { const s2 = new String(s); s2.greek = b; return s2; }); const cgify = s => s.greek ? `<span class="greek">${s}</span>` : s; const it = sbu.length > 1 ? `${cgify(sbu[0])}<sub>${sbu.slice(1).map(cgify).join("_")}</sub>`: cgify(sbu[0]); const spacify = sbu[0].replace(new RegExp(ucFromNcr("prime"), "g"), "").length >= 2 ? ` class="spaced"` : ``; return (AST.BUILTIN_FUNCS[this.v] != null || EXTRA_BUILTINS.has(this.v)) ? `<var class="builtin">${it}</var>` : `<var${spacify}>${it}</var>`; break; case 2: return `${this.v}`; case 3: const l = this.l.toHTML(); const r = this.r.toHTML(); const pl = this.l.shouldParenMul() ? `${lp}${l}${rp}` : l; const pr = this.r.shouldParenMul() ? `${lp}${r}${rp}` : r; switch (this.m) { case 0: if ((this.r.ts == 7 || this.r.ts == 8) && this.r.l.v == "pm") { return `${l} ${r}`; } return `${l} + ${r}`; case 1: return `${l} ${ucFromNcr("minus")} ${pr}`; case 2: const dot = this.l.ts == 2 && this.r.ts == 2; return `${pl}${dot ? ` ${ucFromNcr("sdot")} ` : ""}${pr}`; case 3: return `<table class="frac"> <tr class="numer"><td>${l}</td></tr> <tr class="denom"><td>${r}</td></tr> </table>`; case 4: const spe = this.l.shouldParenExp(); return `${spe ? lp : ""}${l}${spe ? rp : ""}<sup>${r}</sup>`; case 5: return `log<sub>${l}</sub>${lp}${r}${rp}`; case 6: case 11: return `${l} = ${r}`; case 7: return `<table class="rboth"><tr> <td><sup><sup><sup class="rord">${l}</sup></sup></sup><span class="rcal">${ucFromNcr("radic")}</span></td> <td><span class="rcand">${r}</span></td> </tr></table>`; // TODO: case 8 not handled, because comma operator is confusing case 8: return `${l}, ${r}`; case 9: return `${l} < ${r}`; case 10: return `${l} ↦ ${r}`; default: return `${this.l.toHTML()} ? ${this.r.toHTML()}`; } break; case 4: return this.l ? `${this.m.toHTML()} = ${this.r.toHTML()}` : this.m.toHTML(); case 5: const m = this.m.toHTML(); const pm = this.m.shouldParenMul() ? `${lp}${m}${rp}` : m; switch (this.l) { case 0: return `+${pm}`; case 1: return ucFromNcr("minus") + pm; // case 2 not handled, because unary asterisk is not defined case 3: return `<table class="frac"> <tr class="numer"><td>1</td></tr> <tr class="denom"><td>${m}</td></tr> </table>`; case 4: return `<var>e</var><sup>${m}</sup>`; case 5: return `ln(${m})`; case 6: return `${m} = 0`; case 7: return `<table class="rboth"><tr> <td><span class="rcal">${ucFromNcr("radic")}</span></td> <td><span class="rcand">${m}</span></td> </tr></table>`; // likewise, no unary comma case 9: return `${m} < 0`; // likewise, no unary maplet // likewise, no unary assignment default: return `? ${m}`; } break; case 6: return `${this.l.toHTML()}[${this.m.toHTML()}]`; case 7: case 8: const fa = this.m[0] ? this.m[0].toHTML() : "?"; const sa = this.m[1] ? this.m[1].toHTML() : "?"; const ta = this.m[2] ? this.m[2].toHTML() : "?"; const al = this.m.map(x => x.toHTML()); // special functions if (this.l.ts == 1) { switch (this.l.v) { case "abs": return `|${fa}|`; case "all": return `${al.map(x => "(" + x + ")").join(` ${ucFromNcr("and")} `)}`; case "any": return `${al.map(x => "(" + x + ")").join(` ${ucFromNcr("or")} `)}`; case "approx": return `${fa} ${ucFromNcr("approx")} ${sa}`; case "at": return `${lp}${fa}${rp}<span class="ebar">|</span><table class="ebounds"> <tr><td>${this.m[2] ? ta : ""}</td></tr><tr><td>${sa}</td></tr> </table>`; case "brace": return `<span class="big">{</span>${fa}<span class="big">}</span>`; case "bracket": return `<span class="big">[</span>${fa}<span class="big">]</span>`; case "ceil": return ucFromNcr("lceil") + fa + ucFromNcr("rceil"); case "cross": return `${fa} ${ucFromNcr("Cross")} ${sa}`; case "dot": return `${fa} ${ucFromNcr("sdot")} ${sa}`; case "fact": const clp = this.m[0].shouldParenMul() ? lp : ""; const crp = this.m[0].shouldParenMul() ? rp : ""; return `${clp}${fa}${crp}!`; case "filter": return `{<var>x</var> ${ucFromNcr("in")} ${fa} | ${sa}(<var>x</var>)}`; case "floor": return ucFromNcr("lfloor") + fa + ucFromNcr("rfloor"); case "hat": return `${fa}̂`; case "if": return `<span class="big">{</span><table class="pwf"><tr> <td>${sa}</td> <td class="cond">if ${fa}</td> </tr><tr> <td>${ta}</td> <td class="cond">otherwise</td> </tr></table>`; case "in": return `${fa} <span class="greek">${ucFromNcr("in")}</span> ${sa}`; case "int": return this.m[1] ? `<span class="intsym">${ucFromNcr("int")}</span> <table class="intbs"> <tr class="intb"><td class="ub">${sa}</td></tr> <tr class="intb"><td>${fa}</td></tr> </table> ${ta}` : `<span class="intsym">${ucFromNcr("int")}</span> ${fa}`; case "intersection": return `${fa} <span class="greek">${ucFromNcr("Intersection")}</span> ${sa}`; case "lim": return `<table class="limls"> <tr><td>${sa}</td></tr> <tr><td>${true ? `<span class="big">${ucFromNcr("Lambda")}</span>` : "lim"}</td></tr> <tr><td>${fa}</td></tr> </table> ${ta}`; case "map": return `${fa} 𝚳 ${sa}`; case "matrix": const rc = parseInt(this.m[0]?.v || "2") || 2; const cc = parseInt(this.m[1]?.v || "2") || 2; return `<table class="mat"> <tr><td>${(new Array(rc)).fill(0).map((_x, i) => this.m.slice(2 + i * cc, 2 + (i + 1) * cc).map(el => el.toHTML()).join("</td><td>")). join("</td></tr>\n<tr><td>")}</td></tr> </table>`; case "paren": return `${lp}${fa}${rp}`; case "pm": return `${ucFromNcr("plusmn")} ${this.m[0].shouldParenMul() ? lp + fa + rp : fa}`; case "product": return `<table class="sum"> <tr><td>${sa}</td></tr> <tr><td><span class="big">${ucFromNcr("Product")}</span></td></tr> <tr><td>${fa}</td></tr> </table> ${ta}`; case "sum": return `<table class="sum"> <tr><td>${sa}</td></tr> <tr><td><span class="big">${ucFromNcr("sum")}</span></td></tr> <tr><td>${fa}</td></tr> </table> ${ta}`; case "union": return `${fa} <span class="greek">${ucFromNcr("Union")}</span> ${sa}`; case "vec": // TODO: there's a better way (include notation in variable names) const m = fa.match(/(.*)(\<sub\>.*\<\/sub\>)?(.*?)/); //alert(`${m[1]} and ${m[2]} and ${m[3]}`); return `${m[1]}⃗${m[2] || ""}${m[3] || ""}`; } } // anything else return `${this.l.toHTML()}(${this.m.map(x => x.toHTML()).join(", ")})`; default: return "?"; } }, };