import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;

public class Calc4 {
	/****************************************
	 *These need to be set for your system. *
	 *                                      */
	static final String driveLetter = "C";
	static final String javaBin = "Users\\HP\\Desktop\\JDD\\SW\\jdk-17_windows-x64_bin\\jdk-17.0.8\\bin\\";
	static final String expressionFolder = "Users\\HP\\Desktop\\JDD\\";
	/*                                      *
	 ****************************************/

	static final String separator = ":\\";
	static final String javacCommand = driveLetter + separator + javaBin + "javac";
	static final String javaCommand  = driveLetter + separator + javaBin + "java";
	static final String expressionsClassName = "Expressions";
	
	static final String expressionFile = driveLetter + separator + expressionFolder + expressionsClassName + ".java";

	static final double EPSILON = Math.pow(10, -15);
	static final int N_EXPRESSIONS = 7000;
	static final int N_SETS = 5;

	public static void main(String[] args) throws IOException, InterruptedException {	
		String[] e = new String[N_EXPRESSIONS];
		double[] jdv = new double[N_EXPRESSIONS];
		Fraction fv;
		double dv;
		int j;
		for (j = 0; j < N_SETS; j++) {
			for (int i = 0; i < N_EXPRESSIONS; i++) {
				e[i] = generateRandomExpression();
			}

			jdv = javaEvaluateExpressions(e);
			System.out.println("\nDeltas: \n");
			for (int i = 0; i < N_EXPRESSIONS; i++) {
				fv = evaluateExpression(e[i]);
				dv = (double)(fv.n) / fv.d;

                if (compareDoubles(dv, jdv[i])) {
				    System.out.println("evaluateExpression      : " + e[i] + " = " + fv + " = " + dv);
				    System.out.println("javaEvaluateExpressions : " + e[i] + " = " + jdv[i]);
				    System.out.println();
                }
                else {
                    System.out.println("not equal");
                    System.exit(1);
                }
			}
			System.out.println("Completed set " + (j + 1) + " of " + N_SETS + "\n");
		}
		System.out.println("Finished");
	}

	public static Fraction evaluateExpression(String e) {
		e = e.replaceAll(" ", "");
		return eval(e).reduce();
	}

	public static Fraction eval(String e) {
		Fraction v = new Fraction(0,1);

		if (e.contains("+")) {
			String[] p = e.split("\\+", 2);
			String p1 = p[0];
			String p2 = p[1];
			v = eval(p1).add(eval(p2));			
		}
		else if (e.contains("-")) {
			int i = e.lastIndexOf("-");  // right-to-left scan for -
			v = eval(e.substring(0, i)).subtract(eval(e.substring(i + 1)));	
		}
		else if (e.contains("*")) {
			String[] p = e.split("\\*", 2);
			String p1 = p[0];
			String p2 = p[1];
			v = eval(p1).multiply(eval(p2));	
		}
		else if (e.contains("/")) {
			int i = e.lastIndexOf("/");  // right-to-left scan for /
			v = eval(e.substring(0, i)).divide(eval(e.substring(i + 1)));	
		}
		else {
			v = new Fraction((int)Double.parseDouble(e), 1);
		}
		return v;
	}
	public static Fraction eval2(String e) {
		Fraction v = new Fraction(0,1);
		
		if (e.contains("+")) {
			String[] p = e.split("\\+");
			for (int i = 0; i < p.length; i++) v = v.add(eval2(p[i]));	
		}
		else if (e.contains("-")) {
			String[] p = e.split("\\-");
			v = eval2(p[0]);
			for (int i = 1; i < p.length; i++) v = v.subtract(eval2(p[i]));		
		}
		else if (e.contains("*")) {
			String[] p = e.split("\\*");
			v = eval2(p[0]);
			for (int i = 1; i < p.length; i++) v = v.multiply(eval2(p[i]));		
		}
		else if (e.contains("/")) {
			String[] p = e.split("\\/");
			v = eval2(p[0]);
			for (int i = 1; i < p.length; i++) v = v.divide(eval2(p[i]));	
		}
		else {
			v = new Fraction((int)Double.parseDouble(e), 1);
		}
		return v;
	}
	public static double[] javaEvaluateExpressions(String[] e) throws IOException, InterruptedException {
		String code = "public class " + expressionsClassName + " {\n   public static void main(String[] args) {\n";

		for (int i = 0; i < e.length; i++) {
			code += "      System.out.println(" + e[i] + ");\n";
		}

		code += "   }\n}";

		try {
			System.out.print("Write code to " + expressionFile);
			FileWriter myWriter = new FileWriter(expressionFile);
			myWriter.write(code);
			myWriter.close();
			System.out.println(" [done]");
		} 
		catch (IOException ex) {
			System.out.println(" [error]");
			ex.printStackTrace();
			System.exit(0);
		}

		System.out.print("Compile " + expressionFile);
		Process p1 = Runtime.getRuntime().exec(javacCommand + " " + expressionFile);
		p1.waitFor();
		System.out.println(" [done]");
		System.out.print("Run " + expressionsClassName);
		Process p2 = Runtime.getRuntime().exec(javaCommand + " " + "-classpath " + driveLetter + separator + expressionFolder + " " + expressionsClassName);
		System.out.println(" [done]");

		double[] values = new double[e.length];
		String l;
		int i = 0;
		//while((l = (p2.inputReader().readLine())) != null) {
		//	values[i] = Double.parseDouble(l);
		//	i++;
		//}

		BufferedReader output = new BufferedReader(new InputStreamReader(p2.getInputStream()));
		while((l = output.readLine()) != null) {
			values[i] = Double.parseDouble(l);
			i++;
		}

		return values;
	}

	public static String generateRandomExpression() {
		String e = "";

		int operations = (int)(Math.random() * 11);

		for (int i = 0; i < operations; i++) {
			String operator = null;
			int choose = (int)(Math.random() * 4);
			switch (choose) {
			case 0: operator = "+";
			break;
			case 1: operator = "-";
			break;
			case 2: operator = "*";
			break;
			case 3: operator = "/";
			break;
			}
			e += (int)(Math.random() * 100) + 1 + ".0" + " " + operator + " ";
		}
		e += (int)(Math.random() * 100) + 1 + ".0";

		return e;
	}

	private static boolean compareDoubles(double dv, double jdv) {
		return true;
	}
}