/* * Copyright (C) 2012 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMTInterpol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.proof; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm; import de.uni_freiburg.informatik.ultimate.logic.Annotation; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.ConstantTerm; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.Rational; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.Theory; import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.Clausifier; import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.Clausifier.ConditionChain; import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.SMTAffineTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.Utils; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Literal; public class ProofTracker implements IProofTracker { private static class Rewrite { Rewrite mNext; public Term toTerm() { throw new InternalError("toTerm called on sentinel"); } } private static class ExpandRewrite extends Rewrite { ApplicationTerm mOld; public ExpandRewrite(Term old) { mOld = (ApplicationTerm) old; } @Override public Term toTerm() { // We've never produced the new value, hence we have to produce it // here final FunctionSymbol fsym = mOld.getFunction(); final Term[] params = mOld.getParameters(); final Theory t = mOld.getTheory(); Term res; if (fsym.isLeftAssoc()) { res = t.term(fsym, params[0], params[1]); for (int i = 2; i < params.length; ++i) { res = t.term(fsym, res, params[i]); } } else if (fsym.isRightAssoc()) { res = t.term(fsym, params[params.length - 2], params[params.length - 1]); for (int i = params.length - 3; i >= 0; --i) { res = t.term(fsym, params[i], res); } } else if (fsym.isChainable()) { res = t.term(fsym, params[0], params[1]); for (int i = 1; i < params.length - 1; ++i) { res = t.term(t.mAnd, res, t.term(fsym, params[i], params[i + 1])); } } else { throw new InternalError("Cannot expand " + fsym); } res = t.annotatedTerm(new Annotation[] { ProofConstants.REWRITEANNOTS[ProofConstants.RW_EXPAND] }, t.term("=", mOld, res)); return t.term("@rewrite", res); } } private static class ResultRewrite extends Rewrite { Term mOld; Term mNew; int mNum; public ResultRewrite(Term oldTerm, Term newTerm, int rewriteNum) { mOld = SMTAffineTerm.cleanup(oldTerm); mNew = SMTAffineTerm.cleanup(newTerm); mNum = rewriteNum; assert(oldTerm != newTerm) : "ResultRewrite should track changes"; } @Override public Term toTerm() { final Theory t = mOld.getTheory(); Term base = t.term("=", mOld, mNew); base = t.annotatedTerm(new Annotation[] { ProofConstants.REWRITEANNOTS[mNum] }, base); return t.term("@rewrite", base); } } private static class RemoveConnective extends Rewrite { private final int mRule; private final Term[] mArgs; private final Term mRes; public RemoveConnective(int rule, Term[] args, Term res) { mRule = rule; /* If rule is RW_AND_TO_OR, we are called from createAndInplace. We * have to clone the args since they get changed! */ mArgs = rule == ProofConstants.RW_AND_TO_OR ? args.clone() : args; mRes = res; } @Override public Term toTerm() { final Theory t = mArgs[0].getTheory(); Term orig, res; Term[] resArgs = mArgs; switch(mRule) { case ProofConstants.RW_AND_TO_OR: resArgs = new Term[mArgs.length]; orig = t.term(t.mAnd, mArgs); for (int i = 0; i < resArgs.length; ++i) { resArgs[i] = t.term(t.mNot, mArgs[i]); } res = t.term(t.mNot, t.term(t.mOr, resArgs)); break; case ProofConstants.RW_XOR_TO_DISTINCT: orig = t.term(t.mXor, mArgs); res = t.term("distinct", resArgs); break; case ProofConstants.RW_IMP_TO_OR: resArgs = new Term[resArgs.length]; orig = t.term(t.mImplies, mArgs); for (int i = 1; i < resArgs.length; ++i) { resArgs[i] = t.term(t.mNot, mArgs[i - 1]); } resArgs[0] = mArgs[mArgs.length - 1]; res = t.term(t.mOr, resArgs); break; case ProofConstants.RW_LEQ_TO_LEQ0: orig = t.term("<=", mArgs); resArgs = new Term[2]; resArgs[0] = SMTAffineTerm.cleanup(mRes); resArgs[1] = resArgs[0].getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO); res = t.term("<=", resArgs); break; case ProofConstants.RW_GEQ_TO_LEQ0: orig = t.term(">=", mArgs); resArgs = new Term[2]; resArgs[0] = SMTAffineTerm.cleanup(mRes); resArgs[1] = resArgs[0].getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO); res = t.term("<=", resArgs); break; case ProofConstants.RW_GT_TO_LEQ0: orig = t.term(">", mArgs); resArgs = new Term[2]; resArgs[0] = SMTAffineTerm.cleanup(mRes); resArgs[1] = resArgs[0].getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO); res = t.term(t.mNot, t.term("<=", resArgs)); break; case ProofConstants.RW_LT_TO_LEQ0: orig = t.term("<", mArgs); resArgs = new Term[2]; resArgs[0] = SMTAffineTerm.cleanup(mRes); resArgs[1] = resArgs[0].getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO); res = t.term(t.mNot, t.term("<=", resArgs)); break; default: throw new InternalError( "BUG in ProofTracker: RemoveConnective"); } res = t.annotatedTerm(new Annotation[] { ProofConstants.REWRITEANNOTS[mRule] }, t.term("=", orig, res)); return t.term("@rewrite", res); } } private static class InternRewrite extends Rewrite { private final Term mTerm, mLitTerm; public InternRewrite(Term term, Term litTerm) { assert (term != litTerm) : "Intern should track changes!!!"; mTerm = term; mLitTerm = litTerm; } @Override public Term toTerm() { final Theory t = mTerm.getTheory(); final Term res = t.term("=", mTerm, mLitTerm); return t.term("@intern", res); } } private final Rewrite mFirst; private Rewrite mLast, mMarkPos, mSave; private int mNumRewrites = 0, mSaveNumRewrites; private Map<Term, Term> mLits; private boolean addToLits(Term orig, Term lit) { if (mLits == null) { mLits = new HashMap<Term, Term>(); } return mLits.put(orig, lit) == null; } private void prepend(Rewrite rw) { assert invNumRewrites(); rw.mNext = mFirst.mNext; mFirst.mNext = rw; if (rw.mNext == null) { mLast = rw; } ++mNumRewrites; assert invNumRewrites(); } private void append(Rewrite rw) { assert invNumRewrites(); mLast.mNext = rw; mLast = rw; ++mNumRewrites; assert invNumRewrites(); } private void insertAtMarkedPos(Rewrite rw) { assert invNumRewrites(); rw.mNext = mMarkPos.mNext; mMarkPos.mNext = rw; ++mNumRewrites; if (mMarkPos == mLast) { mLast = rw; } assert invNumRewrites(); } private boolean invNumRewrites() { int i = 0; for (Rewrite rw = mFirst.mNext; rw != null; rw = rw.mNext) { ++i; } assert i == mNumRewrites; return i == mNumRewrites; } public ProofTracker() { mFirst = mLast = mMarkPos = new Rewrite(); } @Override public void reset() { mFirst.mNext = null; mLast = mMarkPos = mFirst; mNumRewrites = 0; assert invNumRewrites(); } @Override public void expand(ApplicationTerm orig) { prepend(new ExpandRewrite(orig)); } @Override public void expandDef(Term orig, Term res) { prepend(new ResultRewrite(orig, res, ProofConstants.RW_EXPAND_DEF)); } @Override public void equality(Term[] args, Object res, int rule) { Term tres = null; if (res instanceof Term) { tres = (Term) res; } else { final Theory t = args[0].getTheory(); assert res instanceof Term[]; final Term[] resArgs = (Term[]) res; if (resArgs.length == 0) { tres = t.mTrue; } else if (resArgs.length == 1) { tres = rule == ProofConstants.RW_EQ_FALSE ? t.term(t.mNot, resArgs[0]) : resArgs[0]; } else if (rule == ProofConstants.RW_EQ_TRUE) { // We use inplace algorithms. So clone the array. tres = t.term(t.mAnd, resArgs.clone()); } else if (rule == ProofConstants.RW_EQ_FALSE) { tres = t.term(t.mNot, t.term(t.mOr, resArgs)); } else if (rule == ProofConstants.RW_EQ_SIMP) { tres = t.term("=", resArgs); } else { throw new InternalError("Strange result in equality rewrite"); } } append(new ResultRewrite(tres.getTheory().term("=", args), tres, rule)); } @Override public void distinct(Term[] args, Term res, int rule) { if (res == null) { final Theory t = args[0].getTheory(); if (rule == ProofConstants.RW_DISTINCT_TRUE) { if (args[0] == t.mTrue) { res = t.term(t.mNot, args[1]); } else { res = t.term(t.mNot, args[0]); } } else { throw new InternalError("Result should be given"); } } append(new ResultRewrite( res.getTheory().term("distinct", args), res, rule)); } @Override public void negation(Term pos, Term res, int rule) { final Theory t = res.getTheory(); append(new ResultRewrite(t.term(t.mNot, pos), res, rule)); } @Override public void or(Term[] args, Term res, int rule) { append(new ResultRewrite(res.getTheory().term("or", args), res, rule)); } @Override public void ite(Term cond, Term thenTerm, Term elseTerm, Term res, int rule) { final Theory t = cond.getTheory(); if (res == null) { switch(rule) { case ProofConstants.RW_ITE_BOOL_2: res = t.term(t.mNot, cond); break; case ProofConstants.RW_ITE_BOOL_4: res = t.term(t.mNot, t.term(t.mOr, cond, t.term(t.mNot, elseTerm))); break; case ProofConstants.RW_ITE_BOOL_5: res = t.term(t.mOr, t.term(t.mNot, cond), thenTerm); break; case ProofConstants.RW_ITE_BOOL_6: res = t.term(t.mNot, t.term(t.mOr, t.term(t.mNot, cond), t.term(t.mNot, thenTerm))); break; default: throw new InternalError("BUG in ProofTracker: ITE"); } } append(new ResultRewrite(t.term("ite", cond, thenTerm, elseTerm), res, rule)); } @Override public void strip(AnnotatedTerm orig) { prepend(new ResultRewrite( orig, orig.getSubterm(), ProofConstants.RW_STRIP)); } @Override public void sum(FunctionSymbol fsym, Term[] args, Term res) { final Theory t = fsym.getTheory(); final Term left = SMTAffineTerm.cleanup(t.term(fsym, args)); final Term right = SMTAffineTerm.cleanup(res); if (left != right) { append(new ResultRewrite( left, right, ProofConstants.RW_CANONICAL_SUM)); } } @Override public void leqSimp(SMTAffineTerm leq, Term res, int rule) { final Theory t = res.getTheory(); final Term left = t.term("<=", SMTAffineTerm.cleanup(leq), leq.getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO)); append(new ResultRewrite(left, res, rule)); } @Override public void desugar(ApplicationTerm orig, Term[] origArgs, Term[] newArgs) { final Theory t = orig.getTheory(); append(new ResultRewrite(t.term(orig.getFunction(), origArgs), t.term(orig.getFunction(), newArgs), ProofConstants.RW_DESUGAR)); } @Override public void divisible(FunctionSymbol divn, Term div, Term res) { final Term divisible = res.getTheory().term(divn, SMTAffineTerm.cleanup(div)); append(new ResultRewrite(divisible, res, ProofConstants.RW_DIVISIBLE)); } @Override public Term getRewriteProof(Term asserted) { assert invNumRewrites(); final Theory t = asserted.getTheory(); return getEqProof(t.term("@asserted", asserted)); } private Term getEqProof(Term proofPart) { if (mNumRewrites == 0) { return proofPart; } final Term[] args = new Term[mNumRewrites + 1]; args[0] = proofPart; int i = 1; for (Rewrite rw = mFirst.mNext; rw != null; rw = rw.mNext) { args[i++] = rw.toTerm(); } final Term eq = proofPart.getTheory().term("@eq", args); return eq; } @Override public void distinctBoolEq(Term lhs, Term rhs, boolean firstNegated) { final Theory t = lhs.getTheory(); final Term distinct = t.term("distinct", lhs, rhs); final Term res = firstNegated ? t.term("=", t.term(t.mNot, lhs), rhs) : t.term("=", lhs, t.term(t.mNot, rhs)); append(new ResultRewrite( distinct, res, ProofConstants.RW_DISTINCT_BOOL_EQ)); } @Override public void removeConnective(Term[] origArgs, Term result, int rule) { if (rule == ProofConstants.RW_LEQ_TO_LEQ0) { assert(origArgs.length == 2); final SMTAffineTerm constant = SMTAffineTerm.create(origArgs[1]); if (constant.isConstant() && constant.getConstant().equals(Rational.ZERO)) { final Term tmp = SMTAffineTerm.cleanup(result); final Term tmp2 = SMTAffineTerm.cleanup(origArgs[0]); if (tmp == tmp2) { return; } } } append(new RemoveConnective(rule, origArgs, result)); } @Override public void quoted(Term orig, Literal quote) { final Term t = quote.getSMTFormula(orig.getTheory(), true); append(new InternRewrite(orig, t)); } @Override public void eq(Term lhs, Term rhs, Term res) { final Term orig = res.getTheory().term("=", lhs, rhs); if (orig != res) { append(new InternRewrite(orig, res)); } } @Override public void eq(Term lhs, Term rhs, DPLLAtom eqAtom) { final Theory t = lhs.getTheory(); final Term res = SMTAffineTerm.cleanup(eqAtom.getSMTFormula(t, true)); final Term orig = SMTAffineTerm.cleanup(t.term("=", lhs, rhs)); if (orig != res) { append(new InternRewrite(orig, res)); } } @Override public void leq0(SMTAffineTerm sum, Literal lit) { final Theory t = sum.getTheory(); final Term orig = t.term("<=", SMTAffineTerm.cleanup(sum), sum.getSort().getName().equals("Int") ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO)); final Term res = lit.getSMTFormula(t, true); if (orig != res) { append(new InternRewrite(orig, res)); } } @Override public void intern(Term term, Literal lit) { final Theory t = term.getTheory(); final Term orig = SMTAffineTerm.cleanup(term); final Term res = lit.getSMTFormula(t, true); if (orig != res) { append(new InternRewrite(orig, res)); } } @Override public Term split(Term data, Term proof, int splitKind) { final Theory t = proof.getTheory(); Term res; switch (splitKind) { case ProofConstants.SPLIT_NEG_OR: // data has to be negated here... // if data is already a negation, we have to rewrite the potential // double negation. res = t.term(t.mNot, data); Term base = t.term("@split", t.annotatedTerm( new Annotation[] {ProofConstants.SPLITANNOTS[splitKind]}, proof), res); Term posRes = res; if (Utils.isNegation(data)) { posRes = ((ApplicationTerm) data).getParameters()[0]; final Term rewrite = t.term("@rewrite", t.annotatedTerm( new Annotation[] { ProofConstants.REWRITEANNOTS[ProofConstants.RW_NOT_SIMP] }, t.term("=", res, posRes))); base = t.term("@eq", base, rewrite); } return base; case ProofConstants.SPLIT_POS_EQ_1: Term[] params = ((ApplicationTerm) data).getParameters(); assert params.length == 2; res = t.term(t.mOr, params[0], t.term(t.mNot, params[1])); break; case ProofConstants.SPLIT_POS_EQ_2: params = ((ApplicationTerm) data).getParameters(); assert params.length == 2; res = t.term(t.mOr, t.term(t.mNot, params[0]), params[1]); break; case ProofConstants.SPLIT_NEG_EQ_1: params = ((ApplicationTerm) data).getParameters(); assert params.length == 2; res = t.term(t.mOr, params); break; case ProofConstants.SPLIT_NEG_EQ_2: params = ((ApplicationTerm) data).getParameters(); assert params.length == 2; res = t.term(t.mOr, t.term(t.mNot, params[0]), t.term(t.mNot, params[1])); break; case ProofConstants.SPLIT_POS_ITE_1: params = ((ApplicationTerm) data).getParameters(); assert params.length == 3; // NOCHECKSTYLE since ite has 3 params res = t.term(t.mOr, t.term(t.mNot, params[0]), params[1]); break; case ProofConstants.SPLIT_POS_ITE_2: params = ((ApplicationTerm) data).getParameters(); assert params.length == 3; // NOCHECKSTYLE since ite has 3 params res = t.term(t.mOr, params[0], params[2]); break; case ProofConstants.SPLIT_NEG_ITE_1: params = ((ApplicationTerm) data).getParameters(); assert params.length == 3; // NOCHECKSTYLE since ite has 3 params res = t.term(t.mOr, t.term(t.mNot, params[0]), t.term(t.mNot, params[1])); break; case ProofConstants.SPLIT_NEG_ITE_2: params = ((ApplicationTerm) data).getParameters(); assert params.length == 3; // NOCHECKSTYLE since ite has 3 params res = t.term(t.mOr, params[0], t.term(t.mNot, params[2])); break; default: throw new InternalError("BUG in ProofTracker: Split"); } return getEqProof(t.term("@split", t.annotatedTerm( new Annotation[] {ProofConstants.SPLITANNOTS[splitKind]}, proof), res)); } @Override public Term clause(Term proof) { assert invNumRewrites(); return getEqProof(proof); } @Override public Term auxAxiom( int auxKind, Literal auxLit, Term data, Term base, Object auxData) { final Theory t = data.getTheory(); Term axiom; switch (auxKind) { case ProofConstants.AUX_TRUE_NOT_FALSE: // auxLit is (not (= true false)), i.e., it has negative polarity axiom = auxLit.getSMTFormula(t, true); break; case ProofConstants.AUX_OR_POS: { final Term[] args = ((ApplicationTerm) data).getParameters(); final Term[] nargs = new Term[args.length + 1]; nargs[0] = t.term(t.mNot, auxLit.getSMTFormula(t, true)); System.arraycopy(args, 0, nargs, 1, args.length); axiom = t.term(t.mOr, nargs); break; } case ProofConstants.AUX_OR_NEG: axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), t.term(t.mNot, data)); break; case ProofConstants.AUX_ITE_POS_1: Term[] params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, t.term(t.mNot, auxLit.getSMTFormula(t, true)), t.term(t.mNot, params[0]), params[1]); break; case ProofConstants.AUX_ITE_POS_2: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, t.term(t.mNot, auxLit.getSMTFormula(t, true)), params[0], params[2]); break; case ProofConstants.AUX_ITE_POS_RED: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, t.term(t.mNot, auxLit.getSMTFormula(t, true)), params[1], params[2]); break; case ProofConstants.AUX_ITE_NEG_1: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), t.term(t.mNot, params[0]), t.term(t.mNot, params[1])); break; case ProofConstants.AUX_ITE_NEG_2: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), params[0], t.term(t.mNot, params[2])); break; case ProofConstants.AUX_ITE_NEG_RED: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), t.term(t.mNot, params[1]), t.term(t.mNot, params[2])); break; case ProofConstants.AUX_EQ_POS_1: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, t.term(t.mNot, auxLit.getSMTFormula(t, true)), params[0], t.term(t.mNot, params[1])); break; case ProofConstants.AUX_EQ_POS_2: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, t.term(t.mNot, auxLit.getSMTFormula(t, true)), t.term(t.mNot, params[0]), params[1]); break; case ProofConstants.AUX_EQ_NEG_1: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), params[0], params[1]); break; case ProofConstants.AUX_EQ_NEG_2: params = ((ApplicationTerm) data).getParameters(); axiom = t.term(t.mOr, auxLit.getSMTFormula(t, true), t.term(t.mNot, params[0]), t.term(t.mNot, params[1])); break; case ProofConstants.AUX_EXCLUDED_MIDDLE_1: axiom = t.term(t.mOr, t.term(t.mNot, data), auxLit.getSMTFormula(t, true)); break; case ProofConstants.AUX_EXCLUDED_MIDDLE_2: axiom = t.term(t.mOr, data, auxLit.getSMTFormula(t, true)); break; case ProofConstants.AUX_TERM_ITE: final ConditionChain tmp = (ConditionChain) auxData; // Determine size ConditionChain walk = tmp; int size = 1; while (walk != null) { walk = walk.getPrevious(); ++size; } final Term[] nparams = new Term[size]; walk = tmp; for (int i = size - 2; i >= 0; --i) { nparams[i] = t.term(t.mNot, walk.getTerm()); if (Utils.isNegation(walk.getTerm())) { negation(walk.getTerm(), Utils.createNotUntracked(walk.getTerm()), ProofConstants.RW_NOT_SIMP); } walk = walk.getPrevious(); } nparams[size - 1] = t.term("=", base, data); axiom = t.term(t.mOr, nparams); break; case ProofConstants.AUX_DIV_LOW: axiom = t.term("<=", SMTAffineTerm.cleanup(data), t.numeral(BigInteger.ZERO)); break; case ProofConstants.AUX_TO_INT_LOW: axiom = t.term("<=", SMTAffineTerm.cleanup(data), t.rational(BigInteger.ZERO, BigInteger.ONE)); break; case ProofConstants.AUX_DIV_HIGH: axiom = t.term(t.mNot, t.term("<=", SMTAffineTerm.cleanup(data), t.numeral(BigInteger.ZERO))); break; case ProofConstants.AUX_TO_INT_HIGH: axiom = t.term(t.mNot, t.term("<=", SMTAffineTerm.cleanup(data), t.rational(BigInteger.ZERO, BigInteger.ONE))); break; case ProofConstants.AUX_ARRAY_STORE: params = ((ApplicationTerm) data).getParameters(); axiom = t.term("=", t.term("select", data, params[1]), params[2]); break; case ProofConstants.AUX_ARRAY_DIFF: // Create a = b \/ select(a, diff(a,b)) != select(b, diff(a,b)) params = ((ApplicationTerm) data).getParameters(); axiom = t.term("or", t.term("=", params), t.term("not", t.term("=", t.term("select", params[0], data), t.term("select", params[1], data)))); break; default: throw new InternalError("BUG in ProofTracker: AUX"); } return t.term("@tautology", t.annotatedTerm( new Annotation[] {ProofConstants.AUXANNOTS[auxKind]}, axiom)); } @Override public IProofTracker getDescendent() { return new ProofTracker(); } @Override public void modulo(ApplicationTerm appTerm, Term res) { append(new ResultRewrite(appTerm, res, ProofConstants.RW_MODULO)); } @Override public void mod(Term x, Term y, Term res, int rule) { final Theory t = x.getTheory(); final Term mod = t.term( "mod", SMTAffineTerm.cleanup(x), SMTAffineTerm.cleanup(y)); append(new ResultRewrite(mod, SMTAffineTerm.cleanup(res), rule)); } @Override public void div(Term x, Term y, Term res, int rule) { final Theory t = x.getTheory(); final Term mod = t.term( "div", SMTAffineTerm.cleanup(x), SMTAffineTerm.cleanup(y)); append(new ResultRewrite(mod, SMTAffineTerm.cleanup(res), rule)); } @Override public void toInt(Term arg, Term res) { final Theory t = arg.getTheory(); final Term toint = t.term("to_int", SMTAffineTerm.cleanup(arg)); append(new ResultRewrite(toint, SMTAffineTerm.cleanup(res), ProofConstants.RW_TO_INT)); } @Override public Term[] prepareIRAHack(Term[] args) { return args.clone(); } @Override public void negateLit(Literal lit, Theory theory) { assert lit.getSign() == -1 : "Literal not negated!"; final Term lhs = theory.term(theory.mNot, SMTAffineTerm.cleanup(lit.getSMTFormula(theory, true))); final Term rhs = SMTAffineTerm.cleanup( lit.getAtom().getSMTFormula(theory, true)); append(new ResultRewrite(lhs, rhs, ProofConstants.RW_NOT_SIMP)); } @Override public void arrayRewrite(Term[] args, Term result, int rule) { final Theory t = result.getTheory(); final Term input = rule == ProofConstants.RW_STORE_OVER_STORE ? t.term("store", args) : t.term("select", args); append(new ResultRewrite(input, result, rule)); } private final static class FlattenHelper { private final Term[] mArgs; private int mOffset; public FlattenHelper(Term[] args, int offset) { mArgs = args; mOffset = offset; } public void flatten( ArrayDeque<FlattenHelper> todo, ArrayList<Term> args) { for (int i = mOffset; i < mArgs.length; ++i) { if (mArgs[i] instanceof ApplicationTerm) { final ApplicationTerm tst = (ApplicationTerm) mArgs[i]; if (Clausifier.shouldFlatten(tst)) { mOffset = i + 1; if (mOffset < mArgs.length) { todo.addFirst(this); } todo.addFirst(new FlattenHelper(tst.getParameters(), 0)); return; } } args.add(mArgs[i]); } } } @Override public void flatten(Term[] args, boolean simpOr) { final Theory t = args[0].getTheory(); final ArrayDeque<FlattenHelper> toFlatten = new ArrayDeque<FlattenHelper>(); toFlatten.add(new FlattenHelper(args, 0)); final ArrayList<Term> newArgs = new ArrayList<Term>(); while (!toFlatten.isEmpty()) { final FlattenHelper fh = toFlatten.poll(); fh.flatten(toFlatten, newArgs); } final ApplicationTerm res = t.term(t.mOr, newArgs.toArray(new Term[newArgs.size()])); if (simpOr) { orSimpClause(res.getParameters()); } insertAtMarkedPos( new ResultRewrite(t.term(t.mOr, args), res, ProofConstants.RW_FLATTEN)); } @Override public void orSimpClause(Term[] args) { final Theory t = args[0].getTheory(); final Term[] newArgs = args.clone(); final LinkedHashSet<Term> clause = new LinkedHashSet<Term>(); for (int i = 0; i < newArgs.length; ++i) { final Term tmp = SMTAffineTerm.cleanup(newArgs[i]); Term newDisj = mLits.get(tmp); /* This is the case for proxy literals in aux-clauses. They cannot * merge since otherwise the term would be infinite because the term * would be its own proper subterm. */ if (newDisj == null) { newDisj = tmp; } newArgs[i] = newDisj; if (newDisj != t.mFalse) { clause.add(newDisj); } } Term res; if (clause.isEmpty()) { res = t.mFalse; } else if (clause.size() == 1) { res = clause.iterator().next(); } else { res = t.term(t.mOr, clause.toArray(new Term[clause.size()])); } final Rewrite rw = new ResultRewrite(t.term(t.mOr, newArgs), res, ProofConstants.RW_OR_SIMP); append(rw); } @Override public void markPosition() { mMarkPos = mLast; } @Override public Term[] produceAuxAxiom(Literal auxlit, Term... args) { final Theory t = args[0].getTheory(); final Term[] res = new Term[1 + args.length]; res[0] = auxlit.getSMTFormula(t, true); System.arraycopy(args, 0, res, 1, args.length); return res; } @Override public void save() { mSave = mLast; mSaveNumRewrites = mNumRewrites; } @Override public void restore() { if (mSave != null) { mLast = mSave; mLast.mNext = null; mNumRewrites = mSaveNumRewrites; mSave = null; } assert invNumRewrites(); } @Override public void cleanSave() { mSave = null; } @Override public void normalized(ConstantTerm term, SMTAffineTerm res) { final Term rhs = SMTAffineTerm.cleanup(res); if (term != rhs) { append(new ResultRewrite( term, rhs, ProofConstants.RW_CANONICAL_SUM)); } } @Override public boolean notifyLiteral(Literal lit, Term t) { return addToLits(SMTAffineTerm.cleanup(t), SMTAffineTerm.cleanup(lit.getSMTFormula(t.getTheory(), true))); } @Override public void notifyFalseLiteral(Term t) { addToLits(SMTAffineTerm.cleanup(t), t.getTheory().mFalse); } @Override public void storeRewrite(ApplicationTerm store, Term result, boolean arrayFirst) { final Term array = store.getParameters()[0]; final Term orig = arrayFirst ? store.getTheory().term("=", array, store) : store.getTheory().term("=", store, array); append(new ResultRewrite( orig, result, ProofConstants.RW_STORE_REWRITE)); } @Override public void toReal(Term arg, Term res) { final Term orig = arg.getTheory().term( "to_real", SMTAffineTerm.cleanup(arg)); append(new ResultRewrite(orig, SMTAffineTerm.cleanup(res), ProofConstants.RW_TO_REAL)); } }