package com.annimon.ownlang.parser.ast;

import org.junit.jupiter.api.Test;

import static com.annimon.ownlang.parser.ast.ASTHelper.*;
import static com.annimon.ownlang.parser.ast.BinaryExpression.Operator.*;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
 * @author aNNiMON
 */
public class OperatorExpressionTest {
    
    @Test
    public void testAddition() {
        assertValue(number(4), operator(ADD, value(2), value(2)).eval());
        assertValue(number(6), operator(ADD, value(1), operator(ADD, value(2), value(3))).eval());
        assertValue(string("ABCD"), operator(ADD, value("AB"), value("CD")).eval());
        assertValue(string("AB12"), operator(ADD, value("AB"), operator(ADD, value(10), value(2))).eval());
    }
    
    @Test
    public void testSubtraction() {
        assertValue(number(0), operator(SUBTRACT, value(2), value(2)).eval());
        assertValue(number(110), operator(SUBTRACT, value(100), operator(SUBTRACT, value(20), value(30))).eval());
    }
    
    @Test
    public void testMultiplication() {
        assertValue(number(4), operator(MULTIPLY, value(2), value(2)).eval());
        assertValue(number(30), operator(MULTIPLY, value(5), operator(MULTIPLY, value(-2), value(-3))).eval());
        assertValue(string("ABABAB"), operator(MULTIPLY, value("AB"), value(3)).eval());
    }
    
    @Test
    public void testDivision() {
        assertValue(number(3), operator(DIVIDE, value(6), value(2)).eval());
        assertValue(number(30), operator(DIVIDE, value(-900), operator(DIVIDE, value(60), value(-2))).eval());
    }
    
    @Test
    public void testDivisionZero() {
        assertValue(number(Double.POSITIVE_INFINITY), operator(DIVIDE, value(2.0), value(0.0)).eval());
    }
    
    @Test
    public void testDivisionZeroOnIntegers() {
        assertThrows(RuntimeException.class,
                () -> operator(DIVIDE, value(2), value(0)).eval());

    }
    
    @Test
    public void testRemainder() {
        assertValue(number(2), operator(REMAINDER, value(10), value(4)).eval());
        assertValue(number(5), operator(REMAINDER, value(15), operator(REMAINDER, value(40), value(30))).eval());
    }
    
    @Test()
    public void testRemainderZero() {
        assertValue(number(Double.NaN), operator(REMAINDER, value(2.0), value(0.0)).eval());
    }
    
    @Test
    public void testRemainderZeroOnIntegers() {
        assertThrows(RuntimeException.class,
                () -> operator(REMAINDER, value(2), value(0)).eval());
    }
    
    @Test
    public void testAND() {
        assertValue(number(0x04), operator(AND, value(0x04), value(0x0F)).eval());
        assertValue(number(0x00), operator(AND, value(0x04), value(0x08)).eval());
        assertValue(number(8), operator(AND, value(12), value(9)).eval());
    }
    
    @Test
    public void testOR() {
        assertValue(number(12), operator(OR, value(4), value(8)).eval());
        assertValue(number(0x0F), operator(OR, value(3), value(12)).eval());
        assertValue(number(0x0E), operator(OR, value(10), value(4)).eval());
    }
}
