/*
PIP - Platform Independent Primitives
Evaluator designed for stream computing
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
#include "pievaluator.h"
/*! \class PIEvaluator
* \brief This class provide mathematical evaluations of custom expression
*
* \section PIEvaluator_sec0 Synopsis
* %PIEvaluator developed for stream evaluations of once set expression.
* It`s create internal list of instructions on function \a check() and
* executes very fast on function \a evaluate(). Once given expression
* can be evaluated any times with different variable values. Evaluator
* supports many common mathematic functions described below. Also it`s
* automatic puts unnecessarily signs and bracets. Processed expression
* can be obtains with function \a expression(). If there is an error
* in expression you can get it with function \a error(). Last evaluated
* result you can get with function \a lastResult().
* \section PIEvaluator_sec1 Using
* First you should set your variables with function \a setVariable().
* Next give your expression with function \a check() and check for error
* with functions \a isCorrect() and \a error(). If expression is correct
* you can get processed expression with function \a expression() and
* evaluate it with function \a evaluate(). You can change variable values
* without rechecking expression.
*
* \section PIEvaluator_sec2 Functions
* %PIEvaluator supports arithmetical operations with complex numbers, this
* is their list in priority order:
* * ^ (power)
* * * (multiply)
* * / (divide)
* * % (residue)
* * + (add)
* * - (subtract)
*
* In addition there are compare and logical operations:
* * == (equal)
* * != (not equal)
* * > (greater)
* * < (smaller)
* * >= (greater or equal)
* * <= (smaller or equal)
* * && (and)
* * || (or)
*
* Compare and logical functions works with real operators part and returns 0 or 1.
*
* Mathematical functions:
* * sin(x) - sine
* * cos(x) - cosine
* * tg(x) - tangent
* * ctg(x) - cotangent
* * arcsin(x) - arcsine
* * arccos(x) - arccosine
* * arctg(x) - arctangent
* * arcctg(x) - arccotangent
* * sh(x) - hyperbolical sine
* * ch(x) - hyperbolical cosine
* * th(x) - hyperbolical tangent
* * cth(x) - hyperbolical cotangent
* * sqr(x) - square
* * sqrt(x) - square root
* * abs(x) - absolute value
* * sign(x) - sign of real part (-1 or 1)
* * exp(x) - exponent
* * pow(x, p) - x in power p
* * ln(x) - natural logarithm
* * lg(x) - decimal logarithm
* * log(x, b) - logarithm of x with base b
* * im(x) - imaginary part of complex number
* * re(x) - real part of complex number
* * arg(x) - argument of complex number
* * len(x) - length of complex number
* * conj(x) - length of complex number
* * rad(x) - convert degrees to radians
* * deg(x) - convert radians to degrees
* * j0(x) - Bessel function first kind order 0
* * j1(x) - Bessel function first kind order 1
* * jn(x, n) - Bessel function first kind order n
* * y0(x) - Bessel function second kind order 0
* * y1(x) - Bessel function second kind order 1
* * yn(x, n) - Bessel function second kind order n
* * random(s, a) - regular random with shift s and amp a
* * randomn(s, a) - normalize random with shift s and amp a
* * min(x0, x1, ...) - minimum of x0, x1, ...
* * max(x0, x1, ...) - maximum of x0, x1, ...
* * clamp(x, a, b) - trim x on range [a, b]
* * step(x, s) - 0 if x < s, else 1
* * mix(x, a, b) - interpolate between a and b linear for x (a * (1 - x) + b * x)
* * round(x) - round
*
* There are some built-in constans:
* * i (imaginary 1)
* * e
* * pi
*
* All trigonometric functions takes angle in radians.
*
* \section PIEvaluator_sec3 Example
* \snippet pievaluator.cpp main
*/
using namespace PIEvaluatorTypes;
PIEvaluatorContent::PIEvaluatorContent() {
addFunction(PIStringAscii("arcsin" ), 1);
addFunction(PIStringAscii("arccos" ), 1);
addFunction(PIStringAscii("arctg" ), 1);
addFunction(PIStringAscii("arcctg" ), 1);
addFunction(PIStringAscii("random" ), 2);
addFunction(PIStringAscii("randomn"), 2);
addFunction(PIStringAscii("sin" ), 1);
addFunction(PIStringAscii("cos" ), 1);
addFunction(PIStringAscii("ctg" ), 1);
addFunction(PIStringAscii("tg" ), 1);
addFunction(PIStringAscii("exp" ), 1);
addFunction(PIStringAscii("cth" ), 1);
addFunction(PIStringAscii("sh" ), 1);
addFunction(PIStringAscii("ch" ), 1);
addFunction(PIStringAscii("th" ), 1);
addFunction(PIStringAscii("sqrt" ), 1);
addFunction(PIStringAscii("sqr" ), 1);
addFunction(PIStringAscii("pow" ), 2);
addFunction(PIStringAscii("abs" ), 1);
addFunction(PIStringAscii("ln" ), 1);
addFunction(PIStringAscii("lg" ), 1);
addFunction(PIStringAscii("log" ), 2);
addFunction(PIStringAscii("im" ), 1);
addFunction(PIStringAscii("re" ), 1);
addFunction(PIStringAscii("arg" ), 1);
addFunction(PIStringAscii("len" ), 1);
addFunction(PIStringAscii("conj" ), 1);
addFunction(PIStringAscii("sign" ), 1);
addFunction(PIStringAscii("rad" ), 1);
addFunction(PIStringAscii("deg" ), 1);
addFunction(PIStringAscii("j0" ), 1);
addFunction(PIStringAscii("j1" ), 1);
addFunction(PIStringAscii("jn" ), 2);
addFunction(PIStringAscii("y0" ), 1);
addFunction(PIStringAscii("y1" ), 1);
addFunction(PIStringAscii("yn" ), 2);
addFunction(PIStringAscii("min" ), -2); // (x0,x1,...)
addFunction(PIStringAscii("max" ), -2); // (x0,x1,...)
addFunction(PIStringAscii("clamp" ), 3); // (x,a,b) = x < a ? a : (x > b ? b : x)
addFunction(PIStringAscii("step" ), 2); // (x,s) = x >= s ? 1. : 0. (1 if 'x' >= 's', else 0)
addFunction(PIStringAscii("mix" ), 3); // (x,a,b) = a*(1.-x) + b*x (interpolate between 'a' and 'b' linear for 'x')
addFunction(PIStringAscii("defined"), 1);
addFunction(PIStringAscii("round" ), 1);
clearCustomVariables();
}
bool PIEvaluatorContent::setVariableValue(int index, complexd new_value) {
if (index < 0 || index >= variables.size_s()) return false;
variables[index].value = new_value;
return true;
}
bool PIEvaluatorContent::setVariableName(int index, const PIString & new_name) {
if (index < 0 || index >= variables.size_s()) return false;
variables[index].name = new_name;
return true;
}
void PIEvaluatorContent::addFunction(const PIString & name, int args) {
functions << PIEvaluatorTypes::Function(name, args, getBaseFunction(name));
}
int PIEvaluatorContent::addVariable(const PIString & name, const complexd & val) {
int ind = variables.size_s();
variables << PIEvaluatorTypes::Variable(name, val, ind);
return ind;
}
void PIEvaluatorContent::addCustomFunction(const PIString & name, int args_count, FuncFunc func) {
functions << PIEvaluatorTypes::Function(name, args_count, func);
}
int PIEvaluatorContent::customVariablesCount() const {
return variables.size() - cv_count;
}
int PIEvaluatorContent::findFunction(const PIString & name) const {
for (uint i = 0; i < functions.size(); i++)
if (functions[i].identifier == name)
return i;
return -1;
}
int PIEvaluatorContent::findVariable(const PIString & var_name) const {
for (uint i = 0; i < variables.size(); i++)
if (variables[i].name == var_name)
return variables[i].index;
return -1;
}
PIEvaluatorTypes::Function PIEvaluatorContent::function(int index) {
if (index < 0 || index >= functions.size_s())
return PIEvaluatorTypes::Function();
return functions[index];
}
PIEvaluatorTypes::Variable PIEvaluatorContent::variable(int index) {
if (index < 0 || index >= variables.size_s())
return PIEvaluatorTypes::Variable();
return variables[index];
}
PIEvaluatorTypes::Variable PIEvaluatorContent::customVariable(int index) {
if (index < 0 || index >= variables.size_s() - cv_count)
return PIEvaluatorTypes::Variable();
return variables[index + cv_count];
}
void PIEvaluatorContent::removeVariable(int index) {
if (index < 0 || index >= variables.size_s()) return;
variables.remove(index);
}
void PIEvaluatorContent::clearCustomVariables() {
variables.clear();
addVariable(PIStringAscii("i" ), complexd_i);
addVariable(PIStringAscii("pi"), atan(1.) * 4.);
addVariable(PIStringAscii("e" ), exp(1.));
cv_count = variables.size_s();
}
BaseFunctions PIEvaluatorContent::getBaseFunction(const PIString & name) {
if (name == PIStringAscii("sin" )) return bfSin;
if (name == PIStringAscii("cos" )) return bfCos;
if (name == PIStringAscii("tg" )) return bfTg;
if (name == PIStringAscii("ctg" )) return bfCtg;
if (name == PIStringAscii("arcsin" )) return bfArcsin;
if (name == PIStringAscii("arccos" )) return bfArccos;
if (name == PIStringAscii("arctg" )) return bfArctg;
if (name == PIStringAscii("arcctg" )) return bfArcctg;
if (name == PIStringAscii("exp" )) return bfExp;
if (name == PIStringAscii("random" )) return bfRandom;
if (name == PIStringAscii("randomn")) return bfRandomn;
if (name == PIStringAscii("sh" )) return bfSh;
if (name == PIStringAscii("ch" )) return bfCh;
if (name == PIStringAscii("th" )) return bfTh;
if (name == PIStringAscii("cth" )) return bfCth;
if (name == PIStringAscii("sqrt" )) return bfSqrt;
if (name == PIStringAscii("sqr" )) return bfSqr;
if (name == PIStringAscii("pow" )) return bfPow;
if (name == PIStringAscii("abs" )) return bfAbs;
if (name == PIStringAscii("ln" )) return bfLn;
if (name == PIStringAscii("lg" )) return bfLg;
if (name == PIStringAscii("log" )) return bfLog;
if (name == PIStringAscii("im" )) return bfIm;
if (name == PIStringAscii("re" )) return bfRe;
if (name == PIStringAscii("arg" )) return bfArg;
if (name == PIStringAscii("len" )) return bfLen;
if (name == PIStringAscii("conj" )) return bfConj;
if (name == PIStringAscii("sign" )) return bfSign;
if (name == PIStringAscii("rad" )) return bfRad;
if (name == PIStringAscii("deg" )) return bfDeg;
if (name == PIStringAscii("j0" )) return bfJ0;
if (name == PIStringAscii("j1" )) return bfJ1;
if (name == PIStringAscii("jn" )) return bfJN;
if (name == PIStringAscii("y0" )) return bfY0;
if (name == PIStringAscii("y1" )) return bfY1;
if (name == PIStringAscii("yn" )) return bfYN;
if (name == PIStringAscii("min" )) return bfMin;
if (name == PIStringAscii("max" )) return bfMax;
if (name == PIStringAscii("clamp" )) return bfClamp;
if (name == PIStringAscii("step" )) return bfStep;
if (name == PIStringAscii("mix" )) return bfMix;
if (name == PIStringAscii("defined")) return bfDefined;
if (name == PIStringAscii("round" )) return bfRound;
return bfUnknown;
}
void PIEvaluatorContent::dump() {
for (int i = 0; i < variables.size_s(); ++i) {
Variable v(variables[i]);
PIString str;
//str << "(ind " << v.index << ", mapped " << var_index.value(v.index, -1) << ")";
piCout << i << v.name << "=" << v.value;
}
}
const PIString & PIEvaluator::prepare(const PIString & string) {
currentString = string.trimmed();
if (currentString.isEmpty()) currentString = PIStringAscii("0");
replaceOperators();
removeSpaces();
checkBrackets();
while (fillElements()) checkBrackets();
while (setSignes()) fillElements();
removeJunk();
findUnknownVariables();
return currentString;
}
void PIEvaluator::removeSpaces() {
PIString tmps = currentString;
for (int i = 0; i < tmps.length(); i++) {
if (tmps[i] == ' ' || tmps[i] == '\t') {
tmps.remove(i, 1);
i--;
}
}
currentString = tmps;
}
void PIEvaluator::removeJunk() {
PIChar cc;
bool junk = true;
int bcnt;
while (junk) {
if (currentString.isEmpty()) return;
if (currentString.front() != '(' || currentString.back() != ')') return;
bcnt = 1;
junk = false;
for (int i = 1; i < currentString.length(); i++) {
cc = currentString[i];
if (cc == '(') bcnt++;
if (cc == ')') bcnt--;
if (bcnt == 0) {
if (i == currentString.length() - 1) {
currentString = currentString.mid(1, currentString.length() - 2);
elements.pop_front();
elements.pop_back();
junk = true;
break;
} else break;
}
}
}
}
void PIEvaluator::replaceOperators() {
currentString.replaceAll(PIStringAscii("=="), '=');
currentString.replaceAll(PIStringAscii("!="), ':');
currentString.replaceAll(PIStringAscii(">="), '}');
currentString.replaceAll(PIStringAscii("<="), '{');
currentString.replaceAll(PIStringAscii("&&"), '&');
currentString.replaceAll(PIStringAscii("||"), '|');
currentString.replaceAll(PIString::fromUTF8("≠"), ':');
currentString.replaceAll(PIString::fromUTF8("≥"), '}');
currentString.replaceAll(PIString::fromUTF8("≤"), '{');
currentString.replaceAll(PIString::fromUTF8("⋀"), '&');
currentString.replaceAll(PIString::fromUTF8("⋁"), '|');
}
void PIEvaluator::makeOutput(PIString & string) {
string.replaceAll(PIStringAscii(":"), PIString::fromUTF8("≠"));
string.replaceAll(PIStringAscii("}"), PIString::fromUTF8("≥"));
string.replaceAll(PIStringAscii("{"), PIString::fromUTF8("≤"));
string.replaceAll(PIStringAscii("&"), PIString::fromUTF8("⋀"));
string.replaceAll(PIStringAscii("|"), PIString::fromUTF8("⋁"));
}
void PIEvaluator::findUnknownVariables() {
PIString cvar;
unknownVars.clear();
usedVars.clear();
for (int i = 0; i < currentString.length(); i++) {
if (elements[i].var_num >= 0) usedVars << content.variable(elements[i].var_num).name;
else if (elements[i].var_num == -666) cvar += currentString[i];
else {
if (cvar.length() == 0) continue;
unknownVars << cvar;
cvar.clear();
}
}
if (cvar.length() > 0) unknownVars << cvar;
unknownVars.removeDuplicates();
usedVars.removeDuplicates();
usedVars.removeOne("i");
usedVars.removeOne("pi");
usedVars.removeOne("e");
}
bool PIEvaluator::isSign(const PIChar & ch) {
static PIString signs = PIStringAscii("+-*/%^=:><}{&|");
if (!ch.isAscii()) return false;
return signs.contains(ch.toAscii());
}
void PIEvaluator::checkBrackets() {
PIString tmps = currentString;
PIChar fc, sc;
int bcnt = 0, bpos = 0, inserted = 0;
currentString = tmps;
for (int i = 0; i < tmps.length(); i++) {
if (tmps[i] == '(') {
if (bcnt == 0) bpos = i;
bcnt++;
}
if (tmps[i] == ')') {
if (bcnt == 0) {
currentString.insert(bpos + inserted, '(');
inserted++;
} else bcnt--;
}
}
if (bcnt > 0) currentString += PIString(bcnt, ')');
tmps = currentString;
for (int i = 0; i < tmps.length() - 1; i++) {
fc = tmps[i].toLower();
sc = tmps[i + 1].toLower();
if ((fc == ')' && sc == '(') ||
(fc == ')' && sc >= '0' && sc <= '9') ||
(fc == ')' && sc >= 'a' && sc <= 'z') ) tmps.insert(++i, '*');
}
currentString = tmps;
}
bool PIEvaluator::fillElements() {
int fstart, flen, cnum = 0, cpart = 0, cfunc;
PIChar cc, nc, pc, fc = '!';
bool numFound = false;
PIString curfind, tmps = currentString;
elements.resize(tmps.length());
for (uint i = 0; i < elements.size(); i++) {
elements[i].type = etVariable;
elements[i].var_num = -666;
}
currentVariables.clear();
for (int i = 0; i < content.functionsCount(); i++) {
curfind = content.function(i).identifier;
cfunc = i;
flen = curfind.length();
fstart = 0;
while (fstart >= 0) {
fstart = tmps.find(curfind, fstart);
if (fstart < 0) break;
if (tmps[fstart + flen] != '(') {
fstart++;
continue;
}
for (int j = fstart; j < fstart + flen; j++) {
elements[j].set(etFunction, cnum, cfunc);
tmps.replace(j, 1, fc);
}
cnum++;
}
}
cnum = 0;
PIMap var_index;
PIVector svariables = content.variables;
for (int i = 0; i < svariables.size_s(); ++i)
svariables[i].index = i;
svariables.sort();
for (int i = 0; i < svariables.size_s(); i++) {
curfind = svariables[i].name;
flen = curfind.length();
fstart = 0;
while (fstart >= 0) {
fstart = tmps.find(curfind, fstart);
if (fstart < 0) break;
for (int j = fstart; j < fstart + flen; j++) {
elements[j].set(etVariable, cnum, svariables[i].index);
tmps.replace(j, 1, fc);
}
cnum++;
}
}
curfind.clear();
cnum = 1;
for (int i = 0; i < tmps.length(); i++) {
cc = tmps[i];
switch (cpart) {
case 0:
if ((cc >= '0' && cc <= '9')) {
curfind += cc;
cpart = 1;
continue;
}
if (cc == '.') {
curfind += cc;
cpart = 2;
continue;
}
if (cc == 'E') {
curfind += cc;
cpart = 3;
continue;
}
break;
case 1:
if (cc >= '0' && cc <= '9') {
curfind += cc;
continue;
}
if (cc == '.') {
curfind += cc;
cpart = 2;
continue;
}
if (cc == 'E') {
curfind += cc;
cpart = 3;
continue;
}
numFound = true;
break;
case 2:
if (cc >= '0' && cc <= '9') {
curfind += cc;
continue;
}
if (cc == 'E') {
curfind += cc;
cpart = 3;
continue;
}
numFound = true;
break;
case 3:
if ((cc >= '0' && cc <= '9') || cc == '-' || cc == '+') {
curfind += cc;
cpart = 4;
continue;
}
numFound = true;
break;
case 4:
if (cc >= '0' && cc <= '9') {
curfind += cc;
continue;
}
numFound = true;
break;
}
if (numFound) {
currentVariables.push_back(Variable(PIStringAscii("tmp") + PIString::fromNumber(cnum), curfind.toDouble()));
for (int j = i - curfind.length(); j < i; j++) {
elements[j].set(etNumber, cnum, -cnum);
tmps.replace(j, 1, fc);
}
curfind.clear();
cnum++;
cpart = 0;
numFound = false;
}
}
if (cpart > 0) {
currentVariables.push_back(Variable(PIStringAscii("tmp") + PIString::fromNumber(cnum), curfind.toDouble()));
for (int j = tmps.length() - curfind.length(); j < tmps.length(); j++) {
elements[j].set(etNumber, cnum, -cnum);
tmps.replace(j, 1, fc);
}
}
cc = nc = fc;
for (int i = 0; i < tmps.length(); i++) {
cc = tmps[i];
if (i > 0) pc = tmps[i - 1];
else pc = fc;
if (i < tmps.length() - 1) nc = tmps[i + 1];
else nc = fc;
if (cc == '(' || cc == ')' || cc == ',') {
elements[i].set(etOperator, -1);
continue;
}
if (cc == '-' || cc == '+') {
elements[i].set(etOperator, -1);
if (i < tmps.length() - 1) if (elements[i + 1].type == etVariable ||
elements[i + 1].type == etFunction) continue;
if ((pc == '(' || isSign(pc) || i == 0) && i < tmps.length() - 1) {
if (elements[i + 1].type != etOperator) {
cnum = elements[i + 1].num;
elements[i].set(etNumber, cnum);
tmps.replace(i, 1, fc);
///cout << "found sign " << cc << " :" << cnum - 1 << endl;
if (cc == '-' && currentVariables.size_s() >= cnum)
currentVariables[cnum - 1].value = -currentVariables[cnum - 1].value;
continue;
}
}
}
if (isSign(cc)) {
elements[i].set(etOperator, -1);
continue;
}
}
/*cout << " ";
for (int i = 0; i < elements.size(); i++) {
switch (elements[i].type) {
case etFunction: cout << "f"; break;
case etNumber: cout << "n"; break;
case etOperator: cout << "o"; break;
case etVariable: cout << "v"; break;
}
}
cout << endl; */
return false;
}
bool PIEvaluator::setSignes() {
int inserted = 0, ni, pi = 0, needInsert = 0;
PIChar fc, sc, pc;
PIString tmps = currentString;
for (int i = 0; i < tmps.length() - 1; i++) {
needInsert = 0;
ni = i + 1;
if (i > 0) pi = i - 1;
fc = tmps[i].toLower();
sc = tmps[ni].toLower();
pc = tmps[pi].toLower();
if (fc == ',' || sc == ',') continue;
if (elements[i].type == etOperator && elements[ni].type == etOperator) continue;
if (fc == ')' && (elements[ni].type == etNumber || elements[ni].type == etVariable || elements[ni].type == etFunction)) needInsert = 1;
if (sc == '(' && (elements[i].type == etNumber || elements[i].type == etVariable)) needInsert = 1;
if (elements[i].type == etNumber && elements[ni].type == etNumber && elements[i].num != elements[ni].num) needInsert = 1;
if (elements[i].type == etVariable && elements[ni].type == etVariable && elements[i].num != elements[ni].num) needInsert = 1;
if ((elements[i].type == etNumber && elements[ni].type == etVariable) || (elements[i].type == etVariable && elements[ni].type == etNumber)) needInsert = 1;
if ((elements[i].type == etNumber || elements[i].type == etVariable) && elements[ni].type == etFunction) needInsert = 1;
if (elements[i].type == etFunction && elements[ni].type == etFunction && elements[i].num != elements[ni].num) needInsert = 2;
if (elements[i].type == etFunction && elements[ni].type != etFunction && sc != '(') needInsert = 2;
if (elements[pi].type == etOperator && (elements[ni].type == etFunction || elements[ni].type == etVariable) && fc == '-') needInsert = 3;
switch (needInsert) {
case 1:
currentString.insert(ni + inserted, '*');
elements.insert(ni + inserted, Element(etOperator, -1));
return true;
case 3:
currentString.insert(ni + inserted, PIStringAscii("1*"));
elements.insert(ni + inserted, Element(etOperator, -1));
return true;
}
}
return false;
}
void PIEvaluator::convert() {
int j;
Element ce, pe;
for (int i = 0; i < currentString.length(); i++) {
pe = elements[i];
if (pe.type != etFunction) continue;
j = i + 1;
while (j < currentString.length()) {
ce = elements[j];
if (ce != pe) break;
j++;
}
currentString.replace(i, j - i, ' ');
for (int k = i + 1; k < j; k++) elements.remove(i);
}
for (int i = 0; i < currentString.length(); i++) {
pe = elements[i];
if (pe.type != etNumber) continue;
j = i + 1;
while (j < currentString.length()) {
ce = elements[j];
if (ce != pe) break;
j++;
}
currentString.replace(i, j - i, ' ');
for (int k = i + 1; k < j; k++) elements.remove(i);
}
for (int i = 0; i < currentString.length(); i++) {
pe = elements[i];
if (pe.type != etVariable) continue;
j = i + 1;
while (j < currentString.length()) {
ce = elements[j];
if (ce != pe) break;
j++;
}
currentString.replace(i, j - i, ' ');
for (int k = i + 1; k < j; k++) elements.remove(i);
}
/*cout << " ";
for (int i = 0; i < elements.size(); i++) {
switch (elements[i].type) {
case etFunction: cout << "f"; break;
case etNumber: cout << "n"; break;
case etOperator: cout << "o"; break;
case etVariable: cout << "v"; break;
}
}
cout << endl; */
}
PIString PIEvaluator::preprocess(const PIString & string) {
PIString ret;
int lind;
ret = prepare(string);
convert();
instructions.clear();
variables = currentVariables;
lind = parse(currentString);
if (instructions.size() == 0) {
variables.push_back(Variable());
instructions.push_back(Instruction(oNone, PIVector(1, lind), -variables.size_s()));
}
/*cout << endl << "variables:" << endl;
for (int i = 0; i < variables.size(); i++)
cout << i << " value = " << variables[i].value << endl;
cout << endl << "instructions:" << endl;
for (int i = 0; i < instructions.size(); i++) {
cout << i << endl;
cout << " operation " << instructions[i].operation << endl;
cout << " operators: ";
for (int j = 0; j < instructions[i].operators.size(); j++)
cout << instructions[i].operators[j] << "; ";
cout << endl << " function " << instructions[i].function << endl;
cout << " out " << instructions[i].out << endl;
} */
makeOutput(ret);
return ret;
}
int PIEvaluator::parse(const PIString & string, int offset) {
int slen = string.length(), /*facnt,*/ farg, bcnt, k;
PIChar cc;
Element ce;
Function cfunc;
PIString sbrackets, carg;
PIVector args, atmp;
PIVector opers;
//cout << " "; for (int i = 0; i < slen; i++) cout << preproc->elements[i + offset].type; cout << endl;
for (int i = 0; i < slen; i++) {
ce = elements[i + offset];
cc = string[i];
switch (ce.type) {
case etNumber:
args.push_back(ce.var_num);
continue;
case etVariable:
args.push_back(ce.var_num);
continue;
case etFunction:
i++;
cfunc = content.function(ce.var_num);
atmp.clear();
bcnt = farg = 1;
carg.clear();
k = i + 1;
while (bcnt > 0) {
cc = string[k];
switch (cc.toAscii()) {
case '(': bcnt++; break;
case ')':
bcnt--;
if (bcnt == 0) {
///qDebug() << "arument: " << carg;
atmp.push_back(parse(carg, k + offset - carg.length()));
k++;
carg.clear();
if (atmp.size_s() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back();
continue;
}
break;
case ',':
if (bcnt == 1) {
///qDebug() << "arument: " << carg;
atmp.push_back(parse(carg, k + offset - carg.length()));
k++;
carg.clear();
if (atmp.size_s() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back();
continue;
}
break;
}
carg += cc;
k++;
}
i = k - 1;
if (farg > 0) {
variables.push_back(Variable());
farg = -variables.size_s();
}
instructions.push_back(Instruction(oFunction, atmp, farg, ce.var_num));
args.push_back(farg);
//for (int i = 0; i < args.size_s(); i++) cout << preproc->currentVariables[-args[i]].value << endl;
continue;
case etOperator:
if (cc == '(') {
sbrackets = inBrackets(string.right(slen - i));
args.push_back(parse(sbrackets, i + offset + 1));
i += sbrackets.length() + 1;
continue;
}
if (cc == '+') {opers.push_back(oAdd); continue;}
if (cc == '-') {opers.push_back(oSubtract); continue;}
if (cc == '*') {opers.push_back(oMultiply); continue;}
if (cc == '/') {opers.push_back(oDivide); continue;}
if (cc == '%') {opers.push_back(oResidue); continue;}
if (cc == '^') {opers.push_back(oPower); continue;}
if (cc == '=') {opers.push_back(oEqual); continue;}
if (cc == ':') {opers.push_back(oNotEqual); continue;}
if (cc == '}') {opers.push_back(oGreaterEqual); continue;}
if (cc == '{') {opers.push_back(oSmallerEqual); continue;}
if (cc == '>') {opers.push_back(oGreater); continue;}
if (cc == '<') {opers.push_back(oSmaller); continue;}
if (cc == '&') {opers.push_back(oAnd); continue;}
if (cc == '|') {opers.push_back(oOr); continue;}
}
}
/*cout << "stack: " << endl << "args: ";
for (int i = 0; i < args.size_s(); i++) cout << args[i] << ", ";
cout << endl << "opers: ";
for (int i = 0; i < opers.size_s(); i++) cout << opers[i] << ", "; */
if (opers.size_s() == 0) {
if (args.size_s() > 0) return args.back();
else return -666;
}
int oprior = -1;
PIVector opv;
while(1) {
operationsByPriority(++oprior, opv);
if (opv.isEmpty()) break;
for (int j = 0; j < opers.size_s(); j++) {
if (!opv.contains(opers[j])) continue;
atmp.clear();
if (j < args.size_s() && j >= 0) atmp.push_back(args[j]);
else atmp.push_back(-666);
if (j + 1 < args.size_s() && j >= -1) atmp.push_back(args[j + 1]);
else atmp.push_back(-666);
farg = 1;
if (atmp[0] < 0) farg = atmp[0];
else {
if (atmp[1] < 0) farg = atmp[1];
else {
variables.push_back(Variable());
farg = -variables.size_s();
}
}
instructions.push_back(Instruction(opers[j], atmp, farg));
if (j >= 0 && j < args.size_s()) {
args.remove(j);
if (j < args.size_s()) args[j] = farg;
}
opers.remove(j);
j--;
}
}
return instructions.back().out;
//cout << endl;
}
bool PIEvaluator::check() {
Instruction ci;
bool error;
if (unknownVars.size_s() > 0) {
lastError = PIStringAscii("Unknown variables: \"") + unknownVars.join("\", \"") + PIStringAscii("\"");
return false;
}
for (int i = 0; i < instructions.size_s(); i++) {
error = false;
ci = instructions[i];
Function cf;
int fac, gac;
switch (ci.operation) {
case oNone: break;
case oFunction:
cf = content.function(ci.function);
fac = cf.arguments;
gac = ci.operators.size_s();
for (int j = 0; j < ci.operators.size_s(); j++) {
if (ci.operators[j] == -666) {
error = true;
gac--;
}
}
if (fac > 0) {
if (gac != fac) {
lastError = PIStringAscii("Invalid arguments count for function \"") + cf.identifier +
PIStringAscii("\", expected ") + PIString::fromNumber(fac) + PIStringAscii(" but ") +
PIString::fromNumber(gac) + PIStringAscii(" given");
return false;
}
if (error) {
lastError = PIStringAscii("Invalid at least one of function \"") + cf.identifier + PIStringAscii("\" argument");
return false;
}
}
if (fac < 0) {
if (gac < -fac) {
lastError = PIStringAscii("Invalid arguments count for function \"") + cf.identifier +
PIStringAscii("\", expected at least ") + PIString::fromNumber(-fac) + PIStringAscii(" but ") +
PIString::fromNumber(gac) + PIStringAscii(" given");
return false;
}
if (error) {
lastError = PIStringAscii("Invalid at least one of function \"") + cf.identifier + PIStringAscii("\" argument");
return false;
}
}
break;
default:
if (ci.operators[0] == -666 || ci.operators[1] == -666) error = true;
if (ci.operators.size_s() != 2 || error) {
lastError = PIStringAscii("Invalid arguments count for operation \" ") + operationChar(ci.operation) + PIStringAscii(" \"");
return false;
}
break;
}
if (ci.out < -variables.size_s()) {
lastError = PIStringAscii("Invalid variable index \"") + PIString::fromNumber(ci.out) + PIStringAscii("\"");
return false;
}
for (int j = 0; j < ci.operators.size_s(); j++) {
if (ci.operators[j] < -variables.size_s() || ci.operators[j] >= content.variables.size_s()) {
lastError = PIStringAscii("Invalid variable index \"") + PIString::fromNumber(ci.operators[j]) + PIStringAscii("\"");
return false;
}
}
}
return true;
}
PIString PIEvaluator::inBrackets(const PIString & string) {
int slen = string.length(), bcnt = 0;
PIChar cc;
for (int i = 0; i < slen; i++) {
cc = string[i];
if (cc == '(') bcnt++;
if (cc == ')') {
bcnt--;
if (bcnt == 0) return string.mid(1, i - 1);
}
}
return PIString();
}
PIString PIEvaluator::operationChar(const Operation & operation) {
switch (operation) {
case oAdd : return PIStringAscii("+");
case oSubtract : return PIStringAscii("-");
case oMultiply : return PIStringAscii("*");
case oDivide : return PIStringAscii("/");
case oPower : return PIStringAscii("^");
case oResidue : return PIStringAscii("%");
case oEqual : return PIStringAscii("=");
case oNotEqual : return PIString::fromUTF8("≠");
case oGreaterEqual: return PIString::fromUTF8("≥");
case oSmallerEqual: return PIString::fromUTF8("≤");
case oGreater : return PIStringAscii(">");
case oSmaller : return PIStringAscii("<");
case oAnd : return PIString::fromUTF8("⋀");
case oOr : return PIString::fromUTF8("⋁");
default: break;
}
return PIStringAscii("???");
}
void PIEvaluator::operationsByPriority(int p, PIVector & ret) {
ret.clear();
switch (p) {
case 0: ret << oPower; break;
case 1: ret << oMultiply << oDivide << oResidue; break;
case 2: ret << oAdd << oSubtract; break;
case 3: ret << oEqual << oNotEqual << oGreaterEqual << oSmallerEqual << oGreater << oSmaller; break;
case 4: ret << oAnd; break;
case 5: ret << oOr; break;
default: break;
}
}
inline complexd PIEvaluator::residue(const complexd & f, const complexd & s) {
complexd ret;
if (s.real() != 0.) ret = complexd(f.real() - ((int)(f.real() / s.real())) * s.real(), 0.);
if (s.imag() != 0.) ret = complexd(ret.real(), f.imag() - ((int)(f.imag() / s.imag())) * s.imag());
return ret;
}
inline void PIEvaluator::execFunction(const Instruction & ci) {
const Function & cfunc(content.function(ci.function));
int oi = -ci.out - 1;
complexd tmp, stmp, ttmp;
switch (cfunc.type) {
case bfSin:
tmpvars[oi].value = sin(value(ci.operators[0]));
break;
case bfCos:
tmpvars[oi].value = cos(value(ci.operators[0]));
break;
case bfTg:
tmpvars[oi].value = tan(value(ci.operators[0]));
break;
case bfCtg:
tmp = tan(value(ci.operators[0]));
if (tmp == complexd_0) tmpvars[oi].value = 0.;
else tmpvars[oi].value = complexd_1 / tmp;
break;
case bfArcsin:
tmpvars[oi].value = asinc(value(ci.operators[0]));
break;
case bfArccos:
tmpvars[oi].value = acosc(value(ci.operators[0]));
break;
case bfArctg:
tmpvars[oi].value = atanc(value(ci.operators[0]));
break;
case bfArcctg:
tmpvars[oi].value = atanc(-value(ci.operators[0])) + M_PI_2;
break;
case bfSh:
tmpvars[oi].value = sinh(value(ci.operators[0]));
break;
case bfCh:
tmpvars[oi].value = cosh(value(ci.operators[0]));
break;
case bfTh:
tmpvars[oi].value = tanh(value(ci.operators[0]));
break;
case bfCth:
tmp = tanh(value(ci.operators[0]));
if (tmp == complexd_0) tmpvars[oi].value = 0.;
else tmpvars[oi].value = complexd_1 / tmp;
break;
case bfAbs:
tmpvars[oi].value = abs(value(ci.operators[0]));
break;
case bfSqrt:
tmpvars[oi].value = sqrt(value(ci.operators[0]));
break;
case bfSqr:
tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[0]);
break;
case bfExp:
tmpvars[oi].value = exp(value(ci.operators[0]));
break;
case bfPow:
tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
break;
case bfLn:
tmpvars[oi].value = log(value(ci.operators[0]));
break;
case bfLg:
tmpvars[oi].value = log10(value(ci.operators[0]));
break;
case bfLog:
tmp = log(value(ci.operators[1]));
if (tmp == complexd_0) tmpvars[oi].value = 0.;
else tmpvars[oi].value = log(value(ci.operators[0])) / tmp;
break;
case bfRe:
tmpvars[oi].value = value(ci.operators[0]).real();
break;
case bfIm:
tmpvars[oi].value = value(ci.operators[0]).imag();
break;
case bfArg:
tmpvars[oi].value = arg(value(ci.operators[0]));
break;
case bfLen:
tmpvars[oi].value = abs(value(ci.operators[0]));
break;
case bfConj:
tmpvars[oi].value = conj(value(ci.operators[0]));
break;
case bfSign:
tmpvars[oi].value = value(ci.operators[0]).real() >= 0. ? complexd_1 : -complexd_1;
break;
case bfRad:
tmpvars[oi].value = value(ci.operators[0]) * complexd(deg2rad, 0.);
break;
case bfDeg:
tmpvars[oi].value = value(ci.operators[0]) * complexd(rad2deg, 0.);
break;
case bfJ0:
tmpvars[oi].value = piJ0(value(ci.operators[0]).real());
break;
case bfJ1:
tmpvars[oi].value = piJ1(value(ci.operators[0]).real());
break;
case bfJN:
tmpvars[oi].value = piJn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
break;
case bfY0:
tmpvars[oi].value = piY0(value(ci.operators[0]).real());
break;
case bfY1:
tmpvars[oi].value = piY1(value(ci.operators[0]).real());
break;
case bfYN:
tmpvars[oi].value = piYn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
break;
case bfMin:
tmp = value(ci.operators[0]);
for (int i = 1; i < ci.operators.size_s(); ++i) {
stmp = value(ci.operators[i]);
tmp = complexd(piMind(tmp.real(), stmp.real()), piMind(tmp.imag(), stmp.imag()));
}
tmpvars[oi].value = tmp;
break;
case bfMax:
tmp = value(ci.operators[0]);
for (int i = 1; i < ci.operators.size_s(); ++i) {
stmp = value(ci.operators[i]);
tmp = complexd(piMaxd(tmp.real(), stmp.real()), piMaxd(tmp.imag(), stmp.imag()));
}
tmpvars[oi].value = tmp;
break;
case bfClamp:
tmp = value(ci.operators[0]);
stmp = value(ci.operators[1]);
ttmp = value(ci.operators[2]);
tmpvars[oi].value = complexd(piClampd(tmp.real(), stmp.real(), ttmp.real()), piClampd(tmp.imag(), stmp.imag(), ttmp.imag()));
break;
case bfStep:
tmpvars[oi].value = complexd(value(ci.operators[0]).real() >= value(ci.operators[1]).real() ? complexld_1 : complexld_0);
break;
case bfMix:
tmp = value(ci.operators[0]);
stmp = value(ci.operators[1]);
ttmp = value(ci.operators[2]);
tmpvars[oi].value = stmp.real() * (1. - tmp.real()) + ttmp.real() * tmp.real();
break;
case bfDefined:
tmpvars[oi].value = value(ci.operators[0]).real() > 0. ? complexd_1 : complexd_0;
break;
case bfRandom:
tmpvars[oi].value = value(ci.operators[0]) + randomd() * value(ci.operators[1]);
break;
case bfRandomn:
tmpvars[oi].value = randomn(value(ci.operators[0]).real(), value(ci.operators[1]).real());
break;
case bfRound:
tmpvars[oi].value = piRoundd(value(ci.operators[0]).real());
break;
default: break;
}
}
inline bool PIEvaluator::execInstructions() {
int oi;
tmpvars = variables;
//cout << "var count " << tmpvars.size_s() << endl;
for (int i = 0; i < instructions.size_s(); i++) {
const Instruction & ci(instructions[i]);
oi = -ci.out - 1;
//cout << value(ci.operators[0]) << operationChar(ci.operation) << value(ci.operators[1]) << ", " << oi << endl;
switch (ci.operation) {
case oAdd:
tmpvars[oi].value = value(ci.operators[0]) + value(ci.operators[1]);
break;
case oSubtract:
tmpvars[oi].value = value(ci.operators[0]) - value(ci.operators[1]);
break;
case oMultiply:
tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[1]);
break;
case oDivide: {
complexd tmp = value(ci.operators[1]);
if (tmp == complexd(0., 0.)) tmpvars[oi].value = 0.;
else tmpvars[oi].value = value(ci.operators[0]) / tmp;
} break;
case oResidue:
tmpvars[oi].value = residue(value(ci.operators[0]), value(ci.operators[1]));
break;
case oPower:
tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
break;
case oEqual:
tmpvars[oi].value = value(ci.operators[0]) == value(ci.operators[1]);
break;
case oNotEqual:
tmpvars[oi].value = value(ci.operators[0]) != value(ci.operators[1]);
break;
case oGreaterEqual:
tmpvars[oi].value = value(ci.operators[0]).real() >= value(ci.operators[1]).real();
break;
case oSmallerEqual:
tmpvars[oi].value = value(ci.operators[0]).real() <= value(ci.operators[1]).real();
break;
case oGreater:
tmpvars[oi].value = value(ci.operators[0]).real() > value(ci.operators[1]).real();
break;
case oSmaller:
tmpvars[oi].value = value(ci.operators[0]).real() < value(ci.operators[1]).real();
break;
case oAnd:
tmpvars[oi].value = value(ci.operators[0]).real() > 0. && value(ci.operators[1]).real() > 0.;
break;
case oOr:
tmpvars[oi].value = value(ci.operators[0]).real() > 0. || value(ci.operators[1]).real() > 0.;
break;
case oFunction:
execFunction(ci);
break;
case oNone:
tmpvars[oi].value = value(ci.operators[0]);
break;
default: break;
}
}
if (!instructions.isEmpty())
out = value(instructions.back().out);
return true;
}
bool PIEvaluator::check(const PIString & string) {
currentString = preprocess(string);
correct = check();
if (!correct) {
instructions.clear();
return false;
}
lastError = PIStringAscii("Correct");
return true;
}
int PIEvaluator::setVariable(const PIString & name, complexd value) {
int ind = content.findVariable(name);
if (ind < 0)
ind = content.addVariable(name, value);
else
content.setVariableValue(name, value);
return ind;
}
void PIEvaluator::setVariable(int index, complexd value) {
content.setVariableValue(index, value);
}
complexd PIEvaluator::evaluate() {
if (!execInstructions()) out = 0.;
if (fabs(out.real()) < 1E-300) out = complexd(0., out.imag());
if (fabs(out.imag()) < 1E-300) out = complexd(out.real(), 0.);
return out;
}
PIByteArray PIEvaluator::save() const {
PIByteArray ret;
ret << content << currentVariables << unknownVars << instructions << currentString << lastError << out << correct << usedVars;
return ret;
}
void PIEvaluator::load(PIByteArray ba) {
if (ba.size() <= 4) return;
ba >> content >> currentVariables >> unknownVars >> instructions >> currentString >> lastError >> out >> correct;
if (ba.size() >= 4) ba >> usedVars;
variables = currentVariables;
}