/* ===========================================================
 * DUMMY CALC 1.0, by migf1
 *
 * A dummy calculator, just to demonstrate the use of a jump-table
 * consisting of function pointers, in ANCI C. No overflow checks, no
 * operator priorities, no parentheses... just dummy cumulative calcs
 * with an operator and an operand per line (some operators are unary
 * so they ignore the operand and are applied to the previous result.
 *
 * 15 Supported Operators:
 *
 *	addition	(+)
 *	subtraction	(-)
 *	multiplication	(*)
 *	division	(/)
 *	modulus		(%)
 *	power		(^)
 *	exponent of e	(e)	unary
 *	square root	(@)	unary
 *	cube root	(#)	unary
 *	negation	(~)	unary
 *	factorial	(!)	unary
 *	natural log	(')	unary
 *	base-10 log	(")	unary
 *	integral part	(_)	unary
 *	fractional part	(.)	unary
 *
 * Type h inside the program for help
 * ===========================================================
 */

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

// -- Macros ----------------

#define MAXINBUF	(255+1)
#define MAXOPS		15				// max number of supported ops

// --- Custom Types ----------

typedef enum { FALSE=0, TRUE } Bool;			// our custom boolean type
typedef enum {						// operator codes
	ADD, SUB, MUL, DIV, MOD, POW, EXPO, SQRT, CBRT, NEG,
	FACT, LOGE, LOGTEN, TRUNC, FRAC
} OpCode;

typedef struct opstruct {				// operator structure
	char symbol;					// a single char
	OpCode code;					// corersponding code
	Bool unary;					// UNUSED: is unary operator?
} OpStruct;

// -------------------------------------------------------------------------------------
void help( void )
{
	puts("\n\tcommands are case sensitive");
	puts("\tx\t: exit");
	puts("\tc\t: clear result");
	puts("\t~\t: -result\t\tNegation");
	puts("\t!\t: result!\t\tFactorial");
	puts("\t_\t: trunc( result )\tTruncate Integral Part");
	puts("\t.\t: fractional( result )\tFractional Part of Result");
	puts("\te\t: exp( result )\t\tExponent of Result");
	puts("\t'\t: log( result )\t\tNatural Logarithm");
	puts("\t\"\t: log10( result )\tBase 10 Logarithm");
	puts("\t@\t: sqrt( result )\tSquare Root");
	puts("\t#\t: cbrt( result )\tCube Root");
	puts("\t+ num\t: result + num\t\tAddition");
	puts("\t- num\t: result - num\t\tSubtraction");
	puts("\t* num\t: result * num\t\tMultiplication");
	puts("\t/ num\t: result / num\t\tDivision");
	puts("\t% num\t: result % num\t\tModulus (remainder)");
	puts("\t^ num\t: result ^ num\t\tPower");
	putchar('\n');

	return;
}

// Functions for Supported Operations (some of them only use their first argument)

// -------------------------------------------------------------------------------------
double add( const double x, const double y )			// addition
{ return x + y; }
// -------------------------------------------------------------------------------------
double sub( const double x, const double y )			// subtraction
{ return x - y; }
// -------------------------------------------------------------------------------------
double mul( const double x, const double y )			// multiplication
{ return x * y; }
// -------------------------------------------------------------------------------------
double over( const double x, const double y )			// division
{ return y != 0 ? x/y : 0; }
// -------------------------------------------------------------------------------------
double modulus( const double x, const double y )		// x modulus y
{ return y != 0 ? fmod(x, y) : 0; }
// -------------------------------------------------------------------------------------
double pwr( const double x, const double y )			// power: x at y
{ return pow(x, y); }
// -------------------------------------------------------------------------------------
double expo( const double x, const double y )			// power: e at x
{ return exp(x); }
// -------------------------------------------------------------------------------------
double sqroot( const double x, const double y )			// square root of x
{ return x < 1 ? 0 : sqrt(x); }
// -------------------------------------------------------------------------------------
double cbroot( const double x, const double y )			// cube root of x
{ return cbrt(x); }
// -------------------------------------------------------------------------------------
double neg( const double x, const double y )			// negation of x
{ return x != 0 ? -x : 0; }
// -------------------------------------------------------------------------------------
double fact( const double x, const double y )			// factorial of x
{ return x < 2 ? 1 : x * fact(x-1, 0.0); }
// -------------------------------------------------------------------------------------
double loge( const double x, const double y )			// natural log of x
{ return x < 1 ? 0 : log(x); }
// -------------------------------------------------------------------------------------
double logten( const double x, const double y )			// base-10 log of x
{ return x < 1 ? 0 : log10(x); }
// -------------------------------------------------------------------------------------
double truncate( const double x, const double y )		// integral part of x
{ return trunc(x); }
// -------------------------------------------------------------------------------------
double frac( const double x, const double y )			// fractional part of x
{ return x - trunc(x); }

// -------------------------------------------------------------------------------------
// Return the operator code that corresponds to sym, or -1 on failure
//
OpCode sym2opcode( const char sym, const OpStruct *tabops )
{
	int i;
	for (i=0; i < MAXOPS; i++)
		if ( sym == tabops[i].symbol )
			return tabops[i].code;

	return -1;
}

// -------------------------------------------------------------------------------------
// Read the input line and assign appropriate values to x, opsym and opcode
// (return the number of input tokens read)
//
int get_input(	double *x, char *opsym, OpCode *opcode,
		const OpStruct tabops[], const double res )
{
	char inbuf[MAXINBUF] = "";				// our input buffer
	int ntokens;						// # of tokens to return

	do {
		printf("%9lg: ", res);				// show prompt
		fgets(inbuf, MAXINBUF, stdin);			// read input line
		ntokens = sscanf(inbuf, "%c %lg", opsym, x);	// extract opsym & x

		if ( ntokens == 0 )				// no tokens read
			continue;
								// opsym was a command
		if ( *opsym == 'x' || *opsym == 'c' || *opsym == 'h') {
			*opcode = -1;				// invalidate opcode
			return 1;				// exit function
		}
								// opsym was operator
		*opcode = sym2opcode(*opsym, tabops);		// assign valid opcode

	} while ( ntokens == 0 || *opcode == -1);

	return ntokens;						// # of tokens read
}
// -----------------------------------------------------------------------
int main( void )
{
	double x=0.0, res=0.0;				// x from input, res for result
	char opsym;					// operator symbol from input
	OpCode opcode;					// operator code (from opsym)

							// table of operator structures
	const OpStruct tabops[ MAXOPS ] = {		// (.unary inited but not used)
		{'+', ADD, FALSE}, {'-', SUB, FALSE}, {'*', MUL, FALSE},
		{'/', DIV, FALSE}, {'%', MOD, FALSE}, {'^', POW, FALSE},
		{'e', EXPO, TRUE}, {'@', SQRT, TRUE}, {'#', CBRT, TRUE},
		{'~', NEG, TRUE }, {'!', FACT, TRUE}, {'\'', LOGE, TRUE},
		{'\"', LOGTEN, TRUE}, {'_', TRUNC, TRUE}, {'.', FRAC, TRUE}
	};
							// jump-table of func pointers
	double (*pf[MAXOPS])(const double, const double) = {
		&add, &sub, &mul, &over, &modulus, &pwr, &expo, &sqroot, &cbroot,
		&neg, &fact, &loge, &logten, &truncate, &frac
	};

	puts("\tDUMMY CALC 1.0 by migf1\n\t\tprompt is the result, type h for help\n");
	do {						// our main loop
							// read opsym, x and opcode
		get_input(&x, &opsym, &opcode, tabops, res);
		if ( opsym == 'x')			// exit program
			break;
		if ( opsym == 'c' ) {			// clear the result
			res = 0.0;
			continue;
		}
		if (opsym == 'h') {			// show help
			help();
			continue;
		}

		// at this point opcode != -1
		res = (*pf[opcode])(res, x);		// do the requested operation
	} while ( 1 );

	exit( EXIT_SUCCESS );
}
