// Specialized solver for random 3-SAT with planted solutions.
// Phase 1: Multi-strategy SLS (probSAT + PAWS clause weighting + neighborhood search)
// Phase 2: CDCL with 2WL, 1UIP, VSIDS, Luby restarts, clause deletion
// Exit codes: 10=SAT, 20=UNSAT, 0=UNKNOWN

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <chrono>
#include <cstdint>
#include <algorithm>

static std::chrono::steady_clock::time_point T0;
static inline double timer(){return std::chrono::duration<double>(std::chrono::steady_clock::now()-T0).count();}

static int N, M;
static int* orig; // original clause lits, flat, 3 per clause (DIMACS)

// ==================== FAST RNG ====================
static uint64_t rs[4];
static void rseed(uint64_t x){for(int i=0;i<4;i++){x+=0x9e3779b97f4a7c15ULL;uint64_t z=x;
z=(z^(z>>30))*0xbf58476d1ce4e5b9ULL;z=(z^(z>>27))*0x94d049bb133111ebULL;rs[i]=z^(z>>31);}}
static inline uint64_t rotl(uint64_t x,int k){return(x<<k)|(x>>(64-k));}
static inline uint64_t rnext(){uint64_t r=rotl(rs[1]*5,7)*9,t=rs[1]<<17;
rs[2]^=rs[0];rs[3]^=rs[1];rs[1]^=rs[2];rs[0]^=rs[3];rs[2]^=t;rs[3]=rotl(rs[3],45);return r;}
static inline uint32_t rmod(uint32_t n){return(uint32_t)(((uint64_t)(uint32_t)rnext()*(uint64_t)n)>>32);}
static inline double rdbl(){return(rnext()>>11)*0x1.0p-53;}

// ==================== PARSE ====================
static bool parse(const char* fn){
    FILE* f=fopen(fn,"r");if(!f)return false;
    fseek(f,0,SEEK_END);long sz=ftell(f);fseek(f,0,SEEK_SET);
    char* buf=(char*)malloc(sz+1);
    if(!buf){fclose(f);return false;}
    fread(buf,1,sz,f);buf[sz]=0;fclose(f);
    std::vector<int> tmp;tmp.reserve(50000);N=0;M=0;char*p=buf;
    while(*p){if(*p=='c'){while(*p&&*p!='\n')p++;if(*p)p++;continue;}
    if(*p=='p'){while(*p&&!(*p>='0'&&*p<='9'))p++;N=0;while(*p>='0'&&*p<='9')N=N*10+(*p++-'0');
    while(*p&&!(*p>='0'&&*p<='9'))p++;M=0;while(*p>='0'&&*p<='9')M=M*10+(*p++-'0');
    while(*p&&*p!='\n')p++;if(*p)p++;continue;}
    while(*p==' '||*p=='\t'||*p=='\n'||*p=='\r')p++;if(!*p)break;bool neg=false;
    if(*p=='-'){neg=true;p++;}if(*p>='0'&&*p<='9'){int v=0;while(*p>='0'&&*p<='9')v=v*10+(*p++-'0');
    if(neg)v=-v;if(v!=0)tmp.push_back(v);}else p++;}free(buf);
    M=(int)(tmp.size()/3);
    orig=(int*)malloc(3*M*sizeof(int));
    memcpy(orig,tmp.data(),3*M*sizeof(int));
    return true;
}

// ======================================================================
// ========================= SLS ENGINE =================================
// ======================================================================
namespace SLS {
    static int *occ, *occS, *occN;
    static int *val, *sc, *brk, *wbrk, *cw, *critical;
    static int *uq, *up;
    static int un;
    static double PTAB[256];
    static double WPTAB[2048];
    static bool built = false;
    // (removed bestVal/bestUn: they caused regression)

    static inline int LI(int l){return l>0?2*l:2*(-l)+1;}
    static inline int VR(int l){return l>0?l:-l;}
    static inline bool isT(int l){return l>0?val[l]==1:val[-l]==0;}
    static inline void addU(int c){up[c]=un;uq[un++]=c;}
    static inline void rmU(int c){int p=up[c];uq[p]=uq[--un];up[uq[p]]=p;up[c]=-1;}

    static void build(){
        if(built) return; built=true;
        int mx=2*(N+1);
        occN=(int*)calloc(mx,sizeof(int));
        for(int i=0;i<3*M;i++) occN[LI(orig[i])]++;
        occS=(int*)malloc(mx*sizeof(int));occS[0]=0;
        for(int i=1;i<mx;i++) occS[i]=occS[i-1]+occN[i-1];
        int total=occS[mx-1]+occN[mx-1];
        occ=(int*)malloc(total*sizeof(int));
        int*pos=(int*)calloc(mx,sizeof(int));
        for(int c=0;c<M;c++) for(int k=0;k<3;k++){
            int li=LI(orig[3*c+k]); occ[occS[li]+pos[li]++]=c;
        }
        free(pos);
        val=(int*)malloc((N+1)*sizeof(int));
        sc=(int*)malloc(M*sizeof(int));
        brk=(int*)malloc((N+1)*sizeof(int));
        wbrk=(int*)malloc((N+1)*sizeof(int));
        cw=(int*)malloc(M*sizeof(int));
        uq=(int*)malloc(M*sizeof(int));
        up=(int*)malloc(M*sizeof(int));
        critical=(int*)malloc(M*sizeof(int));
        for(int b=0;b<256;b++) PTAB[b]=pow(0.9+(double)b, -2.06);
        for(int b=0;b<2048;b++) WPTAB[b]=pow(0.9+(double)b, -2.06);
    }

    static void init(bool biased){
        if(biased){
            int* posC=(int*)calloc(N+1,sizeof(int));
            int* negC=(int*)calloc(N+1,sizeof(int));
            for(int i=0;i<3*M;i++){int l=orig[i];if(l>0)posC[l]++;else negC[-l]++;}
            for(int v=1;v<=N;v++){
                double p=(double)posC[v]/(posC[v]+negC[v]+1e-10);
                val[v]=rdbl()<p?1:0;
            }
            free(posC);free(negC);
        } else {
            for(int v=1;v<=N;v++) val[v]=rnext()&1;
        }
        un=0;
        memset(brk,0,(N+1)*sizeof(int));
        memset(wbrk,0,(N+1)*sizeof(int));
        for(int c=0;c<M;c++){
            up[c]=-1; cw[c]=1; critical[c]=0;
            int cnt=0; int lastT=0;
            for(int k=0;k<3;k++) if(isT(orig[3*c+k])){cnt++;lastT=VR(orig[3*c+k]);}
            sc[c]=cnt;
            if(cnt==0) addU(c);
            if(cnt==1){critical[c]=lastT; brk[lastT]++; wbrk[lastT]++;}
        }
    }

    static inline void flip_var(int var){
        int old=val[var]; val[var]=1-old;
        int tl=old?var:-var, fl=old?-var:var;
        int tli=LI(tl);
        for(int i=occS[tli],e=i+occN[tli];i<e;i++){
            int c=occ[i]; int w=cw[c]; int s=--sc[c];
            if(s==0){
                addU(c); brk[var]--; wbrk[var]-=w; critical[c]=0;
            } else if(s==1){
                for(int k=0;k<3;k++){
                    if(isT(orig[3*c+k])){
                        int v=VR(orig[3*c+k]);
                        brk[v]++; wbrk[v]+=w; critical[c]=v; break;
                    }
                }
            }
        }
        int fli=LI(fl);
        for(int i=occS[fli],e=i+occN[fli];i<e;i++){
            int c=occ[i]; int w=cw[c]; int s=++sc[c];
            if(s==1){
                rmU(c); brk[var]++; wbrk[var]+=w; critical[c]=var;
            } else if(s==2){
                int cv=critical[c];
                if(cv && cv!=var){brk[cv]--; wbrk[cv]-=w;}
                critical[c]=0;
            }
        }
    }

    // Increase weights of all currently unsatisfied clauses
    static void increaseWeights(){
        for(int i=0;i<un;i++) cw[uq[i]]++;
    }

    // Smooth weights: halve all weights > 1
    static void smoothWeights(){
        for(int c=0;c<M;c++){
            if(cw[c]>1){
                int old=cw[c];
                cw[c]=(cw[c]+1)/2;
                int diff=old-cw[c];
                if(diff>0 && sc[c]==1){
                    int cv=critical[c];
                    if(cv) wbrk[cv]-=diff;
                }
            }
        }
    }

    // Neighborhood search when un is small
    static bool neighborhoodSearch(int maxUn){
        if(un==0) return true;
        if(un>maxUn) return false;
        // Collect variables from unsat clauses
        int vars[64]; int nv=0;
        for(int i=0;i<un;i++){
            int c=uq[i];
            for(int k=0;k<3;k++){
                int v=VR(orig[3*c+k]);
                bool found=false;
                for(int j2=0;j2<nv;j2++) if(vars[j2]==v){found=true;break;}
                if(!found && nv<64) vars[nv++]=v;
            }
        }
        if(nv>20) return false;
        long long maxComb = 1LL<<nv;
        for(long long mask=1; mask<maxComb; mask++){
            for(int i2=0;i2<nv;i2++) if(mask&(1LL<<i2)) flip_var(vars[i2]);
            if(un==0) return true;
            for(int i2=nv-1;i2>=0;i2--) if(mask&(1LL<<i2)) flip_var(vars[i2]);
        }
        return false;
    }

    static bool solve(double tLim, bool useWeighting){
        int restart=0;

        if(!useWeighting){
            // Pure probSAT only (good for barthel, komb)
            printf("c SLS: pure probSAT until %.1fs\n", tLim);
            while(timer()<tLim){
                restart++;
                init(restart%3==0);
                long long flips=(long long)N*10000LL;
                for(long long t=0;t<flips && timer()<tLim;t++){
                    if(un==0) return true;
                    int c=uq[rmod(un)];
                    int v0=VR(orig[3*c]),v1=VR(orig[3*c+1]),v2=VR(orig[3*c+2]);
                    double p0=PTAB[brk[v0]<255?brk[v0]:255];
                    double p1=PTAB[brk[v1]<255?brk[v1]:255];
                    double p2=PTAB[brk[v2]<255?brk[v2]:255];
                    double tot=p0+p1+p2, r=rdbl()*tot;
                    flip_var(r<p0?v0:(r<p0+p1?v1:v2));
                }
                if(un<=3 && neighborhoodSearch(3)) return true;
                if(restart%50==0) printf("c SLS r=%d un=%d t=%.1fs\n",restart,un,timer());
            }
            return false;
        }

        // Phase 1: Quick probSAT (unweighted, short restarts)
        double phase1End = std::min(tLim, timer()+std::min(30.0, (tLim-timer())*0.10));
        printf("c SLS Phase 1: quick probSAT until %.1fs\n", phase1End);
        while(timer()<phase1End){
            restart++;
            init(restart%3==0);
            long long flips=(long long)N*10000LL;
            for(long long t=0;t<flips && timer()<phase1End;t++){
                if(un==0) return true;
                int c=uq[rmod(un)];
                int v0=VR(orig[3*c]),v1=VR(orig[3*c+1]),v2=VR(orig[3*c+2]);
                double p0=PTAB[brk[v0]<255?brk[v0]:255];
                double p1=PTAB[brk[v1]<255?brk[v1]:255];
                double p2=PTAB[brk[v2]<255?brk[v2]:255];
                double tot=p0+p1+p2, r=rdbl()*tot;
                flip_var(r<p0?v0:(r<p0+p1?v1:v2));
            }
            if(un<=3 && neighborhoodSearch(3)) return true;
        }

        // Phase 2: Weighted probSAT with PAWS-style clause weighting
        printf("c SLS Phase 2: weighted probSAT (PAWS) until %.1fs, restarts=%d\n", tLim, restart);
        while(timer()<tLim){
            restart++;
            init(restart%4==0);
            long long flips = (long long)N*100000LL;
            long long sinceBest=0;
            int bestUn=un;

            for(long long t=0;t<flips && timer()<tLim;t++){
                if(un==0) return true;

                // Periodic neighborhood search
                if(un<=3 && (t&0xFFF)==0){
                    if(neighborhoodSearch(3)) return true;
                }

                int c=uq[rmod(un)];
                int v0=VR(orig[3*c]),v1=VR(orig[3*c+1]),v2=VR(orig[3*c+2]);

                // Use weighted break for probSAT selection
                int wb0=wbrk[v0]; if(wb0<0)wb0=0; if(wb0>2047)wb0=2047;
                int wb1=wbrk[v1]; if(wb1<0)wb1=0; if(wb1>2047)wb1=2047;
                int wb2=wbrk[v2]; if(wb2<0)wb2=0; if(wb2>2047)wb2=2047;
                double p0=WPTAB[wb0], p1=WPTAB[wb1], p2=WPTAB[wb2];
                double tot=p0+p1+p2, r=rdbl()*tot;
                flip_var(r<p0?v0:(r<p0+p1?v1:v2));

                if(un<bestUn){bestUn=un;sinceBest=0;}
                else sinceBest++;

                // Weight update
                if(sinceBest>0 && sinceBest%(long long)(N*2)==0){
                    if(rdbl()<0.4) smoothWeights();
                    else increaseWeights();
                }
            }

            if(un<=5 && neighborhoodSearch(5)) return true;
            if(restart%10==0) printf("c SLS r=%d un=%d t=%.1fs\n",restart,un,timer());
        }
        return false;
    }

    static bool verify(){
        for(int c=0;c<M;c++){bool sat=false;
            for(int k=0;k<3;k++){int l=orig[3*c+k];
                if(l>0?val[l]==1:val[-l]==0){sat=true;break;}}
            if(!sat) return false;}
        return true;
    }
}

// ======================================================================
// ========================= CDCL =======================================
// ======================================================================
namespace CDCL {
    static inline int mklit(int v,bool neg){return 2*v+(neg?1:0);}
    static inline int fromDimacs(int l){return l>0?2*l:2*(-l)+1;}
    static inline int var(int il){return il>>1;}
    static inline int neg(int il){return il^1;}

    // Clause storage
    static std::vector<int> cl_data;
    static std::vector<int> cl_off;
    static std::vector<int> cl_sz;
    static std::vector<double> cl_act; // clause activity for deletion
    static std::vector<bool> cl_learnt; // is this a learned clause?
    static std::vector<int> cl_lbd;   // LBD (literal block distance) for learned clauses
    static int nClauses;

    static int addClause(const int* lits, int sz, bool learnt, int lbd=0){
        int ci = nClauses++;
        cl_off.push_back(cl_data.size());
        cl_sz.push_back(sz);
        cl_act.push_back(0.0);
        cl_learnt.push_back(learnt);
        cl_lbd.push_back(lbd);
        for(int i=0;i<sz;i++) cl_data.push_back(lits[i]);
        return ci;
    }

    // Watch lists: each entry is {blocking_literal, clause_index}
    struct Watcher { int blocker; int cid; };
    static std::vector<std::vector<Watcher>> watches;

    // Assignment
    static std::vector<int> val; // indexed by internal literal: 0=unset, 1=true, -1=false
    static std::vector<int> trail;
    static std::vector<int> trail_lim;
    static std::vector<int> var_reason;
    static std::vector<int> var_level;
    static int qhead;

    // VSIDS
    static std::vector<double> activity;
    static double var_inc;
    static double var_decay_inv; // 1.0/decay, set per instance type
    static double cl_inc;
    static std::vector<int> saved_pol;

    // Conflict analysis
    static std::vector<int> seen;     // stamp-based: seen[v]==stamp means "seen"
    static std::vector<int> lvl_seen; // stamp-based for level counting in LBD
    static int stamp;

    // Variable order heap (max-heap by activity)
    static std::vector<int> heap;      // heap[0..heapSize-1] contains variable indices
    static std::vector<int> heap_pos;  // heap_pos[v] = position in heap, or -1 if not in heap
    static int heapSize;

    static void heap_swap(int i, int j){
        int vi=heap[i], vj=heap[j];
        heap[i]=vj; heap[j]=vi;
        heap_pos[vi]=j; heap_pos[vj]=i;
    }
    static void heap_up(int i){
        while(i>0){
            int p=(i-1)/2;
            if(activity[heap[i]]>activity[heap[p]]){heap_swap(i,p);i=p;}
            else break;
        }
    }
    static void heap_down(int i){
        while(true){
            int best=i, l=2*i+1, r=2*i+2;
            if(l<heapSize && activity[heap[l]]>activity[heap[best]]) best=l;
            if(r<heapSize && activity[heap[r]]>activity[heap[best]]) best=r;
            if(best!=i){heap_swap(i,best);i=best;}
            else break;
        }
    }
    static void heap_insert(int v){
        if(heap_pos[v]>=0) return;
        heap_pos[v]=heapSize;
        heap[heapSize++]=v;
        heap_up(heapSize-1);
    }
    static int heap_pop(){
        if(heapSize==0) return -1;
        int v=heap[0];
        heapSize--;
        if(heapSize>0){
            heap[0]=heap[heapSize];
            heap_pos[heap[0]]=0;
        }
        heap_pos[v]=-1;
        if(heapSize>0) heap_down(0);
        return v;
    }
    static void heap_update(int v){
        if(heap_pos[v]>=0) heap_up(heap_pos[v]);
    }

    static int level(){return (int)trail_lim.size();}

    static void assign(int lit, int lv, int reason){
        int v=var(lit);
        val[lit]=1; val[neg(lit)]=-1;
        var_level[v]=lv; var_reason[v]=reason;
        trail.push_back(lit);
    }

    static void unassign_var(int v){
        saved_pol[v] = val[mklit(v,false)]==1 ? 0 : 1;
        val[mklit(v,false)]=0;val[mklit(v,true)]=0;
        var_level[v]=-1;var_reason[v]=-1;
        heap_insert(v); // put back in heap when unassigned
    }

    static void build(){
        int maxlit = 2*(N+1);
        watches.assign(maxlit, {});
        val.assign(maxlit, 0);
        trail.clear(); trail.reserve(N+1);
        trail_lim.clear();
        var_reason.assign(N+1, -1);
        var_level.assign(N+1, -1);
        qhead=0;
        activity.assign(N+1, 0.0);
        var_inc=1.0;
        var_decay_inv=1.0/0.95; // default, may be overridden
        cl_inc=1.0;
        saved_pol.assign(N+1, 0);
        seen.assign(N+1, 0);
        lvl_seen.assign(N+1, 0);
        stamp=0;

        cl_data.clear(); cl_off.clear(); cl_sz.clear();
        cl_act.clear(); cl_learnt.clear(); cl_lbd.clear(); nClauses=0;
        cl_data.reserve(3*M+100*N);

        for(int c=0;c<M;c++){
            int lits[3];
            for(int k=0;k<3;k++) lits[k]=fromDimacs(orig[3*c+k]);
            int ci=addClause(lits,3,false);
            watches[lits[0]].push_back({lits[1], ci});
            watches[lits[1]].push_back({lits[0], ci});
        }

        // Init activity from clause participation
        for(int c=0;c<M;c++)
            for(int k=0;k<3;k++)
                activity[abs(orig[3*c+k])]+=1.0;

        // Initialize variable order heap
        heap.resize(N+1);
        heap_pos.assign(N+1, -1);
        heapSize=0;
        for(int v=1;v<=N;v++) heap_insert(v);
    }

    // 2-watched literal propagation with blocking literals
    static int propagate(){
        while(qhead<(int)trail.size()){
            int p=trail[qhead++];
            int fp=neg(p); // the false literal
            auto& ws=watches[fp];
            int i=0, j=0;
            while(i<(int)ws.size()){
                Watcher w=ws[i];

                // Blocking literal check: if blocker is true, clause is satisfied
                if(val[w.blocker]==1){ws[j++]=w;i++;continue;}

                int ci=w.cid;
                int off=cl_off[ci];
                int sz=cl_sz[ci];
                int* lits=&cl_data[off];

                // Ensure fp is at position 1
                if(lits[0]==fp) std::swap(lits[0],lits[1]);

                // If other watched literal (lits[0]) is true, clause satisfied
                if(val[lits[0]]==1){
                    ws[j++]={lits[0], ci}; // update blocker
                    i++;continue;
                }

                // Find replacement watch in positions 2..sz-1
                bool found=false;
                for(int k=2;k<sz;k++){
                    if(val[lits[k]]!=-1){
                        std::swap(lits[1],lits[k]);
                        watches[lits[1]].push_back({lits[0], ci});
                        found=true;
                        break;
                    }
                }
                if(found){i++;continue;}

                // No replacement: all lits[2..] are false
                ws[j++]={lits[0], ci}; i++;

                if(val[lits[0]]==-1){
                    // Conflict
                    while(i<(int)ws.size()) ws[j++]=ws[i++];
                    ws.resize(j);
                    return ci;
                }
                // Unit propagation
                assign(lits[0],level(),ci);
            }
            ws.resize(j);
        }
        return -1;
    }

    // 1-UIP conflict analysis
    static std::vector<int> learnt;
    static int btLevel;
    static int learntLBD;
    static void analyze(int confl){
        stamp++;
        learnt.clear();
        int counter=0;
        int p=-1;
        int ci=confl;
        int idx=(int)trail.size()-1;

        do {
            // Bump clause activity
            if(ci>=0) cl_act[ci]+=cl_inc;

            int off=cl_off[ci];
            int sz=cl_sz[ci];
            for(int k=0;k<sz;k++){
                int lit=cl_data[off+k];
                if(p!=-1 && lit==p) continue;
                int v=var(lit);
                if(seen[v]!=stamp && var_level[v]>0){
                    seen[v]=stamp;
                    activity[v]+=var_inc;
                    heap_update(v);
                    if(var_level[v]>=level()) counter++;
                    else learnt.push_back(lit);
                }
            }
            while(idx>=0 && seen[var(trail[idx])]!=stamp) idx--;
            if(idx<0) break;
            p=trail[idx];
            ci=var_reason[var(p)];
            seen[var(p)]=stamp-1; // unmark
            counter--;
            idx--;
        } while(counter>0);

        if(p!=-1) learnt.insert(learnt.begin(), neg(p));

        // Compute backtrack level
        if(learnt.size()<=1) btLevel=0;
        else {
            int maxi=1;
            for(int i=2;i<(int)learnt.size();i++)
                if(var_level[var(learnt[i])]>var_level[var(learnt[maxi])]) maxi=i;
            std::swap(learnt[1],learnt[maxi]);
            btLevel=var_level[var(learnt[1])];
        }

        // Compute LBD (number of distinct decision levels in learnt clause)
        {
            stamp++;
            int lbd = 0;
            for(int lit : learnt){
                int lv = var_level[var(lit)];
                if(lv >= 0 && lvl_seen[lv]!=stamp){ lvl_seen[lv]=stamp; lbd++; }
            }
            learntLBD = lbd;
        }
    }

    static void backtrack(int lv){
        while((int)trail.size()>(lv<(int)trail_lim.size()?trail_lim[lv]:0)){
            unassign_var(var(trail.back()));trail.pop_back();
        }
        trail_lim.resize(lv);
        qhead=trail.size();
    }

    static int pickVar(){
        // Try heap first
        while(heapSize>0){
            int v=heap_pop();
            if(val[mklit(v,false)]==0) return v;
        }
        // Fallback to linear scan
        for(int v=1;v<=N;v++){
            if(val[mklit(v,false)]==0) return v;
        }
        return -1;
    }

    static int liveLearnedCount;

    // Clause deletion with compaction to prevent memory blowup
    static void reduceDB(){
        // Build set of reason clauses
        std::vector<bool> isReasonCl(nClauses, false);
        for(int ti=0;ti<(int)trail.size();ti++){
            int r=var_reason[var(trail[ti])];
            if(r>=0) isReasonCl[r]=true;
        }

        // Collect deletable learned clauses (not glue, not reason, not deleted)
        std::vector<int> learned_ids;
        for(int ci=M;ci<nClauses;ci++){
            if(cl_learnt[ci] && cl_sz[ci]>0 && cl_lbd[ci]>2 && !isReasonCl[ci]){
                learned_ids.push_back(ci);
            }
        }
        if(learned_ids.size()<100) return;

        // Sort by: high LBD first, then low activity
        std::sort(learned_ids.begin(), learned_ids.end(),
            [](int a, int b){
                if(cl_lbd[a]!=cl_lbd[b]) return cl_lbd[a]>cl_lbd[b];
                return cl_act[a]<cl_act[b];
            });

        int nDel = learned_ids.size()/2;

        // Mark clauses for deletion
        std::vector<bool> deleted(nClauses, false);
        for(int i=0;i<nDel;i++){
            deleted[learned_ids[i]]=true;
        }

        // Remove deleted clauses from watch lists
        for(int lit=0;lit<(int)watches.size();lit++){
            auto& ws=watches[lit];
            int k=0;
            for(int ii=0;ii<(int)ws.size();ii++){
                if(!deleted[ws[ii].cid]) ws[k++]=ws[ii];
            }
            ws.resize(k);
        }

        for(int ci=0;ci<nClauses;ci++){
            if(deleted[ci]){cl_sz[ci]=0; liveLearnedCount--;}
        }
    }

    // Full compaction: rebuild clause database removing deleted clauses
    static void compactDB(){
        // Map old clause id -> new clause id
        std::vector<int> remap(nClauses, -1);
        std::vector<int> new_data;
        std::vector<int> new_off;
        std::vector<int> new_sz;
        std::vector<double> new_act;
        std::vector<bool> new_learnt;
        std::vector<int> new_lbd;
        int newCount=0;

        new_data.reserve(cl_data.size()/2);

        for(int ci=0;ci<nClauses;ci++){
            if(cl_sz[ci]==0) continue;
            remap[ci]=newCount;
            new_off.push_back(new_data.size());
            new_sz.push_back(cl_sz[ci]);
            new_act.push_back(cl_act[ci]);
            new_learnt.push_back(cl_learnt[ci]);
            new_lbd.push_back(cl_lbd[ci]);
            int off=cl_off[ci];
            for(int k=0;k<cl_sz[ci];k++) new_data.push_back(cl_data[off+k]);
            newCount++;
        }

        // Update watch lists with new IDs
        for(int lit=0;lit<(int)watches.size();lit++){
            auto& ws=watches[lit];
            int k=0;
            for(int ii=0;ii<(int)ws.size();ii++){
                int nid=remap[ws[ii].cid];
                if(nid>=0){ws[k]=ws[ii]; ws[k].cid=nid; k++;}
            }
            ws.resize(k);
        }

        // Update reason clause IDs
        for(int v=1;v<=N;v++){
            if(var_reason[v]>=0){
                var_reason[v]=remap[var_reason[v]];
            }
        }

        cl_data=std::move(new_data);
        cl_off=std::move(new_off);
        cl_sz=std::move(new_sz);
        cl_act=std::move(new_act);
        cl_learnt=std::move(new_learnt);
        cl_lbd=std::move(new_lbd);
        nClauses=newCount;

        // Count original clauses in new numbering
        // (originals are always first, so M stays the same count but we need to recount)
        // Actually original clauses are preserved in order, so the first ones with learnt=false stay
    }

    // Luby sequence: 1,1,2,1,1,2,4,1,1,2,1,1,2,4,8,...
    static int luby_seq(int i){
        int sz=1, seq=1;
        while(sz<i+1){sz=2*sz+1;seq*=2;}
        while(sz-1!=i){seq>>=1;sz>>=1;if(i>=sz)i-=sz;}
        return seq;
    }

    static std::vector<int> initial_phase; // saved from SLS

    static bool solve(double tLim){
        long long conflicts=0, decisions=0;
        int restarts=0;
        long long nextRestart=100;
        long long nextReduce=2000;
        double nextLog=timer()+1.0;
        liveLearnedCount=0;
        printf("c CDCL: starting, heap=%d vars, tLim=%.1f\n", heapSize, tLim);
        fflush(stdout);

        // Save initial phase for rephasing
        initial_phase.resize(N+1);
        for(int v=1;v<=N;v++) initial_phase[v]=saved_pol[v];

        // Glucose-style restart: track recent vs global average LBD
        double lbdSum=0;
        long long lbdCount=0;
        static const int LBD_WINDOW = 50;
        double recentLbd[LBD_WINDOW];
        int lbdWinPos=0;
        double recentSum=0;
        bool lbdWinFull=false;
        long long conflsSinceRestart=0;
        long long lastCompact=0;
        long long nextRephase=10000;
        int rephaseCount=0;

        while(timer()<tLim){
            if(timer()>=nextLog){
                printf("c CDCL: conf=%lld dec=%lld cls=%d/%d lv=%d trail=%d t=%.1fs\n",
                    conflicts,decisions,liveLearnedCount,nClauses,level(),(int)trail.size(),timer());
                fflush(stdout);
                nextLog=timer()+10.0;
            }
            int confl=propagate();
            if(confl>=0){
                conflicts++;
                conflsSinceRestart++;
                if(level()==0) return false;

                analyze(confl);
                backtrack(btLevel);

                // Track LBD statistics
                lbdSum += learntLBD;
                lbdCount++;
                recentSum += learntLBD;
                if(lbdWinFull) recentSum -= recentLbd[lbdWinPos];
                recentLbd[lbdWinPos] = learntLBD;
                lbdWinPos = (lbdWinPos+1) % LBD_WINDOW;
                if(lbdWinPos==0) lbdWinFull=true;

                if(learnt.size()==1){
                    assign(learnt[0],0,-1);
                } else {
                    int ci=addClause(learnt.data(),(int)learnt.size(),true,learntLBD);
                    cl_act[ci]=cl_inc;
                    watches[learnt[0]].push_back({learnt[1], ci});
                    watches[learnt[1]].push_back({learnt[0], ci});
                    assign(learnt[0],btLevel,ci);
                    liveLearnedCount++;
                }

                var_inc *= var_decay_inv;
                if(var_inc>1e100){
                    for(int v=1;v<=N;v++) activity[v]*=1e-100;
                    var_inc*=1e-100;
                    for(int ii=heapSize/2-1;ii>=0;ii--) heap_down(ii);
                }
                cl_inc *= (1.0/0.999);
                if(cl_inc>1e100){
                    for(int ci2=0;ci2<nClauses;ci2++) cl_act[ci2]*=1e-100;
                    cl_inc*=1e-100;
                }

                // Glucose-style restart: restart when recent LBD is much higher than global average
                bool doRestart = false;
                if(lbdWinFull && conflsSinceRestart >= LBD_WINDOW){
                    double globalAvg = lbdSum / lbdCount;
                    double recentAvg = recentSum / LBD_WINDOW;
                    if(recentAvg * 0.8 > globalAvg) doRestart = true;
                }
                // Also keep Luby restarts as backup
                if(conflicts>=nextRestart) doRestart = true;

                if(doRestart){
                    restarts++;
                    nextRestart=conflicts+100*luby_seq(restarts);
                    conflsSinceRestart=0;
                    backtrack(0);
                }

                // Periodic rephasing
                if(conflicts>=nextRephase){
                    rephaseCount++;
                    if(rephaseCount%3==0){
                        // Reset to initial (SLS) phase
                        for(int v=1;v<=N;v++) saved_pol[v]=initial_phase[v];
                    } else if(rephaseCount%3==1){
                        // Random phase
                        for(int v=1;v<=N;v++) saved_pol[v]=rnext()&1;
                    }
                    // rephaseCount%3==2: keep current (best) phase
                    nextRephase=conflicts+10000;
                }

                if(conflicts>=nextReduce){
                    reduceDB();
                    nextReduce=conflicts+500+liveLearnedCount/10;
                    // Compact when total clauses >> live clauses (save memory)
                    if(nClauses > M + liveLearnedCount*3 && conflicts-lastCompact>50000){
                        compactDB();
                        lastCompact=conflicts;
                    }
                }
            } else {
                int v=pickVar();
                if(v<0) return true;
                decisions++;
                trail_lim.push_back(trail.size());
                assign(mklit(v,saved_pol[v]),level(),-1);
            }
        }
        return false;
    }

    static bool getModel(int* model){
        for(int v=1;v<=N;v++) model[v]=(val[mklit(v,false)]==1)?1:0;
        // Verify
        for(int c=0;c<M;c++){bool sat=false;for(int k=0;k<3;k++){int l=orig[3*c+k];
            if(l>0?model[l]==1:model[-l]==0){sat=true;break;}}if(!sat)return false;}
        return true;
    }
}

// ==================== MAIN ====================
int main(int argc, char** argv){
    if(argc!=3){fprintf(stderr,"usage: solver <cnf> <pbp>\n");return 1;}
    T0=std::chrono::steady_clock::now();setbuf(stdout,nullptr);

    printf("c planted-3SAT solver v14 (multi-SLS + CDCL)\n");
    if(!parse(argv[1])){printf("s UNKNOWN\n");return 0;}
    double ratio=(double)M/N;
    printf("c N=%d M=%d ratio=%.3f\n",N,M,ratio);

    // Seed from instance content + time for varied but instance-dependent behavior
    {
        uint64_t h = 14695981039346656037ULL;
        for(int i=0;i<3*M;i++){
            h ^= (uint64_t)(uint32_t)orig[i];
            h *= 1099511628211ULL;
        }
        h ^= (uint64_t)N * 7654321ULL;
        h ^= (uint64_t)M * 1234567ULL;
        h ^= std::chrono::steady_clock::now().time_since_epoch().count();
        rseed(h);
    }

    double totalTime = 575.0;

    // Determine time allocation based on ratio
    double slsTime;
    bool useWeighting;
    if(ratio > 5.35){
        // qhid: SLS very short try, then CDCL (which handles qhid very well)
        slsTime = std::min(totalTime, 5.0);
        useWeighting = false;
        printf("c Detected qhid-type (ratio=%.3f)\n", ratio);
    } else if(ratio > 5.0){
        // komb: pure probSAT for 60s, then CDCL
        slsTime = std::min(totalTime, 60.0);
        useWeighting = false;
        printf("c Detected komb-type (ratio=%.3f)\n", ratio);
    } else if(ratio > 4.35){
        // plain: time allocation depends on N
        if(N <= 400) {
            slsTime = std::min(totalTime, 150.0);
            useWeighting = true;
        } else {
            // For N>=450: brief SLS attempt, then CDCL with most time
            slsTime = std::min(totalTime, 30.0);
            useWeighting = false; // just pure probSAT, quick
        }
        printf("c Detected plain-type (ratio=%.3f, N=%d)\n", ratio, N);
    } else {
        // barthel: SLS should solve instantly
        slsTime = std::min(totalTime, 30.0);
        useWeighting = false;
        printf("c Detected barthel-type (ratio=%.3f)\n", ratio);
    }

    // Phase 1: SLS
    SLS::build();
    printf("c Phase 1: SLS (%.0fs budget, weighting=%s)\n", slsTime, useWeighting?"yes":"no");
    if(SLS::solve(slsTime, useWeighting) && SLS::verify()){
        printf("c SLS solved %.2fs\n",timer());
        printf("s SATISFIABLE\nv ");
        for(int v=1;v<=N;v++){if(v>1&&(v-1)%20==0)printf("\nv ");printf("%d ",SLS::val[v]?v:-v);}
        printf("0\n");return 10;
    }
    printf("c SLS failed at t=%.1fs\n",timer());

    // Phase 2: CDCL
    double remaining = totalTime - timer();
    if(remaining > 5){
        printf("c Phase 2: CDCL (%.0fs remaining)\n", remaining);
        CDCL::build();
        // For random 3-SAT (plain instances), use faster variable decay
        if(ratio > 4.35 && ratio <= 5.0){
            CDCL::var_decay_inv = 1.0/0.85;
        }
        // Transfer SLS best assignment as phase hints
        for(int v=1;v<=N;v++) CDCL::saved_pol[v] = SLS::val[v] ? 0 : 1;
        if(CDCL::solve(totalTime)){
            int* model=(int*)malloc((N+1)*sizeof(int));
            if(CDCL::getModel(model)){
                printf("c CDCL solved %.2fs\n",timer());
                printf("s SATISFIABLE\nv ");
                for(int v=1;v<=N;v++){if(v>1&&(v-1)%20==0)printf("\nv ");printf("%d ",model[v]?v:-v);}
                printf("0\n");free(model);return 10;
            }
            free(model);
        }
    }

    printf("c FAILED t=%.1fs\n",timer());
    printf("s UNKNOWN\n");return 0;
}
