%{
/*
Auto:		smake MCalc
*/

static void	__yy_bcopy(char *from, char *to, int count);

#undef		alloca
#define		alloca(x) AllocVecPool(ParsePool, x)
#undef		malloc
#define		malloc(x) AllocVecPool(ParsePool, x)
#undef		free
#define		free(x) FreeVecPool(ParsePool, x)

#undef		YYINITDEPTH
#define		YYINITDEPTH 512

#undef		_STDC_
#define		_STDC_

#undef		error
#define		error

extern		APTR		ParsePool;
extern		double		Value;
extern		double		IMem,
				JMem,
				KMem,
				LMem,
				MMem,
				NMem,
				OMem,
				PMem,
				QMem,
				RMem,
				SMem,
				TMem,
				UMem,
				VMem,
				WMem,
				XMem,
				YMem,
				ZMem;
extern		struct	List	StandardList;
extern		struct	List	LinearList;

#define		outputr(x)	{Value = x;}
#define		outputmem(x, y)	{Value = *(x) = y;}
#define		errout(x)	;
#define		erroutm(x, y)	;


/**********************************************************************/
/*                    Chars read and read-position                    */
/**********************************************************************/
extern	UWORD	PColumn;
extern	UWORD	PCharRead;


	// This is for the wanted angle

extern	UWORD		IntAngle;


/**********************************************************************/
/*                            Error-Value                             */
/**********************************************************************/
UWORD		PError;
%}



/**********************************************************************/
/*                       Possible Return-Value                        */
/**********************************************************************/
%union
{
	double	Real;
	double	*MemPtr;
}


/**********************************************************************/
/*                      Token which returns Real                      */
/**********************************************************************/
%token <Real>     INT_CONSTANT


/**********************************************************************/
/*                       Non-Priority functions                       */
/**********************************************************************/
%token EQU_OP OPEN_OP CLOSE_OP COMMA SEMICOLON



/**********************************************************************/
/*                           Set priorities                           */
/**********************************************************************/
%left AND_OP OR_OP XOR_OP ASL ASR LSL LSR ROL ROR
%left ADD_OP SUB_OP
%left MUL_OP DIV_OP MOD_OP
%left NEG_OP NOT_OP PERCENT CHPERCENT TPERCENT
%left POW
%nonassoc MY_ABS COS SIN TAN ACOS ASIN ATAN EXP LOG LOG10 SQRT SINH COSH TANH FAK COT N_OVER_K YROOT REZ SD_INIT SD_AVERAGE SD_DEVIATION1 SD_DEVIATION2 SD_QSUM SD_SUM SD_NUM LR_INIT LR_XAVERAGE LR_YAVERAGE LR_XDEVIATION1 LR_YDEVIATION1 LR_XDEVIATION2 LR_YDEVIATION2 LR_XQSUM LR_YQSUM LR_XSUM LR_YSUM LR_XNUM LR_YNUM LR_XYSUM LR_ALPHA LR_BETA LR_ASSESSX LR_ASSESSY LR_CORR LR_CRITCORR LR_COVAR EXG_OP
%nonassoc I_MEM J_MEM K_MEM L_MEM M_MEM N_MEM O_MEM P_MEM Q_MEM R_MEM S_MEM T_MEM U_MEM V_MEM W_MEM X_MEM Y_MEM Z_MEM



/**********************************************************************/
/*                    Return-Value of my terminal                     */
/**********************************************************************/
%type <Real>   int_expr
%type <Real>   standard_deviation
%type <Real>   linear_regression
%type <MemPtr> memory_name
%%



/**********************************************************************/
/*                          Main "sentence"                           */
/**********************************************************************/
eingabe
	: int_expr					{outputr($1);}
	| standard_deviation				{UpdateHistoryWindow(FALSE); outputr(0.0);}
	| linear_regression				{UpdateHistoryWindow(TRUE); outputr(0.0);}
	| memory_name EQU_OP int_expr			{outputmem($1, $3);}
	| error
	;




/**********************************************************************/
/*                           Do the grammar                           */
/**********************************************************************/
int_expr
	: OPEN_OP int_expr CLOSE_OP			{$$ = $2;}
	| int_expr AND_OP int_expr			{$$ = (make_ulong($1)) & (make_ulong($3));}
	| int_expr OR_OP int_expr			{$$ = (make_ulong($1)) | (make_ulong($3));}
	| int_expr XOR_OP int_expr			{$$ = MyXOR(make_ulong($1),make_ulong($3));}
	| int_expr NOT_OP AND_OP int_expr		{$$ = MyNAND(make_ulong($1), make_ulong($4));}
	| int_expr NOT_OP OR_OP int_expr		{$$ = MyNOR(make_ulong($1), make_ulong($4));}
	| int_expr NOT_OP XOR_OP int_expr		{$$ = MyNXOR(make_ulong($1), make_ulong($4));}
	| int_expr ADD_OP int_expr PERCENT		{$$ = ($1 + (($1 * $3) / 100));}
	| int_expr SUB_OP int_expr PERCENT		{$$ = ($1 - (($1 * $3) / 100));}
	| int_expr MUL_OP int_expr PERCENT		{$$ = (($1 * $3) / 100);}
	| int_expr CHPERCENT int_expr			{$$ = ((($3 - $1) * 100) / $1);}
	| int_expr TPERCENT int_expr			{$$ = (($3 * 100) / $1);}
	| int_expr ADD_OP int_expr			{$$ = $1 + $3;}
	| int_expr SUB_OP int_expr			{$$ = $1 - $3;}
	| int_expr MUL_OP int_expr			{$$ = $1 * $3;}
	| int_expr DIV_OP int_expr			{if($3 == 0.0) {PError = ERR_DIVBY0; yyerror(NULL); } else $$ = $1 / $3;}
	| int_expr MOD_OP int_expr			{if($3 == 0.0) {PError = ERR_DIVBY0; yyerror(NULL); } else $$ = fmod($1,$3);}
	| SUB_OP int_expr %prec NEG_OP			{$$ = -$2; }
	| NOT_OP int_expr				{$$ = (~make_ulong($2)); }
	| int_expr ASL int_expr				{$$ = (double)MyASL(make_ulong($1),make_ulong($3)); }
	| int_expr ASR int_expr				{$$ = (double)MyASR(make_ulong($1),make_ulong($3)); }
	| int_expr LSL int_expr				{$$ = (double)MyLSL(make_ulong($1),make_ulong($3)); }
	| int_expr LSR int_expr				{$$ = (double)MyLSR(make_ulong($1),make_ulong($3)); }
	| int_expr ROL int_expr				{$$ = (double)MyROL(make_ulong($1),make_ulong($3)); }
	| int_expr ROR int_expr				{$$ = (double)MyROR(make_ulong($1),make_ulong($3)); }
	| int_expr POW int_expr				{$$ = pow($1,$3); }
	| SIN int_expr					{$$ = sin(calc_angle($2)); }
	| COS int_expr					{$$ = cos(calc_angle($2)); }
	| TAN int_expr					{$$ = tan(calc_angle($2)); }
	| ASIN int_expr					{$$ = asin(calc_angle($2)); }
	| ACOS int_expr					{$$ = acos(calc_angle($2)); }
	| ATAN int_expr					{$$ = atan(calc_angle($2)); }
	| COT int_expr					{$$ = cot(calc_angle($2)); }
	| SINH int_expr					{$$ = sinh(calc_angle($2)); }
	| COSH int_expr					{$$ = cosh(calc_angle($2)); }
	| TANH int_expr					{$$ = tanh(calc_angle($2)); }
	| EXP int_expr					{$$ = exp($2); }
	| LOG int_expr					{$$ = log($2); }
	| LOG10 int_expr				{$$ = log10($2); }
	| MY_ABS int_expr				{$$ = fabs($2); }
	| SQRT int_expr					{if($2 < 0.0) {PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = sqrt($2);}
	| int_expr N_OVER_K int_expr			{if($1 < 0.0 || ($1 - $3) < 0.0 || $1 > 170.0 || ($1 - $3) > 170.0 || $3 < 0.0 || $3 > 170.0) {PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = (calc_fak($1) / (calc_fak($3) * calc_fak($1 - $3)));}
	| int_expr YROOT int_expr			{if($1 < 0.0) {PError = ERR_DIVBY0; yyerror(NULL);} else $$ = pow($3, (1/$1));}
	| REZ int_expr					{if($2 < 0.0) {PError = ERR_DIVBY0; yyerror(NULL);} else $$ = (1.0 / $2);}
	| FAK int_expr					{if($2 > 170.0) { PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = calc_fak($2);}
	| int_expr FAK					{if($1 > 170.0) { PError = ERR_OVERFLOW; yyerror(NULL); } else $$ = calc_fak($1);}
	| SD_AVERAGE					{if(numbers_in_list(&StandardList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_average(&StandardList, FALSE);}}
	| SD_DEVIATION1					{if(numbers_in_list(&StandardList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_population(&StandardList, FALSE);}}
	| SD_DEVIATION2					{if(numbers_in_list(&StandardList) < 2) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_sample(&StandardList, FALSE);}}
	| SD_QSUM					{$$ = calc_powsum(&StandardList, FALSE);}
	| SD_SUM					{$$ = calc_sum(&StandardList, FALSE);}
	| SD_NUM					{$$ = (double)numbers_in_list(&StandardList);}
	| LR_XAVERAGE					{if(numbers_in_list(&LinearList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_average(&LinearList, FALSE);}}
	| LR_XDEVIATION1				{if(numbers_in_list(&LinearList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_population(&LinearList, FALSE);}}
	| LR_XDEVIATION2				{if(numbers_in_list(&LinearList) < 2) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_sample(&LinearList, FALSE);}}
	| LR_XQSUM					{$$ = calc_powsum(&LinearList, FALSE);}
	| LR_XSUM					{$$ = calc_sum(&LinearList, FALSE);}
	| LR_XNUM					{$$ = (double)numbers_in_list(&LinearList);}
	| LR_YAVERAGE					{if(numbers_in_list(&LinearList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_average(&LinearList, TRUE);}}
	| LR_YDEVIATION1				{if(numbers_in_list(&LinearList) < 1) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_population(&LinearList, TRUE);}}
	| LR_YDEVIATION2				{if(numbers_in_list(&LinearList) < 2) {PError = ERR_UNDERFLOW; yyerror(NULL);} else {$$ = calc_sample(&LinearList, TRUE);}}
	| LR_YQSUM					{$$ = calc_powsum(&LinearList, TRUE);}
	| LR_YSUM					{$$ = calc_sum(&LinearList, TRUE);}
	| LR_YNUM					{$$ = (double)numbers_in_list(&LinearList);}
	| LR_XYSUM					{$$ = calc_xysum();}
	| LR_ALPHA					{$$ = calc_lralpha(); if(PError) yyerror(NULL);}
	| LR_BETA					{$$ = calc_lrbeta(); if(PError) yyerror(NULL);}
	| LR_ASSESSX int_expr				{$$ = calc_lrassessx($2); if(PError) yyerror(NULL);}
	| LR_ASSESSY int_expr				{$$ = calc_lrassessy($2); if(PError) yyerror(NULL);}
	| LR_CORR					{$$ = calc_lrcorr(); if(PError) yyerror(NULL);}
	| LR_CRITCORR					{$$ = pow(calc_lrcorr(), 2.0); if(PError) yyerror(NULL);}
	| LR_COVAR					{$$ = calc_lrcovar(); if(PError) yyerror(NULL);}
	| memory_name					{$<Real>$ = (*($1));}
	| INT_CONSTANT
	| EXG_OP OPEN_OP memory_name COMMA memory_name CLOSE_OP	{double Spare; $$ = 0.0; Spare = *($3); *($3) = *($5); *($5) = Spare;}
	;


standard_deviation
	: SD_INIT OPEN_OP standard_input CLOSE_OP	{$$ = 0.0;}

standard_input
	: int_expr					{if(!add_standard_value($1)) { PError = ERR_NOMEM; yyerror(NULL); }}
	| int_expr COMMA standard_input			{if(!add_standard_value($1)) { PError = ERR_NOMEM; yyerror(NULL); }}
	;


linear_regression
	: LR_INIT OPEN_OP linear_input CLOSE_OP			{$$ = 0.0;}

linear_input
	: int_expr SEMICOLON int_expr				{if(!add_linear_value($1, $3)) { PError = ERR_NOMEM; yyerror(NULL); }}
	| int_expr SEMICOLON int_expr COMMA linear_input	{if(!add_linear_value($1, $3)) { PError = ERR_NOMEM; yyerror(NULL); }}
	;

memory_name
	: I_MEM						{$$ = &IMem;}
	| J_MEM						{$$ = &JMem;}
	| K_MEM						{$$ = &KMem;}
	| L_MEM						{$$ = &LMem;}
	| M_MEM						{$$ = &MMem;}
	| N_MEM						{$$ = &NMem;}
	| O_MEM						{$$ = &OMem;}
	| P_MEM						{$$ = &PMem;}
	| Q_MEM						{$$ = &QMem;}
	| R_MEM						{$$ = &RMem;}
	| S_MEM						{$$ = &SMem;}
	| T_MEM						{$$ = &TMem;}
	| U_MEM						{$$ = &UMem;}
	| V_MEM						{$$ = &VMem;}
	| W_MEM						{$$ = &WMem;}
	| X_MEM						{$$ = &XMem;}
	| Y_MEM						{$$ = &YMem;}
	| Z_MEM						{$$ = &ZMem;}
	;

%%




/**********************************************************************/
/*             Convert a double to a long (the hard way)              */
/**********************************************************************/
ULONG make_ulong(double OldVal)
{
	char	Buffer[26];
	ULONG	Dummy;

	sprintf(Buffer, "%f", OldVal);
	stcd_l(Buffer, (LONG *)&Dummy);
	return(Dummy);
}





/**********************************************************************/
/*                           Calculate Fak                            */
/**********************************************************************/
double calc_fak(double Fak)
{
	double	RetVal = 1.0, i = 1.0;

	while(i <= Fak)
	{
		RetVal *= i;
		i++;
	}

	return(RetVal);
}





/**********************************************************************/
/*              Calculate correct angle value (rad, deg, gra)         */
/**********************************************************************/
double calc_angle(double Value)
{
	switch(IntAngle)
	{
		case ID_GRAD :
		{
			Value = (Value * (9.0 / 10.0));
		}
		case ID_DEG :
		{
			Value = (Value / 180.0) * Pi;
			break;
		}
	}

	return(Value);
}



/**********************************************************************/
/*                          Error-Routine ;)                          */
/**********************************************************************/
int yyerror(char *s)
{
	return(0);
}




/**********************************************************************/
/*            Add a value to the standard regression array            */
/**********************************************************************/
BOOL add_standard_value(double Value)
{
	struct	DoubleNode	*NewNode;

	if((NewNode = AllocVec(sizeof(struct DoubleNode), MEMF_CLEAR)))
	{
		AddHead(&StandardList, (struct Node *)NewNode);

		NewNode->ValueX		= Value;

		return(TRUE);
	}
	else
		return(FALSE);
}




/**********************************************************************/
/*               Add a value to linear regression array               */
/**********************************************************************/
BOOL add_linear_value(double ValueX, double ValueY)
{
	struct	DoubleNode	*NewNode;

	if((NewNode = AllocVec(sizeof(struct DoubleNode), MEMF_CLEAR)))
	{
		AddHead(&LinearList, (struct Node *)NewNode);

		NewNode->ValueX		= ValueX;
		NewNode->ValueY		= ValueY;

		return(TRUE);
	}
	else
		return(FALSE);
}





/**********************************************************************/
/*               Return the number of entries in a list               */
/**********************************************************************/
int numbers_in_list(struct List *Lst)
{
	struct	Node	*CheckNode	= Lst->lh_Head;
	int		Number		= 0;

	while(CheckNode->ln_Succ)
	{
		Number++;
		CheckNode	= CheckNode->ln_Succ;
	}

	return(Number);
}





/**********************************************************************/
/*                       Calc average of a list                       */
/**********************************************************************/
double calc_average(struct List *Lst, BOOL LinearValueY)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)Lst->lh_Head;
	int			Number		= numbers_in_list(Lst);
	double			Sum		= 0;

	if(!Number)
		return(0.0);

	while(CheckNode->Link.ln_Succ)
	{
		if(!LinearValueY)
			Sum		+= CheckNode->ValueX;
		else
			Sum		+= CheckNode->ValueY;

		CheckNode		= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return((Sum / (double)Number));
}




/**********************************************************************/
/*                  Calc std-deviation of population                  */
/**********************************************************************/
double calc_population(struct List *Lst, BOOL LinearValueY)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)Lst->lh_Head;
	int			Number		= numbers_in_list(Lst);
	double			Poss,
				Pop		= 0,
				Ave		= calc_average(Lst, LinearValueY),
				Value;

	Poss		= 1.0 / (double)Number;
	while(CheckNode->Link.ln_Succ)
	{
		if(!LinearValueY)
			Value	= CheckNode->ValueX;
		else
			Value	= CheckNode->ValueY;

		Pop		+= (pow((Value - Ave), 2.0) * Poss);

		CheckNode		= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return(sqrt(Pop));
}



/**********************************************************************/
/*                    Calc std-deviation of sample                    */
/**********************************************************************/
double calc_sample(struct List *Lst, BOOL LinearValueY)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)Lst->lh_Head;
	int			Number		= numbers_in_list(Lst);
	double			Poss,
				Pop		= 0,
				Ave		= calc_average(Lst, LinearValueY),
				Value;

	Poss		= 1.0 / ((double)Number - 1);
	while(CheckNode->Link.ln_Succ)
	{
		if(!LinearValueY)
			Value	= CheckNode->ValueX;
		else
			Value	= CheckNode->ValueY;

		Pop		+= (pow((Value - Ave), 2.0) * Poss);

		CheckNode		= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return(sqrt(Pop));
}



/**********************************************************************/
/*          Calc sum of powers of two of entries within list          */
/**********************************************************************/
double calc_powsum(struct List *Lst, BOOL LinearValueY)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)Lst->lh_Head;
	double			Number,
				Value		= 0;

	while(CheckNode->Link.ln_Succ)
	{
		if(!LinearValueY)
			Number	= CheckNode->ValueX;
		else
			Number	= CheckNode->ValueY;

		Value		+= (pow(Number, 2.0));

		CheckNode		= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return(Value);
}




/**********************************************************************/
/*                  Calc sum of entries within list                   */
/**********************************************************************/
double calc_sum(struct List *Lst, BOOL LinearValueY)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)Lst->lh_Head;
	double			Number,
				Value		= 0;

	while(CheckNode->Link.ln_Succ)
	{
		if(!LinearValueY)
			Number	= CheckNode->ValueX;
		else
			Number	= CheckNode->ValueY;

		Value		+= Number;

		CheckNode		= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return(Value);
}




/**********************************************************************/
/*                    Calc sum of x and y members                     */
/**********************************************************************/
double calc_xysum(void)
{
	struct	DoubleNode	*CheckNode	= (struct DoubleNode *)LinearList.lh_Head;
	double			Value		= 0;

	while(CheckNode->Link.ln_Succ)
	{
		Value		+= (CheckNode->ValueX * CheckNode->ValueY);

		CheckNode	= (struct DoubleNode *)CheckNode->Link.ln_Succ;
	}

	return(Value);
}




/**********************************************************************/
/*             Calc alpha part of formular of regression              */
/**********************************************************************/
double calc_lralpha(void)
{
	double		XSum	= calc_sum(&LinearList, FALSE),
			XYSum	= calc_xysum(),
			YSum	= calc_sum(&LinearList, TRUE),
			XQSum	= calc_powsum(&LinearList, FALSE),
			Number	= (double)numbers_in_list(&LinearList);

	if((pow(XSum, 2.0) - (Number * XQSum)) == 0.0)
	{
		PError	= ERR_DIVBY0;
		return(0.0);
	}

	return( (((XSum * XYSum) - (YSum * XQSum)) / (pow(XSum, 2.0) - (Number * XQSum))) );
}




/**********************************************************************/
/*             Calc alpha part of formular of regression              */
/**********************************************************************/
double calc_lrbeta(void)
{
	double		XSum	= calc_sum(&LinearList, FALSE),
			YSum	= calc_sum(&LinearList, TRUE),
			XYSum	= calc_xysum(),
			XQSum	= calc_powsum(&LinearList, FALSE),
			Number	= (double)numbers_in_list(&LinearList);

	if((pow(XSum, 2.0) - (Number * XQSum)) == 0.0)
	{
		PError	= ERR_DIVBY0;
		return(0.0);
	}

	return( (((XSum * YSum) - (Number * XYSum)) / (pow(XSum, 2.0) - (Number * XQSum))) );
}





/**********************************************************************/
/*           Assess x value of linear regression given a y            */
/**********************************************************************/
double calc_lrassessx(double y)
{
	double		Alpha	= calc_lralpha(),
			Beta	= calc_lrbeta();

	if(Beta == 0.0)
	{
		PError	= ERR_DIVBY0;
		return(0.0);
	}

	return( ((y - Alpha) / Beta) );
}





/**********************************************************************/
/*           Assess y value of linear regression given an x           */
/**********************************************************************/
double calc_lrassessy(double x)
{
	double		Alpha	= calc_lralpha(),
			Beta	= calc_lrbeta();

	return( (Alpha + (Beta * x)) );
}



/**********************************************************************/
/*                    Calc correlation coefficient                    */
/**********************************************************************/
double calc_lrcorr(void)
{
	double		XSum	= calc_sum(&LinearList, FALSE),
			YSum	= calc_sum(&LinearList, TRUE),
			XYSum	= calc_xysum(),
			Number	= numbers_in_list(&LinearList),
			XDev	= calc_population(&LinearList, FALSE),
			YDev	= calc_population(&LinearList, TRUE);

	if(Number == 0.0 || XDev == 0.0 || YDev == 0.0)
	{
		PError	= ERR_DIVBY0;
		return(0.0);
	}

	return( ( ((XYSum / Number) - ((XSum / Number) * (YSum / Number))) / (XDev * YDev) ) );
}



/**********************************************************************/
/*                Calc covariant of linear regression                 */
/**********************************************************************/
double calc_lrcovar(void)
{
	double		XYSum	= calc_xysum(),
			Number	= (double)numbers_in_list(&LinearList),
			XAve	= calc_average(&LinearList, FALSE),
			YAve	= calc_average(&LinearList, TRUE);

	if(Number == 1.0)
	{
		PError	= ERR_DIVBY0;
		return(0.0);
	}

	return( ( (XYSum - (Number * XAve * YAve)) / (Number - 1) ) );
}
