#include <utility>
#include "Debug/RuntimeStatistics.hpp"
#include "Lib/Comparison.hpp"
#include "Lib/Int.hpp"
#include "Lib/Recycled.hpp"
#include "Lib/TriangularArray.hpp"
#include "Kernel/Clause.hpp"
#include "Kernel/Term.hpp"
#include "ClauseCodeTree.hpp"
#undef RSTAT_COLLECTION
#define RSTAT_COLLECTION 0
namespace Indexing
{
using namespace std;
using namespace Lib;
using namespace Kernel;
void ClauseCodeTree::onCodeOpDestroying(CodeOp* op)
{
if (op->isLitEnd()) {
delete op->getILS();
}
}
ClauseCodeTree::ClauseCodeTree()
{
_clauseCodeTree=true;
_onCodeOpDestroying = onCodeOpDestroying;
#if VDEBUG
_clauseMatcherCounter=0;
#endif
}
void ClauseCodeTree::insert(Clause* cl)
{
unsigned clen=cl->length();
static DArray<Literal*> lits;
lits.initFromArray(clen, *cl);
optimizeLiteralOrder(lits);
CodeStack code;
LitCompiler compiler(code);
for(unsigned i=0;i<clen;i++) {
compiler.nextLit();
compiler.handleTerm(lits[i]);
}
code.push(CodeOp::getSuccess(cl));
compiler.updateCodeTree(this);
incorporate(code);
ASS(code.isEmpty());
}
struct ClauseCodeTree::InitialLiteralOrderingComparator
{
Comparison compare(Literal* l1, Literal* l2)
{
if(l1->weight()!=l2->weight()) {
return Int::compare(l2->weight(), l1->weight());
}
return Int::compare(l1->getId(), l2->getId());
}
};
void ClauseCodeTree::optimizeLiteralOrder(DArray<Literal*>& lits)
{
unsigned clen=lits.size();
if(isEmpty() || clen<=1) {
return;
}
lits.sort(InitialLiteralOrderingComparator());
CodeOp* entry=getEntryPoint();
for(unsigned startIndex=0;startIndex<clen-1;startIndex++) {
size_t unshared=1;
unsigned bestIndex=startIndex;
size_t bestSharedLen;
bool bestGround=lits[startIndex]->ground();
CodeOp* nextOp;
evalSharing(lits[startIndex], entry, bestSharedLen, unshared, nextOp);
if(!unshared) {
goto have_best;
}
for(unsigned i=startIndex+1;i<clen;i++) {
size_t sharedLen;
evalSharing(lits[i], entry, sharedLen, unshared, nextOp);
if(!unshared) {
bestIndex=i;
goto have_best;
}
if(sharedLen>bestSharedLen && (!bestGround || lits[i]->ground()) ) {
bestSharedLen=sharedLen;
bestIndex=i;
bestGround=lits[i]->ground();
}
}
have_best:
swap(lits[startIndex],lits[bestIndex]);
if(unshared) {
return;
}
ASS(nextOp);
entry=nextOp;
}
}
void ClauseCodeTree::evalSharing(Literal* lit, CodeOp* startOp, size_t& sharedLen, size_t& unsharedLen, CodeOp*& nextOp)
{
CodeStack code;
LitCompiler compiler(code);
compiler.handleTerm(lit);
matchCode(code, startOp, sharedLen, nextOp);
unsharedLen=code.size()-sharedLen;
ASS(code.top().isLitEnd());
delete code.pop().getILS();
}
void ClauseCodeTree::matchCode(CodeStack& code, CodeOp* startOp, size_t& matchedCnt, CodeOp*& nextOp)
{
size_t clen=code.length();
CodeOp* treeOp=startOp;
for(size_t i=0;i<clen;i++) {
for(;;) {
if(treeOp->isSearchStruct()) {
SearchStruct* ss=treeOp->getSearchStruct();
CodeOp** toPtr;
if(ss->getTargetOpPtr<false>(code[i], toPtr) && *toPtr) {
treeOp=*toPtr;
continue;
}
}
else if(code[i].equalsForOpMatching(*treeOp)) {
break;
}
ASS_NEQ(treeOp,treeOp->alternative());
treeOp=treeOp->alternative();
if(!treeOp) {
matchedCnt=i;
nextOp=0;
return;
}
}
ASS(!treeOp->isSearchStruct());
treeOp++;
}
matchedCnt=clen;
nextOp=treeOp;
}
void ClauseCodeTree::remove(Clause* cl)
{
static DArray<LitInfo> lInfos;
Recycled<Stack<CodeOp*>> firstsInBlocks;
Recycled<Stack<Recycled<RemovingLiteralMatcher, NoReset>>> rlms;
unsigned clen=cl->length();
lInfos.ensure(clen);
if(!clen) {
CodeOp* op=getEntryPoint();
firstsInBlocks->push(op);
if(!removeOneOfAlternatives(op, cl, &*firstsInBlocks)) {
ASSERTION_VIOLATION;
INVALID_OPERATION("empty clause to be removed was not found");
}
return;
}
for(unsigned i=0;i<clen;i++) {
lInfos[i]=LitInfo(cl,i);
lInfos[i].liIndex=i;
}
incTimeStamp();
CodeOp* op=getEntryPoint();
firstsInBlocks->push(op);
unsigned depth=0;
for(;;) {
RemovingLiteralMatcher* rlm = 0;
{
Recycled<RemovingLiteralMatcher, NoReset> rrlm; rlm = &*rrlm; rlm->init(op, lInfos.array(), lInfos.size(), this, &*firstsInBlocks); rlms->push(std::move(rrlm)); }
iteration_restart:
if(!rlm->next()) {
if(depth==0) {
ASSERTION_VIOLATION;
INVALID_OPERATION("clause to be removed was not found");
}
rlms->pop();
depth--;
rlm = &*rlms->top();
goto iteration_restart;
}
op=rlm->op;
ASS(op->isLitEnd());
ASS_EQ(op->getILS()->depth, depth);
if(op->getILS()->timestamp==_curTimeStamp) {
goto iteration_restart;
}
op->getILS()->timestamp=_curTimeStamp;
op++;
if(depth==clen-1) {
if(removeOneOfAlternatives(op, cl, &*firstsInBlocks)) {
break;
}
goto iteration_restart;
}
ASS_L(depth,clen-1);
depth++;
}
for(unsigned i=0;i<clen;i++) {
lInfos[i].dispose();
}
}
void ClauseCodeTree::RemovingLiteralMatcher::init(CodeOp* entry_, LitInfo* linfos_,
size_t linfoCnt_, ClauseCodeTree* tree_, Stack<CodeOp*>* firstsInBlocks_)
{
RemovingMatcher::init(entry_, linfos_, linfoCnt_, tree_, firstsInBlocks_);
ALWAYS(prepareLiteral());
}
bool ClauseCodeTree::removeOneOfAlternatives(CodeOp* op, Clause* cl, Stack<CodeOp*>* firstsInBlocks)
{
unsigned initDepth=firstsInBlocks->size();
while(!op->isSuccess() || op->getSuccessResult<Clause>()!=cl) {
op=op->alternative();
if(!op) {
firstsInBlocks->truncate(initDepth);
return false;
}
firstsInBlocks->push(op);
}
op->makeFail();
optimizeMemoryAfterRemoval(firstsInBlocks, op);
return true;
}
void ClauseCodeTree::LiteralMatcher::init(CodeTree* tree_, CodeOp* entry_,
LitInfo* linfos_, size_t linfoCnt_,
bool seekOnlySuccess)
{
ASS_G(linfoCnt_,0);
Matcher::init(tree_,entry_);
linfos=linfos_;
linfoCnt=linfoCnt_;
_eagerlyMatched=false;
eagerResults.reset();
RSTAT_CTR_INC("LiteralMatcher::init");
if(seekOnlySuccess) {
RSTAT_CTR_INC("LiteralMatcher::init - seekOnlySuccess");
_eagerlyMatched=true;
_fresh=false;
CodeOp* sop=entry;
while(sop) {
if(sop->isSuccess()) {
eagerResults.push(sop);
}
sop=sop->alternative();
}
return;
}
ALWAYS(prepareLiteral());
}
bool ClauseCodeTree::LiteralMatcher::next()
{
if(eagerlyMatched()) {
_matched=!eagerResults.isEmpty();
if(!_matched) {
return false;
}
op=eagerResults.pop();
return true;
}
if(finished()) {
return false;
}
_matched=execute();
if(!_matched) {
return false;
}
ASS(op->isLitEnd() || op->isSuccess());
if(op->isLitEnd()) {
recordMatch();
}
return true;
}
bool ClauseCodeTree::LiteralMatcher::doEagerMatching()
{
ASS(!eagerlyMatched()); ASS(eagerResults.isEmpty());
ASS(!finished());
CodeOp* currOp=op;
static Stack<CodeOp*> eagerResultsRevOrder;
static Stack<CodeOp*> successes;
eagerResultsRevOrder.reset();
successes.reset();
while(execute()) {
if(op->isLitEnd()) {
recordMatch();
eagerResultsRevOrder.push(op);
}
else {
ASS(op->isSuccess());
successes.push(op);
}
}
while(eagerResultsRevOrder.isNonEmpty()) {
eagerResults.push(eagerResultsRevOrder.pop());
}
while(successes.isNonEmpty()) {
eagerResults.push(successes.pop());
}
_eagerlyMatched=true;
op=currOp;
return eagerResults.isNonEmpty();
}
void ClauseCodeTree::LiteralMatcher::recordMatch()
{
ASS(matched());
ILStruct* ils=op->getILS();
ils->ensureFreshness(tree->_curTimeStamp);
if(ils->finished) {
return;
}
if(!ils->matchCnt && linfos[curLInfo].opposite) {
ils->noNonOppositeMatches=true;
}
ils->addMatch(linfos[curLInfo].liIndex, bindings);
}
void ClauseCodeTree::ClauseMatcher::init(ClauseCodeTree* tree_, Clause* query_, bool sres_)
{
ASS(!tree_->isEmpty());
query=query_;
tree=tree_;
sres=sres_;
lms.reset();
#if VDEBUG
ASS_EQ(tree->_clauseMatcherCounter,0);
tree->_clauseMatcherCounter++;
#endif
unsigned clen=query->length();
unsigned baseLICnt=clen;
for(unsigned i=0;i<clen;i++) {
if((*query)[i]->isEquality()) {
baseLICnt++;
}
}
unsigned liCnt=sres ? (baseLICnt*2) : baseLICnt;
lInfos.ensure(liCnt);
unsigned liIndex=0;
for(unsigned i=0;i<clen;i++) {
if(!(*query)[i]->ground()) {
continue;
}
lInfos[liIndex]=LitInfo(query,i);
lInfos[liIndex].liIndex=liIndex;
liIndex++;
if((*query)[i]->isEquality()) {
lInfos[liIndex]=LitInfo::getReversed(lInfos[liIndex-1]);
lInfos[liIndex].liIndex=liIndex;
liIndex++;
}
}
for(unsigned i=0;i<clen;i++) {
if((*query)[i]->ground()) {
continue;
}
lInfos[liIndex]=LitInfo(query,i);
lInfos[liIndex].liIndex=liIndex;
liIndex++;
if((*query)[i]->isEquality()) {
lInfos[liIndex]=LitInfo::getReversed(lInfos[liIndex-1]);
lInfos[liIndex].liIndex=liIndex;
liIndex++;
}
}
if(sres) {
for(unsigned i=0;i<baseLICnt;i++) {
unsigned newIndex=i+baseLICnt;
lInfos[newIndex]=LitInfo::getOpposite(lInfos[i]);
lInfos[newIndex].liIndex=newIndex;
}
sresLiteral=sresNoLiteral;
}
tree->incTimeStamp();
enterLiteral(tree->getEntryPoint(), clen==0);
}
void ClauseCodeTree::ClauseMatcher::reset()
{
unsigned liCnt=lInfos.size();
for(unsigned i=0;i<liCnt;i++) {
lInfos[i].dispose();
}
lms.reset();
#if VDEBUG
ASS_EQ(tree->_clauseMatcherCounter,1);
tree->_clauseMatcherCounter--;
#endif
}
Clause* ClauseCodeTree::ClauseMatcher::next(int& resolvedQueryLit)
{
if(lms.isEmpty()) {
return 0;
}
for(;;) {
LiteralMatcher* lm = &*lms.top();
bool found=lm->next();
if(!found) {
leaveLiteral();
if(lms.isEmpty()) {
return 0;
}
}
else if(lm->op->isSuccess()) {
Clause* candidate=lm->op->getSuccessResult<Clause>();
RSTAT_MCTR_INC("candidates", lms.size()-1);
if(checkCandidate(candidate, resolvedQueryLit)) {
RSTAT_MCTR_INC("candidates (success)", lms.size()-1);
return candidate;
}
}
else if(canEnterLiteral(lm->op)) {
ASS(lm->op->isLitEnd());
ASS_LE(lms.size(), query->length());
CodeOp* newLitEntry=lm->op+1;
ASS(!sres || sresLiteral==sresNoLiteral || sresLiteral<lms.size()-1);
if(sres && sresLiteral==sresNoLiteral) {
if(lm->getILS()->noNonOppositeMatches) {
sresLiteral=lms.size()-1;
}
}
bool seekOnlySuccess=lms.size()==query->length();
enterLiteral(newLitEntry, seekOnlySuccess);
}
}
}
inline bool ClauseCodeTree::ClauseMatcher::canEnterLiteral(CodeOp* op)
{
ASS(op->isLitEnd());
ASS_EQ(lms.top()->op, op);
ILStruct* ils=op->getILS();
if(ils->timestamp==tree->_curTimeStamp && ils->visited) {
return false;
}
if(lms.size()>1) {
if(ils->varCnt && !lms.top()->eagerlyMatched()) {
lms.top()->doEagerMatching();
RSTAT_MST_INC("match count", lms.size()-1, lms.top()->getILS()->matchCnt);
}
for(size_t ilIndex=0;ilIndex<lms.size()-1;ilIndex++) {
ILStruct* prevILS=lms[ilIndex]->getILS();
if(prevILS->varCnt && !lms[ilIndex]->eagerlyMatched()) {
lms[ilIndex]->doEagerMatching();
RSTAT_MST_INC("match count", ilIndex, lms[ilIndex]->getILS()->matchCnt);
}
size_t matchIndex=ils->matchCnt;
while(matchIndex!=0) {
matchIndex--;
MatchInfo* mi=ils->getMatch(matchIndex);
if(!existsCompatibleMatch(ils, mi, prevILS)) {
ils->deleteMatch(matchIndex); }
}
if(!ils->matchCnt) {
return false;
}
}
}
return true;
}
void ClauseCodeTree::ClauseMatcher::enterLiteral(CodeOp* entry, bool seekOnlySuccess)
{
if(!seekOnlySuccess) {
RSTAT_MCTR_INC("enterLiteral levels (non-sos)", lms.size());
}
if(lms.isNonEmpty()) {
Recycled<LiteralMatcher, NoReset>& prevLM = lms.top();
ILStruct* ils=prevLM->op->getILS();
ASS_EQ(ils->timestamp,tree->_curTimeStamp);
ASS(!ils->visited);
ASS(!ils->finished);
ils->visited=true;
}
size_t linfoCnt=lInfos.size();
if(sres && sresLiteral!=sresNoLiteral) {
ASS_L(sresLiteral,lms.size());
ASS_EQ(linfoCnt%2,0);
linfoCnt/=2;
}
Recycled<LiteralMatcher, NoReset> lm;
lm->init(tree, entry, lInfos.array(), linfoCnt, seekOnlySuccess);
lms.push(std::move(lm));
}
void ClauseCodeTree::ClauseMatcher::leaveLiteral()
{
ASS(lms.isNonEmpty());
lms.pop();
if(lms.isNonEmpty()) {
LiteralMatcher* prevLM = &*lms.top();
ILStruct* ils=prevLM->op->getILS();
ASS_EQ(ils->timestamp,tree->_curTimeStamp);
ASS(ils->visited);
ils->finished=true;
if(sres) {
unsigned depth=lms.size()-1;
if(sresLiteral==depth) {
sresLiteral=sresNoLiteral;
}
ASS(sresLiteral==sresNoLiteral || sresLiteral<depth);
}
}
}
bool ClauseCodeTree::ClauseMatcher::checkCandidate(Clause* cl, int& resolvedQueryLit)
{
unsigned clen=cl->length();
ASS_EQ(clen, lms.size()-1);
ASS_EQ(clen, lms[clen-1]->op->getILS()->depth+1);
if(clen<=1) {
resolvedQueryLit=-1;
if(sres && clen==1) {
size_t matchCnt=lms[0]->getILS()->matchCnt;
for(size_t i=0;i<matchCnt;i++) {
MatchInfo* mi=lms[0]->getILS()->getMatch(i);
if(lInfos[mi->liIndex].opposite) {
resolvedQueryLit=lInfos[mi->liIndex].litIndex;
}
else {
resolvedQueryLit=-1;
break;
}
}
}
return true;
}
bool newMatches=false;
for(int i=clen-1;i>=0;i--) {
LiteralMatcher* lm = &*lms[i];
if(lm->eagerlyMatched()) {
break;
}
if(lm->getILS()->varCnt==0) {
continue;
}
newMatches|=lm->doEagerMatching();
}
(void)newMatches; return matchGlobalVars(resolvedQueryLit);
}
bool ClauseCodeTree::ClauseMatcher::matchGlobalVars(int& resolvedQueryLit)
{
unsigned clen=lms.size()-1;
static TriangularArray<int> remaining(10);
remaining.setSide(clen);
for(unsigned j=0;j<clen;j++) {
ILStruct* ils=lms[j]->getILS();
remaining.set(j,0,ils->matchCnt);
}
static DArray<int> matchIndex;
matchIndex.ensure(clen);
unsigned failLev=0;
for(unsigned i=0;i<clen;i++) {
matchIndex[i]=-1;
if(i>0) { failLev=20; }
bind_next_match:
matchIndex[i]++;
if(matchIndex[i]==remaining.get(i,i)) {
if(i==0) {
RSTAT_MCTR_INC("zero level fails at", failLev);
return false;
}
i--;
goto bind_next_match;
}
ASS_L(matchIndex[i],remaining.get(i,i));
ILStruct* bi=lms[i]->getILS(); MatchInfo* bq=bi->getMatch(matchIndex[i]);
for(unsigned j=i+1;j<clen;j++) {
ILStruct* ni=lms[j]->getILS(); unsigned rem=remaining.get(j,i);
unsigned k=0;
while(k<rem) {
MatchInfo* nq=ni->getMatch(k); if(!compatible(bi,bq,ni,nq)) {
rem--;
swap(ni->getMatch(k),ni->getMatch(rem));
continue;
}
k++;
}
if(rem==0) {
if(failLev<j) { failLev=j; }
goto bind_next_match;
}
remaining.set(j,i+1,rem);
}
}
resolvedQueryLit=-1;
if(sres) {
for(unsigned i=0;i<clen;i++) {
ILStruct* ils=lms[i]->getILS();
MatchInfo* mi=ils->getMatch(matchIndex[i]);
if(lInfos[mi->liIndex].opposite) {
resolvedQueryLit=lInfos[mi->liIndex].litIndex;
break;
}
}
}
return true;
}
bool ClauseCodeTree::ClauseMatcher::compatible(ILStruct* bi, MatchInfo* bq, ILStruct* ni, MatchInfo* nq)
{
if( lInfos[bq->liIndex].litIndex==lInfos[nq->liIndex].litIndex ||
(lInfos[bq->liIndex].opposite && lInfos[nq->liIndex].opposite) ) {
return false;
}
unsigned bvars=bi->varCnt;
unsigned* bgvn=bi->sortedGlobalVarNumbers;
TermList* bb=bq->bindings;
unsigned nvars=ni->varCnt;
unsigned* ngvn=ni->sortedGlobalVarNumbers;
TermList* nb=nq->bindings;
while(bvars && nvars) {
while(bvars && *bgvn<*ngvn) {
bvars--;
bgvn++;
bb++;
}
if(!bvars) {
break;
}
while(nvars && *bgvn>*ngvn) {
nvars--;
ngvn++;
nb++;
}
while(bvars && nvars && *bgvn==*ngvn) {
if(*bb!=*nb) {
return false;
}
bvars--;
bgvn++;
bb++;
nvars--;
ngvn++;
nb++;
}
}
return true;
}
bool ClauseCodeTree::ClauseMatcher::existsCompatibleMatch(ILStruct* si, MatchInfo* sq, ILStruct* targets)
{
size_t tcnt=targets->matchCnt;
for(size_t i=0;i<tcnt;i++) {
if(compatible(si,sq,targets,targets->getMatch(i))) {
return true;
}
}
return false;
}
}