package expressions;

import org.junit.jupiter.api.*;

public class ExpressionTest {

    @Test
    public void testMethod_shouldNotFail() {
        Assertions.assertTrue(true);
    }

    @Test
    public void testUnaryFunctionException() {
        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            Expression thisdoesnotexist = new UnaryFunctionExpression("thisdoesnotexist", new XExpression());
            thisdoesnotexist.eval(0);
        });
    }

    @Test
    public void testExpression() {
    	final double epsilon = 1e-15;
    	
        Expression e =  new OperatorExpression(OperatorType.TIMES,
                (new OperatorExpression(OperatorType.DIVIDE,
                                        new NumberExpression(1), new NumberExpression(2))),
                new XExpression());
        
        Assertions.assertEquals(e.eval(-2), -1, epsilon);
        Assertions.assertEquals(e.eval(-1.5), -0.75, epsilon);
        Assertions.assertEquals(e.eval(-1), -0.5, epsilon);
        Assertions.assertEquals(e.eval(-0.5), -0.25, epsilon);
        Assertions.assertEquals(e.eval(0), 0, epsilon);
        Assertions.assertEquals(e.eval(0.5), 0.25, epsilon);
        Assertions.assertEquals(e.eval(1), 0.5, epsilon);
        Assertions.assertEquals(e.eval(1.5), 0.75, epsilon);
        Assertions.assertEquals(e.eval(2), 1, epsilon);
        
        Expression e2 = new UnaryFunctionExpression("sin", new XExpression());
        Assertions.assertEquals(e2.eval(0), 0, epsilon);
        Assertions.assertEquals(e2.eval(Math.PI/2), 1, epsilon);
        Assertions.assertEquals(e2.eval(Math.PI), 0, epsilon);
        Assertions.assertEquals(e2.eval((3./2.)*Math.PI), -1, epsilon);
        Assertions.assertEquals(e2.eval(2*Math.PI), 0, epsilon);

        Expression e3 = new NumberExpression(5);
        for (double x = -5; x < 5; x += 0.1) {
            Assertions.assertEquals(e3.eval(x), 5, epsilon);
        }   

        Expression e4 = new UnaryFunctionExpression("sin", new OperatorExpression(OperatorType.POWER, new XExpression(), new NumberExpression(2)));
        for (double x = -5; x < 5; x += 0.1) {
            Assertions.assertEquals(e4.eval(x), Math.sin(x*x), epsilon);
        }

        Expression e5 = new UnaryFunctionExpression(
            "log",
            new OperatorExpression(
                OperatorType.PLUS,
                new NumberExpression(3),
                new OperatorExpression(
                    OperatorType.TIMES,
                    new NumberExpression(5),
                    new XExpression()
                )
            )
        );
        for (double x = -5; x < 5; x += 0.1) {
            Assertions.assertEquals(e5.eval(x), Math.log(3+5*x), epsilon);
        }

        Expression e6 = new UnaryFunctionExpression(
            "log",
            new UnaryFunctionExpression(
                "sin",
                new XExpression()
            )
        );
        for (double x = -5; x < 5; x += 0.1) {
            Assertions.assertEquals(e6.eval(x), Math.log(Math.sin(x)), epsilon);
        }
    }
}
