/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ca.mcmaster.cas.matlab2smt;

import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 *
 * @author matthew
 */
@RunWith(JUnit4.class)
public class MatlabExpressionBinaryOperationTest {
    /**
     * Test of type method, of class MatlabExpressionBinaryOperation.
     */
    @Test
    public void testWithSimpleRealVariablesAddedTogether() {
        Variable l = new Variable("a", new RealVariableType()), r = new Variable("b", new RealVariableType());
        MatlabExpressionBinaryOperation instance = new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Addition, r);
        
        String CVC3Output = "(a + b)";
        VariableType expressionType = new RealVariableType();
        
        assertEquals(CVC3Output, instance.getCheckerOutput(generator, expressionType));
        assertEquals(expressionType, instance.actualOutputTypeForRequestedOutput(expressionType));
    }
    /**
     * Test of type method, of class MatlabExpressionBinaryOperation.
     */
    @Test
    public void testWithSimpleFixedPointVariablesAddedTogether() {
        Variable l = new Variable("a", new FixedPointVariableType(true, 17, 4)), r = new Variable("b", new FixedPointVariableType(true, 17, 4));
        MatlabExpressionBinaryOperation instance = new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Addition, r);
        
        String CVC3Output = "BVPLUS(17,a,b)";
        VariableType expressionType = new FixedPointVariableType(true, 17, 4);
        
        assertEquals(CVC3Output, instance.getCheckerOutput(generator, expressionType));
        assertEquals(expressionType, instance.actualOutputTypeForRequestedOutput(expressionType));
    }
    /**
     * Test of setLHS/RHS, verify they set variables correctly.
     */
    @Test
    public void testSettingsLHSAndRHS() {
        Variable l = new Variable("a", new RealVariableType()), r = new Variable("b", new RealVariableType());
        MatlabExpressionBinaryOperation instance = new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Addition, r);
        
        //Reverse a+b to b+a.
        instance.setLHS(r);
        instance.setRHS(l);
        
        String CVC3Output = "(b + a)";
        VariableType expressionType = new RealVariableType();

        assertEquals(expressionType, instance.actualOutputTypeForRequestedOutput(expressionType));
        assertEquals(CVC3Output, instance.getCheckerOutput(new CVC3Generator(), expressionType));
    }
    
    /**
     * Test when RHS/LHS have different castable types.
     */
    @Test
    public void testCastableTypes() {
        MatlabLiteral lit = new MatlabLiteral("5");
        Variable var = new Variable("A", new RealVariableType());
        VariableType exprType;
        
        // Test when adding a Real var to a lit, type always becomes Real.
        exprType = new RealVariableType();
        assertEquals(exprType, new MatlabExpressionBinaryOperation(lit, MatlabBinaryOperation.Addition, var).actualOutputTypeForRequestedOutput(exprType));
        assertEquals(exprType, new MatlabExpressionBinaryOperation(var, MatlabBinaryOperation.Addition, lit).actualOutputTypeForRequestedOutput(exprType));

        // Test when comparing a Real var to a lit, type always becomes boolean.
        exprType = new BooleanVariableType();
        assertEquals(exprType, new MatlabExpressionBinaryOperation(lit, MatlabBinaryOperation.Equals, var).actualOutputTypeForRequestedOutput(exprType));
        assertEquals(exprType, new MatlabExpressionBinaryOperation(var, MatlabBinaryOperation.Equals, lit).actualOutputTypeForRequestedOutput(exprType));
    }

    /**
     * Test operations changing types (== doing REAL+REAL -> BOOLEAN)
     */
    @Test
    public void testCastingOperations() {
        MatlabLiteral l = new MatlabLiteral("5"), r = new MatlabLiteral("6");
        MatlabExpressionBinaryOperation exp = new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Equals, r);

        // Test when adding a Real var to a lit, type always becomes Real.
        VariableType expressionType = new BooleanVariableType();
        assertEquals(expressionType, exp.actualOutputTypeForRequestedOutput(expressionType));
        assertEquals("(5 = 6)", exp.getCheckerOutput(generator, expressionType));
    }

    /**
     * Test operations fails when a requested type is invalid!
     */
    @Test(expected=IllegalArgumentException.class)
    public void testInvalidOutputType() {
        MatlabLiteral l = new MatlabLiteral("5"), r = new MatlabLiteral("6");
        MatlabExpressionBinaryOperation exp = new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Equals, r);

        exp.getCheckerOutput(generator, new RealVariableType()); //Returns a boolean, so will fail.
    }

    /**
     * Test doing multiple boolean operations AND'd together.
     */
    @Test
    public void testBooleanAndFromComparision() {
        MatlabExpression l = new Variable("a", new RealVariableType()), r = new MatlabLiteral("6"),
                      l2 = new Variable("b", new RealVariableType()), r2 = new MatlabLiteral("8");
        MatlabExpressionBinaryOperation exp = new MatlabExpressionBinaryOperation(new MatlabExpressionBinaryOperation(l, MatlabBinaryOperation.Equals, r),
                MatlabBinaryOperation.BooleanAnd,
                new MatlabExpressionBinaryOperation(l2, MatlabBinaryOperation.Equals, r2));

        // Test when adding a Real var to a lit, type always becomes Real.
        VariableType expressionType = new BooleanVariableType();
        assertEquals(expressionType, exp.actualOutputTypeForRequestedOutput(expressionType));
        assertEquals("((a = 6) AND (b = 8))", exp.getCheckerOutput(generator, expressionType));
    }

    CheckerGenerator generator = new CVC3Generator();
}
