/********************* */ /*! \file cnf_proof.cpp ** \verbatim ** Top contributors (to current version): ** Liana Hadarean, Guy Katz, Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief [[ Add one-line brief description here ]] ** ** [[ Add lengthier description here ]] ** \todo document this file **/ #include "proof/cnf_proof.h" #include "proof/clause_id.h" #include "proof/proof_manager.h" #include "proof/proof_utils.h" #include "proof/theory_proof.h" #include "prop/cnf_stream.h" #include "prop/minisat/minisat.h" #include "prop/sat_solver_types.h" namespace CVC4 { CnfProof::CnfProof(prop::CnfStream* stream, context::Context* ctx, const std::string& name) : d_cnfStream(stream) , d_clauseToAssertion(ctx) , d_assertionToProofRule(ctx) , d_currentAssertionStack() , d_currentDefinitionStack() , d_clauseToDefinition(ctx) , d_definitions() , d_cnfDeps() , d_name(name) { // Setting the proof object for the CnfStream d_cnfStream->setProof(this); } CnfProof::~CnfProof() {} bool CnfProof::isAssertion(Node node) { return d_assertionToProofRule.find(node) != d_assertionToProofRule.end(); } bool CnfProof::isDefinition(Node node) { return d_definitions.find(node) != d_definitions.end(); } ProofRule CnfProof::getProofRule(Node node) { Assert (isAssertion(node)); NodeToProofRule::iterator it = d_assertionToProofRule.find(node); return (*it).second; } ProofRule CnfProof::getProofRule(ClauseId clause) { TNode assertion = getAssertionForClause(clause); return getProofRule(assertion); } Node CnfProof::getAssertionForClause(ClauseId clause) { ClauseIdToNode::const_iterator it = d_clauseToAssertion.find(clause); Assert (it != d_clauseToAssertion.end()); return (*it).second; } Node CnfProof::getDefinitionForClause(ClauseId clause) { ClauseIdToNode::const_iterator it = d_clauseToDefinition.find(clause); Assert (it != d_clauseToDefinition.end()); return (*it).second; } void CnfProof::registerConvertedClause(ClauseId clause, bool explanation) { Assert (clause != ClauseIdUndef && clause != ClauseIdError && clause != ClauseIdEmpty); // Explanations do not need a CNF conversion proof since they are in CNF // (they will only need a theory proof as they are theory valid) if (explanation) { Debug("proof:cnf") << "CnfProof::registerConvertedClause " << clause << " explanation? " << explanation << std::endl; Assert (d_explanations.find(clause) == d_explanations.end()); d_explanations.insert(clause); return; } Node current_assertion = getCurrentAssertion(); Node current_expr = getCurrentDefinition(); Debug("proof:cnf") << "CnfProof::registerConvertedClause " << clause << " assertion = " << current_assertion << clause << " definition = " << current_expr << std::endl; setClauseAssertion(clause, current_assertion); setClauseDefinition(clause, current_expr); } void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { Debug("proof:cnf") << "CnfProof::setClauseAssertion " << clause << " assertion " << expr << std::endl; // We can add the same clause from different assertions. In this // case we keep the first assertion. For example asserting a /\ b // and then b /\ c where b is an atom, would assert b twice (note // that since b is top level, it is not cached by the CnfStream) if (d_clauseToAssertion.find(clause) != d_clauseToAssertion.end()) return; d_clauseToAssertion.insert (clause, expr); } void CnfProof::setClauseDefinition(ClauseId clause, Node definition) { Debug("proof:cnf") << "CnfProof::setClauseDefinition " << clause << " definition " << definition << std::endl; // We keep the first definition if (d_clauseToDefinition.find(clause) != d_clauseToDefinition.end()) return; d_clauseToDefinition.insert(clause, definition); d_definitions.insert(definition); } void CnfProof::registerAssertion(Node assertion, ProofRule reason) { Debug("proof:cnf") << "CnfProof::registerAssertion " << assertion << " reason " << reason << std::endl; // We can obtain the assertion from different reasons (e.g. if the // assertion is a lemma over shared terms both theories can generate // the same lemma) We only need to prove the lemma in one way, so we // keep the first reason. if (isAssertion(assertion)) { return; } d_assertionToProofRule.insert(assertion, reason); } LemmaProofRecipe CnfProof::getProofRecipe(const std::set &lemma) { Assert(d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end()); return d_lemmaToProofRecipe[lemma]; } bool CnfProof::haveProofRecipe(const std::set &lemma) { return d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end(); } void CnfProof::setCnfDependence(Node from, Node to) { Debug("proof:cnf") << "CnfProof::setCnfDependence " << "from " << from << std::endl << " to " << to << std::endl; Assert (from != to); d_cnfDeps.insert(std::make_pair(from, to)); } void CnfProof::pushCurrentAssertion(Node assertion) { Debug("proof:cnf") << "CnfProof::pushCurrentAssertion " << assertion << std::endl; d_currentAssertionStack.push_back(assertion); } void CnfProof::popCurrentAssertion() { Assert (d_currentAssertionStack.size()); Debug("proof:cnf") << "CnfProof::popCurrentAssertion " << d_currentAssertionStack.back() << std::endl; d_currentAssertionStack.pop_back(); } Node CnfProof::getCurrentAssertion() { Assert (d_currentAssertionStack.size()); return d_currentAssertionStack.back(); } void CnfProof::setProofRecipe(LemmaProofRecipe* proofRecipe) { Assert(proofRecipe); Assert(proofRecipe->getNumSteps() > 0); d_lemmaToProofRecipe[proofRecipe->getBaseAssertions()] = *proofRecipe; } void CnfProof::pushCurrentDefinition(Node definition) { Debug("proof:cnf") << "CnfProof::pushCurrentDefinition " << definition << std::endl; d_currentDefinitionStack.push_back(definition); } void CnfProof::popCurrentDefinition() { Assert (d_currentDefinitionStack.size()); Debug("proof:cnf") << "CnfProof::popCurrentDefinition " << d_currentDefinitionStack.back() << std::endl; d_currentDefinitionStack.pop_back(); } Node CnfProof::getCurrentDefinition() { Assert (d_currentDefinitionStack.size()); return d_currentDefinitionStack.back(); } Node CnfProof::getAtom(prop::SatVariable var) { prop::SatLiteral lit (var); Node node = d_cnfStream->getNode(lit); return node; } void CnfProof::collectAtoms(const prop::SatClause* clause, std::set& atoms) { for (unsigned i = 0; i < clause->size(); ++i) { prop::SatLiteral lit = clause->operator[](i); prop::SatVariable var = lit.getSatVariable(); TNode atom = getAtom(var); if (atoms.find(atom) == atoms.end()) { atoms.insert(atom); } } } prop::SatLiteral CnfProof::getLiteral(TNode atom) { return d_cnfStream->getLiteral(atom); } bool CnfProof::hasLiteral(TNode atom) { return d_cnfStream->hasLiteral(atom); } void CnfProof::ensureLiteral(TNode atom, bool noPreregistration) { d_cnfStream->ensureLiteral(atom, noPreregistration); } void CnfProof::collectAtomsForClauses(const IdToSatClause& clauses, std::set& atoms) { IdToSatClause::const_iterator it = clauses.begin(); for (; it != clauses.end(); ++it) { const prop::SatClause* clause = it->second; collectAtoms(clause, atoms); } } void CnfProof::collectAtomsAndRewritesForLemmas(const IdToSatClause& lemmaClauses, std::set& atoms, NodePairSet& rewrites) { IdToSatClause::const_iterator it = lemmaClauses.begin(); for (; it != lemmaClauses.end(); ++it) { const prop::SatClause* clause = it->second; // TODO: just calculate the map from ID to recipe once, // instead of redoing this over and over again std::vector clause_expr; std::set clause_expr_nodes; for(unsigned i = 0; i < clause->size(); ++i) { prop::SatLiteral lit = (*clause)[i]; Node node = getAtom(lit.getSatVariable()); Expr atom = node.toExpr(); if (atom.isConst()) { Assert (atom == utils::mkTrue()); continue; } clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node); } LemmaProofRecipe recipe = getProofRecipe(clause_expr_nodes); for (unsigned i = 0; i < recipe.getNumSteps(); ++i) { const LemmaProofRecipe::ProofStep* proofStep = recipe.getStep(i); Node atom = proofStep->getLiteral(); if (atom == Node()) { // The last proof step always has the empty node as its target... continue; } if (atom.getKind() == kind::NOT) { atom = atom[0]; } atoms.insert(atom); } LemmaProofRecipe::RewriteIterator rewriteIt; for (rewriteIt = recipe.rewriteBegin(); rewriteIt != recipe.rewriteEnd(); ++rewriteIt) { rewrites.insert(NodePair(rewriteIt->first, rewriteIt->second)); // The unrewritten terms also need to have literals, so insert them into atoms Node rewritten = rewriteIt->first; if (rewritten.getKind() == kind::NOT) { rewritten = rewritten[0]; } atoms.insert(rewritten); } } } void CnfProof::collectAssertionsForClauses(const IdToSatClause& clauses, NodeSet& assertions) { IdToSatClause::const_iterator it = clauses.begin(); for (; it != clauses.end(); ++it) { TNode used_assertion = getAssertionForClause(it->first); assertions.insert(used_assertion); // it can be the case that a definition for a clause is an assertion // but it is not the assertion for the clause // e.g. the assertions [(and a b), a] TNode used_definition = getDefinitionForClause(it->first); if (isAssertion(used_definition)) { assertions.insert(used_definition); } } } void LFSCCnfProof::printAtomMapping(const std::set& atoms, std::ostream& os, std::ostream& paren) { std::set::const_iterator it = atoms.begin(); std::set::const_iterator end = atoms.end(); for (;it != end; ++it) { os << "(decl_atom "; Node atom = *it; prop::SatVariable var = getLiteral(atom).getSatVariable(); //FIXME hideous LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); pe->printLetTerm(atom.toExpr(), os); os << " (\\ " << ProofManager::getVarName(var, d_name); os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; paren << ")))"; } } void LFSCCnfProof::printAtomMapping(const std::set& atoms, std::ostream& os, std::ostream& paren, ProofLetMap &letMap) { std::set::const_iterator it = atoms.begin(); std::set::const_iterator end = atoms.end(); for (;it != end; ++it) { os << "(decl_atom "; Node atom = *it; prop::SatVariable var = getLiteral(atom).getSatVariable(); //FIXME hideous LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); if (pe->printsAsBool(atom.toExpr())) os << "(p_app "; pe->printBoundTerm(atom.toExpr(), os, letMap); if (pe->printsAsBool(atom.toExpr())) os << ")"; os << " (\\ " << ProofManager::getVarName(var, d_name); os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; paren << ")))"; } } // maps each expr to the position it had in the clause and the polarity it had Node LFSCCnfProof::clauseToNode(const prop::SatClause& clause, std::map& childIndex, std::map& childPol ) { std::vector< Node > children; for (unsigned i = 0; i < clause.size(); ++i) { prop::SatLiteral lit = clause[i]; prop::SatVariable var = lit.getSatVariable(); Node atom = getAtom(var); children.push_back( lit.isNegated() ? atom.negate() : atom ); childIndex[atom] = i; childPol[atom] = !lit.isNegated(); } return children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode(kind::OR, children ); } void LFSCCnfProof::printCnfProofForClause(ClauseId id, const prop::SatClause* clause, std::ostream& os, std::ostream& paren) { Debug("cnf-pf") << std::endl << std::endl << "LFSCCnfProof::printCnfProofForClause( " << id << " ) starting " << std::endl; os << "(satlem _ _ "; std::ostringstream clause_paren; printClause(*clause, os, clause_paren); os << "(clausify_false "; // FIXMEEEEEEEEEEEE // os <<"trust)"; // os << clause_paren.str() // << " (\\ " << ProofManager::getInputClauseName(id, d_name) << "\n"; // paren << "))"; // return; Assert( clause->size()>0 ); Node base_assertion = getDefinitionForClause(id); //get the assertion for the clause id std::map childIndex; std::map childPol; Node assertion = clauseToNode( *clause, childIndex, childPol ); //if there is no reason, construct assertion directly. This can happen for unit clauses. if( base_assertion.isNull() ){ base_assertion = assertion; } //os_base is proof of base_assertion std::stringstream os_base; // checks if tautological definitional clause or top-level clause // and prints the proof of the top-level formula bool is_input = printProofTopLevel(base_assertion, os_base); if (is_input) { Debug("cnf-pf") << std::endl << "; base assertion is input. proof: " << os_base.str() << std::endl; } //get base assertion with polarity bool base_pol = base_assertion.getKind()!=kind::NOT; base_assertion = base_assertion.getKind()==kind::NOT ? base_assertion[0] : base_assertion; std::map< Node, unsigned >::iterator itci = childIndex.find( base_assertion ); bool is_in_clause = itci!=childIndex.end(); unsigned base_index = is_in_clause ? itci->second : 0; Trace("cnf-pf") << std::endl; Trace("cnf-pf") << "; input = " << is_input << ", is_in_clause = " << is_in_clause << ", id = " << id << ", assertion = " << assertion << ", base assertion = " << base_assertion << std::endl; if (!is_input){ Assert(is_in_clause); prop::SatLiteral blit = (*clause)[ base_index ]; os_base << ProofManager::getLitName(blit, d_name); base_pol = !childPol[base_assertion]; // WHY? if the case is => } Trace("cnf-pf") << "; polarity of base assertion = " << base_pol << std::endl; Trace("cnf-pf") << "; proof of base : " << os_base.str() << std::endl; bool success = false; if( is_input && is_in_clause && childPol[base_assertion]==base_pol ){ //if both in input and in clause, the proof is trivial. this is the case for unit clauses. Trace("cnf-pf") << "; trivial" << std::endl; os << "(contra _ "; success = true; prop::SatLiteral lit = (*clause)[itci->second]; if( base_pol ){ os << os_base.str() << " " << ProofManager::getLitName(lit, d_name); }else{ os << ProofManager::getLitName(lit, d_name) << " " << os_base.str(); } os << ")"; } else if ((base_assertion.getKind()==kind::AND && !base_pol) || ((base_assertion.getKind()==kind::OR || base_assertion.getKind()==kind::IMPLIES) && base_pol)) { Trace("cnf-pf") << "; and/or case 1" << std::endl; success = true; std::stringstream os_main; std::stringstream os_paren; //eliminate each one for (int j = base_assertion.getNumChildren()-2; j >= 0; j--) { Trace("cnf-pf-debug") << "; base_assertion[" << j << "] is: " << base_assertion[j] << ", and its kind is: " << base_assertion[j].getKind() << std::endl ; Node child_base = base_assertion[j].getKind()==kind::NOT ? base_assertion[j][0] : base_assertion[j]; bool child_pol = base_assertion[j].getKind()!=kind::NOT; Trace("cnf-pf-debug") << "; child " << j << " " << ", child base: " << child_base << ", child pol: " << child_pol << ", childPol[child_base] " << childPol[child_base] << ", base pol: " << base_pol << std::endl; std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); if( itcic!=childIndex.end() ){ //Assert( child_pol==childPol[child_base] ); os_main << "(or_elim_1 _ _ "; prop::SatLiteral lit = (*clause)[itcic->second]; // Should be if in the original formula it was negated // if( childPol[child_base] && base_pol ){ // Adding the below to catch a specific case where the first child of an IMPLIES is negative, // in which case we need not_not introduction. if (base_assertion.getKind() == kind::IMPLIES && !child_pol && base_pol) { os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; } else if (childPol[child_base] && base_pol) { os_main << ProofManager::getLitName(lit, d_name) << " "; }else{ os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; } if( base_assertion.getKind()==kind::AND ){ os_main << "(not_and_elim _ _ "; os_paren << ")"; } os_paren << ")"; }else{ success = false; } } if( success ){ if( base_assertion.getKind()==kind::IMPLIES ){ os_main << "(impl_elim _ _ "; } os_main << os_base.str(); if( base_assertion.getKind()==kind::IMPLIES ){ os_main << ")"; } os_main << os_paren.str(); int last_index = base_assertion.getNumChildren()-1; Node child_base = base_assertion[last_index].getKind()==kind::NOT ? base_assertion[last_index][0] : base_assertion[last_index]; //bool child_pol = base_assertion[last_index].getKind()!=kind::NOT; std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); if( itcic!=childIndex.end() ){ os << "(contra _ "; prop::SatLiteral lit = (*clause)[itcic->second]; if( childPol[child_base] && base_pol){ os << os_main.str() << " " << ProofManager::getLitName(lit, d_name); }else{ os << ProofManager::getLitName(lit, d_name) << " " << os_main.str(); } os << ")"; }else{ success = false; } } }else if ((base_assertion.getKind()==kind::AND && base_pol) || ((base_assertion.getKind()==kind::OR || base_assertion.getKind()==kind::IMPLIES) && !base_pol)) { std::stringstream os_main; Node iatom; if (is_in_clause) { Assert( assertion.getNumChildren()==2 ); iatom = assertion[ base_index==0 ? 1 : 0]; } else { Assert( assertion.getNumChildren()==1 ); iatom = assertion[0]; } Trace("cnf-pf") << "; and/or case 2, iatom = " << iatom << std::endl; Node e_base = iatom.getKind()==kind::NOT ? iatom[0] : iatom; bool e_pol = iatom.getKind()!=kind::NOT; std::map< Node, unsigned >::iterator itcic = childIndex.find( e_base ); if( itcic!=childIndex.end() ){ prop::SatLiteral lit = (*clause)[itcic->second]; //eliminate until we find iatom for( unsigned j=0; j indices; std::vector< bool > pols; success = true; int elimNum = 0; for( unsigned i=0; i<2; i++ ){ Node child_base = base_assertion[i].getKind()==kind::NOT ? base_assertion[i][0] : base_assertion[i]; bool child_pol = base_assertion[i].getKind()!=kind::NOT; std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); if( itcic!=childIndex.end() ){ indices.push_back( itcic->second ); pols.push_back( childPol[child_base] ); if( i==0 ){ //figure out which way to elim elimNum = child_pol==childPol[child_base] ? 2 : 1; if( (elimNum==2)==(k==kind::EQUAL) ){ num_nots_2++; } if( elimNum==1 ){ num_nots_1++; } } }else{ success = false; break; } } Trace("cnf-pf") << std::endl << "; num nots = " << num_nots_2 << std::endl; if( success ){ os << "(contra _ "; std::stringstream os_base_n; if( num_nots_2==2 ){ os_base_n << "(not_not_elim _ "; } os_base_n << "(or_elim_1 _ _ "; prop::SatLiteral lit1 = (*clause)[indices[0]]; if( !pols[0] || num_nots_1==1 ){ os_base_n << "(not_not_intro _ " << ProofManager::getLitName(lit1, d_name) << ") "; }else{ Trace("cnf-pf-debug") << "CALLING getlitname" << std::endl; os_base_n << ProofManager::getLitName(lit1, d_name) << " "; } Assert( elimNum!=0 ); os_base_n << "(" << ( k==kind::EQUAL ? "iff" : "xor" ) << "_elim_" << elimNum << " _ _ "; if( !base_pol ){ os_base_n << "(not_" << ( base_assertion.getKind()==kind::EQUAL ? "iff" : "xor" ) << "_elim _ _ " << os_base.str() << ")"; }else{ os_base_n << os_base.str(); } os_base_n << "))"; if( num_nots_2==2 ){ os_base_n << ")"; num_nots_2 = 0; } prop::SatLiteral lit2 = (*clause)[indices[1]]; if( pols[1]==(num_nots_2==0) ){ os << os_base_n.str() << " "; if( num_nots_2==1 ){ os << "(not_not_intro _ " << ProofManager::getLitName(lit2, d_name) << ")"; }else{ os << ProofManager::getLitName(lit2, d_name); } }else{ os << ProofManager::getLitName(lit2, d_name) << " " << os_base_n.str(); } os << ")"; } }else if( base_assertion.getKind()==kind::ITE ){ std::map< unsigned, unsigned > appears; std::map< unsigned, Node > appears_expr; unsigned appears_count = 0; for( unsigned r=0; r<3; r++ ){ Node child_base = base_assertion[r].getKind()==kind::NOT ? base_assertion[r][0] : base_assertion[r]; std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); if( itcic!=childIndex.end() ){ appears[r] = itcic->second; appears_expr[r] = child_base; appears_count++; } } if( appears_count==2 ){ success = true; int elimNum = 1; unsigned index1 = 0; unsigned index2 = 1; if( appears.find( 0 )==appears.end() ){ elimNum = 3; index1 = 1; index2 = 2; }else if( appears.find( 1 )==appears.end() ){ elimNum = 2; index1 = 0; index2 = 2; } std::stringstream os_main; os_main << "(or_elim_1 _ _ "; prop::SatLiteral lit1 = (*clause)[appears[index1]]; if( !childPol[appears_expr[index1]] || elimNum==1 || ( elimNum==3 && !base_pol ) ){ os_main << "(not_not_intro _ " << ProofManager::getLitName(lit1, d_name) << ") "; }else{ os_main << ProofManager::getLitName(lit1, d_name) << " "; } os_main << "(" << ( base_pol ? "" : "not_" ) << "ite_elim_" << elimNum << " _ _ _ "; os_main << os_base.str() << "))"; os << "(contra _ "; prop::SatLiteral lit2 = (*clause)[appears[index2]]; if( !childPol[appears_expr[index2]] || !base_pol ){ os << ProofManager::getLitName(lit2, d_name) << " " << os_main.str(); }else{ os << os_main.str() << " " << ProofManager::getLitName(lit2, d_name); } os << ")"; } }else if( base_assertion.isConst() ){ bool pol = base_assertion==NodeManager::currentNM()->mkConst( true ); if( pol!=base_pol ){ success = true; if( pol ){ os << "(contra _ truth " << os_base.str() << ")"; }else{ os << os_base.str(); } } } if( !success ){ Trace("cnf-pf") << std::endl; Trace("cnf-pf") << ";!!!!!!!!! CnfProof : Can't process " << assertion << ", base = " << base_assertion << ", id = " << id << std::endl; Trace("cnf-pf") << ";!!!!!!!!! Clause is : "; for (unsigned i = 0; i < clause->size(); ++i) { Trace("cnf-pf") << (*clause)[i] << " "; } Trace("cnf-pf") << std::endl; os << "trust-bad"; } os << ")" << clause_paren.str() << " (\\ " << ProofManager::getInputClauseName(id, d_name) << "\n"; paren << "))"; } void LFSCCnfProof::printClause(const prop::SatClause& clause, std::ostream& os, std::ostream& paren) { for (unsigned i = 0; i < clause.size(); ++i) { prop::SatLiteral lit = clause[i]; prop::SatVariable var = lit.getSatVariable(); if (lit.isNegated()) { os << "(ast _ _ _ " << ProofManager::getAtomName(var, d_name) <<" (\\ " << ProofManager::getLitName(lit, d_name) << " "; paren << "))"; } else { os << "(asf _ _ _ " << ProofManager::getAtomName(var, d_name) <<" (\\ " << ProofManager::getLitName(lit, d_name) << " "; paren << "))"; } } } // print a proof of the top-level formula e, based on the input assertions bool LFSCCnfProof::printProofTopLevel(Node e, std::ostream& out) { if (!isAssertion(e)) { // check if deduced by CNF // dependence on top level fact i.e. a depends on (a and b) NodeToNode::const_iterator itd = d_cnfDeps.find(e); if (itd != d_cnfDeps.end()) { TNode parent = itd->second; //check if parent is an input assertion std::stringstream out_parent; if (printProofTopLevel(parent, out_parent)) { if(parent.getKind()==kind::AND || (parent.getKind()==kind::NOT && (parent[0].getKind()==kind::IMPLIES || parent[0].getKind()==kind::OR))) { Node parent_base = parent.getKind()==kind::NOT ? parent[0] : parent; Node e_base = e.getKind()==kind::NOT ? e[0] : e; bool e_pol = e.getKind()!=kind::NOT; for( unsigned i=0; i