diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js new file mode 100644 index 0000000..65fd96d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js @@ -0,0 +1,50 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "../expr-eval.js" as ExprEval +.import "../utils.js" as Utils +.import "latex.js" as Latex + +var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy + "pi": Math.PI, + "π": Math.PI, + "inf": Infinity, + "Infinity": Infinity, + "∞": Infinity, + "e": Math.E, + "E": Math.E +} + +var currentVars = {} + +const parser = new ExprEval.Parser() +parser.functions.integral = function(a, b, f, variable) { + // https://en.wikipedia.org/wiki/Simpson%27s_rule + f = parser.parse(f).toJSFunction(variable, currentVars) + return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) +} + +const DERIVATION_PRECISION = 0.1 + +parser.functions.derivative = function(f, variable, x) { + f = parser.parse(f).toJSFunction(variable, currentVars) + return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js new file mode 100644 index 0000000..c7a425d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js @@ -0,0 +1,582 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "expression.js" as Expr + +/** + * Main abstract domain class + * It doesn't represent any kind of domain and is meant to be extended. + */ +class Domain { + constructor() {} + + /** + * Checks whether x is included in the domain. + * @param {number} x - The x value. + * @return {bool} true if included, false otherwise. + */ + includes(x) { return false } + + /** + * Returns a string representation of the domain. + * @return {string} String representation of the domain. + */ + toString() { return '???' } + + /** + * Returns a new domain that is the union between this domain and another. + * @param {Domain} domain - Domain to unionise with this. + * @return {Domain} newly created domain. + */ + union(domain) { return domain } + + /** + * Returns a new domain that is the intersection between this domain and another. + * @param {Domain} domain - Domain to get the interscection with this. + * @return {Domain} newly created domain. + */ + intersection(domain) { return this } + + /** + * Imports a domain from a string. + * @return {Domain} Found domain, string otherwise. + */ + static import(frm) { + switch(frm.trim().toUpperCase()) { + case "R": + case "ℝ": + return Domain.R + break; + case "RE": + case "R*": + case "ℝ*": + return Domain.RE + break; + case "RP": + case "R+": + case "ℝ⁺": + return Domain.RP + break; + case "RM": + case "R-": + case "ℝ⁻": + return Domain.RM + break; + case "RPE": + case "REP": + case "R+*": + case "R*+": + case "ℝ*⁺": + case "ℝ⁺*": + return Domain.RPE + break; + case "RME": + case "REM": + case "R-*": + case "R*-": + case "ℝ⁻*": + case "ℝ*⁻": + return Domain.RME + break; + case "ℕ": + case "N": + case "ZP": + case "ℤ⁺": + return Domain.N + break; + case "NLOG": + case "ℕˡᵒᵍ": + return Domain.NLog + break; + case "NE": + case "NP": + case "N*": + case "N+": + case "ℕ*": + case "ℕ⁺": + case "ZPE": + case "ZEP": + case "Z+*": + case "Z*+": + case "ℤ⁺*": + case "ℤ*⁺": + return Domain.NE + break; + case "Z": + case "ℤ": + return Domain.Z + break; + case "ZM": + case "Z-": + case "ℤ⁻": + return Domain.ZM + break; + case "ZME": + case "ZEM": + case "Z-*": + case "Z*-": + case "ℤ⁻*": + case "ℤ*⁻": + return Domain.ZME + break; + case "ZE": + case "Z*": + case "ℤ*": + return Domain.ZE + break; + default: + return new EmptySet() + break; + } + } +} + +/** + * Represents an empty set. + */ +class EmptySet extends Domain { + + includes(x) { return false } + + toString() { return "∅" } + + union(domain) { return domain } + + intersection(domain) { return this } + + static import(frm) { return new EmptySet() } +} + +/** + * Domain classes for ranges (e.g ]0;3[, [1;2[ ...) + */ +class Range extends Domain { + constructor(begin, end, openBegin, openEnd) { + super() + if(typeof begin == 'number' || typeof begin == 'string') begin = new Expr.Expression(begin.toString()) + this.begin = begin + if(typeof end == 'number' || typeof end == 'string') end = new Expr.Expression(end.toString()) + this.end = end + this.openBegin = openBegin + this.openEnd = openEnd + this.displayName = (openBegin ? "]" : "[") + begin.toString() + ";" + end.toString() + (openEnd ? "[" : "]") + } + + includes(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) && + ((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute())) + } + + toString() { + return this.displayName + } + + union(domain) { + if(domain instanceof EmptySet) return this + if(domain instanceof DomainSet) return domain.union(this) + if(domain instanceof UnionDomain) return domain.union(this) + if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) + if(domain instanceof MinusDomain) return new UnionDomain(this, domain) + if(domain instanceof Range) return new UnionDomain(this, domain) + } + + intersection(domain) { + if(domain instanceof EmptySet) return domain + if(domain instanceof DomainSet) return domain.intersection(this) + if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof IntersectionDomain) return domain.intersection(this) + if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) + if(domain instanceof Range) return new IntersectionDomain(this, domain) + } + + static import(frm) { + var openBegin = frm.trim().charAt(0) == "]" + var openEnd = frm.trim().charAt(frm.length -1) == "[" + var [begin, end] = frm.substr(1, frm.length-2).split(";") + return new Range(begin.trim(), end.trim(), openBegin, openEnd) + } +} + +/** + * Domain classes for special domains (N, Z, ...) + */ +class SpecialDomain extends Domain { + /** + * @constructs SpecialDomain + * @param {string} displayName + * @param {function} isValid - function returning true when number is in domain false when it isn't. + * @param {function} next - function provides the next positive value in the domain after the one given. + * @param {function} previous - function provides the previous positive value in the domain before the one given. + * @param {bool} moveSupported - Only true if next and previous functions are valid. + * @param items + */ + constructor(displayName, isValid, next = x => true, previous = x => true, + moveSupported = true) { + super() + this.displayName = displayName + this.isValid = isValid + this.nextValue = next + this.prevValue = previous + this.moveSupported = moveSupported + } + + includes(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + return this.isValid(x) + } + + next(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + return this.nextValue(x) + } + + previous(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + return this.prevValue(x) + } + + toString() { + return this.displayName + } + + union(domain) { + if(domain instanceof EmptySet) return this + if(domain instanceof DomainSet) return domain.union(this) + if(domain instanceof UnionDomain) return new UnionDomain(this, domain) + if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) + if(domain instanceof MinusDomain) return new UnionDomain(this, domain) + if(domain instanceof Range) return new UnionDomain(this, domain) + } + + intersection(domain) { + if(domain instanceof EmptySet) return domain + if(domain instanceof DomainSet) return domain.intersection(this) + if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) + if(domain instanceof Range) return new IntersectionDomain(this, domain) + } +} + +/** + * Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...) + */ +class DomainSet extends SpecialDomain { + constructor(values) { + super('', x => true, x => x, true) + var newVals = {} + this.executedValues = [] + for(var value of values) { + var expr = new Expr.Expression(value.toString()) + var ex = expr.execute() + newVals[ex] = expr + this.executedValues.push(ex) + } + this.executedValues.sort((a,b) => a-b) + this.values = this.executedValues.map(val => newVals[val]) + } + + includes(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + for(var value of this.values) + if(x == value.execute()) return true + return false + } + + next(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + if(x < this.executedValues[0]) return this.executedValues[0] + for(var i = 1; i < this.values.length; i++) { + var prevValue = this.executedValues[i-1] + var value = this.executedValues[i] + if(x >= prevValue && x < value) return value + } + return null + } + + previous(x) { + if(typeof x == 'string') x = Expr.executeExpression(x) + if(x > this.executedValues[this.executedValues.length-1]) + return this.executedValues[this.executedValues.length-1] + for(var i = 1; i < this.values.length; i++) { + var prevValue = this.executedValues[i-1] + var value = this.executedValues[i] + if(x > prevValue && x <= value) return prevValue + } + return null + } + + toString() { + return "{" + this.values.join(";") + "}" + } + + union(domain) { + if(domain instanceof EmptySet) return this + if(domain instanceof DomainSet) { + var newValues = [] + var values = this.values.concat(domain.values).filter(function(val){ + newValues.push(val.execute()) + return newValues.indexOf(val.execute()) == newValues.length - 1 + }) + return new DomainSet(values) + } + var notIncludedValues = [] + for(var value in this.values) { + var value = this.executedValues[i] + if(domain instanceof Range) { + if(domain.begin.execute() == value && domain.openBegin) { + domain.openBegin = false + } + if(domain.end.execute() == value && domain.openEnd) { + domain.openEnd = false + } + } + if(!domain.includes(value)) + notIncludedValues.push(this.values[i].toEditableString()) + } + if(notIncludedValues.length == 0) return domain + return new UnionDomain(domain, new DomainSet(notIncludedValues)) + } + + intersection(domain) { + if(domain instanceof EmptySet) return domain + if(domain instanceof DomainSet) { + var domValues = domain.values.map(expr => expr.execute()) + this.values = this.values.filter(function(val){ + return domValues.indexOf(val.execute()) >= 0 + }) + return this + } + var includedValues = [] + for(var i in this.values) { + var value = this.executedValues[i] + if(domain instanceof Range) { + if(domain.begin.execute() == value && !domain.openBegin) { + domain.openBegin = false + } + if(domain.end.execute() == value && !domain.openEnd) { + domain.openEnd = false + } + } + if(domain.includes(value)) + includedValues.push(this.values[i].toEditableString()) + } + if(includedValues.length == 0) return new EmptySet() + if(includedValues.length == this.values.length) return this + return new IntersectionDomain(domain, new DomainSet(includedValues)) + } + + static import(frm) { + return new DomainSet(frm.substr(1, frm.length-2).split(";")) + } +} + +/** + * Domain representing the union between two domains. + */ +class UnionDomain extends Domain { + constructor(dom1, dom2) { + super() + this.dom1 = dom1 + this.dom2 = dom2 + } + + includes(x) { + return this.dom1.includes(x) || this.dom2.includes(x) + } + + toString() { + return this.dom1.toString() + " ∪ " + this.dom2.toString() + } + + union(domain) { + if(domain instanceof EmptySet) return this + if(domain instanceof DomainSet) return domain.union(this) + if(domain instanceof Range) return domain.union(this) + if(domain instanceof UnionDomain) return new UnionDomain(this, domain) + if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) + if(domain instanceof MinusDomain) return new MinusDomain(this, domain) + } + + intersection(domain) { + if(domain instanceof EmptySet) return domain + if(domain instanceof DomainSet) return domain.intersection(this) + if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof IntersectionDomain) return this.dom1.intersection(domain.dom1).intersection(this.dom2).intersection(domain.dom2) + if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) + } + + static import(frm) { + var domains = frm.trim().split("∪") + if(domains.length == 1) domains = frm.trim().split("U") // Fallback + var dom1 = parseDomain(domains.pop()) + var dom2 = parseDomain(domains.join('∪')) + return dom1.union(dom2) + } +} + +/** + * Domain representing the intersection between two domains. + */ +class IntersectionDomain extends Domain { + constructor(dom1, dom2) { + super() + this.dom1 = dom1 + this.dom2 = dom2 + } + + includes(x) { + return this.dom1.includes(x) && this.dom2.includes(x) + } + + toString() { + return this.dom1.toString() + " ∩ " + this.dom2.toString() + } + + union(domain) { + if(domain instanceof EmptySet) return this + if(domain instanceof DomainSet) return domain.union(this) + if(domain instanceof Range) return domain.union(this) + if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2) + if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) + if(domain instanceof MinusDomain) return new MinusDomain(this, domain) + } + + intersection(domain) { + if(domain instanceof EmptySet) return domain + if(domain instanceof DomainSet) return domain.intersection(this) + if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) + if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) + } + + static import(frm) { + var domains = frm.trim().split("∩") + var dom1 = parseDomain(domains.pop()) + var dom2 = parseDomain(domains.join('∩')) + return dom1.intersection(dom2) + } +} + +/** + * Domain representing the minus between two domains. + */ +class MinusDomain extends Domain { + constructor(dom1, dom2) { + super() + this.dom1 = dom1 + this.dom2 = dom2 + } + + includes(x) { + return this.dom1.includes(x) && !this.dom2.includes(x) + } + + toString() { + return this.dom1.toString() + "∖" + this.dom2.toString() + } + + static import(frm) { + var domains = frm.trim().split("∖") + if(domains.length == 1) domains = frm.trim().split("\\") // Fallback + var dom1 = parseDomain(domains.shift()) + var dom2 = parseDomain(domains.join('∪')) + return new MinusDomain(dom1, dom2) + } +} + +Domain.RE = new MinusDomain("R", "{0}") +Domain.RE.displayName = "ℝ*" + +Domain.R = new Range(-Infinity,Infinity,true,true) +Domain.R.displayName = "ℝ" +Domain.RP = new Range(0,Infinity,true,false) +Domain.RP.displayName = "ℝ⁺" +Domain.RM = new Range(-Infinity,0,true,false) +Domain.RM.displayName = "ℝ⁻" +Domain.RPE = new Range(0,Infinity,true,true) +Domain.RPE.displayName = "ℝ⁺*" +Domain.RME = new Range(-Infinity,0,true,true) +Domain.RME.displayName = "ℝ⁻*" +Domain.N = new SpecialDomain('ℕ', x => x%1==0 && x >= 0, + x => Math.max(Math.floor(x)+1, 0), + x => Math.max(Math.ceil(x)-1, 0)) +Domain.NE = new SpecialDomain('ℕ*', x => x%1==0 && x > 0, + x => Math.max(Math.floor(x)+1, 1), + x => Math.max(Math.ceil(x)-1, 1)) +Domain.Z = new SpecialDomain('ℤ', x => x%1==0, x => Math.floor(x)+1, x => Math.ceil(x)-1) +Domain.ZE = new SpecialDomain('ℤ*', x => x%1==0 && x != 0, + x => Math.floor(x)+1 == 0 ? Math.floor(x)+2 : Math.floor(x)+1, + x => Math.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1) +Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1==0 && x <= 0, + x => Math.min(Math.floor(x)+1, 0), + x => Math.min(Math.ceil(x)-1, 0)) +Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1==0 && x < 0, + x => Math.min(Math.floor(x)+1, -1), + x => Math.min(Math.ceil(x)-1, -1)) +Domain.NLog = new SpecialDomain('ℕˡᵒᵍ', + x => x/Math.pow(10, x.toString().length-1) % 1 == 0 && x > 0, + function(x) { + var x10pow = Math.pow(10, x.toString().length-1) + return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow) + }, + function(x) { + var x10pow = Math.pow(10, x.toString().length-1) + return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow) + }) + +var refedDomains = [] + +/** + * Parses a domain, that can use parenthesises. + * e.g (N ∪ [-1;0[) ∩ (Z \ {0;3}) + * @param {string} domain - string of the domain to be parsed. + * @returns {Domain} Parsed domain. + */ +function parseDomain(domain) { + if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) + var domStr + while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { + var dom = parseDomainSimple(domStr[1].trim()); + domain = domain.replace(domStr[0], 'D' + refedDomains.length) + refedDomains.push(dom) + } + return parseDomainSimple(domain) +} + +/** + * Parses a domain, without parenthesises. + * e.g N ∪ [-1;0[, Z \ {0;3}, N+*... + * @param {string} domain - string of the domain to be parsed. + * @returns {Domain} Parsed domain. + */ +function parseDomainSimple(domain) { + domain = domain.trim() + if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain) + if(domain.includes("∩")) return IntersectionDomain.import(domain) + if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain) + if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain) + if(domain.includes("]") || domain.includes("[")) return Range.import(domain) + if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str))) + return Domain.import(domain) + if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))] + return new EmptySet() +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js new file mode 100644 index 0000000..aef7a61 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -0,0 +1,77 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as C +.import "latex.js" as Latex +.import "../utils.js" as Utils + +/** + * Represents any kind of x-based or non variable based expression. + */ +class Expression { + constructor(expr) { + this.expr = expr + this.calc = C.parser.parse(expr).simplify() + this.cached = this.isConstant() + this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null + this.latexMarkup = Latex.expressionToLatex(this.calc.tokens) + } + + isConstant() { + return !this.expr.includes("x") && !this.expr.includes("n") + } + + execute(x = 1) { + if(this.cached) return this.cachedValue + C.currentVars = Object.assign({'x': x}, C.evalVariables) + return this.calc.evaluate(C.currentVars) + } + + simplify(x) { + var expr = this.calc.substitute('x', x).simplify() + if(expr.evaluate(C.evalVariables) == 0) return '0' + var str = Utils.makeExpressionReadable(expr.toString()); + if(str != undefined && str.match(/^\d*\.\d+$/)) { + if(str.split('.')[1].split('0').length > 7) { + // Likely rounding error + str = parseFloat(str.substring(0, str.length-1)).toString(); + } + } + return str + } + + duplicate() { + return new Expression(this.toEditableString()) + } + + toEditableString() { + return this.calc.toString() + } + + toString(forceSign=false) { + var str = Utils.makeExpressionReadable(this.calc.toString()) + if(str[0] != '-' && forceSign) str = '+' + str + return str + } +} + +function executeExpression(expr){ + return (new Expression(expr.toString())).execute() +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/latex.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js similarity index 80% rename from LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/latex.js rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js index 9d010e3..3a69a47 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/latex.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js @@ -16,8 +16,9 @@ * along with this program. If not, see . */ +.pragma library -.import "expr-eval.js" as ExprEval +.import "../expr-eval.js" as ExprEval /** * Puts element within parenthesis. @@ -38,7 +39,7 @@ function par(elem) { * @returns {string} */ function parif(elem, contents) { - return contents.some(elem.includes) ? par(elem) : elem + return contents.some(x => elem.toString().includes(x)) ? par(elem) : elem } /** @@ -60,9 +61,9 @@ function expressionToLatex(tokens) { if (typeof item.value === 'number' && item.value < 0) { nstack.push(par(item.value)); } else if (Array.isArray(item.value)) { - nstack.push('[' + item.value.map(escapeValue).join(', ') + ']'); + nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']'); } else { - nstack.push(escapeValue(item.value)); + nstack.push(ExprEval.escapeValue(item.value)); } break; case ExprEval.IOP2: @@ -114,7 +115,10 @@ function expressionToLatex(tokens) { break; case ExprEval.IVAR: case ExprEval.IVARNAME: - nstack.push(item.value); + if(item.value == "pi") + nstack.push("π") + else + nstack.push(item.value); break; case ExprEval.IOP1: // Unary operator n1 = nstack.pop(); @@ -139,7 +143,21 @@ function expressionToLatex(tokens) { args.unshift(nstack.pop()); } f = nstack.pop(); - nstack.push(f + '(' + args.join(', ') + ')'); + // Handling various functions + if(f == "derivative") + nstack.push('\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(by, 'g'), 'x') + '}{dx}'); + else if(f == "integral") + nstack.push('\\int\\limits^{' + args[0] + '}_{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3]); + else if(f == "sqrt") + nstack.push('\\sqrt\\left(' + args.join(', ') + '\\right)'); + else if(f == "abs") + nstack.push('\\left|' + args.join(', ') + '\\right|'); + else if(f == "floor") + nstack.push('\\left\\lfloor' + args.join(', ') + '\\right\\rfloor'); + else if(f == "ceil") + nstack.push('\\left\\lceil' + args.join(', ') + '\\right\\rceil'); + else + nstack.push('\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)'); break; case ExprEval.IFUNDEF: nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2)); @@ -173,5 +191,5 @@ function expressionToLatex(tokens) { nstack = [ nstack.join(';') ]; } } - return Utils.makeExpressionReadable(String(nstack[0])); + return String(nstack[0]); } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js new file mode 100644 index 0000000..4dfbd8b --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js @@ -0,0 +1,78 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as C +.import "expression.js" as Expr +.import "../utils.js" as Utils + +/** + * Represents mathematical object for sequences. + */ +class Sequence extends Expr.Expression { + constructor(name, baseValues = {}, valuePlus = 1, expr = "") { + // u[n+valuePlus] = expr + super(expr) + this.name = name + this.baseValues = baseValues + this.calcValues = Object.assign({}, baseValues) + for(var n in this.calcValues) + if(['string', 'number'].includes(typeof this.calcValues[n])) + this.calcValues[n] = parser.parse(this.calcValues[n].toString()).simplify().evaluate(C.evalVariables) + this.valuePlus = parseInt(valuePlus) + } + + isConstant() { + return this.expr.indexOf("n") == -1 + } + + execute(n = 1) { + if(n in this.calcValues) + return this.calcValues[n] + this.cache(n) + return this.calcValues[n] + } + + simplify(n = 1) { + if(n in this.calcValues) + return Utils.makeExpressionReadable(this.calcValues[n].toString()) + this.cache(n) + return Utils.makeExpressionReadable(this.calcValues[n].toString()) + } + + cache(n = 1) { + var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) + var expr = parser.parse(str).simplify() + var l = {'n': n-this.valuePlus} // Just in case, add n (for custom functions) + l[this.name] = this.calcValues + currentVars = Object.assign(l, C.evalVariables) + this.calcValues[n] = expr.evaluate(currentVars) + } + + toString(forceSign=false) { + var str = Utils.makeExpressionReadable(this.calc.toString()) + if(str[0] != '-' && forceSign) str = '+' + str + var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus) + var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}` + ret += Object.keys(this.baseValues).map( + n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}` + ).join('; ') + return ret + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js index 30d93cb..c0b00ac 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js @@ -18,635 +18,24 @@ .pragma library -.import "expr-eval.js" as ExprEval -.import "utils.js" as Utils +.import "math/expression.js" as Expr +.import "math/sequence.js" as Seq +.import "math/domain.js" as Dom - -var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy - "pi": Math.PI, - "π": Math.PI, - "inf": Infinity, - "Infinity": Infinity, - "∞": Infinity, - "e": Math.E, - "E": Math.E -} - -var currentVars = {} - -const parser = new ExprEval.Parser() -parser.functions.integral = function(a, b, f, variable) { - // https://en.wikipedia.org/wiki/Simpson%27s_rule - f = parser.parse(f).toJSFunction(variable, currentVars) - return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) -} - -const DERIVATION_PRECISION = 0.1 - -parser.functions.derivative = function(f, variable, x) { - f = parser.parse(f).toJSFunction(variable, currentVars) - return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION -} - -class Expression { - constructor(expr) { - this.expr = expr - this.calc = parser.parse(expr).simplify() - this.cached = this.isConstant() - this.cachedValue = this.cached ? this.calc.evaluate(evalVariables) : null - } - - isConstant() { - return !this.expr.includes("x") && !this.expr.includes("n") - } - - execute(x = 1) { - if(this.cached) return this.cachedValue - currentVars = Object.assign({'x': x}, evalVariables) - return this.calc.evaluate(currentVars) - } - - simplify(x) { - var expr = this.calc.substitute('x', x).simplify() - if(expr.evaluate(evalVariables) == 0) return '0' - var str = Utils.makeExpressionReadable(expr.toString()); - if(str != undefined && str.match(/^\d*\.\d+$/)) { - if(str.split('.')[1].split('0').length > 7) { - // Likely rounding error - str = parseFloat(str.substring(0, str.length-1)).toString(); - } - } - return str - } - - duplicate() { - return new Expression(this.toEditableString()) - } - - toEditableString() { - return this.calc.toString() - } - - toString(forceSign=false) { - var str = Utils.makeExpressionReadable(this.calc.toString()) - if(str[0] != '-' && forceSign) str = '+' + str - return str - } -} - -function executeExpression(expr){ - return (new Expression(expr.toString())).execute() -} - -class Sequence extends Expression { - constructor(name, baseValues = {}, valuePlus = 1, expr = "") { - // u[n+valuePlus] = expr - super(expr) - this.name = name - this.baseValues = baseValues - this.calcValues = Object.assign({}, baseValues) - for(var n in this.calcValues) - if(['string', 'number'].includes(typeof this.calcValues[n])) - this.calcValues[n] = parser.parse(this.calcValues[n].toString()).simplify().evaluate(evalVariables) - this.valuePlus = parseInt(valuePlus) - } - - isConstant() { - return this.expr.indexOf("n") == -1 - } - - execute(n = 1) { - if(n in this.calcValues) - return this.calcValues[n] - this.cache(n) - return this.calcValues[n] - } - - simplify(n = 1) { - if(n in this.calcValues) - return Utils.makeExpressionReadable(this.calcValues[n].toString()) - this.cache(n) - return Utils.makeExpressionReadable(this.calcValues[n].toString()) - } - - cache(n = 1) { - var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) - var expr = parser.parse(str).simplify() - var l = {'n': n-this.valuePlus} // Just in case, add n (for custom functions) - l[this.name] = this.calcValues - currentVars = Object.assign(l, evalVariables) - this.calcValues[n] = expr.evaluate(currentVars) - } - - toString(forceSign=false) { - var str = Utils.makeExpressionReadable(this.calc.toString()) - if(str[0] != '-' && forceSign) str = '+' + str - var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus) - var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}` - ret += Object.keys(this.baseValues).map( - n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}` - ).join('; ') - return ret - } -} - +var Expression = Expr.Expression +var executeExpression = Expr.executeExpression +var Sequence = Seq.Sequence // Domains -class Domain { - constructor() {} - - includes(x) { return false } - - toString() { return '???' } - - union(domain) { return domain } - - intersection(domain) { return this } - - static import(frm) { - switch(frm.trim().toUpperCase()) { - case "R": - case "ℝ": - return Domain.R - break; - case "RE": - case "R*": - case "ℝ*": - return Domain.RE - break; - case "RP": - case "R+": - case "ℝ⁺": - return Domain.RP - break; - case "RM": - case "R-": - case "ℝ⁻": - return Domain.RM - break; - case "RPE": - case "REP": - case "R+*": - case "R*+": - case "ℝ*⁺": - case "ℝ⁺*": - return Domain.RPE - break; - case "RME": - case "REM": - case "R-*": - case "R*-": - case "ℝ⁻*": - case "ℝ*⁻": - return Domain.RME - break; - case "ℕ": - case "N": - case "ZP": - case "ℤ⁺": - return Domain.N - break; - case "NLOG": - case "ℕˡᵒᵍ": - return Domain.NLog - break; - case "NE": - case "NP": - case "N*": - case "N+": - case "ℕ*": - case "ℕ⁺": - case "ZPE": - case "ZEP": - case "Z+*": - case "Z*+": - case "ℤ⁺*": - case "ℤ*⁺": - return Domain.NE - break; - case "Z": - case "ℤ": - return Domain.Z - break; - case "ZM": - case "Z-": - case "ℤ⁻": - return Domain.ZM - break; - case "ZME": - case "ZEM": - case "Z-*": - case "Z*-": - case "ℤ⁻*": - case "ℤ*⁻": - return Domain.ZME - break; - case "ZE": - case "Z*": - case "ℤ*": - return Domain.ZE - break; - default: - return new EmptySet() - break; - } - } -} +var Domain = Dom.Domain +var EmptySet = Dom.EmptySet +var Range = Dom.Range +var SpecialDomain = Dom.SpecialDomain +var DomainSet = Dom.DomainSet +var UnionDomain = Dom.UnionDomain +var IntersectionDomain = Dom.IntersectionDomain +var MinusDomain = Dom.MinusDomain -class EmptySet extends Domain { - - includes(x) { return false } - - toString() { return "∅" } - - union(domain) { return domain } - - intersection(domain) { return this } - - static import(frm) { return new EmptySet() } -} - -class Range extends Domain { - constructor(begin, end, openBegin, openEnd) { - super() - if(typeof begin == 'number' || typeof begin == 'string') begin = new Expression(begin.toString()) - this.begin = begin - if(typeof end == 'number' || typeof end == 'string') end = new Expression(end.toString()) - this.end = end - this.openBegin = openBegin - this.openEnd = openEnd - this.displayName = (openBegin ? "]" : "[") + begin.toString() + ";" + end.toString() + (openEnd ? "[" : "]") - } - - includes(x) { - if(typeof x == 'string') x = executeExpression(x) - return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) && - ((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute())) - } - - toString() { - return this.displayName - } - - union(domain) { - if(domain instanceof EmptySet) return this - if(domain instanceof DomainSet) return domain.union(this) - if(domain instanceof UnionDomain) return domain.union(this) - if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) - if(domain instanceof MinusDomain) return new UnionDomain(this, domain) - if(domain instanceof Range) return new UnionDomain(this, domain) - } - - intersection(domain) { - if(domain instanceof EmptySet) return domain - if(domain instanceof DomainSet) return domain.intersection(this) - if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof IntersectionDomain) return domain.intersection(this) - if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) - if(domain instanceof Range) return new IntersectionDomain(this, domain) - } - - static import(frm) { - var openBegin = frm.trim().charAt(0) == "]" - var openEnd = frm.trim().charAt(frm.length -1) == "[" - var [begin, end] = frm.substr(1, frm.length-2).split(";") - return new Range(begin.trim(), end.trim(), openBegin, openEnd) - } -} - -class SpecialDomain extends Domain { - // For special domains (N, Z...) - // isValidExpr is function returning true when number is in domain - // false when it isn't. - // nextValue provides the next positive value in the domain - // after the one given. - constructor(displayName, isValid, next = x => true, previous = x => true, - moveSupported = true) { - super() - this.displayName = displayName - this.isValid = isValid - this.nextValue = next - this.prevValue = previous - this.moveSupported = moveSupported - } - - includes(x) { - if(typeof x == 'string') x = executeExpression(x) - return this.isValid(x) - } - - next(x) { - if(typeof x == 'string') x = executeExpression(x) - return this.nextValue(x) - } - - previous(x) { - if(typeof x == 'string') x = executeExpression(x) - return this.prevValue(x) - } - - toString() { - return this.displayName - } - - union(domain) { - if(domain instanceof EmptySet) return this - if(domain instanceof DomainSet) return domain.union(this) - if(domain instanceof UnionDomain) return new UnionDomain(this, domain) - if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) - if(domain instanceof MinusDomain) return new UnionDomain(this, domain) - if(domain instanceof Range) return new UnionDomain(this, domain) - } - - intersection(domain) { - if(domain instanceof EmptySet) return domain - if(domain instanceof DomainSet) return domain.intersection(this) - if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) - if(domain instanceof Range) return new IntersectionDomain(this, domain) - } -} - - -class DomainSet extends SpecialDomain { - constructor(values) { - super('', x => true, x => x, true) - var newVals = {} - this.executedValues = [] - for(var value of values) { - var expr = new Expression(value.toString()) - var ex = expr.execute() - newVals[ex] = expr - this.executedValues.push(ex) - } - this.executedValues.sort((a,b) => a-b) - this.values = this.executedValues.map(val => newVals[val]) - } - - includes(x) { - if(typeof x == 'string') x = executeExpression(x) - for(var value of this.values) - if(x == value.execute()) return true - return false - } - - next(x) { - if(typeof x == 'string') x = executeExpression(x) - if(x < this.executedValues[0]) return this.executedValues[0] - for(var i = 1; i < this.values.length; i++) { - var prevValue = this.executedValues[i-1] - var value = this.executedValues[i] - if(x >= prevValue && x < value) return value - } - return null - } - - previous(x) { - if(typeof x == 'string') x = executeExpression(x) - if(x > this.executedValues[this.executedValues.length-1]) - return this.executedValues[this.executedValues.length-1] - for(var i = 1; i < this.values.length; i++) { - var prevValue = this.executedValues[i-1] - var value = this.executedValues[i] - if(x > prevValue && x <= value) return prevValue - } - return null - } - - toString() { - return "{" + this.values.join(";") + "}" - } - - union(domain) { - if(domain instanceof EmptySet) return this - if(domain instanceof DomainSet) { - var newValues = [] - var values = this.values.concat(domain.values).filter(function(val){ - newValues.push(val.execute()) - return newValues.indexOf(val.execute()) == newValues.length - 1 - }) - return new DomainSet(values) - } - var notIncludedValues = [] - for(var value in this.values) { - var value = this.executedValues[i] - if(domain instanceof Range) { - if(domain.begin.execute() == value && domain.openBegin) { - domain.openBegin = false - } - if(domain.end.execute() == value && domain.openEnd) { - domain.openEnd = false - } - } - if(!domain.includes(value)) - notIncludedValues.push(this.values[i].toEditableString()) - } - if(notIncludedValues.length == 0) return domain - return new UnionDomain(domain, new DomainSet(notIncludedValues)) - } - - intersection(domain) { - if(domain instanceof EmptySet) return domain - if(domain instanceof DomainSet) { - var domValues = domain.values.map(expr => expr.execute()) - this.values = this.values.filter(function(val){ - return domValues.indexOf(val.execute()) >= 0 - }) - return this - } - var includedValues = [] - for(var i in this.values) { - var value = this.executedValues[i] - if(domain instanceof Range) { - if(domain.begin.execute() == value && !domain.openBegin) { - domain.openBegin = false - } - if(domain.end.execute() == value && !domain.openEnd) { - domain.openEnd = false - } - } - if(domain.includes(value)) - includedValues.push(this.values[i].toEditableString()) - } - if(includedValues.length == 0) return new EmptySet() - if(includedValues.length == this.values.length) return this - return new IntersectionDomain(domain, new DomainSet(includedValues)) - } - - static import(frm) { - return new DomainSet(frm.substr(1, frm.length-2).split(";")) - } -} - -class UnionDomain extends Domain { - constructor(dom1, dom2) { - super() - this.dom1 = dom1 - this.dom2 = dom2 - } - - includes(x) { - return this.dom1.includes(x) || this.dom2.includes(x) - } - - toString() { - return this.dom1.toString() + " ∪ " + this.dom2.toString() - } - - union(domain) { - if(domain instanceof EmptySet) return this - if(domain instanceof DomainSet) return domain.union(this) - if(domain instanceof Range) return domain.union(this) - if(domain instanceof UnionDomain) return new UnionDomain(this, domain) - if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) - if(domain instanceof MinusDomain) return new MinusDomain(this, domain) - } - - intersection(domain) { - if(domain instanceof EmptySet) return domain - if(domain instanceof DomainSet) return domain.intersection(this) - if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof IntersectionDomain) return this.dom1.intersection(domain.dom1).intersection(this.dom2).intersection(domain.dom2) - if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) - } - - static import(frm) { - var domains = frm.trim().split("∪") - if(domains.length == 1) domains = frm.trim().split("U") // Fallback - var dom1 = parseDomain(domains.pop()) - var dom2 = parseDomain(domains.join('∪')) - return dom1.union(dom2) - } -} - -class IntersectionDomain extends Domain { - constructor(dom1, dom2) { - super() - this.dom1 = dom1 - this.dom2 = dom2 - } - - includes(x) { - return this.dom1.includes(x) && this.dom2.includes(x) - } - - toString() { - return this.dom1.toString() + " ∩ " + this.dom2.toString() - } - - union(domain) { - if(domain instanceof EmptySet) return this - if(domain instanceof DomainSet) return domain.union(this) - if(domain instanceof Range) return domain.union(this) - if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2) - if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) - if(domain instanceof MinusDomain) return new MinusDomain(this, domain) - } - - intersection(domain) { - if(domain instanceof EmptySet) return domain - if(domain instanceof DomainSet) return domain.intersection(this) - if(domain instanceof UnionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) - if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) - } - - static import(frm) { - var domains = frm.trim().split("∩") - var dom1 = parseDomain(domains.pop()) - var dom2 = parseDomain(domains.join('∩')) - return dom1.intersection(dom2) - } -} - -class MinusDomain extends Domain { - constructor(dom1, dom2) { - super() - this.dom1 = dom1 - this.dom2 = dom2 - } - - includes(x) { - return this.dom1.includes(x) && !this.dom2.includes(x) - } - - toString() { - return this.dom1.toString() + "∖" + this.dom2.toString() - } - - static import(frm) { - var domains = frm.trim().split("∖") - if(domains.length == 1) domains = frm.trim().split("\\") // Fallback - var dom1 = parseDomain(domains.shift()) - var dom2 = parseDomain(domains.join('∪')) - return new MinusDomain(dom1, dom2) - } -} - -Domain.RE = new MinusDomain("R", "{0}") -Domain.RE.displayName = "ℝ*" - -Domain.R = new Range(-Infinity,Infinity,true,true) -Domain.R.displayName = "ℝ" -Domain.RP = new Range(0,Infinity,true,false) -Domain.RP.displayName = "ℝ⁺" -Domain.RM = new Range(-Infinity,0,true,false) -Domain.RM.displayName = "ℝ⁻" -Domain.RPE = new Range(0,Infinity,true,true) -Domain.RPE.displayName = "ℝ⁺*" -Domain.RME = new Range(-Infinity,0,true,true) -Domain.RME.displayName = "ℝ⁻*" -Domain.N = new SpecialDomain('ℕ', x => x%1==0 && x >= 0, - x => Math.max(Math.floor(x)+1, 0), - x => Math.max(Math.ceil(x)-1, 0)) -Domain.NE = new SpecialDomain('ℕ*', x => x%1==0 && x > 0, - x => Math.max(Math.floor(x)+1, 1), - x => Math.max(Math.ceil(x)-1, 1)) -Domain.Z = new SpecialDomain('ℤ', x => x%1==0, x => Math.floor(x)+1, x => Math.ceil(x)-1) -Domain.ZE = new SpecialDomain('ℤ*', x => x%1==0 && x != 0, - x => Math.floor(x)+1 == 0 ? Math.floor(x)+2 : Math.floor(x)+1, - x => Math.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1) -Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1==0 && x <= 0, - x => Math.min(Math.floor(x)+1, 0), - x => Math.min(Math.ceil(x)-1, 0)) -Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1==0 && x < 0, - x => Math.min(Math.floor(x)+1, -1), - x => Math.min(Math.ceil(x)-1, -1)) -Domain.NLog = new SpecialDomain('ℕˡᵒᵍ', - x => x/Math.pow(10, x.toString().length-1) % 1 == 0 && x > 0, - function(x) { - var x10pow = Math.pow(10, x.toString().length-1) - return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow) - }, - function(x) { - var x10pow = Math.pow(10, x.toString().length-1) - return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow) - }) - -var refedDomains = [] - -function parseDomain(domain) { - if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) - var domStr - while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { - var dom = parseDomainSimple(domStr[1].trim()); - domain = domain.replace(domStr[0], 'D' + refedDomains.length) - refedDomains.push(dom) - } - return parseDomainSimple(domain) -} - -function parseDomainSimple(domain) { - domain = domain.trim() - if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain) - if(domain.includes("∩")) return IntersectionDomain.import(domain) - if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain) - if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain) - if(domain.includes("]") || domain.includes("[")) return Range.import(domain) - if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str))) - return Domain.import(domain) - if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))] - return new EmptySet() -} +var parseDomain = Dom.parseDomain +var parseDomainSimple = Dom.parseDomainSimple