/* * Copyright (C) 2007-2008 Mihai Preda. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.javia.eval; import org.javia.eval.MoreMath; import org.javia.eval.Log; class EvalCase { String expr; double result; static final double ERR = -2, FUN = -3; EvalCase(String expr, double result) { this.expr = expr; this.result = result; } } class TestEval { static EvalCase cases[] = { new EvalCase("1", 1), new EvalCase("1+", EvalCase.ERR), new EvalCase("1+1", 2), new EvalCase("1+-1", 0), new EvalCase("-0.5", -.5), new EvalCase("+1e2", 100), new EvalCase("-2^3!", -64), new EvalCase("(-2)^3!", 64), new EvalCase("-2^1^2", -2), new EvalCase("--1", 1), new EvalCase("-3^--2", -9), new EvalCase("1+2)(2+3", 15), new EvalCase("1+2)!^-2", 1./36), new EvalCase("sin(0)", 0), new EvalCase("cos(0)", 1), new EvalCase("sin(-1--1)", 0), new EvalCase("-(2+1)*-(4/2)", 6), new EvalCase("-.5E-1", -.05), new EvalCase("1E1.5", 5), new EvalCase("2 3 4", 24), new EvalCase("pi", Math.PI), new EvalCase("e", Math.E), new EvalCase("sin(pi/2)", 1), new EvalCase("f=sin(2x)", EvalCase.FUN), new EvalCase("f(pi/2)", 0), new EvalCase("a=3", 3), new EvalCase("b=a+1", 4), new EvalCase("f(x, y) = x*(y+1)", EvalCase.FUN), new EvalCase("f(a, b-a)", 6), new EvalCase("f(a pi/4)", -1), new EvalCase(" f ( 1 + 1 , a + 1 )", 10), new EvalCase(" g ( foo ) = f (f(foo, 1)pi/2)", EvalCase.FUN), new EvalCase("g(.5*2)", 0) }; private static boolean equal(double a, double b) { return Math.abs(a-b) < 1E-15; } static boolean testEval() { boolean allOk = true; SymbolTable symbols = new SymbolTable(); double actual = 0; Compiler compiler = new Compiler(); for (int i = 0; i < cases.length; ++i) { EvalCase c = cases[i]; Function f = compiler.compileAndDefine(c.expr, symbols); boolean ok; if (f == null) { ok = equal(c.result, actual=EvalCase.ERR); } else { if (f.arity() == 0) { try { ok = equal(c.result, actual=f.eval()); } catch (ArityException e) { throw new Error("internal error: ArityException on arity 0"); } } else { ok = equal(c.result, actual=EvalCase.FUN); } } if (!ok) { allOk = false; Log.log(c.expr + " expected " + c.result + " got " + actual); } } return allOk; } } class FormatCase { public FormatCase(int rounding, double v, String s) { this.rounding = rounding; this.val = v; this.res = s; } public int rounding; public double val; public String res; } class TestFormat { static FormatCase cases[] = { new FormatCase(0, 0.1, "0.1"), new FormatCase(0, 0.12, "0.12"), new FormatCase(0, 0.001, "0.001"), new FormatCase(0, 0.0012, "0.0012"), new FormatCase(0, 0.0000001, "1E-7"), new FormatCase(0, 0.00000012, "1.2E-7"), new FormatCase(0, 0.123456789012345, "0.123456789012345"), new FormatCase(0, 0, "0"), new FormatCase(0, 1, "1"), new FormatCase(0, 12, "12"), new FormatCase(0, 1234567890., "1234567890"), new FormatCase(0, 1000000000., "1000000000"), new FormatCase(0, 1.23456789012345, "1.23456789012345"), new FormatCase(0, 12345.6789012345, "12345.6789012345"), new FormatCase(0, 1234567890.12345, "1234567890.12345"), new FormatCase(0, 123456789012345., "1.23456789012345E14"), new FormatCase(0, 100000000000000., "1E14"), new FormatCase(0, 120000000000000., "1.2E14"), new FormatCase(0, 100000000000001., "1.00000000000001E14"), new FormatCase(2, 0.1, "0.1"), new FormatCase(2, 0.00000012, "1.2E-7"), new FormatCase(1, 0.123456789012345, "0.12345678901235"), new FormatCase(2, 0, "0"), new FormatCase(1, 1.23456789012345, "1.2345678901235"), new FormatCase(2, 1.23456789012345, "1.234567890123"), new FormatCase(0, 12345.6789012345, "12345.6789012345"), new FormatCase(1, 1234567890.12345, "1234567890.1235"), new FormatCase(2, 123456789012345., "1.234567890123E14"), new FormatCase(1, 100000000000001., "1E14"), new FormatCase(0, 12345678901234567., "1.2345678901234568E16"), new FormatCase(1, 12345678901234567., "1.2345678901235E16"), new FormatCase(0, 99999999999999999., "1E17"), new FormatCase(0, 9999999999999999., "1E16"), new FormatCase(0, 999999999999999., "9.99999999999999E14"), new FormatCase(1, 999999999999999., "1E15"), new FormatCase(1, 999999999999994., "9.9999999999999E14"), new FormatCase(1, MoreMath.log2(1+.00002), "00000.28853612282487") }; static boolean testFormat() { boolean ret = true; for (int i = 0; i < cases.length; ++i) { FormatCase c = cases[i]; double v = Double.parseDouble(c.res); if (c.rounding == 0 && v != c.val) { System.out.println("wrong test? " + c.res + " " + v + " " + c.val); } String res = Util.doubleToString(c.val, c.rounding); if (!res.equals(c.res)) { System.out.println("Expected '" + c.res + "', got '" + res + "'. " + Double.toString(c.val)); ret = false; } int nKeep = c.rounding == 0 ? 17 : 15 - c.rounding; //System.out.println("" + Double.toString(c.val) + " " + Util.round(c.val, nKeep) + " " + c.res + ", got " + res); } return ret; } } /** Runs some unit-tests.
Usage: java -cp arity.jar org.javia.eval.UnitTest */ public class UnitTest { /** Runs unit-tests. */ public static void main(String argv[]) { //UnitTest tester = new UnitTest(); checkCounter = 0; cheq(MoreMath.log(-1), Double.NaN); cheq(MoreMath.log(-0.03), Double.NaN); cheq(MoreMath.intLog10(-0.03), 0); cheq(MoreMath.intLog10(0.03), -2); cheq(MoreMath.intExp10(3), 1000); cheq(MoreMath.intExp10(-1), 0.1); cheq(Util.shortApprox( 1.235, 0.02), 1.24); cheq(Util.shortApprox( 1.235, 0.4), 1.2000000000000002); cheq(Util.shortApprox(-1.235, 0.02), -1.24); cheq(Util.shortApprox(-1.235, 0.4), -1.2000000000000002); check(TestFormat.testFormat()); check(TestEval.testEval()); if (!allOk) { System.exit(1); } else { System.out.println("All tests passed ok"); } /* System.out.println(Util.doubleToTrimmedString(0.9999, 5)); Parser parser = new Parser(); String[] defs = { "f:=x", "g:=x*y", "b:=x", "h:=x*b(x)" }; for (int i = 0; i < defs.length; ++i) { char[] ch = new char[defs[i].length() + 1]; defs[i].getChars(0, defs[i].length(), ch, 0); if (parser.compile(ch, defs[i].length())) { final int idx = Compiler.result.definedSymbol - VMConstants.FIRST_VAR; Variables.funcs[idx] = new CompiledFunction(Compiler.result.function); Variables.types[idx] = Variables.TYPE_FUNC; } else System.out.println("error"); } String input = "a:=x+x+y*y+x^x"; char[] chs = new char[input.length() + 1]; input.getChars(0, input.length(), chs, 0); System.out.println(input); if (compiler.compile(chs, input.length())) { System.out.println(Compiler.result.function); Compiler.result.function.xyFragment(); } else System.out.println("error " + Compiler.result.errorStart + "-" + Compiler.result.errorEnd); */ } static void cheq(double v1, double v2) { ++checkCounter; if (v1 != v2 && !(Double.isNaN(v1) && Double.isNaN(v2))) { allOk = false; Log.log("check equal " + checkCounter + " failed: " + v1 + " " + v2); } } static void check(boolean cond) { ++checkCounter; if (!cond) { allOk = false; Log.log("check " + checkCounter + " failed"); } } static boolean allOk = true; static int checkCounter = 0; }