/******************************************************************************
*		       							      *
* engine/calculate.c (part of rcalc)				       	      *
* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.		      *
*								       	      *
* 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 2 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, write to the Free Software	       	      *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	       	      *
*								       	      *
******************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <engine.h>		/* The calculation engine		*/
#include <output.h>		/* The output routines			*/
#include <debug.h>		/* Debugging functions.			*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#ifndef RCALC_TEXT_ONLY
 #include <gnome.h>
#else
 #include <glib.h>
 #include <libintl.h>
 #define _(String) gettext (String)
#endif

#ifdef USE_COMPILED_FUNCTIONS
#include <gmodule.h>
#endif

#include <locale.h>

/*****************************************************************************/

#include "engine-private.h"	/* TODO: Remove */

/*****************************************************************************/

/* Sizes of various items:
*/
#define MAX_EXPRESSION_LEN	1024	/* Maximum length of expression	     */
#define MAX_ITEMS		512	/* Maximum items per expression	     */
#define STACK_SIZE		512	/* Size of number/string stack	     */

/* Decimal point character from locale
*/
static char decimal;
static gdouble unknown;

gdouble evaluateFunction(gchar *formula, gdouble where);
/*****************************************************************************/

/* Table of unary functions.
*/
static double ufMinus	( double x );
double ufRound	( double x );
double ufSin	( double x );
double ufCos	( double x );
double ufTan	( double x );
double ufASin	( double x );
double ufACos	( double x );
double ufATan	( double x );
double ufFac  ( double x );

static FUNCTION DefaultFunction[NUM_DEFAULT_FUNCTIONS]=
{
   { "-",		ufMinus,	NULL	,"-(x)=-x"          },
	{ "int",	ufRound,        NULL    ,"int(x)"           },
	{ "abs",	fabs,	        NULL   	,"abs(x)"           },
	{ "ln",		log,	        NULL   	,"ln(x)"            },
	{ "log",	log10,	        NULL	,"log(x)"           },
	{ "sqrt",	sqrt,		NULL	,"sqrt(x)"          },
	{ "sin",	ufSin,		NULL	,"sin(x)"           },
	{ "cos",	ufCos,		NULL	,"cos(x)"           },
	{ "tan",	ufTan,		NULL	,"tan(x)"           },
	{ "asin",	ufASin,		NULL	,"asin(x)"          },
	{ "acos",	ufACos,		NULL	,"acos(x)"          },
	{ "atan",	ufATan,		NULL	,"atan(x)"          },
	{ "sinh",	sinh,		NULL	,"sinh(x)"          },
	{ "cosh",	cosh,		NULL	,"cosh(x)"          },
	{ "tanh",	tanh,		NULL	,"tanh(x)"          },
	{ "asinh",	asinh,		NULL	,"asinh(x)"         },
	{ "acosh",	acosh,		NULL	,"acosh(x)"         },
	{ "atanh",	atanh,		NULL	,"atanh(x)"         },
	{ "fac",	ufFac,		NULL	,"x!"         }
};

FUNCTION Function_0 = {"DEFAULT_FUNC",fabs, NULL, "UNSET"};

/*****************************************************************************/

int is_correct(char c)
{
 return  (isalnum( c ));
}

/* Initialise the engine. Basically discover what the decimal separator is.
*/
void rCalc_engine_Initialise( void )
{
	struct lconv *locale = localeconv();

	/* Confess to the hacked nature of this hack.
	*/
	if( strlen( locale->decimal_point )!=1 )
	{
		char *lang = getenv( "LANG" );
		char *lc_numeric = getenv( "LC_NUMERIC" );
		if( !lang )		lang = "<null>";
		if( !lc_numeric )	lc_numeric = "<null>";
		
		rc_printf( "Unfortunatly rcalc cannot handle numbers in your\n");
		rc_printf( "native format. This is being worked on...\n" );
		rc_printf( "\n" );
		rc_printf( "Please email phneutre@icculus.org and\n" );
		rc_printf( "quote the following:\n" );
		rc_printf( "\tVERSION       = `%s'\n", VERSION );
		rc_printf( "\tLANG          = `%s'\n", lang );
		rc_printf( "\tLC_NUMERIC    = `%s'\n", lc_numeric );
		rc_printf( "\tdecimal_point = `%s'\n", locale->decimal_point );
		rc_printf( "\n" );
		rc_printf( "Thank you.\n" );
		rc_printf( "\n" );
	}
	decimal = locale->decimal_point[0];
	rc_debug( RC_DBG_ENGINE, "decimal point character is `%c'.", decimal );
}

void rCalc_engine_setDefaultFunctions(RcEngine *engine)
{
  int i=0;
  engine->nFunctions = NUM_DEFAULT_FUNCTIONS;

  for (i=0; i<MAX_FUNCTIONS; i++) {
    if (i<NUM_DEFAULT_FUNCTIONS)
      engine->functions[i] = DefaultFunction[i];

    else engine->functions[i]=Function_0;
  }
}
	
/*****************************************************************************/

/* Observer functions for the table of functions
** These do not report ufMinus (unary minus). 
*/
int rCalc_engine_GetNumFunctions( void )
{
	return cfgCalc->nFunctions-1;
}
char *rCalc_engine_GetFunctionName( int i )
{
	return _(cfgCalc->functions[i+1].name);
}
char *rCalc_engine_GetFunctionExpr( int i )
{
	return _(cfgCalc->functions[i+1].expression);
}

/*****************************************************************************/

/* The functions themselves.
*/
static double ufMinus( double x )	{ return -x; }
double ufRound( double x )	{ return floor(x+0.5); }

static double DtoR = M_PI / 180.0;
static double RtoD = 180.0 / M_PI;

double ufSin( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? sin(x*DtoR) : sin(x);
}
double ufCos( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? cos(x*DtoR) : cos(x);
}
double ufTan( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? tan(x*DtoR) : tan(x);
}
double ufASin( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? RtoD*asin(x) : asin(x);
}
double ufACos( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? RtoD*acos(x) : acos(x);
}
double ufATan( double x )
{
	return cfgCalc->angleMode==RC_ANGLE_DEGREES ? RtoD*atan(x) : atan(x);
}

double ufFac ( double x )
{
	return (!(int)x) ? 1 : (abs( (int) x ) * ufFac (abs( (int)x )-1));
}

/*****************************************************************************/

/* Table of builtin constants.
*/
#define NUM_CONSTANTS	2
static VARIABLE Constant[NUM_CONSTANTS]=
{
	{ "pi",		M_PI },
	{ "e",		M_E  },
};

/*****************************************************************************/

/* Observer functions for the table of constants.
*/
int rCalc_engine_GetNumConstants( void )
{
	return NUM_CONSTANTS;
}
char *rCalc_engine_GetConstantName( int i )
{
	return _(Constant[i].name);
}
double rCalc_engine_GetConstantValue( int i )
{
	return Constant[i].value;
}

/*****************************************************************************/

/* Handlers for variables
*/
gboolean validVariableName( char *name )
{
	int len,num,i;
	FUNCTION *Function  = cfgCalc->functions;
	int	 nFunctions = cfgCalc->nFunctions;

	len = strlen(name);
	if( len<1 )
	{
		/* Engine | Bad variable name */
		rc_error( _("variable name has zero length.") );
		return FALSE;
	}
	
	else if( len>MAX_VARIABLE_NAME_LEN )
	{
		/* Engine | Bad variable name */
		rc_error( _("%s: variable name too long (>%d characters)."),
			name, MAX_VARIABLE_NAME_LEN );
		return FALSE;
	}
	
	else if( !strcasecmp( name, cfgCalc->func_var_name ) ) {
	  rc_error( _("%s: variable name reserved for functions."),
		       name );
		return FALSE;
	}
	
	else if( !isalpha( name[0] ) )
	{
		/* Engine | Bad variable name */
		rc_error( _("%s: variable name must start with a letter."),
		       name );
		return FALSE;
	}

	for( i=1; i<len; i++ )
	{
		if( is_correct(name[i]) ) continue;

		/* Engine | Bad variable name */
		rc_error( _("%s: variable name must be alphanumeric."), name );
		return FALSE;
	}

	num = rCalc_engine_GetNumCommands();
	for( i=0; i<num; i++ )
	{
		if( strcasecmp( name, rCalc_engine_GetCommandName( i ) ) )
			continue;
		/* Engine | Bad variable name */
		rc_error( _("%s: variable name is a reserved word."), name );
		return FALSE;
	}
	for( i=0; i<NUM_DEFAULT_FUNCTIONS; i++ )
	{
		if( strcasecmp( name, Function[i].name ) ) continue;

		/* Engine | Bad variable name */
		rc_error( _("%s: variable name is a reserved word."), name );
		return FALSE;
	}
	for( i=0; i<NUM_CONSTANTS; i++ )
	{
		if( strcasecmp( name, Constant[i].name ) ) continue;

		/* Engine | Bad variable name */
		rc_error( _("%s: variable name is a reserved word."), name );
		return FALSE;
	}

	return TRUE;
}

static gboolean setVariableValue( char *name, double value )
{
	VARIABLE *Variable = cfgCalc->variables;
	int i;

	/* Check that its name is valid
	*/
	if( !validVariableName(name) ) return FALSE;

	/* If it already exists then overwrite it.
	*/
	for( i=0; i<cfgCalc->nVariables; i++ )
	{
		if( !strcasecmp( Variable[i].name, name) )
		{
			rc_debug( RC_DBG_ENGINE,
				  "%s: overwriting variable.", name );
			strcpy( Variable[i].name, name );
			Variable[i].value = value;
			/*fparser_remove_user_variable(Variable[i].name, 
			  Variable[i].value);
			fparser_add_user_variable(name, value);*/

			/*cfgCalc->variables_mod = TRUE;*/
			
			return TRUE;
		}
	}

	/* It doesn't exist, so create a new one.
	*/
	rc_debug( RC_DBG_ENGINE, "%s: creating new variable.", name );
	if( cfgCalc->nVariables == MAX_VARIABLES )
	{
		/* Engine | too many variables! */
		rc_error( _("out of variable storage space.") );
		return FALSE;
	}
	
	strcpy( Variable[cfgCalc->nVariables].name, name );
	Variable[cfgCalc->nVariables].value = value;
	/*fparser_add_user_variable(name, value);*/
	cfgCalc->nVariables++;
	
	/*cfgCalc->variables_mod = TRUE;*/

	return TRUE;
}

static gboolean getVariableValue( char *name, double *value_p )
{
	VARIABLE *Variable  = cfgCalc->variables;
	int	 nVariables = cfgCalc->nVariables;
	int i;

	/* Check that its name is valid
	*/
	if( !validVariableName(name) ) return FALSE;

	/* Find the variable.
	*/
	for( i=0; i<nVariables; i++ )
	{
		if( !strcasecmp(Variable[i].name,name) )
		{
			*value_p = Variable[i].value;
			return TRUE;
		}
	}
	/* Engine | The specified variable does not exist */
	rc_error( _("%s: undefined variable."), name );
	return FALSE;
}

/*****************************************************************************/

/* Stack handler - vital for expression handling.
** infixToPostfix() uses the stack to store pointers to strings,
** and evaluatePostfix() uses it to store numbers.
*/
typedef union
{
	char	*Str;
	double	 Num;
}
StackItem;


typedef struct {
  StackItem stack_p[STACK_SIZE];
  int stack_i;
} Stack_t;


int stackSize( Stack_t *stack )
{
  g_assert(stack);
  return stack->stack_i;
}

void resetStack( Stack_t *stack )
{
  g_assert(stack);
  stack->stack_i=0;
}

static gboolean pushString( char *str, Stack_t *stack )
{
  StackItem *stack_p = stack->stack_p;
  
	if( stack->stack_i>=STACK_SIZE )
	{
		/* This should *never* occur */
		rc_error( "out of stack space." );
		return FALSE;
	}
	else
	{
		stack_p[stack->stack_i].Str = str;
		stack->stack_i++;
		return TRUE;
	}
}
static gboolean pushNumber( double num, Stack_t *stack )
{
  StackItem *stack_p = stack->stack_p;
  
	if( stack->stack_i>=STACK_SIZE )
	{
		/* This should *never* occur */
		rc_error( "out of stack space." );
		return FALSE;
	}
	else
	{
		stack_p[stack->stack_i].Num = num;
		stack->stack_i++;
		return TRUE;
	}
}

static gboolean popString( char **str_p, Stack_t *stack )
{
  StackItem *stack_p = stack->stack_p;
	if( stack->stack_i <= 0 )
	{
		/* Engine | The expression contains some kind of error */
		rc_error( _("malformed expression.") );
		return FALSE;
	}
	else
	{
		stack->stack_i--;
		*str_p = stack_p[stack->stack_i].Str;
		return TRUE;
	}
}
static gboolean popNumber( double *num_p, Stack_t *stack )
{
  StackItem *stack_p = stack->stack_p;
	if( stack->stack_i <= 0 )
	{
	g_print("ici\n");
		rc_error( _("malformed expression.") );
		return FALSE;
	}
	else
	{
		stack->stack_i--;
		*num_p = stack_p[stack->stack_i].Num;
		return TRUE;
	}
}

static gboolean topString( char **str_p, Stack_t *stack )
{
 StackItem *stack_p = stack->stack_p;
  
	if( stack->stack_i <= 0 )
	{
		rc_error( _("malformed expression.") );
		return FALSE;
	}
	else
	{
		*str_p = stack_p[stack->stack_i-1].Str;
		return TRUE;
	}
}
/*  static gboolean topNumber( double *num_p ) */
/*  { */
/*  	if( stack_i <= 0 ) */
/*  	{ */
/*  		rc_error( _("malformed expression.") ); */
/*  		return FALSE; */
/*  	} */
/*  	else */
/*  	{ */
/*  		*num_p = stack_p[stack_i-1].Num; */
/*  		return TRUE; */
/*  	} */
/*  } */

/*****************************************************************************/

/* Global variables used in the evaluation functions
*/


/* Remove all whitespace from a string
*/
static void stripSpace( char *in )
{
	char *out=in;

	while( *in )
	{
		if( !isspace(*in) ) *out++=*in;
		in++;
	}
	*out=0;
}

/*****************************************************************************/

/* Read in one item, returning NULL if the end of the list is reached.
** If the item is one of ()^*+-/ then it is stored like that. Multi-
** character items are given a prefix ( [N]umber, [V]ariable, [C]onstant,
** unary [F]unction ) for ease of recognition.
** Warning: This function assumes that there is some space at the end
** of the string which it may also use.
*/
static gboolean readItem( char **p, char **last, int *nInfixItems, char **exprInfix )
{
	char *expr=*p;
	char *item=*p;
	char *sp,*ep;

	FUNCTION *Function  = cfgCalc->functions;
	int	 nFunctions = cfgCalc->nFunctions;

	/* Check for the end of the expression.
	*/
	if(*expr==0)
	{
		exprInfix[*nInfixItems]=NULL;
		*last = NULL;
		return TRUE;
	}

	/* Check we are not running out of room.
	*/
	if( *nInfixItems==MAX_ITEMS )
	{
		/* This should *never* occur */
		rc_error( "expresson contains too many terms (>%d).",
			  MAX_ITEMS );
		return FALSE;
	}

	/* Parse this next piece of expression.
	*/
	if( *expr=='-' )
	{
		/* Item is a minus
		*/
		int unary;

		/* Unary or binary?
		*/
		if(*last==NULL)		unary=1;
		else if(**last=='N')	unary=0;
		else if(**last=='C')	unary=0;
		else if(**last=='U')	unary=0;
		else if(**last=='V')	unary=0;
		else if(**last==')')	unary=0;
		else			unary=1;

		if( unary )		/* Convert to F- */
		{
			/* Insert the prefix
			*/
			for( sp=item, ep = sp+strlen(sp); ep>=sp; ep-- )
				*(ep+1)=*ep;
			*item='F';
			expr++;
		}
		expr++;
	}
	else if( strchr("()^/*+",*expr) )
	{
		/* Item is a single character operator (not minus - see above)
		*/
		expr++;
	}
	else if((*expr=='.')||(*expr==decimal)||(isdigit(*expr)))
	{
		/* Item is a number
		*/
		int d=0,e=0;

		if((*expr=='.')||(*expr==decimal))
		{
			d=1;
			*expr = decimal;
		}
		
		expr++;
		while( *expr!=0 )
		{
			if( (*expr=='e')||(*expr=='E') )
			{
				if(e)
				{
				/* Engine | A number is invalid in some way */
					rc_error(_("%s: malformed number."),*p );
					return FALSE;
				}
				else d=e=1;

				expr++;
				if(*expr==0)
				{
					rc_error(_("%s: malformed number."),*p );
					return FALSE;
				}
				if((*expr=='-')||(*expr=='+')) expr++;
				if(!isdigit(*expr))
				{
					rc_error(_("%s: malformed number."),*p );
					return FALSE;
				}
			}
			else if((*expr=='.')||(*expr==decimal))
			{
				*expr = decimal;
				if(d)
				{
					rc_error(_("%s: malformed number."),*p );
					return FALSE;
				}
				else d=1;
				expr++;
				if(!isdigit(*expr))
				{
					rc_error(_("%s: malformed number."),*p );
					return FALSE;
				}
			}
			else if( isdigit(*expr) )
			{
				expr++;
			}
			else break;
		}

		/* Add the N prefix
		*/
		for( sp=item, ep = sp+strlen(sp); ep>=sp; ep-- ) *(ep+1)=*ep;
		*item='N';
		expr++;
	}
	else if(is_correct(*expr)) 
	{
		/* Item is a variable / special operator / function
		*/
		int length,i;

		while( isalnum(*expr) ) expr++;
		length=expr-item;

		/* Make room for the prefix
		*/
		for( sp=item, ep = sp+strlen(sp); ep>=sp; ep-- ) *(ep+1)=*ep;
		expr++;

		/* Function, constant or variable?
		*/
		*item='V';
		
		for(i=0;i<nFunctions;i++)
		{
			if(strlen(Function[i].name)!=length)
				continue;
			if(strncasecmp(item+1,Function[i].name,length))
				continue;
			*item='F';
			break;
		}
		for(i=0;i<NUM_CONSTANTS;i++)
		{
			if(strlen(Constant[i].name)!=length)
				continue;
			if(strncasecmp(item+1,Constant[i].name,length))
				continue;
			*item='C';
			break;
		}
		
		if ((length == 1)&&(!strncasecmp(item+1,cfgCalc->func_var_name,length))) 
		  *item='U';
	}
	else
	{
		rc_error( _("malformed expression.") );
		return FALSE;
	}


	/* Split the string
	*/
	for( sp=expr, ep = sp+strlen(sp); ep>=sp; ep-- ) *(ep+1)=*ep;

	*sp = 0;	/* The end of this item	*/
	expr++;		/* The start of the next item */

	*p = expr;	/* 'this' item for next time round */
	*last = item;	/* 'last' item for next time round */

	exprInfix[(*nInfixItems)++] = item;

	return TRUE;
}

/*****************************************************************************/

/* Using the function readItem() above, split the expression into
** a NULL terminated array of items.
*/
static gboolean breakInfix(char **expression, char **exprInfix)
{
	int nInfixItems;
	char *expr,*c;
	gboolean s;

	/* Strip any whitespace
	*/
	stripSpace( *expression );
	if( !strlen(*expression) ) return FALSE;

	/* Break the expression
	*/
	for( c=NULL, nInfixItems=0, expr=*expression;
	     (s=readItem(&expr,&c,&nInfixItems, exprInfix)) ; )
	{
		if(!s) return FALSE;
		if(c==NULL) break;
	}
	if( nInfixItems<1 ) return FALSE;

	return TRUE;
}

/*****************************************************************************/

/* Hack alert! Wait for rCalc 0.3.0 for a nice fix */
static int priority_of( char operator )
{
	switch( operator )
	{
	case 'F': return 0;
	case '^': return 1;
	case '/': return 2;
	case '*': return 2;
	case '+': return 4;
	case '-': return 4;
	case '(': return 6;
	case ')': return 7;
	default:
		rc_error(_("internal error."));
		rc_debug( RC_DBG_ENGINE, "%c: illegal priority.", operator );
		return 0;
	}
}

/* Convert expression from infix (normal human style) to
** postfix (reverse polish). Basically, infix requires
** brackets to remove ambiguity, whereas postfix does not.
** In addition, postfix is very easy to evaluate using
** a computer. For more details, see:
** http://renoir.vill.edu/~cassel/1200/revpolish.html
*/
static gboolean infixToPostfix( Stack_t *stack, char **exprInfix, char **exprPostfix )
{
	char *Operators="F^/*+-()";
	char *item,*p,*q;
	char **in,**out;

	/* The stack should be empty - if not then we have a leak.
	*/
	if( stackSize(stack) )
	{
		rc_debug( RC_DBG_ENGINE, "data left on stack - clearing." );
		resetStack(stack);
	}

	/* Process the expression
	*/
	for( out=exprPostfix, in=exprInfix; *in; in++ )
	{
		item = *in;

		if( *item == '(' )
		{
			/* Push onto the stack
			*/
			if( !pushString( item, stack ) ) return(FALSE);
		}
		else if( *item == ')' )
		{
			/* Pop the stack to the output until '(' is found 
			*/
			do
			{
				if( !popString(&p, stack) ) return FALSE;
				if( *p != '(' ) *out++ = p;
			}
			while( *p != '(' );
		}
		else if( strchr("NHVCU", *item ) )
		{
			/* Operand - write to output string
			*/
			*out++ = item;
		}
		else if( (p=strchr( Operators, *item ))!=NULL )
		{
			int prio_p = priority_of( *p );
			int prio_q;
			
			/* Operator - pop higher priority items
			*/
			while(1)
			{
				if( !stackSize(stack)) break;
				if( !topString(&q, stack) ) return FALSE;
				prio_q = priority_of( *q );

				/* Low number = high priority */
				if( prio_q>prio_p ) break;

				if( !popString( out++, stack ) ) return FALSE;
			}
			if( !pushString( item, stack ) ) return FALSE;
		}
		else
		{
			/* Engine | Programmer error */
			rc_error( _("internal error.") );
			rc_debug( RC_DBG_ENGINE, "%s: illegal item.", item );
			return FALSE;
		}
	}

	/* Pop the remainder of the stack to the output string, and write the
	** terminating NULL.
	*/
	while( stackSize(stack)>0 ) popString( out++, stack );
	*out = NULL;

	return TRUE;
}

/*****************************************************************************/
gdouble getUnknownValue()
{
  return unknown;
}

void setUnknownValue(gdouble newvalue)
{
  unknown = newvalue;
}


/*****************************************************************************/

/* Evaluate the postfix expression.
*/
static gboolean evaluatePostfix( double *result_p, Stack_t *stack, char **exprPostfix )
{
	char **postfix;
	double x,y,res;
	int i;

	FUNCTION *Function  = cfgCalc->functions;
	int	 nFunctions = cfgCalc->nFunctions;


	/* The stack should be empty - if not then we have a leak.
	*/
	if( stackSize(stack) )
	{
		rc_debug( RC_DBG_ENGINE, "data left on stack - clearing." );
		resetStack(stack);
	}

	/* Evaluate the expression.
	*/
	for( postfix = exprPostfix; *postfix; postfix++ )
	{
		switch( *postfix[0] )
		{
			/* A number - push it
			*/
			case 'N':
				if( !pushNumber( atof( (*postfix)+1 ), stack ) )
					return FALSE;
				break;

			/* Item is a variable - push its value
			*/
			case 'V':
				if( !getVariableValue( (*postfix)+1, &x) )
					return FALSE;
				if( !pushNumber(x, stack) )
					return FALSE;
				break;

			/* Item is a constant - push its value
			*/
			case 'C':
				for( i=0; i<NUM_CONSTANTS; i++ )
				{
					if( strcasecmp( (*postfix)+1,
							Constant[i].name ))
						continue;
					
					if( !pushNumber( Constant[i].value, stack ) )
						return FALSE;
					break;
				}
				if( i==NUM_CONSTANTS )
				{
					rc_error( _("internal error.") );
					rc_debug( RC_DBG_ENGINE,
						  "%s: undefined constant.",
						  (*postfix)+1 );
					return FALSE;
				}
				break;

			/* Item is a unary function - evaluate it
			*/
			case 'F':
				for( i=0; i<nFunctions; i++ )
				{
					if( strcasecmp( (*postfix)+1,
							Function[i].name ))
						continue;
					
					if( !popNumber(&x, stack) )
						return FALSE;						
	
					/*if( !pushNumber(Function[i].func(x)) )
						return FALSE;*/

					if (i < NUM_DEFAULT_FUNCTIONS) {
					  if( !pushNumber(Function[i].func(x), stack) )
						return FALSE;
					}
					else {
					#ifdef USE_COMPILED_FUNCTIONS
					if( !pushNumber(Function[i].func(x), stack) )
						return FALSE;
					#else
					  /*if
                                             (parser_get_value(Function[i].expression, x, &res)==TRUE) {
					  if( !pushNumber(res))
					        return FALSE;
				          }
					  else {rc_error( _("internal error.")); return FALSE;} */
					  res = evaluateFunction(Function[i].expression, x);
					  if( !pushNumber(res, stack))
					        return FALSE;
					 
					#endif
					}
					
					break;
				}
				if( i==nFunctions )
				{
					rc_error( _("internal error.") );
					rc_debug( RC_DBG_ENGINE,
						  "%s: undefined function.",
					       (*postfix)+1 );
					return FALSE;
				}
				break;
				
			case 'U':
				x = getUnknownValue();
				if( !pushNumber(x, stack) )
					return FALSE;
				break;	

			/* Item is a binary function - evaluate it
			*/
			case '+':
				if( !popNumber(&y, stack) )	return FALSE;
				if( !popNumber(&x, stack) )	return FALSE;
				if( !pushNumber(x+y, stack) )	return FALSE;
				break;
			
			case '-':
				if( !popNumber(&y, stack) )	return FALSE;
				if( !popNumber(&x, stack) )	return FALSE;
				if( !pushNumber(x-y, stack) )	return FALSE;
				break;
			
			case '*':
				if( !popNumber(&y, stack) )	return FALSE;
				if( !popNumber(&x, stack) )	return FALSE;
				if( !pushNumber(x*y, stack) )	return FALSE;
				break;
			
			case '/':
				if( !popNumber(&y, stack) )	return FALSE;
				if( !popNumber(&x, stack) )	return FALSE;
				if( y==0.0 )
				{
					/* Engine | division by zero */
					rc_error( _("division by zero.") );
					return FALSE;
				}
				if( !pushNumber(x/y, stack) )	return FALSE;
				break;
			
			case '^':
				if( !popNumber(&y, stack) )	return FALSE;
				if( !popNumber(&x, stack) ) 	return FALSE;
				if( !pushNumber( pow(x,y), stack ) ) return FALSE;
				break;

			default:
				rc_error( _("internal error.") );
				rc_debug( RC_DBG_ENGINE,
					  "%d: illegal item.", *postfix[0] );
				return FALSE;
		}
		
	}

	/* Get the answer off the stack
	*/
	if( stackSize(stack)!=1 )
	{
		rc_error( _("malformed expression.") );
		rc_debug( RC_DBG_ENGINE,
			  "%d items on stack after evaluation.", stackSize(stack) );
		return FALSE;
	}
	popNumber(result_p, stack);
	return TRUE;
}

/*****************************************************************************/
gdouble evaluateFunction(gchar *formula, gdouble where)
{
  double result;
  Stack_t stack;
  char *lhs,*rhs;
  char *name;
  char  exprBuffer [MAX_EXPRESSION_LEN*2];
  char *expression;
  char *exprInfix  [MAX_ITEMS+1];
  char *exprPostfix[MAX_ITEMS+1];
  
 	setUnknownValue(where);
  
	stack.stack_i=0;
	
	/* Make a copy of the input string
	*/
	if( strlen(formula)>MAX_EXPRESSION_LEN )
	{
		/* Engine | the expression is too long */
		rc_error( _("expression too long (>%d characters)"),
		       MAX_EXPRESSION_LEN );
		return;
	}
	strcpy( exprBuffer, formula );
	formula = exprBuffer;

	if( (rhs = strchr(formula,'='))==NULL )
	{
		name	   = "Ans";
		expression = formula;
	}
	else
	{
		g_print("no, that's incorrect...\n");
		lhs = formula;
		*rhs = 0;
		rhs++;

		if( strchr( rhs,'=' ) )
		{
			/* Engine | too many '=' in expression */
			rc_error( _("too many equalities.") );
			return;
		}

		name 	   = lhs;
		expression = rhs;
	}


	/* Evaluate the expression and store the result.
	*/
	if( !breakInfix(&expression, exprInfix) ) return;
	if( !infixToPostfix(&stack, exprInfix, exprPostfix) )  return;
	if( !evaluatePostfix( &result, &stack, exprPostfix ) ) return;

	return result;
}

/* will be introduced in next version ! ;-) */
#if 0
gboolean verifyFunction(gchar *formula)
{
  gboolean result;
  Stack_t stack;
  char *lhs,*rhs;
  char *name;
  char  exprBuffer [MAX_EXPRESSION_LEN*2];
  char *expression;
  char *exprInfix  [MAX_ITEMS+1];
  char *exprPostfix[MAX_ITEMS+1];
  
 	setUnknownValue(0);
  
	stack.stack_i=0;
	
	/* Make a copy of the input string
	*/
	if( strlen(formula)>MAX_EXPRESSION_LEN )
	{
		/* Engine | the expression is too long */
		rc_error( _("expression too long (>%d characters)"),
		       MAX_EXPRESSION_LEN );
		return;
	}
	strcpy( exprBuffer, formula );
	formula = exprBuffer;

	if( (rhs = strchr(formula,'='))==NULL )
	{
		name	   = "Ans";
		expression = formula;
	}
	else
	  return FALSE;


	/* Evaluate the expression and store the result.
	*/
	if( !breakInfix(&expression, exprInfix) ) return FALSE;
	if( !infixToPostfix(&stack, exprInfix, exprPostfix) )  return FALSE;
	/*if( !evaluatePostfix( &result, &stack, exprPostfix ) ) return FALSE;*/

	return TRUE;
}
#endif

void rCalc_engine_Evaluate( char *input )
{
	char *lhs,*rhs;
	char *name;
	double result;
	Stack_t stack;
	char  exprBuffer [MAX_EXPRESSION_LEN*2];
	char *expression;
	char *exprInfix  [MAX_ITEMS+1];
	char *exprPostfix[MAX_ITEMS+1];

	stack.stack_i=0;
	
	/* Make a copy of the input string
	*/
	if( strlen(input)>MAX_EXPRESSION_LEN )
	{
		/* Engine | the expression is too long */
		rc_error( _("expression too long (>%d characters)"),
		       MAX_EXPRESSION_LEN );
		return;
	}
	strcpy( exprBuffer, input );
	input = exprBuffer;

	/* Remove all whitespace.
	*/
	stripSpace( input );

	/* Look for an equals sign.
	*/
	if( (rhs = strchr(input,'='))==NULL )
	{
		name	   = "Ans";
		expression = input;
	}
	else
	{
		lhs = input;
		*rhs = 0;
		rhs++;

		if( strchr( rhs,'=' ) )
		{
			/* Engine | too many '=' in expression */
			rc_error( _("too many equalities.") );
			return;
		}

		name 	   = lhs;
		expression = rhs;
	}

	/*printf("name=%s and expression=%s\n",name,expression);*/

	/* Evaluate the expression and store the result.
	*/
	if( !breakInfix(&expression, exprInfix) ) return;
	if( !infixToPostfix(&stack, exprInfix, exprPostfix) )  return;
	if( !evaluatePostfix( &result, &stack, exprPostfix ) ) return;
	if( !setVariableValue( name, result ) ) return;

	/* Print the result
	*/
	rc_printf("\t%s = %.16g\n", name, result );
}

/*** end of engine/calculate.c ***********************************************/






