/*
* 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.math.BigInteger;
import java.util.HashMap;
import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm;
import de.uni_freiburg.informatik.ultimate.logic.ConstantTerm;
import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol;
import de.uni_freiburg.informatik.ultimate.logic.Sort;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.Theory;
/**
* This class is used to convert a rewrite proof node (@rewrite).
* A rewrite is used for substitution.
*
* @author Christian Schilling
*/
public class RewriteConverter extends AConverter {
// map for the rules
private final HashMap<String, IRule> mAnnot2Rule;
// lemma appendable
private final Appendable mLemmaAppendable;
// prefix :orSimp placeholder variables use
private static final String PATTERN_VAR_PREFIX = "p";
// index number of the pattern lemmata
private int mLemmaNumber;
// last pattern proof (not written immediately for
private PatternProof mLastPatternProof;
/**
* @param appendable appendable to write the proof to
* @param theory the theory
* @param converter term converter
* @param simplifier computation simplifier
* @param lemmaAppendable appendable for the lemmata
*/
public RewriteConverter(final Appendable appendable, final Theory theory,
final TermConverter converter,
final ComputationSimplifier simplifier,
final Appendable lemmaAppendable) {
super(appendable, theory, converter, simplifier);
mLemmaAppendable = lemmaAppendable;
mLemmaNumber = 0;
mLastPatternProof = null;
// fill rule map
mAnnot2Rule = new HashMap<String, RewriteConverter.IRule>(
(int)(50 / 0.75) + 1);
fillMap();
}
// [start] rules //
/**
* This method fills the rule map with the rules.
* For each rule a conversion object is added to a hash map, which handles
* the conversion individually.
*
* Here the rules could be changed or new ones added if necessary.
*/
private void fillMap() {
// expansion of associative/chainable core functions
mAnnot2Rule.put(":expand",
new ExpandRule());
// expansion of user-defined functions
mAnnot2Rule.put(":expandDef",
new ExpandDefRule());
// equality of Boolean terms with 'True' and 'False' is false
mAnnot2Rule.put(":trueNotFalse",
new TrueNotFalseRule());
// equality of different constants is false
mAnnot2Rule.put(":constDiff",
new ConstDiffRule());
// equality with true is a conjunction
mAnnot2Rule.put(":eqTrue",
new EqTrueRule());
// equality with false is a disjunction of negated terms
mAnnot2Rule.put(":eqFalse",
new EqFalseRule());
// equality of syntactically equal terms is true
mAnnot2Rule.put(":eqSame",
new EqSameRule());
// elimination of duplicates in equality (needs symmetry)
mAnnot2Rule.put(":eqSimp",
new EqSimpRule());
// n-ary equality is a negated disjunction of negated binary equalities
mAnnot2Rule.put(":eqBinary",
new SimpleRule("(intro rw_eqBinary, rule not_not [symmetric])\n"));
// distinctness with more than 2 Boolean arguments is false
mAnnot2Rule.put(":distinctBool",
new DistinctBoolRule());
// distinctness for syntactically equal terms is false
mAnnot2Rule.put(":distinctSame",
new SimpleRule("(intro rw_distinctSame_fin "
+ "rw_distinctSame_step rw_distinctSame_fin_bin)\n"));
// distinctness of a Boolean term and its negation is false
mAnnot2Rule.put(":distinctNeg",
new DistinctNegRule());
// distinctness of a Boolean term and 'True' is the negated term
mAnnot2Rule.put(":distinctTrue",
new DistinctTrueRule());
// distinctness of a Boolean term and 'False' is the term
mAnnot2Rule.put(":distinctFalse",
new DistinctFalseRule());
// n-ary distinctness is a negated disjunction of binary equalities
mAnnot2Rule.put(":distinctBinary",
new DistinctBinaryRule());
// double negation and negations of Boolean constants
mAnnot2Rule.put(":notSimp",
new NotSimpRule());
// absorption rule of disjunction
mAnnot2Rule.put(":orSimp",
new OrSimpRule());
// disjunction with 'True' or a Boolean term and its negation is true
mAnnot2Rule.put(":orTaut",
new OrTautRule());
// ite with condition 'True' is the first case
mAnnot2Rule.put(":iteTrue",
new SimpleRule("(rule HOL.if_True)\n"));
// ite with condition 'False' is the second case
mAnnot2Rule.put(":iteFalse",
new SimpleRule("(rule HOL.if_False)\n"));
// ite with syntactically equal cases is the only case
mAnnot2Rule.put(":iteSame",
new SimpleRule("(rule HOL.if_cancel)\n"));
// ite with case 1 'True' and case 2 'False' is the condition
mAnnot2Rule.put(":iteBool1",
new SimpleRule("(rule rw_iteBool1)\n"));
// ite with case 1 'False' and case 2 'True' is the negated condition
mAnnot2Rule.put(":iteBool2",
new SimpleRule("(rule rw_iteBool2)\n"));
// ite with case 1 'True' is a disjunction of the condition and case 2
mAnnot2Rule.put(":iteBool3",
new SimpleRule("(rule rw_iteBool3)\n"));
/*
* ite with case 1 'False' is a negated disjunction of the condition
* and negated case 2
*/
mAnnot2Rule.put(":iteBool4",
new SimpleRule("(rule rw_iteBool4)\n"));
/*
* ite with case 2 'True' is a disjunction of the negated condition
* and case 1
*/
mAnnot2Rule.put(":iteBool5",
new SimpleRule("(rule rw_iteBool5)\n"));
/*
* ite with case 2 'False' is a negated disjunction of the negated
* condition and the negated case 1
*/
mAnnot2Rule.put(":iteBool6",
new SimpleRule("(rule rw_iteBool6)\n"));
// logical and rewrite to disjunction
mAnnot2Rule.put(":andToOr",
new SimpleRule("(intro rw_andToOr, rule HOL.not_not [symmetric])\n"));
// xor rewrite to distinct
mAnnot2Rule.put(":xorToDistinct",
new SimpleRule("(rule rw_xorToDistinct)\n"));
// implication rewrite to disjunction
mAnnot2Rule.put(":impToOr",
new ImpToOrRule());
/*
* remove annotation
* (should neither be translated nor substituted, since annotations are
* automatically dropped)
*/
mAnnot2Rule.put(":strip",
new SimpleRule(ProofConverter.G_ONLY_SUBSTITUTE));
// canonical form of arithmetic terms: (+ (* a1 x1) ... (* an xn) an+1)
mAnnot2Rule.put(":canonicalSum",
new SimpleRule(mSimplifier.getRule() + "\n"));
// binary '<=' to canonical form
mAnnot2Rule.put(":leqToLeq0",
new SimpleRule("(rule rw_leqToLeq0, " + mSimplifier.getRule()
+ ")\n"));
// binary '<' to canonical form
mAnnot2Rule.put(":ltToLeq0",
new SimpleRule("(rule rw_ltToLeq0, " + mSimplifier.getRule()
+ ")\n"));
// binary '>=' to canonical form
mAnnot2Rule.put(":geqToLeq0",
new SimpleRule("(rule rw_geqToLeq0, " + mSimplifier.getRule()
+ ")\n"));
// binary '>' to canonical form
mAnnot2Rule.put(":gtToLeq0",
new SimpleRule("(rule rw_gtToLeq0, " + mSimplifier.getRule()
+ ")\n"));
// constant comparison with zero
mAnnot2Rule.put(":leqTrue",
new SimpleRule(mSimplifier.getRule() + "\n"));
// constant comparison with zero
mAnnot2Rule.put(":leqFalse",
new SimpleRule(mSimplifier.getRule() + "\n"));
// correct typing in mixed logic
mAnnot2Rule.put(":desugar",
new SimpleRule(mSimplifier.getRule() + "\n"));
// divisible rewrite
mAnnot2Rule.put(":divisible",
new DivisibleRule());
// modulo with constant second term different from 0
mAnnot2Rule.put(":modulo",
new SimpleRule("(rule rw_modulo, " + mSimplifier.getRule()
+ ")\n"));
// modulo by 1 is 0
mAnnot2Rule.put(":modulo1",
new SimpleRule("(rule rw_modulo1)\n"));
// modulo by -1 is 0
mAnnot2Rule.put(":modulo-1",
new SimpleRule("(rule rw_moduloM1)\n"));
// constant modulo result
mAnnot2Rule.put(":moduloConst",
new SimpleRule("(unfold SMTmod_def, " + mSimplifier.getRule()
+ ")\n"));
// division by 1 does not change
mAnnot2Rule.put(":div1",
new SimpleRule("(rule rw_div1)\n"));
// division by -1 only inverts
mAnnot2Rule.put(":div-1",
new DivM1Rule());
// constant division result
mAnnot2Rule.put(":divConst",
new SimpleRule("(unfold SMTdiv_def, " + mSimplifier.getRule()
+ ")\n"));
// integer conversion of a constant
mAnnot2Rule.put(":toInt",
new SimpleRule("(subst Orderings.order_class.eq_iff, "
+ mSimplifier.getRule() + ")\n"));
// real conversion of constants
mAnnot2Rule.put(":toReal",
new SimpleRule("(simp only: THF_Arith.int_to_real_def)\n"));
// flattening of disjunctions
mAnnot2Rule.put(":flatten",
new SimpleRule("(intro HOL.refl rw_flatten_drop rw_flatten_par)\n"));
}
/**
* This interface is used for the rule translation.
*/
private interface IRule {
/**
* @param equality the tautology
* @return the proof rule (keywords for special cases!)
*/
String convert(final ApplicationTerm equality);
}
/**
* This class translates trivial rules that need no further investigation.
*/
private class SimpleRule implements IRule {
// Isabelle rule
private final String mRule;
/**
* @param rule the rule
*/
public SimpleRule(final String rule) {
mRule = rule;
}
/**
* The rule is simply written without any additional steps.
*
* {@inheritDoc}
*/
@Override
public String convert(final ApplicationTerm equality) {
return mRule;
}
}
/**
* This class converts the :expand rule.
*
* The translation already prints most of the interpreted functions in the
* correct expansion, so the rule can be ignored.
*
* The interpreted functions it is used for are:
* xor, +, -, *, /, div, <, >, <=, >=
* The latter four functions add conjunctions with associativity different
* from Isabelle, so this has to be translated.
*
* These other functions have their own treatment rewrites:
* and, =>, =, distinct
*/
private class ExpandRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert (equality.getParameters()[0] instanceof ApplicationTerm);
final ApplicationTerm lhs =
(ApplicationTerm)equality.getParameters()[0];
final String function = lhs.getFunction().getName();
/*
* the comparison predicates have to be handled separately,
* but only for more than three arguments
*/
if ((lhs.getParameters().length > 3)
&& ((function == "<") || (function == "<=")
|| (function == ">") || (function == ">="))) {
return "(intro rw_expand, rule HOL.refl)\n";
} else {
// these cases are already correclty translated
return ProofConverter.G_ONLY_SUBSTITUTE;
}
}
}
/**
* This class converts the :expandDef rule.
*
* It just unfolds the definition and finishes by reflexivity.
*/
private class ExpandDefRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert (equality.getParameters()[0] instanceof ApplicationTerm);
final StringBuilder builder = new StringBuilder(32);
builder.append("(unfold ");
builder.append(mConverter.getFunctionName(
((ApplicationTerm)equality.getParameters()[0]).
getFunction()));
builder.append("_def, rule refl)\n");
return builder.toString();
}
}
/**
* This class converts the :trueNotFalse rule.
*
* The proof in Isabelle depends on whether 'True' or 'False' comes first.
* The term is then propagated to the right until the other term is found,
* which finishes the proof.
* There are sixteen possible cases.
*/
private class TrueNotFalseRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
final Term[] eqTerms =
((ApplicationTerm)equality.getParameters()[0]).
getParameters();
/*
* Find the first occurrence of 'True' or 'False'.
*/
final ApplicationTerm otherTerm;
int i = 0;
while (true) {
assert (i < eqTerms.length - 1) : "The first Boolean constant must be found.";
final Term term = eqTerms[i];
// first found 'True'
if (term == mTheory.mTrue) {
otherTerm = mTheory.mFalse;
break;
} else if (term == mTheory.mFalse) {
// first found 'False'
otherTerm = mTheory.mTrue;
break;
}
++i;
}
int j = i + 1;
while (j < eqTerms.length) {
if (eqTerms[j] == otherTerm) {
break;
}
++j;
}
assert (j < eqTerms.length) : "The other Boolean constant must be found.";
// write proof
final StringBuilder builder = new StringBuilder(32);
builder.append("(intro ");
final String type = (otherTerm == mTheory.mFalse) ? "T" : "F";
// constants are neighbors
if (j == i + 1) {
builder.append("rw_tnf_nb");
builder.append(type);
// constants are the last two terms
if (j == eqTerms.length - 1) {
builder.append("_last");
}
} else {
// propagation between constants needed
// second constant is the last term
if (j == eqTerms.length - 1) {
builder.append("rw_tnf_prop");
builder.append(type);
builder.append("_last rw_tnf_prop");
builder.append(type);
} else {
builder.append("rw_tnf_nb");
builder.append(type);
builder.append(" rw_tnf_prop");
builder.append(type);
}
}
// no elimination of previous equalities needed
if (i == 0) {
builder.append(")\n");
} else {
// elimination of previous equalities needed
builder.append(" rw_tnf_elim)\n");
}
return builder.toString();
}
}
/**
* This class converts the :constDiff rule.
*
* For binary equalities the proof is trivial. Else the first two distinct
* constant terms are found. In the proof the first constant is propagated
* to the right until the other term is found, which finishes the proof.
*/
private class ConstDiffRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "="));
final Term[] terms = ((ApplicationTerm)equality.
getParameters()[0]).getParameters();
assert (terms.length > 1);
if (terms.length == 2) {
return "(rule rw_constDiff_bin, " + mSimplifier.getRule() + ")\n";
} else {
// find the distinct constants
final ConstantTerm c, d;
int i = 0;
while (true) {
if (terms[i] instanceof ConstantTerm) {
c = (ConstantTerm)terms[i];
break;
}
++i;
assert (i < terms.length);
}
int j = i + 1;
while (true) {
if (terms[j] instanceof ConstantTerm) {
final ConstantTerm ct = (ConstantTerm)terms[j];
if (c != ct) {
d = ct;
break;
}
}
++j;
assert (j < terms.length);
}
// write rule
final StringBuilder builder = new StringBuilder();
builder.append("(rule rw_constDiff_start [where c = \"");
mConverter.convertToAppendable(c, builder);
builder.append("\" and d = \"");
mConverter.convertToAppendable(d, builder);
builder.append("\"], ");
builder.append(mSimplifier.getRule());
builder.append(", elim");
// at least one argument is given
assert ((j < terms.length - 1) || (i < j - 1) || (i > 0));
// stop when the second constant was found
if (j < terms.length - 1) {
builder.append(" rw_constDiff_fin");
}
// only step if there are terms in between
if (i < j - 1) {
builder.append(" rw_constDiff_step");
}
// ignore all equalities before the first constant
if (i > 0) {
builder.append(" rw_constDiff_elim)\n");
} else {
builder.append(")\n");
}
return builder.toString();
}
}
}
/**
* This class converts the :eqTrue rule.
*
* It uses a pattern proof, since otherwise there would be too many cases
* to consider.
*/
private class EqTrueRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
final Term[] lhs = ((ApplicationTerm)equality.getParameters()[0]).
getParameters();
final Term rhsTerm = equality.getParameters()[1];
final Term[] rhs;
if ((rhsTerm instanceof ApplicationTerm)
&& (((ApplicationTerm)rhsTerm).getFunction()
== mTheory.mAnd)) {
rhs = ((ApplicationTerm)rhsTerm).getParameters();
} else {
rhs = new Term[1];
rhs[0] = rhsTerm;
}
final ApplicationTerm truth = mTheory.mTrue;
// binary case
if (lhs.length == 2) {
if (lhs[0] == truth) {
assert (lhs[1] == equality.getParameters()[1]);
return "(rule HOL.simp_thms(11))\n";
} else {
assert ((lhs[0] == equality.getParameters()[1])
&& (lhs[1] == truth));
return "(rule HOL.eq_True)\n";
}
}
// n-ary case: proof by simplifier
final StringBuilder proof = new StringBuilder(512);
proof.append("\n(simp only: HOL.simp_thms(11) HOL.eq_True "
+ "HOL.simp_thms(21) HOL.simp_thms(22)");
// right-hand side is a disjunction
if (rhs.length > 1) {
proof.append(" rw_eqTrue_merge_left rw_eqTrue_merge_right "
+ "rw_eqTrue_merge_left_bin rw_eqTrue_merge_right_bin");
}
// optional removing of duplicates
proof.append(", (simp only: HOL.conj_absorb "
+ "HOL.conj_left_absorb HOL.conj_commute "
+ "HOL.conj_left_commute) ?)\n");
final Term[] patternLeft = new Term[lhs.length];
final Term[] patternRight = new Term[rhs.length];
setUpEqPattern(lhs, patternLeft, rhs, patternRight, truth);
final FunctionSymbol eq = equality.getFunction();
return getMainProof(
mTheory.term(eq,
mTheory.term(eq, patternLeft),
(patternRight.length == 1)
? patternRight[0]
: mTheory.term(mTheory.mAnd,
patternRight)),
proof.toString());
}
}
/**
* This class converts the :eqFalse rule.
*
* It uses a pattern proof, since otherwise there would be too many cases
* to consider.
*/
private class EqFalseRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1)
&& (equality.getParameters()[1] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[1]).
getFunction() == mTheory.mNot)
&& (((ApplicationTerm)equality.getParameters()[1]).
getParameters().length == 1));
final Term[] lhs = ((ApplicationTerm)equality.getParameters()[0]).
getParameters();
final Term rhsTerm = ((ApplicationTerm)equality.
getParameters()[1]).getParameters()[0];
final Term[] rhs;
if ((rhsTerm instanceof ApplicationTerm)
&& (((ApplicationTerm)rhsTerm).getFunction() == mTheory.mOr)) {
rhs = ((ApplicationTerm)rhsTerm).getParameters();
} else {
rhs = new Term[1];
rhs[0] = rhsTerm;
}
final ApplicationTerm falsity = mTheory.mFalse;
// binary case
if (lhs.length == 2) {
if (lhs[0] == falsity) {
assert (mTheory.term(mTheory.mNot, lhs[1])
== equality.getParameters()[1]);
return "(rule HOL.simp_thms(13))\n";
} else {
assert ((mTheory.term(mTheory.mNot, lhs[0])
== equality.getParameters()[1])
&& (lhs[1] == falsity));
return "(rule HOL.simp_thms(14))\n";
}
}
// n-ary case: proof by simplifier
final StringBuilder proof = new StringBuilder(512);
proof.append("(simp only: HOL.refl HOL.simp_thms(13) "
+ "HOL.simp_thms(14) HOL.simp_thms(21) HOL.simp_thms(22)");
// right-hand side is a disjunction
if (rhs.length > 1) {
proof.append(" rw_eqFalse_merge_left rw_eqFalse_merge_right "
+ "rw_eqFalse_merge_left_bin rw_eqFalse_merge_right_bin");
}
// optional removing of duplicates
proof.append(", (simp only: HOL.conj_absorb "
+ "HOL.conj_left_absorb HOL.conj_commute "
+ "HOL.conj_left_commute) ?");
// de Morgan's rule to solve the negated disjunction
if (rhs.length > 1) {
proof.append(", (intro rw_eqFalse_deMorgan HOL.refl)");
}
proof.append(")\n");
final Term[] patternLeft = new Term[lhs.length];
final Term[] patternRight = new Term[rhs.length];
setUpEqPattern(lhs, patternLeft, rhs, patternRight, falsity);
final FunctionSymbol eq = equality.getFunction();
final FunctionSymbol not = mTheory.mNot;
return getMainProof(
mTheory.term(eq,
mTheory.term(eq, patternLeft),
(patternRight.length == 1)
? mTheory.term(not, patternRight[0])
: mTheory.term(not,
mTheory.term(mTheory.mOr,
patternRight))),
proof.toString());
}
}
/**
* This method creates the patterns for both the :eqTrue and the :eqFalse
* rule, since their structure only differs in the Boolean constant.
*
* @param originLeft the original left equality terms
* @param patternLeft the pattern left equality terms
* @param originRight the original right equality terms
* @param patternRight the pattern right equality terms
* @param constant Boolean constant
*/
private void setUpEqPattern(final Term[] originLeft,
final Term[] patternLeft, final Term[] originRight,
final Term[] patternRight, final ApplicationTerm constant) {
assert (originLeft.length == patternLeft.length);
final HashMap<Term, Term> term2patVar = new HashMap<Term, Term>(
(int)(patternRight.length / 0.75) + 1);
/* create conjunction object */
final Term[] params = new Term[0];
final Sort[] paramSorts = new Sort[0];
int index = 0;
for (int i = 0; i < patternRight.length; ++i) {
assert ((originRight[i] != constant)
&& (term2patVar.get(originRight[i]) == null))
: "Right-hand side should contain no duplicates or constant.";
final Term patternVar = getTerm(PATTERN_VAR_PREFIX + ++index,
params, paramSorts);
term2patVar.put(originRight[i], patternVar);
patternRight[i] = patternVar;
}
for (int i = 0; i < originLeft.length; ++i) {
final Term equalityTerm = originLeft[i];
if (equalityTerm == constant) {
patternLeft[i] = constant;
} else {
assert (term2patVar.get(equalityTerm) != null);
patternLeft[i] = term2patVar.get(equalityTerm);
}
}
}
/**
* This class converts the :eqSame rule.
*
* The only difference in the translation is due to the length of the
* equality (two, three, or more terms).
*/
private class EqSameRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
final int arguments = ((ApplicationTerm)equality.
getParameters()[0]).getParameters().length;
// binary equality needs no absorption
if (arguments == 2) {
return "(rule HOL.simp_thms(6))\n";
} else if (arguments == 3) {
// equality with more than 2 terms needs only binary absorption
return "(rule rw_eqSame_bin)\n";
} else {
// equality with more than 3 terms also needs non-binary absorption
return "(intro rw_eqSame, rule rw_eqSame_bin)\n";
}
}
}
/**
* This class converts the :eqSimp rule.
*
* It uses a pattern proof to prevent the simplifier from rewriting inside
* bigger terms.
*/
private class EqSimpRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1)
&& (equality.getParameters()[1] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[1]).
getFunction().getName() == "=")
&& (((ApplicationTerm)equality.getParameters()[1]).
getParameters().length > 1));
final Term[] lhs = ((ApplicationTerm)equality.getParameters()[0]).
getParameters();
final Term[] rhs = ((ApplicationTerm)equality.getParameters()[1]).
getParameters();
assert (rhs.length > 1);
final Term[] patternLeft = new Term[lhs.length];
final Term[] patternRight = new Term[rhs.length];
final HashMap<Term, Term> term2patVar = new HashMap<Term, Term>(
(int)(rhs.length / 0.75) + 1);
/* create conjunction object */
final Term[] params = new Term[0];
final Sort[] paramSorts = new Sort[0];
int index = 0;
for (int i = 0; i < rhs.length; ++i) {
assert (term2patVar.get(rhs[i]) == null)
: "Right-hand side should contain no duplicates.";
final Term patternVar = getTerm(PATTERN_VAR_PREFIX + ++index,
params, paramSorts);
term2patVar.put(rhs[i], patternVar);
patternRight[i] = patternVar;
}
for (int i = 0; i < lhs.length; ++i) {
final Term equalityTerm = lhs[i];
assert (term2patVar.get(equalityTerm) != null);
patternLeft[i] = term2patVar.get(equalityTerm);
}
final FunctionSymbol eq = equality.getFunction();
return getMainProof(mTheory.term(eq,
mTheory.term(eq, patternLeft),
mTheory.term(eq, patternRight)),
"(simp only: HOL.conj_absorb HOL.conj_left_absorb "
+ "HOL.conj_commute HOL.conj_left_commute HOL.eq_commute)\n"
);
}
}
/**
* This class converts the :distinctBool rule.
*
* The only case distinction is in whether the distinctness has three or
* more arguments. Distinctness is shown for the first three terms.
*/
private class DistinctBoolRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "distinct")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 2));
// ternary distinct uses a direct rule
if (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 3) {
return "(rule rw_distinctBool_ter)\n";
} else {
// all other cases show non-distinctness of the first three terms
return "(rule rw_distinctBool_start, intro "
+ "rw_distinctBool_fin rw_distinctBool_elim)\n";
}
}
}
/**
* This class converts the :distinctNeg rule.
*
* The cases are whether 'True' and 'False' occur and whether the negated
* term is the first or the second one.
*/
private class DistinctNegRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "distinct")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 2));
final Term[] distinct = ((ApplicationTerm)equality.getParameters()[0]).getParameters();
final Term lhs = distinct[0];
/* base cases: 'True' and 'False' */
if (lhs == mTheory.mTrue) {
assert (distinct[1] == mTheory.mFalse);
return "(rule rw_distinctNeg_tf)\n";
} else if (lhs == mTheory.mFalse) {
assert (distinct[1] == mTheory.mTrue);
return "(rule rw_distinctNeg_ft)\n";
} else if ((lhs instanceof ApplicationTerm)
&& (((ApplicationTerm)lhs).getFunction() == mTheory.mNot)
&& (((ApplicationTerm)lhs).getParameters()[0] == distinct[1])) {
/* arbitrary terms */
return "(rule rw_distinctNeg_np)\n";
} else {
assert ((distinct[1] instanceof ApplicationTerm)
&& ((ApplicationTerm)distinct[1]).getFunction()
== mTheory.mNot)
&& (((ApplicationTerm)distinct[1]).getParameters()[0]
== lhs);
return "(rule rw_distinctNeg_pn)\n";
}
}
}
/**
* This class converts the :distinctTrue rule.
*
* The rule depends on whether 'True' is the first or the second term.
*/
private class DistinctTrueRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "distinct")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 2));
// 'True' is the first term
if (((ApplicationTerm)equality.getParameters()[0]).
getParameters()[0] == mTheory.mTrue) {
return "(rule rw_distinctTrue_l)\n";
} else {
// 'True' is the second term
assert (((ApplicationTerm)equality.getParameters()[0]).
getParameters()[1] == mTheory.mTrue);
return "(rule rw_distinctTrue_r)\n";
}
}
}
/**
* This class converts the :distinctFalse rule.
*
* The rule depends on whether 'False' is the first or the second term.
*/
private class DistinctFalseRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "distinct")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 2));
// 'False' is the first term
if (((ApplicationTerm)equality.getParameters()[0]).
getParameters()[0] == mTheory.mFalse) {
return "(rule rw_distinctFalse_l)\n";
} else {
// 'False' is the second term
assert (((ApplicationTerm)equality.getParameters()[0]).
getParameters()[1] == mTheory.mFalse);
return "(rule rw_distinctFalse_r)\n";
}
}
}
/**
* This class converts the :distinctBinary rule.
*
* A binary non-Boolean distinct rewrite is ignored, since the translation
* already does the job. For binary Boolean rewrites the first term is
* negated again if already negated before, else the second term is
* negated.
* Distinct with more arguments uses the obvious translation.
*/
private class DistinctBinaryRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "distinct")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
// binary case
if (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 2) {
final Term first = ((ApplicationTerm)equality.
getParameters()[0]).getParameters()[0];
// Boolean case is handled differently
if (first.getSort() == mTheory.getBooleanSort()) {
if ((first instanceof ApplicationTerm)
&& ((ApplicationTerm)first).getFunction()
== mTheory.mNot) {
return "(rule rw_distinctBinary_bin_first)\n";
} else {
return "(rule rw_distinctBinary_bin_second)\n";
}
} else {
// all other cases are automatically correct by the translation
return ProofConverter.G_ONLY_SUBSTITUTE;
}
} else {
// apply variation of de Morgan's rule, finish with reflexivity
return "(intro rw_distinctBinary_more, rule HOL.refl)\n";
}
}
}
/**
* This class converts the :notSimp rule.
*
* The negations for Boolean constants are handled differently.
*/
private class NotSimpRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
final Term rhs = equality.getParameters()[1];
// trivial negation of 'True'
if (rhs == mTheory.mFalse) {
return "(rule HOL.not_True_eq_False)\n";
} else if (rhs == mTheory.mTrue) {
// trivial negation of 'False'
return "(rule HOL.not_False_eq_True)\n";
} else {
// double negation
return "(rule HOL.not_not)\n";
}
}
}
/**
* This class converts the :orSimp rule.
*
* It uses a pattern proof, since otherwise there would be too many cases
* to consider.
*/
private class OrSimpRule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction() == mTheory.mOr)
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
final Term[] lhs = ((ApplicationTerm)equality.
getParameters()[0]).getParameters();
final Term[] patternLeft = new Term[lhs.length];
final Term rhs = equality.getParameters()[1];
final Term[] patternRight;
if ((rhs instanceof ApplicationTerm)
&& (((ApplicationTerm)rhs).getFunction() == mTheory.mOr)) {
patternRight = new Term[((ApplicationTerm)rhs).getParameters().
length];
} else {
patternRight = new Term[1];
}
final HashMap<Term, Term> disjunct2patVar =
new HashMap<Term, Term>(
(int)(patternRight.length / 0.75) + 1);
int index = 0;
/* create disjunction object */
final Term[] params = new Term[0];
final Sort[] paramSorts = new Sort[0];
final ApplicationTerm falsity = mTheory.mFalse;
int j = -1;
for (int i = 0; i < patternLeft.length; ++i) {
final Term disjunct = lhs[i];
if (disjunct == falsity) {
patternLeft[i] = falsity;
} else {
Term patternVar = disjunct2patVar.get(disjunct);
if (patternVar == null) {
patternVar = getTerm(PATTERN_VAR_PREFIX + ++index, params,
paramSorts);
disjunct2patVar.put(disjunct, patternVar);
patternRight[++j] = patternVar;
}
patternLeft[i] = patternVar;
}
}
final String proof;
// disjunction only contains 'False', so there is an easier proof
if ((patternRight.length == 1) && (rhs == falsity)) {
patternRight[0] = falsity;
proof = "(simp only: HOL.simp_thms(32))\n";
} else {
// at least one disjunct is not 'False'
proof = "(simp only: HOL.disj_commute "
+ "HOL.disj_left_commute HOL.disj_absorb "
+ "HOL.disj_left_absorb HOL.simp_thms(31) "
+ "HOL.simp_thms(32))\n";
}
assert (patternRight[patternRight.length - 1] != null);
final FunctionSymbol or = mTheory.mOr;
return getMainProof(
mTheory.term(equality.getFunction(),
mTheory.term(or, patternLeft),
(patternRight.length == 1)
? patternRight[0]
: mTheory.term(or, patternRight)),
proof);
}
}
/**
* This class converts the :orTaut rule.
*
* It uses a pattern proof and discriminates between the reason ('True' vs.
* 'P | ~ P').
*/
private class OrTautRule implements IRule {
private class TermNegationWrapper {
// the term
private final Term mTerm;
// true iff term was negated
private final boolean mIsNegated;
/**
* @param term the term (without negation)
* @param isNegated true iff term was negated
*/
public TermNegationWrapper(Term term, boolean isNegated) {
mTerm = term;
mIsNegated = isNegated;
}
@Override
public int hashCode() {
return mTerm.hashCode();
}
@Override
public boolean equals(Object o) {
return mTerm.equals(o);
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append('[');
builder.append(mIsNegated ? "not " : "");
builder.append(mTerm.toStringDirect());
builder.append(']');
return builder.toString();
}
}
@Override
public String convert(ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction() == mTheory.mOr)
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1)
&& (((ApplicationTerm)equality.getParameters()[1])
== mTheory.mTrue));
final Term[] disjuncts = ((ApplicationTerm)equality.
getParameters()[0]).getParameters();
final HashMap<Term, TermNegationWrapper> disjunct2patVar =
new HashMap<Term, TermNegationWrapper>(
(int)((disjuncts.length - 1) / 0.75) + 1);
final Term[] pattern = new Term[disjuncts.length];
/* create disjunction object */
final Term[] params = new Term[0];
final Sort[] paramSorts = new Sort[0];
final ApplicationTerm truth = mTheory.mTrue;
final FunctionSymbol not = mTheory.mNot;
int index = -1;
int trueIndex = -1;
boolean firstNegated = false;
Term reasonTerm = null;
int i = 0;
for ( ; i < disjuncts.length; ++i) {
Term disjunct = disjuncts[i];
if (disjunct == truth) {
trueIndex = i;
pattern[i] = truth;
++i;
break;
} else {
final boolean isNegated = ((disjunct instanceof ApplicationTerm)
&&
((ApplicationTerm)disjunct).getFunction() == not);
if (isNegated) {
assert (((ApplicationTerm)disjunct).getParameters().
length == 1);
disjunct =
((ApplicationTerm)disjunct).getParameters()[0];
}
TermNegationWrapper wrapper =
disjunct2patVar.get(disjunct);
if (wrapper == null) {
wrapper = new TermNegationWrapper(
getTerm(PATTERN_VAR_PREFIX + ++index, params,
paramSorts), isNegated);
disjunct2patVar.put(disjunct, wrapper);
} else {
// check for dual occurrence
if (wrapper.mIsNegated ^ isNegated) {
firstNegated = wrapper.mIsNegated;
reasonTerm = wrapper.mTerm;
pattern[i] = (isNegated)
? mTheory.not(wrapper.mTerm)
: wrapper.mTerm;
++i;
break;
}
}
pattern[i] = (isNegated)
? mTheory.not(wrapper.mTerm)
: wrapper.mTerm;
}
}
/*
* more efficient filler of the pattern, do not care what the other
* variables look like
*/
for ( ; i < disjuncts.length; ++i) {
pattern[i] = getTerm(PATTERN_VAR_PREFIX + ++index, params,
paramSorts);
}
assert (pattern[pattern.length - 1] != null);
final String proof;
// no 'True' before term and its negation
if (trueIndex == -1) {
assert (reasonTerm != null);
if (pattern.length == 2) {
if (firstNegated) {
proof = "(rule HOL.simp_thms(5))\n";
} else {
proof = "(rule HOL.simp_thms(4))\n";
}
} else {
final StringBuilder builder = new StringBuilder(128);
if (firstNegated) {
builder.append("(intro rw_orTaut_neg [where p = \"");
} else {
builder.append("(intro rw_orTaut_pos [where p = \"");
}
mConverter.convertToAppendable(reasonTerm, builder);
builder.append("\"] rw_orTaut_elim, "
+ "(erule HOL.disjI1 | rule HOL.disjI2) +)\n");
proof = builder.toString();
}
} else {
// 'True' before term and its negation
// 'True' at first position, shorter proof
if (trueIndex == 0) {
proof = "(rule HOL.simp_thms(30))\n";
} else if (trueIndex == disjuncts.length - 1) {
// 'True' only at last position, extra rule needed
proof = "(intro HOL.simp_thms(30) rw_orTaut_elim, "
+ "rule HOL.refl)\n";
} else {
// 'True' at earlier position
proof = "(intro HOL.simp_thms(30) rw_orTaut_elim)\n";
}
}
return getMainProof(
mTheory.term(equality.getFunction(),
mTheory.term(mTheory.mOr, pattern),
equality.getParameters()[1]),
proof);
}
}
/**
* This class converts the :impToOr rule.
*
* For binary implications the proof has one argument less.
*/
private class ImpToOrRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getName() == "=>")
&& (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length > 1));
if (((ApplicationTerm)equality.getParameters()[0]).
getParameters().length == 2) {
return "(rule rw_impToOr_start, rule rw_impToOr_fin)\n";
} else {
return "(rule rw_impToOr_start, intro rw_impToOr_step, "
+ "rule rw_impToOr_fin)\n";
}
}
}
/**
* This class converts the :divisible rule.
*
* There are four cases: The dividing constant is 1, the divided term is a
* constant (divisible or not), and else a general rewrite.
*/
private class DivisibleRule implements IRule {
@Override
public String convert(final ApplicationTerm equality) {
assert ((equality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getIndices().length == 1));
final Term rhs = equality.getParameters()[1];
// constant divisible result
if (rhs == mTheory.mFalse) {
return mSimplifier.getRule() + "\n";
} else if ((rhs == mTheory.mTrue)) {
// every integer is divisible by 1
if (((ApplicationTerm)equality.getParameters()[0]).
getFunction().getIndices()[0].equals(BigInteger.ONE)) {
return "(rule rw_divisible1)\n";
} else {
// constant divisible result
return mSimplifier.getRule() + "\n";
}
} else {
// normal rewrite
return "(rule rw_divisible, " + mSimplifier.getRule() + ")\n";
}
}
}
/**
* This class converts the :div-1 rule.
*
* The problem is to apply the correct rule according to the factor of the
* term being positive or negative, 1 or -1.
*/
private class DivM1Rule implements IRule {
@Override
public String convert(ApplicationTerm equality) {
assert (equality.getParameters()[0] instanceof ApplicationTerm);
final ApplicationTerm div =
(ApplicationTerm)equality.getParameters()[0];
assert ((div.getFunction().getName() == "div")
&& (div.getParameters().length == 2));
if (div.getParameters()[0] instanceof ApplicationTerm) {
final ApplicationTerm lhs =
(ApplicationTerm)div.getParameters()[0];
final String function = lhs.getFunction().getName();
if ((function == "-") && (lhs.getParameters().length == 1)) {
return "(rule rw_divM1_neg)\n";
} else if (function == "*") {
assert (lhs.getParameters().length == 2);
if (lhs.getParameters()[0] instanceof ApplicationTerm) {
final ApplicationTerm factor =
(ApplicationTerm)lhs.getParameters()[0];
if ((factor.getFunction().getName() == "-")
&& (factor.getParameters().length == 1)
&& (factor.getParameters()[0]
instanceof ConstantTerm)) {
return "(rule rw_divM1_fac_neg)\n";
}
} else {
return "(rule rw_divM1_fac_pos)\n";
}
}
}
return "(rule rw_divM1_pos)\n";
}
}
// [end] rules //
// [start] pattern lemma specific //
/**
* 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 class wraps a pattern and the associated proof.
*/
private class PatternProof {
// the pattern
private final Term mPattern;
// the proof
private final String mProof;
/**
* @param pattern the pattern
* @param proof the proof
*/
public PatternProof(final Term pattern, final String proof) {
mPattern = pattern;
mProof = proof;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append('[');
builder.append(mPattern.toStringDirect());
builder.append(", ");
builder.append(mProof);
builder.deleteCharAt(builder.length() - 1);
builder.append(']');
return builder.toString();
}
}
/**
* This method returns the proof for a pattern lemma in the main file.
* That is, it just uses the pattern lemma.
*
* @param pattern the pattern term
* @param proof the pattern lemma proof string
* @return the proof string in the main file
*/
private String getMainProof(final Term pattern, final String proof) {
// store pattern proof
mLastPatternProof = new PatternProof(pattern, proof);
return "(rule " + REWRITE_PREFIX + ++mLemmaNumber + ")\n";
}
/**
* This method writes a stored pattern lemma to the lemma appendable.
*/
public void writeLemma() {
if (mLastPatternProof != null) {
try {
mLemmaAppendable.append("\nlemma ");
mLemmaAppendable.append(REWRITE_PREFIX);
mLemmaAppendable.append(Integer.toString(mLemmaNumber));
mLemmaAppendable.append(": \"");
mConverter.convertToAppendable(
mLastPatternProof.mPattern, mLemmaAppendable);
mLemmaAppendable.append("\"\nby ");
mLemmaAppendable.append(mLastPatternProof.mProof);
} catch (final IOException e) {
throw new RuntimeException("Appender throws IOException", e);
}
mLastPatternProof = null;
}
}
/**
* This method forgets a stored pattern lemma.
*/
public void forgetLemma() {
if (mLastPatternProof != null) {
mLastPatternProof = null;
--mLemmaNumber;
}
}
// [end] pattern lemma specific //
/**
* This method converts the rewrite rule.
* Many rules only need one application of a lemma.
* Only the :trueNotFalse, :eqSame, :distinctBool, :distinctNeg,
* :distinctTrue, :distinctFalse, :distinctBinary, :notSimp, :impToOr, and
* :divisible rules need more effort.
*
* @param equality the rewrite equality
* @param annotation the specific rule that is used
* @return the proof rule (keywords for special cases!)
*/
public String convert(final ApplicationTerm equality,
final String annotation) {
assert (mAnnot2Rule.get(annotation) != null);
return mAnnot2Rule.get(annotation).convert(equality);
}
}