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

void print_help();
void push(char **, int *, char *);
char *pop(char **, int *);
char *operate(char *, char *, char *);
int is_oper(char *);
double fact(char *);

// String handling stuff.
// char *strdup(char *);
char *ftoa(double);
int is_num(char *);
void printf_f(char *);

int main(int argc, char const *argv[])
{
	int i, top = 0;
	char *stack[argc], *op1, *op2, *res;
	if(argc == 1) {
		print_help();
		return 0;
	}
	op1 = op2 = NULL;
	for(i = 0; i < argc-1; ++i) {
		if(top == argc) {
			fprintf(stderr, "[!] Stack overflow.\n");
			print_help();
			exit(EXIT_FAILURE);
		}
		if(is_oper(argv[i+1])) { // Check for operator.
			op2 = pop(&stack, &top);
			if(argv[i+1][0] != '!') // Factorial is unary, others are binary.
				op1 = pop(&stack, &top);
			else
				op1 = NULL;
			res = operate(argv[i+1], op1, op2);
			push(&stack, &top, res);
			free(op1);
			free(op2);
		}
		else if(is_num(argv[i+1])) { // Check for operand.
			push(&stack, &top, argv[i+1]);
		}
		else { // Error.
			fprintf(stderr, "[-] Invalid input.\n");
			print_help();
			exit(EXIT_FAILURE);
		}
	}
	printf_f(stack[0]);
	free(stack[0]);
	return 0;
}

// Stack functions start...

char *operate(char *oper, char *op1, char *op2) {
	// Apply operand to operators.
	char *res;
	float fres;
	switch(oper[0]) {
		case '+':
			res = ftoa(atof(op1) + atof(op2));
			break;
		case '-':
			res = ftoa(atof(op1) - atof(op2));
			break;
		case '*':
		case 'x':
			res = ftoa(atof(op1) * atof(op2));
			break;
		case '%':
			res = ftoa((int)(atof(op1)) % (int)(atof(op2)));
			break;
		case '^':
			res = ftoa(pow(atof(op1), atof(op2)));
			break;
		case '!':
			res = ftoa(fact(op2));
			break;
		case '/':
			fres = atof(op1) / atof(op2);
			if(oper[1] == '/') // For the '//' operator...
				fres = (int)fres;
			res = ftoa(fres);
			// else res = ftoa((int)(atof(op1) / atof(op2)));
			break;
	}
	return res;
}

char *pop(char **stack, int *top) {
	*top -= 1;
	if(*top < 0) {
		fprintf(stderr, "[!] Stack underflow.\n");
		print_help();
		exit(EXIT_FAILURE);
	}
	return stack[*top];
}

void push(char **stack, int *top, char *data) {
	char *elem;
	elem = strdup(data);
	stack[(*top)++] = elem;
}

// ...Stack functions end.

// String-handling functions start...

char *ftoa(double d) {
	// Return string from a double.
	char *f;
	f = (char *) malloc(32 * sizeof(char));
	if(f == NULL) {
		fprintf(stderr, "Malloc failed.\n");
		exit(EXIT_FAILURE);
	}
	sprintf(f, "%f", d);
	return f;
}

int is_oper(char *arg) {
	// Check if arg is an operator.
	switch(arg[0]) {
		case '+':
		case '-':
		case '*':
		case 'x':
		case '%':
		case '^':
		case '!':
			if(arg[1] == '\0')
				return 1;
			break;
		case '/':
			if(arg[1] == '\0' || (arg[1] == '/' && arg[2] == '\0'))
				return 1;
			break;
		default:
			break;
	}
	return 0;
}

int is_num(char *str) {
	// Check if str is a valid number, int or float.
	int dots = 0; // Keep track of number of dots in str.
	for(; *str; str++) {
		if(!isdigit(*str)){
			if(*str == '.'){
				// ++dots;
				if(++dots > 1)
					return 0;
			}
			else
				return 0;
		}
	}
	return 1;
}

void printf_f(char *str) {
	// Print float(as a string) upto the last signifant digit only.
	int i = 0, pos;
	while(str[i] != '.' && str[i] != '\0')
		++i;
	if(str[i] == '\0') { // Print the integer.
		printf("%s\n", str);
		return;
	}
	pos = i-1; // Stop before dot.
	++i;
	while(str[i] != '\0') {
		if(str[i] > '0') // If significant digit found after dot...
			pos = i; // ...set it as the furthest point to print to.
		++i;
	}
	str[pos + 1] = '\0';
	printf("%s\n", str);
}

// char *strdup(char *str) {
//	// Implementation of 'strdup', since some systems may not have it.
// 	unsigned int len;
// 	char *dup;
// 	len = strlen(str) + 1;
// 	dup = (char *) malloc(len * sizeof(char));
// 	memcpy(dup, str, len * sizeof(char));
// 	return dup;
// }

// ...String handling functions end.

double fact(char *str) {
	// Get factorial of str.
	int n = atoi(str);
	double res;
	for(res = 1.0; n > 1; --n)
		res *= n;
	return res;
}

void print_help() {
	printf("Help:\n");
	printf("Reverse Polish Notation Calculator\n");
	printf("Provide space-delimited RPN.\n");
	printf("Supported operators are:\n");
	printf("\t+\t - addition\n"
		   "\t-\t - subtraction\n"
		   "\t*, x\t - multiplication\n"
		   "\t/\t - division (floating point, e.g. 3/2=1.5, not 3/2=1)\n"
		   "\t//\t - integer division (e.g. 3/2=1)\n"
		   "\t%%\t - modulus, or \"remainder\" division (e.g. 14%%3=2 and 21%%7=0)\n"
		   "\t^\t - power (two carats '^^' for Windows systems.)\n"
		   "\t!\t - factorial (unary operator)\n");
}