sellquiz
Version:
An open source domain-specific language for online assessment
930 lines (879 loc) • 35.9 kB
text/typescript
/******************************************************************************
* SELL - SIMPLE E-LEARNING LANGUAGE *
* *
* Copyright (c) 2019-2021 TH Köln *
* Author: Andreas Schwenk, contact@compiler-construction.com *
* *
* Partly funded by: Digitale Hochschule NRW *
* https://www.dh.nrw/kooperationen/hm4mint.nrw-31 *
* *
* GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 *
* *
* This library is licensed as described in LICENSE, which you should have *
* received as part of this distribution. *
* *
* This software is distributed on "AS IS" basis, WITHOUT WARRENTY OF ANY *
* KIND, either impressed or implied. *
******************************************************************************/
// this file implements math functions that are NOT provided by math.js
import * as math from 'mathjs';
export class SellSymTermElement {
type : string; // "var", "const", "uniop", "binop", "fct1", "fct2"; TUDO: enum!
v : any;
deriv : SellSymTermElement;
constructor(type : string, v : any) {
this.type = type;
this.v = v;
this.deriv = null;
}
}
export class SellSymTerm {
symbolIDs : Array<string>;
stack : Array<SellSymTermElement>;
state : string; // TODO: enum
contains_forbidden_ode_subtree : boolean;
constructor(symbolIDs=[]) {
this.symbolIDs = symbolIDs;
this.stack = [];
this.state = ""; // e.g. "syntax-error"
this.contains_forbidden_ode_subtree = false;
}
clear() {
this.stack = [];
}
importTerm(str : string) : boolean {
let n = math.parse(str);
return this.importMathJsTermJsRecursively(n);
}
importMathJsTermJsRecursively(node) {
// TODO: this is incomplete...
switch (node.type) {
case "ConstantNode":
this.pushConstant(node.value);
break;
case "SymbolNode":
this.pushVariable(node.name);
break;
case "OperatorNode":
case "ParenthesisNode":
while (node.type == "ParenthesisNode")
node = node.content;
if (node.fn == "unaryMinus") {
if (this.importMathJsTermJsRecursively(node.args[0]) == false)
return false;
this.pushUnaryOperation("-");
}
else if (node.op == "+" || node.op == "-" || node.op == "*" || node.op == "/" || node.op == "^") {
if (this.importMathJsTermJsRecursively(node.args[0]) == false)
return false;
if (this.importMathJsTermJsRecursively(node.args[1]) == false)
return false;
this.pushBinaryOperation(node.op);
} else {
console.log("warning: SellSymTerm::importMathJsTermJsRecursively(..): unknown/unimplemented operation " + node.op);
return false;
}
break;
case "FunctionNode":
if (node.name == "exp" || node.name == "sin" || node.name == "cos" || node.name == "sqrt") {
if (this.importMathJsTermJsRecursively(node.args[0]) == false)
return false;
this.pushUnaryFunction(node.name);
} else {
console.log("warning: SellSymTerm::importMathJsTermJsRecursively(..): unknown/unimplemented function " + node.name);
return false;
}
break;
default:
console.log("warning: SellSymTerm::importMathJsTermJsRecursively(..): unknown/unimplemented node type " + node.type);
return false;
}
return true;
}
pushVariable(id) {
this.stack.push(new SellSymTermElement("var", id));
}
pushOdeFunction(f) {
this.stack.push(new SellSymTermElement("ode_fun", f));
}
pushConstant(v) {
v = parseFloat(v)
this.stack.push(new SellSymTermElement("const", v));
}
pushSymbolicTerm(st) {
if (st.stack.length == 0)
this.stack.push(new SellSymTermElement("const", 0));
else
this.stack.push(st.stack[0]);
}
pushUnaryFunction(name) {
let param = this.stack.pop();
this.stack.push(new SellSymTermElement("fct1", [name, param]));
}
pushBinaryFunction(name) {
let param2 = this.stack.pop();
let param1 = this.stack.pop();
this.stack.push(new SellSymTermElement("fct2", [name, param1, param2]));
}
pushDiff() {
// TODO: must check, if following variables are "OK"...
let diffVar = this.stack.pop();
let diffFct = this.stack.pop();
let term = new SellSymTerm();
term.symbolIDs = this.symbolIDs; // TODO: copy?
term.stack.push(diffFct); // TODO: copy?
let diff = term.derivate(diffVar.v);
this.pushSymbolicTerm(diff);
}
pushUnaryOperation(type) {
let op = this.stack.pop();
this.stack.push(new SellSymTermElement("uniop", [type, op]))
}
pushBinaryOperation(type) {
type = type.replace("add", "+").replace("sub", "-").replace("mul", "*").replace("div", "/").replace("pow", "^");
let op2 = this.stack.pop();
let op1 = this.stack.pop();
if (op1.type == "const" && op2.type == "const") {
let res : any;
res = 0;
switch (type) {
case "+": res = op1.v + op2.v; break;
case "-": res = op1.v - op2.v; break;
case "*": res = op1.v * op2.v; break;
case "/": res = op1.v / op2.v; break;
case "^": res = math.pow(op1.v, op2.v); break;
default:
alert("unimplemented: SellSymTerm:pushBinaryOperation(..): operator " + type);
}
this.stack.push(new SellSymTermElement("const", res));
}
else {
this.stack.push(new SellSymTermElement("binop", [type, op1, op2]));
}
}
appendVariableSet(v, v_new) {
for (let i = 0; i < v_new.length; i++) {
let found = false;
for (let j = 0; j < v.length; j++) {
if (v_new[i] === v[j]) {
found = true;
break;
}
}
if (found == false) {
v.push(v_new[i]);
}
}
}
getVariables(element = null) {
let v = [], v1, v2;
if (element == null) {
element = this.stack[0];
}
switch (element.type) {
case "var":
//v.push(element.v);
this.appendVariableSet(v, [element.v]);
break;
case "const":
break;
case "uniop":
v1 = this.getVariables(element.v[1]);
this.appendVariableSet(v, v1);
break;
case "binop":
v1 = this.getVariables(element.v[1]);
this.appendVariableSet(v, v1);
v2 = this.getVariables(element.v[2]);
this.appendVariableSet(v, v2);
break;
case "ode_fun":
this.appendVariableSet(v, [element.v]);
//element.mathsymbol_ids // TODO: mathsymbol_ids relevant??
break;
case "fct1":
v1 = this.getVariables(element.v[1]);
this.appendVariableSet(v, v1);
break;
case "fct2":
v1 = this.getVariables(element.v[1]);
this.appendVariableSet(v, v1);
v2 = this.getVariables(element.v[2]);
this.appendVariableSet(v, v2);
break;
default:
alert("unimplemented: SellSymTerm:getVariables(..): " + element.type);
}
return v;
}
getOdeOrder(element = null) {
// TODO: does not work for PDE!!
let o = 0;
let root = false;
if (element == null) {
root = true;
element = this.stack[0];
}
switch (element.type) {
case "uniop":
o = math.max(o, this.getOdeOrder(element.v[1]));
break;
case "binop":
o = math.max(o, this.getOdeOrder(element.v[1]));
o = math.max(o, this.getOdeOrder(element.v[2]));
break;
case "fct2":
if (element.v[0] == "diff_ode")
o = math.max(o, 1);
else if (element.v[0] == "diff2_ode")
o = math.max(o, 2);
break;
}
return o;
}
optimizeOdeConstants(element = null) {
let root = false;
let e, e1, e2;
if (element == null) {
root = true;
element = this.stack[0];
}
switch (element.type) {
case "var":
break;
case "uniop":
this.optimizeOdeConstants(element.v[1]);
e = element.v[1];
if(e.type === "var" && e.v.startsWith("C")) {
element.type = "var";
element.v = e.v;
}
break;
case "binop":
this.optimizeOdeConstants(element.v[1]);
this.optimizeOdeConstants(element.v[2]);
e1 = element.v[1];
e2 = element.v[2];
if(e1.type === "var" && e1.v.startsWith("C") && e2.type === "const") {
element.type = "var";
element.v = e1.v;
}
else if(e2.type === "var" && e2.v.startsWith("C") && e1.type === "const") {
element.type = "var";
element.v = e2.v;
}
break;
case "fct1":
this.optimizeOdeConstants(element.v[1]);
e = element.v[1];
if(e.type === "var" && e.v.startsWith("C")) {
element.type = "var";
element.v = e.v;
}
break;
}
}
searchForForbiddenODESecondOrderSubterms(element = null) {
let v = [], v1, v2;
if (element == null) {
element = this.stack[0];
}
switch (element.type) {
case "var":
//v.push(element.v);
this.appendVariableSet(v, [element.v]);
break;
case "const":
break;
case "uniop":
v1 = this.searchForForbiddenODESecondOrderSubterms(element.v[1]);
this.appendVariableSet(v, v1);
break;
case "binop":
v1 = this.searchForForbiddenODESecondOrderSubterms(element.v[1]);
this.appendVariableSet(v, v1);
v2 = this.searchForForbiddenODESecondOrderSubterms(element.v[2]);
this.appendVariableSet(v, v2);
break;
case "ode_fun":
this.appendVariableSet(v, [element.v]);
//element.mathsymbol_ids // TODO: mathsymbol_ids relevant??
break;
case "fct1":
v1 = this.searchForForbiddenODESecondOrderSubterms(element.v[1]);
this.appendVariableSet(v, v1);
break;
case "fct2":
v1 = this.searchForForbiddenODESecondOrderSubterms(element.v[1]);
this.appendVariableSet(v, v1);
v2 = this.searchForForbiddenODESecondOrderSubterms(element.v[2]);
this.appendVariableSet(v, v2);
break;
default:
alert("unimplemented: SellSymTerm:searchForForbiddenODESecondOrderSubterms(..): " + element.type);
}
console.log("TEST:");
console.log(v);
if(v.length == 2) {
this.contains_forbidden_ode_subtree = true;
for(let i=0; i<v.length; i++) {
if(v[i].startsWith("C") == false)
this.contains_forbidden_ode_subtree = false;
}
}
return v;
}
getOperator(element) {
let op = "";
if (element.type == "uniop" || element.type == "binop")
op = element.v[0];
return op;
}
getOperatorPrecedence(op) {
let p = 0;
switch (op) {
case "+": p = 10; break;
case "-": p = 10; break; // TODO: this was 11
case "*": p = 20; break;
case "/": p = 20; break; // TODO: this was 21
case "^": p = 30; break;
case "": p = 99; break
default:
alert("unimplemented: SellSymTerm:getOperatorPrecedence(..): op=" + op);
}
return p;
}
toString(element = null) : string {
let s = "";
let root = false;
if (element == null) {
root = true;
element = this.stack[0];
}
let name, arg, arg1, arg2;
switch (element.type) {
case "var":
s = element.v;
break;
case "const":
s = element.v.toString();
if (math.abs(element.v - 3.141592653589793) < 1e-12)
s = "pi";
break;
case "uniop":
s = element.v[0] + this.toString(element.v[1]);
if (element.v[1].type == 'uniop')
s = "(" + s + ")";
break;
case "ode_fun":
/*if(element.v[0].type === "function")
s = element.value.toString();
else*/
s = element.v.id + "(" + element.v.mathsymbol_ids[0] + ")";
break;
case "binop":
let c1 = this.toString(element.v[1]);
let c2 = this.toString(element.v[2]);
let op = element.v[0];
let op_precedence = this.getOperatorPrecedence(op);
let op1 = this.getOperator(element.v[1]);
let op1_precedence = this.getOperatorPrecedence(op1);
let op2 = this.getOperator(element.v[2]);
let op2_precedence = this.getOperatorPrecedence(op2);
if (op1_precedence < op_precedence)
c1 = "(" + c1 + ")";
else if (op === "/")
c1 = "(" + c1 + ")";
if (op2_precedence < op_precedence /*|| op2.type == 'uniop'*/)
c2 = "(" + c2 + ")";
else if (c2.startsWith("-"))
c2 = "(" + c2 + ")";
else if (op === "-" || op === "/") {
if(element.v[2].type !== "const")
c2 = "(" + c2 + ")";
}
s = c1 + op + c2;
break;
case "fct1":
name = element.v[0];
arg = this.toString(element.v[1]);
if (name === "exp" && arg.length < 5)
s = "e^(" + arg + ")";
else
s = name + "(" + arg + ")";
break;
case "fct2":
name = element.v[0];
if (name === "diff_ode") {
// TODO: should now work, since we have "diff2_ode"
/*// TODO: this is yet only statically implemented for second order, as well as nested differentials with the same derivative variable
//alert(element.v.length)
if(element.v[1].v.type == "function")
s = element.v[1].v.value.stack[0].v[1].v.id + "''(" + element.v[2].v + ")";
else*/
s = element.v[1].v.id + "'(" + element.v[2].v + ")";
}
else if (name === "diff2_ode") {
s = element.v[1].v.id + "''(" + element.v[2].v + ")";
}
else {
arg1 = this.toString(element.v[1]);
arg2 = this.toString(element.v[2]);
s = name + "(" + arg1 + ", " + arg2 + ")";
}
break;
default:
alert("unimplemented: SellSymTerm:toString(..): " + element.type);
}
if (root && s.startsWith("(")) {
s = s; // s.substr(1, s.length-2); TODO
}
return s;
}
derivate(variable/*id*/, element = null) {
let u, v, op, name, p1;
let isRoot = element == null;
if (isRoot)
element = this.stack[0];
switch (element.type) {
case "var":
element.deriv = new SellSymTermElement("const", element.v == variable ? 1 : 0);
break;
case "const":
element.deriv = new SellSymTermElement("const", 0);
break;
case "uniop":
u = element.v[1];
this.derivate(variable, u);
op = element.v[0];
switch (op) {
case "-":
// f = - u
// f' = - u'
element.deriv =
new SellSymTermElement("uniop", [op, u.deriv]);
break;
default:
alert("unimplemented: SellSymTerm:derivate(..): uniop: " + op);
}
break;
case "binop":
u = element.v[1];
this.derivate(variable, u);
v = element.v[2];
this.derivate(variable, v);
op = element.v[0];
switch (op) {
case "+":
case "-":
// f = u + v
// f' = u' + v'
element.deriv =
new SellSymTermElement("binop", [op, u.deriv, v.deriv]);
break;
case "*":
// f = u * v
// f' = u'*v + v'*u
element.deriv =
new SellSymTermElement("binop", ["+",
new SellSymTermElement("binop", ["*", u.deriv, v]),
new SellSymTermElement("binop", ["*", v.deriv, u])
]);
break;
case "/":
// f = u / v
// f' = (u'*v - v'*u) / v^2
element.deriv =
new SellSymTermElement("binop", ["/",
new SellSymTermElement("binop", ["-",
new SellSymTermElement("binop", ["*", u.deriv, v]),
new SellSymTermElement("binop", ["*", v.deriv, u])
]),
new SellSymTermElement("binop", ["*", v, v])
]);
break;
case "^":
// f = u^v (assuming v is const)
// f' = u' * v * u^(v-1)
if (v.type !== "const")
alert("unimplemented: SellSymTerm:derivate(..): derivation of u^v with v != const")
element.deriv =
new SellSymTermElement("binop", ["*",
u.deriv,
new SellSymTermElement("binop", ["*",
v,
new SellSymTermElement("binop", ["^",
u,
new SellSymTermElement("binop", ["-",
v,
new SellSymTermElement("const", 1)
])
])
])
]);
break;
default:
alert("unimplemented: SellSymTerm:derivate(..): binop: " + op);
}
break;
case "fct1":
name = element.v[0];
p1 = element.v[1];
this.derivate(variable, p1);
switch (name) {
case "sin":
// f = sin(p1)
// f' = p1' * cos(p1)
element.deriv =
new SellSymTermElement("binop", ["*",
p1.deriv,
new SellSymTermElement("fct1", ["cos", p1])
]);
break;
case "cos":
// f = cos(p1)
// f' = - p1' * sin(p1)
element.deriv =
new SellSymTermElement("uniop", ["-",
new SellSymTermElement("binop", ["*",
p1.deriv,
new SellSymTermElement("fct1", ["sin", p1])
])
]);
break;
case "exp":
// f = exp(p1)
// f' = p1' * exp(p1)
element.deriv =
new SellSymTermElement("binop", ["*",
p1.deriv,
new SellSymTermElement("fct1", ["exp", p1])
]);
break;
case "sqrt":
// f = sqrt(p1) = p1^(0.5)
// f' = p1' / (2*sqrt(p1))
element.deriv =
new SellSymTermElement("binop", ["/",
p1.deriv,
new SellSymTermElement("binop", ["*",
new SellSymTermElement("const", 2),
new SellSymTermElement("fct1", ["sqrt", p1])
])
]);
break;
default:
alert("unimplemented: SellSymTerm:derivate(..): fct1: " + name);
}
break;
default:
alert("unimplemented: SellSymTerm:derivate(..): " + element.type);
}
if (isRoot) {
let t = new SellSymTerm();
t.symbolIDs = this.symbolIDs;
t.stack = [this.stack[0].deriv];
t.optimize();
return t;
}
else
return null;
}
eval(var_values/*dict*/, element = null) {
let res : any, u, v, op, name, param, param1, param2;
res = 0;
if (element == null)
element = this.stack[0];
switch (element.type) {
case "var":
if (element.v in var_values) {
res = parseFloat(var_values[element.v]);
} else {
alert("SellSymTerm:eval(..): variable " + element.v + " has no value!");
return 0;
}
break;
case "const":
res = element.v;
break;
case "uniop":
u = this.eval(var_values, element.v[1]);
op = element.v[0];
switch (op) {
case "-": res = - u; break;
default:
alert("unimplemented: SellSymTerm:eval(..): uniop: " + op);
}
break;
case "binop":
u = this.eval(var_values, element.v[1]);
v = this.eval(var_values, element.v[2]);
op = element.v[0];
switch (op) {
case "+": res = u + v; break;
case "-": res = u - v; break;
case "*": res = u * v; break;
case "/": res = u / v; break;
case "^": res = math.pow(u, v); break;
default:
alert("unimplemented: SellSymTerm:eval(..): binop: " + op);
}
break;
case "fct1":
name = element.v[0];
param = this.eval(var_values, element.v[1]);
switch (name) {
case "sin": res = math.sin(param); break;
case "cos": res = math.cos(param); break;
case "exp": res = math.exp(param); break;
case "sqrt": res = math.sqrt(param); break;
default:
alert("unimplemented: SellSymTerm:eval(..): fct1: " + name);
}
break;
case "fct2":
name = element.v[0];
//param1 = this.eval(var_values, element.v[1]);
//param2 = this.eval(var_values, element.v[2]);
switch (name) {
case "diff_ode":
case "diff2_ode":
let t = null;
let t_id = element.v[1].v.id;
if (t_id in var_values)
t = var_values[t_id];
else
alert("SellSymTerm:eval(..): unknown term " + t_id);
let t_deriv_var_id = element.v[2].v;
let t_deriv = t.derivate(t_deriv_var_id);
if (name === "diff2_ode")
t_deriv = t_deriv.derivate(t_deriv_var_id);
res = t_deriv.eval(var_values);
// TODO: (update): should now work: following can be deleted!
// TODO: THIS CODE MUST BE REWRITTEN, AS IT IS VERY STATIC
// AND ONLY SUPPORTS diff(y, x) and diff(diff(y,x), x)!!!!!
/*let order = 1;
if(element.v[1].v.type === "function")
order = 2;
t = var_values["y"]; // TODO!!
let t_deriv_var_id = element.v[2].v;
let t_deriv = t.derivate(t_deriv_var_id);
if(order == 2)
t_deriv = t_deriv.derivate(t_deriv_var_id);
res = t_deriv.eval(var_values);
/*if(element.v[1].v.type === "function") {
t = element.v[1].v.value;
order = 2;
} else {
let t_id = element.v[1].v.id;
if(t_id in var_values)
t = var_values[t_id];
else
alert("SellSymTerm:eval(..): unknown term " + t_id);
}
let t_deriv_var_id = element.v[2].v;
let t_deriv = t.derivate(t_deriv_var_id);
res = t_deriv.eval(var_values);*/
break;
default:
alert("unimplemented: SellSymTerm:eval(..): fct1: " + name);
}
break;
case "ode_fun":
let t_id = element.v.id;
let t = null;
if (t_id in var_values)
t = var_values[t_id];
else
alert("SellSymTerm:eval(..): unknown term " + t_id);
res = t.eval(var_values);
break;
default:
alert("unimplemented: SellSymTerm:eval(..): " + element.type);
}
return res;
}
optimize(element = null) {
let op, u, v;
let isRoot = element == null;
if (isRoot)
element = this.stack[0];
if (element.type === "uniop") {
op = element.v[0];
u = this.optimize(element.v[1]);
element.v[1] = u; // TODO: this was commented out (why??)
if (op === "-" && u.type == "const" && u.v == 0) {
element.type = "const";
element.v = 0;
}
}
else if (element.type === "fct1") {
u = this.optimize(element.v[1]);
element.v[1] = u; // TODO: this was commented out (why??)
}
else if (element.type === "binop") {
op = element.v[0];
u = this.optimize(element.v[1]);
element.v[1] = u; // TODO: this was commented out (why??)
v = this.optimize(element.v[2]);
element.v[2] = v; // TODO: this was commented out (why??)
// calculate constant term
if (u.type === "const" && v.type === "const") {
//console.log('***')
//console.log(u.v)
//console.log(v.v)
element.type = "const";
switch (op) {
case "+": element.v = u.v + v.v; break;
case "-": element.v = u.v - v.v; break;
case "*": element.v = u.v * v.v; break;
case "/": element.v = u.v / v.v; break;
default:
alert("unimplemented: SellSymTerm:optimize(..): binop: " + op);
}
//console.log(element)
}
// 0, if u or v zero
else if (op === "*" && ((u.type === "const" && u.v == 0) || (v.type === "const" && v.v == 0))) {
element.type = "const";
element.v = 0;
}
// 1 * v = v
else if (op === "*" && (u.type === "const" && u.v == 1)) {
element.type = v.type;
element.v = v.v;
}
// (-1) * v = -v
else if (op === "*" && (u.type === "const" && u.v == -1)) {
element = new SellSymTermElement("uniop", ["-", v]);
}
// u * 1 = u
else if (op === "*" && (v.type === "const" && v.v == 1)) {
element.type = u.type;
element.v = u.v;
}
// u * (-1) = -u
else if (op === "*" && (v.type === "const" && u.v == -1)) {
element = new SellSymTermElement("uniop", ["-", u]);
}
// 0 + v = v
else if (op === "+" && (u.type === "const" && u.v == 0)) {
element.type = v.type;
element.v = v.v;
}
// u + 0 = u
else if (op === "+" && (v.type === "const" && v.v == 0)) {
element.type = u.type;
element.v = u.v;
}
// u - 0 = u
else if (op === "-" && (v.type === "const" && v.v == 0)) {
element.type = u.type;
element.v = u.v;
}
// u^1 = u
else if (op === "^" && (v.type === "const" && v.v == 1)) {
element.type = u.type;
element.v = u.v;
}
// u - (-v) = u + v
else if (op === "-" && v.type === "uniop" && v.v[0] === "-") {
element = new SellSymTermElement("binop", ["+", u, v.v[1]]);
}
// u + (-v) = u - v
else if (op === "+" && v.type === "uniop" && v.v[0] === "-") {
element = new SellSymTermElement("binop", ["-", u, v.v[1]]);
}
}
if (isRoot) {
this.stack[0] = element;
//console.log('yyyyy')
//console.log(this.toString(element))
//console.log(element.v)
}
return element;
}
integrateNumerically(varId, a, b) {
if(b < a)
return - this.integrateNumerically(varId, b, a);
const steps = 1e6; // TODO: configure
// TODO: must check if all variables are set!! -> error handling!!
let h = (b-a) / steps;
let res = 0;
for(let x=a; x<b; x+=h) {
let f1 = this.eval({ varId: x });
let f2 = this.eval({ varId: x+h });
res += (f1+f2)/2 * h;
}
return res;
}
compareWithStringTerm(t, listOfSymbols = []) {
// listOfSymbols is an optional list of symbols that could be present in string input t
this.state = "";
if (t.length == 0)
t = "0";
let vars = this.getVariables(); // actually needed variables
if (listOfSymbols.length > 0)
vars = listOfSymbols;
const n = 50 // TODO: configure number of tests
const l = -1 // TODO: configure lower bound for EACH variable IN SYNTAX
const u = 1 // TODO: configure upper bound for EACH variable IN SYNTAX
let epsilon = 1e-6; // TODO: configure espilon
for (let i = 0; i < n; i++) {
let scope = {};
for (let j = 0; j < vars.length; j++)
scope[vars[j]] = math.random(l, u);
//console.log("scope")
//console.log(scope)
//console.log("t")
//console.log(t)
let res = this.eval(scope);
if(res > 1000.0) { // too large values (e.g. due to exp(x)) are numerically instable: skip them!
i --; // TODO: can result infinite loops!
continue;
}
let t_res = 0;
try {
t_res = math.evaluate(t, scope);
} catch (e) {
this.state = "syntaxerror";
return false;
}
if(typeof(t_res) !== 'number') {
this.state = "syntaxerror";
return false;
}
//console.log(res + " vs " + t_res)
//console.log("type of res and type of tres: " + typeof(res) + ", " + typeof(t_res) );
if (math.abs(res - t_res) >= epsilon)
return false;
}
return true;
}
}
export class SellSymTerm_Matrix {
m : number;
n : number;
elements : Array<SellSymTerm>;
constructor(m : number, n : number, elements=[]) {
this.m = m;
this.n = n;
this.elements = elements;
}
toString() {
let s = '[';
for(let i=0; i<this.m; i++) {
if(i > 0)
s += ', ';
s += '[';
for(let j=0; j<this.n; j++) {
if(j > 0)
s += ', ';
s += this.elements[i*this.n + j].toString();
}
s += ']';
}
s += ']';
return s;
}
}