package calc.dsl

import java.math.BigDecimal
import rational.Rational
import org.junit._, Assert._
 
class Calc3Test {

    val delta = Float.Epsilon
    val delta2 = 1.0e-6

    @Before
    def setUp: Unit = {
    }

    @After
    def tearDown: Unit = {
    }

    @Test
    def parseAnExpr0 =
    assertEquals(
        Number(Rational(5)),
          Calc3.parse("5") )

    @Test
    def parseAnExpr1 =
    assertEquals(
        UnaryOp("-", Number(Rational(5))),
          Calc3.parse("-5"))

    @Test
    def parseAnExpr2 =
    assertEquals(
        Number(Rational(5)),
          Calc3.parse("(5)"))
    @Test
    def parseAnExpr3 =
    assertEquals(
        BinaryOp("+", Number(Rational(5)), Number(Rational(4))),
          Calc3.parse("5 + 4"))

    @Test
    def parseAnExpr4 =
    assertEquals(
        BinaryOp("+", Number(Rational(5)), Number(Rational(4))),
          Calc3.parse("(5 + 4)"))
    @Test
    def parseAnExpr5 =
    assertEquals(
        BinaryOp("+", BinaryOp("+", Number(Rational(5)), Number(Rational(4))),
                 Number(Rational(3))),
                   Calc3.parse("(5 + 4) + 3"))
    @Test
    def parseAnExpr6 =
    assertEquals(
        BinaryOp("+",BinaryOp("+",Number(Rational(5)),Number(Rational(4))),
                 BinaryOp("+", Number(Rational(3)),Number(Rational(2)))),
                   Calc3.parse("(5 + 4) + (3 + 2)")
    )

    @Test
    def parseAnExpr7 =
    assertEquals(
        BinaryOp("+",
                 BinaryOp("+", Number(Rational(5)),Number(Rational(4))),
                 Number(Rational(3))),
                   Calc3.parse("5 + 4 + 3")
    )

    @Test
    def parseAnExpr8 =
    assertEquals(
        BinaryOp("/",
                 BinaryOp("*", Number(Rational(5)),Number(Rational(4))),
                 Number(Rational(3))),
                   Calc3.parse("5 * 4 / 3")
    )

    @Test
    def add1 =
    assertEquals(Calc3.evaluate("1 + 1").toString, "2")

    @Test
    def plus0 =
    assertEquals(Calc3.evaluate("1").toString, "1")

    @Test
    def plus1 =
    assertEquals(Calc3.evaluate("+1").toString, "1")

    @Test
    def minus1 =
    assertEquals(Calc3.evaluate("-2").toString, "-2")

    @Test
    def lprp =
    assertEquals(Calc3.evaluate("(1)").toString, "1")

    @Test
    def pluslprp =
    assertEquals(Calc3.evaluate("+(1)").toString, "1")

    @Test
    def minuslprp =
    assertEquals(Calc3.evaluate("-(1)").toString, "-1")

    @Test{ val expected = classOf[RuntimeException] }
    def minusminus { Calc3.evaluate("--1") }

    @Test{ val expected = classOf[RuntimeException] }
    def plusminus { Calc3.evaluate("+-1") }

    @Test{ val expected = classOf[RuntimeException] }
    def minusplus { Calc3.evaluate("-+1") }

    @Test{ val expected = classOf[RuntimeException] }
    def plusplus { Calc3.evaluate("++1") }

    @Test
    def expAdd = {
        assertEquals(Calc3.evaluate("2").toString, "2")
        assertEquals(Calc3.evaluate("-2").toString, "-2")
        assertEquals(Calc3.evaluate("+2").toString, "2")
        assertEquals(Calc3.evaluate("-2+3").toString, "1")
        assertEquals(Calc3.evaluate("2+3").toString, "5")
    }
    @Test
    def expMul = {
        assertEquals(Calc3.evaluate("2*3").toString, "" + (2*3))
        assertEquals(Calc3.evaluate("-2*3").toString, "" + (-2*3))
        assertEquals(Calc3.evaluate("+2*3").toString, "" + (+2*3))
        assertEquals(Calc3.evaluate("-(-3)").toString, "3")
        assertEquals(Calc3.evaluate("-(3)").toString, "-3")
    }
    @Test
    def expExp = {
        assertEquals(Calc3.evaluate("2+3*4").toString, "" + (2+3*4))
        assertEquals(Calc3.evaluate("-2+3*4").toString, "" + (-2+3*4))
        assertEquals(Calc3.evaluate("-(2+3*4)").toString, "" + (-(2+3*4)))
    }

    @Test{ val expected = classOf[RuntimeException] }
    def expmulminus { Calc3.evaluate("1*-2") }

    @Test{ val expected = classOf[RuntimeException] }
    def expmulplus { Calc3.evaluate("1*+2") }

    @Test{ val expected = classOf[RuntimeException] }
    def expdiv0plus { Calc3.evaluate("1/0") }

    @Test{ val expected = classOf[RuntimeException] }
    def expdiv0minus { Calc3.evaluate("(-1)/0") }

    @Test
    def expadds {
        assertEquals(Calc3.evaluate("1+2+3+4").toString, "10")
    }

    @Test
    def expmuls {
        assertEquals(Calc3.evaluate("1*2*3*4").toString, "" + (1*2*3*4))
    }

    @Test{ val expected = classOf[RuntimeException] }
    def exppows {
        // ここで定義した文法では、(2^3)^4 のように書かないと、エラーになる。
        // 演算子の右・左結合の複雑さを回避する為。
        Calc3.evaluate("2^3^4")
    }

    @Test
    def exp3 =
    assertEquals(Calc3.evaluate("3^2").toString, "9")

    @Test
    def exp4 = {
        assertEquals(Calc3.evaluate("(3^2)^3").toString, "" + (9 * 9 * 9))
        assertEquals(Calc3.evaluate("3^(2^3)").toString, "" + (3 * 3 * 3 * 3 * 3 * 3 * 3 * 3))
    }

    @Test
    def exp5 =
    assertEquals(Calc3.evaluate("3^0").toString, "1")

    @Test
    def exp6 =
    assertEquals(Calc3.evaluate("(-3)^3").toString, "" + ((-3) * (-3) * (-3)))

    @Test
    def exp7 =
    assertEquals(Calc3.evaluate("3^(-3)").doubleValue, Math.pow(3.0,-3.0), delta2)


    @Test
    def exp8 =
    assertEquals(Calc3.evaluate("0^1").toString, "0")

    @Test
    def exp9 =
    assertEquals(Calc3.evaluate("0^0").toString, "1")

    @Test
    def exp10 =
    assertEquals(Calc3.evaluate("2^1.5").doubleValue, Math.pow(2.0, 1.5), delta2)

    @Test
    def exp11 =
    assertEquals(Calc3.evaluate("1.5^2").toString, "9/4")

    @Test
    def exp12 =
    assertEquals(Calc3.evaluate("2*3^2").toString, "" + (2*9))

    @Test
    def exp13 =
    assertEquals(Calc3.evaluate("2^3*2").toString, "" + (8*2))

    @Test
    def exp14 = {
        assertEquals(Calc3.evaluate("1+2+3").toString, "" + (1+2+3))
        assertEquals(Calc3.evaluate("1+2+3-4").toString, "" + (1+2+3-4))
        assertEquals(Calc3.evaluate("1+2+3-4-5").toString, "" + (1+2+3-4-5))
    }

    @Test
    def exp15 = {
        assertEquals(Calc3.evaluate("2*3*4").toString, "" + (2*3*4))
    }

    @Test
    def exp16 = {
        assertEquals(Calc3.evaluate("1*2/3*4").doubleValue, (1.0*2/3*4), delta2)
    }

    @Test
    def exp17 = {
        assertEquals(Calc3.evaluate("12345679*9").toString, "111111111")
    }

    @Test
    def exp18 = {
        assertEquals(Calc3.evaluate("1/3*3").doubleValue, 1, delta2)
    }
}