/////////////////////////////////////////////////////////////////
//
//	elzedsample
//
//	Demonstration App for Elzed
//
//	Copyright (c) 2001-2010 by R R Le Cropane
//	All Rights Reserved
//
/////////////////////////////////////////////////////////////////

#ifdef WIN32
	#define _CRT_SECURE_NO_DEPRECATE

	#include <windows.h>
	#include <wtypes.h>
	#include <oleauto.h>
	#include <crtdbg.h>
	#include "LZElzedDLL.h"
	
	#define TEST_FILE_PATH_PREFIX	"./"
#endif

#ifdef MACOSX
	#define TEST_FILE_PATH_PREFIX	"./"
#endif

#ifdef LINUX
	#define TEST_FILE_PATH_PREFIX	"./"
#endif

#include <iostream>

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

#ifndef WIN32
	#include "LZElzedAPI.h"
#endif

using namespace std;

/////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////

char* GetInputString( char* cpTheInputLine, long lTheInputLineLength );

void CallbackDemo( void );
void CommandLine( void );
void ContextDemo( void );
void ExternalVariableTest( void );
void LifeDemo( void );
void RecursiveDemo( void );
void TimingTest( void );
void SelectDemo( void );
void ShowDataTypeSizes( void );

double DoTestExpression( double dFester, double dWazoo );

long ELZED_CALL_TYPE MyExternalFunction( LZObjectHandle );
long ELZED_CALL_TYPE MyOtherExternalFunction( LZObjectHandle );
void ELZED_CALL_TYPE MyErrorCallback( long lTheError );

/////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////

int main(int argc, char* argv[])
{
	char			theExpression[ 128 ];
	bool			keepLooping = true;
	long			lOption = 0;

	keepLooping = lzLoadElzed();

	lzInitialize( NULL );

	while ( keepLooping )
	{
		cout << '\n' << '\n';

		cout << " --------------------------------"			<< '\n';
		cout << "|||| Elzed Demonstration Menu ||||"		<< '\n';
		cout << " --------------------------------"			<< '\n';
		cout												<< '\n';
		cout << "10 -- Command Line"						<< '\n';
		cout << "20 -- External Operator Demo"				<< '\n';
		cout << "30 -- External Variable Demo"				<< '\n';
		cout << "40 -- Recursive Operator Demo"				<< '\n';
		cout << "50 -- Multiple Context Demo"				<< '\n';
		cout << "60 -- Select-Case Demo"					<< '\n';
		cout << "70 -- Performance Test"        			<< '\n';
		cout << "80 -- Data Type Sizes"         			<< '\n';
		cout << "90 -- Life Game Demo" 						<< '\n';
		cout												<< '\n';
		cout << "99 -- Quit"								<< '\n';
		cout												<< '\n';

		cout << "Selection ";		

		GetInputString( theExpression, 128 );
		
		lOption = atol( theExpression );

		switch ( lOption )
		{
		case 10:
			CommandLine();
			break;

		case 20:
			CallbackDemo();
			break;

		case 30:
			ExternalVariableTest();
			break;

		case 40:
			RecursiveDemo();
			break;

		case 50:
			ContextDemo();
			break;

		case 60:
			SelectDemo();
			break;

		case 70:
			TimingTest();
			break;
			
		case 80:
			ShowDataTypeSizes();
			break;

		case 90:
			LifeDemo();
			break;

		case 99:
			keepLooping = false;
		}
	}

	lzUnloadElzed();

	return 0;
}

char* GetInputString( char* cpTheInputLine, long lTheInputLineLength )
{
	char	cInputChar;
	long	lExpIndex = 0;
	long	lExpIndexMax = lTheInputLineLength - 1;

	if ( cpTheInputLine )
	{
		cout << "> ";
		cout.flush();

		while ( lExpIndex <= lExpIndexMax )
		{
			cInputChar = getchar();
			
			if ( cInputChar == '\n' )
				break;
			else
				cpTheInputLine[ lExpIndex++ ] = cInputChar;
		}
		
		cpTheInputLine[ lExpIndex ] = '\0';
	}
	
	return cpTheInputLine;
}

void CallbackDemo( void )
{
	char			theExpression[ 128 ];
	long			expLength;
	bool			keepLooping = true;

	LZObjectHandle	hCurrentContext = 0;
	bool			bSetContextResult = false;
	long			lDefineOpResult = 0;
	
	bool			bIsSymbol = false;
	bool			bIsOp = false;
	bool			bIsVar = false;

	lzCreatePublicContext( "CallbackDemo" );
	hCurrentContext = lzSetContext( "CallbackDemo", false );

	if ( !hCurrentContext )
	{
		cout << "Failed to set Elzed context.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
	}

	lDefineOpResult = lzLinkCallbackOp(	kGlobalScope,									// scope
										"xfunc",										// operator name
										"prefix",										// operator type
										"boolean",										// return type
										"number first, string second, boolean third",	// parm list
										"/",											// precedence matching operator
										MyExternalFunction );							// function pointer

	lDefineOpResult = lzLinkCallbackOp(	kGlobalScope,									// scope
										"yfunc",										// operator name
										"prefix",										// operator type
										"number",										// return type
										"number first, number second",					// variable parm list
										"^",											// precedence matching operator
										MyOtherExternalFunction );						// function pointer

	lDefineOpResult = lzDefineExpOp(	kGlobalScope,									// scope
										"zfunc",										// operator name
										"prefix",										// operator type
										"number",										// return type
										"number first, number second",					// parm list
										"/",											// precedence matching operator
										"first^second" );								// expression

	if ( lDefineOpResult )
	{
		cout << "Failed to load external operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
	}

	bIsSymbol = lzIsSymbol( kAnyScope, "+" );

	bIsSymbol = lzIsSymbol( kCosmicScope, "zfunc" );
	bIsSymbol = lzIsSymbol( kGlobalScope, "zfunc" );
	bIsSymbol = lzIsSymbol( kAnyScope, "zfunc" );

	bIsOp = lzIsOp( kCosmicScope, "zfunc" );
	bIsOp = lzIsOp( kGlobalScope, "zfunc" );
	bIsOp = lzIsOp( kAnyScope, "zfunc" );

	bIsVar = lzIsVar( kAnyScope, "sauron" );

	while ( keepLooping )
	{
		GetInputString( theExpression, 128 );

		expLength = strlen( theExpression );
		
		if ( expLength == 0 )
			keepLooping = false;

		else if ( (expLength == 1) && (theExpression[ 0 ] == '\r') )
			keepLooping = false;
			
		else if (theExpression[ 0 ] == '\\')
			keepLooping = false;

		else
		{
			cout << "  " << lzEvalExpStrToStr( theExpression ) << '\n';
			
			if ( lzGetLastErrorID() )
				cout << lzGetLastErrorText() << '\n';
				
			cout.flush();
		}
	}

	bSetContextResult = lzDestroyPublicContext( "CallbackDemo" );
}

void CommandLine( void )
{
	char	theExpression[ 1024 ];
	bool	keepLooping = true;

	lzClearContext();
	lzSetRadixMark( kRadixPeriod );
	lzSetAllowImpliedMult( false );
	//lzSetBooleanTrue( kTrueNegOne );

	lzCreatePrivateContext( "RadixTest" );
	lzSetContext( "RadixTest", false );

	lzSetCoordinateFormat( kCoordPolar );
	lzSetAngleUnit( kAngleDegrees );
	
	lzSetDefaultVariableScope( kGlobalScope );
	lzSetFormattedResultPrecisionLimit( 7 );

	lzSetErrorCallback( MyErrorCallback );

	while ( keepLooping )
	{
		GetInputString( theExpression, 1024 );
		
		if ( (theExpression[ 0 ] == '\r') || (theExpression[ 0 ] == '\0') || (theExpression[ 0 ] == '\\') )
			keepLooping = false;
		else
		{			
			cout << "  " << lzAnnotateExp( theExpression ) << '\n' << '\n';

			cout << "  " << lzEvalExpStrToStr( theExpression ) << '\n';

			//cout << "  " << lzEvalExpStrToNum( theExpression ) << '\n';
			//cout << "  " << lzEvalExpStrToBool( theExpression ) << '\n';

			//cout << "type:      " << lzGetLastResultType() << '\n';
			//cout << "imaginary: " << lzGetLastImagResult() << '\n';
			//cout << "real:      " << lzGetLastRealResult() << '\n';
			//cout << "numeric:   " << lzGetLastNumResult() << '\n';
			//cout << "string:    " << lzGetLastStrResult() << '\n';
			//cout << "boolean:   " << lzGetLastBoolResult() << '\n';
			
			if ( lzGetLastErrorID() )
			{
				cout << lzGetLastErrorText() << '\n';
				cout << lzGetLastParseIndex() << '\n';
			}
			else
			{
				cout << "  " << lzCheckExp( theExpression ) << '\n';
			}
				
			cout.flush();
		}

		lzClearErrorCallback();
	}

	lzClearContext();
	lzDestroyPrivateContext( "RadixTest" );

	lzReset();
}

void ContextDemo( void )
{
	char			theExpression[ 128 ];
	long			expLength;
	bool			keepLooping = true;
	bool			useFirstContext = true;

	// Make way...
	lzClearContext();

	// Set up the first context...
	lzSetAllowImpliedMult( false );
	lzSetOutputBase( kBase10 );
	lzCreatePrivateContext( "FirstContext" );

	// Make way...
	lzClearContext();

	// Set up the second context...
	lzSetAllowImpliedMult( true );
	lzSetOutputBase( kBase16 );
	lzCreatePrivateContext( "SecondContext" );

	while ( keepLooping )
	{
		if ( useFirstContext )
		{
			lzSetContext( "FirstContext", false );
			useFirstContext = false;
		}

		else
		{
			lzSetContext( "SecondContext", false );
			useFirstContext = true;
		}

		GetInputString( theExpression, 128 );

		expLength = strlen( theExpression );
		
		if ( expLength == 0 )
			keepLooping = false;

		else if ( (expLength == 1) && (theExpression[ 0 ] == '\r') )
			keepLooping = false;
			
		else if (theExpression[ 0 ] == '\\')
			keepLooping = false;

		else
		{
			cout << "  " << lzEvalExpStrToStr( theExpression ) << '\n';
			
			if ( lzGetLastErrorID() )
				cout << lzGetLastErrorText() << '\n';
				
			cout.flush();
		}
	}

	lzDestroyPrivateContext( "FirstContext" );
	lzDestroyPrivateContext( "SecondContext" );
}

void ExternalVariableTest( void )
{
	char			theExpression[ 128 ];
	long			expLength;
	bool			keepLooping = true;

	long			lResult = 0;
	bool			bResult = false;
	double			dResult = 0.0;
	double			dReal = 0.0;
	double			dImag = 0.0;
	char			cVarType = 'X';

	double			dFester = 14.0;
	double			dpOohBaby[ 3 ][ 3 ][ 3 ];

	double			dComplexFrodo[ 2 ] = { 22.0, -31.0 };
	double			dpComplexSauron[ 4 ][ 2 ];

	char			szGimli[ 16 ][ 256 ];
    
    dpOohBaby[ 0 ][ 0 ][ 0 ] = 0;
    dpOohBaby[ 0 ][ 0 ][ 1 ] = 1;
    dpOohBaby[ 0 ][ 0 ][ 2 ] = 2;
    
    dpOohBaby[ 0 ][ 1 ][ 0 ] = 10;
    dpOohBaby[ 0 ][ 1 ][ 1 ] = 11;
    dpOohBaby[ 0 ][ 1 ][ 2 ] = 12;
    
    dpOohBaby[ 0 ][ 2 ][ 0 ] = 20;
    dpOohBaby[ 0 ][ 2 ][ 1 ] = 21;
    dpOohBaby[ 0 ][ 2 ][ 2 ] = 22;
     
    dpOohBaby[ 1 ][ 0 ][ 0 ] = 100;
    dpOohBaby[ 1 ][ 0 ][ 1 ] = 101;
    dpOohBaby[ 1 ][ 0 ][ 2 ] = 102;
    
    dpOohBaby[ 1 ][ 1 ][ 0 ] = 110;
    dpOohBaby[ 1 ][ 1 ][ 1 ] = 111;
    dpOohBaby[ 1 ][ 1 ][ 2 ] = 112;
    
    dpOohBaby[ 1 ][ 2 ][ 0 ] = 120;
    dpOohBaby[ 1 ][ 2 ][ 1 ] = 121;
    dpOohBaby[ 1 ][ 2 ][ 2 ] = 122;
     
    dpOohBaby[ 2 ][ 0 ][ 0 ] = 200;
    dpOohBaby[ 2 ][ 0 ][ 1 ] = 201;
    dpOohBaby[ 2 ][ 0 ][ 2 ] = 202;
    
    dpOohBaby[ 2 ][ 1 ][ 0 ] = 210;
    dpOohBaby[ 2 ][ 1 ][ 1 ] = 211;
    dpOohBaby[ 2 ][ 1 ][ 2 ] = 212;
    
    dpOohBaby[ 2 ][ 2 ][ 0 ] = 220;
    dpOohBaby[ 2 ][ 2 ][ 1 ] = 221;
    dpOohBaby[ 2 ][ 2 ][ 2 ] = 222;

	dpComplexSauron[ 0 ][ 0 ] = 2000;
	dpComplexSauron[ 0 ][ 1 ] = -3000;

	lzCreatePublicContext( "ExtVarTstContext" );
	lzSetContext( "ExtVarTstContext", false );

	lzSetNumVar( 'G', "wazoo", 13.0 );
	lzLinkNumVar( 'G', "fester", &dFester );

	lzCreateNumArray( 'G', "hodaddy", 2, 2, 2, 0, 0, 0, 0 );

	lzSetNumArrayElement( 'G', "hodaddy", 1959.05, 2, 0, 1, 0, 0, 0, 0 );
	
	lzSetNumArrayElement( 'G', "hodaddy", 1958.05, 2, 1, 0, 0, 0, 0, 0 );

	lzLinkNumArray( 'G', "oohbaby", (double*)dpOohBaby, 3, 3, 3, 3, 0, 0, 0 );

	//lzSetComplexVar( 'G', "bilbo", 111.0, -37.2 );
	lzSetRealVar( 'G', "bilbo", 111.0 );
	lzSetImagVar( 'G', "bilbo", -37.2 );

	lzLinkComplexVar( 'G', "frodo", (double*)(&dComplexFrodo) );

	lzCreateComplexArray( 'G', "gandalf", 2, 2, 2, 0, 0, 0, 0 );

	lzSetComplexArrayElement( 'G', "gandalf", 1959.05, 14, 2, 0, 1, 0, 0, 0, 0 );
	
	//lzSetComplexArrayElement( 'G', "gandalf", 1958.05, 1, 2, 1, 0, 0, 0, 0, 0 );
	
	lzSetRealArrayElement( 'G', "gandalf", 1958.05, 2, 1, 0, 0, 0, 0, 0 );
	
	lzSetImagArrayElement( 'G', "gandalf", 1, 2, 1, 0, 0, 0, 0, 0 );

	lzLinkComplexArray( 'G', "sauron", (double*)dpComplexSauron, 2, 2, 2, 0, 0, 0, 0 );

	lzLinkStrArray( 'G', "gimli", (char*)szGimli, 256, 1, 16, 0, 0, 0, 0, 0 );

	lzCreateStrArray( 'G', "ringo", 32, 1, 10, 0, 0, 0, 0, 0);

	lzSetStrArrayElement( 'G', "ringo", "starr", 1, 4, 0, 0, 0, 0, 0 );

	lzCreateStrVar( 'C', "samwise", 256, NULL );

	dResult = lzGetNumVarValue( 'G', "wazoo" );
	dResult = lzGetNumVarValue( 'G', "fester" );

	dResult = lzGetNumArrayElement( 'G', "hodaddy", 2, 0, 1, 0, 0, 0, 0 );

	dResult = lzGetNumArrayElement( 'G', "hodaddy", 2, 1, 0, 0, 0, 0, 0 );

	dResult = lzGetNumArrayElement( 'G', "oohbaby", 3, 1, 2, 0, 0, 0, 0 );

	lResult = lzDeleteVar( 'G', "bilbo" );

	bResult = lzGetComplexVarValue( 'G', "bilbo", &dReal, &dImag );
	bResult = lzGetComplexVarValue( 'G', "frodo", &dReal, &dImag );

	bResult = lzGetComplexArrayElement( 'G', "gandalf", &dReal, &dImag, 2, 0, 1, 0, 0, 0, 0 );

	bResult = lzGetComplexArrayElement( 'G', "gandalf", &dReal, &dImag, 2, 1, 0, 0, 0, 0, 0 );

	bResult = lzGetComplexArrayElement( 'G', "sauron", &dReal, &dImag, 2, 0, 1, 0, 0, 0, 0 );

	bResult = lzSetBoolVar( 'G', "gollum", false );
	bResult = lzGetBoolVarValue( 'G', "gollum" );
	bResult = lzSetBoolVar( 'G', "gollum", true );
	bResult = lzGetBoolVarValue( 'G', "gollum" );

	bResult = lzIsVar( 'G', "oddjob" );
	bResult = lzIsVar( 'C', "gollum" );
	bResult = lzIsVar( 'A', "gollum" );

	cVarType = lzGetVarType( 'C', "oddjob" );
	cVarType = lzGetVarType( 'G', "gollum" );
	cVarType = lzGetVarType( 'A', "gandalf" );
	cVarType = lzGetVarType( 'A', "bilbo" );

	while ( keepLooping )
	{
		GetInputString( theExpression, 128 );

		expLength = strlen( theExpression );
		
		if ( expLength == 0 )
			keepLooping = false;

		else if ( (expLength == 1) && (theExpression[ 0 ] == '\r') )
			keepLooping = false;
			
		else if (theExpression[ 0 ] == '\\')
			keepLooping = false;

		else
		{
			cout << "  " << lzEvalExpStrToStr( theExpression ) << '\n';
			
			if ( lzGetLastErrorID() )
				cout << lzGetLastErrorText() << '\n';
				
			cout.flush();
		}
	}

	lzDestroyPublicContext( "ExtVarTstContext" );
}

void SelectDemo( void )
{
	char	theExpression[ 128 ];
	char	szDemoFile[ 128 ];
	long	expLength;
	bool	keepLooping = true;
	long	lDefineOpResult = 0;

	lzCreatePublicContext( "SelectContext" );
	lzSetContext( "SelectContext", false );

	sprintf( szDemoFile, "%s%s", TEST_FILE_PATH_PREFIX, "select.lzo" );

	lDefineOpResult = lzReadExpOp(	kGlobalScope,
									"stepfunc",
									"prefix",
									"number",
									"number x",
									"sin",
									szDemoFile );

	if ( lDefineOpResult )
	{
		cout << "Failed to load STEPFUNC operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
	}

	while ( keepLooping )
	{
		GetInputString( theExpression, 128 );
		expLength = strlen( theExpression );
		
		if ( expLength == 0 )
			keepLooping = false;

		else if ( (expLength == 1) && (theExpression[ 0 ] == '\r') )
			keepLooping = false;
			
		else if (theExpression[ 0 ] == '\\')
			keepLooping = false;

		else
		{
			cout << "  " << lzEvalExpStrToStr( theExpression ) << '\n';
			
			if ( lzGetLastErrorID() )
				cout << lzGetLastErrorText() << '\n';
				
			cout.flush();
		}
	}

	lzDestroyPublicContext( "SelectContext" );
}

void ShowDataTypeSizes( void )
{
	cout << endl << endl;
	cout << "Data Type sizes:" << endl << endl;
	
	cout << "bool         " << sizeof( bool ) << endl;
	cout << "char         " << sizeof( char ) << endl;
	cout << "int          " << sizeof( int ) << endl;
	cout << "short        " << sizeof( short ) << endl;
	cout << "long         " << sizeof( long ) << endl;
	cout << "long long    " << sizeof( long long ) << endl;
	cout << "float        " << sizeof( float ) << endl;
	cout << "double       " << sizeof( double ) << endl;
	cout << "long double  " << sizeof( long double ) << endl;
}

void LifeDemo( void )
{
	char			inputChar;
	char			theExpression[ 128 ];

	char			szDemoFile[ 128 ];

	LZObjectHandle	hTheExpression = 0;
	long			lCycleIndex = 0;
	long			lGridLineIndex = 0;

	long			lDefineOpResult = 0;

	lzCreatePublicContext( "LifeContext" );
	lzSetContext( "LifeContext", false );

	sprintf( szDemoFile, "%s%s", TEST_FILE_PATH_PREFIX, "initlife.lzo" );
	cout << "Opening " << szDemoFile << "\r\n";
	
	lDefineOpResult = lzReadExpOp(	kGlobalScope,
									"initlife",
									"nofix",
									"void",
									NULL,
									"sin",
									szDemoFile );

	if ( lDefineOpResult )
	{
		cout << "Failed to load INITLIFE operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
		goto LifeDemoExit;
	}

	lDefineOpResult = lzDefineExpOp(	kGlobalScope,
										"life",
										"nofix",
										"void",
										NULL,
										"sin",
										NULL );

	if ( lDefineOpResult )
	{
		cout << "Failed to define LIFE operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
		goto LifeDemoExit;
	}

	sprintf( szDemoFile, "%s%s", TEST_FILE_PATH_PREFIX, "life.lzo" );
	cout << "Opening " << szDemoFile << "\r\n";

	lDefineOpResult = lzReadExpOpStr(	kGlobalScope,
										"life",
										szDemoFile );

	if ( lDefineOpResult )
	{
		cout << "Failed to read LIFE operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
		goto LifeDemoExit;
	}

	strcpy( theExpression, "initlife" );
	lzEvalExpStrToStr( theExpression );

	strcpy( theExpression, "life" );
	hTheExpression = lzParseExp( theExpression );

	if ( !hTheExpression )
	{
		cout << "Failed to parse LIFE operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";
		goto LifeDemoExit;
	}

	for ( lCycleIndex = 0; lCycleIndex < 50; lCycleIndex++ )
	{
		lzEvalExpHndlToStr( hTheExpression );

		cout << " Generation " << (lCycleIndex + 1) << '\n';
		cout << "+----------------+" << '\n';

		for ( lGridLineIndex = 15; lGridLineIndex >= 0; lGridLineIndex-- )
		{
			cout << "|" << lzGetStrArrayElement(	kGlobalScope,
													"board",
													1, lGridLineIndex, 0, 0, 0, 0, 0 ) << "|" << '\n';
		}

		cout << "+----------------+" << '\n';

		cout.flush();

		inputChar = '\0';
		inputChar = getchar();

		if ( inputChar == '\\' )
			break;
	}

LifeDemoExit:
	
	lzDestroyPublicContext( "LifeContext" );
}

void RecursiveDemo( void )
{
	char			theExpression[ 128 ];

	LZObjectHandle	hTheExpression = 0;
	long			lDefineOp = 0;
	long			lDefineOpA = 0;
	long			lDefineOpB = 0;

	lzCreatePublicContext( "RecurseContext" );
	lzSetContext( "RecurseContext", false );

	///////////////////////////////////
	//  Direct Recursion
	///////////////////////////////////

	// Forward declare...
	lDefineOp = lzDefineExpOp(	kGlobalScope,
								"fact",
								"prefix",
								"number",
								"number x",
								"sin",
								NULL );

	if ( lDefineOp )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	// Copy in the expression...
	strcpy( theExpression, "if (x>0) then return (x * fact( x-1 )); else return 1; end" );

	// Complete the definition...
	lDefineOp = lzDefineExpOpStr(	kGlobalScope,
									"fact",
									theExpression );

	if ( lDefineOp )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	///////////////////////////////////
	//  Indirect Recursion
	///////////////////////////////////

	// Forward declare...
	lDefineOpA = lzDefineExpOp(	kGlobalScope,
									"facta",
									"prefix",
									"number",
									"number x",
									"sin",
									NULL );

	if ( lDefineOpA )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	lDefineOpB = lzDefineExpOp(	kGlobalScope,
									"factb",
									"prefix",
									"number",
									"number x",
									"sin",
									NULL );

	if ( lDefineOpB )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	// Copy in the 'A' expression...
	strcpy( theExpression, "if (x>0) then return (x * factb( x-1 )); else return 1; end" );

	// Complete the definition...
	lDefineOpA = lzDefineExpOpStr(	kGlobalScope,
									"facta",
									theExpression );

	if ( lDefineOpA )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	// Copy in the 'B' expression...
	strcpy( theExpression, "if (x>0) then return (x * facta( x-1 )); else return 1; end" );

	// Complete the definition...
	lDefineOpB = lzDefineExpOpStr(	kGlobalScope,
									"factb",
									theExpression );

	if ( lDefineOpB )
	{
		cout << "Failed to load recursive operator.\r\n";
		cout << lzGetLastErrorText() << "\r\n\n";

		goto RecursiveDemoExit;
	}

	// Test direct recursion...
	strcpy( theExpression, "fact( 6 )" );

	if ( (hTheExpression = lzParseExp( theExpression )) )
	{
		cout << "  " << lzEvalExpHndlToStr( hTheExpression ) << "\r\n";
	}
	
	if ( lzGetLastErrorID() )
		cout << lzGetLastErrorText() << "\r\n\n";
		
	cout.flush();

	// Test indirect recursion...
	strcpy( theExpression, "facta( 6 )" );

	if ( (hTheExpression = lzParseExp( theExpression )) )
	{
		cout << "  " << lzEvalExpHndlToStr( hTheExpression ) << "\r\n";
	}
	
	if ( lzGetLastErrorID() )
		cout << lzGetLastErrorText() << "\r\n\n";
		
	cout.flush();

RecursiveDemoExit:

	lzDeleteOp( kGlobalScope, "fact" );
	lzDeleteOp( kGlobalScope, "facta" );
	lzDeleteOp( kGlobalScope, "factb" );

	lzDestroyPublicContext( "RecurseContext" );

	return;
}


void TimingTest( void )
{
	char			theExpression[ 512 ];

	long			lInnerLoop;
	long			lOuterLoop;
	
	long			lStartTime;
	long			lEndTime;
	
	double			dWazoo = 0.0;
	double			dFester = 0.0;
	double			dResult = 0.0;
	long			lTests = 0;
	
	double			dBaselineMIPS;
	double			dBaselineWCallMIPS;
	double			dParserInnerMIPS;
	double			dParserOuterMIPS;
	
	static double	dTBaselineMIPS = 0.0;
	static double	dTBaselineWCallMIPS = 0.0;
	static double	dTParserInnerMIPS = 0.0;
	static double	dTParserOuterMIPS = 0.0;
	
	static long		lTTestRuns = 0;
		
	double			dParserTime = 0.0;

	LZObjectHandle	hContextID = 0;
	LZObjectHandle	hExpression = 0;

	// Baseline...
	lInnerLoop = 2000;
	lOuterLoop = 2000;

	lTests = lInnerLoop * lOuterLoop;

	cout <<	'\n' << '\n';
	cout.flush();
	
	lStartTime = clock();

	for ( dFester = lOuterLoop; dFester > 0; dFester-- )
		for ( dWazoo = lInnerLoop; dWazoo > 0; dWazoo-- )
			dResult += fabs( sin( sqrt( pow(dFester, 2) + pow(dWazoo, 2) ) ) * 255 );

	lEndTime = clock();
	dParserTime = lEndTime - lStartTime;
	dBaselineMIPS = (lTests / (dParserTime / CLOCKS_PER_SEC) / 1000000);
	dTBaselineMIPS += dBaselineMIPS;
	
	dResult = 0.0;

	// Baseline w/call...
	lInnerLoop = 2000;
	lOuterLoop = 2000;

	lTests = lInnerLoop * lOuterLoop;

	cout.flush();
	lStartTime = clock();

	for ( dFester = lOuterLoop; dFester > 0; dFester-- )
		for ( dWazoo = lInnerLoop; dWazoo > 0; dWazoo-- )
			dResult += DoTestExpression( dFester, dWazoo );

	lEndTime = clock();
	dParserTime = lEndTime - lStartTime;
	dBaselineWCallMIPS = (lTests / (dParserTime / CLOCKS_PER_SEC) / 1000000);
	dTBaselineWCallMIPS += dBaselineWCallMIPS;
			
	cout << "Baseline result: " << dResult << '\n';
		
	dResult = 0.0;

	lzClearContext();
	lzSetAngleUnit( kAngleRadians );
	lzCreatePrivateContext( "TimingTest" );
	hContextID = lzSetContext( "TimingTest", false );
	
	if ( lzGetLastErrorID() )
		cout << lzGetLastErrorText() << '\n';
	
	// Internal Loop
		
	//strcpy( theExpression, "tests=5000000; until (tests-- < 0) do fester++; wazoo += fester^2 + 5*fester - 10; next; return wazoo" );
	//strcpy( theExpression, "tests = -5000000; until (tests++ > 0) do wazoo += sqrt(tests); next; return wazoo" );
	//strcpy( theExpression, "tests = 5000000; until (tests-- < 0) do wazoo += sqrt(tests); next; return wazoo" );
		
	lTests = 2000 * 2000;
		
	strcpy( theExpression, "answer=0; fester=2000; until (fester-- == 0) do wazoo=2000; until (wazoo-- == 0) do answer+=abs(sin(sqrt(fester^2+wazoo^2))*255); next; next; return answer;");
		
	lzEvalExpStrToNum( "global number answer; global number fester; global number wazoo; answer=fester=wazoo=0" );
		
	hExpression = lzParseExp( theExpression );
				
	if ( lzGetLastErrorID() )
		cout << lzGetLastErrorText() << '\n';

	cout.flush();
	lStartTime = clock();
	dResult = lzEvalExpHndlToNum( hExpression );

	lEndTime = clock();
	dParserTime = lEndTime - lStartTime;
	dParserInnerMIPS = (lTests / (dParserTime / CLOCKS_PER_SEC) / 1000000);
	dTParserInnerMIPS += dParserInnerMIPS;
			
	cout << "Internal result: " << dResult << '\n';
		
	// External Loop
	
	// Expressions from Gary Beene's review of parsers...
	//  http://www.garybeene.com/reviews/rev-parsers.htm

	//strcpy( theExpression, "calico*2+cheshire*2" );
	//strcpy( theExpression, "sin(calico)+sin(cheshire)" );
	//strcpy( theExpression, "calico^2+cheshire^2" );
	strcpy( theExpression, "abs(sin(sqrt(calico^2+cheshire^2))*255)" );
	//strcpy( theExpression, "abs(sin(sqrt(calico*calico+cheshire*cheshire))*255)" );
	
	// Must link under different names, as wazoo and fester already exist in the current context...
	lzLinkNumVar( 'G', "cheshire", &dWazoo );
	lzLinkNumVar( 'G', "calico", &dFester );

	hExpression = lzParseExp( theExpression );
				
	if ( lzGetLastErrorID() )
		cout << lzGetLastErrorText() << '\n';

	lInnerLoop = 2000;
	lOuterLoop = 2000;

	lTests = lInnerLoop * lOuterLoop;
	
	dResult = 0.0;

	cout.flush();
	lStartTime = clock();

	for ( dFester = lOuterLoop; dFester > 0; dFester-- )
		for ( dWazoo = lInnerLoop; dWazoo > 0; dWazoo-- )
			dResult += lzEvalExpHndlToNum( hExpression );

	lEndTime = clock();
	dParserTime = lEndTime - lStartTime;
	dParserOuterMIPS = (lTests / (dParserTime / CLOCKS_PER_SEC) / 1000000);
	dTParserOuterMIPS += dParserOuterMIPS;
		
	lTTestRuns++;
			
	cout << "External result: " << dResult << '\n';
		
	cout << "                 " << "\t\t\t" << "  Elzed   " << '\t' << "  Elzed   " << '\n'; 
	cout << "                 " << "\t\t\t" << "Inner Loop" << '\t' << "Outer Loop" << '\n'; 
	cout << "                 " << "\t\t\t" << "----------" << '\t' << "----------" << '\n'; 
	
	cout << "Cummulative      " << "\t" << "MIPS      |" << "\t" << (dTParserInnerMIPS / lTTestRuns) << '\t' << '\t' << (dTParserOuterMIPS / lTTestRuns) << '\n';
	cout << "                 " << "\t" << "----------+" << '\n';
	cout << "   Baseline:     " << '\t' << (dTBaselineMIPS / lTTestRuns) << '\t' << '\t' << (dTParserInnerMIPS / dTBaselineMIPS)*100 << '%' << '\t' << (dTParserOuterMIPS / dTBaselineMIPS)*100 << '%' << '\n';
	cout << "     w/call:     " << '\t' << (dTBaselineWCallMIPS / lTTestRuns) << '\t' << '\t' << (dTParserInnerMIPS / dTBaselineWCallMIPS)*100 << '%' << '\t' << (dTParserOuterMIPS / dTBaselineWCallMIPS)*100 << '%' << '\n';
	cout << '\n';
	
	cout << "Current          " << "\t" << "MIPS      |" << "\t" << dParserInnerMIPS << '\t' << '\t' << dParserOuterMIPS << '\n';
	cout << "                 " << "\t" << "----------+" << '\n';
	cout << "   Baseline:     " << '\t' << dBaselineMIPS << '\t' << '\t' << (dParserInnerMIPS / dBaselineMIPS)*100 << '%' << '\t' << (dParserOuterMIPS / dBaselineMIPS)*100 << '%' << '\n';
	cout << "     w/call:     " << '\t' << dBaselineWCallMIPS << '\t' << '\t' << (dParserInnerMIPS / dBaselineWCallMIPS)*100 << '%' << '\t' << (dParserOuterMIPS / dBaselineWCallMIPS)*100 << '%' << '\n';
	cout << '\n';
	
	cout << "                 " << '\t' << "MIPS: Million Iterations Per Second." << '\n';
	cout << "                 " << '\t' << "Total Test Runs: " << lTTestRuns << '\n';
	cout << "                 " << '\t' << "Clocks Per Second = " << CLOCKS_PER_SEC << '\n';
	
	cout.flush();
	
	lzClearContext();
	lzDestroyPrivateContext( "TimingTest" );
	
	lzReset();
}

double DoTestExpression( double dFester, double dWazoo )
{
	return (fabs( sin( sqrt( pow(dFester, 2) + pow(dWazoo, 2) ) ) * 255 ));
}

long ELZED_CALL_TYPE MyExternalFunction( LZObjectHandle lExOpUID )
{
	char*	signature = NULL;
	
#ifdef WIN32
	BSTR	signatureB;
#endif

	double	first = 0.0;
	double	firstReal = 0.0;
	double	firstImag = 0.0;
	char*	second = NULL;
	bool	third = false;

#ifdef WIN32
	BSTR	second2;
#endif

	signature = lzGetParmSig( lExOpUID );
	
#ifdef WIN32
	signatureB = lzGetParmSigB( lExOpUID );
#endif

	first = lzGetNumParm( lExOpUID, 0 );
	firstReal = lzGetRealParm( lExOpUID, 0 );
	firstImag = lzGetImagParm( lExOpUID, 0 );
	second = lzGetStrParm( lExOpUID, 1 );

#ifdef WIN32
	second2 = lzGetStrParmB( lExOpUID, 1 );
#endif

	third = lzGetBoolParm( lExOpUID, 2 );

	//lzSetNumResult( lExOpUID, 28.0 );
	//lzSetRealResult( lExOpUID, firstImag );
	//lzSetImagResult( lExOpUID, firstReal );
	//lzSetStrResult( lExOpUID, second );
	lzSetBoolResult( lExOpUID, third );

	return 0;
}

long ELZED_CALL_TYPE MyOtherExternalFunction( LZObjectHandle lExOpUID )
{
	double first = 0.0;
	double second = 0.0;

	first = lzGetNumParm( lExOpUID, 0 );
	second = lzGetNumParm( lExOpUID, 1 );

	lzSetNumResult( lExOpUID, first/second );

	return 0;
}

void ELZED_CALL_TYPE MyErrorCallback( long lTheError )
{
#ifdef WIN32
	MessageBox( NULL, "Error callback function invoked", "Elzed Math Parser Demo", MB_OK | MB_ICONEXCLAMATION );
#endif
}

