Compare commits
No commits in common. "a01b7a17ef3bd5db4e2e96028dee4e7d706f0210" and "6251835aa0c1d96ec0e4752260c90a34b297cd3a" have entirely different histories.
a01b7a17ef
...
6251835aa0
11 changed files with 30 additions and 496 deletions
11
common/package-lock.json
generated
11
common/package-lock.json
generated
|
@ -13,7 +13,6 @@
|
||||||
"@rollup/plugin-babel": "^6.0.4",
|
"@rollup/plugin-babel": "^6.0.4",
|
||||||
"@rollup/plugin-commonjs": "^28.0.0",
|
"@rollup/plugin-commonjs": "^28.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
"@types/chai-as-promised": "^8.0.1",
|
|
||||||
"c8": "^10.1.2",
|
"c8": "^10.1.2",
|
||||||
"rollup": "^4.22.4",
|
"rollup": "^4.22.4",
|
||||||
"rollup-plugin-cleanup": "^3.2.1"
|
"rollup-plugin-cleanup": "^3.2.1"
|
||||||
|
@ -2199,17 +2198,9 @@
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.0.tgz",
|
||||||
"integrity": "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA==",
|
"integrity": "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/chai-as-promised": {
|
|
||||||
"version": "8.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-8.0.1.tgz",
|
|
||||||
"integrity": "sha512-dAlDhLjJlABwAVYObo9TPWYTRg9NaQM5CXeaeJYcYAkvzUf0JRLIiog88ao2Wqy/20WUnhbbUZcgvngEbJ3YXQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/chai": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/chai-spies": {
|
"node_modules/@types/chai-spies": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz",
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^5.0.0",
|
"@types/chai": "^5.0.0",
|
||||||
"@types/chai-spies": "^1.0.6",
|
"@types/chai-spies": "^1.0.6",
|
||||||
"@types/chai-as-promised": "^8.0.1",
|
|
||||||
"@types/mocha": "^10.0.8",
|
"@types/mocha": "^10.0.8",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"chai-as-promised": "^8.0.0",
|
"chai-as-promised": "^8.0.0",
|
||||||
|
|
|
@ -266,6 +266,11 @@ export function roundTo(value, exp) {
|
||||||
return +(value[0] + "e" + (value[1] ? (+value[1] + exp) : exp))
|
return +(value[0] + "e" + (value[1] ? (+value[1] + exp) : exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setVar(name, value, variables) {
|
||||||
|
if(variables) variables[name] = value
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
export function arrayIndex(array, index) {
|
export function arrayIndex(array, index) {
|
||||||
return array[index | 0]
|
return array[index | 0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import * as Instruction from "../lib/expr-eval/instruction.mjs"
|
||||||
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
||||||
import { HelperInterface, LatexInterface } from "./interface.mjs"
|
import { HelperInterface, LatexInterface } from "./interface.mjs"
|
||||||
|
|
||||||
const unicodechars = ["pi", "∞",
|
const unicodechars = [
|
||||||
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
||||||
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
||||||
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
||||||
|
@ -30,9 +30,9 @@ const unicodechars = ["pi", "∞",
|
||||||
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
|
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
|
||||||
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
|
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
|
||||||
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
||||||
"₄", "₅", "₆", "₇", "₈", "₉", "₀"
|
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
|
||||||
]
|
"pi", "∞"]
|
||||||
const equivalchars = ["\\pi", "\\infty",
|
const equivalchars = [
|
||||||
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
||||||
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
||||||
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
||||||
|
@ -42,7 +42,7 @@ const equivalchars = ["\\pi", "\\infty",
|
||||||
"{}_{t}", "{}^{1}", "{}^{2}", "{}^{3}", "{}^{4}", "{}^{5}", "{}^{6}",
|
"{}_{t}", "{}^{1}", "{}^{2}", "{}^{3}", "{}^{4}", "{}^{5}", "{}^{6}",
|
||||||
"{}^{7}", "{}^{8}", "{}^{9}", "{}^{0}", "{}_{1}", "{}_{2}", "{}_{3}",
|
"{}^{7}", "{}^{8}", "{}^{9}", "{}^{0}", "{}_{1}", "{}_{2}", "{}_{3}",
|
||||||
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
|
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
|
||||||
]
|
"\\pi", "\\infty"]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class containing the result of a LaTeX render.
|
* Class containing the result of a LaTeX render.
|
||||||
|
@ -142,10 +142,9 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
parif(elem, contents) {
|
parif(elem, contents) {
|
||||||
elem = elem.toString()
|
elem = elem.toString()
|
||||||
const contains = contents.some(x => elem.indexOf(x) > 0)
|
if(elem[0] !== "(" && elem.at(-1) !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
||||||
if(contains && (elem[0] !== "(" || elem.at(-1) !== ")"))
|
|
||||||
return this.par(elem)
|
return this.par(elem)
|
||||||
if(!contains && elem[0] === "(" && elem.at(-1) === ")")
|
if(elem[0] === "(" && elem.at(-1) === ")")
|
||||||
return elem.removeEnclosure()
|
return elem.removeEnclosure()
|
||||||
return elem
|
return elem
|
||||||
}
|
}
|
||||||
|
@ -170,14 +169,13 @@ class LatexAPI extends Module {
|
||||||
else
|
else
|
||||||
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
|
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
|
||||||
case "sqrt":
|
case "sqrt":
|
||||||
const arg = this.parif(args.join(", "), [])
|
return `\\sqrt\\left(${args.join(", ")}\\right)`
|
||||||
return `\\sqrt{${arg}}`
|
|
||||||
case "abs":
|
case "abs":
|
||||||
return `\\left|${args.join(", ")}\\right|`
|
return `\\left|${args.join(", ")}\\right|`
|
||||||
case "floor":
|
case "floor":
|
||||||
return `\\left\\lfloor{${args.join(", ")}}\\right\\rfloor`
|
return `\\left\\lfloor${args.join(", ")}\\right\\rfloor`
|
||||||
case "ceil":
|
case "ceil":
|
||||||
return `\\left\\lceil{${args.join(", ")}}\\right\\rceil`
|
return `\\left\\lceil${args.join(", ")}\\right\\rceil`
|
||||||
default:
|
default:
|
||||||
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
|
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
|
||||||
}
|
}
|
||||||
|
@ -191,17 +189,16 @@ class LatexAPI extends Module {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
variable(vari, wrapIn$ = false) {
|
variable(vari, wrapIn$ = false) {
|
||||||
if(wrapIn$) {
|
if(wrapIn$)
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
|
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return vari
|
return vari
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +293,7 @@ class LatexAPI extends Module {
|
||||||
nstack.push(this.parif(n1, ["+", "-", "*", "/", "^"]) + "!")
|
nstack.push(this.parif(n1, ["+", "-", "*", "/", "^"]) + "!")
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
nstack.push(this.functionToLatex(f, [this.parif(n1, ["+", "-", "*", "/", "^"])]))
|
nstack.push(f + this.parif(n1, ["+", "-", "*", "/", "^"]))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -331,6 +328,9 @@ class LatexAPI extends Module {
|
||||||
throw new EvalError("invalid Expression")
|
throw new EvalError("invalid Expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(nstack.length > 1) {
|
||||||
|
nstack = [nstack.join(";")]
|
||||||
|
}
|
||||||
return String(nstack[0])
|
return String(nstack[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,231 +0,0 @@
|
||||||
/**
|
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
|
||||||
* Copyright (C) 2021-2024 Ad5001
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Load prior tests
|
|
||||||
|
|
||||||
import { describe, it } from "mocha"
|
|
||||||
import { expect } from "chai"
|
|
||||||
|
|
||||||
import * as Polyfill from "../../src/lib/expr-eval/polyfill.mjs"
|
|
||||||
import {
|
|
||||||
andOperator,
|
|
||||||
cbrt,
|
|
||||||
equal,
|
|
||||||
expm1,
|
|
||||||
hypot,
|
|
||||||
lessThan,
|
|
||||||
log1p,
|
|
||||||
log2,
|
|
||||||
notEqual
|
|
||||||
} from "../../src/lib/expr-eval/polyfill.mjs"
|
|
||||||
|
|
||||||
describe("Math/Polyfill", () => {
|
|
||||||
describe("#AADDDD", function() {
|
|
||||||
it("should add two numbers", function() {
|
|
||||||
expect(Polyfill.add(2, 3)).to.equal(5)
|
|
||||||
expect(Polyfill.add("2", "3")).to.equal(5)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#sub", function() {
|
|
||||||
it("should subtract two numbers", function() {
|
|
||||||
expect(Polyfill.sub(2, 1)).to.equal(1)
|
|
||||||
expect(Polyfill.sub("2", "1")).to.equal(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#mul", function() {
|
|
||||||
it("should multiply two numbers", function() {
|
|
||||||
expect(Polyfill.mul(2, 3)).to.equal(6)
|
|
||||||
expect(Polyfill.mul("2", "3")).to.equal(6)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#div", function() {
|
|
||||||
it("should divide two numbers", function() {
|
|
||||||
expect(Polyfill.div(10, 2)).to.equal(5)
|
|
||||||
expect(Polyfill.div("10", "2")).to.equal(5)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#mod", function() {
|
|
||||||
it("should return the modulo of two numbers", function() {
|
|
||||||
expect(Polyfill.mod(10, 3)).to.equal(1)
|
|
||||||
expect(Polyfill.mod("10", "3")).to.equal(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#concat", function() {
|
|
||||||
it("should return the concatenation of two strings", function() {
|
|
||||||
expect(Polyfill.concat(10, 3)).to.equal("103")
|
|
||||||
expect(Polyfill.concat("abc", "def")).to.equal("abcdef")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#equal", function() {
|
|
||||||
it("should return whether its two arguments are equal", function() {
|
|
||||||
expect(Polyfill.equal(10, 3)).to.be.false
|
|
||||||
expect(Polyfill.equal(10, 10)).to.be.true
|
|
||||||
expect(Polyfill.equal("abc", "def")).to.be.false
|
|
||||||
expect(Polyfill.equal("abc", "abc")).to.be.true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#notEqual", function() {
|
|
||||||
it("should return whether its two arguments are not equal", function() {
|
|
||||||
expect(Polyfill.notEqual(10, 3)).to.be.true
|
|
||||||
expect(Polyfill.notEqual(10, 10)).to.be.false
|
|
||||||
expect(Polyfill.notEqual("abc", "def")).to.be.true
|
|
||||||
expect(Polyfill.notEqual("abc", "abc")).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#greaterThan", function() {
|
|
||||||
it("should return whether its first argument is strictly greater than its second", function() {
|
|
||||||
expect(Polyfill.greaterThan(10, 3)).to.be.true
|
|
||||||
expect(Polyfill.greaterThan(10, 10)).to.be.false
|
|
||||||
expect(Polyfill.greaterThan(10, 30)).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#lessThan", function() {
|
|
||||||
it("should return whether its first argument is strictly less than its second", function() {
|
|
||||||
expect(Polyfill.lessThan(10, 3)).to.be.false
|
|
||||||
expect(Polyfill.lessThan(10, 10)).to.be.false
|
|
||||||
expect(Polyfill.lessThan(10, 30)).to.be.true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#greaterThanEqual", function() {
|
|
||||||
it("should return whether its first argument is greater or equal to its second", function() {
|
|
||||||
expect(Polyfill.greaterThanEqual(10, 3)).to.be.true
|
|
||||||
expect(Polyfill.greaterThanEqual(10, 10)).to.be.true
|
|
||||||
expect(Polyfill.greaterThanEqual(10, 30)).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#lessThanEqual", function() {
|
|
||||||
it("should return whether its first argument is strictly less than its second", function() {
|
|
||||||
expect(Polyfill.lessThanEqual(10, 3)).to.be.false
|
|
||||||
expect(Polyfill.lessThanEqual(10, 10)).to.be.true
|
|
||||||
expect(Polyfill.lessThanEqual(10, 30)).to.be.true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#andOperator", function() {
|
|
||||||
it("should return whether its arguments are both true", function() {
|
|
||||||
expect(Polyfill.andOperator(true, true)).to.be.true
|
|
||||||
expect(Polyfill.andOperator(true, false)).to.be.false
|
|
||||||
expect(Polyfill.andOperator(false, true)).to.be.false
|
|
||||||
expect(Polyfill.andOperator(false, false)).to.be.false
|
|
||||||
expect(Polyfill.andOperator(10, 3)).to.be.true
|
|
||||||
expect(Polyfill.andOperator(10, 0)).to.be.false
|
|
||||||
expect(Polyfill.andOperator(0, 0)).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#orOperator", function() {
|
|
||||||
it("should return whether one of its arguments is true", function() {
|
|
||||||
expect(Polyfill.orOperator(true, true)).to.be.true
|
|
||||||
expect(Polyfill.orOperator(true, false)).to.be.true
|
|
||||||
expect(Polyfill.orOperator(false, true)).to.be.true
|
|
||||||
expect(Polyfill.orOperator(false, false)).to.be.false
|
|
||||||
expect(Polyfill.orOperator(10, 3)).to.be.true
|
|
||||||
expect(Polyfill.orOperator(10, 0)).to.be.true
|
|
||||||
expect(Polyfill.orOperator(0, 0)).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#inOperator", function() {
|
|
||||||
it("should check if second argument contains first", function() {
|
|
||||||
expect(Polyfill.inOperator("a", ["a", "b", "c"])).to.be.true
|
|
||||||
expect(Polyfill.inOperator(3, [0, 1, 2])).to.be.false
|
|
||||||
expect(Polyfill.inOperator(3, [0, 1, 3, 2])).to.be.true
|
|
||||||
expect(Polyfill.inOperator("a", "abcdef")).to.be.true
|
|
||||||
expect(Polyfill.inOperator("a", "bcdefg")).to.be.false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#sinh, #cosh, #tanh, #asinh, #acosh, #atanh", function() {
|
|
||||||
const EPSILON = 1e-12
|
|
||||||
for(let x = -.9; x < 1; x += 0.1) {
|
|
||||||
expect(Polyfill.sinh(x)).to.be.approximately(Math.sinh(x), EPSILON)
|
|
||||||
expect(Polyfill.cosh(x)).to.be.approximately(Math.cosh(x), EPSILON)
|
|
||||||
expect(Polyfill.tanh(x)).to.be.approximately(Math.tanh(x), EPSILON)
|
|
||||||
expect(Polyfill.asinh(x)).to.be.approximately(Math.asinh(x), EPSILON)
|
|
||||||
expect(Polyfill.atanh(x)).to.be.approximately(Math.atanh(x), EPSILON)
|
|
||||||
}
|
|
||||||
for(let x = 1.1; x < 10; x += 0.1) {
|
|
||||||
expect(Polyfill.sinh(x)).to.be.approximately(Math.sinh(x), EPSILON)
|
|
||||||
expect(Polyfill.cosh(x)).to.be.approximately(Math.cosh(x), EPSILON)
|
|
||||||
expect(Polyfill.tanh(x)).to.be.approximately(Math.tanh(x), EPSILON)
|
|
||||||
expect(Polyfill.asinh(x)).to.be.approximately(Math.asinh(x), EPSILON)
|
|
||||||
expect(Polyfill.acosh(x)).to.be.approximately(Math.acosh(x), EPSILON)
|
|
||||||
expect(Polyfill.log10(x)).to.be.approximately(Math.log10(x), EPSILON)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#trunc", function() {
|
|
||||||
it("should return the decimal part of floats", function() {
|
|
||||||
for(let x = -10; x < 10; x += 0.1)
|
|
||||||
expect(Polyfill.trunc(x)).to.equal(Math.trunc(x))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#gamma", function() {
|
|
||||||
it("should return the product of factorial(x - 1)", function() {
|
|
||||||
expect(Polyfill.gamma(0)).to.equal(Infinity)
|
|
||||||
expect(Polyfill.gamma(1)).to.equal(1)
|
|
||||||
expect(Polyfill.gamma(2)).to.equal(1)
|
|
||||||
expect(Polyfill.gamma(3)).to.equal(2)
|
|
||||||
expect(Polyfill.gamma(4)).to.equal(6)
|
|
||||||
expect(Polyfill.gamma(5)).to.equal(24)
|
|
||||||
expect(Polyfill.gamma(172)).to.equal(Infinity)
|
|
||||||
expect(Polyfill.gamma(172.3)).to.equal(Infinity)
|
|
||||||
expect(Polyfill.gamma(.2)).to.approximately(4.590_843_712, 1e-8)
|
|
||||||
expect(Polyfill.gamma(5.5)).to.be.approximately(52.34277778, 1e-8)
|
|
||||||
expect(Polyfill.gamma(90.2)).to.equal(4.0565358202825355e+136)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#hypot", function() {
|
|
||||||
it("should return the hypothenus length of a triangle whose length are provided in arguments", function() {
|
|
||||||
for(let x = 0; x < 10; x += 0.3) {
|
|
||||||
expect(Polyfill.hypot(x)).to.be.approximately(Math.hypot(x), Number.EPSILON)
|
|
||||||
for(let y = 0; y < 10; y += 0.3) {
|
|
||||||
expect(Polyfill.hypot(x, y)).to.be.approximately(Math.hypot(x, y), Number.EPSILON)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#sign, #cbrt, #exmp1", function() {
|
|
||||||
for(let x = -10; x < 10; x += 0.3) {
|
|
||||||
expect(Polyfill.sign(x)).to.approximately(Math.sign(x), 1e-12)
|
|
||||||
expect(Polyfill.cbrt(x)).to.approximately(Math.cbrt(x), 1e-12)
|
|
||||||
expect(Polyfill.expm1(x)).to.approximately(Math.expm1(x), 1e-12)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#log1p, #log2", function() {
|
|
||||||
for(let x = 1; x < 10; x += 0.3) {
|
|
||||||
expect(Polyfill.log1p(x)).to.be.approximately(Math.log1p(x), 1e-12)
|
|
||||||
expect(Polyfill.log2(x)).to.be.approximately(Math.log2(x), 1e-12)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -22,10 +22,8 @@ import { MockLatex } from "./mock/latex.mjs"
|
||||||
|
|
||||||
import { use } from "chai"
|
import { use } from "chai"
|
||||||
import spies from "chai-spies"
|
import spies from "chai-spies"
|
||||||
import promised from "chai-as-promised"
|
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
use(promised)
|
|
||||||
const { spy } = use(spies)
|
const { spy } = use(spies)
|
||||||
|
|
||||||
globalThis.Helper = new MockHelper()
|
globalThis.Helper = new MockHelper()
|
||||||
|
|
|
@ -22,8 +22,7 @@ const DEFAULT_SETTINGS = {
|
||||||
"check_for_updates": true,
|
"check_for_updates": true,
|
||||||
"reset_redo_stack": true,
|
"reset_redo_stack": true,
|
||||||
"last_install_greet": "0",
|
"last_install_greet": "0",
|
||||||
"enable_latex": true,
|
"enable_latex": false,
|
||||||
"enable_latex_async": true,
|
|
||||||
"expression_editor": {
|
"expression_editor": {
|
||||||
"autoclose": true,
|
"autoclose": true,
|
||||||
"colorize": true,
|
"colorize": true,
|
||||||
|
|
|
@ -22,7 +22,10 @@ const PIXEL = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAA
|
||||||
|
|
||||||
export class MockLatex {
|
export class MockLatex {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.supportsAsyncRender = true
|
}
|
||||||
|
|
||||||
|
get supportsAsyncRender() {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,231 +0,0 @@
|
||||||
/**
|
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
|
||||||
* Copyright (C) 2021-2024 Ad5001
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Load prior tests
|
|
||||||
import "../basics/utils.mjs"
|
|
||||||
import "./base.mjs"
|
|
||||||
import "./expreval.mjs"
|
|
||||||
|
|
||||||
import { describe, it } from "mocha"
|
|
||||||
import { expect } from "chai"
|
|
||||||
import { existsSync } from "../mock/fs.mjs"
|
|
||||||
|
|
||||||
const { spy } = chaiPlugins
|
|
||||||
|
|
||||||
import ExprEval from "../../src/module/expreval.mjs"
|
|
||||||
import LatexAPI from "../../src/module/latex.mjs"
|
|
||||||
|
|
||||||
describe("Module/Latex", function() {
|
|
||||||
it("is defined as a global", function() {
|
|
||||||
expect(globalThis.Modules.Latex).to.equal(LatexAPI)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#initialize", function() {
|
|
||||||
it("isn't enabled before initialization", function() {
|
|
||||||
expect(LatexAPI.enabled).to.be.false
|
|
||||||
})
|
|
||||||
|
|
||||||
it("is enabled after initialization", function() {
|
|
||||||
LatexAPI.initialize({ latex: Latex, helper: Helper })
|
|
||||||
expect(LatexAPI.enabled).to.equal(Helper.getSetting("enable_latex"))
|
|
||||||
expect(LatexAPI.initialized).to.be.true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#requestAsyncRender", function() {
|
|
||||||
it("should return a render result with a valid source, a width, and a height", async function() {
|
|
||||||
const data = await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
expect(data).to.be.an("object")
|
|
||||||
expect(data.source).to.be.a("string")
|
|
||||||
expect(data.source).to.satisfy(existsSync)
|
|
||||||
expect(data.height).to.be.a("number")
|
|
||||||
expect(data.width).to.be.a("number")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should call functions from the LaTeX module", async function() {
|
|
||||||
const renderSyncSpy = spy.on(Latex, "renderSync")
|
|
||||||
const renderAsyncSpy = spy.on(Latex, "renderAsync")
|
|
||||||
Latex.supportsAsyncRender = true
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
expect(renderAsyncSpy).to.have.been.called.once
|
|
||||||
expect(renderSyncSpy).to.have.been.called.once // Called async
|
|
||||||
Latex.supportsAsyncRender = false
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
expect(renderAsyncSpy).to.have.been.called.once // From the time before
|
|
||||||
expect(renderSyncSpy).to.have.been.called.twice
|
|
||||||
Latex.supportsAsyncRender = true
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should not reply with the same source for different markup, font size, or color.", async function() {
|
|
||||||
const datas = [
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033"),
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{4}", 13, "#AA0033"),
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 14, "#AA0033"),
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#0033AA")
|
|
||||||
]
|
|
||||||
const sources = datas.map(x => x.source)
|
|
||||||
expect(new Set(sources)).to.have.a.lengthOf(4)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#findPrerendered", function() {
|
|
||||||
it("should return the same data as async render for the same markup, font size, and color", async function() {
|
|
||||||
const data = await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
const found = LatexAPI.findPrerendered("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
expect(found).to.not.be.null
|
|
||||||
expect(found.source).to.equal(data.source)
|
|
||||||
expect(found.width).to.equal(data.width)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return null if the markup hasn't been prerendered with the same markup, font size, and color", async function() {
|
|
||||||
await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033")
|
|
||||||
expect(LatexAPI.findPrerendered("\\frac{y}{3}", 13, "#AA0033")).to.be.null
|
|
||||||
expect(LatexAPI.findPrerendered("\\frac{x}{3}", 12, "#AA0033")).to.be.null
|
|
||||||
expect(LatexAPI.findPrerendered("\\frac{x}{3}", 13, "#3300AA")).to.be.null
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#par", function() {
|
|
||||||
it("should add parentheses to strings", function() {
|
|
||||||
expect(LatexAPI.par("string")).to.equal("(string)")
|
|
||||||
expect(LatexAPI.par("aaaa")).to.equal("(aaaa)")
|
|
||||||
expect(LatexAPI.par("")).to.equal("()")
|
|
||||||
expect(LatexAPI.par("(example)")).to.equal("((example))")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#parif", function() {
|
|
||||||
it("should add parentheses to strings that contain one of the ones in the list", function() {
|
|
||||||
expect(LatexAPI.parif("string", ["+"])).to.equal("string")
|
|
||||||
expect(LatexAPI.parif("string+assert", ["+"])).to.equal("(string+assert)")
|
|
||||||
expect(LatexAPI.parif("string+assert", ["+", "-"])).to.equal("(string+assert)")
|
|
||||||
expect(LatexAPI.parif("string-assert", ["+", "-"])).to.equal("(string-assert)")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("shouldn't add new parentheses to strings that contains one of the ones in the list if they already have one", function() {
|
|
||||||
expect(LatexAPI.parif("(string+assert", ["+"])).to.equal("((string+assert)")
|
|
||||||
expect(LatexAPI.parif("string+assert)", ["+"])).to.equal("(string+assert))")
|
|
||||||
expect(LatexAPI.parif("(string+assert)", ["+"])).to.equal("(string+assert)")
|
|
||||||
expect(LatexAPI.parif("(string+assert)", ["+", "-"])).to.equal("(string+assert)")
|
|
||||||
expect(LatexAPI.parif("(string-assert)", ["+", "-"])).to.equal("(string-assert)")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("shouldn't add parentheses to strings that does not contains one of the ones in the list", function() {
|
|
||||||
expect(LatexAPI.parif("string", ["+"])).to.equal("string")
|
|
||||||
expect(LatexAPI.parif("string+assert", ["-"])).to.equal("string+assert")
|
|
||||||
expect(LatexAPI.parif("(string*assert", ["+", "-"])).to.equal("(string*assert")
|
|
||||||
expect(LatexAPI.parif("string/assert)", ["+", "-"])).to.equal("string/assert)")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should remove parentheses from strings that does not contains one of the ones in the list", function() {
|
|
||||||
expect(LatexAPI.parif("(string)", ["+"])).to.equal("string")
|
|
||||||
expect(LatexAPI.parif("(string+assert)", ["-"])).to.equal("string+assert")
|
|
||||||
expect(LatexAPI.parif("((string*assert)", ["+", "-"])).to.equal("(string*assert")
|
|
||||||
expect(LatexAPI.parif("(string/assert))", ["+", "-"])).to.equal("string/assert)")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#variable", function() {
|
|
||||||
const from = [
|
|
||||||
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
|
||||||
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
|
||||||
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
|
||||||
"Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ",
|
|
||||||
"Φ", "Ψ", "Ω", "ₐ", "ₑ", "ₒ", "ₓ",
|
|
||||||
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
|
|
||||||
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
|
|
||||||
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
|
||||||
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
|
|
||||||
"pi", "∞"]
|
|
||||||
const to = [
|
|
||||||
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
|
||||||
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
|
||||||
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
|
||||||
"\\Gamma", "\\Delta", "\\Theta", "\\Lambda", "\\Xi", "\\Pi", "\\Sigma",
|
|
||||||
"\\Phy", "\\Psi", "\\Omega", "{}_{a}", "{}_{e}", "{}_{o}", "{}_{x}",
|
|
||||||
"{}_{h}", "{}_{k}", "{}_{l}", "{}_{m}", "{}_{n}", "{}_{p}", "{}_{s}",
|
|
||||||
"{}_{t}", "{}^{1}", "{}^{2}", "{}^{3}", "{}^{4}", "{}^{5}", "{}^{6}",
|
|
||||||
"{}^{7}", "{}^{8}", "{}^{9}", "{}^{0}", "{}_{1}", "{}_{2}", "{}_{3}",
|
|
||||||
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
|
|
||||||
"\\pi", "\\infty"]
|
|
||||||
|
|
||||||
it("should convert unicode characters to their latex equivalent", function() {
|
|
||||||
for(let i = 0; i < from.length; i++)
|
|
||||||
expect(LatexAPI.variable(from[i])).to.include(to[i])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should wrap within dollar signs when the option is included", function() {
|
|
||||||
for(let i = 0; i < from.length; i++) {
|
|
||||||
expect(LatexAPI.variable(from[i], false)).to.equal(to[i])
|
|
||||||
expect(LatexAPI.variable(from[i], true)).to.equal(`$${to[i]}$`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to convert multiple of them", function() {
|
|
||||||
expect(LatexAPI.variable("α₂", false)).to.equal("\\alpha{}_{2}")
|
|
||||||
expect(LatexAPI.variable("∞piΠ", false)).to.equal("\\infty\\pi\\Pi")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#functionToLatex", function() {
|
|
||||||
it("should transform derivatives into latex fractions", function() {
|
|
||||||
const d1 = LatexAPI.functionToLatex("derivative", ["'3t'", "'t'", "x+2"])
|
|
||||||
const d2 = LatexAPI.functionToLatex("derivative", ["f", "x+2"])
|
|
||||||
expect(d1).to.equal("\\frac{d3x}{dx}")
|
|
||||||
expect(d2).to.equal("\\frac{df}{dx}(x+2)")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should transform integrals into latex limits", function() {
|
|
||||||
const i1 = LatexAPI.functionToLatex("integral", ["0", "x", "'3y'", "'y'"])
|
|
||||||
const i2 = LatexAPI.functionToLatex("integral", ["1", "2", "f"])
|
|
||||||
expect(i1).to.equal("\\int\\limits_{0}^{x}3y dy")
|
|
||||||
expect(i2).to.equal("\\int\\limits_{1}^{2}f(t) dt")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should transform sqrt functions to sqrt latex", function() {
|
|
||||||
const sqrt1 = LatexAPI.functionToLatex("sqrt", ["(x+2)"])
|
|
||||||
const sqrt2 = LatexAPI.functionToLatex("sqrt", ["\\frac{x}{2}"])
|
|
||||||
expect(sqrt1).to.equal("\\sqrt{x+2}")
|
|
||||||
expect(sqrt2).to.equal("\\sqrt{\\frac{x}{2}}")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should transform abs, floor and ceil", function() {
|
|
||||||
const abs = LatexAPI.functionToLatex("abs", ["x+3"])
|
|
||||||
const floor = LatexAPI.functionToLatex("floor", ["x+3"])
|
|
||||||
const ceil = LatexAPI.functionToLatex("ceil", ["x+3"])
|
|
||||||
expect(abs).to.equal("\\left|x+3\\right|")
|
|
||||||
expect(floor).to.equal("\\left\\lfloor{x+3}\\right\\rfloor")
|
|
||||||
expect(ceil).to.equal("\\left\\lceil{x+3}\\right\\rceil")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should transform regular functions into latex", function() {
|
|
||||||
const f1 = LatexAPI.functionToLatex("f", ["x+3", true])
|
|
||||||
const f2 = LatexAPI.functionToLatex("h_1", ["10"])
|
|
||||||
expect(f1).to.equal("\\mathrm{f}\\left(x+3, true\\right)")
|
|
||||||
expect(f2).to.equal("\\mathrm{h_1}\\left(10\\right)")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("#expression", function() {
|
|
||||||
it("should transform parsed expressions", function() {
|
|
||||||
const expr = ExprEval.parse("(+1! == 2/2 ? sin [-2.2][0] : f(t)^(1+1-1) + sqrt(A.t)) * 3 % 1")
|
|
||||||
const expected = "((((+1!))==(\\frac{2}{2}) ? (\\mathrm{sin}\\left(([(-2.2)][0])\\right)) : (\\mathrm{f}\\left(t\\right)^{1+1-1}+\\sqrt{A.t})) \\times 3) \\mathrm{mod} 1"
|
|
||||||
expect(LatexAPI.expression(expr.tokens)).to.equal(expected)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -181,7 +181,7 @@ class Latex(QObject):
|
||||||
"""
|
"""
|
||||||
markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color)
|
markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color)
|
||||||
if self.latexSupported and not path.exists(export_path + ".png"):
|
if self.latexSupported and not path.exists(export_path + ".png"):
|
||||||
print("Rendering", latex_markup)
|
print("Rendering", latex_markup, export_path)
|
||||||
# Generating file
|
# Generating file
|
||||||
latex_path = path.join(self.tempdir, str(markup_hash))
|
latex_path = path.join(self.tempdir, str(markup_hash))
|
||||||
# If the formula is just recolored or the font is just changed, no need to recreate the DVI.
|
# If the formula is just recolored or the font is just changed, no need to recreate the DVI.
|
||||||
|
|
|
@ -153,6 +153,7 @@ class PyPromise(QObject):
|
||||||
def _fulfill(self, data):
|
def _fulfill(self, data):
|
||||||
self._state = "fulfilled"
|
self._state = "fulfilled"
|
||||||
no_return = [None, QJSValue.SpecialValue.UndefinedValue]
|
no_return = [None, QJSValue.SpecialValue.UndefinedValue]
|
||||||
|
print("Fulfill")
|
||||||
for i in range(len(self._fulfills)):
|
for i in range(len(self._fulfills)):
|
||||||
try:
|
try:
|
||||||
result = self._fulfills[i](data)
|
result = self._fulfills[i](data)
|
||||||
|
|
Loading…
Reference in a new issue