//Coded entirely by Terence Cox
#include <iostream> 
#include <string>
#include <cstdlib> 
#include <math.h>
#include<iomanip>

using namespace std;

string substitute(double, double, string);
string simplify(bool,char, string);
double** fill2Ddyrow6(int, int, double, double, double, double, double, double**);

double evaluator(string p_equ, bool debug)
{
start1:
	int fp = -1, lp = -1;
	//Search for Paraenthesis
	for (int c = 0; c < p_equ.size(); c++)
	{
		if (p_equ.at(c) == '(')
			fp = c;
		if (p_equ.at(c) == ')')
		{
			lp = c;
			break;
		}
	}
	//if parenthesis exist extract the expression within them
	if (fp < 0 && lp < 0) 
		{
		if (debug == true)
			cout << "no more parentheses" << endl;
		}
	else
	{
		//extract from parenthesis and start the evauluation with the rest, then replaces solution in primary equ
		string s_equ(p_equ, fp + 1, (lp - 1) - fp);
		//solves what's in the parenthesis and eliminates the ()'s
		p_equ.replace(fp, (lp + 1) - fp, to_string(evaluator(s_equ,debug)));
		goto start1;
	}
	//perform PEMDAS
	p_equ = simplify(debug,'-', simplify(debug,'+', simplify(debug,'*', simplify(debug,'/', simplify(debug,'^', p_equ)))));

	//string fsresult(p_equ, fp + 1, lp - fp);
	double fresult = stod(p_equ);
	return fresult;
}


string simplify(bool debug, char oper, string expr)
	{
	start2:
	int p, fnfp = -1, snlp = -1, reader = 1, runs = 0;
	double num1, num2, result;
	bool single_neg=false;

	p = -1;
	for (int c = 0; c < expr.size(); c++)
	{
		if (expr.at(c) == oper)
		{
			if (c > 0) //prevents error when a negative number is first in the expression
			{
				//assigns the position of the operator to p
				p = c;
				break;
			}

		}
	}
	if (p < 0)
		{
		if (debug == true)
			cout << "no " << oper << endl;
		}
	else
	{
		runs++;
		while (expr.at(p - reader) == '-' || expr.at(p - reader) == '.' || isdigit(expr.at(p - reader)))
		{
			fnfp = p - reader;
			reader++;
			if (p - reader < 0) //handles exception where this reads outside of memory
				break;
			if (expr.at(fnfp) == '-')//hnadles a runtime error which causes negative numbers to be omitted
			{
				break;
			}
		}
		reader = 1;
		while (expr.at(p + reader) == '-' ||expr.at(p + reader) == '.' || isdigit(expr.at(p + reader)))
		{
			snlp = p + reader;
			//This code checks to make sure only one negative symbol is allowed, will break if another is found.
			if (expr.at(snlp) == '-')
			{
				if (single_neg == true)
					break;
				else
					single_neg = true;
			}
			reader++;
			if (p + reader > expr.size() - 1) //handles exception where this reads outside of memory
				break;
		}
		string strnum1(expr, fnfp, p - fnfp);
		string strnum2(expr, p + 1, snlp - p);
		double num1 = stod(strnum1);
		double num2 = stod(strnum2);


		switch (oper)
		{
		case '^':
			result = pow(num1, num2);
			break;
		case '/':
			result = num1 / num2;
			break;
		case '*':
			result = num1 * num2;
			break;
		case '+':
			result = num1 + num2;
			break;
		case '-':
			result = num1 - num2;
			break;
		}
		//result = (int)(result * 10000.0) / 10000.0;
		if (debug == true)
			cout << "after performing " << oper << " the result is " << result << endl;
		//replace the solve part of the expression into the entire expression
		expr.replace(fnfp, (snlp + 1) - fnfp, to_string(result));
		goto start2;
	}
	if (debug == true)
		cout << "solved " << runs << " " << oper << " operations" << endl;
	return expr;
}

string substitute(double X, double Y, string equ)
{
	//checks each character for X or Y to replace them with actual values
	for (int c = 0; c < equ.size(); c++)
	{
		if (equ.at(c) == 'X' || equ.at(c) == 'x')
			equ.replace(c, 1, to_string(X));
		if (equ.at(c) == 'Y' || equ.at(c) == 'y')
			equ.replace(c, 1, to_string(Y));
	}
	return equ;
}



int main()
{
Begin:
	cout.precision(5);
	string equation;
	int steps;
	float X1, X2, Y1, K1, Z1, K2, h, newY;
	double change;
	bool debug= false,use_table=true,userres;
	cout << "What is the differential equation you want to use Euler's method on?\ny'= ";
	cin >> equation;
	cout << "Fill this in\n (y(x) = ___)\n    |     \n    v     \nx= ";
	cin >> X1;
	cout << "and this\n (y(" << X1 << ") = __)\n           |\n           v\n        y= ";
	cin >> Y1;
	cout << "How many steps of accuracy do you want to use: ";
	cin >> steps;
	cout << "What is the value of X you want to use to solve Y" << endl;
	cin >> X2;
	h = (X2 - X1) / steps;
	cout << "The size of h is " << h << "." << endl;
	cout << "Last Question, do you want to see the table of values(Takes longer to process) 1 or 0" << endl;
	cin >> use_table;
	//create a new 2D Dynamic Array
	double** table = new double*[steps + 1];
	for (int i = 0; i < steps + 1; ++i)
		table[i] = new double[6];

	//fill in table with obtained information
	table = fill2Ddyrow6(0, 0, X1, Y1, 0,0,0, table);

	//perform the Euler's process for the inputted amount of steps
	for (int t = 0; t < steps; t++)
	{
		K1 = evaluator(substitute(table[t][1], table[t][2], equation),debug);
		table[t][3] = K1;
		Z1 = table[t][2] + abs(h)*K1;
		table[t][4] = Z1;
		K2 = evaluator(substitute(X1 + (h*(t + 1)), Z1, equation),debug);
		table[t][5] = K2;
		newY = table[t][2]+((K1+K2)/2)*abs(h);

		table = fill2Ddyrow6(t + 1, t + 1, X1 + (h*(t + 1)), newY, 0, 0, 0, table);
	}

	//outputs the values in the table into the console
	if (use_table == true|| use_table == 't'|| use_table == 'T' )
		{
		cout << left << setw(16) << "step i" << setw(16) << "X1" << setw(16) << "Y1" << setw(16) << "K1" << setw(16) << "Z1" << setw(16) << "K2" << endl;
		for (int row = 0; row < steps + 1; row++)
			{
			cout << endl;
			for (int column = 0; column < 6; column++)
				{
				cout << setw(16) << table[row][column];
				}
			}
		}
	cout << endl;
	cout << "The Final Answer is " << table[steps][2] << endl;
	cout << "Done" << endl;
	cout << "Do you want to enter another Diffy-Q?, 1(True) or 0(False)" << endl;
	cin >> userres;
	if (userres == 1)
		goto Begin;
	system("pause");
}

double** fill2Ddyrow6(int row, int step, double X, double Y, double  K1,double Z1,double K2, double** tablef)
{
	tablef[row][0] = step;
	tablef[row][1] = X;
	tablef[row][2] = Y;
	tablef[row][3] = K1;
	tablef[row][4] = Z1;
	tablef[row][5] = K2;
	return tablef;
}


