/*
    KmPlot - a math. function plotter for the KDE-Desktop

    SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de>
    SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>

    This file is part of the KDE Project.
    KmPlot is part of the KDE-EDU Project.

    SPDX-License-Identifier: GPL-2.0-or-later

*/


// local includes
#include "parser.h"
#include "parseradaptor.h"
#include "settings.h"
#include "xparser.h"

//KDE includes
#include <KLocalizedString>
#include <KMessageBox>
#include <KConfig>

#include <QDebug>
#include <QList>

// standard c(++) includes
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <cmath>
#include <limits>
#include <locale.h>

double Parser::m_radiansPerAngleUnit = 0;

/**
 * List of predefined functions.
 * \note Some function names include other function names (e.g. "sinh" has the
 * string "sin" in it). The Parser will stop once it has found a matching
 * function name, so such functions must be in order of longest first.
 */
ScalarFunction Parser::scalarFunctions[ ScalarCount ]=
{
	// Hyperbolic trig
	{"sinh", QString(), sinh},	 				// Sinus hyperbolicus
	{"cosh", QString(), cosh}, 					// Cosinus hyperbolicus
	{"tanh", QString(), tanh},					// Tangens hyperbolicus
	{"arcsinh", "arsinh", asinh},		// Area-sinus hyperbolicus = inverse of sinh
	{"arccosh", "arcosh", acosh},		// Area-cosinus hyperbolicus = inverse of cosh
	{"arctanh", "artanh", atanh}, 		// Area-tangens hyperbolicus = inverse of tanh
	
	// Reciprocal-hyperbolic
	{"cosech", QString(), cosech},				// Co-Secans hyperbolicus
	{"sech", QString(), sech},					// Secans hyperbolicus
	{"coth", QString(), coth},					// Co-Tangens hyperbolicus
	{"arccosech", "arcosech", arcosech},// Area-co-secans hyperbolicus = inverse of cosech
	{"arcsech", "arsech", arsech},		// Area-secans hyperbolicus = inverse of sech
	{"arccoth", "arcoth", arcoth},		// Area-co-tangens hyperbolicus = inverse of coth
	
	// Reciprocal-trig
	{"cosec", QString(), lcosec},				// Co-Secans = 1/sin
	{"sec", QString(), lsec},					// Secans = 1/cos
	{"cot", QString(), lcot},					// Co-Tangens = 1/tan
	{"arccosec", "arcosech", larccosec},// Arcus co-secans = inverse of cosec
	{"arcsec", "arsec", larcsec},		// Arcus secans = inverse of sec
	{"arccot", "arcot", larccot},		// Arcus co-tangens = inverse of cotan
	
	// Trigonometric functions
	{"sin", QString(), lsin}, 					// Sinus
	{"cos", QString(), lcos}, 					// Cosinus
	{"tan", QString(), ltan}, 					// Tangens
	{"arcsin", QString(), larcsin}, 			// Arcus sinus = inverse of sin
	{"arccos", QString(), larccos}, 			// Arcus cosinus = inverse of cos
	{"arctan", QString(), larctan},				// Arcus tangens = inverse of tan
	
	// Other
	{"sqrt", QString(), sqrt},					// Square root
	{"sqr", QString(), sqr}, 					// Square
	{"sign", QString(), sign},					// Signum
	{"H", QString(), heaviside},				// Heaviside step function
	{"log", QString(), log10},					// Logarithm base 10
	{"ln", QString(), log}, 					// Logarithm base e
	{"exp", QString(), exp}, 					// Exponential function base e
	{"abs", QString(), fabs},					// Absolute value
	{"floor", QString(), floor},				// round down to nearest integer
	{"ceil", QString(), ceil},					// round up to nearest integer
	{"round", QString(), round},				// round to nearest integer
	{"gamma", QString(), tgamma},			// gamma function
	{"lgamma", QString(), lgamma},			// log-gamma function
	{"factorial", QString(), factorial},		// factorial
	{"erfc", QString(), lerfc},			// error function
	{"erf", QString(), lerf},			// complementary error function
	
	// legendre
	{"P_0", QString(), legendre0},				// lengedre polynomial (n=0)
	{"P_1", QString(), legendre1},				// lengedre polynomial (n=1)
	{"P_2", QString(), legendre2},				// lengedre polynomial (n=2)
	{"P_3", QString(), legendre3},				// lengedre polynomial (n=3)
	{"P_4", QString(), legendre4},				// lengedre polynomial (n=4)
	{"P_5", QString(), legendre5},				// lengedre polynomial (n=5)
	{"P_6", QString(), legendre6},				// lengedre polynomial (n=6)
};

VectorFunction Parser::vectorFunctions[ VectorCount ]=
{
	{"min", min},						// minimum of a set of reals
	{"max", max},						// maximum of a set of reals
	{"mod", mod},						// l2 modulus of a set of reals
};


/**
 * Order by longest string first, useful in parsing since we want to at each point
 * match the longest string first, so e.g. "sinh(x)" shouldn't be read as "sin(h) * x"
 */
class LengthOrderedString : public QString
{
	public:
		LengthOrderedString() {}
		LengthOrderedString( const QString & s ) : QString(s) {}
		
		bool operator <( const LengthOrderedString & other ) const
		{
			return (length() > other.length()) ||
					((length() == other.length()) && (static_cast<const QString &>(*this) < static_cast<const QString &>(other)));
		}
};



//BEGIN class Parser
Parser::Parser()
	: m_sanitizer( this )
{
	m_evalPos = 0;
	m_nextFunctionID = 0;
	m_stack = new double [STACKSIZE];
	stkptr = m_stack;
	m_constants = new Constants;
	
	m_error = 0;
	m_ownEquation = 0;
	m_currentEquation = 0;
}


Parser::~Parser()
{
	for ( Function * function : qAsConst(m_ufkt) )
		delete function;
	delete m_ownEquation;
	delete m_constants;
	delete [] m_stack;
}


QStringList Parser::predefinedFunctions( bool includeAliases ) const
{
	QStringList names;
	
	for ( int func = 0; func < ScalarCount; ++func )
	{
		names << scalarFunctions[func].name1;
		if ( includeAliases && !scalarFunctions[func].name2.isEmpty() )
			names << scalarFunctions[func].name2;
	}
			
	for ( int func = 0; func < VectorCount; ++func )
		names << vectorFunctions[func].name;
	
	return names;
}


QStringList Parser::userFunctions( ) const
{
	QStringList names;
	
	for ( Function * f : qAsConst(m_ufkt) )
	{
		for ( Equation * eq : qAsConst(f->eq) )
		{
			if ( !eq->name().isEmpty() )
				names << eq->name();
		}
	}
	
	names.sort();
	return names;
}


void Parser::reparseAllFunctions()
{
    for ( Function * f : m_ufkt )
    {
        for ( Equation * eq : f->eq )
            initEquation( eq );
    }
}


void Parser::setAngleMode( AngleMode mode )
{
	switch ( mode )
	{
		case Radians:
			m_radiansPerAngleUnit = 1.0;
			break;
			
		case Degrees:
			m_radiansPerAngleUnit = M_PI/180;	
			break;
	}
}


uint Parser::getNewId()
{
	uint i = m_nextFunctionID;
	while (1)
	{
		if ( !m_ufkt.contains( i ) )
		{
			m_nextFunctionID = i+1;
			return i;
		}
		++i;
	}
}

double Parser::eval( const QString & str, Error * error, int * errorPosition )
{
	Error t1;
	if ( ! error )
		error = & t1;
	int t2;
	if ( ! errorPosition )
		errorPosition = & t2;
	
	
	if ( !m_ownEquation )
		m_ownEquation = new Equation( Equation::Constant, 0 );
	
	QString fName = XParser::self()->findFunctionName( QStringLiteral("f"), -1 );
	
	QString eq = QStringLiteral( "%1=%2" ).arg( fName ).arg( str );
	if ( !m_ownEquation->setFstr( eq, (int*)error, errorPosition ) )
	{
		if ( errorPosition )
			*errorPosition -= fName.length()+1;
		return 0;
	}
	
	return fkt( m_ownEquation, Vector() );
}


double Parser::fkt(uint id, int eq, double x )
{
	if ( !m_ufkt.contains( id ) || m_ufkt[id]->eq.size() <= eq )
	{
		*m_error = NoSuchFunction;
		return 0;
	}
	
	return fkt( m_ufkt[id]->eq[eq], x );
}


double Parser::fkt( Equation * eq, double x )
{
	Function * function = eq->parent();
	Q_ASSERT_X( function->type() != Function::Differential, "Parser::fkt", "Do not use this function directly! Instead, call XParser::differential" );
	
	switch ( function->type() )
	{
		case Function::Cartesian:
		case Function::Parametric:
		case Function::Polar:
		{
			Vector var(2);
			var[0] = x;
			var[1] = function->k;
			
			return fkt( eq, var );
		}
			
		case Function::Implicit:
		{
			Vector var(3);
			
			// Can only calculate when one of x, y is fixed
			assert( function->m_implicitMode != Function::UnfixedXY );
			
			if ( function->m_implicitMode == Function::FixedX )
			{
				var[0] = function->x;
				var[1] = x;
			}
			else
			{
				// fixed y
				var[0] = x;
				var[1] = function->y;
			}
			var[2] = function->k;
			
			return fkt( eq, var );
		}
		
		case Function::Differential:
			return 0;
	}
	
	qWarning() << "Unknown function type!\n";
	return 0;
}


double Parser::fkt( Equation * eq, const Vector & x )
{
	if ( eq->mem.isEmpty() )
		return 0;
	
	// Consistency check: Make sure that we leave the stkptr at the same place
	// that we started it
	double * stkInitial = stkptr;
	
	double *pDouble;
	double (**pScalarFunction)(double);
	double (**pVectorFunction)(const Vector &);
	uint *pUint;
	eq->mptr = eq->mem.data();
	
	// Start with zero in our stackpointer
	// 
	*stkptr = 0;
	
	while(1)
	{
// 		qDebug() << "*eq->mptr: "<<int(*eq->mptr);
		
		switch(*eq->mptr++)
		{
			case KONST:
			{
				pDouble=(double*)eq->mptr;
				*stkptr=*pDouble++;
				eq->mptr=(char*)pDouble;
				break;
			}
				
			case VAR:
			{
				pUint = (uint*)eq->mptr;
				uint var = *pUint++;
				if ( int(var) >= x.size() )
				{
					// Assume variable has value zero
					*stkptr = 0;
				}
				else
					*stkptr = x[var];
				eq->mptr = (char*)pUint;
				break;
			}
				
			case PUSH:
			{
				++stkptr;
				break;
			}
			
			case PLUS:
			{
				stkptr[-1]+=*stkptr;
				--stkptr;
				break;
			}
			
			case MINUS:
			{
				stkptr[-1]-=*stkptr;
				--stkptr;
				break;
			}
			
			case GT:
			{
				stkptr[-1] = (*(stkptr-1) > *stkptr) ? 1 : 0;
				stkptr--;
				break;
			}
			
			case GE:
			{
				stkptr[-1] = (*(stkptr-1) >= *stkptr) ? 1 : 0;
				stkptr--;
				break;
			}
			
			case LT:
			{
				stkptr[-1] = (*(stkptr-1) < *stkptr) ? 1 : 0;
				stkptr--;
				break;
			}
			
			case LE:
			{
				stkptr[-1] = (*(stkptr-1) <= *stkptr) ? 1 : 0;
				stkptr--;
				break;
			}
				
			case PM:
			{
				pUint = (uint*)eq->mptr;
				uint whichPM = *pUint++;
				eq->mptr = (char*)pUint;
				
				assert( int(whichPM) < eq->pmSignature().size() );				
				bool plus = eq->pmSignature()[ whichPM ];
				
				if ( plus )
					stkptr[-1] += *stkptr;
				else
					stkptr[-1] -= *stkptr;
				
				--stkptr;
				break;
			}
			
			case MULT:
			{
				stkptr[-1]*=*stkptr;
				--stkptr;
				break;
			}
			
			case DIV:
			{
				if(*stkptr==0.)
					*(--stkptr)=HUGE_VAL;
				else
				{
					stkptr[-1]/=*stkptr;
					--stkptr;
				}
				break;
			}
			
			case POW:
			{
				stkptr[-1]=pow(*(stkptr-1), *stkptr);
				--stkptr;
				break;
			}
			
			case NEG:
			{
				*stkptr=-*stkptr;
				break;
			}
			
			case SQRT:
			{
				*stkptr = sqrt(*stkptr);
				break;
			}
			
			case FACT:
			{
				*stkptr = factorial(*stkptr);
				break;
			}
				
			case FKT_1:
			{
				pScalarFunction=(double(**)(double))eq->mptr;
				*stkptr=(*pScalarFunction++)(*stkptr);
				eq->mptr=(char*)pScalarFunction;
				break;
			}
				
			case FKT_N:
			{
				pUint = (uint*)eq->mptr;
				int numArgs = *pUint++;
				
				eq->mptr = (char*)pUint;
						
				pVectorFunction = (double(**)(const Vector &))eq->mptr;
				
				Vector args( numArgs );
				for ( int i=0; i < int(numArgs); ++i )
					args[i] = *(stkptr-numArgs+1+i);
				
				if ( numArgs > 0 )
					stkptr += 1-numArgs;
				*stkptr = (*pVectorFunction++)(args);
				
				eq->mptr = (char*)pVectorFunction;
				break;
			}
				
			case UFKT:
			{
				pUint=(uint*)eq->mptr;
				uint id = *pUint++;
				uint id_eq = *pUint++;
				
				// The number of arguments being passed to the function
				int numArgs = *pUint++;
				
				Vector args( numArgs );
				for ( int i=0; i<numArgs; ++i )
					args[i] = *(stkptr-numArgs+1+i);
				
				if ( m_ufkt.contains( id ) )
				{
					if ( numArgs > 0 )
						stkptr += 1-numArgs;
					*stkptr = fkt( m_ufkt[id]->eq[id_eq], args );
				}
				
				eq->mptr=(char*)pUint;
				break;
			}
			
			case ENDE:
			{
				// If the stack isn't where we started at, then we've gone
				// up / down the wrong number of places - definitely a bug (and
				// will lead to crashes over time as memory rapidly runs out).
				assert( stkptr == stkInitial );
				return *stkptr;
			}
			case ERROR:
			{
				// something went wrong due to a incorrect formular or
				// missing const.
				qDebug()  << "Error in equation " << eq->fstr();
				// Adjust stack again. Stack is wrong, only if we have a PUSH token
				// just before the ERROR token, afaik.
				while( stkptr != stkInitial )
				{
					stkptr--;
				}
				return *stkptr;
			}
		}
	}
}


int Parser::addFunction( const QString & str1, const QString & str2, Function::Type type, bool force )
{
	QString str[2] = { str1, str2 };
	
	Function * temp = new Function( type );
	temp->setId( getNewId() );
	
	for ( int i = 0; i < 2; ++i )
	{
		if ( str[i].isEmpty() || temp->eq.size() <= i )
			continue;
		
		int error;
		if ( !temp->eq[i]->setFstr( str[i], & error, 0, force ) && !force )
		{
			qDebug() << "could not set fstr to \""<<str[i]<<"\"!  error:"<<errorString(Error(error))<<"\n";
			delete temp;
			return -1;
		}
	
		bool duplicate = (fnameToID( temp->eq[i]->name() ) != -1);
		if ( temp->eq[i]->looksLikeFunction() && duplicate && !force )
		{
			qDebug() << "function name reused.\n";
			*m_error = FunctionNameReused;
			delete temp;
			return -1;
		}
	}
	
	m_ufkt[ temp->id() ] = temp;

	temp->plotAppearance( Function::Derivative0 ).color = XParser::self()->defaultColor( temp->id() );
	// Better use QRandomGenerator::global()->generate() instead of qrand() since Qt 5.10
	temp->plotAppearance( Function::Derivative1 ).color = QColor::fromRgb( qrand() );
	temp->plotAppearance( Function::Derivative2 ).color = QColor::fromRgb( qrand() );
	temp->plotAppearance( Function::Integral ).color = QColor::fromRgb( qrand() );

	emit functionAdded( temp->id() );
	return temp->id(); //return the unique ID-number for the function
}


void Parser::initEquation( Equation * eq, Error * error, int * errorPosition )
{
	Error t1;
	if ( ! error )
		error = & t1;
	int t2;
	if ( ! errorPosition )
		errorPosition = & t2;
	
	
	if ( eq->parent() )
		eq->parent()->clearFunctionDependencies();
	
	m_error = error;
	
	*m_error = ParseSuccess;
	*errorPosition = -1;
	
	m_currentEquation = eq;
	mem = & eq->mem;
	mptr = mem->data();
	m_pmAt = 0;
	
	m_eval = eq->fstr();
	m_sanitizer.fixExpression( & m_eval );
	m_evalRemaining = m_eval;
	m_evalPos = m_eval.indexOf( '=' ) + 1;
	heir0();
	
	if ( !evalRemaining().isEmpty() && *m_error == ParseSuccess )
		*m_error = SyntaxError;
	
	if ( *m_error != ParseSuccess )
	{
		*errorPosition = m_sanitizer.realPos( m_evalPos );
		qDebug() << "add an error token for " << eq->fstr();
		// add an error token and let the user decide
		addToken( ERROR );
	}
	addToken( ENDE );
}


bool Parser::removeFunction( Function * item )
{
	// Build up a list of functions that need to be removed is this function is removed
	QList<Function *> toRemove;
	QStringList otherRemoveNames;
	QList<Function *> newFunctions; // Added since the last iteration
	
	toRemove << item;
	newFunctions << item;
	
	while ( ! newFunctions.isEmpty() )
	{
		const QList<Function *> currentFunctions = newFunctions;
		newFunctions.clear();
		
		for ( Function *f : currentFunctions )
		{
			for ( Function *other : qAsConst(m_ufkt) )
			{
				if ( (other==f) || toRemove.contains(other) )
					continue;
				
				if ( other->dependsOn( f ) )
				{
					toRemove << other;
					otherRemoveNames << other->name();
					newFunctions << other;
				}
			}
		}
	}
	
	if ( toRemove.size() > 1 )
	{
		KGuiItem buttonContinue = KStandardGuiItem::cont();
		buttonContinue.setText( i18n("Remove all") );
		
		int answer = KMessageBox::warningContinueCancel( 0, i18n( "The function %1 is depended upon by the following functions: %2. These must be removed in addition.", item->name(), otherRemoveNames.join(", ") ), QString(), buttonContinue );
		
		if ( answer == KMessageBox::Cancel )
			return false;
	}
	
	for ( Function *f : qAsConst(toRemove) )
	{
		uint id = f->id();
		m_ufkt.remove( id );
		delete f;
		emit functionRemoved( id );
	}
	
	return true;
}

bool Parser::removeFunction(uint id)
{
	return m_ufkt.contains( id ) && removeFunction( m_ufkt[id] );
}


void Parser::removeAllFunctions()
{
	while ( !m_ufkt.isEmpty() )
	{
		Function *f = *m_ufkt.begin();
		int id = f->id();
		m_ufkt.remove( id );
		delete f;
		emit functionRemoved( id );
	}
}


uint Parser::countFunctions()
{
	return m_ufkt.count();
}

void Parser::heir0()
{
	heir1();
	
	if (*m_error!=ParseSuccess)
		return;
	
	while(1)
	{
		if ( m_eval.length() <= m_evalPos )
			return;
		
		QChar c = m_eval[m_evalPos];
		
		switch ( c.unicode() )
		{
			default:
				return;
				
			case '<':
			case '>':
			case 0x2264: // less than or equal
			case 0x2265: // greater than or equal
				++m_evalPos;
				addToken(PUSH);
				heir1();
				if ( *m_error != ParseSuccess )
					return;
		}
		switch ( c.unicode() )
		{
			case '<':
				addToken(LT);
				break;
				
			case '>':
				addToken(GT);
				break;
				
			case 0x2264: // less than or equal
				addToken(LE);
				break;
				
			case 0x2265: // greater than or equal
				addToken(GE);
				break;
		}
	}
}

void Parser::heir1()
{
	heir2();
	
	if (*m_error!=ParseSuccess)
		return;
	
	while(1)
	{
		if ( m_eval.length() <= m_evalPos )
			return;
		
		QChar c = m_eval[m_evalPos];
		
		switch ( c.unicode() )
		{
			default:
				return;

			case 0xb1:
				if ( m_pmAt >= MAX_PM )
				{
					*m_error = TooManyPM;
					return;
				}
				if ( m_currentEquation == m_ownEquation )
				{
					*m_error = InvalidPM;
					return;
				}
				// no break
				Q_FALLTHROUGH();
			case '+':
			case '-':
				++m_evalPos;
				addToken(PUSH);
				heir2();
				if(*m_error!=ParseSuccess)
					return;
		}
		switch ( c.unicode() )
		{
			case '+':
				addToken(PLUS);
				break;
				
			case '-':
				addToken(MINUS);
				break;
				
			case 0xb1:
				addToken(PM);
				adduint( m_pmAt++ );
				break;
		}
	}
}


void Parser::heir2()
{
	if ( match( SqrtSymbol ) ) // square root symbol
	{
		heir2();
		if(*m_error!=ParseSuccess)
			return;
		addToken(SQRT);
	}
	else
		heir3();
}


void Parser::heir3()
{
	QChar c;
	heir4();
	if(*m_error!=ParseSuccess)
		return;
	while(1)
	{
		if ( m_eval.length() <= m_evalPos )
			return;
		
		c = m_eval[m_evalPos];
		switch ( c.unicode() )
		{
			default:
				return;
			case '*':
			case '/':
				++m_evalPos;
				addToken(PUSH);
				heir4();
				if(*m_error!=ParseSuccess)
					return ;
		}
		switch ( c.unicode() )
		{
			case '*':
				addToken(MULT);
				break;
			case '/':
				addToken(DIV);
				break;
		}
	}
}


void Parser::heir4()
{
    if (match(QStringLiteral("-"))) {
        heir4();
        if (*m_error != ParseSuccess)
            return;
        addToken(NEG);
    } else if (match(QStringLiteral("+"))) {
        heir4();
    } else {
        heir5();
    }
}


void Parser::heir5()
{
	primary();
	if(*m_error!=ParseSuccess)
		return;
	
	while ( true )
	{
		if ( match(QStringLiteral("^")) )
		{
			addToken(PUSH);
			heir4();
			if(*m_error!=ParseSuccess)
				return;
			addToken(POW);
		}
		else if ( match(QStringLiteral("!")) )
			addToken(FACT);
		else
			return;
	}
}


void Parser::primary()
{
	// Notes:
	// - tryUserFunction has to go after tryVariable since differential
	//   equations treat the function name as a variable
	// - tryConstant has to go before tryUserFunction. This solves a problem,
	//   when a function and a constant share the same name
	
	tryFunction() || tryPredefinedFunction() || tryVariable() || tryConstant() || tryUserFunction() || tryNumber();
}


bool Parser::tryFunction()
{
	if ( !match(QStringLiteral("(")) && !match(QStringLiteral(",")) )
		return false;
	
	heir0();
	if ( !match(QStringLiteral(")")) && !match(QStringLiteral(",")) )
		*m_error = MissingBracket;
	return true;
}
	
	
bool Parser::tryPredefinedFunction()
{
	for ( int i=0; i < ScalarCount; ++i )
	{
		if ( match(scalarFunctions[i].name1) || match(scalarFunctions[i].name2) )
		{
			primary();
			addToken(FKT_1);
			addfptr(scalarFunctions[i].mfadr);
			return true;
		}
	}
	for ( int i=0; i < VectorCount; ++i )
	{
		if ( match(vectorFunctions[i].name) )
		{
			int argCount = readFunctionArguments();
			
			addToken(FKT_N);
			addfptr( vectorFunctions[i].mfadr, argCount );
			return true;
		}
	}
	
	return false;
}
	
	
bool Parser::tryVariable()
{
	const QStringList variables = m_currentEquation->variables();
	
	// Sort the parameters by size, so that when identifying parameters, want to
	// match e.g. "ab" before "a"
	typedef QMultiMap <int, QString> ISMap;
	ISMap sorted;
	for ( const QString &var : variables )
		sorted.insert( -var.length(), var );
	
	for ( const QString &var : qAsConst(sorted) )
	{
		if ( match( var ) )
		{
			addToken( VAR );
			adduint( variables.indexOf( var ) );
			return true;
		}
	}
	
	return false;
}
	

bool Parser::tryUserFunction()
{
	for ( Function * it : qAsConst(m_ufkt) )
	{
		for ( int i = 0; i < it->eq.size(); ++i )
		{
			if ( !match( it->eq[i]->name()) )
				continue;
			
			if ( it->eq[i] == m_currentEquation || (m_currentEquation && it->dependsOn( m_currentEquation->parent() )) )
			{
				*m_error = RecursiveFunctionCall;
				return true;
			}
			
			int argCount = readFunctionArguments();
			if ( argCount != it->eq[i]->variables().size() )
			{
				*m_error = IncorrectArgumentCount;
				return true;
			}
			
			addToken(UFKT);
			addfptr( it->id(), i, argCount );
			if ( m_currentEquation->parent() )
				m_currentEquation->parent()->addFunctionDependency( it );
			
			return true;
		}
	}
	
	return false;
}


bool Parser::tryConstant()
{
#define CHECK_CONSTANT( a, b ) \
	if ( match(a) ) \
	{ \
		addConstant( b ); \
		return true; \
	}
	
	ConstantList constants = m_constants->list( Constant::All );
	
	QMap<LengthOrderedString, Constant> orderedConstants;
	for ( ConstantList::iterator i = constants.begin(); i != constants.end(); ++i )
		orderedConstants[ i.key() ] = i.value();
	
	for ( QMap<LengthOrderedString, Constant>::iterator i = orderedConstants.begin(); i != orderedConstants.end(); ++i )
		CHECK_CONSTANT( i.key(), i.value().value.value() );
	
	// Or a predefined constant?
	CHECK_CONSTANT( "pi", M_PI );
	CHECK_CONSTANT( PiSymbol, M_PI );
	CHECK_CONSTANT( "e", M_E );
	CHECK_CONSTANT( InfinitySymbol, std::numeric_limits<double>::infinity());
	
	return false;
}


bool Parser::tryNumber()
{	
	QByteArray remaining = evalRemaining().toLatin1();
	char * lptr = remaining.data();
	char * p = 0;
	// we converted all to "C" format in fixExpression
	char *oldLocale = setlocale(LC_NUMERIC, "C");
	double const w = strtod(lptr, &p);
	setlocale(LC_NUMERIC, oldLocale);
	if( lptr != p )
	{
		m_evalPos += p-lptr;
		addConstant(w);
		return true;
	}
	
	return false;
}


int Parser::readFunctionArguments()
{
	if ( !evalRemaining().startsWith( '(' ) )
		return 0;
	
	int argCount = 0;
	bool argLeft = true;
	do
	{
		argCount++;
		primary();
					
		argLeft = m_eval.at(m_evalPos-1) == ',';
		if (argLeft)
		{
			addToken(PUSH);
			m_evalPos--;
		}
	}
	while ( *m_error == ParseSuccess && argLeft && !evalRemaining().isEmpty() );
	
	return argCount;
}


void Parser::growEqMem( int growth )
{
	int pos = mptr - mem->data();
	mem->resize( mem->size() + growth );
	mptr = mem->data() + pos;
}


void Parser::addToken( Token token )
{
	growEqMem( sizeof(Token) );
	*mptr++=token;
}


void Parser::addConstant(double x)
{
	addToken(KONST);
	
	growEqMem( sizeof(double) );
	double *pd=(double*)mptr;
	
	*pd++=x;
	mptr=(char*)pd;
}


void Parser::adduint(uint x)
{
	growEqMem( sizeof(uint) );
	uint *p=(uint*)mptr;
	*p++=x;
	mptr=(char*)p;
}


void Parser::addfptr( double(*fadr)(double) )
{
	typedef double(**sfPtr)(double);
	
	growEqMem( sizeof(sfPtr) );
// 	double (**pf)(double)=(double(**)(double))mptr;
	
	sfPtr pf = (sfPtr)mptr;
	*pf++=fadr;
	mptr=(char*)pf;
}


void Parser::addfptr( double(*fadr)(const Vector &), int argCount )
{
	typedef double(**vfPtr)(const Vector &);
	
	growEqMem( sizeof(uint) );
	uint *p = (uint*)mptr;
	*p++ = argCount;
	mptr = (char*)p;
	
	growEqMem( sizeof(vfPtr) );
	vfPtr pf = (vfPtr)mptr;
	*pf++=fadr;
	mptr=(char*)pf;
}


void Parser::addfptr( uint id, uint eq_id, uint args )
{
	growEqMem( 3*sizeof(uint) );
	
	uint *p=(uint*)mptr;
	*p++=id;
	*p++=eq_id;
	*p++=args;
	mptr=(char*)p;
}


int Parser::fnameToID(const QString &name)
{
	for ( Function * it : qAsConst(m_ufkt) )
	{
		for ( Equation * eq : qAsConst(it->eq) )
		{
			if ( eq->looksLikeFunction() && (name == eq->name()) )
				return it->id();
		}
	}
	return -1;     // Name not found
}


// static
QString Parser::errorString( Error error )
{
	switch ( error )
	{
		case ParseSuccess:
			return QString();
			
		case SyntaxError:
			return i18n("Syntax error");
			
		case MissingBracket:
			return i18n("Missing parenthesis");
			
		case StackOverflow:
			return i18n("Stack overflow");
			
		case FunctionNameReused:
			return i18n("Name of function is not free");
			
		case RecursiveFunctionCall:
			return i18n("recursive function not allowed");
			
		case EmptyFunction:
			return i18n("Empty function");
			
		case NoSuchFunction:
			return i18n("Function could not be found");
			
		case ZeroOrder:
			return i18n("The differential equation must be at least first-order");
			
		case TooManyPM:
			return i18n("Too many plus-minus symbols");
			
		case InvalidPM:
			return i18n("Invalid plus-minus symbol (expression must be constant)");
			
		case TooManyArguments:
			return i18n("The function has too many arguments");
			
		case IncorrectArgumentCount:
			return i18n("The function does not have the correct number of arguments");
	}
	
	return QString();
}


void Parser::displayErrorDialog( Error error )
{	
	QString message( errorString(error) );
	if ( !message.isEmpty() )
		KMessageBox::sorry(0, message, QStringLiteral("KmPlot"));
}


QString Parser::evalRemaining()
{
	/// note changing this code may need to change code in match() as well; similar
	int newLength = qMax( 0, m_eval.length() - m_evalPos );
	if ( newLength != m_evalRemaining.length() )
		m_evalRemaining = m_eval.right( newLength );
	return m_evalRemaining;
}


bool Parser::match( const QString & lit )
{
	if ( lit.isEmpty() )
		return false;
	
	/// note changing this code may need to change code in evalRemaining() as well; similar
	// Do we need to update m_evalRemaining ?
	int newLength = qMax( 0, m_eval.length() - m_evalPos );
	if ( newLength != m_evalRemaining.length() )
		evalRemaining();
	
	if ( !m_evalRemaining.startsWith( lit ) )
		return false;
	
	m_evalPos += lit.length();
	return true;
}


Function * Parser::functionWithID( int id ) const
{
	return m_ufkt.contains( id ) ? m_ufkt[id] : 0;
}


// static
QString Parser::number( double value )
{
	QString str = QString::number( value, 'g', 16 );
	str.replace( 'e', QLatin1String("*10^") );
	return str;
}
//END class Parser


//BEGIN predefined mathematical functions
double sqr(double x) {
	return x*x;
}
double lsec(double x) {
	return (1 / cos(x*Parser::radiansPerAngleUnit()));
}
double lcosec(double x) {
	return (1 / sin(x*Parser::radiansPerAngleUnit()));
}
double lcot(double x) {
	return (1 / tan(x*Parser::radiansPerAngleUnit()));
}
double larcsec(double x) {
	return acos(1/x) / Parser::radiansPerAngleUnit();
}
double larccosec(double x) {
	return asin(1/x) / Parser::radiansPerAngleUnit();
}
double larccot(double x) {
	return (M_PI/2 - atan(x)) / Parser::radiansPerAngleUnit();
}
double sech(double x) {
	return (1 / cosh(x));
}
double cosech(double x) {
	return (1 / sinh(x));
}
double coth(double x) {
	return (1 / tanh(x));
}
double arsech(double x) {
	return acosh(1/x);
}
double arcosech(double x) {
	return asinh(1/x);
}
double arcoth(double x) {
	return atanh(1/x);
}
double lcos(double x) {
	return cos(x*Parser::radiansPerAngleUnit());
}
double lsin(double x) {
	return sin(x*Parser::radiansPerAngleUnit());
}
double ltan(double x) {
	return tan(x*Parser::radiansPerAngleUnit());
}
double larccos(double x) {
	return acos(x) / Parser::radiansPerAngleUnit();
}
double larcsin(double x) {
	return asin(x) / Parser::radiansPerAngleUnit();
}
double larctan(double x) {
	return atan(x) / Parser::radiansPerAngleUnit();
}
double factorial( double x ) {
	return tgamma(x+1);
}
double legendre0( double ) {
	return 1.0;
}
double legendre1( double x ) {
	return x;
}
double legendre2( double x ) {
	return (3*x*x-1)/2;
}
double legendre3( double x ) {
	return (5*x*x*x - 3*x)/2;
}
double legendre4( double x ) {
	return (35*x*x*x*x - 30*x*x +3)/8;
}
double legendre5( double x ) {
	return (63*x*x*x*x*x - 70*x*x*x + 15*x)/8;
}
double legendre6( double x ) {
	return (231*x*x*x*x*x*x - 315*x*x*x*x + 105*x*x - 5)/16;
}

double sign(double x)
{
	if(x<0.)
		return -1.;
	else if(x>0.)
		return 1.;
	return 0.;
}

double heaviside( double x )
{
	if ( x < 0.0 )
		return 0.0;
	else if ( x > 0.0 )
		return 1.0;
	else
		return 0.5;
}

double min( const Vector & args )
{
	double best = HUGE_VAL;
	for ( int i=0; i < args.size(); ++i )
	{
		if ( args[i] < best )
			best = args[i];
	}
	
	return best;
}

double max( const Vector & args )
{
	double best = -HUGE_VAL;
	for ( int i=0; i < args.size(); ++i )
	{
		if ( args[i] > best )
			best = args[i];
	}
	
	return best;
}


double mod( const Vector & args )
{
	double squared = 0;
	for ( int i=0; i < args.size(); ++i )
		squared += args[i]*args[i];
	
	return std::sqrt( squared );
}

double lerf(double x) {
	return erf(x);
}

double lerfc(double x) {
	return erfc(x);
}

//END predefined mathematical functions



//BEGIN class ExpressionSanitizer
enum StringType { ConstantString, NumberString, UnknownLetter, FunctionString, Other };


ExpressionSanitizer::ExpressionSanitizer( Parser * parser )
	: m_parser( parser )
{
	m_str = 0l;
	m_decimalSymbol = QLocale().decimalPoint();
}


void ExpressionSanitizer::fixExpression( QString * str )
{
	m_str = str;
	
	m_map.resize( m_str->length() );
	for ( int i = 0; i < m_str->length(); ++i )
		m_map[i] = i;
	
	// greater-equal, less-equal with proper symbols
	// note that this must go before the next code for implicit equations, since
	// it removes the spurious equals signs
	replace( QStringLiteral(">="), GeSymbol );
	replace( QStringLiteral("<="), LeSymbol );
	
	// hack for implicit functions: change e.g. "y = x + 2" to "y - (x+2)" so
	// that they can be evaluated via equality with zero.
	if ( str->count( '=' ) > 1 )
	{
		int equalsPos = str->lastIndexOf( '=' );
		replace( equalsPos, 1, QStringLiteral("-(") );
		append( ')' );
	}
	
	stripWhiteSpace();
	
	// make sure all minus-like signs (including the actual unicode minus sign)
	// are represented by a dash (unicode 0x002d)
	QChar dashes[6] = { 0x2012, 0x2013, 0x2014, 0x2015, 0x2053, 0x2212 };
	for ( unsigned i = 0; i < 6; ++i )
		replace( dashes[i], '-' );
	
	// replace the proper unicode divide sign by the forward-slash
	replace( QChar( 0xf7 ), '/' );
	replace( QChar( 0x2215 ), '/' );
	
	// replace the unicode middle-dot for multiplication by the star symbol
	replace( QChar( 0xd7 ), '*' );
	replace( QChar( 0x2219 ), '*' );
	
	// minus-plus symbol to plus-minus symbol
	replace( QChar( 0x2213 ), PmSymbol );
	
	// various power symbols
	replace( QChar(0x00B2), QStringLiteral("^2") );
	replace( QChar(0x00B3), QStringLiteral("^3") );
	replace( QChar(0x2070), QStringLiteral("^0") );
	replace( QChar(0x2074), QStringLiteral("^4") );
	replace( QChar(0x2075), QStringLiteral("^5") );
	replace( QChar(0x2076), QStringLiteral("^6") );
	replace( QChar(0x2077), QStringLiteral("^7") );
	replace( QChar(0x2078), QStringLiteral("^8") );
	replace( QChar(0x2079), QStringLiteral("^9") );
	
	// fractions
	replace( QChar(0x00BC), QStringLiteral("(1/4)") );
	replace( QChar(0x00BD), QStringLiteral("(1/2)") );
	replace( QChar(0x00BE), QStringLiteral("(3/4)") );
	replace( QChar(0x2153), QStringLiteral("(1/3)") );
	replace( QChar(0x2154), QStringLiteral("(2/3)") );
	replace( QChar(0x2155), QStringLiteral("(1/5)") );
	replace( QChar(0x2156), QStringLiteral("(2/5)") );
	replace( QChar(0x2157), QStringLiteral("(3/5)") );
	replace( QChar(0x2158), QStringLiteral("(4/5)") );
	replace( QChar(0x2159), QStringLiteral("(1/6)") );
	replace( QChar(0x215a), QStringLiteral("(5/6)") );
	replace( QChar(0x215b), QStringLiteral("(1/8)") );
	replace( QChar(0x215c), QStringLiteral("(3/8)") );
	replace( QChar(0x215d), QStringLiteral("(5/8)") );
	replace( QChar(0x215e), QStringLiteral("(7/8)") );
	
	//BEGIN replace e.g. |x+2| with abs(x+2)
	str->replace( AbsSymbol, '|' );
	
	int maxDepth = str->count( '(' );
	QVector<bool> absAt( maxDepth+1 );
	for ( int i = 0; i < maxDepth+1; ++i )
		absAt[i] = false;
	
	int depth = 0;
	
	for ( int i = 0; i < str->length(); ++i )
	{
		if ( str->at(i) == '|' )
		{
			if ( absAt[depth] )
			{
				// Closing it
				replace( i, 1, QStringLiteral(")") );
				absAt[depth] = false;
			}
			else
			{
				// Opening it
				replace( i, 1, QStringLiteral("abs(") );
				i += 3;
				absAt[depth] = true;
			}
		}
		else if ( str->at(i) == '(' )
			depth++;
		else if ( str->at(i) == ')' )
		{
			depth--;
			if ( depth < 0 )
				depth = 0;
		}
	}
	//END replace e.g. |x+2| with abs(x+2)
	
	if (m_decimalSymbol == QLatin1String(","))
		str->replace(QRegularExpression(QStringLiteral("(\\d),(\\d)")), "\\1.\\2"); //if we use comma as a decimal separator it is reasonable to separate numbers in function arguments by comma with space
	else
		str->replace(m_decimalSymbol, QLatin1String(".")); //replace the locale decimal symbol with a '.' otherwise
	
	//BEGIN build up strings
	QMap< LengthOrderedString, StringType > strings;
	
	const QStringList predefinedFunctions = XParser::self()->predefinedFunctions( true );
	for ( const QString &f : predefinedFunctions )
		strings[f] = FunctionString;
	
	for ( Function * it : qAsConst(m_parser->m_ufkt) )
	{
		for ( Equation * eq : qAsConst(it->eq) )
			strings[eq->name()] = FunctionString;
	}
	
	const QStringList constantNames = m_parser->constants()->names();
	for ( const QString &name : constantNames )
		strings[name] = ConstantString;
	strings[QStringLiteral("pi")] = ConstantString;
	//END build up strings
	
	strings.remove( QString() );
	
	StringType prevType = Other;
	
	for(int i=1; i < str->length(); )
	{
		int incLength = 1;
		StringType currentType = Other;
		QChar ch = str->at(i);
		
		QString remaining = str->right( str->length() - i );
		
		QMap< LengthOrderedString, StringType >::const_iterator end = strings.constEnd();
		for ( QMap< LengthOrderedString, StringType >::const_iterator it = strings.constBegin(); it != end; ++it )
		{
			if ( !remaining.startsWith( it.key() ) )
				continue;
			
			currentType = it.value();
			incLength = it.key().length();
			break;
		}
		
		if ( currentType == Other )
		{
			if ( ch.isNumber() || (ch == '.') )
				currentType = NumberString;
		}
		
		if ( currentType == Other )
		{
			if ( ch.isLetter() )
				currentType = UnknownLetter; // probably a variable
		}
		
		if ( ((currentType == FunctionString) || (ch == '(') || (currentType == UnknownLetter) || (currentType == ConstantString)) &&
					 ((prevType == NumberString) || (prevType == ConstantString) || (prevType == UnknownLetter) || (str->at(i-1) == ')')) )
		{
			insert( i, '*' );
			incLength++;
		}
		
		prevType = currentType;
		i += incLength;
	}
}


void ExpressionSanitizer::stripWhiteSpace()
{
	int i = 0;
	
	while ( i < m_str->length() )
	{
		if ( m_str->at(i).isSpace() )
		{
			m_str->remove( i, 1 );
			m_map.remove( i, 1 );
		}
		else
			i++;
	}
}


void ExpressionSanitizer::remove( const QString & str )
{
	int at = 0;
	
	do
	{
		at = m_str->indexOf( str, at );
		if ( at != -1 )
		{
			m_map.remove( at, str.length() );
			m_str->remove( at, str.length() );
		}
	}
	while ( at != -1 );
}


void ExpressionSanitizer::remove( const QChar & str )
{
	remove( QString(str) );
}


void ExpressionSanitizer::replace( QChar before, QChar after )
{
	m_str->replace( before, after );
}


void ExpressionSanitizer::replace( QChar before, const QString & after )
{
	if ( after.isEmpty() )
	{
		remove( before );
		return;
	}
	
	int at = 0;
	
	do
	{
		at = m_str->indexOf( before, at );
		if ( at != -1 )
		{
			int to = m_map[ at ];
			for ( int i = at + 1; i < at + after.length(); ++i )
				m_map.insert( i, to );
			
			m_str->replace( at, 1, after );
			at += after.length() - 1;
		}
	}
	while ( at != -1 );
}


void ExpressionSanitizer::replace( int pos, int len, const QString & after )
{
	int before = m_map[pos];
	m_map.remove( pos, len );
	m_map.insert( pos, after.length(), before );
	m_str->replace( pos, len, after );
}


void ExpressionSanitizer::replace( const QString & before, const QString & after )
{
	int index;
	while ( (index = m_str->indexOf(before)) > -1 )
		replace( index, before.length(), after );
}


void ExpressionSanitizer::insert( int i, QChar ch )
{
	m_map.insert( i, m_map[i] );
	m_str->insert( i, ch );
}


void ExpressionSanitizer::append( QChar str )
{
	m_map.insert( m_map.size(), m_map[ m_map.size() - 1 ] );
	m_str->append( str );
}


int ExpressionSanitizer::realPos( int evalPos )
{
	if ( m_map.isEmpty() || (evalPos < 0) )
		return -1;
	
	if ( evalPos >= m_map.size() )
	{
// 		qWarning() << "evalPos="<<evalPos<<" is out of range.\n";
// 		return m_map[ m_map.size() - 1 ];
		return -1;
	}
	
	return m_map[evalPos];
}


void ExpressionSanitizer::displayMap( )
{
	QString out('\n');
	
	for ( int i = 0; i < m_map.size(); ++i )
		out += QStringLiteral("%1").arg( m_map[i], 3 );
	out += '\n';
	
	for ( int i = 0; i < m_str->length(); ++i )
		out += "  " + (*m_str)[i];
	out += '\n';
	
	qDebug() << out;
}
//END class ExpressionSanitizer
