Back to index

wims  3.65+svn20090927
UnitTest.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007-2008 Mihai Preda.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 package org.javia.arity;
00018 
00019 class EvalCase {
00020     String expr;
00021     double result;
00022     
00023     static final double ERR = -2, FUN = -3;
00024 
00025     EvalCase(String expr, double result) {
00026         this.expr = expr;
00027         this.result = result;
00028     }
00029 }
00030 
00031 class TestEval {
00032     static EvalCase cases[] = {
00033         new EvalCase(".", 0),
00034         new EvalCase("1+.", 1),
00035         new EvalCase("1", 1),
00036         new EvalCase("\u03c0", Math.PI),
00037         new EvalCase("2\u00d73", 6), //2*3
00038         new EvalCase("1+\u221a9*2", 7), //1+sqrt(9)*2
00039         new EvalCase("3\u221a 4", 6), //3*sqrt(4)
00040         new EvalCase("\u221a16sin(2\u03c0/4)", 4), //sqrt(16)*sin(2pi/4)
00041         new EvalCase("1+", EvalCase.ERR),
00042         new EvalCase("1+1", 2),
00043         new EvalCase("1+-1", 0),
00044         new EvalCase("-0.5", -.5),
00045         new EvalCase("+1e2", 100),
00046         new EvalCase("-2^3!", -64),
00047         new EvalCase("(-2)^3!", 64),
00048         new EvalCase("-2^1^2", -2),
00049         new EvalCase("--1", 1),
00050         new EvalCase("-3^--2", -9),
00051         new EvalCase("1+2)(2+3", 15),
00052         new EvalCase("1+2)!^-2", 1./36),
00053         new EvalCase("sin(0)", 0),
00054         new EvalCase("cos(0)", 1),
00055         new EvalCase("sin(-1--1)", 0),
00056         new EvalCase("-(2+1)*-(4/2)", 6),
00057         new EvalCase("-.5E-1", -.05),
00058         new EvalCase("1E1.5", EvalCase.ERR),
00059         new EvalCase("2 3 4", 24),
00060         new EvalCase("pi", Math.PI),
00061         new EvalCase("e", Math.E),
00062         new EvalCase("sin(pi/2)", 1),
00063         new EvalCase("f=sin(2x)", EvalCase.FUN),
00064         new EvalCase("f(pi/2)", 0),
00065         new EvalCase("a=3", 3),
00066         new EvalCase("b=a+1", 4),
00067         new EvalCase("f(x, y) = x*(y+1)", EvalCase.FUN),
00068         new EvalCase("f(a, b-a)", 6),
00069         new EvalCase(" f(a pi/4)", -1),
00070         new EvalCase("f (  1  +  1  , a+1)", 10),
00071         new EvalCase("g(foo) = f (f(foo, 1)pi/2)", EvalCase.FUN),
00072         new EvalCase("g(.5*2)", 0),
00073         new EvalCase("NaN", Double.NaN),
00074         new EvalCase("Inf", Double.POSITIVE_INFINITY),
00075         new EvalCase("Infinity", Double.POSITIVE_INFINITY),
00076         new EvalCase("-Inf", Double.NEGATIVE_INFINITY),
00077         new EvalCase("0/0", Double.NaN)
00078     };
00079 
00080     private static boolean equal(double a, double b) {
00081         return Math.abs(a-b) < 1E-15 ||
00082             Double.doubleToLongBits(a) == Double.doubleToLongBits(b);
00083     }
00084 
00085     static boolean testEval() throws ArityException {
00086         final String spaces = "                                           ";
00087         boolean allOk = true;
00088         Symbols symbols = new Symbols();
00089         for (int i = 0; i < cases.length; ++i) {
00090             EvalCase c = cases[i];
00091             String strResult;
00092             boolean ok;
00093             try {
00094                 double actual;
00095                 if (Symbols.isDefinition(c.expr)) {
00096                     FunctionAndName fan = symbols.compile(c.expr);
00097                     symbols.define(fan);
00098                     Function f = fan.function;
00099                     actual = f.arity()==0 ? f.eval() : EvalCase.FUN;
00100                     strResult = (f.arity()==0 ? Util.doubleToString(actual, 1) : f.toString());
00101                 } else {
00102                     actual = symbols.eval(c.expr);
00103                     strResult = Util.doubleToString(actual, 1);
00104                 }
00105                 ok = equal(c.result, actual);
00106             } catch (SyntaxException e) {
00107                 strResult = e.toString();
00108                 ok = c.result == EvalCase.ERR;
00109             }
00110             System.out.println((ok ? "" : "failed (expected " + c.result + "): ")
00111                                + c.expr
00112                                + spaces.substring(0, Math.max(15-c.expr.length(), 0)) + " = " 
00113                                + strResult);
00114             if (!ok) {
00115                 allOk = false;
00116             }
00117         }
00118         return allOk;
00119     }
00120 }
00121 
00122 
00123 class FormatCase {
00124     public FormatCase(int rounding, double v, String s) {
00125         this.rounding = rounding;
00126         this.val = v;
00127         this.res = s;
00128     }
00129     
00130     public int rounding;
00131     public double val;
00132     public String res;            
00133 }
00134 
00135 class TestFormat {
00136     static FormatCase cases[] = { 
00137         new FormatCase(0, 0.1, "0.1"),
00138         new FormatCase(0, 0.12, "0.12"),
00139         new FormatCase(0, 0.001, "0.001"),
00140         new FormatCase(0, 0.0012, "0.0012"),
00141         new FormatCase(0, 0.0000001, "1E-7"),
00142         new FormatCase(0, 0.00000012, "1.2E-7"),
00143         new FormatCase(0, 0.123456789012345, "0.123456789012345"),
00144 
00145         new FormatCase(0, 0, "0"),
00146         new FormatCase(0, 1, "1"),
00147         new FormatCase(0, 12, "12"),
00148         new FormatCase(0, 1234567890.,   "1234567890"), 
00149         new FormatCase(0, 1000000000.,   "1000000000"),
00150         
00151         new FormatCase(0, 1.23456789012345,  "1.23456789012345"),
00152         new FormatCase(0, 12345.6789012345,  "12345.6789012345"),
00153         new FormatCase(0, 1234567890.12345,  "1234567890.12345"),  
00154         new FormatCase(0, 123456789012345.,   "1.23456789012345E14"), 
00155         new FormatCase(0, 100000000000000.,   "1E14"),
00156         new FormatCase(0, 120000000000000.,   "1.2E14"),
00157         new FormatCase(0, 100000000000001.,   "1.00000000000001E14"),
00158 
00159         new FormatCase(2, 0.1, "0.1"),
00160         new FormatCase(2, 0.00000012, "1.2E-7"),
00161         new FormatCase(1, 0.123456789012345, "0.12345678901235"),
00162 
00163         new FormatCase(2, 0, "0"),
00164         
00165         new FormatCase(1, 1.23456789012345,  "1.2345678901235"),
00166         new FormatCase(2, 1.23456789012345,  "1.234567890123"),
00167         
00168         new FormatCase(0, 12345.6789012345,  "12345.6789012345"),
00169         new FormatCase(1, 1234567890.12345,  "1234567890.1235"),  
00170         new FormatCase(2, 123456789012345.,  "1.234567890123E14"), 
00171         new FormatCase(1, 100000000000001.,  "1E14"),
00172 
00173         new FormatCase(0, 12345678901234567.,   "1.2345678901234568E16"),
00174         new FormatCase(1, 12345678901234567.,   "1.2345678901235E16"),
00175 
00176         new FormatCase(0, 99999999999999999.,   "1E17"),
00177         new FormatCase(0, 9999999999999999.,    "1E16"),
00178         new FormatCase(0, 999999999999999.,     "9.99999999999999E14"),
00179         new FormatCase(1, 999999999999999.,     "1E15"),
00180         new FormatCase(1, 999999999999994.,     "9.9999999999999E14"),
00181 
00182         new FormatCase(1, MoreMath.log2(1+.00002), "00000.28853612282487")
00183     };
00184 
00185     static boolean testFormat() {
00186         boolean ret = true;
00187         for (int i = 0; i < cases.length; ++i) {
00188             FormatCase c = cases[i];
00189             double v = Double.parseDouble(c.res);
00190             if (c.rounding == 0 && v != c.val) {
00191                 System.out.println("wrong test? " + c.res + " " + v + " " + c.val);
00192             }
00193             String res = Util.doubleToString(c.val, c.rounding);
00194             if (!res.equals(c.res)) {
00195                 System.out.println("Expected '" + c.res + "', got '" + res + "'. " + Double.toString(c.val));
00196                 ret = false;
00197             }
00198             int nKeep = c.rounding == 0 ? 17 : 15 - c.rounding;
00199             //System.out.println("" + Double.toString(c.val) + " " + Util.round(c.val, nKeep) + " " + c.res + ", got " + res);
00200         }
00201         return ret;
00202     }
00203 }
00204 
00209 public class UnitTest {
00215     public static void main(String argv[]) throws SyntaxException, ArityException {
00216         int size = argv.length;
00217         if (size == 0) {
00218             runUnitTests();
00219             profile();
00220         } else if (size == 1 && argv[0].equals("-profile")) {
00221             profile();
00222         } else {
00223             Symbols symbols = new Symbols();
00224             for (int i = 0; i < size; ++i) {
00225                 FunctionAndName fan = symbols.compile(argv[i]);
00226                 symbols.define(fan);
00227             }
00228             profile(symbols, argv[size-1]);
00229         }
00230     }
00231 
00232     static void profile(Symbols symbols, String str) throws SyntaxException, ArityException {
00233         Function f = symbols.compile(str).function;
00234         System.out.println("\n" + str + ": " + f);
00235 
00236         Runtime runtime = Runtime.getRuntime();        
00237 
00238         runtime.gc();
00239         /*
00240         long m1 = runtime.freeMemory();
00241         for (int i = 0; i < 200; ++i) {
00242             symbols.compile(str);
00243         }
00244         long m2 = runtime.freeMemory();
00245         System.out.println("compilation memory: " + (m1 - m2)/200 + " bytes");
00246         */
00247         
00248         runtime.gc();
00249         long t1 = System.currentTimeMillis();
00250         for (int i = 0; i < 1000; ++i) {
00251             symbols.compile(str);
00252         }
00253         long t2 = System.currentTimeMillis();
00254         System.out.println("compilation time: " + (t2 - t1) + " us");
00255         
00256         double args[] = new double[f.arity()];
00257         /*
00258         runtime.gc();
00259         m1 = runtime.freeMemory();
00260         f.eval(args);
00261         m2 = runtime.freeMemory();
00262         if (m2 != m1) {
00263             System.out.println("execution memory: " + (m1 - m2) + " bytes");
00264         }
00265         */
00266   
00267         runtime.gc();
00268         t1 = System.currentTimeMillis();
00269         for (int i = 0; i < 100000; ++i) {
00270             f.eval(args);
00271         }
00272         t2 = System.currentTimeMillis();
00273         long delta = t2 - t1;        
00274         System.out.println("execution time: " + (delta > 100 ? ""+delta/100.+" us" :  ""+delta+" ns"));
00275     }
00276 
00277     private static final String profileCases[] = {
00278         //"1+1",
00279         "(100.5 + 20009.999)*(7+4+3)/(5/2)^3!)*2",
00280         "fun1(x)=(x+2)*(x+3)",
00281         "otherFun(x)=(fun1(x-1)*x+1)*(fun1(2-x)+10)",
00282         "log(x+30.5, 3)^.7*sin(x+.5)"
00283     };
00284 
00285     private static void profile() {
00286         String cases[] = profileCases;
00287         Symbols symbols = new Symbols();
00288         try {
00289             for (int i = 0; i < cases.length; ++i) {
00290                 symbols.define(symbols.compile(cases[i]));
00291                 profile(symbols, cases[i]);
00292             }
00293         } catch (Exception e) {
00294             throw new Error("" + e);
00295         }
00296     }
00297 
00298     static void runUnitTests() {
00299         checkCounter = 0;
00300 
00301         cheq(MoreMath.log(-1), Double.NaN);
00302         cheq(MoreMath.log(-0.03), Double.NaN);
00303         cheq(MoreMath.intLog10(-0.03), 0);
00304         cheq(MoreMath.intLog10(0.03), -2);
00305         cheq(MoreMath.intExp10(3), 1000);
00306         cheq(MoreMath.intExp10(-1), 0.1);
00307         cheq(Util.shortApprox( 1.235, 0.02),  1.24);
00308         cheq(Util.shortApprox( 1.235, 0.4),   1.2000000000000002);
00309         cheq(Util.shortApprox(-1.235, 0.02), -1.24);
00310         cheq(Util.shortApprox(-1.235, 0.4),  -1.2000000000000002);
00311 
00312         check(TestFormat.testFormat());
00313         try {
00314             check(TestEval.testEval());
00315         } catch (ArityException e) {
00316             System.out.println(""+e);
00317         }
00318 
00319         if (!allOk) {
00320             System.out.println("\n*** Some tests FAILED ***\n");
00321             System.exit(1);
00322         } else {
00323             System.out.println("\n*** All tests passed OK ***\n");
00324         }
00325     }
00326 
00327     static void cheq(double v1, double v2) {
00328         ++checkCounter;
00329         if (v1 != v2 && !(Double.isNaN(v1) && Double.isNaN(v2))) {
00330             allOk = false;
00331             //Log.log("check equal " + checkCounter + " failed: " + v1 + " " + v2);
00332         }
00333     }
00334     
00335     static void check(boolean cond) {
00336         ++checkCounter;
00337         if (!cond) {
00338             allOk = false;
00339             //Log.log("check " + checkCounter + " failed");
00340         }
00341     }
00342 
00343     static boolean allOk = true;
00344     static int checkCounter = 0;
00345 }