/*
    PIP - Platform Independent Primitives
    Evaluator designed for stream computing
	Copyright (C) 2016  Ivan Pelipenko peri4ko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU 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 General Public License for more details.

    You should have received a copy of the GNU 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) -arccotangent
 * * arcctg(x) - arctangent
 * * 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, f) - regular random number in range [s, f]
 * * 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)
 * 
 * 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
 */


PIEvaluatorContent::PIEvaluatorContent() {
	addFunction("arcsin", 1);
	addFunction("arccos", 1);
	addFunction("arctg", 1);
	addFunction("arcctg", 1);
	addFunction("random", 2);
	addFunction("sin", 1);
	addFunction("cos", 1);
	addFunction("ctg", 1);
	addFunction("tg", 1);
	addFunction("exp", 1);
	addFunction("cth", 1);
	addFunction("sh", 1);
	addFunction("ch", 1);
	addFunction("th", 1);
	addFunction("sqrt", 1);
	addFunction("sqr", 1);
	addFunction("pow", 2);
	addFunction("abs", 1);
	addFunction("ln", 1);
	addFunction("lg", 1);
	addFunction("log", 2);
	addFunction("im", 1);
	addFunction("re", 1);
	addFunction("arg", 1);
	addFunction("len", 1);
	addFunction("conj", 1);
	addFunction("sign", 1);
	addFunction("rad", 1);
	addFunction("deg", 1);
	addFunction("j0", 1);
	addFunction("j1", 1);
	addFunction("jn", 2);
	addFunction("y0", 1);
	addFunction("y1", 1);
	addFunction("yn", 2);
	addFunction("min", -2); // (x0,x1,...)
	addFunction("max", -2); // (x0,x1,...)
	addFunction("clamp", 3); // (x,a,b) =  x < a ? a : (x > b ? b : x)
	addFunction("step", 2); // (x,s) =     x >= s ? 1. : 0.  (1 if 'x' >= 's', else 0)
	addFunction("mix", 3); // (x,a,b) =    a*(1.-x) + b*x    (interpolate between 'a' and 'b' linear for 'x')
	addFunction("defined", 1);
	clearCustomVariables();
	//addVariable("n", 0.);
	//addVariable("x1", 123);
}


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::clearCustomVariables() {
	variables.clear();
	addVariable("i", complexd_i);
	addVariable("pi", atan(1.) * 4.);
	addVariable("e", exp(1.));
	cv_count = variables.size();
}


void PIEvaluatorContent::sortVariables() {
	//PIEvaluatorTypes::Variable tv;
	for (uint i = 0; i < variables.size(); i++) {
		for (uint j = variables.size() - 1; j > i; j--) {
			if (variables[j].name.length() <= variables[i].name.length()) continue;
			piSwap<PIEvaluatorTypes::Variable>(variables[i], variables[j]);
		}
	}
	/*
	 *	qDebug() << "---";
	 *	for (int i = 0; i < variables.size(); i++) {
	 *		qDebug() << variables[i].name;
	 }
	 */
}


PIEvaluatorTypes::BaseFunctions PIEvaluatorContent::getBaseFunction(const PIString & name) {
	if (name == "sin") return PIEvaluatorTypes::bfSin;
	if (name == "cos") return PIEvaluatorTypes::bfCos;
	if (name == "tg") return PIEvaluatorTypes::bfTg;
	if (name == "ctg") return PIEvaluatorTypes::bfCtg;
	if (name == "arcsin") return PIEvaluatorTypes::bfArcsin;
	if (name == "arccos") return PIEvaluatorTypes::bfArccos;
	if (name == "arctg") return PIEvaluatorTypes::bfArctg;
	if (name == "arcctg") return PIEvaluatorTypes::bfArcctg;
	if (name == "exp") return PIEvaluatorTypes::bfExp;
	if (name == "random") return PIEvaluatorTypes::bfRandom;
	if (name == "sh") return PIEvaluatorTypes::bfSh;
	if (name == "ch") return PIEvaluatorTypes::bfCh;
	if (name == "th") return PIEvaluatorTypes::bfTh;
	if (name == "cth") return PIEvaluatorTypes::bfCth;
	if (name == "sqrt") return PIEvaluatorTypes::bfSqrt;
	if (name == "sqr") return PIEvaluatorTypes::bfSqr;
	if (name == "pow") return PIEvaluatorTypes::bfPow;
	if (name == "abs") return PIEvaluatorTypes::bfAbs;
	if (name == "ln") return PIEvaluatorTypes::bfLn;
	if (name == "lg") return PIEvaluatorTypes::bfLg;
	if (name == "log") return PIEvaluatorTypes::bfLog;
	if (name == "im") return PIEvaluatorTypes::bfIm;
	if (name == "re") return PIEvaluatorTypes::bfRe;
	if (name == "arg") return PIEvaluatorTypes::bfArg;
	if (name == "len") return PIEvaluatorTypes::bfLen;
	if (name == "conj") return PIEvaluatorTypes::bfConj;
	if (name == "sign") return PIEvaluatorTypes::bfSign;
	if (name == "rad") return PIEvaluatorTypes::bfRad;
	if (name == "deg") return PIEvaluatorTypes::bfDeg;
	if (name == "j0") return PIEvaluatorTypes::bfJ0;
	if (name == "j1") return PIEvaluatorTypes::bfJ1;
	if (name == "jn") return PIEvaluatorTypes::bfJN;
	if (name == "y0") return PIEvaluatorTypes::bfY0;
	if (name == "y1") return PIEvaluatorTypes::bfY1;
	if (name == "yn") return PIEvaluatorTypes::bfYN;
	if (name == "min") return PIEvaluatorTypes::bfMin;
	if (name == "max") return PIEvaluatorTypes::bfMax;
	if (name == "clamp") return PIEvaluatorTypes::bfClamp;
	if (name == "step") return PIEvaluatorTypes::bfStep;
	if (name == "mix") return PIEvaluatorTypes::bfMix;
	if (name == "defined") return PIEvaluatorTypes::bfDefined;
	return PIEvaluatorTypes::bfUnknown;
}

const PIString & PIEvaluator::prepare(const PIString & string) {
	currentString = string.trimmed();
	if (currentString.isEmpty()) currentString = "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.left(1) != "(" || currentString.right(1) != ")") 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("==", "=");
	currentString.replaceAll("!=", ":");
	currentString.replaceAll(">=", "}");
	currentString.replaceAll("<=", "{");
	currentString.replaceAll("&&", "&");
	currentString.replaceAll("||", "|");
}


void PIEvaluator::makeOutput(PIString & string) {
	string.replaceAll(":", "≠");
	string.replaceAll("}", "≥");
	string.replaceAll("{", "≤");
	string.replaceAll("&", "⋀");
	string.replaceAll("|", "⋁");
}


void PIEvaluator::findUnknownVariables() {
	PIString cvar;
	unknownVars.clear();
	for (int i = 0; i < currentString.length(); i++) {
		if (elements[i].var_num == -666) cvar += currentString[i];
		else {
			if (cvar.length() == 0) continue;
			unknownVars << cvar;
			cvar = "";
		}
	}
	if (cvar.length() > 0) unknownVars << cvar;
	unknownVars.removeDuplicates();
}


bool PIEvaluator::isSign(const PIChar & ch) {
	return ch == '+' || ch == '-' ||
		   ch == '*' || ch == '/' ||
		   ch == '%' || ch == '^' ||
		   ch == '=' || ch == ':' ||
		   ch == '>' || ch == '<' ||
		   ch == '}' || ch == '{' ||
		   ch == '&' || ch == '|';
}


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 = PIEvaluatorTypes::etVariable;
		elements[i].var_num = -666;
	}
	currentVariables.clear();
	//qDebug().nospace() << "search for functions ...";
	for (int i = 0; i < content.functionsCount(); i++) {
		curfind = content.function(i).identifier;
		cfunc = i; //(int)content.function(i).type;
		flen = curfind.length();
		fstart = 0;
		while (fstart >= 0) {
			fstart = tmps.find(curfind, fstart);
			if (fstart < 0) break;
			if (tmps[fstart + flen] != '(') {
				//currentString.insert(fstart + flen, "(");
				//return true;
				fstart++;
				continue;
			}
			for (int j = fstart; j < fstart + flen; j++) {
				elements[j].set(PIEvaluatorTypes::etFunction, cnum, cfunc);
				tmps.replace(j, 1, fc);
			}
			cnum++;
		}
	}
	cnum = 0;
	//qDebug().nospace() << "search for variables ...";
	for (int i = 0; i < content.variablesCount(); i++) {
		curfind = content.variable(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(PIEvaluatorTypes::etVariable, cnum, i);
				tmps.replace(j, 1, fc);
			}
			cnum++;
		}
	}
	curfind = "";
	cnum = 1;
	//qDebug().nospace() << "search for numbers ...";
	for (int i = 0; i < tmps.length(); i++) {
		cc = tmps[i];
		/*if (cc == " " || cc == "(" || cc == ")") {
			curfind = "";
			cpart = 0;
			numFound = false;
			continue;
		}*/
		switch (cpart) {
		case 0:
			if ((cc >= '0' && cc <= '9')) {// || cc == '-' || cc == '+') {
				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) {
			//qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble();
			currentVariables.push_back(PIEvaluatorTypes::Variable("tmp" + PIString::fromNumber(cnum), curfind.toDouble()));
			for (int j = i - curfind.length(); j < i; j++) {
				elements[j].set(PIEvaluatorTypes::etNumber, cnum, -cnum);
				tmps.replace(j, 1, fc);
			}
			curfind = "";
			cnum++;
			cpart = 0;
			numFound = false;
		}
	}
	if (cpart > 0) {
		//qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble();
		currentVariables.push_back(PIEvaluatorTypes::Variable("tmp" + PIString::fromNumber(cnum), curfind.toDouble()));
		for (int j = tmps.length() - curfind.length(); j < tmps.length(); j++) {
			elements[j].set(PIEvaluatorTypes::etNumber, cnum, -cnum);
			tmps.replace(j, 1, fc);
		}
	}
	cc = nc = fc;
	//qDebug().nospace() << "search for signes ...";
	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(PIEvaluatorTypes::etOperator, -1);
			continue;
		}
		if (cc == '-' || cc == '+') {
			elements[i].set(PIEvaluatorTypes::etOperator, -1);
			if (i < tmps.length() - 1) if (elements[i + 1].type == PIEvaluatorTypes::etVariable ||
										   elements[i + 1].type == PIEvaluatorTypes::etFunction) continue;
			if ((pc == '(' || isSign(pc) || i == 0) && i < tmps.length() - 1) {
				if (elements[i + 1].type != PIEvaluatorTypes::etOperator) {
					cnum = elements[i + 1].num;
					elements[i].set(PIEvaluatorTypes::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;
					//i++;
					continue;
				}
			}
		}
		if (isSign(cc)) {
			elements[i].set(PIEvaluatorTypes::etOperator, -1);
			continue;
		}
	}
	/*
	qDebug().nospace() << tmps;
	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;
	//for (int i = 0; i < currentVariables.size(); i++) qDebug() << "var " << i << ": " << currentVariables[i].value.real();
}


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 (elements[i].type == etOperator || elements[ni].type == etVariable) continue;
		if (fc == ',' || sc == ',') continue;
		if (elements[i].type == PIEvaluatorTypes::etOperator && elements[ni].type == PIEvaluatorTypes::etOperator) continue;
		if (fc == ')' && (elements[ni].type == PIEvaluatorTypes::etNumber || elements[ni].type == PIEvaluatorTypes::etVariable || elements[ni].type == PIEvaluatorTypes::etFunction)) needInsert = 1;
		if (sc == '(' && (elements[i].type == PIEvaluatorTypes::etNumber || elements[i].type == PIEvaluatorTypes::etVariable)) needInsert = 1;
		if (elements[i].type == PIEvaluatorTypes::etNumber && elements[ni].type == PIEvaluatorTypes::etNumber && elements[i].num != elements[ni].num) needInsert = 1;
		if (elements[i].type == PIEvaluatorTypes::etVariable && elements[ni].type == PIEvaluatorTypes::etVariable && elements[i].num != elements[ni].num) needInsert = 1;
		if ((elements[i].type == PIEvaluatorTypes::etNumber && elements[ni].type == PIEvaluatorTypes::etVariable) || (elements[i].type == PIEvaluatorTypes::etVariable && elements[ni].type == PIEvaluatorTypes::etNumber)) needInsert = 1;
		if ((elements[i].type == PIEvaluatorTypes::etNumber || elements[i].type == PIEvaluatorTypes::etVariable) && elements[ni].type == PIEvaluatorTypes::etFunction) needInsert = 1;
		if (elements[i].type == PIEvaluatorTypes::etFunction && elements[ni].type == PIEvaluatorTypes::etFunction && elements[i].num != elements[ni].num) needInsert = 2;
		if (elements[i].type == PIEvaluatorTypes::etFunction && elements[ni].type != PIEvaluatorTypes::etFunction && sc != '(') needInsert = 2;
		if (elements[pi].type == PIEvaluatorTypes::etOperator && (elements[ni].type == PIEvaluatorTypes::etFunction || elements[ni].type == PIEvaluatorTypes::etVariable) && fc == '-') needInsert = 3;
		switch (needInsert) {
		case 1:
			currentString.insert(ni + inserted, "*");
			elements.insert(ni + inserted, PIEvaluatorTypes::Element(PIEvaluatorTypes::etOperator, -1));
			//inserted++;
			//i++;
			return true;
		/*case 2:
			currentString.insert(ni + inserted, ")");
			currentString.insert(ni + inserted, "(");
			elements.insert(ni + inserted, Element(etOperator, -1));
			elements.insert(ni + inserted, Element(etOperator, -1));
			inserted++;
			i++;
			return true;*/
		case 3:
			currentString.insert(ni + inserted, "1*");
			elements.insert(ni + inserted, PIEvaluatorTypes::Element(PIEvaluatorTypes::etOperator, -1));
			//inserted;
			//i++;
			return true;
		}
	}
	/*if (elements[tmps.length() - 1].type == etFunction) {
		currentString.insert(tmps.length() + inserted, ")");
		currentString.insert(tmps.length() + inserted, "(");
		elements.insert(tmps.length() + inserted, Element(etOperator, -1));
		elements.insert(tmps.length() + inserted, Element(etOperator, -1));
		return true;
	}*/
	return false;
}


void PIEvaluator::convert() {
	int j;
	PIEvaluatorTypes::Element ce, pe;
	for (int i = 0; i < currentString.length(); i++) {
		pe = elements[i];
		if (pe.type != PIEvaluatorTypes::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);
		//i++;
	}
	for (int i = 0; i < currentString.length(); i++) {
		pe = elements[i];
		if (pe.type != PIEvaluatorTypes::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);
		//i++;
	}
	for (int i = 0; i < currentString.length(); i++) {
		pe = elements[i];
		if (pe.type != PIEvaluatorTypes::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);
		//i++;
	}
	/*qDebug().nospace() << currentString;
	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;*/
}



const PIString & PIEvaluator::preprocess(const PIString & string) {
	static PIString ret;
	int lind;
	ret = prepare(string);
	convert();
	instructions.clear();
	//qDebug() << preproc->currentString;
	variables = currentVariables;
	lind = parse(currentString);
	if (instructions.size() == 0) {
		variables.push_back(PIEvaluatorTypes::Variable());
		instructions.push_back(PIEvaluatorTypes::Instruction(PIEvaluatorTypes::oNone, PIVector<int>(1, lind), -variables.size_s()));
	}
	kvars = &(content.variables);
	/*
	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;
}


PIEvaluatorTypes::Operation PIEvaluator::operationInOrder(const int & index) {
	switch (index) {
	case 0: return PIEvaluatorTypes::oPower;
	case 1: return PIEvaluatorTypes::oMultiply;
	case 2: return PIEvaluatorTypes::oDivide;
	case 3: return PIEvaluatorTypes::oResidue;
	case 4: return PIEvaluatorTypes::oAdd;
	case 5: return PIEvaluatorTypes::oSubtract;
	case 6: return PIEvaluatorTypes::oEqual;
	case 7: return PIEvaluatorTypes::oNotEqual;
	case 8: return PIEvaluatorTypes::oGreaterEqual;
	case 9: return PIEvaluatorTypes::oSmallerEqual;
	case 10: return PIEvaluatorTypes::oGreater;
	case 11: return PIEvaluatorTypes::oSmaller;
	case 12: return PIEvaluatorTypes::oAnd;
	case 13: return PIEvaluatorTypes::oOr;
	default: return PIEvaluatorTypes::oNone;
	}
}


int PIEvaluator::parse(const PIString & string, int offset) {
	int slen = string.length(), /*facnt,*/ farg, bcnt, k;
	PIChar cc;
	PIEvaluatorTypes::Element ce;
	PIEvaluatorTypes::Function cfunc;
	PIEvaluatorTypes::Operation coper;
	PIString sbrackets, carg;
	PIVector<int> args, atmp;
	PIVector<PIEvaluatorTypes::Operation> opers;

	///qDebug() << "to parse :" + string;
	///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 PIEvaluatorTypes::etNumber:
			args.push_back(ce.var_num);
			continue;
		case PIEvaluatorTypes::etVariable:
			args.push_back(ce.var_num);
			continue;
		case PIEvaluatorTypes::etFunction:
			i++;
			cfunc = content.function(ce.var_num);
			//facnt = cfunc.arguments;
			atmp.clear();
			bcnt = farg = 1;
			///qDebug() << "function: " + cfunc.identifier;
			//for (int k = 0; k < facnt; k++) {
			bcnt = 1;
			carg = "";
			k = i + 1;
			//if (string.size_s() <= k || k < 0) return -666;
			while (bcnt > 0) {
				//if (k < facnt - 1) fcomma = string.indexOf(',', j);
				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 = "";
						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 = "";
						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(PIEvaluatorTypes::Variable());
				farg = -variables.size_s();
			}
			instructions.push_back(PIEvaluatorTypes::Instruction(PIEvaluatorTypes::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;
			//i = j + 1;
			continue;
		case PIEvaluatorTypes::etOperator:
			//qDebug() << "operator: " << cc;
			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(PIEvaluatorTypes::oAdd); continue;}
			if (cc == '-') {opers.push_back(PIEvaluatorTypes::oSubtract); continue;}
			if (cc == '*') {opers.push_back(PIEvaluatorTypes::oMultiply); continue;}
			if (cc == '/') {opers.push_back(PIEvaluatorTypes::oDivide); continue;}
			if (cc == '%') {opers.push_back(PIEvaluatorTypes::oResidue); continue;}
			if (cc == '^') {opers.push_back(PIEvaluatorTypes::oPower); continue;}
			if (cc == '=') {opers.push_back(PIEvaluatorTypes::oEqual); continue;}
			if (cc == ':') {opers.push_back(PIEvaluatorTypes::oNotEqual); continue;}
			if (cc == '}') {opers.push_back(PIEvaluatorTypes::oGreaterEqual); continue;}
			if (cc == '{') {opers.push_back(PIEvaluatorTypes::oSmallerEqual); continue;}
			if (cc == '>') {opers.push_back(PIEvaluatorTypes::oGreater); continue;}
			if (cc == '<') {opers.push_back(PIEvaluatorTypes::oSmaller); continue;}
			if (cc == '&') {opers.push_back(PIEvaluatorTypes::oAnd); continue;}
			if (cc == '|') {opers.push_back(PIEvaluatorTypes::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;
	}
	for (int i = 0; i < PIEvaluatorTypes::operationCount; i++) {
		coper = operationInOrder(i);
		for (int j = 0; j < opers.size_s(); j++) {
			if (coper == PIEvaluatorTypes::oDivide || coper == PIEvaluatorTypes::oMultiply) {
				if (opers[j] != PIEvaluatorTypes::oDivide && opers[j] != PIEvaluatorTypes::oMultiply) continue;
			} else {
				if (opers[j] != coper) 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(PIEvaluatorTypes::Variable());
					farg = -variables.size_s();
				}
			}
			instructions.push_back(PIEvaluatorTypes::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() {
	PIEvaluatorTypes::Instruction ci;
	bool error;
	if (unknownVars.size_s() > 0) {
		lastError = "Unknown variables: \"" + unknownVars.join("\", \"") + "\"";
		return false;
	}
	for (int i = 0; i < instructions.size_s(); i++) {
		error = false;
		ci = instructions[i];
		PIEvaluatorTypes::Function cf;
		int fac, gac;
		switch (ci.operation) {
		case PIEvaluatorTypes::oNone: break;
		case PIEvaluatorTypes::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) { //(ci.operators[j] < -variables.size_s() || ci.operators[j] >= kvars->size()) {
					error = true;
					gac--;
				}
			}
			if (fac > 0) {
				if (gac != fac) {
					lastError = "Invalid arguments count for function \"" + cf.identifier +
								"\", expected " + PIString::fromNumber(fac) + " but " +
								PIString::fromNumber(gac) + " given";
					return false;
				}
				if (error) {
					lastError = "Invalid at least one of function \"" + cf.identifier + "\" argument";
					return false;
				}
			}
			if (fac < 0) {
				if (gac < -fac) {
					lastError = "Invalid arguments count for function \"" + cf.identifier +
								"\", expected at least " + PIString::fromNumber(-fac) + " but " +
								PIString::fromNumber(gac) + " given";
					return false;
				}
				if (error) {
					lastError = "Invalid at least one of function \"" + cf.identifier + "\" argument";
					return false;
				}
			}
			break;
		default:
			if (ci.operators[0] == -666 || ci.operators[1] == -666) error = true;
			if (ci.operators.size_s() != 2 || error) {
				lastError = "Invalid arguments count for operation \" " + operationChar(ci.operation) + " \"";
				return false;
			}
			break;
		}
		if (ci.out < -variables.size_s()) {
			lastError = "Invalid variable index \"" + PIString::fromNumber(ci.out) + "\"";
			return false;
		}
		for (int j = 0; j < ci.operators.size_s(); j++) {
			if (ci.operators[j] < -variables.size_s() || ci.operators[j] >= kvars->size_s()) {
				lastError = "Invalid variable index \"" + PIString::fromNumber(ci.operators[j]) + "\"";
				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 PIEvaluatorTypes::Operation & operation) {
	switch (operation) {
	case PIEvaluatorTypes::oAdd: return "+";
	case PIEvaluatorTypes::oSubtract: return "-";
	case PIEvaluatorTypes::oMultiply: return "*";
	case PIEvaluatorTypes::oDivide: return "/";
	case PIEvaluatorTypes::oPower: return "^";
	case PIEvaluatorTypes::oResidue: return "%";
	case PIEvaluatorTypes::oEqual: return "=";
	case PIEvaluatorTypes::oNotEqual: return ("≠");
	case PIEvaluatorTypes::oGreaterEqual: return ("≥");
	case PIEvaluatorTypes::oSmallerEqual: return ("≤");
	case PIEvaluatorTypes::oGreater: return ">";
	case PIEvaluatorTypes::oSmaller: return "<";
	case PIEvaluatorTypes::oAnd: return ("⋀");
	case PIEvaluatorTypes::oOr: return ("⋁");
	default: return "???";
	}
}


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 PIEvaluatorTypes::Instruction & ci) {
	PIEvaluatorTypes::Function cfunc = content.function(ci.function);
	int oi = -ci.out - 1;
	complexd tmp, stmp, ttmp;
	//qDebug() << "function " << (int)cfunc.type;
	switch (cfunc.type) {
	case PIEvaluatorTypes::bfSin:
		tmpvars[oi].value = sin(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfCos:
		tmpvars[oi].value = cos(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfTg:
		tmpvars[oi].value = tan(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfCtg:
		tmp = tan(value(ci.operators[0]));
		if (tmp == complexd_0) tmpvars[oi].value = 0.;
		else tmpvars[oi].value = complexd_1 / tmp;
		break;
	case PIEvaluatorTypes::bfArcsin:
		tmpvars[oi].value = asinc(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfArccos:
		tmpvars[oi].value = acosc(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfArctg:
		tmpvars[oi].value = atanc(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfArcctg:
		tmp = atanc(value(ci.operators[0]));
		if (tmp == complexd_0) tmpvars[oi].value = 0.;
		else tmpvars[oi].value = complexd_1 / tmp;
		break;
	case PIEvaluatorTypes::bfSh:
		tmpvars[oi].value = sinh(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfCh:
		tmpvars[oi].value = cosh(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfTh:
		tmpvars[oi].value = tanh(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfCth:
		tmp = tanh(value(ci.operators[0]));
		if (tmp == complexd_0) tmpvars[oi].value = 0.;
		else tmpvars[oi].value = complexd_1 / tmp;
		break;
	case PIEvaluatorTypes::bfAbs:
		tmpvars[oi].value = abs(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfSqrt:
		tmpvars[oi].value = sqrt(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfSqr:
		tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[0]);
		break;
	case PIEvaluatorTypes::bfExp:
		tmpvars[oi].value = exp(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfPow:
		tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
		break;
	case PIEvaluatorTypes::bfLn:
		tmpvars[oi].value = log(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfLg:
		tmpvars[oi].value = log10(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::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 PIEvaluatorTypes::bfRe:
		tmpvars[oi].value = value(ci.operators[0]).real();
		break;
	case PIEvaluatorTypes::bfIm:
		tmpvars[oi].value = value(ci.operators[0]).imag();
		break;
	case PIEvaluatorTypes::bfArg:
		tmpvars[oi].value = arg(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfLen:
		tmpvars[oi].value = abs(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfConj:
		tmpvars[oi].value = conj(value(ci.operators[0]));
		break;
	case PIEvaluatorTypes::bfSign:
		tmpvars[oi].value = value(ci.operators[0]).real() >= 0. ? complexd_1 : -complexd_1;
		break;
	case PIEvaluatorTypes::bfRad:
		tmpvars[oi].value = value(ci.operators[0]) * complexd(deg2rad, 0.);
		break;
	case PIEvaluatorTypes::bfDeg:
		tmpvars[oi].value = value(ci.operators[0]) * complexd(rad2deg, 0.);
		break;
	case PIEvaluatorTypes::bfJ0:
		tmpvars[oi].value = piJ0(value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::bfJ1:
		tmpvars[oi].value = piJ1(value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::bfJN:
		tmpvars[oi].value = piJn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::bfY0:
		tmpvars[oi].value = piY0(value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::bfY1:
		tmpvars[oi].value = piY1(value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::bfYN:
		tmpvars[oi].value = piYn(piRoundd(value(ci.operators[1]).real()), value(ci.operators[0]).real());
		break;
	case PIEvaluatorTypes::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 PIEvaluatorTypes::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 PIEvaluatorTypes::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 PIEvaluatorTypes::bfStep:
		tmpvars[oi].value = complexd(value(ci.operators[0]).real() >= value(ci.operators[1]).real() ? complexld_1 : complexld_0);
		break;
	case PIEvaluatorTypes::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 PIEvaluatorTypes::bfDefined:
		tmpvars[oi].value = value(ci.operators[0]).real() > 0. ? complexd_1 : complexd_0;
		break;
	case PIEvaluatorTypes::bfRandom:
		tmp = static_cast<ldouble>(rand()) / RAND_MAX;
		stmp = value(ci.operators[1]) - value(ci.operators[0]);
		tmpvars[oi].value = value(ci.operators[0]) + tmp * stmp;
		break;
	default: break;
	}
}


inline bool PIEvaluator::execInstructions() {
	PIEvaluatorTypes::Instruction ci;
	int oi;
	complexd tmp;
	tmpvars = variables;
	//cout << "var count " << tmpvars.size_s() << endl;
	for (int i = 0; i < instructions.size_s(); i++) {
		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 PIEvaluatorTypes::oAdd:
			tmpvars[oi].value = value(ci.operators[0]) + value(ci.operators[1]);
			break;
		case PIEvaluatorTypes::oSubtract:
			tmpvars[oi].value = value(ci.operators[0]) - value(ci.operators[1]);
			break;
		case PIEvaluatorTypes::oMultiply:
			tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[1]);
			break;
		case PIEvaluatorTypes::oDivide:
			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 PIEvaluatorTypes::oResidue:
			tmpvars[oi].value = residue(value(ci.operators[0]), value(ci.operators[1]));
			break;
		case PIEvaluatorTypes::oPower:
			tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1]));
			break;
		case PIEvaluatorTypes::oEqual:
			tmpvars[oi].value = value(ci.operators[0]) == value(ci.operators[1]);
			break;
		case PIEvaluatorTypes::oNotEqual:
			tmpvars[oi].value = value(ci.operators[0]) != value(ci.operators[1]);
			break;
		case PIEvaluatorTypes::oGreaterEqual:
			tmpvars[oi].value = value(ci.operators[0]).real() >= value(ci.operators[1]).real();
			break;
		case PIEvaluatorTypes::oSmallerEqual:
			tmpvars[oi].value = value(ci.operators[0]).real() <= value(ci.operators[1]).real();
			break;
		case PIEvaluatorTypes::oGreater:
			tmpvars[oi].value = value(ci.operators[0]).real() > value(ci.operators[1]).real();
			break;
		case PIEvaluatorTypes::oSmaller:
			tmpvars[oi].value = value(ci.operators[0]).real() < value(ci.operators[1]).real();
			break;
		case PIEvaluatorTypes::oAnd:
			tmpvars[oi].value = value(ci.operators[0]).real() > 0. && value(ci.operators[1]).real() > 0.;
			break;
		case PIEvaluatorTypes::oOr:
			tmpvars[oi].value = value(ci.operators[0]).real() > 0. || value(ci.operators[1]).real() > 0.;
			break;
		case PIEvaluatorTypes::oFunction:
			execFunction(ci);
			break;
		case PIEvaluatorTypes::oNone:
			tmpvars[oi].value = value(ci.operators[0]);
			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 = "Correct";
	return true;
}


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