From d53f50193a1988a002e35ce0bdac91f001ce6ac4 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Tue, 22 Oct 2024 02:50:24 +0200 Subject: [PATCH] Finishing latex and polyfill tests. --- common/test/basics/polyfill.mjs | 231 ++++++++++++++++++++++++++++++++ common/test/module/latex.mjs | 91 ++++++++++++- 2 files changed, 320 insertions(+), 2 deletions(-) diff --git a/common/test/basics/polyfill.mjs b/common/test/basics/polyfill.mjs index e69de29..ce018f2 100644 --- a/common/test/basics/polyfill.mjs +++ b/common/test/basics/polyfill.mjs @@ -0,0 +1,231 @@ +/** + * 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 . + */ + +// 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) + } + }) +}) \ No newline at end of file diff --git a/common/test/module/latex.mjs b/common/test/module/latex.mjs index 13ffd03..02aaca5 100644 --- a/common/test/module/latex.mjs +++ b/common/test/module/latex.mjs @@ -17,6 +17,7 @@ */ // Load prior tests +import "../basics/utils.mjs" import "./base.mjs" import "./expreval.mjs" @@ -26,6 +27,7 @@ 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() { @@ -125,7 +127,7 @@ describe("Module/Latex", function() { 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") expect(LatexAPI.parif("string/assert)", ["+", "-"])).to.equal("string/assert)") }) @@ -139,6 +141,91 @@ describe("Module/Latex", function() { }) 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) + }) }) }) \ No newline at end of file