/*
* 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.util.HashMap;
import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm;
import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm;
import de.uni_freiburg.informatik.ultimate.logic.ConstantTerm;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.Theory;
/**
* This class is used to convert a tautology proof node (@tautology).
* A tautology is used to justify a contradiction.
*
* @author Christian Schilling
*/
public class TautologyConverter extends AConverter {
// map for the rules
private final HashMap<String, IRule> mAnnot2Rule;
/**
* @param appendable appendable to write the proof to
* @param theory the theory
* @param converter term converter
* @param simplifier computation simplifier
*/
public TautologyConverter(final Appendable appendable, final Theory theory,
final TermConverter converter,
final ComputationSimplifier simplifier) {
super(appendable, theory, converter, simplifier);
// fill rule map
mAnnot2Rule = new HashMap<String, TautologyConverter.IRule>(
(int)(21 / 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() {
// CC disequality of 'True' and 'False'
mAnnot2Rule.put(":trueNotFalse",
new SimpleRule("(rule HOL.True_not_False)\n"));
/*
* excluded middle for a binary disjunction of a negated :quoted
* disjunction and its non-quoted version
*/
mAnnot2Rule.put(":or+",
new SimpleRule("(rule HOL.excluded_middle)\n"));
// disjunction of a disjunction and one of its disjuncts negated
mAnnot2Rule.put(":or-",
new OrMinusRule());
/*
* disjunction of negated if-then-else, negative condition, and
* positive first case
*/
mAnnot2Rule.put(":ite+1",
new SimpleRule("(rule taut_iteP1)\n"));
/*
* disjunction of negated if-then-else, positive condition, and
* positive second case
*/
mAnnot2Rule.put(":ite+2",
new SimpleRule("(rule taut_iteP2)\n"));
/*
* disjunction of negated if-then-else, positive first case, and
* positive second case
*/
mAnnot2Rule.put(":ite+red",
new SimpleRule("(rule taut_itePRed)\n"));
/*
* disjunction of if-then-else, negative condition, and
* negative first case
*/
mAnnot2Rule.put(":ite-1",
new SimpleRule("(rule taut_iteM1)\n"));
/*
* disjunction of if-then-else, positive condition, and
* negative second case
*/
mAnnot2Rule.put(":ite-2",
new SimpleRule("(rule taut_iteM2)\n"));
/*
* disjunction of if-then-else, negative first case, and
* negative second case
*/
mAnnot2Rule.put(":ite-red",
new SimpleRule("(rule taut_iteMRed)\n"));
/*
* disjunction of negated Boolean equality, first term positive, and
* second term negative
*/
mAnnot2Rule.put(":=+1",
new SimpleRule("(rule taut_EqP1)\n"));
/*
* disjunction of negated Boolean equality, first term negative, and
* second term positive
*/
mAnnot2Rule.put(":=+2",
new SimpleRule("(rule taut_EqP2)\n"));
// disjunction of Boolean equality and both terms positive
mAnnot2Rule.put(":=-1",
new SimpleRule("(rule taut_EqM1)\n"));
// disjunction of Boolean equality and both terms negative
mAnnot2Rule.put(":=-2",
new SimpleRule("(rule taut_EqM2)\n"));
// excluded middle with equality with 'True'
mAnnot2Rule.put(":excludedMiddle1",
new ExcludedMiddle1Rule());
// excluded middle with equality with 'False'
mAnnot2Rule.put(":excludedMiddle2",
new ExcludedMiddle2Rule());
// non-Boolean if-then-else
mAnnot2Rule.put(":termITE",
new TermIteRule());
// lower bound for div
mAnnot2Rule.put(":divLow",
new DivLowRule());
// upper bound for div
mAnnot2Rule.put(":divHigh",
new DivHighRule());
// lower bound for mixed logic conversion
mAnnot2Rule.put(":toIntLow",
new ToIntLowRule());
// upper bound for mixed logic conversion
mAnnot2Rule.put(":toIntHigh",
new ToIntHighRule());
/*
* excluded middle for equality in canonical sum
* NOTE: The simplifier is too weak, for instance the formula
* '(y + (- x) = (0::int)) | (~ x = y)' with x, y integers
* cannot be proven.
*/
mAnnot2Rule.put(":eq",
new SimpleRule("auto\n"));
}
/**
* This interface is used for the rule translation.
*/
private interface IRule {
/**
* @param tautology the tautology
*/
void convert(final ApplicationTerm tautology);
}
/**
* 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 void convert(final ApplicationTerm tautology) {
writeString(mRule);
}
}
/**
* This class translates the :or- rule.
*
* The tautology has the form of a binary disjunction, where the first
* disjunct is a :quoted disjunction itself and the second disjunct is a
* negated term (possibly doubly negated). The second term without the
* negation can be found in the :quoted disjunction.
*
* The translation is based on the 'intro' method in Isabelle.
*
* If the disjunct can be found in the lowest level of the disjunction,
* a slightly faster translation is used.
*
* But the :quoted disjunction may have to be flattened (that is, inner
* disjunctions have to be unpacked). This case is also possible with
* 'intro', but needs more rule arguments.
*/
private class OrMinusRule implements IRule {
@Override
public void convert(final ApplicationTerm tautology) {
final Term[] disjunction = tautology.getParameters();
assert ((disjunction.length == 2)
&& (disjunction[0] instanceof AnnotatedTerm)
&& (((AnnotatedTerm)disjunction[0]).getSubterm()
instanceof ApplicationTerm)
&& (((ApplicationTerm)((AnnotatedTerm)disjunction[0]).
getSubterm()).getFunction() == mTheory.mOr)
&& (disjunction[1] instanceof ApplicationTerm)
&& (((ApplicationTerm)disjunction[1]).getFunction()
== mTheory.mNot));
final Term[] quotedDisjunction =
((ApplicationTerm)((AnnotatedTerm)disjunction[0]).
getSubterm()).getParameters();
final Term target =
((ApplicationTerm)disjunction[1]).getParameters()[0];
// find the single disjunct (without negation)
for (int i = 0; i < quotedDisjunction.length; ++i) {
// disjunct was found
if (quotedDisjunction[i] == target) {
// disjunct is the last one, use different final rule
if (i == quotedDisjunction.length - 1) {
writeString("(intro taut_orM_fin_bin taut_orM_intro)\n");
} else {
// disjunct is not the last one, shortcut after finding it
writeString("(intro taut_orM_fin taut_orM_intro)\n");
}
return;
}
}
// the disjunct was not found, so flatten the quoted disjunction
writeString("(intro taut_orM_fin taut_orM_fin_bin taut_orM_flat taut_orM_intro)\n");
}
}
/**
* This class converts the :excludedMiddle1 rule.
*
* This is a variation of the 'rule of the excluded middle', where a
* disjunction consists of a negated formula '~F' and the equality
* 'F = True'.
*
* The reason why the translation is not possible with a static rule is
* that the order in the equality is not fixed.
* Note that this is not necessary, but only for better performance.
* Alternatively, both rules could be added to an alternative choice ('|')
* like '(rule r1 | rule r2)'.
*/
private class ExcludedMiddle1Rule implements IRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction() == mTheory.mOr)
&& (tautology.getParameters().length == 2));
final Term[] equality;
// unquoted term
if (tautology.getParameters()[1] instanceof ApplicationTerm) {
assert (((ApplicationTerm)tautology.getParameters()[1]).
getFunction().getName() == "=");
equality = ((ApplicationTerm)tautology.getParameters()[1]).
getParameters();
} else {
// quoted term
assert ((tautology.getParameters()[1] instanceof AnnotatedTerm)
&&
(((AnnotatedTerm)tautology.getParameters()[1]).
getAnnotations().length == 1)
&& (((AnnotatedTerm)tautology.getParameters()[1]).
getAnnotations()[0].getKey() == ":quoted")
&& (((AnnotatedTerm)tautology.getParameters()[1]).
getSubterm() instanceof ApplicationTerm)
&& (((ApplicationTerm)((AnnotatedTerm)tautology.
getParameters()[1]).getSubterm()).
getFunction().getName() == "="));
equality = ((ApplicationTerm)((AnnotatedTerm)tautology.
getParameters()[1]).getSubterm()).getParameters();
}
assert (equality.length == 2);
// 'True' is on the right-hand side in the equality
if (equality[0] == mTheory.mTrue) {
writeString("(rule taut_exclMid_tl)\n");
} else {
// 'True' is on the left-hand side in the equality
assert (equality[1] == mTheory.mTrue);
writeString("(rule taut_exclMid_tr)\n");
}
}
}
/**
* This class converts the :excludedMiddle2 rule.
*
* This is a variation of the 'rule of the excluded middle', where a
* disjunction consists of a positive formula 'F' and the equality
* 'F = False'.
*
* The reason why the translation is not possible with a static rule is
* that the order in the equality is not fixed.
* Note that this is not necessary, but only for better performance.
* Alternatively, both rules could be added to an alternative choice ('|')
* like '(rule r1 | rule r2)'.
*/
private class ExcludedMiddle2Rule implements IRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction() == mTheory.mOr)
&& (tautology.getParameters().length == 2));
final Term[] equality;
// unquoted term
if (tautology.getParameters()[1] instanceof ApplicationTerm) {
assert (((ApplicationTerm)tautology.getParameters()[1]).
getFunction().getName() == "=");
equality = ((ApplicationTerm)tautology.getParameters()[1]).
getParameters();
} else {
// quoted term
assert ((tautology.getParameters()[1] instanceof AnnotatedTerm)
&& (((AnnotatedTerm)tautology.getParameters()[1]).
getAnnotations().length == 1)
&& (((AnnotatedTerm)tautology.getParameters()[1]).
getAnnotations()[0].getKey() == ":quoted")
&& (((AnnotatedTerm)tautology.getParameters()[1]).
getSubterm() instanceof ApplicationTerm)
&& (((ApplicationTerm)((AnnotatedTerm)tautology.
getParameters()[1]).getSubterm()).
getFunction().getName() == "="));
equality = ((ApplicationTerm)((AnnotatedTerm)tautology.
getParameters()[1]).getSubterm()).getParameters();
}
assert (equality.length == 2);
// 'False' is on the right-hand side in the equality
if (equality[0] == mTheory.mTrue) {
writeString("(rule taut_exclMid_fl)\n");
} else {
// 'False' is on the left-hand side in the equality
assert (equality[1] == mTheory.mFalse);
writeString("(rule taut_exclMid_fr)\n");
}
}
}
/**
* This class converts the :termITE rule.
*
* The tautology has the form of an (n+1)-ary disjunction.
* It justifies the simplification of an if-then-else tree
* ((n+1)-th disjunct), where the i-th disjunct corresponds the i-th
* condition.
*
* The translation is based on the 'intro' method in Isabelle.
*
* The binary case is just a shorter version of the n-ary case.
*/
private class TermIteRule implements IRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction() == mTheory.mOr)
&& (tautology.getParameters().length > 1));
final Term[] disjuncts = tautology.getParameters();
// n-ary case
if (disjuncts.length > 2) {
writeString("(intro taut_termITE_unroll, intro "
+ "taut_termITE_then taut_termITE_else, "
+ "intro taut_termITE_then_last "
+ "taut_termITE_else_last)\n");
} else {
writeString("(intro taut_termITE_then_last taut_termITE_else_last)\n");
}
}
}
/**
* This class handles identical steps for the rules :divLow and :divHigh.
*/
private abstract class ADivRule implements IRule {
/**
* There are several problems with this rule:
* SMTInterpol does not use this rule for the constant zero, but
* Isabelle has to prove this.
* The left-hand side of the inequality can be permuted, which is why
* the simplifier is used to capture all possible cases. Also, to find
* the constant and the term (which must be given to Isabelle) becomes
* harder.
* Identifying the correct 'div' term is not trivial, since two
* summands can have this form.
*/
@Override
public abstract void convert(final ApplicationTerm tautology);
/**
* This method finds the correct terms.
* It can be the case that the other summand also has the form
* 'd * (x div d)'.
*
* NOTE: In the end the rule uses the simplifier to show that the
* constant is different from zero, but this is no problem, since
* a constant cannot be unpacked.
*
* @param first the first summand
* @param second the second summand
* @param isLow true iff rule is 'Low', else rule is 'High'
*/
protected void findDiv(final ApplicationTerm first,
final ApplicationTerm second, final boolean isLow) {
ApplicationTerm product = null;
if ((first.getFunction().getName() == "*")
&& (first.getParameters().length == 2)) {
// further investigation
if ((second.getFunction().getName() == "*")
&& (second.getParameters().length == 2)) {
// look into first summand
final Term factor = first.getParameters()[1];
if ((factor instanceof ApplicationTerm)
&& (((ApplicationTerm)factor).getFunction().
getName() == "div")) {
final ApplicationTerm div = (ApplicationTerm)factor;
assert ((div.getParameters().length == 2)
&& (div.getParameters()[0]
instanceof ApplicationTerm)
&& (((ApplicationTerm)div.getParameters()[0]).
getFunction().getName() == "*")
&& (((ApplicationTerm)div.getParameters()[0]).
getParameters().length == 2));
final ApplicationTerm divFirst =
(ApplicationTerm)div.getParameters()[0];
// first summand is the target product
if ((divFirst.getFunction().getName() == "*")
&& (divFirst.getParameters().length == 2)
&& (divFirst.getParameters()[1]
== second.getParameters()[1])) {
product = first;
}
}
// second summand is the target product
if (product == null) {
product = second;
}
} else {
// first summand is the product
product = first;
}
} else {
// second summand is the product
assert ((second.getFunction().getName() == "*")
&& (second.getParameters().length == 2));
product = second;
}
// print swap
if (product == first) {
if (isLow) {
writeString("(rule taut_divLow");
} else {
writeString("(rule taut_divHigh");
}
} else {
if (isLow) {
writeString("(rule taut_swap_sum_bin, rule taut_divLow");
} else {
writeString("(rule taut_swap_sum_ter, rule taut_divHigh");
}
}
// found the target product, extract the terms
assert ((product != null)
&& (product.getParameters().length == 2)
&& (product.getParameters()[1] instanceof ApplicationTerm)
&& (((ApplicationTerm)product.getParameters()[1]).
getFunction().getName() == "div"));
final ApplicationTerm div =
(ApplicationTerm)product.getParameters()[1];
assert ((div.getParameters().length == 2)
&& (div.getParameters()[0] instanceof ApplicationTerm)
&& (product.getParameters()[0] == div.getParameters()[1]));
final ApplicationTerm term =
(ApplicationTerm)div.getParameters()[0];
final String function = term.getFunction().getName();
// factor is -1 (special syntax)
if (function == "-") {
writeString("_neg1, ");
} else if (function == "*") {
// factor is > 1 or < -1, use a different rule
boolean negative = false;
assert (term.getParameters().length > 1);
final Term firstFactor = term.getParameters()[0];
if (firstFactor instanceof ApplicationTerm) {
final ApplicationTerm aFirstFactor =
(ApplicationTerm)firstFactor;
if (aFirstFactor.getFunction().getName() == "-") {
assert (aFirstFactor.getParameters().length > 0);
// factor is < -1
if (aFirstFactor.getParameters()[0] instanceof ConstantTerm) {
negative = true;
writeString("_neg2, ");
}
}
}
// factor is > 1
if (!negative) {
writeString("_pos2, ");
}
} else {
// factor is 1 (special syntax)
writeString("_pos1, ");
}
writeString(mSimplifier.getRule());
writeString(")\n");
}
}
/**
* This class converts the :divLow rule.
*/
private class DivLowRule extends ADivRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction().getName() == "<=")
&& (tautology.getParameters().length == 2)
&& (tautology.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)tautology.getParameters()[0]).
getFunction().getName() == "+"));
final Term[] summands = ((ApplicationTerm)tautology.
getParameters()[0]).getParameters();
assert ((summands.length == 2)
&& (summands[0] instanceof ApplicationTerm)
&& (summands[1] instanceof ApplicationTerm));
super.findDiv((ApplicationTerm)summands[0],
(ApplicationTerm)summands[1], true);
}
}
/**
* This class converts the :divHigh rule.
*/
private class DivHighRule extends ADivRule {
/**
* {@inheritDoc}
* Also, the absolute value of the constant has to be explicitly
* computed (again, this is not problematic).
*/
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction() == mTheory.mNot)
&& (tautology.getParameters().length == 1)
&& (tautology.getParameters()[0] instanceof ApplicationTerm));
final ApplicationTerm inequality =
(ApplicationTerm)tautology.getParameters()[0];
assert ((inequality.getFunction().getName() == "<=")
&& (inequality.getParameters().length == 2)
&& (inequality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)inequality.getParameters()[0]).
getFunction().getName() == "+"));
final Term[] summands = ((ApplicationTerm)inequality.
getParameters()[0]).getParameters();
// NOTE: The constant 1 is always the right-most term in the sum.
assert ((summands.length == 3)
&& (summands[0] instanceof ApplicationTerm)
&& (summands[1] instanceof ApplicationTerm)
&& (summands[2] instanceof ConstantTerm));
super.findDiv((ApplicationTerm)summands[0],
(ApplicationTerm)summands[1], false);
}
}
/**
* This class handles identical steps for the rules :divLow and :divHigh.
*/
private abstract class AToIntRule implements IRule {
/**
* There are several problems with this rule:
* The left-hand side of the inequality can be permuted.
* Identifying the correct outer term is not trivial, since both
* summands can have the form 'to_real(to_int(x))'.
*/
@Override
public abstract void convert(final ApplicationTerm tautology);
/**
* This method finds the correct term.
* It can be the case that the other summand also has the form
* 'to_real(to_int(x))'.
*
* @param first the first summand
* @param second the second summand
* @param isLow true iff rule is 'Low', else rule is 'High'
*/
private void findToInt(final ApplicationTerm first,
final ApplicationTerm second, final boolean isLow) {
ApplicationTerm toReal = null;
if (first.getFunction().getName() == "to_real") {
assert (first.getParameters().length == 1);
// further investigation
if (second.getFunction().getName() == "to_real") {
assert (second.getParameters().length == 1);
// look into first summand
if (first.getParameters()[0] instanceof ApplicationTerm) {
final ApplicationTerm toInt =
(ApplicationTerm)first.getParameters()[0];
if (toInt.getFunction().getName() == "to_int") {
assert (toInt.getParameters().length == 1);
if (toInt.getParameters()[0] instanceof ApplicationTerm) {
final ApplicationTerm subTerm =
(ApplicationTerm)toInt.
getParameters()[0];
if (subTerm.getFunction().getName() == "-") {
assert (subTerm.getParameters().length == 1);
// first summand is the target term
if (subTerm.getParameters()[0] == second) {
toReal = first;
}
}
}
}
}
// second summand is the target term
if (toReal == null) {
toReal = second;
}
} else {
// first summand is the target term
toReal = first;
}
} else {
// second summand is the target term
assert ((second.getFunction().getName() == "to_real")
&& (second.getParameters().length == 1));
toReal = second;
}
// print swap
if (toReal == first) {
if (isLow) {
writeString("(rule taut_toIntLow");
} else {
writeString("(rule taut_toIntHigh");
}
} else {
if (isLow) {
writeString("(rule taut_swap_sum_bin, rule taut_toIntLow");
} else {
writeString(
"(rule taut_swap_sum_ter, rule taut_toIntHigh");
}
}
// found the target term, extract the sub-term
assert ((toReal != null)
&& (toReal.getFunction().getName() == "to_real")
&& (toReal.getParameters().length == 1)
&& (toReal.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)toReal.getParameters()[0]).
getFunction().getName() == "to_int")
&& (((ApplicationTerm)toReal.getParameters()[0]).
getParameters().length == 1)
&& (((ApplicationTerm)toReal.getParameters()[0]).
getParameters()[0] instanceof ApplicationTerm));
final ApplicationTerm term = (ApplicationTerm)((ApplicationTerm)
toReal.getParameters()[0]).getParameters()[0];
final String function = term.getFunction().getName();
// factor is -1 (special syntax)
if (function == "-") {
writeString("_neg1)\n");
} else if (function == "*") {
// factor is > 1 or < -1, use a different rule
assert (term.getParameters().length > 1);
final Term firstFactor = term.getParameters()[0];
if (firstFactor instanceof ApplicationTerm) {
final ApplicationTerm aFirstFactor =
(ApplicationTerm)firstFactor;
if (aFirstFactor.getFunction().getName() == "-") {
assert (aFirstFactor.getParameters().length > 0);
// factor is < -1
if (aFirstFactor.getParameters()[0] instanceof ConstantTerm) {
writeString("_neg2)\n");
return;
}
}
}
// factor is > 1
writeString("_pos2)\n");
} else {
// factor is 1 (special syntax)
writeString("_pos1)\n");
}
}
}
/**
* This class converts the :toIntLow rule.
*/
private class ToIntLowRule extends AToIntRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction().getName() == "<=")
&& (tautology.getParameters().length == 2)
&& (tautology.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)tautology.getParameters()[0]).
getFunction().getName() == "+"));
final Term[] summands = ((ApplicationTerm)tautology.
getParameters()[0]).getParameters();
assert ((summands.length == 2)
&& (summands[0] instanceof ApplicationTerm)
&& (summands[1] instanceof ApplicationTerm));
super.findToInt((ApplicationTerm)summands[0],
(ApplicationTerm)summands[1], true);
}
}
/**
* This class converts the :toIntHigh rule.
*/
private class ToIntHighRule extends AToIntRule {
@Override
public void convert(final ApplicationTerm tautology) {
assert ((tautology.getFunction() == mTheory.mNot)
&& (tautology.getParameters().length == 1)
&& (tautology.getParameters()[0] instanceof ApplicationTerm));
final ApplicationTerm inequality =
(ApplicationTerm)tautology.getParameters()[0];
assert ((inequality.getFunction().getName() == "<=")
&& (inequality.getParameters().length == 2)
&& (inequality.getParameters()[0] instanceof ApplicationTerm)
&& (((ApplicationTerm)inequality.getParameters()[0]).
getFunction().getName() == "+"));
final Term[] summands = ((ApplicationTerm)inequality.
getParameters()[0]).getParameters();
// NOTE: The constant 1 is always the right-most term in the sum.
assert ((summands.length == 3)
&& (summands[0] instanceof ApplicationTerm)
&& (summands[1] instanceof ApplicationTerm)
&& (summands[2] instanceof ConstantTerm));
super.findToInt((ApplicationTerm)summands[0],
(ApplicationTerm)summands[1], false);
}
}
// [end] rules //
/**
* This method converts the tautology rule.
* Many rules only need one application of a lemma.
* Only the rules :or-, :excludedMiddle1, :excludedMiddle2, :termITE,
* :divLow, :divHigh, :toIntLow, and :toIntHigh need more effort.
*
* @param tautology the tautology
* @param annotation the specific rule that is used
*/
public void convert(final ApplicationTerm tautology,
final String annotation) {
mConverter.convert(tautology);
writeString("\" by ");
assert (mAnnot2Rule.get(annotation) != null);
mAnnot2Rule.get(annotation).convert(tautology);
}
}