/*
 * Starting with C: Primitive 2 operands calculator
 */

#include <stdio.h>
#include <ctype.h>				// for tolower()
#include <math.h>				// for pow(), sqrt(), ...
#include <stdlib.h>				// for exit()
#include <errno.h>				// for errno

#define MAXSLEN_LINEBUF		255+1		// for reading menu * nums as a strings

typedef enum { FALSE=0, TRUE } bool;		// our custom boolean type

// -------------------------------------------------------------------------------
// Read s from stdin until either 'ENTER is pressed or len-1 chars have been entered
// Remove ENTER (if any), null terminate s and return it
//
char *s_get(char *s, size_t len)
{
	register char *cp;

	for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
		;					// for-loop with empty body */
	*cp = '\0';					// null-terminate last character

	return s;
}

// -------------------------------------------------------------------------------
// Read num as a string from stdin and convert it to double. Demand a successfull
// convertion (otherwise keep asking). The conversion is done via the standard
// function: strtod()... read the C manual for more info.
//
void num_askuser( char *prompt, double *num, int maxslen_num )
{
	char inbuf[ maxslen_num ], *pend=NULL;
	bool stop;

	do
	{
		errno = 0;								// reset errno (defined in <errno.h>
		stop = TRUE;			// reset boolen flag

		printf( prompt );		// display a prompt asking for input
		fflush(stdin);			// clear the input buffer (stdin)
		s_get( inbuf, maxslen_num );	// read user input as string in inbuf

		*num = strtod( inbuf, &pend );	// convert it to double
		if ( *pend )			// *pend != '\0', possibly invalid num
		{
			if ( *num == 0.0 ) { 	// num was indeed invalid, not converted
				puts("\t*** error: invalid number");
				stop = FALSE;
				continue;
			}
			else {			// converted ok, invalid chars ignored
				printf("\t*** info: \"%s\" ignored\n", pend);
				stop = TRUE;
				continue;
			}
		}

		if ( errno == ERANGE )		// too big (or too small) num
		{
			puts("\t*** error: number out of range");
			stop = FALSE;
		}

	} while ( !stop || *inbuf =='\0' );

	return;
}

// -------------------------------------------------------------------------------
int main (void)
{
	double x, y; 				// operands
	int stop = FALSE;			// boolean controlling the main loop
#ifdef __GNUC__
	char inbuf[ MAXSLEN_LINEBUF ];		// menu choice 
#endif
	do {					// display menu
		puts("\n\nAVAILABLE OPTIONS\n=================");
		printf ("1 or +\t add\n2 or -\t subtract\n3 or *\t multiply\n4 or /\t divide\n5 or %%\t percentage\n6 or ^\t power\n7\t square root\n8 or x\t exit\n\nYour choice: ");

		fflush(stdin);			// clear input buffer
#ifdef __GNUC__
		switch ( tolower( *s_get(inbuf, MAXSLEN_LINEBUF) ) )// GET USER CHOICE
#else
		switch ( tolower( getchar() ) )	// GET USER CHOICE
#endif
		{
			case '1':		// 1. ADDITTION
			case '+':
				puts("\nAddition\n--------");
				num_askuser("1st addendum: ", &x, MAXSLEN_LINEBUF);
				num_askuser("2nd addendum: ", &y, MAXSLEN_LINEBUF);
				printf ("--------\nRESULT: %g + %g = %g\n",
				x, y, x + y );
			break;

			case '2':		// 2. SUBTRACTION
			case '-':
				puts("\nSubtraction\n-----------");
				num_askuser("Reducer   : ", &x, MAXSLEN_LINEBUF);
				num_askuser("Subtrahend: ", &y, MAXSLEN_LINEBUF);
				printf ("-----------\nRESULT: %g - %g = %g\n",
					x, y, x - y );
			break;

			case '3':		// 3. MULTIPLICATION
			case '*':
				puts("\nMultiplication\n--------------");
				num_askuser("1st number: ", &x, MAXSLEN_LINEBUF);
				num_askuser("2nd number: ", &y, MAXSLEN_LINEBUF);
				printf ("--------------\nRESULT: %g * %g = %g\n",
					x, y, x * y );
			break;

			case '4':		// 4. DIVISION
			case '/':
			{	double intpart;
				puts("\nDivision\n--------");
				num_askuser("Divident: ", &x, MAXSLEN_LINEBUF);
				do {							// demand non-zero value
				   num_askuser("Divisor : ", &y, MAXSLEN_LINEBUF);
				   if ( y == 0.0 )
					  puts("*** error: divisor cannot be 0");
				} while (y == 0.0);
				printf ("--------\nRESULT: %g / %g = %g (quotient = %g, remainder = %g)\n",
					x, y, x/y, trunc(x/y), y * modf(x/y, &intpart) );
			}
			break;

			case '5':		// 5. PERCENTAGE
			case '%':
				puts("\nPercentage\n----------");
				num_askuser("Number: ", &x, MAXSLEN_LINEBUF);
				printf ("----------\nRESULT: %g%% = %g\n", x, x/100);
			break;

			case '6':		// 6. POWER
			case '^':
				puts("\nPower\n-----");
				num_askuser("Base    : ", &x, MAXSLEN_LINEBUF);
				num_askuser("Exponent: ", &y, MAXSLEN_LINEBUF);
				printf ("-----\nRESULT: %g ^ %g = %g\n", x, y, pow(x, y));
			break;

			case '7':		// 7. SQUARE ROOT
				puts("\nSquare Root\n-----------");
				num_askuser("Number: ", &x, MAXSLEN_LINEBUF);
				printf ("-----------\nRESULT: sqrt(%g) = %g\n", x, sqrt(x) );
			break;

			case '8':		// 8. EXIT
			case 'x':
				stop = TRUE;
			break;

			default:		// ALL OTHER CHOICES
				puts("*** error: invalid choice, try again...");
			break;
		}	// switch

	} while ( !stop );

	exit( 0 );
}
