/* 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; }