Complex domain parsing
This commit is contained in:
parent
550b524a27
commit
29cb6a22a1
1 changed files with 214 additions and 60 deletions
|
@ -55,6 +55,10 @@ class Expression {
|
||||||
return Utils.makeExpressionReadable(expr.toString())
|
return Utils.makeExpressionReadable(expr.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duplicate() {
|
||||||
|
return new Expression(this.toEditableString())
|
||||||
|
}
|
||||||
|
|
||||||
toEditableString() {
|
toEditableString() {
|
||||||
return this.calc.toString()
|
return this.calc.toString()
|
||||||
}
|
}
|
||||||
|
@ -74,39 +78,20 @@ function executeExpression(expr){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domains
|
// Domains
|
||||||
|
class Domain {
|
||||||
class EmptySet {
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
includes(x) { return false }
|
includes(x) { return false }
|
||||||
|
|
||||||
toString() { return "∅" }
|
toString() { return '???' }
|
||||||
|
|
||||||
|
union(domain) { return domain }
|
||||||
|
|
||||||
|
intersection(domain) { return this }
|
||||||
|
|
||||||
static import(frm) { return new EmptySet() }
|
static import(frm) { return new EmptySet() }
|
||||||
}
|
|
||||||
|
|
||||||
class Domain {
|
|
||||||
constructor(begin, end, openBegin, openEnd) {
|
|
||||||
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) {
|
static import(frm) {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
static importFrom(frm) {
|
|
||||||
switch(frm.trim().toUpperCase()) {
|
switch(frm.trim().toUpperCase()) {
|
||||||
case "R":
|
case "R":
|
||||||
case "ℝ":
|
case "ℝ":
|
||||||
|
@ -144,27 +129,86 @@ class Domain {
|
||||||
return Domain.RME
|
return Domain.RME
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
var openBegin = frm.trim().charAt(0) == "]"
|
return EmptySet()
|
||||||
var openEnd = frm.trim().charAt(frm.length -1) == "["
|
|
||||||
var [begin, end] = frm.substr(1, frm.length-2).split(";")
|
|
||||||
return new Domain(begin.trim(), end.trim(), openBegin, openEnd)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Domain.R = new Domain(-Infinity,Infinity,true,true)
|
|
||||||
|
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 Interval 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 Interval) 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 Interval) 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 Interval(begin.trim(), end.trim(), openBegin, openEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Domain.R = new Interval(-Infinity,Infinity,true,true)
|
||||||
Domain.R.displayName = "ℝ"
|
Domain.R.displayName = "ℝ"
|
||||||
Domain.RP = new Domain(0,Infinity,true,false)
|
Domain.RP = new Interval(0,Infinity,true,false)
|
||||||
Domain.RP.displayName = "ℝ⁺"
|
Domain.RP.displayName = "ℝ⁺"
|
||||||
Domain.RM = new Domain(-Infinity,0,true,false)
|
Domain.RM = new Interval(-Infinity,0,true,false)
|
||||||
Domain.RM.displayName = "ℝ⁻"
|
Domain.RM.displayName = "ℝ⁻"
|
||||||
Domain.RPE = new Domain(0,Infinity,true,true)
|
Domain.RPE = new Interval(0,Infinity,true,true)
|
||||||
Domain.RPE.displayName = "ℝ⁺*"
|
Domain.RPE.displayName = "ℝ⁺*"
|
||||||
Domain.RME = new Domain(-Infinity,0,true,true)
|
Domain.RME = new Interval(-Infinity,0,true,true)
|
||||||
Domain.RME.displayName = "ℝ⁻*"
|
Domain.RME.displayName = "ℝ⁻*"
|
||||||
|
|
||||||
class DomainSet {
|
class DomainSet extends Domain {
|
||||||
constructor(values) {
|
constructor(values) {
|
||||||
|
super()
|
||||||
var newVals = []
|
var newVals = []
|
||||||
values.forEach(function(value){
|
values.forEach(function(value){
|
||||||
newVals.push(new Expression(value.toString()))
|
newVals.push(new Expression(value.toString()))
|
||||||
|
@ -174,27 +218,79 @@ class DomainSet {
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(typeof x == 'string') x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
var found = false
|
for(var i = 0; i < this.values.length; i++)
|
||||||
this.values.forEach(function(value){
|
if(x == this.values[i].execute()) return true
|
||||||
if(x == value.execute()) {
|
return false
|
||||||
found = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return "{" + this.values.join(";") + "}"
|
return "{" + this.values.join(";") + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
static importFrom(frm) {
|
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 i = 0; i < this.values.length; i++) {
|
||||||
|
var value = this.values[i].execute()
|
||||||
|
if(domain instanceof Interval) {
|
||||||
|
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 = 0; i < this.values.length; i++) {
|
||||||
|
var value = this.values[i].execute()
|
||||||
|
if(domain instanceof Interval) {
|
||||||
|
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(";"))
|
return new DomainSet(frm.substr(1, frm.length-2).split(";"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnionDomain {
|
class UnionDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
this.dom2 = dom2
|
this.dom2 = dom2
|
||||||
}
|
}
|
||||||
|
@ -207,15 +303,36 @@ class UnionDomain {
|
||||||
return this.dom1.toString() + " ∪ " + this.dom2.toString()
|
return this.dom1.toString() + " ∪ " + this.dom2.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
static importFrom(frm) {
|
union(domain) {
|
||||||
|
if(domain instanceof EmptySet) return this
|
||||||
|
if(domain instanceof DomainSet) return domain.union(this)
|
||||||
|
if(domain instanceof Interval) 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("∪")
|
var domains = frm.trim().split("∪")
|
||||||
if(domains.length == 1) domains = frm.trim().split("U") // Fallback
|
if(domains.length == 1) domains = frm.trim().split("U") // Fallback
|
||||||
return new UnionDomain(parseDomain(domains[0].trim()), parseDomain(domains[1].trim()))
|
var dom1 = parseDomain(domains.pop())
|
||||||
|
var dom2 = parseDomain(domains.join('∪'))
|
||||||
|
console.log('Union', dom1, dom2)
|
||||||
|
return dom1.union(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntersectionDomain {
|
class IntersectionDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
this.dom2 = dom2
|
this.dom2 = dom2
|
||||||
}
|
}
|
||||||
|
@ -228,14 +345,34 @@ class IntersectionDomain {
|
||||||
return this.dom1.toString() + " ∩ " + this.dom2.toString()
|
return this.dom1.toString() + " ∩ " + this.dom2.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
static importFrom(frm) {
|
union(domain) {
|
||||||
|
if(domain instanceof EmptySet) return this
|
||||||
|
if(domain instanceof DomainSet) return domain.union(this)
|
||||||
|
if(domain instanceof Interval) 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 domains = frm.trim().split("∩")
|
||||||
return new IntersectionDomain(parseDomain(domains[0].trim()), parseDomain(domains[1].trim()))
|
var dom1 = parseDomain(domains.pop())
|
||||||
|
var dom2 = parseDomain(domains.join('∪'))
|
||||||
|
return dom1.intersection(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MinusDomain {
|
class MinusDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
this.dom2 = dom2
|
this.dom2 = dom2
|
||||||
}
|
}
|
||||||
|
@ -248,23 +385,40 @@ class MinusDomain {
|
||||||
return this.dom1.toString() + "∖" + this.dom2.toString()
|
return this.dom1.toString() + "∖" + this.dom2.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
static importFrom(frm) {
|
static import(frm) {
|
||||||
var domains = frm.trim().split("∖")
|
var domains = frm.trim().split("∖")
|
||||||
if(domains.length == 1) domains = frm.trim().split("\\") // Fallback
|
if(domains.length == 1) domains = frm.trim().split("\\") // Fallback
|
||||||
return new MinusDomain(parseDomain(domains[0].trim()), parseDomain(domains[1].trim()))
|
var dom1 = parseDomain(domains.pop())
|
||||||
|
var dom2 = parseDomain(domains.join('∪'))
|
||||||
|
return new MinusDomain(dom1, dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Domain.RE = new MinusDomain("R", "{0}")
|
Domain.RE = new MinusDomain("R", "{0}")
|
||||||
Domain.RE.displayName = "ℝ*"
|
Domain.RE.displayName = "ℝ*"
|
||||||
|
|
||||||
|
var refedDomains = []
|
||||||
|
|
||||||
function parseDomain(domain) {
|
function parseDomain(domain) {
|
||||||
if(domain.indexOf("U") >= 0 || domain.indexOf("∪") >= 0) return UnionDomain.importFrom(domain)
|
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
|
||||||
if(domain.indexOf("∩") >= 0) return IntersectionDomain.importFrom(domain)
|
var domStr
|
||||||
if(domain.indexOf("∖") >= 0 || domain.indexOf("\\") >= 0) return MinusDomain.importFrom(domain)
|
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
|
||||||
if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.importFrom(domain)
|
var dom = parseDomainSimple(domStr[1]);
|
||||||
if(domain.indexOf("]") >= 0 || domain.indexOf("]") >= 0) return Domain.importFrom(domain)
|
console.log(domain, domStr[0])
|
||||||
if(domain.toUpperCase().indexOf("R") >= 0 || domain.indexOf("ℝ") >= 0) return Domain.importFrom(domain)
|
domain = domain.replace(domStr[0], 'D' + refedDomains.length)
|
||||||
|
refedDomains.push(dom)
|
||||||
|
}
|
||||||
|
return parseDomainSimple(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDomainSimple(domain) {
|
||||||
|
console.log('Parsing domain', domain, typeof domain)
|
||||||
|
if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))]
|
||||||
|
if(domain.indexOf("U") >= 0 || domain.indexOf("∪") >= 0) return UnionDomain.import(domain)
|
||||||
|
if(domain.indexOf("∩") >= 0) return IntersectionDomain.import(domain)
|
||||||
|
if(domain.indexOf("∖") >= 0 || domain.indexOf("\\") >= 0) return MinusDomain.import(domain)
|
||||||
|
if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain)
|
||||||
|
if(domain.indexOf("]") >= 0 || domain.indexOf("[") >= 0) return Interval.import(domain)
|
||||||
|
if(domain.toUpperCase().indexOf("R") >= 0 || domain.indexOf("ℝ") >= 0) return Domain.import(domain)
|
||||||
return new EmptySet()
|
return new EmptySet()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue