/*****************************************************************************/ /*! * \file search_impl_base.cpp * * Author: Clark Barrett, Vijay Ganesh (CNF Converter) * * Created: Fri Jan 17 14:19:54 2003 * *
* * License to use, copy, modify, sell and/or distribute this software * and its documentation for any purpose is hereby granted without * royalty, subject to the terms and conditions defined in the \ref * LICENSE file provided with this distribution. * *
* */ /*****************************************************************************/ #include "search_impl_base.h" #include "theory.h" #include "eval_exception.h" #include "search_rules.h" #include "variable.h" #include "command_line_flags.h" #include "statistics.h" #include "theorem_manager.h" #include "expr_transform.h" using namespace std; namespace CVC3 { class CoreSatAPI_implBase :public TheoryCore::CoreSatAPI { SearchImplBase* d_se; public: CoreSatAPI_implBase(SearchImplBase* se) : d_se(se) {} virtual ~CoreSatAPI_implBase() {} virtual void addLemma(const Theorem& thm) { d_se->addFact(thm); } virtual Theorem addAssumption(const Expr& assump) { return d_se->newIntAssumption(assump); } virtual void addSplitter(const Expr& e, int priority) { d_se->addSplitter(e, priority); } virtual bool check(const Expr& e); }; bool CoreSatAPI_implBase::check(const Expr& e) { Theorem thm; int scope = d_se->theoryCore()->getCM()->scopeLevel(); d_se->theoryCore()->getCM()->push(); QueryResult res = d_se->checkValid(e, thm); d_se->theoryCore()->getCM()->popto(scope); return res == VALID; } } using namespace CVC3; // class SearchImplBase::Splitter methods // Constructor SearchImplBase::Splitter::Splitter(const Literal& lit): d_lit(lit) { d_lit.count()++; TRACE("Splitter", "Splitter(", d_lit, ")"); } // Copy constructor SearchImplBase::Splitter::Splitter(const SearchImplBase::Splitter& s) : d_lit(s.d_lit) { d_lit.count()++; TRACE("Splitter", "Splitter[copy](", d_lit, ")"); } // Assignment SearchImplBase::Splitter& SearchImplBase::Splitter::operator=(const SearchImplBase::Splitter& s) { if(this == &s) return *this; // Self-assignment d_lit.count()--; d_lit = s.d_lit; d_lit.count()++; TRACE("Splitter", "Splitter[assign](", d_lit, ")"); return *this; } // Destructor SearchImplBase::Splitter::~Splitter() { d_lit.count()--; TRACE("Splitter", "~Splitter(", d_lit, ")"); } //! Constructor SearchImplBase::SearchImplBase(TheoryCore* core) : SearchEngine(core), d_bottomScope(core->getCM()->getCurrentContext()), d_dpSplitters(core->getCM()->getCurrentContext()), d_lastValid(d_commonRules->trueTheorem()), d_assumptions(core->getCM()->getCurrentContext()), d_cnfCache(core->getCM()->getCurrentContext()), d_cnfVars(core->getCM()->getCurrentContext()), d_cnfOption(&(core->getFlags()["cnf"].getBool())), d_ifLiftOption(&(core->getFlags()["iflift"].getBool())), d_ignoreCnfVarsOption(&(core->getFlags()["ignore-cnf-vars"].getBool())), d_origFormulaOption(&(core->getFlags()["orig-formula"].getBool())), d_enqueueCNFCache(core->getCM()->getCurrentContext()), d_applyCNFRulesCache(core->getCM()->getCurrentContext()), d_replaceITECache(core->getCM()->getCurrentContext()) { d_vm = new VariableManager(core->getCM(), d_rules, core->getFlags()["mm"].getString()); IF_DEBUG(d_assumptions.setName("CDList[SearchImplBase::d_assumptions]")); d_coreSatAPI_implBase = new CoreSatAPI_implBase(this); core->registerCoreSatAPI(d_coreSatAPI_implBase); } //! Destructor SearchImplBase::~SearchImplBase() { delete d_coreSatAPI_implBase; delete d_vm; } // Returns a new theorem if e has not already been asserted, otherwise returns // a NULL theorem. Theorem SearchImplBase::newUserAssumption(const Expr& e) { Theorem thm; CDMap::iterator i(d_assumptions.find(e)); IF_DEBUG(if(debugger.trace("search verbose")) { ostream& os(debugger.getOS()); os << "d_assumptions = ["; for(CDMap::iterator i=d_assumptions.begin(), iend=d_assumptions.end(); i!=iend; ++i) { debugger.getOS() << "(" << (*i).first << " => " << (*i).second << "), "; } os << "]" << endl; }); if(i!=d_assumptions.end()) { TRACE("search verbose", "newUserAssumption(", e, "): assumption already exists"); } else { thm = d_commonRules->assumpRule(e); d_assumptions[e] = thm; e.setUserAssumption(); TRACE("search verbose", "newUserAssumption(", thm, "): created new assumption"); } if (!thm.isNull()) d_core->addFact(d_core->getExprTrans()->preprocess(thm)); return thm; } // Same as newUserAssertion, except it's an error to assert something that's // already been asserted. Theorem SearchImplBase::newIntAssumption(const Expr& e) { Theorem thm = d_commonRules->assumpRule(e); Expr atom = e.isNot() ? e[0] : e; thm.setQuantLevel(theoryCore()->getQuantLevelForTerm(atom)); newIntAssumption(thm); return thm; } void SearchImplBase::newIntAssumption(const Theorem& thm) { DebugAssert(!d_assumptions.count(thm.getExpr()), "newIntAssumption: repeated assertion: "+thm.getExpr().toString()); d_assumptions[thm.getExpr()] = thm; thm.getExpr().setIntAssumption(); TRACE("search verbose", "newIntAssumption(", thm, "): new assumption"); } void SearchImplBase::getUserAssumptions(vector& assumptions) { for(CDMap::orderedIterator i=d_assumptions.orderedBegin(), iend=d_assumptions.orderedEnd(); i!=iend; ++i) if ((*i).first.isUserAssumption()) assumptions.push_back((*i).first); } void SearchImplBase::getInternalAssumptions(std::vector& assumptions) { for(CDMap::orderedIterator i=d_assumptions.orderedBegin(), iend=d_assumptions.orderedEnd(); i!=iend; ++i) if ((*i).first.isIntAssumption()) assumptions.push_back((*i).first); } void SearchImplBase::getAssumptions(std::vector& assumptions) { for(CDMap::orderedIterator i=d_assumptions.orderedBegin(), iend=d_assumptions.orderedEnd(); i!=iend; ++i) assumptions.push_back((*i).first); } bool SearchImplBase::isAssumption(const Expr& e) { return d_assumptions.count(e) > 0; } void SearchImplBase::addCNFFact(const Theorem& thm, bool fromCore) { TRACE("addCNFFact", "addCNFFact(", thm.getExpr(), ") {"); if(thm.isAbsLiteral()) { addLiteralFact(thm); // These literals are derived, and should also be reported to the core. if(!fromCore) d_core->enqueueFact(thm); } else { addNonLiteralFact(thm); } TRACE_MSG("addCNFFact", "addCNFFact => }"); } void SearchImplBase::addFact(const Theorem& thm) { TRACE("search addFact", "SearchImplBase::addFact(", thm.getExpr(), ") {"); if(*d_cnfOption) enqueueCNF(thm); else addCNFFact(thm, true); TRACE_MSG("search addFact", "SearchImplBase::addFact => }"); } void SearchImplBase::addSplitter(const Expr& e, int priority) { DebugAssert(e.isAbsLiteral(), "SearchImplBase::addSplitter("+e.toString()+")"); d_dpSplitters.push_back(Splitter(newLiteral(e))); } void SearchImplBase::getCounterExample(vector& assertions, bool inOrder) { if(!d_core->getTM()->withAssumptions()) throw EvalException ("Method getCounterExample() (or command COUNTEREXAMPLE) cannot be used\n" " without assumptions activated"); if(!d_lastValid.isNull()) throw EvalException ("Method getCounterExample() (or command COUNTEREXAMPLE)\n" " must be called only after failed QUERY"); getInternalAssumptions(assertions); /* if(!d_lastCounterExample.empty()) { if (inOrder) { for(CDMap::orderedIterator i=d_assumptions.orderedBegin(), iend=d_assumptions.orderedEnd(); i!=iend; ++i) { if (d_lastCounterExample.find((*i).first) != d_lastCounterExample.end()) { assertions.push_back((*i).first); } } DebugAssert(assertions.size()==d_lastCounterExample.size(), "getCounterExample: missing assertion"); } else { ExprHashMap::iterator i=d_lastCounterExample.begin(), iend = d_lastCounterExample.end(); for(; i!=iend; ++i) assertions.push_back((*i).first); } } */ } Proof SearchImplBase::getProof() { if(!d_core->getTM()->withProof()) throw EvalException ("DUMP_PROOF cannot be used without proofs activated"); if(d_lastValid.isNull()) throw EvalException ("DUMP_PROOF must be called only after successful QUERY"); // Double-check for optimized version if(d_lastValid.isNull()) return Proof(); return d_lastValid.getProof(); } const Assumptions& SearchImplBase::getAssumptionsUsed() { if(!d_core->getTM()->withAssumptions()) throw EvalException ("DUMP_ASSUMPTIONS cannot be used without assumptions activated"); if(d_lastValid.isNull()) throw EvalException ("DUMP_ASSUMPTIONS must be called only after successful QUERY"); // Double-check for optimized version if(d_lastValid.isNull()) return Assumptions::emptyAssump(); return d_lastValid.getAssumptionsRef(); } /*! * Given a proof of FALSE ('res') from an assumption 'e', derive the * proof of the inverse of e. * * \param res is a proof of FALSE * \param e is the assumption used in the above proof */ void SearchImplBase::processResult(const Theorem& res, const Expr& e) { // Result must be either Null (== SAT) or false (== unSAT) DebugAssert(res.isNull() || res.getExpr().isFalse(), "processResult: bad input" + res.toString()); if(res.isNull()) { // Didn't prove valid (if CVC-lite is complete, it means invalid). // The assumptions for the counterexample must have been already // set by checkSAT(). d_lastValid = Theorem(); // Reset last proof // Remove !e, keep just the counterexample d_lastCounterExample.erase(!e); if (e.isNot()) d_lastCounterExample.erase(e[0]); } else { // Proved valid Theorem res2 = d_rules->eliminateSkolemAxioms(res, d_commonRules->getSkolemAxioms()); if(e.isNot()) d_lastValid = d_rules->negIntro(e, res2); else d_lastValid = d_rules->proofByContradiction(e, res2); d_lastCounterExample.clear(); // Reset counterexample } } QueryResult SearchImplBase::checkValid(const Expr& e, Theorem& result) { d_commonRules->clearSkolemAxioms(); QueryResult qres = checkValidInternal(e); result = d_lastValid; return qres; } QueryResult SearchImplBase::restart(const Expr& e, Theorem& result) { QueryResult qres = restartInternal(e); result = d_lastValid; return qres; } void SearchImplBase::enqueueCNF(const Theorem& beta) { TRACE("mycnf", "enqueueCNF(", beta, ") {"); if(*d_origFormulaOption) addCNFFact(beta); enqueueCNFrec(beta); TRACE_MSG("mycnf", "enqueueCNF => }"); } void SearchImplBase::enqueueCNFrec(const Theorem& beta) { Theorem theta = beta; TRACE("mycnf","enqueueCNFrec(", theta.getExpr(), ") { "); TRACE("cnf-clauses", "enqueueCNF call", theta.getExpr(), ""); // The theorem scope shouldn't be higher than current DebugAssert(theta.getScope() <= scopeLevel(), "\n theta.getScope()="+int2string(theta.getScope()) +"\n scopeLevel="+int2string(scopeLevel()) +"\n e = "+theta.getExpr().toString()); Expr thetaExpr = theta.getExpr(); if(d_enqueueCNFCache.count(thetaExpr) > 0) { TRACE_MSG("mycnf", "enqueueCNFrec[cached] => }"); return; } d_enqueueCNFCache[thetaExpr] = true; // // Strip double negations first while(thetaExpr.isNot() && thetaExpr[0].isNot()) { theta = d_commonRules->notNotElim(theta); thetaExpr = theta.getExpr(); // Cache all the derived theorems too d_enqueueCNFCache[thetaExpr] = true; } // Check for a propositional literal if(thetaExpr.isPropLiteral()) { theta = d_commonRules->iffMP(theta, replaceITE(thetaExpr)); DebugAssert(!*d_ifLiftOption || theta.isAbsLiteral(), "Must be a literal: theta = " +theta.getExpr().toString()); addCNFFact(theta); TRACE_MSG("mycnf", "enqueueCNFrec[literal] => }"); TRACE("cnf-clauses", "literal with proofs(", theta.getExpr(), ")"); return; } thetaExpr = theta.getExpr(); // Obvious optimizations for AND and OR switch(thetaExpr.getKind()) { case AND: // Split up the top-level conjunction and translate individually for(int i=0; iandElim(theta, i)); TRACE_MSG("mycnf", "enqueueCNFrec[AND] => }"); return; case OR: { // Check if we are already in CNF (ignoring ITEs in the terms), // and if we are, translate term ITEs on the way bool cnf(true); TRACE("bv mycnf", "enqueueCNFrec[OR] ( ", theta.getExpr().toString(), ")"); for(Expr::iterator i=thetaExpr.begin(),iend=thetaExpr.end(); i!=iend && cnf; i++) { if(!(*i).isPropLiteral()) cnf = false; } if(cnf) { vector thms; vector changed; unsigned int cc=0; for(Expr::iterator i=thetaExpr.begin(),iend=thetaExpr.end(); i!=iend; i++,cc++) { Theorem thm = replaceITE(*i); if(thm.getLHS() != thm.getRHS()) { thms.push_back(thm); changed.push_back(cc); } } if(changed.size() > 0) { Theorem rewrite = d_commonRules->substitutivityRule(theta.getExpr(), changed, thms); theta = d_commonRules->iffMP(theta, rewrite); } addCNFFact(theta); TRACE_MSG("mycnf", "enqueueCNFrec[cnf] => }"); return; } break; } case IFF: { // Check for "var <=> phi" and "phi <=> var" const Expr& t0 = thetaExpr[0]; const Expr& t1 = thetaExpr[1]; if(t1.isPropLiteral()) { if(!t1.isAbsLiteral()) theta = d_commonRules->transitivityRule(theta, replaceITE(t1)); applyCNFRules(theta); return; } else if(thetaExpr[0].isPropLiteral()) { theta = d_commonRules->symmetryRule(theta); if(!t0.isAbsLiteral()) theta = d_commonRules->transitivityRule(theta, replaceITE(t0)); applyCNFRules(theta); return; } break; } case ITE: if(thetaExpr[0].isPropLiteral() && thetaExpr[1].isPropLiteral() && thetaExpr[2].isPropLiteral()) { // Replace ITEs vector thms; vector changed; for(int c=0, cend=thetaExpr.arity(); c }"); return; } // std::vector clauses; Theorem result; // (\phi <==> v) result = d_commonRules->varIntroSkolem(theta.getExpr()); TRACE("mycnf", "enqueueCNFrec: varIntroSkolem => ", result.getExpr(), " @ "+int2string(result.getScope()) +" (current="+int2string(scopeLevel())+")"); //result = skolemize(result, false); TRACE("mycnf", "enqueueCNFrec: skolemize => ", result.getExpr(), " @ "+int2string(result.getScope()) +" (current="+int2string(scopeLevel())+")"); DebugAssert(result.isRewrite(), "SearchImplBase::CNF(): result = "+result.toString()); DebugAssert(!result.getLHS().isPropLiteral() && result.getRHS().isPropLiteral(), "SearchImplBase::CNF(): result = "+result.toString()); DebugAssert(result.getLHS() == theta.getExpr(), "SearchImplBase::CNF(): result = "+result.toString() +"\n theta = "+theta.toString()); //enqueue v Theorem var(d_commonRules->iffMP(theta, result)); // Add variable to the set of CNF vars d_cnfVars[var.getExpr()] = true; TRACE("mycnf", "enqueueCNFrec: theta = ", theta.getExpr(), " @ "+int2string(theta.getScope()) +" (current="+int2string(scopeLevel())+")"); TRACE("mycnf", "enqueueCNFrec: var = ", var.getExpr(), " @ "+int2string(var.getScope()) +" (current="+int2string(scopeLevel())+")"); DebugAssert(var.isAbsLiteral(), "var = "+var.getExpr().toString()); addCNFFact(var); // phi <=> v addToCNFCache(result); applyCNFRules(result); TRACE_MSG("mycnf", "enqueueCNFrec => }"); } /*! * \param thm is the input of the form phi <=> v */ void SearchImplBase::applyCNFRules(const Theorem& thm) { TRACE("mycnf", "applyCNFRules(", thm.getExpr(), ") { "); Theorem result = thm; DebugAssert(result.isRewrite(), "SearchImplBase::applyCNFRules: input must be an iff: " + result.getExpr().toString()); Expr lhs = result.getLHS(); Expr rhs = result.getRHS(); DebugAssert(rhs.isAbsLiteral(), "SearchImplBase::applyCNFRules: rhs of input must be literal: " + result.getExpr().toString()); // Eliminate negation first while(result.getLHS().isNot()) result = d_commonRules->iffContrapositive(result); lhs = result.getLHS(); rhs = result.getRHS(); CDMap::iterator it = d_applyCNFRulesCache.find(lhs); if(it == d_applyCNFRulesCache.end()) d_applyCNFRulesCache[lhs] = true; else { TRACE_MSG("mycnf","applyCNFRules[temp cache] => }"); return; } // Catch the trivial case v1 <=> v2 if(lhs.isPropLiteral()) { if(!lhs.isAbsLiteral()) { Theorem replaced = d_commonRules->symmetryRule(replaceITE(lhs)); result = d_commonRules->transitivityRule(replaced, result); lhs = result.getLHS(); DebugAssert(rhs == result.getRHS(), "applyCNFRules [literal lhs]\n result = " +result.getExpr().toString() +"\n rhs = "+rhs.toString()); } Theorem thm = d_rules->iffToClauses(result); DebugAssert(thm.getExpr().isAnd() && thm.getExpr().arity()==2, "applyCNFRules [literal lhs]\n thm = " +thm.getExpr().toString()); addCNFFact(d_commonRules->andElim(thm, 0)); addCNFFact(d_commonRules->andElim(thm, 1)); return; } // Check the kids in e[0], replace them with cache[e[0][i]], rebuild thm vector changed; vector substitutions; int c=0; for(Expr::iterator j = lhs.begin(), jend = lhs.end(); j!=jend; ++c,++j) { const Expr& phi = *j; if(!phi.isPropLiteral()) { Theorem phiIffVar = findInCNFCache(phi); if(phiIffVar.isNull()) { // Create a new variable for this child phiIffVar = d_commonRules->varIntroSkolem(phi); addToCNFCache(phiIffVar); } DebugAssert(phiIffVar.isRewrite(), "SearchImplBase::applyCNFRules: result = " + result.toString()); DebugAssert(phi == phiIffVar.getLHS(), "SearchImplBase::applyCNFRules:\n phi = " + phi.toString() + "\n\n phiIffVar = " + phiIffVar.toString()); DebugAssert(phiIffVar.getRHS().isAbsLiteral(), "SearchImplBase::applyCNFRules: phiIffVar = " + phiIffVar.toString()); changed.push_back(c); substitutions.push_back(phiIffVar); applyCNFRules(phiIffVar); } } if(changed.size() > 0) { Theorem subst = d_commonRules->substitutivityRule(lhs, changed, substitutions); subst = d_commonRules->symmetryRule(subst); result = d_commonRules->transitivityRule(subst, result); } switch(result.getLHS().getKind()) { case AND: result = d_rules->andCNFRule(result); break; case OR: result = d_rules->orCNFRule(result); break; case IFF: result = d_rules->iffCNFRule(result); break; case IMPLIES: result = d_rules->impCNFRule(result); break; case ITE: result = d_rules->iteCNFRule(result); break; default: DebugAssert(false, "SearchImplBase::applyCNFRules: " "the input operator must be and|or|iff|imp|ite\n result = " + result.getLHS().toString()); break; } // FIXME: eliminate this once debugged Theorem clauses(result); // Enqueue the clauses DebugAssert(!clauses.isNull(), "Oops!.."); DebugAssert(clauses.getExpr().isAnd(), "clauses = " +clauses.getExpr().toString()); // The clauses may containt term ITEs, and we need to translate those for(int i=0, iend=clauses.getExpr().arity(); iandElim(clauses,i)); addCNFFact(clause); } TRACE_MSG("mycnf","applyCNFRules => }"); } bool SearchImplBase::isClause(const Expr& e) { if(e.isAbsLiteral()) return true; if(!e.isOr()) return false; bool cnf(true); for(Expr::iterator i=e.begin(), iend=e.end(); i!=iend && cnf; ++i) cnf = (*i).isAbsLiteral(); return cnf; } bool SearchImplBase::isPropClause(const Expr& e) { if(e.isPropLiteral()) return true; if(!e.isOr()) return false; bool cnf(true); for(Expr::iterator i=e.begin(), iend=e.end(); i!=iend && cnf; ++i) cnf = (*i).isPropLiteral(); return cnf; } bool SearchImplBase::isGoodSplitter(const Expr& e) { if(!*d_ignoreCnfVarsOption) return true; TRACE("isGoodSplitter", "isGoodSplitter(", e, ") {"); // Check for var being an auxiliary CNF variable const Expr& var = e.isNot()? e[0] : e; bool res(!isCNFVar(var)); TRACE("isGoodSplitter", "isGoodSplitter => ", res? "true" : "false", " }"); return res; } void SearchImplBase::addToCNFCache(const Theorem& thm) { TRACE("mycnf", "addToCNFCache(", thm.getExpr(), ")"); d_core->getStatistics().counter("CNF New Vars")++; Theorem result = thm; DebugAssert(result.isRewrite(), "SearchImplBase::addToCNFCache: input must be an iff: " + result.getExpr().toString()); // Record the CNF variable d_cnfVars[thm.getRHS()] = true; Theorem t(thm); Expr phi = thm.getLHS(); while(phi.isNot()) { t = d_commonRules->iffContrapositive(thm); phi = phi[0]; } DebugAssert(d_cnfCache.count(phi) == 0, "thm = "+thm.getExpr().toString() + "\n t = "+ t.getExpr().toString()); //d_cnfCache.insert(phi, t); d_cnfCache.insert(phi, t, d_bottomScope); } Theorem SearchImplBase::findInCNFCache(const Expr& e) { TRACE("mycnf", "findInCNFCache(", e, ") { "); Expr phi(e); int numNegs(0); // Strip all the top-level negations from e while(phi.isNot()) { phi = phi[0]; numNegs++; } CDMap::iterator it = d_cnfCache.find(phi); if(it != d_cnfCache.end()) { // IF_DEBUG(debugger.counter("CNF cache hits")++); d_core->getStatistics().counter("CNF cache hits")++; Theorem thm = (*it).second; DebugAssert(thm.isRewrite() && thm.getLHS() == phi, "SearchImplBase::findInCNFCache: thm must be an iff: " + thm.getExpr().toString()); // Now put all the negations back. If the number of negations is // odd, first transform phi<=>v into !phi<=>!v. Then apply // !!phi<=>phi rewrite as many times as needed. if(numNegs % 2 != 0) { thm = d_commonRules->iffContrapositive(thm); numNegs--; } for(; numNegs > 0; numNegs-=2) { Theorem t = d_commonRules->rewriteNotNot(!!(thm.getLHS())); thm = d_commonRules->transitivityRule(t,thm); } DebugAssert(numNegs == 0, "numNegs = "+int2string(numNegs)); TRACE("mycnf", "findInCNFCache => ", thm.getExpr(), " }"); return thm; } TRACE_MSG("mycnf", "findInCNFCache => null }"); return Theorem(); } /*! * Strategy: * * For a given atomic formula phi(ite(c, t1, t2)) which depends on a * term ITE, generate an equisatisfiable formula: * * phi(x) & ite(c, t1=x, t2=x), * * where x is a new variable. * * The phi(x) part will be generated by the caller, and our job is to * assert the 'ite(c, t1=x, t2=x)', and return a rewrite theorem * phi(ite(c, t1, t2)) <=> phi(x). */ Theorem SearchImplBase::replaceITE(const Expr& e) { TRACE("replaceITE","replaceITE(", e, ") { "); Theorem res; CDMap::iterator i=d_replaceITECache.find(e), iend=d_replaceITECache.end(); if(i!=iend) { TRACE("replaceITE", "replaceITE[cached] => ", (*i).second, " }"); return (*i).second; } if(e.isAbsLiteral()) res = d_core->rewriteLiteral(e); else res = d_commonRules->reflexivityRule(e); // If 'res' is e<=>phi, and the resulting formula phi is not a // literal, introduce a new variable x, enqueue phi<=>x, and return // e<=>x. if(!res.getRHS().isPropLiteral()) { Theorem varThm(findInCNFCache(res.getRHS())); if(varThm.isNull()) { varThm = d_commonRules->varIntroSkolem(res.getRHS()); Theorem var; if(res.isRewrite()) var = d_commonRules->transitivityRule(res,varThm); else var = d_commonRules->iffMP(res,varThm); //d_cnfVars[var.getExpr()] = true; //addCNFFact(var); addToCNFCache(varThm); } applyCNFRules(varThm); //enqueueCNFrec(varThm); res = d_commonRules->transitivityRule(res, varThm); } d_replaceITECache[e] = res; TRACE("replaceITE", "replaceITE => ", res, " }"); return res; }