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