package com.lemenzo.calculator.engine import junit.framework.TestCase import java.math.BigDecimal class EngineTest : TestCase() { fun testDecimalPrecision() { val e = ExpressionEngine() assertEquals("0.3", e.format(e.evaluate("0.1+0.2"))) } fun testOperatorPrecedence() { val e = ExpressionEngine() assertEquals("14", e.format(e.evaluate("2+3*4"))) assertEquals("20", e.format(e.evaluate("(2+3)*4"))) } fun testCalculatorPercentageSemantics() { val e = ExpressionEngine() assertEquals("220", e.format(e.evaluate("200+10%"))) assertEquals("180", e.format(e.evaluate("200-10%"))) assertEquals("20", e.format(e.evaluate("200*10%"))) } fun testScientificFunctions() { val e = ExpressionEngine() e.angleMode = ExpressionEngine.AngleMode.DEG assertEquals("0.5", e.format(e.evaluate("sin(30)"))) assertEquals("9", e.format(e.evaluate("sqrt(81)"))) assertEquals("120", e.format(e.evaluate("5!"))) } fun testProgrammerOperations() { val p = ProgrammerEngine() p.base = ProgrammerEngine.Base.HEX assertEquals("F", p.format(p.evaluate("FF AND 0F"), ProgrammerEngine.Base.HEX)) assertEquals("100", p.format(p.evaluate("1 << 8"), ProgrammerEngine.Base.HEX)) try { p.evaluate("1 << 64"); fail("shift count must be bounded") } catch (_: IllegalArgumentException) {} } fun testCombinatoricsAndConstants() { val e = ExpressionEngine() assertEquals("120", e.format(e.evaluate("ncr(10,3)"))) assertEquals("720", e.format(e.evaluate("npr(10,3)"))) assertEquals("1.618033988749894848204586834365638", e.format(e.evaluate("phi"))) } fun testDomainsAndExactIntegerPower() { val e = ExpressionEngine() assertEquals("1024", e.format(e.evaluate("2^10"))) assertEquals("0.125", e.format(e.evaluate("2^-3"))) assertEquals("-4", e.format(e.evaluate("-2^2"))) assertEquals("4", e.format(e.evaluate("(-2)^2"))) try { e.evaluate("ln(0)"); fail("ln(0) must fail") } catch (_: IllegalArgumentException) {} try { e.evaluate("sqrt(-1)"); fail("sqrt(-1) must fail") } catch (_: IllegalArgumentException) {} } fun testTemperatureConversion() { val units = ConversionEngine.categories["Temperature"]!! val c = units.first { it.symbol == "°C" } val f = units.first { it.symbol == "°F" } assertEquals("32", ConversionEngine.convert(BigDecimal.ZERO, c, f).stripTrailingZeros().toPlainString()) assertEquals("100", ConversionEngine.convert(BigDecimal("212"), f, c).stripTrailingZeros().toPlainString()) } fun testConverter() { val units = ConversionEngine.categories["Length"]!! val km = units.first { it.symbol == "km" } val mi = units.first { it.symbol == "mi" } assertEquals("0.6213711922373339696174341843633182", ConversionEngine.convert(BigDecimal.ONE, km, mi).toPlainString()) } fun testDisplayFormatting() { assertEquals("1.234,5", DisplayFormatter.format(BigDecimal("1234.5"), DisplayFormatter.Notation.AUTO, ',', '.', true)) assertEquals("1,25E6", DisplayFormatter.format(BigDecimal("1250000"), DisplayFormatter.Notation.SCIENTIFIC, ',', '.', true)) assertEquals("12,5E3", DisplayFormatter.format(BigDecimal("12500"), DisplayFormatter.Notation.ENGINEERING, ',', '.', true)) } fun testLocaleNumberInputParsing() { assertEquals("1234.5", NumberInputParser.parse("1.234,5", ',', '.')!!.toPlainString()) assertEquals("1234.5", NumberInputParser.parse("1,234.5", '.', ',')!!.toPlainString()) assertEquals("1.5", NumberInputParser.parse("1,5", '.', ',')!!.toPlainString()) assertEquals("1.5", NumberInputParser.parse("1.5", ',', '.')!!.toPlainString()) assertEquals("1000", NumberInputParser.parse("1,000", '.', ',')!!.toPlainString()) assertEquals("1000", NumberInputParser.parse("1.000", ',', '.')!!.toPlainString()) } fun testLocaleExpressionCodec() { assertEquals("nCr(10;3)+1,5", ExpressionCodec.toDisplay("nCr(10,3)+1.5", ',')) assertEquals("nCr(10,3)+1.5", ExpressionCodec.toCanonical("nCr(10;3)+1,5", ',')) assertEquals("nCr(10,3)+1.5", ExpressionCodec.toCanonical("nCr(10,3)+1.5", '.')) } fun testExactAbsFloorCeilAndTanDomain() { val e = ExpressionEngine() assertEquals("12345678901234567890.123", e.format(e.evaluate("abs(-12345678901234567890.123)"))) assertEquals("-2", e.format(e.evaluate("floor(-1.2)"))) assertEquals("-1", e.format(e.evaluate("ceil(-1.2)"))) e.angleMode = ExpressionEngine.AngleMode.DEG try { e.evaluate("tan(90)"); fail("tan(90°) must fail") } catch (_: IllegalArgumentException) {} } fun testPowerAssociativityAndUnaryPrecedence() { val e = ExpressionEngine() assertEquals("512", e.format(e.evaluate("2^3^2"))) assertEquals("-4", e.format(e.evaluate("-2^2"))) assertEquals("4", e.format(e.evaluate("(-2)^2"))) } fun testAngleModes() { val e = ExpressionEngine() e.angleMode = ExpressionEngine.AngleMode.DEG assertEquals("1", e.format(e.evaluate("sin(90)"))) e.angleMode = ExpressionEngine.AngleMode.GRAD assertEquals("1", e.format(e.evaluate("sin(100)"))) } fun testCombinatoricBounds() { val e = ExpressionEngine() try { e.evaluate("ncr(3,4)"); fail("nCr with r > n must fail") } catch (_: IllegalArgumentException) {} try { e.evaluate("npr(-1,1)"); fail("negative n must fail") } catch (_: IllegalArgumentException) {} } fun testDivisionByZero() { val e = ExpressionEngine() try { e.evaluate("1/0"); fail("division by zero must fail") } catch (_: ArithmeticException) {} } }