Files
pip/libs/main/math/pievaluator.cpp

1291 lines
39 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 <http://www.gnu.org/licenses/>.
*/
#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) {
variables << PIEvaluatorTypes::Variable(name, val);
return variables.size_s() - 1;
}
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 i;
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::clearCustomVariables() {
variables.clear();
addVariable(PIStringAscii("i" ), complexd_i);
addVariable(PIStringAscii("pi"), 3.141592653589793238462643383280);
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]);
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;
typedef PIPair<int, int> PairII;
PIVector<PairII> var_index;
for (int i = 0; i < content.variables.size_s(); ++i) {
var_index << PairII(i, content.variables[i].name.length());
}
var_index.sort([](const PairII & v1, const PairII & v2){return v1.second > v2.second;});
for (int i = 0; i < var_index.size_s(); i++) {
int vind = var_index[i].first;
curfind = content.variables[vind].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, vind);
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<short>(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<short> args, atmp;
PIVector<Operation> 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<Operation> 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<Operation> & 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;
}