/* * Copyright (C) 2012-2013 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.proofcheck; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.Sort; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.Theory; /** * This class is used to convert a resolution proof step. * * @author Christian Schilling */ public class ResolutionConverter extends AConverter { // true iff fast proofs shall be printed private final boolean mFastProofs; // appendable for the lemmata private final Appendable mLemmaAppendable; // map from pattern to lemma number private final HashMap<ResolutionPattern, Integer> mPattern2lemma; // number of lemmata already proven private int mLemmaCount; // prefix resolution placeholder variables use private static final String RESOLUTION_L_PREFIX = "l"; private static final String RESOLUTION_C_PREFIX = "c"; private static final String RESOLUTION_R_PREFIX = "r"; private static final String RESOLUTION_PIVOT_L = "pl"; private static final String RESOLUTION_PIVOT_R = "pr"; /** * @param appendable appendable to write the proof to * @param theory the theory * @param converter term converter * @param simplifier computation simplifier * @param fastProofs true iff fast proofs shall be printed * @param lemmaAppendable the theory file for the lemmata */ public ResolutionConverter(final Appendable appendable, final Theory theory, final TermConverter converter, final ComputationSimplifier simplifier, final boolean fastProofs, final Appendable lemmaAppendable) { super(appendable, theory, converter, simplifier); mFastProofs = fastProofs; mPattern2lemma = new HashMap<ResolutionPattern, Integer>(); mLemmaAppendable = lemmaAppendable; mLemmaCount = 0; } /** * This method creates the resolution result. * * It first detects if the proof pattern already exists and in this case * just prints a proof by the lemma. Else a new lemma is written. * In both cases the result has to be computed anyway, since the other * resolution proofs need the information. * * @param first the first (possibly singleton) disjunction * @param second the second (possibly singleton) disjunction * @param pivot the pivot * @return the resulting (possibly singleton or 0-ary) disjunction */ public Term convert(final Term first, final Term second, final Term pivot) { // create a pattern final PatternSwapWrapper wrapper = createWrapper(first, second, pivot); // check if the lemma was already proven Integer lemmaNumber = wrapper.isStoreCandidate() ? mPattern2lemma.get(wrapper.mPatternWrap) : null; // lemma is new, create it if (lemmaNumber == null) { lemmaNumber = ++mLemmaCount; createLemma(wrapper, lemmaNumber); // store the lemma if it is small enough if (wrapper.isStoreCandidate()) { mPattern2lemma.put(wrapper.mPatternWrap, lemmaNumber); } } final Term result = createResult(wrapper.mPatternWrap, wrapper.mSwap ? second : first, wrapper.mSwap ? first : second, wrapper.mSharedTerms.length); // write the proof by lemma to the file writeProof(wrapper.mSwap, wrapper.mDoubleNegation, lemmaNumber, result); return result; } /** * This method creates the pattern data structure. This is used for both * checking if the lemma has already been proven and for creating a new * lemma. * * @param first the first disjunction * @param second the second disjunction * @param pivot the pivot * @return a wrapper with the pattern and swapping information */ private PatternSwapWrapper createWrapper(Term first, Term second, final Term pivot) { // find out the size int firstSize = 1, secondSize = 1; if (first instanceof ApplicationTerm) { if (((ApplicationTerm)first).getFunction() == mTheory.mOr) { firstSize = ((ApplicationTerm)first).getParameters().length; } } else { assert ((first instanceof AnnotatedTerm) && (((AnnotatedTerm)first).getAnnotations().length == 1) && (((AnnotatedTerm)first).getAnnotations()[0].getKey() == ":quoted")); } if (second instanceof ApplicationTerm) { if (((ApplicationTerm)second).getFunction() == mTheory.mOr) { secondSize = ((ApplicationTerm)second).getParameters().length; } } else { assert ((second instanceof AnnotatedTerm) && (((AnnotatedTerm)second).getAnnotations().length == 1) && (((AnnotatedTerm)second).getAnnotations()[0].getKey() == ":quoted")); } // make sure the first term is the bigger one final Term pivotFirst, pivotSecond; final boolean swap; boolean isNegated = isNegated(pivot); if (firstSize < secondSize) { swap = true; final Term tmpT = first; final int tmpI = firstSize; first = second; firstSize = secondSize; second = tmpT; secondSize = tmpI; pivotFirst = pivot; if (isNegated) { pivotSecond = ((ApplicationTerm)pivot).getParameters()[0]; } else { pivotSecond = mTheory.term(mTheory.mNot, pivot); } isNegated ^= true; } else { swap = false; if (isNegated) { pivotFirst = ((ApplicationTerm)pivot).getParameters()[0]; } else { pivotFirst = mTheory.term(mTheory.mNot, pivot); } pivotSecond = pivot; } assert ((firstSize > 0) && (secondSize > 0)); // check that the pivot is contained assert checkPivotContainment(first, second, pivotFirst, pivotSecond, firstSize, secondSize); // find pivot in P final int plIndex; if (firstSize == 1) { assert (first == pivotFirst) : "A singleton clause must be the (correctly negated) pivot."; plIndex = 0; } else { assert ((first instanceof ApplicationTerm) && (((ApplicationTerm)first).getFunction() == mTheory.mOr)); plIndex = findPivot(((ApplicationTerm)first).getParameters(), pivotFirst); } // create pattern only for Q final PatternSharedWrapper wrapper = createQPattern( firstSize == 1 ? null : ((ApplicationTerm)first).getParameters(), second, secondSize, plIndex, pivotSecond); return new PatternSwapWrapper( new ResolutionPattern(firstSize, plIndex, wrapper.mPattern), wrapper.mSharedTerms, swap, !isNegated); } /** * This method checks if the pivot is negated. * * @param pivot the pivot * @return true iff the pivot is negated */ private boolean isNegated(final Term pivot) { return (pivot instanceof ApplicationTerm && (((ApplicationTerm)pivot).getFunction() == mTheory.mNot)); } /** * This method finds the index of the pivot. * * @param disjuncts the disjuncts * @param pivot the pivot * @return the index of the pivot in the disjunction */ private int findPivot(final Term[] disjuncts, final Term pivot) { assert (disjuncts.length > 1); for (int i = disjuncts.length - 1; i >= 0; --i) { if (disjuncts[i] == pivot) { return i; } } throw new IllegalArgumentException("The pivot was not found."); } /** * This method creates the pattern for the second disjunction. * * @param first the disjuncts of the first disjunction * @param second the second disjunction * @param secondSize the size of the second disjunction * @param plIndex the index of the pivot in the first disjunction * @param pivot the pivot in the second disjunction * @return the partial pattern wrapper with the pattern and shared terms */ private PatternSharedWrapper createQPattern(final Term[] first, final Term second, final int secondSize, final int plIndex, final Term pivot) { final int[] result = new int[secondSize]; final int[] sharedTerms; // trivial case if (secondSize == 1) { assert (second == pivot) : "A singleton clause must be the (correctly negated) pivot."; result[0] = -1; sharedTerms = new int[0]; } else { assert (second instanceof ApplicationTerm); final Term[] qDisjuncts = ((ApplicationTerm)second).getParameters(); assert (qDisjuncts.length == secondSize); assert ((first != null) && (first.length > 1)); // create a mapping from terms in the first disjunction final HashMap<Term, Integer> disjunct2index = new HashMap<Term, Integer>((int)(first.length / 0.75) + 1); for (int i = first.length - 1; i > plIndex; --i) { disjunct2index.put(first[i], i); } for (int i = plIndex - 1; i >= 0; --i) { disjunct2index.put(first[i], i); } assert checkForDuplicates(disjunct2index.size(), first.length, qDisjuncts); // create Q pattern int index = first.length - 1; final ArrayList<Integer> shared = new ArrayList<Integer>(secondSize); int i = -1; while (++i < qDisjuncts.length) { final Term disjunct = qDisjuncts[i]; if (disjunct == pivot) { break; } final Integer oldIndex = disjunct2index.get(disjunct); if (oldIndex == null) { result[i] = ++index; } else { result[i] = oldIndex; shared.add(oldIndex); } } result[i] = -1; while (++i < qDisjuncts.length) { final Integer oldIndex = disjunct2index.get(qDisjuncts[i]); if (oldIndex == null) { result[i] = ++index; } else { result[i] = oldIndex; shared.add(oldIndex); } } sharedTerms = new int[shared.size()]; i = -1; for (final Integer j : shared) { assert (i < sharedTerms.length - 1); sharedTerms[++i] = j; } assert (i == sharedTerms.length - 1); } return new PatternSharedWrapper(result, sharedTerms); } /** * This method creates the lemma for the pattern. It constructs the * necessary pattern disjunctions and the result and calls the writing * method. * * @param wrapper the pattern wrapper * @param lemmaNummer the new lemma number */ private void createLemma(final PatternSwapWrapper wrapper, final int lemmaNummer) { final Term[] first = new Term[wrapper.mPatternWrap.mPLength]; final int[] qPattern = wrapper.mPatternWrap.mPattern; final Term[] second = new Term[qPattern.length]; final Term[] resultDisj = new Term[first.length + second.length - wrapper.mSharedTerms.length - 2]; // sort shared terms array in ascending order final int[] sharedTerms = wrapper.mSharedTerms; Arrays.sort(sharedTerms); /* create disjunction objects */ final Term[] params = new Term[0]; final Sort[] paramSorts = new Sort[0]; final Term pivotFirst = getTerm(RESOLUTION_PIVOT_L, params, paramSorts); final Term pivotSecond = getTerm(RESOLUTION_PIVOT_R, params, paramSorts); // first disjunction final int pivotIndex = wrapper.mPatternWrap.mPlIndex; first[pivotIndex] = pivotFirst; if (sharedTerms.length == 0) { for (int i = 0; i < pivotIndex; ++i) { final Term term = getTerm(RESOLUTION_L_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[i] = term; } for (int i = pivotIndex + 1; i < first.length; ++i) { final Term term = getTerm(RESOLUTION_L_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[i - 1] = term; } } else { int sharedIndex = 0; int nextShared = sharedTerms[0]; int resultL = -1; int resultC = first.length - wrapper.mSharedTerms.length - 2; for (int i = 0; i < pivotIndex; ++i) { // term shared in both disjunctions if (i == nextShared) { final Term term = getTerm(RESOLUTION_C_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[++resultC] = term; if (sharedIndex < sharedTerms.length - 1) { nextShared = sharedTerms[++sharedIndex]; } } else { // term only in the first disjunction final Term term = getTerm(RESOLUTION_L_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[++resultL] = term; } } for (int i = pivotIndex + 1; i < first.length; ++i) { // term shared in both disjunctions if (i == nextShared) { final Term term = getTerm(RESOLUTION_C_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[++resultC] = term; if (sharedIndex < sharedTerms.length - 1) { nextShared = sharedTerms[++sharedIndex]; } } else { // term only in the first disjunction final Term term = getTerm(RESOLUTION_L_PREFIX + i, params, paramSorts); first[i] = term; resultDisj[++resultL] = term; } } } // second disjunction int i = 0; int resultR = first.length - 2; for ( ; i < qPattern.length; ++i) { final int patternIndex = qPattern[i]; if (patternIndex < first.length) { // pivot if (patternIndex == -1) { second[i] = pivotSecond; ++i; break; } else { // term shared in both disjunctions second[i] = getTerm(RESOLUTION_C_PREFIX + patternIndex, params, paramSorts); } } else { // term only in the second disjunction final Term term = getTerm(RESOLUTION_R_PREFIX + patternIndex, params, paramSorts); second[i] = term; resultDisj[++resultR] = term; } } for ( ; i < qPattern.length; ++i) { final int patternIndex = qPattern[i]; // term shared in both disjunctions if (patternIndex < first.length) { assert (patternIndex > -1); second[i] = getTerm(RESOLUTION_C_PREFIX + patternIndex, params, paramSorts); } else { // term only in the second disjunction final Term term = getTerm(RESOLUTION_R_PREFIX + patternIndex, params, paramSorts); second[i] = term; resultDisj[++resultR] = term; } } // write the lemma proof writeLemmaProof(first, second, resultDisj, pivotFirst, pivotSecond, lemmaNummer); } /** * This method creates a new term given the function name and the * parameters. * * @param name function name * @param parameters parameters * @param parameterSorts parameter sorts * @return ApplicationTerm with the function and the parameters */ private ApplicationTerm getTerm(final String name, final Term[] parameters, final Sort[] parameterSorts) { final ApplicationTerm result = mTheory.term(name, parameters); if (result == null) { return mTheory.term( mTheory.declareFunction( name, parameterSorts, mTheory.getBooleanSort()), parameters); } return result; } /** * This method is only used for asserting that the pivot is contained in * both disjunctions. * * @param first the bigger disjunction * @param second the smaller disjunction * @param pivotFirst the first pivot * @param pivotSecond the second pivot * @param firstSize the size of the first term * @param secondSize the size of the second term * @return true * @throws AssertionError iff pivot is not found */ private boolean checkPivotContainment(final Term first, final Term second, final Term pivotFirst, final Term pivotSecond, final int firstSize, final int secondSize) throws AssertionError { if (firstSize == 1) { assert (first == pivotFirst) : "A singleton clause must be the (correctly negated) pivot."; } else { assert ((first instanceof ApplicationTerm) && (((ApplicationTerm)first).getFunction() == mTheory.mOr)); assert (findPivot(((ApplicationTerm)first).getParameters(), pivotFirst) >= 0); } if (secondSize == 1) { assert (second == pivotSecond) : "A singleton clause must be the (correctly negated) pivot."; } else { assert ((second instanceof ApplicationTerm) && (((ApplicationTerm)second).getFunction() == mTheory.mOr)); assert (findPivot(((ApplicationTerm)second).getParameters(), pivotSecond) >= 0); } return true; } /** * This method is only used for asserting that the disjunction does not * contain any duplicates. * * @param mapSize size of the map for the first disjunction (without pivot) * @param firstLength length of the first disjunction * @param qDisjuncts the second disjunction * @return true iff there are no duplicates */ private boolean checkForDuplicates(final int mapSize, final int firstLength, final Term[] qDisjuncts) { if (mapSize != firstLength - 1) { return false; } final HashSet<Term> set = new HashSet<Term>( (int)(qDisjuncts.length / 0.75) + 1); for (final Term disjunct : qDisjuncts) { set.add(disjunct); } return (set.size() == qDisjuncts.length); } /** * This method writes the lemma to the lemma appendable. * * @param first the first disjuncts * @param second the second disjuncts * @param result the result disjuncts * @param pivotFirst pattern pivot in the first term * @param pivotSecond pattern pivot in the second term * @param lemmaNumber the lemma number */ private void writeLemmaProof(final Term[] first, final Term[] second, final Term[] result, final Term pivotFirst, final Term pivotSecond, final int lemmaNumber) { final FunctionSymbol or = mTheory.mOr; final Term firstDisjunction = (first.length == 1) ? first[0] : mTheory.term(or, first); final Term secondDisjunction = (second.length == 1) ? second[0] : mTheory.term(or, second); final Term resultDisjunction = (result.length == 0) ? mTheory.mFalse : (result.length == 1) ? result[0] : mTheory.term(or, result); // head line writeLemmaString("\nlemma "); writeLemmaString(RESOLUTION_LEMMA_PREFIX); writeLemmaString(Integer.toString(lemmaNumber)); writeLemmaString(": \"[|"); mConverter.convertToAppendable(firstDisjunction, mLemmaAppendable); writeLemmaString("; "); mConverter.convertToAppendable(secondDisjunction, mLemmaAppendable); writeLemmaString("; (~ "); mConverter.convertToAppendable(pivotFirst, mLemmaAppendable); writeLemmaString(") = "); mConverter.convertToAppendable(pivotSecond, mLemmaAppendable); writeLemmaString("|] ==> "); mConverter.convertToAppendable(resultDisjunction, mLemmaAppendable); writeLemmaString("\"\n"); /* proof */ // both disjunctions consist of only the pivot if (first.length == 1) { writeLemmaString("by (rule res_false)\n"); return; } // strings for fast and slow proofs final String next = ")\nby ("; final String finish = ") +\n"; writeLemmaString("apply (erule (2) res_"); final int sharedCount = first.length + second.length - result.length - 2; // shared terms exist if (sharedCount > 0) { // only shared terms (C terms) if (first.length == sharedCount + 1) { writeLemmaString("c"); writeLemmaString(next); writeLemmaString( "simp only: HOL.disj_commute HOL.disj_left_commute"); writeLemmaString(finish); } else if (second.length == sharedCount + 1) { // second disjunction only contains shared terms (L and C terms) writeLemmaString("lc [where l = \""); final Term[] lTerms = getLTerms(result, first.length, sharedCount); mConverter.convertToAppendable( (lTerms.length == 1) ? lTerms[0] : mTheory.term(or, lTerms), mLemmaAppendable); writeLemmaString("\" and c = \""); final Term[] cTerms = getCTerms(result, first.length, sharedCount); mConverter.convertToAppendable( (cTerms.length == 1) ? cTerms[0] : mTheory.term(or, cTerms), mLemmaAppendable); writeLemmaString("\"]"); writeLemmaString(next); writeLemmaString("simp only: HOL.disj_commute " + "HOL.disj_left_commute HOL.disj_assoc"); writeLemmaString(finish); } else { // L, C, and R terms writeLemmaString("lcr [where l = \""); final Term[] lTerms = getLTerms(result, first.length, sharedCount); mConverter.convertToAppendable( lTerms.length == 1 ? lTerms[0] : mTheory.term(or, lTerms), mLemmaAppendable); writeLemmaString("\" and c = \""); final Term[] cTerms = getCTerms(result, first.length, sharedCount); mConverter.convertToAppendable( cTerms.length == 1 ? cTerms[0] : mTheory.term(or, cTerms), mLemmaAppendable); writeLemmaString("\" and r = \""); final Term[] rTerms = getRTerms(result, second.length, sharedCount); mConverter.convertToAppendable( rTerms.length == 1 ? rTerms[0] : mTheory.term(or, rTerms), mLemmaAppendable); writeLemmaString("\"]"); writeLemmaString(next); writeLemmaString("simp only: HOL.disj_commute " + "HOL.disj_left_commute HOL.disj_assoc"); writeLemmaString(finish); } } else { // no shared terms exist // only the disjuncts from the first disjunction (L terms) if (second.length == 1) { writeLemmaString("l"); writeLemmaString(next); writeLemmaString( "simp only: HOL.disj_commute HOL.disj_left_commute"); writeLemmaString(finish); } else { // L and R terms writeLemmaString("lr [where l = \""); final Term[] lTerms = getLTerms(result, first.length, sharedCount); mConverter.convertToAppendable( lTerms.length == 1 ? lTerms[0] : mTheory.term(or, lTerms), mLemmaAppendable); writeLemmaString("\" and r = \""); final Term[] rTerms = getRTerms(result, second.length, sharedCount); mConverter.convertToAppendable( rTerms.length == 1 ? rTerms[0] : mTheory.term(or, rTerms), mLemmaAppendable); writeLemmaString("\"]"); writeLemmaString(next); writeLemmaString("simp only: HOL.disj_commute " + "HOL.disj_left_commute HOL.disj_assoc"); writeLemmaString(finish); } } } /** * This method extracts the L terms from the result. * * @param result the result disjunction * @param length length of the first disjunction * @param sharedCount length of the shared terms * @return array with the L terms */ private Term[] getLTerms(final Term[] result, final int length, final int sharedCount) { final Term[] lTerms = new Term[length - sharedCount - 1]; assert (lTerms.length <= result.length); System.arraycopy(result, 0, lTerms, 0, lTerms.length); return lTerms; } /** * This method extracts the C terms from the result. * * @param result the result disjunction * @param length length of the first disjunction * @param sharedCount length of the shared terms * @return array with the C terms */ private Term[] getCTerms(final Term[] result, final int length, final int sharedCount) { final int offset = length - sharedCount - 1; final Term[] cTerms = new Term[sharedCount]; for (int i = 0; i < cTerms.length; ++i) { cTerms[i] = result[i + offset]; } return cTerms; } /** * This method extracts the R terms from the result. * * @param result the result disjunction * @param length length of the second disjunction * @param sharedCount length of the shared terms * @return array with the R terms */ private Term[] getRTerms(final Term[] result, final int length, final int sharedCount) { final int offset = result.length - length + sharedCount + 1; final Term[] rTerms = new Term[length - sharedCount - 1]; for (int i = 0; i < rTerms.length; ++i) { rTerms[i] = result[i + offset]; } return rTerms; } /** * This method creates the result disjunction. * * @param pattern the pattern * @param first the first term * @param second the second term * @return the result term */ private Term createResult(final ResolutionPattern pattern, final Term first, final Term second, final int sharedLength) { final int firstSize = pattern.mPLength; // both disjunctions consist of only the pivot if (firstSize == 1) { return mTheory.mFalse; } final FunctionSymbol or = mTheory.mOr; final int secondSize = pattern.mPattern.length; assert ((first instanceof ApplicationTerm) && (((ApplicationTerm)first).getFunction() == or) && (((ApplicationTerm)first).getParameters().length > 1)); final Term[] firstDisjuncts = ((ApplicationTerm)first).getParameters(); // only the disjuncts from the first disjunction (without the pivot) if (secondSize == 1) { if (firstSize == 2) { return firstDisjuncts[1 - pattern.mPlIndex]; } final Term[] disjuncts = new Term[firstSize - 1]; assert ((firstSize > 2) && (disjuncts.length > 1)); System.arraycopy(firstDisjuncts, 0, disjuncts, 0, pattern.mPlIndex); System.arraycopy(firstDisjuncts, pattern.mPlIndex + 1, disjuncts, pattern.mPlIndex, firstSize - pattern.mPlIndex - 1); return mTheory.term(or, disjuncts); } assert ((second instanceof ApplicationTerm) && (((ApplicationTerm)second).getFunction() == or) && (((ApplicationTerm)second).getParameters().length > 1)); final Term[] secondDisjuncts = ((ApplicationTerm)second).getParameters(); final Term[] disjuncts = new Term[ firstSize + secondSize - sharedLength - 2]; assert (disjuncts.length > 0); // add R terms and find C terms int rIndex = firstSize - 2; final int[] qPattern = pattern.mPattern; final ArrayList<Integer> sharedList = new ArrayList<Integer>(secondDisjuncts.length); int i = 0; for ( ; i < secondSize; ++i) { final int patternIndex = qPattern[i]; if (patternIndex < firstSize) { // pivot if (patternIndex == -1) { ++i; break; } else { // shared term sharedList.add(patternIndex); } } else { // R term disjuncts[++rIndex] = secondDisjuncts[i]; assert (rIndex < disjuncts.length); } } for ( ; i < secondSize; ++i) { final int patternIndex = qPattern[i]; if (patternIndex < firstSize) { assert (patternIndex > -1); // shared term sharedList.add(patternIndex); } else { // R term disjuncts[++rIndex] = secondDisjuncts[i]; assert (rIndex < disjuncts.length); } } // no shared terms, faster implementation if (sharedList.isEmpty()) { System.arraycopy(firstDisjuncts, 0, disjuncts, 0, pattern.mPlIndex); System.arraycopy(firstDisjuncts, pattern.mPlIndex + 1, disjuncts, pattern.mPlIndex, firstSize - pattern.mPlIndex - 1); return mTheory.term(or, disjuncts); } // sort shared terms indices final Integer[] shared = new Integer[sharedList.size()]; assert (shared.length == sharedLength); sharedList.toArray(shared); Arrays.sort(shared); // add L and C terms int sharedIndex = 0; int currentShared = shared[0]; int lIndex = -1; int cIndex = firstSize - shared.length - 2; i = 0; for ( ; i < firstSize; ++i) { if (i == pattern.mPlIndex) { continue; } if (i == currentShared) { disjuncts[++cIndex] = firstDisjuncts[i]; assert (cIndex < firstSize); if (++sharedIndex < shared.length) { currentShared = shared[sharedIndex]; } else { ++i; break; } } else { disjuncts[++lIndex] = firstDisjuncts[i]; assert (lIndex < firstSize - shared.length); } } for ( ; i < pattern.mPlIndex; ++i) { disjuncts[++lIndex] = firstDisjuncts[i]; assert (lIndex < firstSize - shared.length); } if (i == pattern.mPlIndex) { ++i; } for ( ; i < firstSize; ++i) { disjuncts[++lIndex] = firstDisjuncts[i]; assert (lIndex < firstSize - shared.length); } return (disjuncts.length == 1) ? disjuncts[0] : mTheory.term(or, disjuncts); } /** * This method writes the proof of the lemma in the main file. * * @param swap true iff arguments were swapped * @param doubleNegation true iff pivot is doubly negated * @param lemmaNumber number of the lemma * @param result the result term */ private void writeProof(final boolean swap, final boolean doubleNegation, final Integer lemmaNumber, final Term result) { mConverter.convert(result); writeString("\"\napply "); // swap if (swap) { writeString("rotate_tac\n"); if (mFastProofs) { writeString("by (erule (1) "); } else { writeString("apply (erule (1) "); } } else { // no swap writeString("(rule "); } // lemma writeString(RESOLUTION_LEMMA_PREFIX); writeString(Integer.toString(lemmaNumber)); // finishing if (swap && mFastProofs) { writeString(", rule "); } else { writeString(")\nby (rule "); } if (doubleNegation) { writeString("HOL.not_not)\n"); } else { writeString("HOL.refl)\n"); } } /** * This class contains the minimal information necessary to discriminate * two different proof patterns. It contains the length and the position of * the pivot in the first disjunction and the whole pattern of the second * disjunction. * This makes the construction of the result term slightly more difficult, * but saves memory, which should be more important, since all patterns are * stored in a global hash map. */ private class ResolutionPattern { // length of the P private final int mPLength; // index of the pivot in P private final int mPlIndex; // wrapper with pattern of Q and number of shared terms private final int[] mPattern; /** * @param pLength length of P * @param plIndex index of the pivot in P * @param pattern pattern of Q */ public ResolutionPattern(final int pLength, final int plIndex, final int[] pattern) { mPLength = pLength; mPlIndex = plIndex; mPattern = pattern; } @Override public int hashCode() { int hashCode = mPLength + mPlIndex + mPattern.length; for (int i = Math.min(mPattern.length - 1, 5); i >= 0; --i) { hashCode += mPattern[i]; } return hashCode; } @Override public boolean equals(final Object o) { assert (o instanceof ResolutionPattern); final ResolutionPattern other = (ResolutionPattern)o; if ((mPLength == other.mPLength) && (mPlIndex == other.mPlIndex) && (mPattern.length == other.mPattern.length)) { for (int i = mPattern.length - 1; i >= 0; --i) { if (mPattern[i] != other.mPattern[i]) { return false; } } return true; } return false; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("(P: "); builder.append(mPLength); builder.append(", "); builder.append(mPlIndex); builder.append("; Q: ["); String append = ""; for (int i = 0; i < mPattern.length; ++i) { builder.append(append); append = ", "; builder.append(mPattern[i]); } builder.append("])"); return builder.toString(); } } /** * This class is used to wrap the return types of a method. * The pattern is wrapped here even more (cp. {@link ResolutionPattern}). */ private class PatternSwapWrapper { // pattern private final ResolutionPattern mPatternWrap; // array for shared terms private final int[] mSharedTerms; // true iff terms were swapped private final boolean mSwap; // true iff pivot is doubly negated private final boolean mDoubleNegation; /** * @param pattern pattern * @param sharedTerms array of the shared terms indices * @param doubleNegation true iff pivot is doubly negated * @param swap true iff terms were swapped */ public PatternSwapWrapper(final ResolutionPattern pattern, final int[] sharedTerms, final boolean swap, final boolean doubleNegation) { mPatternWrap = pattern; mSharedTerms = sharedTerms; mSwap = swap; mDoubleNegation = doubleNegation; } /** * This method indicates whether the pattern is relevant for storing. * The reason this is considered is that for big proofs, the hash map * could become too big and the chance for reusing a pattern with many * variables is small due to combinatorial explosions in the number * of permutations. * * Currently the implementation only stores patterns whose bigger * disjunction has size less than 6. * * @return true iff the pattern does not exceed a fixed size */ public boolean isStoreCandidate() { return (mPatternWrap.mPLength < 6); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append('{'); builder.append(mPatternWrap.toString()); builder.append(", ["); String append = ""; for (int i = 0; i < mSharedTerms.length; ++i) { builder.append(append); append = ", "; builder.append(mSharedTerms[i]); } builder.append("], "); builder.append(mSwap); builder.append(", "); builder.append(mDoubleNegation); builder.append('}'); return builder.toString(); } } /** * This class is used to wrap the return types of a method. * The pattern is packed together with the shared terms. */ private class PatternSharedWrapper { // pattern private final int[] mPattern; // array for shared terms private final int[] mSharedTerms; /** * @param qPattern pattern * @param sharedTerms array for shared terms */ public PatternSharedWrapper(final int[] qPattern, final int[] sharedTerms) { mPattern = qPattern; mSharedTerms = sharedTerms; } } /** * This method writes a string to the lemma appendable. * * @param string string that is written * @throws RuntimeException thrown if an IOException is caught */ private void writeLemmaString(String string) { try { mLemmaAppendable.append(string); } catch (final IOException e) { throw new RuntimeException("Appender throws IOException", e); } } }