This commit is contained in:
parent
8f1bc652b4
commit
8b01a8f0e8
6 changed files with 828 additions and 634 deletions
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.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()
|
||||||
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.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()
|
||||||
|
}
|
|
@ -16,8 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.pragma library
|
||||||
|
|
||||||
.import "expr-eval.js" as ExprEval
|
.import "../expr-eval.js" as ExprEval
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts element within parenthesis.
|
* Puts element within parenthesis.
|
||||||
|
@ -38,7 +39,7 @@ function par(elem) {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function parif(elem, contents) {
|
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) {
|
if (typeof item.value === 'number' && item.value < 0) {
|
||||||
nstack.push(par(item.value));
|
nstack.push(par(item.value));
|
||||||
} else if (Array.isArray(item.value)) {
|
} else if (Array.isArray(item.value)) {
|
||||||
nstack.push('[' + item.value.map(escapeValue).join(', ') + ']');
|
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
|
||||||
} else {
|
} else {
|
||||||
nstack.push(escapeValue(item.value));
|
nstack.push(ExprEval.escapeValue(item.value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ExprEval.IOP2:
|
case ExprEval.IOP2:
|
||||||
|
@ -114,7 +115,10 @@ function expressionToLatex(tokens) {
|
||||||
break;
|
break;
|
||||||
case ExprEval.IVAR:
|
case ExprEval.IVAR:
|
||||||
case ExprEval.IVARNAME:
|
case ExprEval.IVARNAME:
|
||||||
nstack.push(item.value);
|
if(item.value == "pi")
|
||||||
|
nstack.push("π")
|
||||||
|
else
|
||||||
|
nstack.push(item.value);
|
||||||
break;
|
break;
|
||||||
case ExprEval.IOP1: // Unary operator
|
case ExprEval.IOP1: // Unary operator
|
||||||
n1 = nstack.pop();
|
n1 = nstack.pop();
|
||||||
|
@ -139,7 +143,21 @@ function expressionToLatex(tokens) {
|
||||||
args.unshift(nstack.pop());
|
args.unshift(nstack.pop());
|
||||||
}
|
}
|
||||||
f = 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;
|
break;
|
||||||
case ExprEval.IFUNDEF:
|
case ExprEval.IFUNDEF:
|
||||||
nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
||||||
|
@ -173,5 +191,5 @@ function expressionToLatex(tokens) {
|
||||||
nstack = [ nstack.join(';') ];
|
nstack = [ nstack.join(';') ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Utils.makeExpressionReadable(String(nstack[0]));
|
return String(nstack[0]);
|
||||||
}
|
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,635 +18,24 @@
|
||||||
|
|
||||||
.pragma library
|
.pragma library
|
||||||
|
|
||||||
.import "expr-eval.js" as ExprEval
|
.import "math/expression.js" as Expr
|
||||||
.import "utils.js" as Utils
|
.import "math/sequence.js" as Seq
|
||||||
|
|
||||||
|
.import "math/domain.js" as Dom
|
||||||
|
|
||||||
|
var Expression = Expr.Expression
|
||||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy
|
var executeExpression = Expr.executeExpression
|
||||||
"pi": Math.PI,
|
var Sequence = Seq.Sequence
|
||||||
"π": 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Domains
|
// Domains
|
||||||
class Domain {
|
var Domain = Dom.Domain
|
||||||
constructor() {}
|
var EmptySet = Dom.EmptySet
|
||||||
|
var Range = Dom.Range
|
||||||
includes(x) { return false }
|
var SpecialDomain = Dom.SpecialDomain
|
||||||
|
var DomainSet = Dom.DomainSet
|
||||||
toString() { return '???' }
|
var UnionDomain = Dom.UnionDomain
|
||||||
|
var IntersectionDomain = Dom.IntersectionDomain
|
||||||
union(domain) { return domain }
|
var MinusDomain = Dom.MinusDomain
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmptySet extends Domain {
|
var parseDomain = Dom.parseDomain
|
||||||
|
var parseDomainSimple = Dom.parseDomainSimple
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue