package me.despawningbone.arithtrain;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

public class ExpressionTest {

	@Test
	public void testEvaluation() {  //will not test stepEval since it is vulnerable by design and needs evaluateWithSteps as a linter
		//normal cases
		assertEquals(String.valueOf(4.0), ExpressionGenerator.evaluateWithSteps("2+2").get(1));   //normal operation
		assertEquals(String.valueOf(2.5), ExpressionGenerator.evaluateWithSteps("2+2/4").get(2));   //priority
		assertEquals(String.valueOf(1-(2-3/(4+5d))-2), ExpressionGenerator.evaluateWithSteps("1-(2-3/(4+5))-2").get(5));  //parentheses
		assertEquals(String.valueOf(2.0E7), ExpressionGenerator.evaluateWithSteps("1E10 / 500").get(1));   //E notation

		assertEquals(Arrays.asList("5/(4%8)+8-34+43*2", "5/4.0+8-34+43*2" ,"1.25+8-34+43*2", "1.25+8-34+86.0", "9.25-34+86.0", "-24.75+86.0", "61.25"), ExpressionGenerator.evaluateWithSteps("5/(4%8)+8-34+43*2"));  //steps
		
		//edge cases
		assertEquals(String.valueOf(0.0), ExpressionGenerator.evaluateWithSteps("1 / 1E1000").get(1));
		assertEquals(String.valueOf(23.0), ExpressionGenerator.evaluateWithSteps("4 0+++2 8*-   1 1 %  -1 2+-++-+--- 8   +- 1 ").get(5));   //space placements + positive/negative indicators
		assertEquals(String.valueOf(210.0), ExpressionGenerator.evaluateWithSteps("7(((((5))))6)").get(2));   //redundant parentheses
		assertEquals(Arrays.asList(""), ExpressionGenerator.evaluateWithSteps(""));   //empty string
		assertEquals(String.valueOf(-2323628.56), ExpressionGenerator.evaluateWithSteps("2374*-982+19005-11396+3044E-2").get(4));  //negative E notation
		
		//invalid cases
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("32-(432+13/3/2"));   //imbalanced parentheses
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("!3a9-(231)-439-3"));  //invalid characters
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("\\39-(231)-439-3"));  //invalid characters
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("(1-1)%0"));   //division by zero
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("1/0"));   //division by zero
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps("1E100 * 1E1000 - 1"));   //infinity
		testEvaluationThrows(() -> ExpressionGenerator.evaluateWithSteps(null));   //null
	}
	
	private void testEvaluationThrows(Runnable run) {
		try {
			run.run();
		} catch(IllegalArgumentException e) {
			return;
		}
		fail();
	}
	
	@Test
	public void testGenerator() {
		
		Main.initOperators();  //need to manually invoke this since main class initConfig() is not invoked in test cases
		
		ExpressionGenerator exp1 = new ExpressionGenerator(19, 5, -1000, 19343);
		
		//normal cases
		ExpressionGenerator exp2 = new ExpressionGenerator(10, 3, 0, 1000);
		assertEquals(true, exp1.isHarder(exp2));
		
		assertEquals("ExpGen{bracket=19,opCount=5,range=-1000-19343}", exp1.toString());
		
		String exp = exp1.generate().get(0);
		assertTrue(exp.length() - exp.replaceAll("(?<=[0-9)])[+\\-*/%]", "").length() == 5);  //check how many actual operators are there, excluding negative indicators ("-"s not preceding numbers or parentheses, i.e. "+-" etc)
		
		//volume test
		for(int i = 0; i < 10000; i++) {
			List<String> gen = exp1.generate();
			assertTrue(ExpressionGenerator.evaluateWithSteps(gen.get(0)).indexOf(gen.get(gen.size() - 1)) != -1);
		}
	}

}
