/* * Copyright (C) 2009-2016 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.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Stack; 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.FormulaUnLet; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.NonRecursive; import de.uni_freiburg.informatik.ultimate.logic.Rational; import de.uni_freiburg.informatik.ultimate.logic.Script; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermTransformer; import de.uni_freiburg.informatik.ultimate.smtinterpol.LogProxy; import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.SMTAffineTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.util.SymmetricPair; /** * This proof checker checks compliance of SMTInterpol proofs with * its documented format. * * @author Pascal Raiola, Jochen Hoenicke, Tanja Schindler */ public class ProofChecker extends NonRecursive { public static class ProofWalker implements Walker { final ApplicationTerm mTerm; public ProofWalker(Term term) { assert term.getSort().getName().equals("@Proof"); mTerm = (ApplicationTerm) term; } @Override public void walk(NonRecursive engine) { ((ProofChecker) engine).walk(mTerm); } } public static class ResolutionWalker implements Walker { final ApplicationTerm mTerm; public ResolutionWalker(ApplicationTerm term) { assert term.getFunction().getName().equals("@res"); mTerm = term; } @Override public void walk(NonRecursive engine) { ((ProofChecker) engine).walkResolution(mTerm); } } public static class EqualityWalker implements Walker { final ApplicationTerm mTerm; public EqualityWalker(ApplicationTerm term) { assert term.getFunction().getName().equals("@eq"); mTerm = term; } @Override public void walk(NonRecursive engine) { ((ProofChecker) engine).walkEquality(mTerm); } } public static class ClauseWalker implements Walker { final ApplicationTerm mTerm; public ClauseWalker(ApplicationTerm term) { assert term.getFunction().getName().equals("@clause"); mTerm = term; } @Override public void walk(NonRecursive engine) { ((ProofChecker) engine).walkClause(mTerm); } } public static class SplitWalker implements Walker { final ApplicationTerm mTerm; public SplitWalker(ApplicationTerm term) { assert term.getFunction().getName().equals("@split"); mTerm = term; } @Override public void walk(NonRecursive engine) { ((ProofChecker) engine).walkSplit(mTerm); } } /** * Class converting Terms to SMTAffineTerm. * @author hoenicke */ class SMTAffineTermTransformer extends TermTransformer { private final HashSet<String> mFuncSet = new HashSet<String>(); { mFuncSet.add("+"); mFuncSet.add("-"); mFuncSet.add("*"); mFuncSet.add("/"); mFuncSet.add("to_real"); } @Override public void convert(Term t) { if (t instanceof ApplicationTerm) { final ApplicationTerm appTerm = (ApplicationTerm) t; final FunctionSymbol funcSymb = appTerm.getFunction(); if (funcSymb.isIntern() && mFuncSet.contains(funcSymb.getName())) { super.convert(t); return; } } /* do not descend into any other term */ setResult(t); } @Override public void convertApplicationTerm(ApplicationTerm appTerm, Term[] newArgs) { final String funcName = appTerm.getFunction().getName(); assert mFuncSet.contains(funcName); if (funcName == "+") { SMTAffineTerm sum = SMTAffineTerm.create(newArgs[0]); for (int i = 1; i < newArgs.length; i++) { sum = sum.add(SMTAffineTerm.create(newArgs[i])); } setResult(sum); } else if (funcName == "-") { SMTAffineTerm sum = SMTAffineTerm.create(newArgs[0]); if (newArgs.length == 1) { /* unary minus */ sum = sum.negate(); } else { /* subtract other arguments */ for (int i = 1; i < newArgs.length; i++) { sum = sum.add( SMTAffineTerm.create(newArgs[i]).negate()); } } setResult(sum); } else if (funcName == "*") { SMTAffineTerm prod = SMTAffineTerm.create(newArgs[0]); for (int i = 1; i < newArgs.length; i++) { final SMTAffineTerm other = SMTAffineTerm.create(newArgs[i]); if (prod.isConstant()) { prod = other.mul(prod.getConstant()); } else if (other.isConstant()) { prod = prod.mul(other.getConstant()); } else { super.convertApplicationTerm(appTerm, newArgs); return; } } setResult(prod); } else if (funcName == "/") { SMTAffineTerm prod = SMTAffineTerm.create(newArgs[0]); for (int i = 1; i < newArgs.length; i++) { final SMTAffineTerm other = SMTAffineTerm.create(newArgs[i]); if (other.isConstant()) { prod = prod.mul(other.getConstant().inverse()); } else { super.convertApplicationTerm(appTerm, newArgs); return; } } setResult(prod); } else if (funcName == "to_real") { final SMTAffineTerm t = SMTAffineTerm.create(newArgs[0]); setResult(t.typecast(appTerm.getSort())); } else { throw new AssertionError( "Unexpected Function: " + funcName); } } } HashSet<Term> mAssertions; Script mSkript; LogProxy mLogger; int mError; HashSet<String> mDebug = new HashSet<String>(); // Just for debugging HashMap<Term, Term> mCacheConv; //Proof Checker Cache for conversions // Declarations for the Walker Stack<Term> mStackResults = new Stack<Term>(); Stack<Term> mStackResultsDebug = new Stack<Term>(); Stack<Annotation[]> mStackAnnots = new Stack<Annotation[]>(); SMTAffineTermTransformer mAffineConverter = new SMTAffineTermTransformer(); public ProofChecker(Script script, LogProxy logger) { mSkript = script; final Term[] assertions = script.getAssertions(); final FormulaUnLet unletter = new FormulaUnLet(); mAssertions = new HashSet<Term>(assertions.length); for (final Term ass : assertions) { mAssertions.add(unletter.transform(ass)); } mLogger = logger; } public boolean check(Term proof) { // Just for debugging //mDebug.add("currently"); //mDebug.add("hardTerm"); //mDebug.add("LemmaLAadd"); //mDebug.add("calculateTerm"); //mDebug.add("WalkerPath"); //mDebug.add("WalkerPathSmall"); //mDebug.add("LemmaCC"); //mDebug.add("newRules"); //mDebug.add("convertAppID"); //mDebug.add("cacheUsed"); //mDebug.add("cacheUsedSmall"); //mDebug.add("allSubpaths"); //mDebug.add("split_notOr"); //mDebug.add("CacheRuntimeCheck"); //mLogger.setLevel(Level.DEBUG); // Initializing the proof-checker-cache mCacheConv = new HashMap<Term, Term>(); mError = 0; // Now non-recursive: proof = new FormulaUnLet().unlet(proof); run(new ProofWalker(proof)); assert (mStackResults.size() == 1); final Term resCalc = stackPopCheck(proof); if (resCalc != mSkript.term("false")) { reportError("The proof did not yield a contradiction but " + resCalc); } return mError == 0; } private void reportError(String msg) { mLogger.error(msg); mError++; } private void reportWarning(String msg) { mLogger.warn(msg); } public Term negate(Term formula) { if (formula instanceof ApplicationTerm) { final ApplicationTerm appFormula = (ApplicationTerm) formula; if (appFormula.getFunction().getName() == "not") { return appFormula.getParameters()[0]; } } //Formula is not negative return mSkript.term("not", formula); } /** * The proof walker. This takes a proof term and pushes the proven * formula on the result stack. It also checks the proof cache to * prevent running over the same term twice. * * @param proofTerm The proof term. Its sort must be {@literal @}Proof. */ public void walk(ApplicationTerm proofTerm) { /* Check the cache, if the unfolding step was already done */ if (mCacheConv.containsKey(proofTerm)) { if (mDebug.contains("CacheRuntimeCheck")) { mLogger.debug("Cache-RT: K: " + proofTerm.toString() + " (known)"); } if (mDebug.contains("cacheUsed")) { mLogger.debug("Calculation of the term " + proofTerm.toString() + " is known: " + mCacheConv.get(proofTerm).toString()); } if (mDebug.contains("cacheUsedSmall")) { mLogger.debug("Calculation known."); } stackPush(mCacheConv.get(proofTerm), proofTerm); return; } else if (mDebug.contains("CacheRuntimeCheck")) { mLogger.debug("Cache-RT: U: " + proofTerm.toString() + " (unknown)"); } /* Get the function and parameters */ final String rulename = proofTerm.getFunction().getName(); final Term[] params = proofTerm.getParameters(); /* Just for debugging */ if (mDebug.contains("currently")) { mLogger.debug("Currently looking at: " + rulename + " \t (function)"); } /* Look at the rule name and treat each different */ if (rulename == "@res") { /* The resolution rule. * * This function is expected to have as first argument the * main clause. The other parameters are clauses annotated * with a pivot literal, on which they are resolved. */ enqueueWalker(new ResolutionWalker(proofTerm)); for (int i = params.length - 1; i >= 1; i--) { final AnnotatedTerm pivotClause = (AnnotatedTerm)params[i]; enqueueWalker(new ProofWalker(pivotClause.getSubterm())); } enqueueWalker(new ProofWalker(params[0])); } else if (rulename == "@eq") { enqueueWalker(new EqualityWalker(proofTerm)); for (int i = params.length - 1; i >= 0; i--) { enqueueWalker(new ProofWalker(params[i])); } } else if (rulename == "@lemma") { walkLemma(proofTerm); } else if (rulename == "@tautology") { walkTautology(proofTerm); } else if (rulename == "@asserted") { walkAsserted(proofTerm); } else if (rulename == "@rewrite") { walkRewrite(proofTerm); } else if (rulename == "@intern") { walkIntern(proofTerm); } else if (rulename == "@split") { enqueueWalker(new SplitWalker(proofTerm)); enqueueWalker(new ProofWalker( ((AnnotatedTerm) params[0]).getSubterm())); } else if (rulename == "@clause") { enqueueWalker(new ClauseWalker(proofTerm)); enqueueWalker(new ProofWalker(params[0])); } else { throw new AssertionError("Unknown proof rule " + rulename + "."); } } /** * Walk a lemma rule. This checks the correctness of the lemma and * returns the lemma, which is always the annotated sub term of this * application. The result is pushed to the stack instead of being * returned. * * If the lemma cannot be verified, an error is reported but the lemma * is still used to check the remainder of the proof. * @param lemmaApp The {@literal @}lemma application. */ public void walkLemma(ApplicationTerm lemmaApp) { final AnnotatedTerm annTerm = (AnnotatedTerm) lemmaApp.getParameters()[0]; final String lemmaType = annTerm.getAnnotations()[0].getKey(); final Object lemmaAnnotation = annTerm.getAnnotations()[0].getValue(); final Term lemma = annTerm.getSubterm(); if (mDebug.contains("currently")) { mLogger.debug("Lemma-type: " + lemmaType); } if (lemmaType == ":LA") { checkLALemma(termToClause(lemma), (Term[]) lemmaAnnotation); } else if (lemmaType == ":CC" || lemmaType == ":read-over-weakeq" || lemmaType == ":weakeq-ext") { checkArrayLemma(lemmaType, termToClause(lemma), (Object[]) lemmaAnnotation); } else if (lemmaType == ":trichotomy") { checkTrichotomy(termToClause(lemma)); } else if (lemmaType == ":store") { checkStore(termToClause(lemma), (ApplicationTerm) lemmaAnnotation); } else { reportError("Cannot deal with lemma " + lemmaType); mLogger.error(annTerm); } stackPush(lemma, lemmaApp); } /** * Check an array lemma for correctness. If a problem is found, an error * is reported. * @param type the lemma type * @param clause the clause to check * @param ccAnnotation the argument of the :CC annotation. */ private void checkArrayLemma(String type, Term[] clause, Object[] ccAnnotation) { int startSubpathAnnot = 0; Term goalEquality; if (ccAnnotation[0] instanceof Term) { startSubpathAnnot++; goalEquality = (Term) ccAnnotation[0]; } else { goalEquality = mSkript.term("false"); } /* * weakPaths maps from a symmetric pair to the set of weak * indices such that a weak path was proven for this pair. * strongPaths contains the sets of all proven strong paths. */ final HashSet<SymmetricPair<Term>> strongPaths = new HashSet<SymmetricPair<Term>>(); /* indexDiseqs contains all index equalities in the clause */ final HashSet<SymmetricPair<Term>> indexDiseqs = new HashSet<SymmetricPair<Term>>(); /* collect literals and search for the disequality */ boolean foundDiseq = false; for (final Term literal : clause) { if (isApplication("not", literal)) { Term atom = ((ApplicationTerm) literal).getParameters()[0]; atom = unquote(atom); if (!isApplication("=", atom)) { reportError("Unknown literal in CC lemma."); return; } final Term[] sides = ((ApplicationTerm) atom).getParameters(); if (sides.length != 2) { reportError("Expected binary equality, found " + atom); return; } strongPaths.add(new SymmetricPair<Term>(sides[0], sides[1])); } else { final Term atom = unquote(literal); if (!isApplication("=", atom)) { reportError("Unknown literal in CC lemma."); return; } if (unquote(literal) != goalEquality) { if (type == ":CC") { reportError("Unexpected positive literal in CC lemma."); } final Term[] sides = ((ApplicationTerm) atom).getParameters(); indexDiseqs.add(new SymmetricPair<Term>(sides[0], sides[1])); } foundDiseq = true; } } SymmetricPair<Term> lastPath = null; /* Check the paths in reverse order. Collect proven paths in * a hash set, so that they can be used later. */ final HashMap<SymmetricPair<Term>, HashSet<Term>> weakPaths = new HashMap<SymmetricPair<Term>, HashSet<Term>>(); for (int i = ccAnnotation.length-2; i >= startSubpathAnnot; i -= 2) { if (!(ccAnnotation[i] instanceof String) || !(ccAnnotation[i+1] instanceof Object[])) { reportError("Malformed Array subpath"); return; } final Object[] annot = (Object[]) ccAnnotation[i+1]; if (ccAnnotation[i] == ":weakpath") { if (annot.length != 2 || !(annot[0] instanceof Term) || !(annot[1] instanceof Term[])) { reportError("Malformed Array weakpath"); return; } final Term idx = (Term) annot[0]; final Term[] path = (Term[]) annot[1]; /* check weak path */ checkArrayPath(idx, path, strongPaths, null, indexDiseqs); /* add it to premises */ final SymmetricPair<Term> endPoints = new SymmetricPair<Term>(path[0], path[path.length - 1]); HashSet<Term> weakIdxs = weakPaths.get(endPoints); if (weakIdxs == null) { weakIdxs = new HashSet<Term>(); weakPaths.put(endPoints, weakIdxs); } weakIdxs.add(idx); } else if (ccAnnotation[i] == ":subpath" && (annot instanceof Term[])) { final Term[] path = (Term[]) annot; final SymmetricPair<Term> endPoints = new SymmetricPair<Term>(path[0], path[path.length - 1]); /* check path */ checkArrayPath(null, path, strongPaths, weakPaths.get(endPoints), indexDiseqs); /* add it to premises */ strongPaths.add(endPoints); lastPath = endPoints; } else { reportError("Unknown subpath annotation"); } } if (startSubpathAnnot == 0) { /* check that the mainPath is really a contradiction */ final SMTAffineTerm diff = convertAffineTerm(lastPath.getFirst()).add( convertAffineTerm(lastPath.getSecond()).negate()); if (!diff.isConstant() || diff.getConstant().equals(Rational.ZERO)) { reportError("No diseq, but main path is " + lastPath); } } else { if (!foundDiseq) { reportError("Did not find goal equality in CC lemma"); } if (!isApplication("=", goalEquality)) { reportError("Goal equality is not an equality in CC lemma"); return; } final Term[] sides = ((ApplicationTerm) goalEquality).getParameters(); if (sides.length != 2) { reportError("Expected binary equality in CC lemma"); return; } final SymmetricPair<Term> endPoints = new SymmetricPair<Term>(sides[0], sides[1]); if (strongPaths.contains(endPoints)) { /* everything fine */ return; } if (isApplication("select", sides[0]) && isApplication("select", sides[1])) { final Term[] p1 = ((ApplicationTerm) sides[0]).getParameters(); final Term[] p2 = ((ApplicationTerm) sides[1]).getParameters(); if (p1[1] == p2[1] || strongPaths.contains( new SymmetricPair<Term>(p1[1], p2[1]))) { final HashSet<Term> weakPs = weakPaths.get( new SymmetricPair<Term>(p1[0], p2[0])); if (weakPs != null && (weakPs.contains(p1[1]) || weakPs.contains(p2[1]))) { return; } } } reportError("Cannot explain main equality " + goalEquality); } } /** * Check if each step in a CC or array path is valid. * This means, for each pair of consecutive terms, either there is a strong * path between the two, or there exists a select path explaining element * equality of array terms at the weak path index, or it is a weak store * step, or a congruence. * This reports errors using reportError. * * @param weakIdx the weak path index or null for subpaths. * @param path the path to check. * @param strongPaths the equality literals and subpaths from the CC- and * array lemma annotations. * @param weakPaths the weak paths (given by their weak index) needed for * the main path in array lemmas, null if path is not the main path. * @param indexDiseqs the index disequality literals. */ void checkArrayPath(Term weakIdx, Term[] path, HashSet<SymmetricPair<Term>> strongPaths, HashSet<Term> weakPaths, HashSet<SymmetricPair<Term>> indexDiseqs) { if (path.length < 2) { reportError("Short path in ArrayLemma"); return; } for (int i = 0; i < path.length - 1; i++) { final SymmetricPair<Term> pair = new SymmetricPair<Term>(path[i], path[i+1]); /* check for strong path first */ if (strongPaths.contains(pair)) { continue; } /* check for select path (only for weakeq-ext) */ if (weakIdx != null) { /* check for select path with select indices equal to weakIdx, * both trivially equal and proven equal by a strong path */ if (checkSelectPath(pair, weakIdx, strongPaths)){ continue; } } /* check for weak store step */ final Term storeIndex = checkStoreIndex(path[i], path[i + 1]); if (storeIndex != null) { if (weakIdx != null && indexDiseqs.contains( new SymmetricPair<Term>(weakIdx, storeIndex))) { continue; } if (weakPaths != null && weakPaths.contains(storeIndex)) { continue; } } /* check for congruence */ if (path[i] instanceof ApplicationTerm && path[i + 1] instanceof ApplicationTerm) { final ApplicationTerm app1 = (ApplicationTerm) path[i]; final ApplicationTerm app2 = (ApplicationTerm) path[i + 1]; if (app1.getFunction() == app2.getFunction()) { final Term[] p1 = app1.getParameters(); final Term[] p2 = app2.getParameters(); for (int j = 0; j < p1.length; j++) { if (p1[j] == p2[j]) { continue; } if (!strongPaths.contains( new SymmetricPair<Term>(p1[j], p2[j]))) { reportError("unexplained equality"); } } continue; } } reportError("unexplained equality " + path[i] + " == " + path[i + 1]); } } private boolean checkSelectPath(SymmetricPair<Term> termPair, Term weakIdx, HashSet<SymmetricPair<Term>> strongPaths){ for (final SymmetricPair<Term> strongPath : strongPaths){ /* check for select terms */ if (! (isApplication("select", strongPath.getFirst()) && isApplication("select", strongPath.getSecond()))){ continue; } /* check select arrays */ final Term array1 = ((ApplicationTerm) strongPath.getFirst()).getParameters()[0]; final Term array2 = ((ApplicationTerm) strongPath.getSecond()).getParameters()[0]; final SymmetricPair<Term> arrayPair = new SymmetricPair<Term>(array1,array2); if (!arrayPair.equals(termPair)){ continue; } /* check index paths */ final Term idx1 = ((ApplicationTerm) strongPath.getFirst()).getParameters()[1]; final Term idx2 = ((ApplicationTerm) strongPath.getSecond()).getParameters()[1]; if (idx1 != weakIdx && !strongPaths.contains(new SymmetricPair<Term>(idx1,weakIdx))){ continue; } if (idx2 != weakIdx && !strongPaths.contains(new SymmetricPair<Term>(idx2,weakIdx))){ continue; } return true; } return false; } private Term checkStoreIndex(Term term1, Term term2) { if (isApplication("store", term1)) { final Term[] storeArgs = ((ApplicationTerm) term1).getParameters(); if (storeArgs[0] == term2) { return storeArgs[1]; } } if (isApplication("store", term2)) { final Term[] storeArgs = ((ApplicationTerm) term2).getParameters(); if (storeArgs[0] == term1) { return storeArgs[1]; } } return null; } /** * Check an LA lemma for correctness. If a problem is found, an error * is reported. * @param clause the clause to check * @param coefficients the argument of the :LA annotation, which is * the list of Farkas coefficients. */ private void checkLALemma(Term[] clause, Term[] coefficients) { if (clause.length != coefficients.length) { reportError("Clause and coefficients have different length"); return; } boolean sumHasStrict = false; SMTAffineTerm sum = null; for (int i = 0; i < clause.length; i++) { final Rational coeff = convertAffineTerm(coefficients[i]).getConstant(); if (coeff.equals(Rational.ZERO)) { reportWarning("Coefficient in LA lemma is zero."); continue; } Term literal = clause[i]; boolean isNegated = false; if (isApplication("not", literal)) { literal = ((ApplicationTerm) literal).getParameters()[0]; isNegated = true; } literal = unquote(literal); boolean isStrict; if (isNegated) { if (isApplication("<=", literal)) { isStrict = false; if (coeff.isNegative()) { reportError("Negative coefficient for <="); } } else if (isApplication("=", literal)) { isStrict = false; } else if (isApplication("<", literal)) { isStrict = true; if (coeff.isNegative()) { reportError("Negative coefficient for <"); } } else { reportError("Unknown atom in LA lemma: " + literal); continue; } } else { if (isApplication("<=", literal)) { isStrict = true; if (!coeff.isNegative()) { reportError("Positive coefficient for negated <="); } } else if (isApplication("<", literal)) { isStrict = false; if (!coeff.isNegative()) { reportError("Positive coefficient for negated <"); } } else { reportError("Unknown atom in LA lemma: " + literal); continue; } } final Term[] params = ((ApplicationTerm)literal).getParameters(); if (params.length != 2) { reportError("not a binary comparison in LA lemma"); continue; } SMTAffineTerm affine = convertAffineTerm(params[1]); if (!affine.isConstant() || !affine.getConstant().equals(Rational.ZERO)) { reportError("Right hand side is not zero"); } affine = convertAffineTerm(params[0]); if (isStrict && params[0].getSort().getName().equals("Int")) { /* make integer equalities non-strict by adding one. * x < 0 iff x + 1 <= 0 * x > 0 iff x - 1 >= 0 */ affine = affine.add(isNegated ? Rational.ONE : Rational.MONE); isStrict = false; } affine = affine.mul(coeff); if (sum == null) { sum = affine; } else { sum = sum.add(affine); } sumHasStrict |= isStrict; } final Rational sumConstant = sum.getConstant(); if (sum.isConstant()) { final int signum = sumConstant.signum(); if (signum > 0 || (sumHasStrict && signum == 0)) { return; } } reportError("LA lemma sums up to " + sum + (sumHasStrict ? " < 0" : " <= 0")); } /** * Check an trichotomy lemma for correctness. If a problem is found, * an error is reported. * @param clause the clause to check. */ private void checkTrichotomy(Term[] clause) { if (clause.length != 3) { // NOCHECKSTYLE reportError("Malformed Trichotomy clause: " + Arrays.toString(clause)); return; } SMTAffineTerm trichotomyTerm = null; final int NEQ = 1; final int LEQ = 2; final int GEQ = 4; int foundlits = 0; for (Term lit : clause) { final boolean isNegated = isApplication("not", lit); if (isNegated) { lit = ((ApplicationTerm) lit).getParameters()[0]; } lit = unquote(lit); Rational offset = Rational.ZERO; if (isApplication("=", lit)) { if (isNegated) { reportError("Equality in trichotomy has wrong polarity"); return; } if ((foundlits & NEQ) != 0) { reportError("Two Disequalities in trichotomy"); return; } foundlits |= NEQ; } else if (isApplication("<=", lit)) { if (isNegated) { if ((foundlits & GEQ) != 0) { reportError("Two > in trichotomy"); return; } foundlits |= GEQ; } else { if ((foundlits & LEQ) != 0) { reportError("Two <= in trichotomy"); return; } foundlits |= LEQ; offset = Rational.MONE; // x <= 0 iff x - 1 < 0 } } else if (isApplication("<", lit)) { if (isNegated) { if ((foundlits & GEQ) != 0) { reportError("Two >= in trichotomy"); return; } foundlits |= GEQ; offset = Rational.ONE; // x >= 0 iff x + 1 > 0 } else { if ((foundlits & LEQ) != 0) { reportError("Two < in trichotomy"); return; } foundlits |= LEQ; } } else { reportError("Unknown literal in trichotomy " + lit); return; } final Term[] params = ((ApplicationTerm) lit).getParameters(); if (params.length != 2) { reportError("not a binary comparison in LA lemma"); return; } SMTAffineTerm affine = convertAffineTerm(params[1]); if (!affine.isConstant() || !affine.getConstant().equals(Rational.ZERO)) { reportError("Right hand side is not zero"); } if (offset != Rational.ZERO && !params[1].getSort().getName().equals("Int")) { reportError("<= or >= in non-integer trichotomy"); } affine = convertAffineTerm(params[0]).add(offset); if (trichotomyTerm == null) { trichotomyTerm = affine; } else if (!trichotomyTerm.equals(affine)) { reportError("Invalid trichotomy"); } } assert foundlits == (NEQ + LEQ + GEQ); } /** * Check an select over store lemma for correctness. If a problem is * found, an error is reported. * @param clause the clause to check. */ private void checkStore(Term[] clause, Term store) { if (clause.length == 1) { final Term eqlit = unquote(clause[0]); if (isApplication("=", eqlit)) { final Term[] sides = ((ApplicationTerm) eqlit).getParameters(); if (isApplication("select", sides[0])) { final ApplicationTerm select = (ApplicationTerm) sides[0]; if (store == select.getParameters()[0] && isApplication("store", store)) { final Term[] storeArgs = ((ApplicationTerm) store).getParameters(); if (storeArgs[1] == select.getParameters()[1] && storeArgs[2] == sides[1]) { return; } } } } } reportError("Malformed store clause: " + Arrays.toString(clause)); } public void walkTautology(ApplicationTerm tautologyApp) { final AnnotatedTerm annTerm = (AnnotatedTerm) tautologyApp.getParameters()[0]; final String tautType = annTerm.getAnnotations()[0].getKey(); final Term tautology = annTerm.getSubterm(); final Term[] clause = termToClause(tautology); /* push it and check later */ stackPush(tautology, tautologyApp); if (tautType == ":eq") { if (clause.length != 2) { reportError("Tautology :eq must have two literals"); } boolean term1Negated = false; final Term term1 = clause[0]; final Term term2 = clause[1]; if (term1 instanceof ApplicationTerm && pm_func_weak(convertApp(term1),"not")) { term1Negated = true; } ApplicationTerm termNegApp = null; // The term t with (not t) ApplicationTerm termPosApp = null; // the term without a not around if (term1Negated) { termNegApp = convertApp_hard(convertApp(term1).getParameters()[0]); termPosApp = convertApp_hard(term2); } else { pm_func(term2,"not"); termNegApp = convertApp_hard(convertApp(term2).getParameters()[0]); termPosApp = convertApp_hard(term1); } final String funcSymb = termNegApp.getFunction().getName(); pm_func(termPosApp,funcSymb); final ApplicationTerm termNegUnif = uniformizeInEquality(termNegApp); final ApplicationTerm termPosUnif = uniformizeInEquality(termPosApp); if (!uniformedSame(termNegUnif,termPosUnif)) { throw new AssertionError("Error in @taut_eq"); } } else if (tautType == ":or+") { final Term[] split = termToClause(unquote(negate(clause[0]))); if (split.length < 2) { reportError("Expected or-term in rule :or+ but got " + clause[0]); } else { boolean problem = false; for (int i = 1; i < clause.length; i++) { if (split[i - 1] != clause[i]) { problem = true; } } if (problem) { reportError("Malformed tautology " + tautologyApp); } } } else if (tautType == ":or-") { if (clause.length != 2 || !checkOrMinus(unquote(clause[0]),clause[1])) { reportError("Invalid application of rule :or-"); } } else if (tautType == ":termITE") { try { final ApplicationTerm termOr = convertApp(tautology); // The term with or pm_func(termOr, "or"); checkNumber(termOr,2); // Find the terms which may be re-ordered because of commutativity Term termNotEq = null; ApplicationTerm equalityApp = null; ApplicationTerm equalityIteApp = null; Term equalityNotIte = null; boolean foundEq = false; if (termOr.getParameters()[0] instanceof ApplicationTerm) { final ApplicationTerm termAppTemp = convertApp(termOr); if (pm_func_weak(termAppTemp,"=") && pm_func_weak(termAppTemp.getParameters()[0],"ite")) { foundEq = true; equalityApp = convertApp(termOr.getParameters()[0]); termNotEq = termOr.getParameters()[1]; } } if (!foundEq) { equalityApp = convertApp(termOr.getParameters()[1]); termNotEq = termOr.getParameters()[0]; } if (equalityApp.getParameters()[0] instanceof ApplicationTerm) { final ApplicationTerm termAppTemp2 = convertApp(equalityApp.getParameters()[0]); if (pm_func_weak(termAppTemp2, "ite")) { equalityIteApp = convertApp(equalityApp.getParameters()[0]); equalityNotIte = equalityApp.getParameters()[1]; } else { equalityIteApp = convertApp(equalityApp.getParameters()[1]); equalityNotIte = equalityApp.getParameters()[0]; } } // Syntactical Correctness pm_func(equalityApp, "="); pm_func(equalityIteApp, "ite"); checkNumber(equalityApp, 2); checkNumber(equalityIteApp, 3); // The Rule-Check if (termITEHelper_isEqual(termNotEq, equalityIteApp.getParameters()[0])) { if (equalityNotIte != equalityIteApp.getParameters()[2]) { throw new AssertionError("Error 1 in @taut_termITE"); } } else { if (equalityNotIte != equalityIteApp.getParameters()[1]) { throw new AssertionError("Error 2 in @taut_termITE"); } } } catch (final RuntimeException ex) { reportError("Invalid application of rule :termIte"); return; } } else if (tautType == ":excludedMiddle1") { if (clause.length == 2) { final Term falseEq = unquote(clause[1]); if (isApplication("not", clause[0]) && isApplication("=", falseEq)) { final Term negClause0 = ((ApplicationTerm) clause[0]) .getParameters()[0]; final Term[] ps = ((ApplicationTerm)falseEq).getParameters(); if (isApplication("true", ps[1]) && negClause0 == ps[0]) { return; } } } reportError("Invalid application of rule :excludedMiddle1"); return; } else if (tautType == ":excludedMiddle2") { if (clause.length == 2) { final Term falseEq = unquote(clause[1]); if (isApplication("=", falseEq)) { final Term[] ps = ((ApplicationTerm)falseEq).getParameters(); if (isApplication("false", ps[1]) && clause[0] == ps[0]) { return; } } } reportError("Invalid application of rule :excludedMiddle2"); return; } else { reportError("Unknown tautology rule " + tautologyApp); } } void walkAsserted(ApplicationTerm assertedApp) { final Term assertedTerm = assertedApp.getParameters()[0]; if (!mAssertions.contains(assertedTerm)) { reportError("Could not find asserted term " + assertedTerm); } /* Just return the part without @asserted */ stackPush(assertedTerm, assertedApp); } void walkRewrite(ApplicationTerm rewriteApp) { /* Treatment: * - At first check if the rewrite rule was correctly executed. * - OLD: Secondly, remove the @rewrite and the annotation for later uses in the @eq-function. */ /* Get access to the internal terms */ final AnnotatedTerm termAppInnerAnn = convertAnn(rewriteApp.getParameters()[0]); //The annotated term inside the rewrite-term final ApplicationTerm termEqApp = convertApp(termAppInnerAnn.getSubterm()); //The application term inside the annotated term inside the rewrite-term pm_func(termEqApp, "="); /* The result is simply the equality (without annotation). * Compute it first and check later. */ stackPush(termEqApp, rewriteApp); checkNumber(termEqApp,2); /* Read the rule and handle each differently */ final String rewriteRule = termAppInnerAnn.getAnnotations()[0].getKey(); if (mDebug.contains("currently")) { System.out.println("Rewrite-Rule: " + rewriteRule); } if (mDebug.contains("hardTerm")) { System.out.println("Term: " + rewriteApp.toStringDirect()); } if (rewriteRule == ":trueNotFalse") { if (termEqApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be true, but isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"="); boolean foundTrue = false; boolean foundFalse = false; for (final Term subterm : termOldApp.getParameters()) { if (subterm == mSkript.term("false")) { foundFalse = true; } if (subterm == mSkript.term("true")) { foundTrue = true; } if (foundFalse && foundTrue) { return; } } throw new AssertionError("Error at the end of rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } else if (rewriteRule == ":constDiff") { if (termEqApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be false, but isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"="); final HashSet<Term> constTerms = new HashSet<Term>(); // Get all constant terms for (final Term subterm : termOldApp.getParameters()) { if (subterm instanceof ConstantTerm) { constTerms.add(subterm); } else if (subterm instanceof ApplicationTerm) { // Maybe a negated constant final ApplicationTerm subtermApp = convertApp(subterm); if (pm_func_weak(subtermApp, "-") && subtermApp.getParameters()[0] instanceof ConstantTerm) { constTerms.add(subterm); } } } if (mDebug.contains("newRules")) { System.out.println("The constant terms are:"); for (final Term termC : constTerms) { System.out.println(termC.toStringDirect()); } } // Check if there are two different constant terms if (constTerms.size() <= 1) { throw new AssertionError("Error at the end of rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } } else if (rewriteRule == ":eqTrue") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"="); boolean multiconjunct = false; ApplicationTerm termNewApp = null; //Not nice: Initialisation as null if ((termEqApp.getParameters()[1] instanceof ApplicationTerm) && pm_func_weak(convertApp(termEqApp.getParameters()[1]), "and")) { termNewApp = convertApp(termEqApp.getParameters()[1]); multiconjunct = true; } final HashSet<Term> oldTerms = new HashSet<Term>(); final HashSet<Term> newTerms = new HashSet<Term>(); oldTerms.addAll(Arrays.asList(termOldApp.getParameters())); if (multiconjunct) { newTerms.addAll(Arrays.asList(termNewApp.getParameters())); } else { newTerms.add(termEqApp.getParameters()[1]); } if (!oldTerms.contains(mSkript.term("true"))) { throw new AssertionError("Error 1 at " + rewriteRule + ".\n The term was " + termEqApp.toString()); } /* The line below is needed, to have a short equivalence check, even * if more than one term is "true". */ newTerms.add(mSkript.term("true")); if (!oldTerms.equals(newTerms)) { throw new AssertionError("Error 2 at " + rewriteRule + ".\n The term was " + termEqApp.toString()); } // Not nice: j \notin I' isn't checked, but even if j \in I' it's still correct } else if (rewriteRule == ":eqFalse") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp, "="); pm_func(termNewApp, "not"); boolean multidisjunct = false; ApplicationTerm termNewAppInnerApp = null; //Not nice: Initialisation as null if ((termNewApp.getParameters()[0] instanceof ApplicationTerm) && pm_func_weak(convertApp(termNewApp.getParameters()[0]), "or")) { termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); multidisjunct = true; } final HashSet<Term> oldTerms = new HashSet<Term>(); final HashSet<Term> newTerms = new HashSet<Term>(); oldTerms.addAll(Arrays.asList(termOldApp.getParameters())); if (multidisjunct) { newTerms.addAll(Arrays.asList(termNewAppInnerApp.getParameters())); } else { newTerms.add(termNewApp.getParameters()[0]); } if (!oldTerms.contains(mSkript.term("false"))) { throw new AssertionError("Error 1 at " + rewriteRule + ".\n The term was " + termEqApp.toString()); } /* The line below is needed, to have a short equivalence check, even * if more than one term is "true". */ newTerms.add(mSkript.term("false")); if (!oldTerms.equals(newTerms)) { throw new AssertionError("Error 2 at " + rewriteRule + ".\n The term was " + termEqApp.toString()); } // Not nice: j \notin I' isn't checked, but even if j \in I' it's still correct } else if (rewriteRule == ":eqSame") { if (termEqApp.getParameters()[1] != mSkript.term("true")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be true, but isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "="); final Term termComp = termOldApp.getParameters()[0]; //compare-term for (final Term subterm : termOldApp.getParameters()) { if (subterm != termComp) { throw new AssertionError("Error 2 at rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } } } else if (rewriteRule == ":eqSimp") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp, "="); pm_func(termNewApp, "="); final HashSet<Term> oldTerms = new HashSet<Term>(); final HashSet<Term> newTerms = new HashSet<Term>(); oldTerms.addAll(Arrays.asList(termOldApp.getParameters())); newTerms.addAll(Arrays.asList(termNewApp.getParameters())); if (!oldTerms.equals(newTerms)) { throw new AssertionError("Error 1 at " + rewriteRule + ".\n The term was " + termEqApp.toString()); } // Not nice: I' \subsetneq I isn't checked, but even if I' \supset I, it's still correct // Not nice: Not checked if there aren't two doubled terms in termNewApp, but even if there are, it's still correct } else if (rewriteRule == ":eqBinary") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); pm_func(termOldApp, "="); pm_func(termNewApp, "not"); // Is it a binary equality? if (termOldApp.getParameters().length == 2) { pm_func(termNewAppInnerApp, "not"); if (termOldApp != termNewAppInnerApp.getParameters()[0]) { throw new AssertionError("Error A in " + rewriteRule); } return; } pm_func(termNewAppInnerApp, "or"); // The array which contains the equalities final ApplicationTerm[] arrayNewEqApp = new ApplicationTerm[termNewAppInnerApp.getParameters().length]; final Term[] arrayOldTerm = termOldApp.getParameters(); for (int i = 0; i < termNewAppInnerApp.getParameters().length; i++) { final ApplicationTerm termIneqApp = convertApp(termNewAppInnerApp.getParameters()[i]); pm_func(termIneqApp,"not"); arrayNewEqApp[i] = convertApp(termIneqApp.getParameters()[0]); pm_func(arrayNewEqApp[i],"="); } final boolean[] eqFound = new boolean[arrayNewEqApp.length]; for (int i = 0; i < eqFound.length; i++) { eqFound[i] = false; } // Look for each two distinct terms (j > i) if there exists a fitting equality for (int i = 0; i < arrayOldTerm.length; i++) { for (int j = i + 1; j < arrayOldTerm.length; j++) { // boolean found = false; for (int k = 0; k < arrayNewEqApp.length; k++) { if (!eqFound[k]) { checkNumber(arrayNewEqApp[k], 2); if (arrayNewEqApp[k].getParameters()[0] == arrayOldTerm[i] && arrayNewEqApp[k].getParameters()[1] == arrayOldTerm[j]) { eqFound[k] = true; // found = true; } if (arrayNewEqApp[k].getParameters()[1] == arrayOldTerm[i] && arrayNewEqApp[k].getParameters()[0] == arrayOldTerm[j]) { eqFound[k] = true; // found = true; } } } // Wrong, because the rule allows to leave out disjuncts. // if (!found) // { // throw new AssertionError("Error: Couldn't find the equality that " // + "corresponds to " + arrayOldTerm[i].toStringDirect() // + " and " + arrayOldTerm[j].toStringDirect() + ".\n" // + "The term was " + term.toStringDirect()); // } } } // At last check if each equality is alright for (int i = 0; i < eqFound.length; i++) { if (!eqFound[i]) { throw new AssertionError("Error: Coulnd't associate the equality " + arrayNewEqApp[i] + "\n. The term was " + rewriteApp.toStringDirect()); } } } else if (rewriteRule == ":distinctBool") { if (termEqApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be false, but it isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "distinct"); // Check if there are at least three parameters if (termOldApp.getParameters().length < 3) { throw new AssertionError("Error 1 at " + rewriteRule); } // Check if two are they are all boolean for (final Term subterm : termOldApp.getParameters()) { if (subterm.getSort() != mSkript.sort("Bool")) { throw new AssertionError("Error 2 at " + rewriteRule); } } } else if (rewriteRule == ":distinctSame") { if (termEqApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be false, but it isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "distinct"); // Check if two are the same for (int i = 0; i < termOldApp.getParameters().length; i++) { for (int j = i + 1; j < termOldApp.getParameters().length; j++) { if (termOldApp.getParameters()[i] == termOldApp.getParameters()[j]) { return; } } } throw new AssertionError("Error at the end of rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } else if (rewriteRule == ":distinctNeg") { if (termEqApp.getParameters()[1] != mSkript.term("true")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be true, but it isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "distinct"); if (termOldApp.getParameters().length != 2) { throw new AssertionError("Error 1 at " + rewriteRule); } // Check if one is the negation of the other final Term term1 = termOldApp.getParameters()[0]; final Term term2 = termOldApp.getParameters()[1]; if (term1 != negate(term2)) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":distinctTrue") { final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "distinct"); pm_func(termNewApp,"not"); if (termOldApp.getParameters().length != 2) { throw new AssertionError("Error 2 at " + rewriteRule); } // Check if one is true final Term term1 = termOldApp.getParameters()[0]; final Term term2 = termOldApp.getParameters()[1]; Term termNotTrue; // The term on the left side which is not true if (term1.equals(mSkript.term("true"))) { termNotTrue = term2; } else if (term2.equals(mSkript.term("true"))) { termNotTrue = term1; } else { throw new AssertionError("Error 1 at " + rewriteRule); } if (termNewApp.getParameters()[0] != termNotTrue) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":distinctFalse") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "distinct"); if (termOldApp.getParameters().length != 2) { throw new AssertionError("Error 2 at " + rewriteRule); } // Check if one is false final Term term1 = termOldApp.getParameters()[0]; final Term term2 = termOldApp.getParameters()[1]; Term termNotFalse; // The term on the left side which is not true if (term1.equals(mSkript.term("false"))) { termNotFalse = term2; } else if (term2.equals(mSkript.term("false"))) { termNotFalse = term1; } else { throw new AssertionError("Error 1 at " + rewriteRule); } final Term termNew = termEqApp.getParameters()[1]; if (termNew != termNotFalse) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":distinctBinary") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); pm_func(termOldApp, "distinct"); // Maybe it's the distinctBoolEq-rule if (pm_func_weak(termNewApp, "=")) { checkNumber(termOldApp,2); checkNumber(termNewApp,2); final Term termP1 = termOldApp.getParameters()[0]; // The first Boolean variable final Term termP2 = termOldApp.getParameters()[1]; // The first Boolean variable if (termP1.getSort() != mSkript.sort("Bool") || termP2.getSort() != mSkript.sort("Bool")) { throw new AssertionError("Error 1 in :distinctBinary_distinctBoolEq"); } boolean correctRightSide = false; // Four cases need to be checked if (termNewApp.getParameters()[0].equals(termP1) && termNewApp.getParameters()[1].equals(negate(termP2))) { correctRightSide = true; } if (termNewApp.getParameters()[0].equals(termP2) && termNewApp.getParameters()[1].equals(negate(termP1))) { correctRightSide = true; } if (termNewApp.getParameters()[1].equals(termP1) && termNewApp.getParameters()[0].equals(negate(termP2))) { correctRightSide = true; } if (termNewApp.getParameters()[1].equals(termP2) && termNewApp.getParameters()[0].equals(negate(termP1))) { correctRightSide = true; } if (!correctRightSide) { throw new AssertionError("Error at the end of :distinctBinary_distinctBoolEq"); } } else { pm_func(termNewApp, "not"); // The array which contains the equalities Term[] arrayNewEq = null; final Term[] arrayOldTerm = termOldApp.getParameters(); if (pm_func_weak(termNewAppInnerApp,"or")) { arrayNewEq = termNewAppInnerApp.getParameters(); } else { arrayNewEq = termNewApp.getParameters(); } final boolean[] eqFound = new boolean[arrayNewEq.length]; for (int i = 0; i < eqFound.length; i++) { eqFound[i] = false; } // Look for each two distinct terms (j > i) if there exists a fitting equality for (int i = 0; i < arrayOldTerm.length; i++) { for (int j = i + 1; j < arrayOldTerm.length; j++) { boolean found = false; for (int k = 0; k < arrayNewEq.length; k++) { if (!eqFound[k]) { final ApplicationTerm termAppTemp = convertApp(arrayNewEq[k]); pm_func(termAppTemp, "="); checkNumber(termAppTemp,2); if (termAppTemp.getParameters()[0] == arrayOldTerm[i] && termAppTemp.getParameters()[1] == arrayOldTerm[j]) { found = true; eqFound[k] = true; } if (termAppTemp.getParameters()[1] == arrayOldTerm[i] && termAppTemp.getParameters()[0] == arrayOldTerm[j]) { found = true; eqFound[k] = true; } } } if (!found) { throw new AssertionError("Error: Couldn't find the equality that " + "corresponds to " + arrayOldTerm[i].toStringDirect() + " and " + arrayOldTerm[j].toStringDirect() + ".\n" + "The term was " + rewriteApp.toStringDirect()); } } } // At last check if each equality is alright for (int i = 0; i < eqFound.length; i++) { if (!eqFound[i]) { throw new AssertionError("Error: Coulnd't associate the equality " + arrayNewEq[i] + "\n. The term was " + rewriteApp.toStringDirect()); } } } } else if (rewriteRule == ":notSimp") { /* The first argument of the rewrite has to be the * double-negated version of the second argument or * "(not true)" iff the second is "false" or * "(not false)" iff the second is "true". */ // Check syntactical correctness final ApplicationTerm innerAppTermFirstNeg = convertApp(termEqApp.getParameters()[0]); pm_func(innerAppTermFirstNeg, "not"); if ((innerAppTermFirstNeg.getParameters()[0] == mSkript.term("false") && termEqApp.getParameters()[1] == mSkript.term("true")) || (innerAppTermFirstNeg.getParameters()[0] == mSkript.term("true") && termEqApp.getParameters()[1] == mSkript.term("false"))) { return; } final ApplicationTerm innerAppTermSecondNeg = convertApp(innerAppTermFirstNeg.getParameters()[0]); pm_func(innerAppTermSecondNeg, "not"); // Check if the rule was executed correctly if (innerAppTermSecondNeg.getParameters()[0] != termEqApp.getParameters()[1]) { throw new AssertionError("Error: The rule \"notSimp\" couldn't be verified, because the following " + "two terms aren't the same: " + innerAppTermSecondNeg.getParameters()[0].toString() + " and " + termEqApp.getParameters()[1].toStringDirect() + ".\n" + "The original term was: " + rewriteApp.toStringDirect()); } // Important: The return is done later, the following is false: // return innerAppTerm.getParameters()[1]; } else if (rewriteRule == ":orSimp") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); boolean multidisjunct = true; final Term termNew = termEqApp.getParameters()[1]; ApplicationTerm termNewApp = null; pm_func(termOldApp,"or"); final HashSet<Term> oldDisjuncts = new HashSet<Term>(); final HashSet<Term> newDisjuncts = new HashSet<Term>(); oldDisjuncts.addAll(Arrays.asList(termOldApp.getParameters())); oldDisjuncts.remove(mSkript.term("false")); if (oldDisjuncts.size() <= 1) { multidisjunct = false; } if (multidisjunct) { termNewApp = convertApp(termNew); pm_func(termNewApp,"or"); } if (multidisjunct) { newDisjuncts.addAll(Arrays.asList(termNewApp.getParameters())); } else { newDisjuncts.add(termNew); } /* OLD: The line below is needed, to have a short equivalence check, even * if the new term still contains a disjunct false */ //newDisjuncts.add(mSkript.term("false")); if (!oldDisjuncts.equals(newDisjuncts)) { newDisjuncts.remove(mSkript.term("false")); if (!oldDisjuncts.equals(newDisjuncts)) { throw new AssertionError("Error 2 at " + rewriteRule + ".\n The term was " + termEqApp.toStringDirect()); } } // Not nice: I' \subsetneq I isn't checked, but even if I' \supseteq I it's still correct // Not nice: The work with false } else if (rewriteRule == ":orTaut") { if (termEqApp.getParameters()[1] != mSkript.term("true")) { throw new AssertionError("Error: The second argument of a rewrite of the rule " + rewriteRule + " should be true, but it isn't.\n" + "The term was " + termEqApp.toString()); } final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp, "or"); // Case 1: One disjunct is true for (final Term disjunct : termOldApp.getParameters()) { if (disjunct == mSkript.term("true")) { return; } } // Case 2: One disjunct is the negate of another for (final Term disjunct1 : termOldApp.getParameters()) { for (final Term disjunct2 : termOldApp.getParameters()) { if (disjunct1 == negate(disjunct2)) { return; } } } throw new AssertionError("Error at the end of rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } else if (rewriteRule == ":iteTrue") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"ite"); checkNumber(termOldApp.getParameters(),3); // checkNumber(termEqApp.getParameters(),2); Is already checked //Check syntactical correctness if (termOldApp.getParameters()[0] != mSkript.term("true")) { throw new AssertionError("Error 1 at " + rewriteRule); } if (termOldApp.getParameters()[1] != termEqApp.getParameters()[1]) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":iteFalse") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"ite"); checkNumber(termOldApp.getParameters(),3); //checkNumber(termEqApp.getParameters(),2); //Check syntactical correctness if (termOldApp.getParameters()[0] != mSkript.term("false")) { throw new AssertionError("Error 1 at " + rewriteRule); } if (termOldApp.getParameters()[2] != termEqApp.getParameters()[1]) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":iteSame") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"ite"); checkNumber(termOldApp.getParameters(),3); //Check syntactical correctness if (termOldApp.getParameters()[1] != termEqApp.getParameters()[1]) { throw new AssertionError("Error 2 at " + rewriteRule); } if (termOldApp.getParameters()[2] != termEqApp.getParameters()[1]) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":iteBool1") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"ite"); checkNumber(termOldApp.getParameters(),3); //Check syntactical correctness if (termOldApp.getParameters()[0] != termEqApp.getParameters()[1]) { throw new AssertionError("Error 1 at " + rewriteRule); } if (termOldApp.getParameters()[1] != mSkript.term("true")) { throw new AssertionError("Error 2 at " + rewriteRule); } if (termOldApp.getParameters()[2] != mSkript.term("false")) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":iteBool2") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"ite"); checkNumber(termOldApp.getParameters(),3); //Check syntactical correctness if (mSkript.term("not",termOldApp.getParameters()[0]) != termEqApp.getParameters()[1]) { throw new AssertionError("Error 1 at " + rewriteRule); } if (termOldApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error 2 at " + rewriteRule); } if (termOldApp.getParameters()[2] != mSkript.term("true")) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":iteBool3") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp,"ite"); pm_func(termNewApp,"or"); checkNumber(termOldApp,3); checkNumber(termNewApp,2); // Names as in the documentation proof.pdf final Term t1 = termOldApp.getParameters()[1]; //Check syntactical correctness if (t1 != mSkript.term("true")) { throw new AssertionError("Error 2 at " + rewriteRule); } // or is commutative final Term t0 = termOldApp.getParameters()[0]; if (termNewApp.getParameters()[0] != t0 && termNewApp.getParameters()[1] != t0) { throw new AssertionError("Error 4 at " + rewriteRule); } final Term t2 = termOldApp.getParameters()[2]; if (termNewApp.getParameters()[0] != t2 && termNewApp.getParameters()[1] != t2) { throw new AssertionError("Error 5 at " + rewriteRule); } } else if (rewriteRule == ":iteBool4") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); checkNumber(termOldApp,3); checkNumber(termNewAppInnerApp,2); // Names as in the documentation proof.pdf final Term t1 = termOldApp.getParameters()[1]; pm_func(termOldApp,"ite"); pm_func(termNewApp,"not"); pm_func(termNewAppInnerApp,"or"); //Check syntactical correctness if (t1 != mSkript.term("false")) { throw new AssertionError("Error 2 at " + rewriteRule); } // or is commutative final Term t0 = termOldApp.getParameters()[0]; if (termNewAppInnerApp.getParameters()[0] != t0 && termNewAppInnerApp.getParameters()[1] != t0) { throw new AssertionError("Error 4 at " + rewriteRule); } final Term t2 = termOldApp.getParameters()[2]; if (termNewAppInnerApp.getParameters()[0] != mSkript.term("not",t2) && termNewAppInnerApp.getParameters()[1] != mSkript.term("not",t2)) { throw new AssertionError("Error 5 at " + rewriteRule); } } else if (rewriteRule == ":iteBool5") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp,"ite"); pm_func(termNewApp,"or"); checkNumber(termOldApp,3); checkNumber(termNewApp,2); // Names as in the documentation proof.pdf final Term t2 = termOldApp.getParameters()[2]; //Check syntactical correctness if (t2 != mSkript.term("true")) { throw new AssertionError("Error 3 at " + rewriteRule); } final Term t1 = termOldApp.getParameters()[1]; // or is commutative if (termNewApp.getParameters()[0] != t1 && termNewApp.getParameters()[1] != t1) { throw new AssertionError("Error 4 at " + rewriteRule); } final Term t0 = termOldApp.getParameters()[0]; if (termNewApp.getParameters()[0] != mSkript.term("not",t0) && termNewApp.getParameters()[1] != mSkript.term("not",t0)) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":iteBool6") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); pm_func(termOldApp,"ite"); pm_func(termNewApp,"not"); pm_func(termNewAppInnerApp,"or"); checkNumber(termOldApp,3); checkNumber(termNewAppInnerApp,2); // Names as in the documentation proof.pdf final Term t2 = termOldApp.getParameters()[2]; //Check syntactical correctness if (t2 != mSkript.term("false")) { throw new AssertionError("Error 3 at " + rewriteRule); } // or is commutative final Term t0 = termOldApp.getParameters()[0]; if (termNewAppInnerApp.getParameters()[0] != mSkript.term("not",t0) && termNewAppInnerApp.getParameters()[1] != mSkript.term("not",t0)) { throw new AssertionError("Error 4 at " + rewriteRule); } final Term t1 = termOldApp.getParameters()[1]; if (termNewAppInnerApp.getParameters()[0] != mSkript.term("not",t1) && termNewAppInnerApp.getParameters()[1] != mSkript.term("not",t1)) { throw new AssertionError("Error 5 at " + rewriteRule); } } else if (rewriteRule == ":andToOr") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); pm_func(termOldApp, "and"); pm_func(termNewApp, "not"); pm_func(termNewAppInnerApp, "or"); // Check if they are the same // HashSets are needed to allow permutations final HashSet<Term> oldTerms = new HashSet<Term>(); final HashSet<Term> newTermsInner = new HashSet<Term>(); oldTerms.addAll(Arrays.asList(termOldApp.getParameters())); for (int i = 0; i < termNewAppInnerApp.getParameters().length; i++) { final ApplicationTerm termAppTemp = convertApp(termNewAppInnerApp.getParameters()[i]); pm_func(termAppTemp,"not"); newTermsInner.add(termAppTemp.getParameters()[0]); } if (!oldTerms.equals(newTermsInner)) { throw new AssertionError("Error at rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } } else if (rewriteRule == ":xorToDistinct") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp, "xor"); pm_func(termNewApp, "distinct"); if (termOldApp.getParameters().length != termNewApp.getParameters().length) { throw new AssertionError("Error 1 at " + rewriteRule); } for (int i = 0; i < termOldApp.getParameters().length; i++) { if (!termOldApp.getParameters() [i].equals( termNewApp.getParameters()[i])) { throw new AssertionError("Error 2 at " + rewriteRule); } } // Nicer, but didn't work: //if (!termOldApp.getParameters().equals(termNewApp.getParameters())) } else if (rewriteRule == ":impToOr") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp, "=>"); pm_func(termNewApp, "or"); // Check if they are the same // HashSets are needed to allow permutations final HashSet<Term> oldTerms = new HashSet<Term>(); for (int i = 0; i < termOldApp.getParameters().length - 1; i++) { oldTerms.add(termOldApp.getParameters()[i]); } final Term termImp = termOldApp.getParameters()[termOldApp.getParameters().length - 1]; if (termImp != termNewApp.getParameters()[0]) { throw new AssertionError("Error 1 at " + rewriteRule); } final HashSet<Term> newTerms = new HashSet<Term>(); for (int i = 1; i < termNewApp.getParameters().length; i++) { final ApplicationTerm termAppTemp = convertApp(termNewApp.getParameters()[i]); pm_func(termAppTemp,"not"); newTerms.add(termAppTemp.getParameters()[0]); } if (!oldTerms.equals(newTerms)) { throw new AssertionError("Error at rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } } else if (rewriteRule == ":strip") { //Term which has to be stripped, annotated term final AnnotatedTerm stripAnnTerm = convertAnn(termEqApp.getParameters()[0]); if (stripAnnTerm.getSubterm() != termEqApp.getParameters()[1]) { throw new AssertionError("Error: Couldn't verify a strip-rewrite. Those two terms should be the same but arent" + stripAnnTerm.getSubterm() + "vs. " + termEqApp.getParameters()[1] + "."); } } else if (rewriteRule == ":canonicalSum") { final Term termOld = termEqApp.getParameters()[0]; final Term termNew = termEqApp.getParameters()[1]; if (!convertAffineTerm(termOld).equals( convertAffineTerm(termNew))) { throw new AssertionError("Error at " + rewriteRule); } } else if (rewriteRule == ":gtToLeq0" || rewriteRule == ":geqToLeq0" || rewriteRule == ":ltToLeq0" || rewriteRule == ":leqToLeq0") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); //termBeforeRewrite final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); checkNumber(termOldApp,2); checkNumber(termNewApp,1); if (!((rewriteRule == ":gtToLeq0" && pm_func_weak(termOldApp,">")) || (rewriteRule == ":geqToLeq0" && pm_func_weak(termOldApp, ">=")) || (rewriteRule == ":ltToLeq0" && pm_func_weak(termOldApp, "<")) || (rewriteRule == ":leqToLeq0" && pm_func_weak(termOldApp, "<=")))) { throw new AssertionError("Expected not the function symbol " + termOldApp.getFunction().getName() + " for the rule " + rewriteRule + ". \n The term is: " + termEqApp.toString()); } final Term termT1 = termOldApp.getParameters()[0]; //t_1 and t_2 as in the documentation proof.pdf final Term termT2 = termOldApp.getParameters()[1]; // The second term may be a negation ApplicationTerm termNewIneqApp; //the inequality of termAfterRewrite if (rewriteRule == ":ltToLeq0" || rewriteRule == ":gtToLeq0") { pm_func(termNewApp,"not"); termNewIneqApp = convertApp(termNewApp.getParameters()[0]); } else { termNewIneqApp = termNewApp; } pm_func(termNewIneqApp, "<="); checkNumber(termNewIneqApp,2); // Warning: Code almost-duplicates (Random number: 29364) final SMTAffineTerm termAffTemp = convertAffineTerm(termNewIneqApp.getParameters()[1]); isConstant(termAffTemp,Rational.ZERO); final SMTAffineTerm leftside = convertAffineTerm(termNewIneqApp.getParameters()[0]); final SMTAffineTerm termT1Aff = convertAffineTerm(termT1); final SMTAffineTerm termT2Aff = convertAffineTerm(termT2); if (rewriteRule == ":gtToLeq0" || rewriteRule == ":leqToLeq0") { if (!leftside.equals(termT1Aff.add(termT2Aff.negate()))) { throw new AssertionError("Error: Wrong term on the left side of " + "the new inequality. The term was: " + rewriteApp.toStringDirect() + "\n" + "Same should be " + leftside.toStringDirect() + " and " + termT1Aff.add(termT2Aff.negate()).toStringDirect() + "\n" + "Random number: 02653"); } // Then the rule was correctly executed } else { if (!leftside.equals(termT2Aff.add(termT1Aff.negate()))) { throw new AssertionError("Error: Wrong term on the left side of " + "the new inequality. The term was: " + termEqApp.toStringDirect() + "\n" + "Same should be " + leftside.toStringDirect() + " and " + termT2Aff.add(termT1Aff.negate()).toStringDirect() + "\n" + "Random number: 20472"); } // Then the rule was correctly executed } } else if (rewriteRule == ":leqTrue") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); checkNumber(termOldApp,2); pm_func(termOldApp,"<="); final SMTAffineTerm constant = convertAffineTerm( convertConst_Neg(termOldApp.getParameters()[0])); // Rule-Execution was wrong if c > 0 <=> -c < 0 if (constant.negate().getConstant().isNegative()) { throw new AssertionError("Error 2 at " + rewriteRule); } final SMTAffineTerm termTemp = convertAffineTerm(termOldApp.getParameters()[1]); isConstant(termTemp,Rational.ZERO); if (termEqApp.getParameters()[1] != mSkript.term("true")) { throw new AssertionError("Error 4 at " + rewriteRule); } } else if (rewriteRule == ":leqFalse") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"<="); checkNumber(termOldApp, 2); final SMTAffineTerm constant = convertAffineTerm( convertConst_Neg(termOldApp.getParameters()[0])); // Rule-Execution was wrong if c <= 0 <=> c < 0 || c = 0 if (constant.getConstant().isNegative() || isConstant_weak(constant,Rational.ZERO)) { throw new AssertionError("Error 2 at " + rewriteRule); } final SMTAffineTerm termTemp = convertAffineTerm(termOldApp.getParameters()[1]); isConstant(termTemp,Rational.ZERO); if (termEqApp.getParameters()[1] != mSkript.term("false")) { throw new AssertionError("Error 4 at " + rewriteRule); } } else if (rewriteRule == ":desugar") { /* All Int-Parameters of the outermost function * are getting converted into Real-Parameters */ final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); // Both must have the same function symbol pm_func(termOldApp,termNewApp.getFunction().getName()); if (termOldApp.getParameters().length != termNewApp.getParameters().length) { throw new AssertionError("Error 1 in :desugar"); } for (int i = 0; i < termNewApp.getParameters().length; i++) { final Term paramIOld = termOldApp.getParameters()[i]; final Term paramINew = termNewApp.getParameters()[i]; if (!paramIOld.equals(paramINew)) { if (!convertAffineTerm(paramIOld).isIntegral()) { throw new AssertionError("Error 2 in :desugar"); } // Then paramINew has to be either old.0 or (to_real old) // Case 1: (to_real old), Case 2: old.0 boolean correct = false; if (paramINew instanceof ApplicationTerm) { // Case 1 and parts of Case 2: (Just handling of the complete Case 1) final ApplicationTerm paramINewApp = convertApp(paramINew); if (pm_func_weak(paramINewApp,"to_real")) { if (paramIOld.equals( paramINewApp.getParameters()[0])) { correct = true; } else { throw new AssertionError("Error 4 in :desugar"); } } } // Case 2 and parts of Case 1: (Just handling of the complete Case 2) if (convertAffineTerm(paramINew).getSort() == mSkript.sort("Real")) { // Check for equalitiy, ? and ?.0 have to be equal, therefor .equals doesn't work final SMTAffineTerm diffZero = convertAffineTerm(paramINew).add( convertAffineTerm(paramIOld).negate()); if (diffZero.isConstant() && diffZero.getConstant() == Rational.ZERO) { correct = true; } } if (!correct) { throw new AssertionError("Error 5 in :desugar"); } } } } else if (rewriteRule == ":divisible") { // This rule is a combination of 3-4 sub-rules // Declaration of the variables which can be declared for all sub-rules + syntactical check final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); checkNumber(termOldApp,1); final Term termNew = termEqApp.getParameters()[1]; final Term termT = termOldApp.getParameters()[0]; final BigInteger bigIN = termOldApp.getFunction().getIndices()[0]; //bigInteger N pm_func(termOldApp,"divisible"); //Old: termNew instanceof ApplicationTerm if (!termNew.equals(mSkript.term("true")) // NOPMD && !termNew.equals(mSkript.term("false"))) { // Sub-rule 4 final ApplicationTerm termNewApp = convertApp(termNew); pm_func(termNewApp, "="); checkNumber(termNewApp,2); // = and * are commutative ApplicationTerm termNewAppProd; if (termNewApp.getParameters()[0].equals(termT)) { termNewAppProd = convertApp(termNewApp.getParameters()[1]); } else if (termNewApp.getParameters()[1].equals(termT)) { termNewAppProd = convertApp(termNewApp.getParameters()[0]); } else { throw new AssertionError("Error 1 in divisible"); } ApplicationTerm termNewAppDiv = null; // Not nice: Use of null boolean found = false; checkNumber(termNewAppProd, 2); if (termNewAppProd.getParameters()[0] instanceof ConstantTerm && convertConst(termNewAppProd.getParameters()[0]).getValue().equals(bigIN)) { termNewAppDiv = convertApp(termNewAppProd.getParameters()[1]); found = true; } if ((termNewAppProd.getParameters()[1] instanceof ConstantTerm) && convertConst(termNewAppProd.getParameters()[1]).getValue().equals(bigIN)) { termNewAppDiv = convertApp(termNewAppProd.getParameters()[0]); found = true; } checkNumber(termNewAppDiv, 2); if (!found) { throw new AssertionError("Error 2 in divisible"); } pm_func(termNewAppProd, "*"); if (!pm_func_weak(termNewAppDiv, "div") && !pm_func_weak(termNewAppDiv, "/")) { throw new AssertionError("Error 3 in divisible"); } if (!termNewAppDiv.getParameters()[0].equals(termT)) { throw new AssertionError("Error 4 in divisible"); } if (!convertConst(termNewAppDiv.getParameters()[1]).getValue().equals(bigIN)) { throw new AssertionError("Error 5 in divisible"); } } else { final Rational constT = convertAffineTerm(convertConst_Neg(termT)).getConstant(); final Rational constN = Rational.valueOf(bigIN,BigInteger.ONE); if (constT.div(constN).isIntegral() && !termNew.equals(mSkript.term("true"))) { throw new AssertionError("Error 6 in divisible"); } if (!constT.div(constN).isIntegral() && !termNew.equals(mSkript.term("false"))) { throw new AssertionError("Error 7 in divisible"); } // No special treatment of the case n = 1, but it's still correct. } } else if (rewriteRule == ":div1") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"div"); checkNumber(termOldApp, 2); final SMTAffineTerm constant = convertAffineTerm( convertConst_Neg(termOldApp.getParameters()[1])); // Rule-Execution was wrong if c != 1 if (!constant.isConstant()) { throw new AssertionError("Error 1 at " + rewriteRule); } if (!(constant.getConstant().equals(Rational.ONE))) { throw new AssertionError("Error 2 at " + rewriteRule); } if (termEqApp.getParameters()[1] != termOldApp.getParameters()[0]) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":div-1") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"div"); checkNumber(termOldApp, 2); convertConst_Neg(termOldApp.getParameters()[1]); final SMTAffineTerm constant = convertAffineTerm(termOldApp.getParameters()[1]); // Rule-Execution was wrong if c != 1 if (!constant.negate().isConstant()) { throw new AssertionError("Error 1 at " + rewriteRule); } if (!(constant.negate().getConstant().equals(Rational.ONE))) { throw new AssertionError("Error 2 at " + rewriteRule); } if (!convertAffineTerm(termEqApp.getParameters()[1]).negate().equals( convertAffineTerm(termOldApp.getParameters()[0]))) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":divConst") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"div"); checkNumber(termOldApp, 2); convertConst_Neg(termOldApp.getParameters()[0]); convertConst_Neg(termOldApp.getParameters()[1]); final SMTAffineTerm c1 = convertAffineTerm(termOldApp.getParameters()[0]); if (!c1.isConstant()) { throw new AssertionError("Error 1 at " + rewriteRule); } final SMTAffineTerm c2 = convertAffineTerm(termOldApp.getParameters()[1]); if (!c2.isConstant()) { throw new AssertionError("Error 2 at " + rewriteRule); } if (c2.getConstant().equals(Rational.ZERO)) { throw new AssertionError("Error 3 at " + rewriteRule); } final SMTAffineTerm d = convertAffineTerm(termEqApp.getParameters()[1]); if (!c1.isIntegral() || !c2.isIntegral() || !d.isIntegral()) { throw new AssertionError("Error 4 at " + rewriteRule); } if (c2.getConstant().isNegative() && !d.getConstant().equals( c1.getConstant().div(c2.getConstant()).ceil())) { throw new AssertionError("Error 5 at " + rewriteRule); } if (!c2.getConstant().isNegative() && !d.getConstant().equals(c1.getConstant().div( c2.getConstant()).floor())) { throw new AssertionError("Error 6 at " + rewriteRule); } } else if (rewriteRule == ":modulo1") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"mod"); checkNumber(termOldApp, 2); //Check syntactical correctness if (!(termOldApp.getParameters()[0] instanceof ConstantTerm) && !checkInt_weak(termOldApp.getParameters()[0])) { throw new AssertionError("Error 1 at " + rewriteRule); } convertConst(termOldApp.getParameters()[1]); convertConst(termEqApp.getParameters()[1]); final SMTAffineTerm constant1 = convertAffineTerm(termOldApp.getParameters()[1]); if (!(constant1.getConstant().equals(Rational.ONE))) { throw new AssertionError("Error 2 at " + rewriteRule); } final SMTAffineTerm constant0 = convertAffineTerm(termEqApp.getParameters()[1]); if (!(constant0.getConstant().equals(Rational.ZERO))) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":modulo-1") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"mod"); checkNumber(termOldApp, 2); //Check syntactical correctness if (!(termOldApp.getParameters()[0] instanceof ConstantTerm) && !checkInt_weak(termOldApp.getParameters()[0])) { throw new AssertionError("Error 1 at " + rewriteRule); } convertConst_Neg(termOldApp.getParameters()[1]); convertConst(termEqApp.getParameters()[1]); final SMTAffineTerm constantm1 = convertAffineTerm(termOldApp.getParameters()[1]); if (!(constantm1.getConstant().negate().equals(Rational.ONE))) { throw new AssertionError("Error 2 at " + rewriteRule); } final SMTAffineTerm constant0 = convertAffineTerm(termEqApp.getParameters()[1]); if (!(constant0.getConstant().equals(Rational.ZERO))) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":moduloConst") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"mod"); checkNumber(termOldApp,2); //Check syntactical correctness if (!(termOldApp.getParameters()[0] instanceof ConstantTerm) && !checkInt_weak(termOldApp.getParameters()[0])) { throw new AssertionError("Error 1a at " + rewriteRule); } if (!(termOldApp.getParameters()[1] instanceof ConstantTerm) && !checkInt_weak(termOldApp.getParameters()[1])) { throw new AssertionError("Error 1b at " + rewriteRule); } if (!(termEqApp.getParameters()[1] instanceof ConstantTerm) && !checkInt_weak(termEqApp.getParameters()[1])) { throw new AssertionError("Error 1c at " + rewriteRule); } final SMTAffineTerm c2 = convertAffineTerm(termOldApp.getParameters()[1]); if (c2.getConstant().equals(Rational.ZERO)) { throw new AssertionError("Error 2 at " + rewriteRule); } final SMTAffineTerm c1 = convertAffineTerm(termOldApp.getParameters()[0]); final SMTAffineTerm d = convertAffineTerm(termEqApp.getParameters()[1]); if (!c1.isIntegral() || !c2.isIntegral() || !d.isIntegral()) { throw new AssertionError("Error 3 at " + rewriteRule); } if (c2.getConstant().isNegative()) { // d = c1 + c2 * ceil(c1/c2) if (!d.equals(c1.add( c2.mul(c1.div(c2.getConstant()).getConstant().ceil()).negate()))) { throw new AssertionError("Error 4 at " + rewriteRule); } } else { if (!d.equals(c1.add( c2.mul(c1.div(c2.getConstant()).getConstant().floor()).negate()))) { throw new AssertionError("Error 5 at " + rewriteRule); } } } else if (rewriteRule == ":modulo") { final ApplicationTerm termOldMod = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewSum = convertApp(termEqApp.getParameters()[1]); checkNumber(termOldMod, 2); checkNumber(termNewSum, 2); ApplicationTerm termNewProd; Term termNewNotProd; if (termNewSum.getParameters()[0] instanceof ApplicationTerm) { if (pm_func_weak(termNewSum.getParameters()[0],"*")) { termNewProd = convertApp(termNewSum.getParameters()[0]); termNewNotProd = termNewSum.getParameters()[1]; } else { termNewProd = convertApp(termNewSum.getParameters()[1]); termNewNotProd = termNewSum.getParameters()[0]; } } else { termNewProd = convertApp(termNewSum.getParameters()[1]); termNewNotProd = termNewSum.getParameters()[0]; } checkNumber(termNewProd, 2); ApplicationTerm termNewDiv; Term termNewNotDiv; if (termNewProd.getParameters()[0] instanceof ApplicationTerm) { if (pm_func_weak(termNewProd.getParameters()[0],"/") || pm_func_weak(termNewProd.getParameters()[0],"div")) { termNewDiv = convertApp(termNewProd.getParameters()[0]); termNewNotDiv = termNewProd.getParameters()[1]; } else { termNewDiv = convertApp(termNewProd.getParameters()[1]); termNewNotDiv = termNewProd.getParameters()[0]; } } else { termNewDiv = convertApp(termNewProd.getParameters()[1]); termNewNotDiv = termNewProd.getParameters()[0]; } checkNumber(termNewDiv, 2); //ApplicationTerm termNewDiv = convertApp(termNewProd.getParameters()[1]); pm_func(termOldMod,"mod"); pm_func(termNewSum,"+"); pm_func(termNewProd,"*"); if (!pm_func_weak(termNewDiv,"div") && !pm_func_weak(termNewDiv,"/")) { throw new AssertionError("Error 1 at " + rewriteRule); } final Term termOldX = termOldMod.getParameters()[0]; final Term termOldY = termOldMod.getParameters()[1]; if (termNewNotProd != termOldX || !convertAffineTerm(termNewNotDiv).equals( convertAffineTerm(termOldY).negate()) || termNewDiv.getParameters()[0] != termOldX || termNewDiv.getParameters()[1] != termOldY) { throw new AssertionError("Error 2 at " + rewriteRule); } } else if (rewriteRule == ":toInt") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"to_int"); // r and v as in the documentation proof.pdf final Term termV = convertConst_Neg(termEqApp.getParameters()[1]); final Term termR = termOldApp.getParameters()[0]; // r can be a positive/negative fraction // Case A: Positive Integer, Case B: Negative Integer // Case C: Positive Fraction, Case D: Negative Fraction if (termR instanceof ApplicationTerm) { // Case B, C, D: final ApplicationTerm termRApp = convertApp(termR); ApplicationTerm termRInnerApp; if (pm_func_weak(termRApp,"-") && termRApp.getParameters()[0] instanceof ApplicationTerm) { // Case D: termRInnerApp = convertApp(termRApp.getParameters()[0]); pm_func(termRInnerApp,"/"); checkNumber(termRInnerApp,2); convertConst_Neg(termRInnerApp.getParameters()[0]); // Presumably the neg isn't needed convertConst_Neg(termRInnerApp.getParameters()[1]); // Presumably the neg isn't needed } else if (pm_func_weak(termRApp,"/")) { // Case C: pm_func(termRApp,"/"); checkNumber(termRApp,2); convertConst_Neg(termRApp.getParameters()[0]); // Presumably the neg isn't needed convertConst_Neg(termRApp.getParameters()[1]); // Presumably the neg isn't needed } else { // Case B: pm_func(termRApp,"-"); convertConst(termRApp.getParameters()[0]); } } else { // Case A: convertConst(termR); } if (!convertAffineTerm(termR).getConstant().floor().equals( convertAffineTerm(termV).getConstant())) { throw new AssertionError("Error 2 at " + rewriteRule); } /* Not nice: Not checked, if v is an integer and * r a real, but it is still correct. */ } else if (rewriteRule == ":toReal") { final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); pm_func(termOldApp,"to_real"); final Term termOldC = convertConst_Neg(termOldApp.getParameters()[0]); final Term termNewC = convertConst_Neg(termEqApp.getParameters()[1]); if (!convertAffineTerm(termOldC).getConstant().equals( convertAffineTerm(termNewC).getConstant())) { throw new AssertionError("Error 2 at " + rewriteRule); } /* Not nice: Not checked, if cOld is an integer and * cNew a real, but it is still correct. */ } else if (rewriteRule == ":storeOverStore") { System.out.println("\n \n \n Now finally tested: " + rewriteRule); //TODO checkNumber(termEqApp.getParameters(),2); final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termOldAppInnerApp = convertApp(termOldApp.getParameters()[0]); checkNumber(termOldApp.getParameters(), 3); checkNumber(termOldAppInnerApp.getParameters(), 3); checkNumber(termNewApp.getParameters(), 3); pm_func(termOldApp,"store"); pm_func(termOldAppInnerApp,"store"); pm_func(termNewApp,"store"); if (termOldApp.getParameters()[1] != termOldAppInnerApp.getParameters()[1] || termOldApp.getParameters()[1] != termNewApp.getParameters()[1]) { throw new AssertionError("Error 1 at " + rewriteRule); } if (termOldApp.getParameters()[2] != termNewApp.getParameters()[2]) { throw new AssertionError("Error 2 at " + rewriteRule); } if (termOldAppInnerApp.getParameters()[0] != termNewApp.getParameters()[0]) { throw new AssertionError("Error 3 at " + rewriteRule); } } else if (rewriteRule == ":selectOverStore") { System.out.println("\n \n \n Now finally tested: " + rewriteRule); //TODO checkNumber(termEqApp.getParameters(),2); final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final Term termNew = termEqApp.getParameters()[1]; final ApplicationTerm termOldAppInnerApp = convertApp(termOldApp.getParameters()[0]); checkNumber(termOldApp,2); checkNumber(termOldAppInnerApp,3); pm_func(termOldApp,"select"); pm_func(termOldAppInnerApp,"store"); if (termOldApp.getParameters()[1] == termOldAppInnerApp.getParameters()[1]) { if (termOldAppInnerApp.getParameters()[2] != termNew) { throw new AssertionError("Error 2 at " + rewriteRule); } } else { final ApplicationTerm termNewApp = convertApp(termNew); checkNumber(termNewApp.getParameters(),2); pm_func(termNewApp,"select"); final Term c1 = convertConst_Neg(termOldAppInnerApp.getParameters()[1]); final Term c2 = convertConst_Neg(termOldApp.getParameters()[1]); if (c1 == c2) { throw new AssertionError("Error 3 at " + rewriteRule); } if (c2 != termNewApp.getParameters()[1]) { throw new AssertionError("Error 4 at " + rewriteRule); } /* TODO: Checked, if c1 and c2 are distinct constants. */ throw new AssertionError("selectOverStore with distinct indices"); } } else if (rewriteRule == ":storeRewrite") { System.out.println("\n \n \n Now finally tested: " + rewriteRule); //TODO // checkNumber(termEqApp.getParameters(),2); already checked final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); final ApplicationTerm termNewAppInnerApp = convertApp(termNewApp.getParameters()[0]); ApplicationTerm termOldAppInnerApp = null; checkNumber(termNewApp.getParameters(),2); checkNumber(termNewAppInnerApp.getParameters(),2); checkNumber(termOldApp.getParameters(), 2); final Term termA = termNewAppInnerApp.getParameters()[0]; if (termOldApp.getParameters()[0] == termA) { termOldAppInnerApp = convertApp(termOldApp.getParameters()[1]); } else if (termOldApp.getParameters()[1] == termA) { termOldAppInnerApp = convertApp(termOldApp.getParameters()[0]); } else { throw new AssertionError("Error 1 in " + rewriteRule); } checkNumber(termOldAppInnerApp.getParameters(),3); pm_func(termOldApp,"="); pm_func(termOldAppInnerApp,"store"); pm_func(termNewApp,"="); pm_func(termNewAppInnerApp,"select"); final Term termI = termNewAppInnerApp.getParameters()[1]; final Term termV = termNewApp.getParameters()[1]; if (termOldAppInnerApp.getParameters()[0] != termA || termOldAppInnerApp.getParameters()[1] != termI || termOldAppInnerApp.getParameters()[2] != termV) { throw new AssertionError("Error 2 at " + rewriteRule); } /* Not nice: Not checked, if i is an integer, but * it is still correct. */ } else if (rewriteRule == ":expand") { final Term termOld = termEqApp.getParameters()[0]; final Term termNew = termEqApp.getParameters()[1]; if (!convertAffineTerm(termOld).equals( convertAffineTerm(termNew))) { throw new AssertionError("Error in " + rewriteRule); } } else if (rewriteRule == ":flatten") { // TODO: Testing /* Assumption: All nested disjunctions are put into one, i.e. * no new disjunct is itself a disjunction */ final ApplicationTerm termOldApp = convertApp(termEqApp.getParameters()[0]); final ApplicationTerm termNewApp = convertApp(termEqApp.getParameters()[1]); pm_func(termOldApp, "or"); pm_func(termNewApp, "or"); final HashSet<Term> oldDisjuncts = new HashSet<Term>(); final HashSet<Term> newDisjuncts = new HashSet<Term>(); final ArrayList<Term> disjuncts = new ArrayList<Term>(); disjuncts.addAll(Arrays.asList(termOldApp.getParameters())); while (!disjuncts.isEmpty()) { final Term currentDisjunct = disjuncts.remove(disjuncts.size() - 1); boolean currentIsDisjunction = false; if (currentDisjunct instanceof ApplicationTerm) { currentIsDisjunction = pm_func_weak(currentDisjunct, "or"); } if (currentIsDisjunction) { final ApplicationTerm currentDisjunctApp = convertApp(currentDisjunct); disjuncts.addAll(Arrays.asList(currentDisjunctApp.getParameters())); } else { oldDisjuncts.add(currentDisjunct); } } newDisjuncts.addAll(Arrays.asList(termNewApp.getParameters())); if (!oldDisjuncts.equals(newDisjuncts)) { throw new AssertionError("Error in the rule " + rewriteRule + "!\n The term was " + rewriteApp.toStringDirect()); } } else { System.out.println("Can't handle the following rule " + termAppInnerAnn.getAnnotations()[0].getKey() + ", therefore..."); System.out.println("...believed as alright to be rewritten: " + rewriteApp.getParameters()[0].toStringDirect() + " ."); } // The second part, cut the @rewrite and the annotation out, both aren't needed for the @eq-function. // stackPush(innerAnnTerm.getSubterm(), term); } public void walkIntern(ApplicationTerm internApp) { final Term equality = internApp.getParameters()[0]; /* The result is simply the equality. */ stackPush(equality, internApp); if (!isApplication("=", equality) || ((ApplicationTerm) equality).getParameters().length != 2) { reportError("Not an equality"); return; } Term oldTerm = ((ApplicationTerm) equality).getParameters()[0]; Term newTerm = ((ApplicationTerm) equality).getParameters()[1]; boolean isNegated = isApplication("not", newTerm); if (isNegated) { newTerm = ((ApplicationTerm) newTerm).getParameters()[0]; } newTerm = unquote(newTerm); if (isApplication("not", oldTerm)) { oldTerm = ((ApplicationTerm) oldTerm).getParameters()[0]; isNegated ^= true; } /* check if this was just introducing a quote */ if (oldTerm == newTerm && !isNegated) { return; } /* Check for normalization of <= */ if (isApplication("<=", oldTerm) || isApplication("<", oldTerm)) { final Term[] oldArgs = ((ApplicationTerm) oldTerm).getParameters(); SMTAffineTerm zero = convertAffineTerm(oldArgs[1]); if (oldArgs.length != 2 || !zero.isConstant() || !zero.getConstant().equals(Rational.ZERO)) { reportError("Not a normalized <= on LHS: " + equality); return; } final Term[] newArgs = ((ApplicationTerm) newTerm).getParameters(); zero = convertAffineTerm(newArgs[1]); if (newArgs.length != 2 || !zero.isConstant() || !zero.getConstant().equals(Rational.ZERO)) { reportError("Not a normalized <= on RHS: " + equality); return; } SMTAffineTerm oldAffine = convertAffineTerm(oldArgs[0]); SMTAffineTerm newAffine = convertAffineTerm(newArgs[0]); if (oldAffine.isConstant() || newAffine.isConstant()) { reportError("Invalid @intern rule " + equality); } if (!oldAffine.getGcd().equals(Rational.ONE)) { oldAffine = oldAffine.mul(oldAffine.getGcd().inverse()); } if (!newAffine.getGcd().equals(Rational.ONE)) { reportError("Not normalized RHS: " + equality); return; } if (newArgs[0].getSort().getName().equals("Int")) { if (!isApplication("<=", oldTerm) || !isApplication("<=", newTerm)) { return; } oldAffine = oldAffine.add( oldAffine.getConstant().frac().negate()); } boolean newIsStrict = isApplication("<", newTerm); if (isNegated) { newAffine = newAffine.negate(); if (newArgs[0].getSort().getName().equals("Int")) { newAffine = newAffine.add(Rational.ONE); // x > 0 iff -x + 1 <= 0) } else { newIsStrict ^= true; } } if (!oldAffine.equals(newAffine) || newIsStrict != isApplication("<", oldTerm)) { reportError("LHS and RHS not equal: " + oldAffine + " != " + newAffine + " in " + equality); } return; } /* Check for normalization of = */ if (isApplication("=", oldTerm) && isApplication("=", newTerm) && !isNegated) { final Term[] oldArgs = ((ApplicationTerm) oldTerm).getParameters(); final Term[] newArgs = ((ApplicationTerm) newTerm).getParameters(); if (oldArgs.length != 2 || newArgs.length != 2) { reportError("Not a binary equality rewrite: " + equality); return; } /* Check for reversed CC equalities */ if (oldArgs[0] == newArgs[1] && oldArgs[1] == newArgs[0]) { return; } /* Otherwise this is an CC to LA intern */ final SMTAffineTerm zero = convertAffineTerm(newArgs[1]); if (newArgs.length != 2 || !zero.isConstant() || !zero.getConstant().equals(Rational.ZERO)) { reportError("Not a normalized = on RHS: " + equality); return; } SMTAffineTerm oldAffine = convertAffineTerm(oldArgs[0]).add( convertAffineTerm(oldArgs[1]).negate()); final SMTAffineTerm newAffine = convertAffineTerm(newArgs[0]); if (oldAffine.isConstant() || newAffine.isConstant()) { reportError("Invalid @intern rule " + equality); } if (!oldAffine.getGcd().equals(Rational.ONE)) { oldAffine = oldAffine.mul(oldAffine.getGcd().inverse()); } if (!newAffine.getGcd().equals(Rational.ONE)) { reportError("Not normalized RHS: " + equality); return; } if (!oldAffine.equals(newAffine) && !oldAffine.negate().equals(newAffine)) { reportError("LHS and RHS not equal: " + oldAffine + " != " + newAffine + " in " + equality); } return; } /* Check for boolean literals */ if (!isApplication("=", oldTerm) && isApplication("=", newTerm)) { final Term[] sides = ((ApplicationTerm) newTerm).getParameters(); if (sides.length == 2 && isApplication("true", sides[1]) && sides[0] == oldTerm) { return; } } reportError("Unhandled @intern rule " + equality.toStringDirect()); } /** * Convert a clause term into an Array of terms, one entry for each * disjunct. This also handles singleton and empty clause correctly. * @param clauseTerm The term representing a clause. * @return The disjuncts of the clause. */ private Term[] termToClause(Term clauseTerm) { assert clauseTerm != null && clauseTerm.getSort().getName() == "Bool"; if (isApplication("or", clauseTerm)) { return ((ApplicationTerm) clauseTerm).getParameters(); } else if (isApplication("false", clauseTerm)) { return new Term[0]; } else { /* in all other cases, this is a singleton clause. */ return new Term[] { clauseTerm }; } } /** * Convert a collection of terms into a clause term. This also handles * singleton and empty clause correctly. * @param disjuncts the disjuncts of the clause. * @return a term representing the clause. */ private Term clauseToTerm(Collection<Term> disjuncts) { if (disjuncts.size() <= 1) { if (disjuncts.isEmpty()) { return mSkript.term("false"); } else { return disjuncts.iterator().next(); } } else { final Term[] args = disjuncts.toArray(new Term[disjuncts.size()]); return mSkript.term("or", args); } } /** * Handle the resolution rule. The stack should contain the converted * input clauses. * @param resApp The <code>{@literal @}res</code> application from the * original proof. */ public void walkResolution(ApplicationTerm resApp) { final Term[] termArgs = resApp.getParameters(); /* Get the pivot literals (pivots[0] is always null) * and retrieve the calculations for the proofs from the stack. */ final Term[] pivots = new Term[termArgs.length]; final Term[] clauseTerms = new Term[termArgs.length]; for (int i = termArgs.length - 1; i >= 1; i--) { final AnnotatedTerm pivotPlusProof = (AnnotatedTerm) termArgs[i]; /* Check if it is a pivot-annotation */ if (pivotPlusProof.getAnnotations().length != 1 || pivotPlusProof.getAnnotations()[0].getKey() != ":pivot") { throw new IllegalArgumentException( "Annotation :pivot expected"); } /* Just take the first annotation, because * it should have exactly one - otherwise * the proof-checker throws an error */ pivots[i] = (Term) pivotPlusProof.getAnnotations()[0].getValue(); clauseTerms[i] = stackPopCheck(pivotPlusProof.getSubterm()); } // The 0th argument is the main clause an has no pivot. clauseTerms[0] = stackPopCheck(termArgs[0]); /* allDisjuncts is the currently computed resolution result. */ final HashSet<Term> allDisjuncts = new HashSet<Term>(); /* Now get the disjuncts of the first argument. */ allDisjuncts.addAll(Arrays.asList(termToClause(clauseTerms[0]))); /* Resolve the other clauses */ for (int i = 1; i < termArgs.length; i++) { /* Remove the negated pivot from allDisjuncts */ if (!allDisjuncts.remove(negate(pivots[i]))) { reportWarning("Could not find negated pivot in main clause"); } /* For each clause check for the pivot and add all other * literals. */ final Term[] clause = termToClause(clauseTerms[i]); boolean pivotFound = false; for (final Term t : clause) { if (t == pivots[i]) { pivotFound = true; } else { allDisjuncts.add(t); } } if (!pivotFound) { reportWarning("Could not find pivot in secondary clause"); } } stackPush(clauseToTerm(allDisjuncts), resApp); } public void walkEquality(ApplicationTerm eqApp) { final Term[] eqParams = eqApp.getParameters(); /* Expected: The first argument is a boolean formula each other * argument a binary equality. * * Each not-first argument describes a rewrite of a (sub)term of the * first term. Important is the order, e.g. the rewrite of the second * argument has to be executed before the rewrite of the third * argument! */ /* Get the rewrite equalities from the stack. These should be * proof nodes that come from an @intern or @rewrite rule. */ final Term[] rewrites = new Term[eqParams.length - 1]; for (int i = eqParams.length - 1; i >= 1; i--) { rewrites[i - 1] = stackPopCheck(eqParams[i]); } /* the first argument is the term on which rewrites are applied */ Term termEdit; termEdit = stackPopCheck(eqParams[0]); // Rewriting the term for (final Term rewrite : rewrites) { pm_func(rewrite, "="); final Term[] rewriteSides = ((ApplicationTerm) rewrite).getParameters(); if (rewriteSides.length != 2) { reportError("Rewrite equality with more than two sides?"); } termEdit = rewriteTerm(termEdit, rewriteSides[0], rewriteSides[1]); } stackPush(termEdit, eqApp); } public void walkClause(ApplicationTerm clauseApp) { /* Check if the parameters of clause are two disjunctions (which they should be) */ final Term clauseTerm1 = stackPopCheck(clauseApp.getParameters()[0]); final Term clauseTerm2 = clauseApp.getParameters()[1]; // The disjuncts of each parameter final HashSet<Term> param1Disjuncts = new HashSet<Term>( Arrays.asList(termToClause(clauseTerm1))); final HashSet<Term> param2Disjuncts = new HashSet<Term>( Arrays.asList(termToClause(clauseTerm2))); /* Check if the clause operation was correct. Each later disjunct has to be in * the first disjunction and reverse. */ if (!param1Disjuncts.equals(param2Disjuncts)) { reportError("The clause-operation didn't permute correctly!"); } stackPush(clauseTerm2, clauseApp); } public void walkSplit(ApplicationTerm splitApp) { // term is just the first term // The first term casted to an ApplicationTerm final AnnotatedTerm termAppSplitInnerAnn = (AnnotatedTerm) splitApp.getParameters()[0]; final ApplicationTerm termSplitReturnApp = (ApplicationTerm) splitApp.getParameters()[1]; final ApplicationTerm termOldCalcApp = (ApplicationTerm) stackPopCheck(termAppSplitInnerAnn.getSubterm()); final Term termSplitReturnInner = termSplitReturnApp.getParameters()[0]; final String splitRule = termAppSplitInnerAnn.getAnnotations()[0].getKey(); if (mDebug.contains("currently")) { System.out.println("Split-Rule: " + splitRule); } if (mDebug.contains("hardTerm")) { System.out.println("Term: " + splitApp.toStringDirect()); } if (splitRule == ":notOr") { if (mDebug.contains("split_notOr")) { System.out.println("Meldung: Wandle um (berechnet):"); System.out.println(termOldCalcApp.toStringDirect()); System.out.println("in"); System.out.println(splitApp.getParameters()[1].toStringDirect()); } pm_func(termSplitReturnApp,"not"); if (!pm_func_weak(termOldCalcApp, "not")) { System.out.println("Breakpoint"); } pm_func(termOldCalcApp, "not"); final ApplicationTerm termOldCalcAppInnerApp = convertApp(termOldCalcApp.getParameters()[0]); pm_func(termOldCalcAppInnerApp, "or"); for (final Term disjunct : termOldCalcAppInnerApp.getParameters()) { if (disjunct == termSplitReturnInner) { stackPush(splitApp.getParameters()[1], splitApp); return; } } throw new AssertionError("Error in \"split\""); } else if (splitRule == ":=+1" || splitRule == ":=+2") { int rr = 2; if (splitRule == ":=+1") { rr = 1; } // checkNumber(termApp.getParameters(),2); already checked final ApplicationTerm termOldApp = termOldCalcApp; final ApplicationTerm termNewApp = termSplitReturnApp; checkNumber(termOldApp,2); checkNumber(termNewApp,2); //The term (F1 or F2) which is negated in new term final Term termNewNeg = termOldApp.getParameters()[2 - rr]; pm_func(termOldApp,"="); pm_func(termNewApp,"or"); if (termNewApp.getParameters()[rr - 1] != mSkript.term("not",termNewNeg) && termNewApp.getParameters()[2 - rr] != mSkript.term("not",termNewNeg)) { throw new AssertionError("Error 1 at " + splitRule); } final Term termNewPos = termOldApp.getParameters()[rr - 1]; if (termNewApp.getParameters()[rr - 1] != termNewPos && termNewApp.getParameters()[2 - rr] != termNewPos) { throw new AssertionError("Error 2 at " + splitRule); } /* Not nice: Not checked, if the F are boolean, which * they should. */ stackPush(splitApp.getParameters()[1], splitApp); return; } else if (splitRule == ":=-1" || splitRule == ":=-2") { checkNumber(splitApp,2); final ApplicationTerm termOldApp = termOldCalcApp; final ApplicationTerm termNewApp = termSplitReturnApp; checkNumber(termOldApp,1); checkNumber(termNewApp,2); final ApplicationTerm termOldAppInnerApp = convertApp(termOldApp.getParameters()[0]); checkNumber(termOldAppInnerApp,2); final Term termF1 = termOldAppInnerApp.getParameters()[0]; final Term termF2 = termOldAppInnerApp.getParameters()[1]; pm_func(termOldApp,"not"); pm_func(termOldAppInnerApp,"="); pm_func(termNewApp,"or"); if (splitRule == ":=-1") { // or is commutative if (termNewApp.getParameters()[0] != termF1 && termNewApp.getParameters()[1] != termF1) { throw new AssertionError("Error 1 at " + splitRule); } if (termNewApp.getParameters()[0] != termF2 && termNewApp.getParameters()[1] != termF2) { throw new AssertionError("Error 2 at " + splitRule); } } else { final ApplicationTerm termNewAppInner1App = convertApp(termNewApp.getParameters()[0]); final ApplicationTerm termNewAppInner2App = convertApp(termNewApp.getParameters()[1]); pm_func(termNewAppInner1App,"not"); pm_func(termNewAppInner2App,"not"); // or is commutative if (termNewAppInner1App.getParameters()[0] != termF1 && termNewAppInner2App.getParameters()[0] != termF1) { throw new AssertionError("Error 3 at " + splitRule); } if (termNewAppInner1App.getParameters()[0] != termF2 && termNewAppInner2App.getParameters()[0] != termF2) { throw new AssertionError("Error 4 at " + splitRule); } } /* Not nice: Not checked, if the F are boolean, which * they should. */ stackPush(splitApp.getParameters()[1], splitApp); return; } else if (splitRule == ":ite+1" || splitRule == ":ite+2") { checkNumber(splitApp,2); final ApplicationTerm termOldApp = termOldCalcApp; final ApplicationTerm termNewApp = termSplitReturnApp; checkNumber(termOldApp,3); checkNumber(termNewApp,2); final Term termF1 = termOldApp.getParameters()[0]; final Term termF2 = termOldApp.getParameters()[1]; final Term termF3 = termOldApp.getParameters()[2]; pm_func(termOldApp,"ite"); pm_func(termNewApp,"or"); if (splitRule == ":ite+2") { // or is commutative if (termNewApp.getParameters()[0] != termF1 && termNewApp.getParameters()[1] != termF1) { throw new AssertionError("Error 1a at " + splitRule); } if (termNewApp.getParameters()[0] != termF3 && termNewApp.getParameters()[1] != termF3) { throw new AssertionError("Error 1b at " + splitRule); } } else { if (termNewApp.getParameters()[0] != termF2 && termNewApp.getParameters()[1] != termF2) { throw new AssertionError("Error 2a at " + splitRule); } if (termNewApp.getParameters()[0] != mSkript.term("not", termF1) && termNewApp.getParameters()[1] != mSkript.term("not", termF1)) { throw new AssertionError("Error 2b at " + splitRule); } } /* Not nice: Not checked, if the F are boolean, which * they should. */ stackPush(splitApp.getParameters()[1], splitApp); return; } else if (splitRule == ":ite-1" || splitRule == ":ite-2") { checkNumber(splitApp,2); final ApplicationTerm termOldApp = termOldCalcApp; final ApplicationTerm termNewApp = termSplitReturnApp; checkNumber(termOldApp,1); checkNumber(termNewApp,2); final ApplicationTerm termOldAppInnerApp = convertApp(termOldApp.getParameters()[0]); checkNumber(termOldAppInnerApp,3); final Term termF1 = termOldAppInnerApp.getParameters()[0]; final Term termF2 = termOldAppInnerApp.getParameters()[1]; final Term termF3 = termOldAppInnerApp.getParameters()[2]; pm_func(termOldApp,"not"); pm_func(termOldAppInnerApp,"ite"); pm_func(termNewApp,"or"); if (splitRule == ":ite-2") { // or is commutative if (termNewApp.getParameters()[0] != termF1 && termNewApp.getParameters()[1] != termF1) { throw new AssertionError("Error 1 at " + splitRule); } if (termNewApp.getParameters()[0] != mSkript.term("not", termF3) && termNewApp.getParameters()[1] != mSkript.term("not", termF3)) { throw new AssertionError("Error 2 at " + splitRule); } } else { final ApplicationTerm termNewAppInner2App = convertApp(termNewApp.getParameters()[1]); final ApplicationTerm termNewAppInner1App = convertApp(termNewApp.getParameters()[0]); pm_func(termNewAppInner1App,"not"); pm_func(termNewAppInner2App,"not"); checkNumber(termNewAppInner1App, 1); checkNumber(termNewAppInner2App, 1); // or is commutative if (termNewAppInner1App.getParameters()[0] != termF2 && termNewAppInner2App.getParameters()[0] != termF1) { throw new AssertionError("Error 3 at " + splitRule); } if (termNewAppInner1App.getParameters()[0] != termF1 && termNewAppInner2App.getParameters()[0] != termF2) { throw new AssertionError("Error 4 at " + splitRule); } } /* Not nice: Not checked, if the F are boolean, which * they should. */ stackPush(splitApp.getParameters()[1], splitApp); return; } else { throw new AssertionError("Error: The following split-rule " + "is unknown: " + splitRule); } } public void stackPush(Term pushTerm, ApplicationTerm keyTerm) { mCacheConv.put(keyTerm, pushTerm); mStackResults.push(pushTerm); mStackResultsDebug.push(keyTerm); } public Term stackPopCheck(Term expected) { if (mStackResults.size() != mStackResultsDebug.size()) { throw new AssertionError( "The debug-stack and the result-stack have different size"); } final Term returnTerm = mStackResults.pop(); final Term debugTerm = mStackResultsDebug.pop(); if (mCacheConv.get(debugTerm) != returnTerm) { throw new AssertionError("The debugger couldn't associate " + returnTerm.toStringDirect() + " with " + debugTerm.toStringDirect()); } if (expected != null && debugTerm != expected) { throw new AssertionError("Unexpected Term on proofchecker stack."); } return returnTerm; } public Term rewriteTerm(final Term termOrig, final Term termDelete, final Term termInsert) { return new TermTransformer() { private boolean isQuoted(Term t) { if (t instanceof AnnotatedTerm) { final AnnotatedTerm annot = (AnnotatedTerm) t; for (final Annotation a : annot.getAnnotations()) { if (a.getKey().equals(":quoted")) { return true; } } } return false; } @Override public void convert(Term t) { if (t == termDelete) { setResult(termInsert); } else if (isQuoted(t)) { setResult(t); } else { super.convert(t); } } } .transform(termOrig); } /* Convert a term to an SMTAffineTerm * @param term The term to convert. * @return The converted term. */ SMTAffineTerm convertAffineTerm(Term term) { return SMTAffineTerm.create(mAffineConverter.transform(term)); } ApplicationTerm convertApp(Term term, String debugString) { if (mDebug.contains("convertApp")) { System.out.println("Der untere Aufruf hat die ID: " + debugString); } return convertApp(term); } ApplicationTerm convertApp(Term term) { if (mDebug.contains("convertApp")) { System.out.println("Aufruf"); } if (!(term instanceof ApplicationTerm)) { throw new AssertionError("Error: The following term should be an ApplicationTerm, " + "but is of the class " + term.getClass().getSimpleName() + ".\n" + "The term was: " + term.toString()); } return (ApplicationTerm) term; } ApplicationTerm convertApp_hard(Term term) { if (term instanceof AnnotatedTerm) { return convertApp(((AnnotatedTerm) term).getSubterm(), "annot"); } return convertApp(term, "hard"); } AnnotatedTerm convertAnn(Term term) { if (!(term instanceof AnnotatedTerm)) { throw new AssertionError("Error: The following term should be an AnnotatedTerm, " + "but is of the class " + term.getClass().getSimpleName() + ".\n" + "The term was: " + term.toString()); } return (AnnotatedTerm) term; } ConstantTerm convertConst(Term term) { if (!(term instanceof ConstantTerm)) { throw new AssertionError("Error: The following term should be a ConstantTerm, " + "but is of the class " + term.getClass().getSimpleName() + ".\n" + "The term was: " + term.toString()); } return (ConstantTerm) term; } Term convertConst_Neg(Term term) { if (term instanceof ConstantTerm) { return term; } // Then it must be a negative number final ApplicationTerm termApp = convertApp(term); pm_func(termApp,"-"); if (termApp.getParameters()[0] instanceof ConstantTerm) { return termApp; } throw new AssertionError("Error: The following term should be a ConstantTerm, " + "but is of the class " + term.getClass().getSimpleName() + ".\n" + "The term was: " + term.toString()); } boolean checkInt_weak(Term term) { if (term.getSort() == mSkript.sort("Int")) { return true; } // Then it must be a negative Integer final ApplicationTerm termApp = convertApp(term); pm_func(termApp,"-"); if (termApp.getParameters()[0].getSort() == mSkript.sort("Int")) { return true; } return false; // throw new AssertionError("Error: The following term should be an Integer, " // + "but is of the sort " + term.getSort().getName() + ".\n" // + "The term was: " + term.toString()); } // Now some pattern-match-functions. //Throws an error if the pattern doesn't match void pm_func(ApplicationTerm termApp, String pattern) { if (!termApp.getFunction().getName().equals(pattern)) { throw new AssertionError("Error: The pattern \"" + pattern + "\" was supposed to be the function symbol of " + termApp.toStringDirect() + "\n" + "Instead it was " + termApp.getFunction().getName()); } } void pm_func(Term term, String pattern) { pm_func(convertApp(term),pattern); } boolean pm_func_weak(ApplicationTerm termApp, String pattern) { return termApp.getFunction().getName().equals(pattern); } boolean pm_func_weakest(Term term, String pattern) { if (term instanceof ApplicationTerm) { return pm_func_weak((ApplicationTerm) term, pattern); } return false; } // Does this function make any sense? boolean pm_func_weak(Term term, String pattern) { if (term instanceof ApplicationTerm) { return pm_func_weak((ApplicationTerm) term, pattern); } throw new AssertionError("Expected an ApplicationTerm in func_weak!"); } void pm_annot(AnnotatedTerm termAnn, String pattern) { if (termAnn.getAnnotations()[0].getKey() != pattern) { throw new AssertionError("Error: The pattern \"" + pattern + "\" was supposed to be the annotation of " + termAnn.toString() + "\n" + "Instead it was " + termAnn.getAnnotations()[0].toString()); } if (termAnn.getAnnotations().length != 1) { throw new AssertionError("Error: A term has " + termAnn.getAnnotations().length + " annotations," + ", but was supposed to have just one."); } } void checkNumber(Term[] termArray, int n) { if (termArray.length < n) { System.out.println("The array: [..."); for (final Term el: termArray) { System.out.println(el.toStringDirect()); } System.out.println("...]"); throw new AssertionError("Error: " + "The array is to short!" + "\n It should have length " + n); } } void checkNumber(ApplicationTerm termApp, int n) { if (termApp.getParameters().length < n) { throw new AssertionError("Error: " + "The parameter-array of " + termApp.toStringDirect() + " is to short!" + "\n It should have length " + n); } } ApplicationTerm uniformizeInEquality(ApplicationTerm termApp) { if (!pm_func_weak(termApp, "<=") && !pm_func_weak(termApp, "<") && !pm_func_weak(termApp, ">=") && !pm_func_weak(termApp, ">") && !pm_func_weak(termApp, "=") && !pm_func_weak(termApp, "not")) { throw new AssertionError("Error 0 in uniformizeInequality"); } // Get the inequality ApplicationTerm termIneq; final boolean negated = pm_func_weak(termApp, "not"); if (negated) { termIneq = convertApp_hard(termApp.getParameters()[0]); } else { termIneq = termApp; } String relation = termIneq.getFunction().getName(); checkNumber(termIneq,2); // Take everything to the left side // Convert the negation into the inequality if (negated) { if (relation == "<=") { relation = ">"; } else if (relation == ">=") { relation = "<"; } else if (relation == "<") { relation = ">="; } else if (relation == ">") { relation = "<="; } else { throw new AssertionError("Error 1 in uniformizeInequality"); } } final SMTAffineTerm termLeft = convertAffineTerm(termIneq.getParameters()[0]); final SMTAffineTerm termRight = convertAffineTerm(termIneq.getParameters()[1]); // Convert: >= to <= and > to < SMTAffineTerm termLeftNew = termLeft.add(termRight.negate()); if (relation == ">=") { termLeftNew = termLeftNew.negate(); relation = "<="; } else if (relation == ">") { termLeftNew = termLeftNew.negate(); relation = "<"; } // Extra-Case for Integers if (onlyInts(termLeftNew) && relation == "<") { termLeftNew = termLeftNew.add(Rational.ONE); relation = "<="; } // Now build the to-be-returned term final Term[] params = new Term[2]; params[0] = termLeftNew; if (!termLeftNew.getSort().isNumericSort()) { throw new AssertionError("Error 2 in uniformizeInequality"); } params[1] = Rational.ZERO.toTerm(termLeftNew.getSort()); return convertApp(mSkript.term(relation, params), "unif2"); } boolean onlyInts(Term term) { if (term instanceof AnnotatedTerm) { return onlyInts(((AnnotatedTerm) term).getSubterm()); } else if (term instanceof ApplicationTerm) { final ApplicationTerm termApp = convertApp(term); for (final Term param : termApp.getParameters()) { if (!onlyInts(param)) { return false; } } return true; } else if (term instanceof SMTAffineTerm) { final SMTAffineTerm termAff = (SMTAffineTerm) term; return termAff.isIntegral(); } else { // So the term is constant return term.getSort().equals(mSkript.sort("Int")); } } void isConstant(SMTAffineTerm term, Rational constant) { if (!isConstant_weak(term, constant)) { throw new AssertionError("The following term should be the " + "constant " + constant.toString() + " but isn't: " + term.toStringDirect()); } } boolean isConstant_weak(SMTAffineTerm term, Rational constant) { if (!term.isConstant() || term.getConstant() != constant) { return false; } return true; } boolean uniformedSame(ApplicationTerm term1, ApplicationTerm term2) { if (term1.equals(term2)) { return true; } final SMTAffineTerm term1leftAff = convertAffineTerm(term1.getParameters()[0]); final SMTAffineTerm term2leftAff = convertAffineTerm(term1.getParameters()[0]); if (term1leftAff.equals(term2leftAff)) { return true; //Should be unreachable } // Maybe one side of the equation is the negation of the other if (term1leftAff.equals(term2leftAff.negate())) { return true; //Should be unreachable } return false; } boolean termITEHelper_isEqual(Term termNeg, Term termGoal) { if (termNeg == termGoal) { return true; } final ApplicationTerm termNegApp = convertApp(termNeg); pm_func(termNegApp,"not"); checkNumber(termNegApp,1); return (!termITEHelper_isEqual(termNegApp.getParameters()[0], termGoal)); } Term splitNotOrHelper_pushNotInside(Term term) { if (!(term instanceof ApplicationTerm)) { return term; } final ApplicationTerm termApp = convertApp(term); if (!pm_func_weak(termApp,"not")) { final Term[] paramsCalc = new Term[termApp.getParameters().length]; for (int i = 0; i < termApp.getParameters().length; i++) { paramsCalc[i] = splitNotOrHelper_pushNotInside(termApp.getParameters()[i]); } return mSkript.term(termApp.getFunction().getName(), paramsCalc); } pm_func(termApp, "not"); // Should be impossible to throw an error checkNumber(termApp, 1); if (!(termApp.getParameters()[0] instanceof ApplicationTerm)) { return term; } final ApplicationTerm termAppInnerApp = convertApp(termApp.getParameters()[0]); if (pm_func_weak(termAppInnerApp, "not")) { return splitNotOrHelper_pushNotInside(termAppInnerApp.getParameters()[0]); } if (pm_func_weak(termAppInnerApp, "or")) { final Term[] paramsCalc = new Term[termAppInnerApp.getParameters().length]; for (int i = 0; i < paramsCalc.length; i++) { paramsCalc[i] = splitNotOrHelper_pushNotInside( mSkript.term("not", termAppInnerApp.getParameters()[i])); } return mSkript.term("and", paramsCalc); } return term; } ArrayList<Term> splitNotOrHelper_getConjunctsPushed(Term term) { /* Important: * Assumes that the input-term is an * output of the pushNot-helper-function */ final ArrayList<Term> termRet = new ArrayList<Term>(); if (!pm_func_weakest(term, "and")) { termRet.add(term); return termRet; } // So the term has the form (and ... ) final ApplicationTerm termApp = convertApp(term); for (final Term param : termApp.getParameters()) { termRet.addAll(splitNotOrHelper_getConjunctsPushed(param)); } return termRet; } public Term unquote(Term quotedTerm) { if (quotedTerm instanceof AnnotatedTerm) { final AnnotatedTerm annTerm = (AnnotatedTerm) quotedTerm; final Annotation[] annots = annTerm.getAnnotations(); if (annots.length == 1 && annots[0].getKey() == ":quoted") { final Term result = annTerm.getSubterm(); return result; } } reportError("Expected quoted literal, but got " + quotedTerm); return quotedTerm; } /** * Checks if a term is an application of an internal function symbol. * @param funcSym the expected function symbol. * @param term the term to check. * @return true if term is an application of funcSym. */ public boolean isApplication(String funcSym, Term term) { if (term instanceof ApplicationTerm) { final ApplicationTerm appTerm = (ApplicationTerm) term; final FunctionSymbol func = appTerm.getFunction(); if (func.isIntern() && func.getName().equals(funcSym)) { return true; } } return false; } private List<Term> collectArguments(String fun, Term term) { final ArrayDeque<Term> todo = new ArrayDeque<Term>(); final ArrayList<Term> result = new ArrayList<Term>(); todo.addLast(term); while (!todo.isEmpty()) { final Term t = todo.removeLast(); if (t instanceof ApplicationTerm) { final ApplicationTerm appTerm = (ApplicationTerm) t; if (appTerm.getFunction().getName() == fun) { final Term[] args = appTerm.getParameters(); for (int i = args.length - 1; i >= 0; i--) { todo.addLast(args[i]); } continue; } } result.add(t); } return result; } public boolean checkOrMinus(Term orTerm, Term literal) { final List<Term> params = collectArguments("or", orTerm); if (params.size() > 1 && params.contains(negate(literal))) { return true; } return false; } }