/*++
 Copyright (c) 2012 Microsoft Corporation

Module Name:

    Program.java

Abstract:

    Z3 Java API: Example program

Author:

    Christoph Wintersteiger (cwinter) 2012-11-27

Notes:
    
--*/

import java.util.*;

import com.microsoft.z3.*;

class JavaExample
{
    @SuppressWarnings("serial")
    class TestFailedException extends Exception
    {
        public TestFailedException()
        {
            super("Check FAILED");
        }
    };

    // / Create axiom: function f is injective in the i-th argument.

    // / <remarks>
    // / The following axiom is produced:
    // / <code>
    // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i
    // / </code>
    // / Where, <code>finv</code>is a fresh function declaration.

    public BoolExpr injAxiom(Context ctx, FuncDecl f, int i) 
    {
        Sort[] domain = f.getDomain();
        int sz = f.getDomainSize();

        if (i >= sz)
        {
            System.out.println("failed to create inj axiom");
            return null;
        }

        /* declare the i-th inverse of f: finv */
        Sort finv_domain = f.getRange();
        Sort finv_range = domain[i];
        FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range);

        /* allocate temporary arrays */
        Expr[] xs = new Expr[sz];
        Symbol[] names = new Symbol[sz];
        Sort[] types = new Sort[sz];

        /* fill types, names and xs */

        for (int j = 0; j < sz; j++)
        {
            types[j] = domain[j];
            names[j] = ctx.mkSymbol("x_" + Integer.toString(j));
            xs[j] = ctx.mkBound(j, types[j]);
        }
        Expr x_i = xs[i];

        /* create f(x_0, ..., x_i, ..., x_{n-1}) */
        Expr fxs = f.apply(xs);

        /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */
        Expr finv_fxs = finv.apply(fxs);

        /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */
        Expr eq = ctx.mkEq(finv_fxs, x_i);

        /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */
        Pattern p = ctx.mkPattern(fxs);

        /* create & assert quantifier */
        BoolExpr q = ctx.mkForall(types, /* types of quantified variables */
                names, /* names of quantified variables */
                eq, 1, new Pattern[] { p } /* patterns */, null, null, null);

        return q;
    }

    // / Create axiom: function f is injective in the i-th argument.

    // / <remarks>
    // / The following axiom is produced:
    // / <code>
    // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i
    // / </code>
    // / Where, <code>finv</code>is a fresh function declaration.

    public BoolExpr injAxiomAbs(Context ctx, FuncDecl f, int i)
    {
        Sort[] domain = f.getDomain();
        int sz = f.getDomainSize();

        if (i >= sz)
        {
            System.out.println("failed to create inj axiom");
            return null;
        }

        /* declare the i-th inverse of f: finv */
        Sort finv_domain = f.getRange();
        Sort finv_range = domain[i];
        FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range);

        /* allocate temporary arrays */
        Expr[] xs = new Expr[sz];

        /* fill types, names and xs */
        for (int j = 0; j < sz; j++)
        {
            xs[j] = ctx.mkConst("x_" + Integer.toString(j), domain[j]);
        }
        Expr x_i = xs[i];

        /* create f(x_0, ..., x_i, ..., x_{n-1}) */
        Expr fxs = f.apply(xs);

        /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */
        Expr finv_fxs = finv.apply(fxs);

        /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */
        Expr eq = ctx.mkEq(finv_fxs, x_i);

        /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */
        Pattern p = ctx.mkPattern(fxs);

        /* create & assert quantifier */
        BoolExpr q = ctx.mkForall(xs, /* types of quantified variables */
                eq, /* names of quantified variables */
                1, new Pattern[] { p } /* patterns */, null, null, null);

        return q;
    }

    // / Assert the axiom: function f is commutative.

    // / <remarks>
    // / This example uses the SMT-LIB parser to simplify the axiom
    // construction.
    // / </remarks>
    private BoolExpr commAxiom(Context ctx, FuncDecl f) throws Exception
    {
        Sort t = f.getRange();
        Sort[] dom = f.getDomain();

        if (dom.length != 2 || !t.equals(dom[0]) || !t.equals(dom[1]))
        {
            System.out.println(Integer.toString(dom.length) + " "
                    + dom[0].toString() + " " + dom[1].toString() + " "
                    + t.toString());
            throw new Exception(
                    "function must be binary, and argument types must be equal to return type");
        }

        String bench = "(assert (forall (x " + t.getName()
                + ") (y " + t.getName() + ") (= (" + f.getName() + " x y) ("
                + f.getName() + " y x))))";
        return ctx.parseSMTLIB2String(bench, new Symbol[] { t.getName() },
                new Sort[] { t }, new Symbol[] { f.getName() },
                new FuncDecl[] { f })[0];
    }

    // / "Hello world" example: create a Z3 logical context, and delete it.

    public void simpleExample()
    {
        System.out.println("SimpleExample");
        Log.append("SimpleExample");

        {
            Context ctx = new Context();
            /* do something with the context */

            /* be kind to dispose manually and not wait for the GC. */
            ctx.close();
        }
    }

    Model check(Context ctx, BoolExpr f, Status sat) throws TestFailedException
    {
        Solver s = ctx.mkSolver();
        s.add(f);
        if (s.check() != sat)
            throw new TestFailedException();
        if (sat == Status.SATISFIABLE)
            return s.getModel();
        else
            return null;
    }

    void solveTactical(Context ctx, Tactic t, Goal g, Status sat)
            throws TestFailedException
    {
        Solver s = ctx.mkSolver(t);
        System.out.println("\nTactical solver: " + s);

        for (BoolExpr a : g.getFormulas())
            s.add(a);
        System.out.println("Solver: " + s);

        if (s.check() != sat)
            throw new TestFailedException();
    }

    ApplyResult applyTactic(Context ctx, Tactic t, Goal g)
    {
        System.out.println("\nGoal: " + g);

        ApplyResult res = t.apply(g);
        System.out.println("Application result: " + res);

        Status q = Status.UNKNOWN;
        for (Goal sg : res.getSubgoals())
            if (sg.isDecidedSat())
                q = Status.SATISFIABLE;
            else if (sg.isDecidedUnsat())
                q = Status.UNSATISFIABLE;

        switch (q)
        {
        case UNKNOWN:
            System.out.println("Tactic result: Undecided");
            break;
        case SATISFIABLE:
            System.out.println("Tactic result: SAT");
            break;
        case UNSATISFIABLE:
            System.out.println("Tactic result: UNSAT");
            break;
        }

        return res;
    }

    void prove(Context ctx, BoolExpr f, boolean useMBQI) throws TestFailedException
    {
        BoolExpr[] assumptions = new BoolExpr[0];
        prove(ctx, f, useMBQI, assumptions);
    }

    void prove(Context ctx, BoolExpr f, boolean useMBQI,
            BoolExpr... assumptions) throws TestFailedException
    {
        System.out.println("Proving: " + f);
        Solver s = ctx.mkSolver();
        Params p = ctx.mkParams();
        p.add("mbqi", useMBQI);
        s.setParameters(p);
        for (BoolExpr a : assumptions)
            s.add(a);
        s.add(ctx.mkNot(f));
        Status q = s.check();

        switch (q)
        {
        case UNKNOWN:
            System.out.println("Unknown because: " + s.getReasonUnknown());
            break;
        case SATISFIABLE:
            throw new TestFailedException();
        case UNSATISFIABLE:
            System.out.println("OK, proof: " + s.getProof());
            break;
        }
    }

    void disprove(Context ctx, BoolExpr f, boolean useMBQI) 
        throws TestFailedException
    {
        BoolExpr[] a = {};
        disprove(ctx, f, useMBQI, a);
    }

    void disprove(Context ctx, BoolExpr f, boolean useMBQI,
            BoolExpr... assumptions) throws TestFailedException
    {
        System.out.println("Disproving: " + f);
        Solver s = ctx.mkSolver();
        Params p = ctx.mkParams();
        p.add("mbqi", useMBQI);
        s.setParameters(p);
        for (BoolExpr a : assumptions)
            s.add(a);
        s.add(ctx.mkNot(f));
        Status q = s.check();

        switch (q)
        {
        case UNKNOWN:
            System.out.println("Unknown because: " + s.getReasonUnknown());
            break;
        case SATISFIABLE:
            System.out.println("OK, model: " + s.getModel());
            break;
        case UNSATISFIABLE:
            throw new TestFailedException();
        }
    }

    void modelConverterTest(Context ctx) throws TestFailedException
    {
        System.out.println("ModelConverterTest");

        ArithExpr xr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("x"),
                ctx.mkRealSort());
        ArithExpr yr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("y"),
                ctx.mkRealSort());
        Goal g4 = ctx.mkGoal(true, false, false);
        g4.add(ctx.mkGt(xr, ctx.mkReal(10, 1)));
        g4.add(ctx.mkEq(yr, ctx.mkAdd(xr, ctx.mkReal(1, 1))));
        g4.add(ctx.mkGt(yr, ctx.mkReal(1, 1)));

        ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g4);
        if (ar.getNumSubgoals() == 1
                && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0]
                        .isDecidedUnsat()))
            throw new TestFailedException();

        ar = applyTactic(ctx, ctx.andThen(ctx.mkTactic("simplify"),
                ctx.mkTactic("solve-eqs")), g4);
        if (ar.getNumSubgoals() == 1
                && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0]
                        .isDecidedUnsat()))
            throw new TestFailedException();

        Solver s = ctx.mkSolver();
        for (BoolExpr e : ar.getSubgoals()[0].getFormulas())
            s.add(e);
        Status q = s.check();
        System.out.println("Solver says: " + q);
        System.out.println("Model: \n" + s.getModel());
        if (q != Status.SATISFIABLE)
            throw new TestFailedException();
    }

    // / A simple array example.

    void arrayExample1(Context ctx) throws TestFailedException
    {
        System.out.println("ArrayExample1");
        Log.append("ArrayExample1");

        Goal g = ctx.mkGoal(true, false, false);
        ArraySort asort = ctx.mkArraySort(ctx.getIntSort(),
                ctx.mkBitVecSort(32));
        ArrayExpr aex = (ArrayExpr) ctx.mkConst(ctx.mkSymbol("MyArray"), asort);
        Expr sel = ctx.mkSelect(aex, ctx.mkInt(0));
        g.add(ctx.mkEq(sel, ctx.mkBV(42, 32)));
        Symbol xs = ctx.mkSymbol("x");
        IntExpr xc = (IntExpr) ctx.mkConst(xs, ctx.getIntSort());

        Symbol fname = ctx.mkSymbol("f");
        Sort[] domain = { ctx.getIntSort() };
        FuncDecl fd = ctx.mkFuncDecl(fname, domain, ctx.getIntSort());
        Expr[] fargs = { ctx.mkConst(xs, ctx.getIntSort()) };
        IntExpr fapp = (IntExpr) ctx.mkApp(fd, fargs);

        g.add(ctx.mkEq(ctx.mkAdd(xc, fapp), ctx.mkInt(123)));

        Solver s = ctx.mkSolver();
        for (BoolExpr a : g.getFormulas())
            s.add(a);
        System.out.println("Solver: " + s);

        Status q = s.check();
        System.out.println("Status: " + q);

        if (q != Status.SATISFIABLE)
            throw new TestFailedException();

        System.out.println("Model = " + s.getModel());

        System.out.println("Interpretation of MyArray:\n"
                + s.getModel().getFuncInterp(aex.getFuncDecl()));
        System.out.println("Interpretation of x:\n"
                + s.getModel().getConstInterp(xc));
        System.out.println("Interpretation of f:\n"
                + s.getModel().getFuncInterp(fd));
        System.out.println("Interpretation of MyArray as Term:\n"
                + s.getModel().getFuncInterp(aex.getFuncDecl()));
    }

    // / Prove <tt>store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2
    // = i3 or select(a1, i3) = select(a2, i3))</tt>.

    // / <remarks>This example demonstrates how to use the array
    // theory.</remarks>
    public void arrayExample2(Context ctx) throws TestFailedException
    {
        System.out.println("ArrayExample2");
        Log.append("ArrayExample2");

        Sort int_type = ctx.getIntSort();
        Sort array_type = ctx.mkArraySort(int_type, int_type);

        ArrayExpr a1 = (ArrayExpr) ctx.mkConst("a1", array_type);
        ArrayExpr a2 = ctx.mkArrayConst("a2", int_type, int_type);
        Expr i1 = ctx.mkConst("i1", int_type);
        Expr i2 = ctx.mkConst("i2", int_type);
        Expr i3 = ctx.mkConst("i3", int_type);
        Expr v1 = ctx.mkConst("v1", int_type);
        Expr v2 = ctx.mkConst("v2", int_type);

        Expr st1 = ctx.mkStore(a1, i1, v1);
        Expr st2 = ctx.mkStore(a2, i2, v2);

        Expr sel1 = ctx.mkSelect(a1, i3);
        Expr sel2 = ctx.mkSelect(a2, i3);

        /* create antecedent */
        BoolExpr antecedent = ctx.mkEq(st1, st2);

        /*
         * create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2,
         * i3)
         */
        BoolExpr consequent = ctx.mkOr(ctx.mkEq(i1, i3), ctx.mkEq(i2, i3),
                ctx.mkEq(sel1, sel2));

        /*
         * prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 =
         * i3 or select(a1, i3) = select(a2, i3))
         */
        BoolExpr thm = ctx.mkImplies(antecedent, consequent);
        System.out
                .println("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))");
        System.out.println(thm);
        prove(ctx, thm, false);
    }

    // / Show that <code>distinct(a_0, ... , a_n)</code> is
    // / unsatisfiable when <code>a_i</code>'s are arrays from boolean to
    // / boolean and n > 4.

    // / <remarks>This example also shows how to use the <code>distinct</code>
    // construct.</remarks>
    public void arrayExample3(Context ctx) throws TestFailedException
    {
        System.out.println("ArrayExample3");
        Log.append("ArrayExample2");

        for (int n = 2; n <= 5; n++)
        {
            System.out.println("n = " + Integer.toString(n));

            Sort bool_type = ctx.mkBoolSort();
            Sort array_type = ctx.mkArraySort(bool_type, bool_type);
            Expr[] a = new Expr[n];

            /* create arrays */
            for (int i = 0; i < n; i++)
            {
                a[i] = ctx.mkConst("array_" + Integer.toString(i), array_type);
            }

            /* assert distinct(a[0], ..., a[n]) */
            BoolExpr d = ctx.mkDistinct(a);
            System.out.println(d);

            /* context is satisfiable if n < 5 */
            Model model = check(ctx, d, n < 5 ? Status.SATISFIABLE
                    : Status.UNSATISFIABLE);
            if (n < 5)
            {
                for (int i = 0; i < n; i++)
                {
                    System.out.println(a[i].toString() + " = "
                            + model.evaluate(a[i], false));
                }
            }
        }
    }

    // / Sudoku solving example.

    void sudokuExample(Context ctx) throws TestFailedException
    {
        System.out.println("SudokuExample");
        Log.append("SudokuExample");

        // 9x9 matrix of integer variables
        IntExpr[][] X = new IntExpr[9][];
        for (int i = 0; i < 9; i++)
        {
            X[i] = new IntExpr[9];
            for (int j = 0; j < 9; j++)
                X[i][j] = (IntExpr) ctx.mkConst(
                        ctx.mkSymbol("x_" + (i + 1) + "_" + (j + 1)),
                        ctx.getIntSort());
        }

        // each cell contains a value in {1, ..., 9}
        BoolExpr[][] cells_c = new BoolExpr[9][];
        for (int i = 0; i < 9; i++)
        {
            cells_c[i] = new BoolExpr[9];
            for (int j = 0; j < 9; j++)
                cells_c[i][j] = ctx.mkAnd(ctx.mkLe(ctx.mkInt(1), X[i][j]),
                        ctx.mkLe(X[i][j], ctx.mkInt(9)));
        }

        // each row contains a digit at most once
        BoolExpr[] rows_c = new BoolExpr[9];
        for (int i = 0; i < 9; i++)
            rows_c[i] = ctx.mkDistinct(X[i]);

        // each column contains a digit at most once
        BoolExpr[] cols_c = new BoolExpr[9];
        for (int j = 0; j < 9; j++) {
            IntExpr[] col = new IntExpr[9];
            for (int i = 0; i < 9; i++) {
                col[i] = X[i][j];
            }
            cols_c[j] = ctx.mkDistinct(col);
        }

        // each 3x3 square contains a digit at most once
        BoolExpr[][] sq_c = new BoolExpr[3][];
        for (int i0 = 0; i0 < 3; i0++)
        {
            sq_c[i0] = new BoolExpr[3];
            for (int j0 = 0; j0 < 3; j0++)
            {
                IntExpr[] square = new IntExpr[9];
                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 3; j++)
                        square[3 * i + j] = X[3 * i0 + i][3 * j0 + j];
                sq_c[i0][j0] = ctx.mkDistinct(square);
            }
        }

        BoolExpr sudoku_c = ctx.mkTrue();
        for (BoolExpr[] t : cells_c)
            sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c);
        sudoku_c = ctx.mkAnd(ctx.mkAnd(rows_c), sudoku_c);
        sudoku_c = ctx.mkAnd(ctx.mkAnd(cols_c), sudoku_c);
        for (BoolExpr[] t : sq_c)
            sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c);

        // sudoku instance, we use '0' for empty cells
        int[][] instance = { { 0, 0, 0, 0, 9, 4, 0, 3, 0 },
                { 0, 0, 0, 5, 1, 0, 0, 0, 7 }, { 0, 8, 9, 0, 0, 0, 0, 4, 0 },
                { 0, 0, 0, 0, 0, 0, 2, 0, 8 }, { 0, 6, 0, 2, 0, 1, 0, 5, 0 },
                { 1, 0, 2, 0, 0, 0, 0, 0, 0 }, { 0, 7, 0, 0, 0, 0, 5, 2, 0 },
                { 9, 0, 0, 0, 6, 5, 0, 0, 0 }, { 0, 4, 0, 9, 7, 0, 0, 0, 0 } };

        BoolExpr instance_c = ctx.mkTrue();
        for (int i = 0; i < 9; i++)
            for (int j = 0; j < 9; j++)
                if (0 != instance[i][j])
                    instance_c = ctx.mkAnd(
                            instance_c,
                            ctx.mkEq(X[i][j], ctx.mkInt(instance[i][j])));

        Solver s = ctx.mkSolver();
        s.add(sudoku_c);
        s.add(instance_c);

        if (s.check() == Status.SATISFIABLE)
        {
            Model m = s.getModel();
            Expr[][] R = new Expr[9][9];
            for (int i = 0; i < 9; i++)
                for (int j = 0; j < 9; j++)
                    R[i][j] = m.evaluate(X[i][j], false);
            System.out.println("Sudoku solution:");
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                    System.out.print(" " + R[i][j]);
                System.out.println();
            }
        } else
        {
            System.out.println("Failed to solve sudoku");
            throw new TestFailedException();
        }
    }

    // / A basic example of how to use quantifiers.

    void quantifierExample1(Context ctx)
    {
        System.out.println("QuantifierExample");
        Log.append("QuantifierExample");

        IntSort[] types = new IntSort[3];
        IntExpr[] xs = new IntExpr[3];
        Symbol[] names = new Symbol[3];
        IntExpr[] vars = new IntExpr[3];

        for (int j = 0; j < 3; j++)
        {
            types[j] = ctx.getIntSort();
            names[j] = ctx.mkSymbol("x_" + Integer.toString(j));
            xs[j] = (IntExpr) ctx.mkConst(names[j], types[j]);
            vars[j] = (IntExpr) ctx.mkBound(2 - j, types[j]); // <-- vars
                                                              // reversed!
        }

        Expr body_vars = ctx.mkAnd(
                ctx.mkEq(ctx.mkAdd(vars[0], ctx.mkInt(1)), ctx.mkInt(2)),
                ctx.mkEq(ctx.mkAdd(vars[1], ctx.mkInt(2)),
                        ctx.mkAdd(vars[2], ctx.mkInt(3))));

        Expr body_const = ctx.mkAnd(
                ctx.mkEq(ctx.mkAdd(xs[0], ctx.mkInt(1)), ctx.mkInt(2)),
                ctx.mkEq(ctx.mkAdd(xs[1], ctx.mkInt(2)),
                        ctx.mkAdd(xs[2], ctx.mkInt(3))));

        Expr x = ctx.mkForall(types, names, body_vars, 1, null, null,
                ctx.mkSymbol("Q1"), ctx.mkSymbol("skid1"));
        System.out.println("Quantifier X: " + x.toString());

        Expr y = ctx.mkForall(xs, body_const, 1, null, null,
                ctx.mkSymbol("Q2"), ctx.mkSymbol("skid2"));
        System.out.println("Quantifier Y: " + y.toString());
    }

    void quantifierExample2(Context ctx)
    {

        System.out.println("QuantifierExample2");
        Log.append("QuantifierExample2");

        Expr q1, q2;
        FuncDecl f = ctx.mkFuncDecl("f", ctx.getIntSort(), ctx.getIntSort());
        FuncDecl g = ctx.mkFuncDecl("g", ctx.getIntSort(), ctx.getIntSort());

        // Quantifier with Exprs as the bound variables.
        {
            Expr x = ctx.mkConst("x", ctx.getIntSort());
            Expr y = ctx.mkConst("y", ctx.getIntSort());
            Expr f_x = ctx.mkApp(f, x);
            Expr f_y = ctx.mkApp(f, y);
            Expr g_y = ctx.mkApp(g, y);
            @SuppressWarnings("unused")
            Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) };
            Expr[] no_pats = new Expr[] { f_y };
            Expr[] bound = new Expr[] { x, y };
            Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y));

            q1 = ctx.mkForall(bound, body, 1, null, no_pats, ctx.mkSymbol("q"),
                    ctx.mkSymbol("sk"));

            System.out.println(q1);
        }

        // Quantifier with de-Bruijn indices.
        {
            Expr x = ctx.mkBound(1, ctx.getIntSort());
            Expr y = ctx.mkBound(0, ctx.getIntSort());
            Expr f_x = ctx.mkApp(f, x);
            Expr f_y = ctx.mkApp(f, y);
            Expr g_y = ctx.mkApp(g, y);
            @SuppressWarnings("unused")
            Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) };
            Expr[] no_pats = new Expr[] { f_y };
            Symbol[] names = new Symbol[] { ctx.mkSymbol("x"),
                    ctx.mkSymbol("y") };
            Sort[] sorts = new Sort[] { ctx.getIntSort(), ctx.getIntSort() };
            Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y));

            q2 = ctx.mkForall(sorts, names, body, 1, null, // pats,
                    no_pats, ctx.mkSymbol("q"), ctx.mkSymbol("sk"));
            System.out.println(q2);
        }

        System.out.println(q1.equals(q2));
    }

    // / Prove that <tt>f(x, y) = f(w, v) implies y = v</tt> when
    // / <code>f</code> is injective in the second argument. <seealso
    // cref="inj_axiom"/>

    public void quantifierExample3(Context ctx) throws TestFailedException
    {
        System.out.println("QuantifierExample3");
        Log.append("QuantifierExample3");

        /*
         * If quantified formulas are asserted in a logical context, then the
         * model produced by Z3 should be viewed as a potential model.
         */

        /* declare function f */
        Sort I = ctx.getIntSort();
        FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I);

        /* f is injective in the second argument. */
        BoolExpr inj = injAxiom(ctx, f, 1);

        /* create x, y, v, w, fxy, fwv */
        Expr x = ctx.mkIntConst("x");
        Expr y = ctx.mkIntConst("y");
        Expr v = ctx.mkIntConst("v");
        Expr w = ctx.mkIntConst("w");
        Expr fxy = ctx.mkApp(f, x, y);
        Expr fwv = ctx.mkApp(f, w, v);

        /* f(x, y) = f(w, v) */
        BoolExpr p1 = ctx.mkEq(fxy, fwv);

        /* prove f(x, y) = f(w, v) implies y = v */
        BoolExpr p2 = ctx.mkEq(y, v);
        prove(ctx, p2, false, inj, p1);

        /* disprove f(x, y) = f(w, v) implies x = w */
        BoolExpr p3 = ctx.mkEq(x, w);
        disprove(ctx, p3, false, inj, p1);
    }

    // / Prove that <tt>f(x, y) = f(w, v) implies y = v</tt> when
    // / <code>f</code> is injective in the second argument. <seealso
    // cref="inj_axiom"/>

    public void quantifierExample4(Context ctx) throws TestFailedException
    {
        System.out.println("QuantifierExample4");
        Log.append("QuantifierExample4");

        /*
         * If quantified formulas are asserted in a logical context, then the
         * model produced by Z3 should be viewed as a potential model.
         */

        /* declare function f */
        Sort I = ctx.getIntSort();
        FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I);

        /* f is injective in the second argument. */
        BoolExpr inj = injAxiomAbs(ctx, f, 1);

        /* create x, y, v, w, fxy, fwv */
        Expr x = ctx.mkIntConst("x");
        Expr y = ctx.mkIntConst("y");
        Expr v = ctx.mkIntConst("v");
        Expr w = ctx.mkIntConst("w");
        Expr fxy = ctx.mkApp(f, x, y);
        Expr fwv = ctx.mkApp(f, w, v);

        /* f(x, y) = f(w, v) */
        BoolExpr p1 = ctx.mkEq(fxy, fwv);

        /* prove f(x, y) = f(w, v) implies y = v */
        BoolExpr p2 = ctx.mkEq(y, v);
        prove(ctx, p2, false, inj, p1);

        /* disprove f(x, y) = f(w, v) implies x = w */
        BoolExpr p3 = ctx.mkEq(x, w);
        disprove(ctx, p3, false, inj, p1);
    }

    // / Some basic tests.

    void basicTests(Context ctx) throws TestFailedException
    {
        System.out.println("BasicTests");

        Symbol fname = ctx.mkSymbol("f");
        Symbol x = ctx.mkSymbol("x");
        Symbol y = ctx.mkSymbol("y");

        Sort bs = ctx.mkBoolSort();

        Sort[] domain = { bs, bs };
        FuncDecl f = ctx.mkFuncDecl(fname, domain, bs);
        Expr fapp = ctx.mkApp(f, ctx.mkConst(x, bs), ctx.mkConst(y, bs));

        Expr[] fargs2 = { ctx.mkFreshConst("cp", bs) };
        Sort[] domain2 = { bs };
        Expr fapp2 = ctx.mkApp(ctx.mkFreshFuncDecl("fp", domain2, bs), fargs2);

        BoolExpr trivial_eq = ctx.mkEq(fapp, fapp);
        BoolExpr nontrivial_eq = ctx.mkEq(fapp, fapp2);

        Goal g = ctx.mkGoal(true, false, false);
        g.add(trivial_eq);
        g.add(nontrivial_eq);
        System.out.println("Goal: " + g);

        Solver solver = ctx.mkSolver();

        for (BoolExpr a : g.getFormulas())
            solver.add(a);

        if (solver.check() != Status.SATISFIABLE)
            throw new TestFailedException();

        ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g);
        if (ar.getNumSubgoals() == 1
                && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0]
                        .isDecidedUnsat()))
            throw new TestFailedException();

        ar = applyTactic(ctx, ctx.mkTactic("smt"), g);
        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat())
            throw new TestFailedException();

        g.add(ctx.mkEq(ctx.mkNumeral(1, ctx.mkBitVecSort(32)),
                ctx.mkNumeral(2, ctx.mkBitVecSort(32))));
        ar = applyTactic(ctx, ctx.mkTactic("smt"), g);
        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat())
            throw new TestFailedException();

        Goal g2 = ctx.mkGoal(true, true, false);
        ar = applyTactic(ctx, ctx.mkTactic("smt"), g2);
        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat())
            throw new TestFailedException();

        g2 = ctx.mkGoal(true, true, false);
        g2.add(ctx.mkFalse());
        ar = applyTactic(ctx, ctx.mkTactic("smt"), g2);
        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat())
            throw new TestFailedException();

        Goal g3 = ctx.mkGoal(true, true, false);
        Expr xc = ctx.mkConst(ctx.mkSymbol("x"), ctx.getIntSort());
        Expr yc = ctx.mkConst(ctx.mkSymbol("y"), ctx.getIntSort());
        g3.add(ctx.mkEq(xc, ctx.mkNumeral(1, ctx.getIntSort())));
        g3.add(ctx.mkEq(yc, ctx.mkNumeral(2, ctx.getIntSort())));
        BoolExpr constr = ctx.mkEq(xc, yc);
        g3.add(constr);
        ar = applyTactic(ctx, ctx.mkTactic("smt"), g3);
        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat())
            throw new TestFailedException();

        modelConverterTest(ctx);

        // Real num/den test.
        RatNum rn = ctx.mkReal(42, 43);
        Expr inum = rn.getNumerator();
        Expr iden = rn.getDenominator();
        System.out.println("Numerator: " + inum + " Denominator: " + iden);
        if (!inum.toString().equals("42") || !iden.toString().equals("43"))
            throw new TestFailedException();

        if (!rn.toDecimalString(3).toString().equals("0.976?"))
            throw new TestFailedException();

        bigIntCheck(ctx, ctx.mkReal("-1231231232/234234333"));
        bigIntCheck(ctx, ctx.mkReal("-123123234234234234231232/234234333"));
        bigIntCheck(ctx, ctx.mkReal("-234234333"));
        bigIntCheck(ctx, ctx.mkReal("234234333/2"));

        String bn = "1234567890987654321";

        if (!ctx.mkInt(bn).getBigInteger().toString().equals(bn))
            throw new TestFailedException();

        if (!ctx.mkBV(bn, 128).getBigInteger().toString().equals(bn))
            throw new TestFailedException();

        if (ctx.mkBV(bn, 32).getBigInteger().toString().equals(bn))
            throw new TestFailedException();

        // Error handling test.
        try
        {
            @SuppressWarnings("unused")
            IntExpr i = ctx.mkInt("1/2");
            throw new TestFailedException(); // unreachable
        } catch (Z3Exception e)
        {
        }
    }

    // / Some basic expression casting tests.

    void castingTest(Context ctx) throws TestFailedException
    {
        System.out.println("CastingTest");

        Sort[] domain = { ctx.getBoolSort(), ctx.getBoolSort() };
        FuncDecl f = ctx.mkFuncDecl("f", domain, ctx.getBoolSort());

        AST upcast = ctx.mkFuncDecl(ctx.mkSymbol("q"), domain,
                ctx.getBoolSort());

        try
        {
            @SuppressWarnings("unused")
            FuncDecl downcast = (FuncDecl) f; // OK
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        try
        {
            @SuppressWarnings("unused")
            Expr uc = (Expr) upcast;
            throw new TestFailedException(); // should not be reachable!
        } catch (ClassCastException e)
        {
        }

        Symbol s = ctx.mkSymbol(42);
        IntSymbol si = (s.getClass() == IntSymbol.class) ? (IntSymbol) s : null;
        if (si == null)
            throw new TestFailedException();
        try
        {
            @SuppressWarnings("unused")
            IntSymbol si2 = (IntSymbol) s;
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        s = ctx.mkSymbol("abc");
        StringSymbol ss = (s.getClass() == StringSymbol.class) ? (StringSymbol) s
                : null;
        if (ss == null)
            throw new TestFailedException();
        try
        {
            @SuppressWarnings("unused")
            StringSymbol ss2 = (StringSymbol) s;
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }
        try
        {
            @SuppressWarnings("unused")
            IntSymbol si2 = (IntSymbol) s;
            throw new TestFailedException(); // unreachable
        } catch (Exception e)
        {
        }

        Sort srt = ctx.mkBitVecSort(32);
        BitVecSort bvs = null;
        try
        {
            bvs = (BitVecSort) srt;
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        if (bvs.getSize() != 32)
            throw new TestFailedException();

        Expr q = ctx.mkAdd(ctx.mkInt(1), ctx.mkInt(2));
        Expr q2 = q.getArgs()[1];
        Sort qs = q2.getSort();
        if (qs.getClass() != IntSort.class)
            throw new TestFailedException();
        try
        {
            @SuppressWarnings("unused")
            IntSort isrt = (IntSort) qs;
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        AST a = ctx.mkInt(42);

        try
        {
            Expr.class.cast(a);
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        try
        {
            ArithExpr.class.cast(a);
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        try
        {
            IntExpr.class.cast(a);
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        try
        {
            IntNum.class.cast(a);
        } catch (ClassCastException e)
        {
            throw new TestFailedException();
        }

        Expr[][] earr = new Expr[2][];
        earr[0] = new Expr[2];
        earr[1] = new Expr[2];
        earr[0][0] = ctx.mkTrue();
        earr[0][1] = ctx.mkTrue();
        earr[1][0] = ctx.mkFalse();
        earr[1][1] = ctx.mkFalse();
        for (Expr[] ea : earr)
            for (Expr e : ea)
            {
                try
                {
                    Expr ns = ctx.mkNot((BoolExpr) e);
                    @SuppressWarnings("unused")
                    BoolExpr ens = (BoolExpr) ns;
                } catch (ClassCastException ex)
                {
                    throw new TestFailedException();
                }
            }
    }


    // / Shows how to read an SMT2 file.

    void smt2FileTest(String filename)
    {
        Date before = new Date();

        System.out.println("SMT2 File test ");
        System.gc();

        {
            HashMap<String, String> cfg = new HashMap<String, String>();
            cfg.put("model", "true");
            Context ctx = new Context(cfg);
            BoolExpr a = ctx.mkAnd(ctx.parseSMTLIB2File(filename, null, null, null, null));

            long t_diff = ((new Date()).getTime() - before.getTime()) / 1000;

            System.out.println("SMT2 file read time: " + t_diff + " sec");

            // Iterate over the formula.

            LinkedList<Expr> q = new LinkedList<Expr>();
            q.add(a);
            int cnt = 0;
            while (q.size() > 0)
            {
                AST cur = (AST) q.removeFirst();
                cnt++;

                if (cur.getClass() == Expr.class)
                    if (!(cur.isVar()))
                        for (Expr c : ((Expr) cur).getArgs())
                            q.add(c);
            }
            System.out.println(cnt + " ASTs");
        }

        long t_diff = ((new Date()).getTime() - before.getTime()) / 1000;
        System.out.println("SMT2 file test took " + t_diff + " sec");
    }

    // / Shows how to use Solver(logic)

    // / @param ctx 
    void logicExample(Context ctx) throws TestFailedException
    {
        System.out.println("LogicTest");
        Log.append("LogicTest");

        com.microsoft.z3.Global.ToggleWarningMessages(true);

        BitVecSort bvs = ctx.mkBitVecSort(32);
        Expr x = ctx.mkConst("x", bvs);
        Expr y = ctx.mkConst("y", bvs);
        BoolExpr eq = ctx.mkEq(x, y);

        // Use a solver for QF_BV
        Solver s = ctx.mkSolver("QF_BV");
        s.add(eq);
        Status res = s.check();
        System.out.println("solver result: " + res);

        // Or perhaps a tactic for QF_BV
        Goal g = ctx.mkGoal(true, false, false);
        g.add(eq);

        Tactic t = ctx.mkTactic("qfbv");
        ApplyResult ar = t.apply(g);
        System.out.println("tactic result: " + ar);

        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat())
            throw new TestFailedException();
    }

    // / Demonstrates how to use the ParOr tactic.

    void parOrExample(Context ctx) throws TestFailedException
    {
        System.out.println("ParOrExample");
        Log.append("ParOrExample");

        BitVecSort bvs = ctx.mkBitVecSort(32);
        Expr x = ctx.mkConst("x", bvs);
        Expr y = ctx.mkConst("y", bvs);
        BoolExpr q = ctx.mkEq(x, y);

        Goal g = ctx.mkGoal(true, false, false);
        g.add(q);

        Tactic t1 = ctx.mkTactic("qfbv");
        Tactic t2 = ctx.mkTactic("qfbv");
        Tactic p = ctx.parOr(t1, t2);

        ApplyResult ar = p.apply(g);

        if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat())
            throw new TestFailedException();
    }

    void bigIntCheck(Context ctx, RatNum r)
    {
        System.out.println("Num: " + r.getBigIntNumerator());
        System.out.println("Den: " + r.getBigIntDenominator());
    }

    // / Find a model for <code>x xor y</code>.

    public void findModelExample1(Context ctx) throws TestFailedException
    {
        System.out.println("FindModelExample1");
        Log.append("FindModelExample1");

        BoolExpr x = ctx.mkBoolConst("x");
        BoolExpr y = ctx.mkBoolConst("y");
        BoolExpr x_xor_y = ctx.mkXor(x, y);

        Model model = check(ctx, x_xor_y, Status.SATISFIABLE);
        System.out.println("x = " + model.evaluate(x, false) + ", y = "
                + model.evaluate(y, false));
    }

    // / Find a model for <tt>x < y + 1, x > 2</tt>.
    // / Then, assert <tt>not(x = y)</tt>, and find another model.

    public void findModelExample2(Context ctx) throws TestFailedException
    {
        System.out.println("FindModelExample2");
        Log.append("FindModelExample2");

        IntExpr x = ctx.mkIntConst("x");
        IntExpr y = ctx.mkIntConst("y");
        IntExpr one = ctx.mkInt(1);
        IntExpr two = ctx.mkInt(2);

        ArithExpr y_plus_one = ctx.mkAdd(y, one);

        BoolExpr c1 = ctx.mkLt(x, y_plus_one);
        BoolExpr c2 = ctx.mkGt(x, two);

        BoolExpr q = ctx.mkAnd(c1, c2);

        System.out.println("model for: x < y + 1, x > 2");
        Model model = check(ctx, q, Status.SATISFIABLE);
        System.out.println("x = " + model.evaluate(x, false) + ", y ="
                + model.evaluate(y, false));

        /* assert not(x = y) */
        BoolExpr x_eq_y = ctx.mkEq(x, y);
        BoolExpr c3 = ctx.mkNot(x_eq_y);

        q = ctx.mkAnd(q, c3);

        System.out.println("model for: x < y + 1, x > 2, not(x = y)");
        model = check(ctx, q, Status.SATISFIABLE);
        System.out.println("x = " + model.evaluate(x, false) + ", y = "
                + model.evaluate(y, false));
    }

    // / Prove <tt>x = y implies g(x) = g(y)</tt>, and
    // / disprove <tt>x = y implies g(g(x)) = g(y)</tt>.

    // / <remarks>This function demonstrates how to create uninterpreted
    // / types and functions.</remarks>
    public void proveExample1(Context ctx) throws TestFailedException
    {
        System.out.println("ProveExample1");
        Log.append("ProveExample1");

        /* create uninterpreted type. */
        Sort U = ctx.mkUninterpretedSort(ctx.mkSymbol("U"));

        /* declare function g */
        FuncDecl g = ctx.mkFuncDecl("g", U, U);

        /* create x and y */
        Expr x = ctx.mkConst("x", U);
        Expr y = ctx.mkConst("y", U);
        /* create g(x), g(y) */
        Expr gx = g.apply(x);
        Expr gy = g.apply(y);

        /* assert x = y */
        BoolExpr eq = ctx.mkEq(x, y);

        /* prove g(x) = g(y) */
        BoolExpr f = ctx.mkEq(gx, gy);
        System.out.println("prove: x = y implies g(x) = g(y)");
        prove(ctx, ctx.mkImplies(eq, f), false);

        /* create g(g(x)) */
        Expr ggx = g.apply(gx);

        /* disprove g(g(x)) = g(y) */
        f = ctx.mkEq(ggx, gy);
        System.out.println("disprove: x = y implies g(g(x)) = g(y)");
        disprove(ctx, ctx.mkImplies(eq, f), false);

        /* Print the model using the custom model printer */
        Model m = check(ctx, ctx.mkNot(f), Status.SATISFIABLE);
        System.out.println(m);
    }

    // / Prove <tt>not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0
    // </tt>.
    // / Then, show that <tt>z < -1</tt> is not implied.

    // / <remarks>This example demonstrates how to combine uninterpreted
    // functions
    // / and arithmetic.</remarks>
    public void proveExample2(Context ctx) throws TestFailedException
    {
        System.out.println("ProveExample2");
        Log.append("ProveExample2");

        /* declare function g */
        Sort I = ctx.getIntSort();

        FuncDecl g = ctx.mkFuncDecl("g", I, I);

        /* create x, y, and z */
        IntExpr x = ctx.mkIntConst("x");
        IntExpr y = ctx.mkIntConst("y");
        IntExpr z = ctx.mkIntConst("z");

        /* create gx, gy, gz */
        Expr gx = ctx.mkApp(g, x);
        Expr gy = ctx.mkApp(g, y);
        Expr gz = ctx.mkApp(g, z);

        /* create zero */
        IntExpr zero = ctx.mkInt(0);

        /* assert not(g(g(x) - g(y)) = g(z)) */
        ArithExpr gx_gy = ctx.mkSub((IntExpr) gx, (IntExpr) gy);
        Expr ggx_gy = ctx.mkApp(g, gx_gy);
        BoolExpr eq = ctx.mkEq(ggx_gy, gz);
        BoolExpr c1 = ctx.mkNot(eq);

        /* assert x + z <= y */
        ArithExpr x_plus_z = ctx.mkAdd(x, z);
        BoolExpr c2 = ctx.mkLe(x_plus_z, y);

        /* assert y <= x */
        BoolExpr c3 = ctx.mkLe(y, x);

        /* prove z < 0 */
        BoolExpr f = ctx.mkLt(z, zero);
        System.out
                .println("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0");
        prove(ctx, f, false, c1, c2, c3);

        /* disprove z < -1 */
        IntExpr minus_one = ctx.mkInt(-1);
        f = ctx.mkLt(z, minus_one);
        System.out
                .println("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1");
        disprove(ctx, f, false, c1, c2, c3);
    }

    // / Show how push & pop can be used to create "backtracking" points.

    // / <remarks>This example also demonstrates how big numbers can be
    // / created in ctx.</remarks>
    public void pushPopExample1(Context ctx) throws TestFailedException
    {
        System.out.println("PushPopExample1");
        Log.append("PushPopExample1");

        /* create a big number */
        IntSort int_type = ctx.getIntSort();
        IntExpr big_number = ctx
                .mkInt("1000000000000000000000000000000000000000000000000000000");

        /* create number 3 */
        IntExpr three = (IntExpr) ctx.mkNumeral("3", int_type);

        /* create x */
        IntExpr x = ctx.mkIntConst("x");

        Solver solver = ctx.mkSolver();

        /* assert x >= "big number" */
        BoolExpr c1 = ctx.mkGe(x, big_number);
        System.out.println("assert: x >= 'big number'");
        solver.add(c1);

        /* create a backtracking point */
        System.out.println("push");
        solver.push();

        /* assert x <= 3 */
        BoolExpr c2 = ctx.mkLe(x, three);
        System.out.println("assert: x <= 3");
        solver.add(c2);

        /* context is inconsistent at this point */
        if (solver.check() != Status.UNSATISFIABLE)
            throw new TestFailedException();

        /*
         * backtrack: the constraint x <= 3 will be removed, since it was
         * asserted after the last ctx.Push.
         */
        System.out.println("pop");
        solver.pop(1);

        /* the context is consistent again. */
        if (solver.check() != Status.SATISFIABLE)
            throw new TestFailedException();

        /* new constraints can be asserted... */

        /* create y */
        IntExpr y = ctx.mkIntConst("y");

        /* assert y > x */
        BoolExpr c3 = ctx.mkGt(y, x);
        System.out.println("assert: y > x");
        solver.add(c3);

        /* the context is still consistent. */
        if (solver.check() != Status.SATISFIABLE)
            throw new TestFailedException();
    }

    // / Tuples.

    // / <remarks>Check that the projection of a tuple
    // / returns the corresponding element.</remarks>
    public void tupleExample(Context ctx) throws TestFailedException
    {
        System.out.println("TupleExample");
        Log.append("TupleExample");

        Sort int_type = ctx.getIntSort();
        TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of
                                                                    // tuple
                                                                    // constructor
                new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names
                                                                                // of
                                                                                // projection
                                                                                // operators
                new Sort[] { int_type, int_type } // types of projection
                                                  // operators
                );
        FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for
                                                   // projections
        @SuppressWarnings("unused")
        FuncDecl second = tuple.getFieldDecls()[1];
        Expr x = ctx.mkConst("x", int_type);
        Expr y = ctx.mkConst("y", int_type);
        Expr n1 = tuple.mkDecl().apply(x, y);
        Expr n2 = first.apply(n1);
        BoolExpr n3 = ctx.mkEq(x, n2);
        System.out.println("Tuple example: " + n3);
        prove(ctx, n3, false);
    }

    // / Simple bit-vector example.

    // / <remarks>
    // / This example disproves that x - 10 &lt;= 0 IFF x &lt;= 10 for (32-bit)
    // machine integers
    // / </remarks>
    public void bitvectorExample1(Context ctx) throws TestFailedException
    {
        System.out.println("BitvectorExample1");
        Log.append("BitvectorExample1");

        BitVecSort bv_type = ctx.mkBitVecSort(32);
        BitVecExpr x = (BitVecExpr) ctx.mkConst("x", bv_type);
        BitVecNum zero = (BitVecNum) ctx.mkNumeral("0", bv_type);
        BitVecNum ten = ctx.mkBV(10, 32);
        BitVecExpr x_minus_ten = ctx.mkBVSub(x, ten);
        /* bvsle is signed less than or equal to */
        BoolExpr c1 = ctx.mkBVSLE(x, ten);
        BoolExpr c2 = ctx.mkBVSLE(x_minus_ten, zero);
        BoolExpr thm = ctx.mkIff(c1, c2);
        System.out
                .println("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers");
        disprove(ctx, thm, false);
    }

    // / Find x and y such that: x ^ y - 103 == x * y

    public void bitvectorExample2(Context ctx) throws TestFailedException
    {
        System.out.println("BitvectorExample2");
        Log.append("BitvectorExample2");

        /* construct x ^ y - 103 == x * y */
        BitVecSort bv_type = ctx.mkBitVecSort(32);
        BitVecExpr x = ctx.mkBVConst("x", 32);
        BitVecExpr y = ctx.mkBVConst("y", 32);
        BitVecExpr x_xor_y = ctx.mkBVXOR(x, y);
        BitVecExpr c103 = (BitVecNum) ctx.mkNumeral("103", bv_type);
        BitVecExpr lhs = ctx.mkBVSub(x_xor_y, c103);
        BitVecExpr rhs = ctx.mkBVMul(x, y);
        BoolExpr ctr = ctx.mkEq(lhs, rhs);

        System.out
                .println("find values of x and y, such that x ^ y - 103 == x * y");

        /* find a model (i.e., values for x an y that satisfy the constraint */
        Model m = check(ctx, ctr, Status.SATISFIABLE);
        System.out.println(m);
    }

    // / Demonstrates how to use the SMTLIB parser.

    public void parserExample1(Context ctx) throws TestFailedException
    {
        System.out.println("ParserExample1");
        Log.append("ParserExample1");

        BoolExpr f = ctx.parseSMTLIB2String(
                "(declare-const x Int) (declare-const y Int) (assert (and (> x y) (> x 0)))",
                null, null, null, null)[0];
        System.out.println("formula " + f);

        @SuppressWarnings("unused")
        Model m = check(ctx, f, Status.SATISFIABLE);
    }

    // / Demonstrates how to initialize the parser symbol table.

    public void parserExample2(Context ctx) throws TestFailedException
    {
        System.out.println("ParserExample2");
        Log.append("ParserExample2");

        Symbol[] declNames = { ctx.mkSymbol("a"), ctx.mkSymbol("b") };
        FuncDecl a = ctx.mkConstDecl(declNames[0], ctx.mkIntSort());
        FuncDecl b = ctx.mkConstDecl(declNames[1], ctx.mkIntSort());
        FuncDecl[] decls = new FuncDecl[] { a, b };

        BoolExpr f = ctx.parseSMTLIB2String("(assert (> a b))", null, null,
                declNames, decls)[0];
        System.out.println("formula: " + f);
        check(ctx, f, Status.SATISFIABLE);
    }

    // / Demonstrates how to initialize the parser symbol table.

    public void parserExample3(Context ctx) throws Exception
    {
        System.out.println("ParserExample3");
        Log.append("ParserExample3");

        /* declare function g */
        Sort I = ctx.mkIntSort();
        FuncDecl g = ctx.mkFuncDecl("g", new Sort[] { I, I }, I);

        BoolExpr ca = commAxiom(ctx, g);

        BoolExpr thm = ctx.parseSMTLIB2String(
                "(declare-fun (Int Int) Int) (assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))",
                null, null, new Symbol[] { ctx.mkSymbol("gg") },
                new FuncDecl[] { g })[0];
        System.out.println("formula: " + thm);
        prove(ctx, thm, false, ca);
    }


    // / Demonstrates how to handle parser errors using Z3 error handling
    // support.

    // / <remarks></remarks>
    public void parserExample5(Context ctx)
    {
        System.out.println("ParserExample5");

        try
        {
            ctx.parseSMTLIB2String(
                    /*
                     * the following string has a parsing error: missing
                     * parenthesis
                     */
                    "(declare-const x Int (declare-const y Int)) (assert (> x y))",
                    null, null, null, null);
        } catch (Z3Exception e)
        {
            System.out.println("Z3 error: " + e);
        }
    }

    // / Create an ite-Expr (if-then-else Exprs).

    public void iteExample(Context ctx)
    {
        System.out.println("ITEExample");
        Log.append("ITEExample");

        BoolExpr f = ctx.mkFalse();
        Expr one = ctx.mkInt(1);
        Expr zero = ctx.mkInt(0);
        Expr ite = ctx.mkITE(f, one, zero);

        System.out.println("Expr: " + ite);
    }

    // / Create an enumeration data type.

    public void enumExample(Context ctx) throws TestFailedException
    {
        System.out.println("EnumExample");
        Log.append("EnumExample");

        Symbol name = ctx.mkSymbol("fruit");

        EnumSort fruit = ctx.mkEnumSort(name, ctx.mkSymbol("apple"),
                ctx.mkSymbol("banana"), ctx.mkSymbol("orange"));

        System.out.println((fruit.getConsts()[0]));
        System.out.println((fruit.getConsts()[1]));
        System.out.println((fruit.getConsts()[2]));

        System.out.println((fruit.getTesterDecls()[0]));
        System.out.println((fruit.getTesterDecls()[1]));
        System.out.println((fruit.getTesterDecls()[2]));

        Expr apple = fruit.getConsts()[0];
        Expr banana = fruit.getConsts()[1];
        Expr orange = fruit.getConsts()[2];

        /* Apples are different from oranges */
        prove(ctx, ctx.mkNot(ctx.mkEq(apple, orange)), false);

        /* Apples pass the apple test */
        prove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], apple),
                false);

        /* Oranges fail the apple test */
        disprove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], orange),
                false);
        prove(ctx,
                (BoolExpr) ctx.mkNot((BoolExpr) ctx.mkApp(
                        fruit.getTesterDecls()[0], orange)), false);

        Expr fruity = ctx.mkConst("fruity", fruit);

        /* If something is fruity, then it is an apple, banana, or orange */

        prove(ctx,
                ctx.mkOr(ctx.mkEq(fruity, apple), ctx.mkEq(fruity, banana),
                        ctx.mkEq(fruity, orange)), false);
    }

    // / Create a list datatype.

    public void listExample(Context ctx) throws TestFailedException
    {
        System.out.println("ListExample");
        Log.append("ListExample");

        Sort int_ty;
        ListSort int_list;
        Expr nil, l1, l2, x, y, u, v;
        BoolExpr fml, fml1;

        int_ty = ctx.mkIntSort();

        int_list = ctx.mkListSort(ctx.mkSymbol("int_list"), int_ty);

        nil = ctx.mkConst(int_list.getNilDecl());
        l1 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(1), nil);
        l2 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(2), nil);

        /* nil != cons(1, nil) */
        prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false);

        /* cons(2,nil) != cons(1, nil) */
        prove(ctx, ctx.mkNot(ctx.mkEq(l1, l2)), false);

        /* cons(x,nil) = cons(y, nil) => x = y */
        x = ctx.mkConst("x", int_ty);
        y = ctx.mkConst("y", int_ty);
        l1 = ctx.mkApp(int_list.getConsDecl(), x, nil);
        l2 = ctx.mkApp(int_list.getConsDecl(), y, nil);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false);

        /* cons(x,u) = cons(x, v) => u = v */
        u = ctx.mkConst("u", int_list);
        v = ctx.mkConst("v", int_list);
        l1 = ctx.mkApp(int_list.getConsDecl(), x, u);
        l2 = ctx.mkApp(int_list.getConsDecl(), y, v);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false);

        /* is_nil(u) or is_cons(u) */
        prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(int_list.getIsNilDecl(), u),
                (BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u)), false);

        /* occurs check u != cons(x,u) */
        prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false);

        /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */
        fml1 = ctx.mkEq(
                u,
                ctx.mkApp(int_list.getConsDecl(),
                        ctx.mkApp(int_list.getHeadDecl(), u),
                        ctx.mkApp(int_list.getTailDecl(), u)));
        fml = ctx.mkImplies((BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u),
                fml1);
        System.out.println("Formula " + fml);

        prove(ctx, fml, false);

        disprove(ctx, fml1, false);
    }

    // / Create a binary tree datatype.

    public void treeExample(Context ctx) throws TestFailedException
    {
        System.out.println("TreeExample");
        Log.append("TreeExample");

        Sort cell;
        FuncDecl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl;
        Expr nil, l1, l2, x, y, u, v;
        BoolExpr fml, fml1;
        String[] head_tail = new String[] { "car", "cdr" };
        Sort[] sorts = new Sort[] { null, null };
        int[] sort_refs = new int[] { 0, 0 };
        Constructor nil_con, cons_con;

        nil_con = ctx.mkConstructor("nil", "is_nil", null, null, null);
        cons_con = ctx.mkConstructor("cons", "is_cons", head_tail, sorts,
                sort_refs);
        Constructor[] constructors = new Constructor[] { nil_con, cons_con };

        cell = ctx.mkDatatypeSort("cell", constructors);

        nil_decl = nil_con.ConstructorDecl();
        is_nil_decl = nil_con.getTesterDecl();
        cons_decl = cons_con.ConstructorDecl();
        is_cons_decl = cons_con.getTesterDecl();
        FuncDecl[] cons_accessors = cons_con.getAccessorDecls();
        car_decl = cons_accessors[0];
        cdr_decl = cons_accessors[1];

        nil = ctx.mkConst(nil_decl);
        l1 = ctx.mkApp(cons_decl, nil, nil);
        l2 = ctx.mkApp(cons_decl, l1, nil);

        /* nil != cons(nil, nil) */
        prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false);

        /* cons(x,u) = cons(x, v) => u = v */
        u = ctx.mkConst("u", cell);
        v = ctx.mkConst("v", cell);
        x = ctx.mkConst("x", cell);
        y = ctx.mkConst("y", cell);
        l1 = ctx.mkApp(cons_decl, x, u);
        l2 = ctx.mkApp(cons_decl, y, v);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false);

        /* is_nil(u) or is_cons(u) */
        prove(ctx,
                ctx.mkOr((BoolExpr) ctx.mkApp(is_nil_decl, u),
                        (BoolExpr) ctx.mkApp(is_cons_decl, u)), false);

        /* occurs check u != cons(x,u) */
        prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false);

        /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */
        fml1 = ctx.mkEq(
                u,
                ctx.mkApp(cons_decl, ctx.mkApp(car_decl, u),
                        ctx.mkApp(cdr_decl, u)));
        fml = ctx.mkImplies((BoolExpr) ctx.mkApp(is_cons_decl, u), fml1);
        System.out.println("Formula " + fml);
        prove(ctx, fml, false);

        disprove(ctx, fml1, false);
    }

    // / Create a forest of trees.

    // / <remarks>
    // / forest ::= nil | cons(tree, forest)
    // / tree ::= nil | cons(forest, forest)
    // / </remarks>
    public void forestExample(Context ctx) throws TestFailedException
    {
        System.out.println("ForestExample");
        Log.append("ForestExample");

        Sort tree, forest;
        @SuppressWarnings("unused")
        FuncDecl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl;
        @SuppressWarnings("unused")
        FuncDecl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl;
        @SuppressWarnings("unused")
        Expr nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v;

        //
        // Declare the names of the accessors for cons.
        // Then declare the sorts of the accessors.
        // For this example, all sorts refer to the new types 'forest' and
        // 'tree'
        // being declared, so we pass in null for both sorts1 and sorts2.
        // On the other hand, the sort_refs arrays contain the indices of the
        // two new sorts being declared. The first element in sort1_refs
        // points to 'tree', which has index 1, the second element in sort1_refs
        // array
        // points to 'forest', which has index 0.
        //
        Symbol[] head_tail1 = new Symbol[] { ctx.mkSymbol("head"),
                ctx.mkSymbol("tail") };
        Sort[] sorts1 = new Sort[] { null, null };
        int[] sort1_refs = new int[] { 1, 0 }; // the first item points to a
                                               // tree, the second to a forest

        Symbol[] head_tail2 = new Symbol[] { ctx.mkSymbol("car"),
                ctx.mkSymbol("cdr") };
        Sort[] sorts2 = new Sort[] { null, null };
        int[] sort2_refs = new int[] { 0, 0 }; // both items point to the forest
                                               // datatype.
        Constructor nil1_con, cons1_con, nil2_con, cons2_con;
        Constructor[] constructors1 = new Constructor[2], constructors2 = new Constructor[2];
        Symbol[] sort_names = { ctx.mkSymbol("forest"), ctx.mkSymbol("tree") };

        /* build a forest */
        nil1_con = ctx.mkConstructor(ctx.mkSymbol("nil"),
                ctx.mkSymbol("is_nil"), null, null, null);
        cons1_con = ctx.mkConstructor(ctx.mkSymbol("cons1"),
                ctx.mkSymbol("is_cons1"), head_tail1, sorts1, sort1_refs);
        constructors1[0] = nil1_con;
        constructors1[1] = cons1_con;

        /* build a tree */
        nil2_con = ctx.mkConstructor(ctx.mkSymbol("nil2"),
                ctx.mkSymbol("is_nil2"), null, null, null);
        cons2_con = ctx.mkConstructor(ctx.mkSymbol("cons2"),
                ctx.mkSymbol("is_cons2"), head_tail2, sorts2, sort2_refs);
        constructors2[0] = nil2_con;
        constructors2[1] = cons2_con;

        Constructor[][] clists = new Constructor[][] { constructors1,
                constructors2 };

        Sort[] sorts = ctx.mkDatatypeSorts(sort_names, clists);
        forest = sorts[0];
        tree = sorts[1];

        //
        // Now that the datatype has been created.
        // Query the constructors for the constructor
        // functions, testers, and field accessors.
        //
        nil1_decl = nil1_con.ConstructorDecl();
        is_nil1_decl = nil1_con.getTesterDecl();
        cons1_decl = cons1_con.ConstructorDecl();
        is_cons1_decl = cons1_con.getTesterDecl();
        FuncDecl[] cons1_accessors = cons1_con.getAccessorDecls();
        car1_decl = cons1_accessors[0];
        cdr1_decl = cons1_accessors[1];

        nil2_decl = nil2_con.ConstructorDecl();
        is_nil2_decl = nil2_con.getTesterDecl();
        cons2_decl = cons2_con.ConstructorDecl();
        is_cons2_decl = cons2_con.getTesterDecl();
        FuncDecl[] cons2_accessors = cons2_con.getAccessorDecls();
        car2_decl = cons2_accessors[0];
        cdr2_decl = cons2_accessors[1];

        nil1 = ctx.mkConst(nil1_decl);
        nil2 = ctx.mkConst(nil2_decl);
        f1 = ctx.mkApp(cons1_decl, nil2, nil1);
        t1 = ctx.mkApp(cons2_decl, nil1, nil1);
        t2 = ctx.mkApp(cons2_decl, f1, nil1);
        t3 = ctx.mkApp(cons2_decl, f1, f1);
        t4 = ctx.mkApp(cons2_decl, nil1, f1);
        f2 = ctx.mkApp(cons1_decl, t1, nil1);
        f3 = ctx.mkApp(cons1_decl, t1, f1);

        /* nil != cons(nil,nil) */
        prove(ctx, ctx.mkNot(ctx.mkEq(nil1, f1)), false);
        prove(ctx, ctx.mkNot(ctx.mkEq(nil2, t1)), false);

        /* cons(x,u) = cons(x, v) => u = v */
        u = ctx.mkConst("u", forest);
        v = ctx.mkConst("v", forest);
        x = ctx.mkConst("x", tree);
        y = ctx.mkConst("y", tree);
        l1 = ctx.mkApp(cons1_decl, x, u);
        l2 = ctx.mkApp(cons1_decl, y, v);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false);
        prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false);

        /* is_nil(u) or is_cons(u) */
        prove(ctx,
                ctx.mkOr((BoolExpr) ctx.mkApp(is_nil1_decl, u),
                        (BoolExpr) ctx.mkApp(is_cons1_decl, u)), false);

        /* occurs check u != cons(x,u) */
        prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false);
    }

    // / Demonstrate how to use #Eval.

    public void evalExample1(Context ctx)
    {
        System.out.println("EvalExample1");
        Log.append("EvalExample1");

        IntExpr x = ctx.mkIntConst("x");
        IntExpr y = ctx.mkIntConst("y");
        IntExpr two = ctx.mkInt(2);

        Solver solver = ctx.mkSolver();

        /* assert x < y */
        solver.add(ctx.mkLt(x, y));

        /* assert x > 2 */
        solver.add(ctx.mkGt(x, two));

        /* find model for the constraints above */
        Model model = null;
        if (Status.SATISFIABLE == solver.check())
        {
            model = solver.getModel();
            System.out.println(model);
            System.out.println("\nevaluating x+y");
            Expr v = model.evaluate(ctx.mkAdd(x, y), false);
            if (v != null)
            {
                System.out.println("result = " + (v));
            } else
            {
                System.out.println("Failed to evaluate: x+y");
            }
        } else
        {
            System.out.println("BUG, the constraints are satisfiable.");
        }
    }

    // / Demonstrate how to use #Eval on tuples.

    public void evalExample2(Context ctx)
    {
        System.out.println("EvalExample2");
        Log.append("EvalExample2");

        Sort int_type = ctx.getIntSort();
        TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of
                                                                    // tuple
                                                                    // constructor
                new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names
                                                                                // of
                                                                                // projection
                                                                                // operators
                new Sort[] { int_type, int_type } // types of projection
                                                  // operators
                );
        FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for
                                                   // projections
        FuncDecl second = tuple.getFieldDecls()[1];
        Expr tup1 = ctx.mkConst("t1", tuple);
        Expr tup2 = ctx.mkConst("t2", tuple);

        Solver solver = ctx.mkSolver();

        /* assert tup1 != tup2 */
        solver.add(ctx.mkNot(ctx.mkEq(tup1, tup2)));
        /* assert first tup1 = first tup2 */
        solver.add(ctx.mkEq(ctx.mkApp(first, tup1), ctx.mkApp(first, tup2)));

        /* find model for the constraints above */
        Model model = null;
        if (Status.SATISFIABLE == solver.check())
        {
            model = solver.getModel();
            System.out.println(model);
            System.out.println("evaluating tup1 "
                    + (model.evaluate(tup1, false)));
            System.out.println("evaluating tup2 "
                    + (model.evaluate(tup2, false)));
            System.out.println("evaluating second(tup2) "
                    + (model.evaluate(ctx.mkApp(second, tup2), false)));
        } else
        {
            System.out.println("BUG, the constraints are satisfiable.");
        }
    }

    // / Demonstrate how to use <code>Push</code>and <code>Pop</code>to
    // / control the size of models.

    // / <remarks>Note: this test is specialized to 32-bit bitvectors.</remarks>
    public void checkSmall(Context ctx, Solver solver, BitVecExpr... to_minimize)
    {
        int num_Exprs = to_minimize.length;
        int[] upper = new int[num_Exprs];
        int[] lower = new int[num_Exprs];
        for (int i = 0; i < upper.length; ++i)
        {
            upper[i] = Integer.MAX_VALUE;
            lower[i] = 0;
        }
        boolean some_work = true;
        int last_index = -1;
        int last_upper = 0;
        while (some_work)
        {
            solver.push();

            boolean check_is_sat = true;
            while (check_is_sat && some_work)
            {
                // Assert all feasible bounds.
                for (int i = 0; i < num_Exprs; ++i)
                {
                    solver.add(ctx.mkBVULE(to_minimize[i],
                            ctx.mkBV(upper[i], 32)));
                }

                check_is_sat = Status.SATISFIABLE == solver.check();
                if (!check_is_sat)
                {
                    if (last_index != -1)
                    {
                        lower[last_index] = last_upper + 1;
                    }
                    break;
                }
                System.out.println(solver.getModel());

                // narrow the bounds based on the current model.
                for (int i = 0; i < num_Exprs; ++i)
                {
                    Expr v = solver.getModel().evaluate(to_minimize[i], false);
                    int ui = ((BitVecNum) v).getInt();
                    if (ui < upper[i])
                    {
                        upper[i] = (int) ui;
                    }
                    System.out.println(i + " " + lower[i] + " " + upper[i]);
                }

                // find a new bound to add
                some_work = false;
                last_index = 0;
                for (int i = 0; i < num_Exprs; ++i)
                {
                    if (lower[i] < upper[i])
                    {
                        last_upper = (upper[i] + lower[i]) / 2;
                        last_index = i;
                        solver.add(ctx.mkBVULE(to_minimize[i],
                                ctx.mkBV(last_upper, 32)));
                        some_work = true;
                        break;
                    }
                }
            }
            solver.pop();
        }
    }

    // / Reduced-size model generation example.

    public void findSmallModelExample(Context ctx)
    {
        System.out.println("FindSmallModelExample");
        Log.append("FindSmallModelExample");

        BitVecExpr x = ctx.mkBVConst("x", 32);
        BitVecExpr y = ctx.mkBVConst("y", 32);
        BitVecExpr z = ctx.mkBVConst("z", 32);

        Solver solver = ctx.mkSolver();

        solver.add(ctx.mkBVULE(x, ctx.mkBVAdd(y, z)));
        checkSmall(ctx, solver, x, y, z);
    }

    // / Simplifier example.

    public void simplifierExample(Context ctx)
    {
        System.out.println("SimplifierExample");
        Log.append("SimplifierExample");

        IntExpr x = ctx.mkIntConst("x");
        IntExpr y = ctx.mkIntConst("y");
        IntExpr z = ctx.mkIntConst("z");
        @SuppressWarnings("unused")
        IntExpr u = ctx.mkIntConst("u");

        Expr t1 = ctx.mkAdd(x, ctx.mkSub(y, ctx.mkAdd(x, z)));
        Expr t2 = t1.simplify();
        System.out.println((t1) + " -> " + (t2));
    }

    // / Extract unsatisfiable core example

    public void unsatCoreAndProofExample(Context ctx)
    {
        System.out.println("UnsatCoreAndProofExample");
        Log.append("UnsatCoreAndProofExample");

        Solver solver = ctx.mkSolver();

        BoolExpr pa = ctx.mkBoolConst("PredA");
        BoolExpr pb = ctx.mkBoolConst("PredB");
        BoolExpr pc = ctx.mkBoolConst("PredC");
        BoolExpr pd = ctx.mkBoolConst("PredD");
        BoolExpr p1 = ctx.mkBoolConst("P1");
        BoolExpr p2 = ctx.mkBoolConst("P2");
        BoolExpr p3 = ctx.mkBoolConst("P3");
        BoolExpr p4 = ctx.mkBoolConst("P4");
        BoolExpr[] assumptions = new BoolExpr[] { ctx.mkNot(p1), ctx.mkNot(p2),
                ctx.mkNot(p3), ctx.mkNot(p4) };
        BoolExpr f1 = ctx.mkAnd(pa, pb, pc);
        BoolExpr f2 = ctx.mkAnd(pa, ctx.mkNot(pb), pc);
        BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc));
        BoolExpr f4 = pd;

        solver.add(ctx.mkOr(f1, p1));
        solver.add(ctx.mkOr(f2, p2));
        solver.add(ctx.mkOr(f3, p3));
        solver.add(ctx.mkOr(f4, p4));
        Status result = solver.check(assumptions);

        if (result == Status.UNSATISFIABLE)
        {
            System.out.println("unsat");
            System.out.println("proof: " + solver.getProof());
            System.out.println("core: ");
            for (Expr c : solver.getUnsatCore())
            {
                System.out.println(c);
            }
        }
    }
    
    /// Extract unsatisfiable core example with AssertAndTrack
    
    public void unsatCoreAndProofExample2(Context ctx)
    {
        System.out.println("UnsatCoreAndProofExample2");
        Log.append("UnsatCoreAndProofExample2");

        Solver solver = ctx.mkSolver();

        BoolExpr pa = ctx.mkBoolConst("PredA");
        BoolExpr pb = ctx.mkBoolConst("PredB");
        BoolExpr pc = ctx.mkBoolConst("PredC");
        BoolExpr pd = ctx.mkBoolConst("PredD");
        
        BoolExpr f1 = ctx.mkAnd(new BoolExpr[] { pa, pb, pc });
        BoolExpr f2 = ctx.mkAnd(new BoolExpr[] { pa, ctx.mkNot(pb), pc });
        BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc));
        BoolExpr f4 = pd;

        BoolExpr p1 = ctx.mkBoolConst("P1");
        BoolExpr p2 = ctx.mkBoolConst("P2");
        BoolExpr p3 = ctx.mkBoolConst("P3");
        BoolExpr p4 = ctx.mkBoolConst("P4");

        solver.assertAndTrack(f1, p1);
        solver.assertAndTrack(f2, p2);
        solver.assertAndTrack(f3, p3);
        solver.assertAndTrack(f4, p4);
        Status result = solver.check();

        if (result == Status.UNSATISFIABLE)
        {
            System.out.println("unsat");
            System.out.println("core: ");
            for (Expr c : solver.getUnsatCore())
            {
                System.out.println(c);
            }
        }
    }

    public void finiteDomainExample(Context ctx)
    {
        System.out.println("FiniteDomainExample");
        Log.append("FiniteDomainExample");

        FiniteDomainSort s = ctx.mkFiniteDomainSort("S", 10);
        FiniteDomainSort t = ctx.mkFiniteDomainSort("T", 10);
        FiniteDomainNum s1 = (FiniteDomainNum)ctx.mkNumeral(1, s);
        FiniteDomainNum t1 = (FiniteDomainNum)ctx.mkNumeral(1, t);
        System.out.println(s);
        System.out.println(t);
        System.out.println(s1);        
        System.out.println(t1);
        System.out.println(s1.getInt());
        System.out.println(t1.getInt());
        // But you cannot mix numerals of different sorts
        // even if the size of their domains are the same:
        // System.out.println(ctx.mkEq(s1, t1));
    }    

    public void floatingPointExample1(Context ctx) throws TestFailedException
    {
        System.out.println("FloatingPointExample1");
        Log.append("FloatingPointExample1");
        
        FPSort s = ctx.mkFPSort(11, 53);
        System.out.println("Sort: " + s);

        FPNum x = (FPNum)ctx.mkNumeral("-1e1", s); /* -1 * 10^1 = -10 */
        FPNum y = (FPNum)ctx.mkNumeral("-10", s); /* -10 */
        FPNum z = (FPNum)ctx.mkNumeral("-1.25p3", s); /* -1.25 * 2^3 = -1.25 * 8 = -10 */
        System.out.println("x=" + x.toString()  + 
                           "; y=" + y.toString() + 
                           "; z=" + z.toString());
        
        BoolExpr a = ctx.mkAnd(ctx.mkFPEq(x, y), ctx.mkFPEq(y, z));
        check(ctx, ctx.mkNot(a), Status.UNSATISFIABLE);

        /* nothing is equal to NaN according to floating-point 
         * equality, so NaN == k should be unsatisfiable. */
        FPExpr k = (FPExpr)ctx.mkConst("x", s);
        FPExpr nan = ctx.mkFPNaN(s);

        /* solver that runs the default tactic for QF_FP. */
        Solver slvr = ctx.mkSolver("QF_FP");
        slvr.add(ctx.mkFPEq(nan, k));
        if (slvr.check() != Status.UNSATISFIABLE)
            throw new TestFailedException();
        System.out.println("OK, unsat:" + System.getProperty("line.separator") + slvr);

        /* NaN is equal to NaN according to normal equality. */
        slvr = ctx.mkSolver("QF_FP");
        slvr.add(ctx.mkEq(nan, nan));
        if (slvr.check() != Status.SATISFIABLE)
            throw new TestFailedException();
        System.out.println("OK, sat:" + System.getProperty("line.separator")  + slvr);

        /* Let's prove -1e1 * -1.25e3 == +100 */
        x = (FPNum)ctx.mkNumeral("-1e1", s);
        y = (FPNum)ctx.mkNumeral("-1.25p3", s);
        FPExpr x_plus_y = (FPExpr)ctx.mkConst("x_plus_y", s);
        FPNum r = (FPNum)ctx.mkNumeral("100", s);
        slvr = ctx.mkSolver("QF_FP");

        slvr.add(ctx.mkEq(x_plus_y, ctx.mkFPMul(ctx.mkFPRoundNearestTiesToAway(), x, y)));
        slvr.add(ctx.mkNot(ctx.mkFPEq(x_plus_y, r)));
        if (slvr.check() != Status.UNSATISFIABLE)
            throw new TestFailedException();
        System.out.println("OK, unsat:" + System.getProperty("line.separator")  + slvr);
    }

    public void floatingPointExample2(Context ctx) throws TestFailedException
    {
        System.out.println("FloatingPointExample2");
        Log.append("FloatingPointExample2");
        FPSort double_sort = ctx.mkFPSort(11, 53);
        FPRMSort rm_sort = ctx.mkFPRoundingModeSort();
        
        FPRMExpr rm = (FPRMExpr)ctx.mkConst(ctx.mkSymbol("rm"), rm_sort);
        BitVecExpr x = (BitVecExpr)ctx.mkConst(ctx.mkSymbol("x"), ctx.mkBitVecSort(64));
        FPExpr y = (FPExpr)ctx.mkConst(ctx.mkSymbol("y"), double_sort);            
        FPExpr fp_val = ctx.mkFP(42, double_sort);
        
        BoolExpr c1 = ctx.mkEq(y, fp_val);
        BoolExpr c2 = ctx.mkEq(x, ctx.mkFPToBV(rm, y, 64, false));
        BoolExpr c3 = ctx.mkEq(x, ctx.mkBV(42, 64));
        BoolExpr c4 = ctx.mkEq(ctx.mkNumeral(42, ctx.getRealSort()), ctx.mkFPToReal(fp_val));
        BoolExpr c5 = ctx.mkAnd(c1, c2, c3, c4);
        System.out.println("c5 = " + c5);

        /* Generic solver */
        Solver s = ctx.mkSolver();
        s.add(c5);

        if (s.check() != Status.SATISFIABLE)
            throw new TestFailedException();

        System.out.println("OK, model: " + s.getModel().toString());        
    }

    public void optimizeExample(Context ctx) 
    {
        System.out.println("Opt");

        Optimize opt = ctx.mkOptimize();

        // Set constraints.
        IntExpr xExp = ctx.mkIntConst("x");
        IntExpr yExp = ctx.mkIntConst("y");

        opt.Add(ctx.mkEq(ctx.mkAdd(xExp, yExp), ctx.mkInt(10)),
                ctx.mkGe(xExp, ctx.mkInt(0)),
                ctx.mkGe(yExp, ctx.mkInt(0)));

        // Set objectives.
        Optimize.Handle mx = opt.MkMaximize(xExp);
        Optimize.Handle my = opt.MkMaximize(yExp);

        System.out.println(opt.Check()); 
        System.out.println(mx);
        System.out.println(my);
    }

    public void translationExample() {
        Context ctx1 = new Context();
        Context ctx2 = new Context();

        Sort s1 = ctx1.getIntSort();
        Sort s2 = ctx2.getIntSort();
        Sort s3 = s1.translate(ctx2);

        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s2.equals(s3));
        System.out.println(s1.equals(s3));

        Expr e1 = ctx1.mkIntConst("e1");
        Expr e2 = ctx2.mkIntConst("e1");
        Expr e3 = e1.translate(ctx2);

        System.out.println(e1 == e2);
        System.out.println(e1.equals(e2));
        System.out.println(e2.equals(e3));
        System.out.println(e1.equals(e3));
    }

    public static void main(String[] args)
    {
        JavaExample p = new JavaExample();
        try
        {
            com.microsoft.z3.Global.ToggleWarningMessages(true);
            Log.open("test.log");

            System.out.print("Z3 Major Version: ");
            System.out.println(Version.getMajor());
            System.out.print("Z3 Full Version: ");
            System.out.println(Version.getString());
            System.out.print("Z3 Full Version String: ");
            System.out.println(Version.getFullVersion());

            p.simpleExample();

            { // These examples need model generation turned on.
                HashMap<String, String> cfg = new HashMap<String, String>();
                cfg.put("model", "true");
                Context ctx = new Context(cfg);
        
                p.optimizeExample(ctx);
                p.basicTests(ctx);
                p.castingTest(ctx);
                p.sudokuExample(ctx);
                p.quantifierExample1(ctx);
                p.quantifierExample2(ctx);
                p.logicExample(ctx);
                p.parOrExample(ctx);
                p.findModelExample1(ctx);
                p.findModelExample2(ctx);
                p.pushPopExample1(ctx);
                p.arrayExample1(ctx);
                p.arrayExample3(ctx);
                p.bitvectorExample1(ctx);
                p.bitvectorExample2(ctx);
                p.parserExample1(ctx);
                p.parserExample2(ctx);
                p.parserExample5(ctx);
                p.iteExample(ctx);
                p.evalExample1(ctx);
                p.evalExample2(ctx);
                p.findSmallModelExample(ctx);
                p.simplifierExample(ctx);
                p.finiteDomainExample(ctx);
                p.floatingPointExample1(ctx);
                // core dumps: p.floatingPointExample2(ctx);
            }

            { // These examples need proof generation turned on.
                HashMap<String, String> cfg = new HashMap<String, String>();
                cfg.put("proof", "true");
                Context ctx = new Context(cfg);
                p.proveExample1(ctx);
                p.proveExample2(ctx);
                p.arrayExample2(ctx);
                p.tupleExample(ctx);
                // throws p.parserExample3(ctx);
                p.enumExample(ctx);
                p.listExample(ctx);
                p.treeExample(ctx);
                p.forestExample(ctx);
                p.unsatCoreAndProofExample(ctx);
                p.unsatCoreAndProofExample2(ctx);
            }

            { // These examples need proof generation turned on and auto-config
              // set to false.
                HashMap<String, String> cfg = new HashMap<String, String>();
                cfg.put("proof", "true");
                cfg.put("auto-config", "false");
                Context ctx = new Context(cfg);
                p.quantifierExample3(ctx);
                p.quantifierExample4(ctx);
            }

            p.translationExample();

            Log.close();
            if (Log.isOpen())
                System.out.println("Log is still open!");
        } catch (Z3Exception ex)
        {
            System.out.println("Z3 Managed Exception: " + ex.getMessage());
            System.out.println("Stack trace: ");
            ex.printStackTrace(System.out);
        } catch (TestFailedException ex)
        {
            System.out.println("TEST CASE FAILED: " + ex.getMessage());
            System.out.println("Stack trace: ");
            ex.printStackTrace(System.out);
        } catch (Exception ex)
        {
            System.out.println("Unknown Exception: " + ex.getMessage());
            System.out.println("Stack trace: ");
            ex.printStackTrace(System.out);
        }
    }
}
