// Diagnosis SAT Solver v13 - FLP + BVE + CDCL
// Adaptive: Quick CDCL -> FLP -> BVE -> Full CDCL
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <vector>
#include <algorithm>
#include <chrono>
#include <cmath>
using namespace std;
static auto g_start = chrono::steady_clock::now();
static double elapsed(){return chrono::duration<double>(chrono::steady_clock::now()-g_start).count();}
static constexpr double TIME_LIMIT = 595.0;

// Literal encoding: var v -> pos lit = 2v, neg lit = 2v+1
static inline int mklit(int v,bool neg){return 2*v+(neg?1:0);}
static inline int litvar(int l){return l>>1;}
static inline int litneg(int l){return l^1;}
static inline bool litsgn(int l){return l&1;}

struct Clause{
    unsigned header; int lbd; int lits[1];
    int size()const{return(int)(header>>2);}
    bool learnt()const{return header&1;}
    bool deleted()const{return header&2;}
    void mark_deleted(){header|=2;}
    int&operator[](int i){return lits[i];}
    int operator[](int i)const{return lits[i];}
};

struct ClauseAllocator{
    int*mem;int sz,cap;
    ClauseAllocator():mem(nullptr),sz(0),cap(0){}
    ~ClauseAllocator(){free(mem);}
    void reserve(int n){cap=n;mem=(int*)malloc(cap*sizeof(int));}
    void ensure(int need){if(sz+need>cap){cap=max(cap*2,sz+need+4096);mem=(int*)realloc(mem,cap*sizeof(int));}}
    int alloc(int n,bool l,int lbd){int w=2+n;ensure(w);int r=sz;Clause*c=(Clause*)&mem[r];
        c->header=((unsigned)n<<2)|(unsigned)l;c->lbd=lbd;sz+=w;return r;}
    Clause&operator[](int r){return*(Clause*)&mem[r];}
};

struct Watcher{int cr,blocker;};

    struct Solver{
    int nvars=0; ClauseAllocator ca;
    int eo_max_var=0;
    vector<vector<Watcher>>watches;
    vector<vector<int>>bins;
    int8_t*val; int*dlev_arr; int*reason_arr;
    double*act; int8_t*phase_arr; uint8_t*seen_arr;
    uint8_t*eo_var;
    vector<int>trail,trail_lim; int qhead=0;
    static constexpr int Reason_Undef=-1,Reason_Binary_Base=-3;
    static int make_bin_reason(int o){return-(o+3);}
    static int bin_reason_lit(int r){return-(r+3);}
    static bool is_bin_reason(int r){return r<=Reason_Binary_Base;}
    
    // Heap (max-heap by activity)
    double var_bump=1.0;vector<int>heap;int*hpos_arr;
    void hup(int p){int v=heap[p];while(p>0){int par=(p-1)/2;if(act[heap[par]]>=act[v])break;heap[p]=heap[par];hpos_arr[heap[par]]=p;p=par;}heap[p]=v;hpos_arr[v]=p;}
    void hdown(int p){int v=heap[p];int n=heap.size();while(true){int c=2*p+1;if(c>=n)break;if(c+1<n&&act[heap[c+1]]>act[heap[c]])c++;if(act[v]>=act[heap[c]])break;heap[p]=heap[c];hpos_arr[heap[c]]=p;p=c;}heap[p]=v;hpos_arr[v]=p;}
    void hinsert(int v){if(hpos_arr[v]>=0)return;hpos_arr[v]=heap.size();heap.push_back(v);hup(heap.size()-1);}
    void hupdate(int v){if(hpos_arr[v]>=0)hup(hpos_arr[v]);}
    int hpop(){int v=heap[0];hpos_arr[v]=-1;if(heap.size()>1){heap[0]=heap.back();hpos_arr[heap[0]]=0;heap.pop_back();hdown(0);}else heap.pop_back();return v;}
    void bump(int v){act[v]+=var_bump;if(act[v]>1e100){for(int i=1;i<=nvars;i++)act[i]*=1e-100;var_bump*=1e-100;}hupdate(v);}
    
    // Proof
    FILE*proof_fp=nullptr; int proof_id=0,orig_clause_count=0;
    bool proof_enabled=true;
    long long n_conflicts=0,n_decisions=0,n_props=0;
    int n_restarts=0,n_learnt=0,n_reduced=0;
    vector<int>orig_crefs,learnt_crefs;
    uint32_t*min_stamp;uint32_t min_counter=0;
    long long last_restart_confl=0;
    int dl()const{return trail_lim.size();}
    int litval(int l)const{int8_t v=val[litvar(l)];return v==0?0:(litsgn(l)?-v:v);}
    int conf_np,conf_imp;
    
    // BVE data
    struct BVEClause{vector<int>lits;int proof_id;bool alive;};
    vector<BVEClause>bve_clauses;
    vector<vector<int>>bve_pos,bve_neg; // occurrence lists
    struct ElimEntry{int var;vector<vector<int>>saved_clauses;};
    vector<ElimEntry>elim_stack; // for SAT model reconstruction
    int bve_eliminated=0;
    
    void init(int nv){
        nvars=nv;int nv1=nv+1;
        val=(int8_t*)calloc(nv1,sizeof(int8_t));
        dlev_arr=(int*)malloc(nv1*sizeof(int));
        reason_arr=(int*)malloc(nv1*sizeof(int));
        act=(double*)calloc(nv1,sizeof(double));
        phase_arr=(int8_t*)malloc(nv1*sizeof(int8_t));
        seen_arr=(uint8_t*)calloc(nv1,sizeof(uint8_t));
        eo_var=(uint8_t*)calloc(nv1,sizeof(uint8_t));
        min_stamp=(uint32_t*)calloc(nv1,sizeof(uint32_t));
        hpos_arr=(int*)malloc(nv1*sizeof(int));
        memset(dlev_arr,0xff,nv1*sizeof(int));
        memset(reason_arr,0xff,nv1*sizeof(int));
        memset(phase_arr,1,nv1*sizeof(int8_t));
        memset(hpos_arr,0xff,nv1*sizeof(int));
        bins.resize(2*nv1);watches.resize(2*nv1);
        for(int v=1;v<=nv;v++)hinsert(v);
        ca.reserve(16*1024*1024);
    }
    
    void enqueue(int lit,int rsn){
        int v=litvar(lit);val[v]=litsgn(lit)?-1:1;
        dlev_arr[v]=dl();reason_arr[v]=rsn;trail.push_back(lit);
    }
    
    void attach_clause(int cr){
        Clause&c=ca[cr];
        watches[c[0]].push_back({cr,c[1]});
        watches[c[1]].push_back({cr,c[0]});
    }
    
    int propagate(){
        while(qhead<(int)trail.size()){
            int p=trail[qhead++];int np=litneg(p);n_props++;
            // Binary propagation
            auto&blist=bins[np];int bsz=blist.size();
            for(int k=0;k<bsz;k++){
                int imp=blist[k];int iv=litvar(imp);int8_t vv=val[iv];
                if(vv==0){enqueue(imp,make_bin_reason(np));}
                else{int8_t ex=litsgn(imp)?-1:1;if(vv!=ex){conf_np=np;conf_imp=imp;return-2;}}
            }
            // Long clause propagation (2WL)
            auto&ws=watches[np];int i=0,j=0;
            while(i<(int)ws.size()){
                Watcher w=ws[i++];
                if(litval(w.blocker)>0){ws[j++]=w;continue;}
                Clause&c=ca[w.cr];
                if(c.deleted())continue;
                int sz=c.size();
                if(c[0]==np){int t=c[0];c[0]=c[1];c[1]=t;}
                int first=c[0];w.blocker=first;
                if(litval(first)>0){ws[j++]=w;continue;}
                bool found=false;
                for(int k=2;k<sz;k++){
                    if(litval(c[k])>=0){
                        int t=c[1];c[1]=c[k];c[k]=t;
                        watches[c[1]].push_back({w.cr,first});
                        found=true;break;
                    }
                }
                if(found)continue;
                ws[j++]=w;
                if(litval(first)<0){
                    while(i<(int)ws.size())ws[j++]=ws[i++];
                    ws.resize(j);return w.cr;
                }
                enqueue(first,w.cr);
            }
            ws.resize(j);
        }
        return-1;
    }
    
    // Analysis
    vector<int>learnt_lits;int bt_level,learnt_lbd;
    vector<int>rm_stack,rm_visited;
    
    bool lit_removable(int v0){
        int r=reason_arr[v0];if(r==Reason_Undef)return false;
        rm_stack.clear();rm_visited.clear();rm_stack.push_back(v0);
        while(!rm_stack.empty()){
            int cv=rm_stack.back();rm_stack.pop_back();
            int cr=reason_arr[cv];if(cr==Reason_Undef){for(int vv:rm_visited)min_stamp[vv]=0;return false;}
            if(is_bin_reason(cr)){
                int rv=litvar(bin_reason_lit(cr));
                if(dlev_arr[rv]==0||seen_arr[rv]||min_stamp[rv]==min_counter)continue;
                min_stamp[rv]=min_counter;rm_visited.push_back(rv);rm_stack.push_back(rv);
            }else{
                Clause&c=ca[cr];
                for(int k=1;k<c.size();k++){
                    int rv=litvar(c[k]);
                    if(dlev_arr[rv]==0||seen_arr[rv]||min_stamp[rv]==min_counter)continue;
                    min_stamp[rv]=min_counter;rm_visited.push_back(rv);rm_stack.push_back(rv);
                }
            }
        }
        return true;
    }
    
    void analyze(int confl){
        learnt_lits.clear();learnt_lits.push_back(0);
        int counter=0,p=0,idx=trail.size()-1;
        auto process=[&](int lit){
            int v=litvar(lit);if(seen_arr[v]||dlev_arr[v]==0)return;
            seen_arr[v]=1;bump(v);if(dlev_arr[v]==dl())counter++;else learnt_lits.push_back(lit);
        };
        if(confl==-2){process(conf_np);process(conf_imp);}
        else{Clause&c=ca[confl];for(int k=0;k<c.size();k++)process(c[k]);}
        while(counter>0){
            while(idx>=0&&!seen_arr[litvar(trail[idx])])idx--;
            if(idx<0)break;p=trail[idx--];int v=litvar(p);seen_arr[v]=0;counter--;
            if(counter==0)break;
            int r=reason_arr[v];if(r==Reason_Undef)continue;
            if(is_bin_reason(r)){process(bin_reason_lit(r));}
            else{Clause&c=ca[r];for(int k=1;k<c.size();k++)process(c[k]);}
        }
        learnt_lits[0]=litneg(p);
        min_counter++;if(min_counter==0){memset(min_stamp,0,(nvars+1)*sizeof(uint32_t));min_counter=1;}
        for(int l:learnt_lits)min_stamp[litvar(l)]=min_counter;
        int j2=1;
        for(int k=1;k<(int)learnt_lits.size();k++)
            if(!lit_removable(litvar(learnt_lits[k])))learnt_lits[j2++]=learnt_lits[k];
        learnt_lits.resize(j2);
        seen_arr[litvar(learnt_lits[0])]=0;
        for(int k=1;k<(int)learnt_lits.size();k++)seen_arr[litvar(learnt_lits[k])]=0;
        for(int k=0;k<(int)trail.size();k++)seen_arr[litvar(trail[k])]=0;
        static vector<int>lvl_tmp;lvl_tmp.clear();
        for(int l:learnt_lits){int lv=dlev_arr[litvar(l)];if(lv>0)lvl_tmp.push_back(lv);}
        sort(lvl_tmp.begin(),lvl_tmp.end());
        int lbd=unique(lvl_tmp.begin(),lvl_tmp.end())-lvl_tmp.begin();learnt_lbd=max(lbd,1);
        if(learnt_lits.size()==1){bt_level=0;}
        else{int mx=-1,mi=1;for(int i=1;i<(int)learnt_lits.size();i++)
            if(dlev_arr[litvar(learnt_lits[i])]>mx){mx=dlev_arr[litvar(learnt_lits[i])];mi=i;}
            swap(learnt_lits[1],learnt_lits[mi]);bt_level=dlev_arr[litvar(learnt_lits[1])];}
    }
    
    void backtrack(int level){
        int target=level<(int)trail_lim.size()?trail_lim[level]:(int)trail.size();
        for(int i=trail.size()-1;i>=target;i--){
            int v=litvar(trail[i]);
            phase_arr[v]=val[v]>0?0:1;val[v]=0;dlev_arr[v]=-1;reason_arr[v]=Reason_Undef;hinsert(v);
        }
        trail.resize(target);trail_lim.resize(level);qhead=trail.size();
    }
    
    int decide(){
        while(heap.size()>0){
            int v=hpop();if(val[v]==0){
                n_decisions++;trail_lim.push_back(trail.size());
                enqueue(mklit(v,phase_arr[v]!=0),Reason_Undef);return 1;
            }
        }
        return 0;
    }
    
    double lbd_ema_fast=0,lbd_ema_slow=0;
    int luby_idx=1;
    static int luby(int i){int k=1;while(k<i+1)k<<=1;if(k==i+1)return k>>1;return luby(i-(k>>1)+1);}
    int next_luby_restart=0;
    bool should_restart(){
        if(n_conflicts<100)return false;
        // Luby restarts with base unit 128
        if(next_luby_restart==0)next_luby_restart=128;
        if(n_conflicts-last_restart_confl>=(long long)next_luby_restart){
            next_luby_restart=128*luby(luby_idx++);
            return true;
        }
        // Also glucose-style: agressive EMA
        if(n_conflicts-last_restart_confl<32)return false;
        return lbd_ema_fast>lbd_ema_slow*1.2;
    }
    
    bool is_locked(int cr){
        Clause&c=ca[cr];int v=litvar(c[0]);
        return val[v]!=0&&litval(c[0])>0&&reason_arr[v]==cr;
    }
    
    void reduce_db(){
        sort(learnt_crefs.begin(),learnt_crefs.end(),[this](int a,int b){return ca[a].lbd>ca[b].lbd;});
        int keep=learnt_crefs.size()/2;int j=0;
        for(int i=0;i<(int)learnt_crefs.size();i++){
            int cr=learnt_crefs[i];Clause&c=ca[cr];
            if(is_locked(cr)||c.lbd<=2){learnt_crefs[j++]=cr;} // LBD≤2: permanent (core)
            else if(i>=keep||c.lbd<=5){learnt_crefs[j++]=cr;} // LBD 3-5: keep half
            else{c.mark_deleted();n_reduced++;}
        }
        learnt_crefs.resize(j);
    }
    
    void init_activity(){
        int nlits=2*(nvars+1);
        for(int l=2;l<nlits;l++)act[litvar(l)]+=(double)bins[l].size();
        for(int l=2;l<nlits;l++)act[litvar(l)]+=(double)watches[l].size()*0.5;
        bool flat_eo_span = eo_max_var>0 && eo_max_var*4<nvars;
        if(flat_eo_span)for(int v=1;v<=nvars;v++)if(eo_var[v])act[v]+=100.0;
        heap.clear();memset(hpos_arr,0xff,(nvars+1)*sizeof(int));
        for(int v=1;v<=nvars;v++)if(val[v]==0)hinsert(v);
    }
    
    // Proof helpers
    void open_proof(const char*fn){
        if(!proof_enabled)return;
        proof_fp=fopen(fn,"w");
        if(proof_fp){
            setvbuf(proof_fp,nullptr,_IOFBF,1<<20);
            fprintf(proof_fp,"pseudo-Boolean proof version 2.0\n");
            fprintf(proof_fp,"f %d\n",orig_clause_count);
            proof_id=orig_clause_count;
        }
    }
    void log_learnt_proof(const vector<int>&lits){
        if(!proof_fp)return;proof_id++;
        fputs("u ",proof_fp);
        for(int l:lits)fprintf(proof_fp,"1 %sx%d ",litsgn(l)?"~":"",litvar(l));
        fputs(">= 1 ;\n",proof_fp);
    }
    void log_probe_unit(int lit){
        if(!proof_fp)return;proof_id++;
        fprintf(proof_fp,"u 1 %sx%d >= 1 ;\n",litsgn(lit)?"~":"",litvar(lit));
    }
    void close_proof(){
        if(proof_fp){
            proof_id++;
            fprintf(proof_fp,"u >= 1 ;\n");
            fprintf(proof_fp,"output NONE\n");
            fprintf(proof_fp,"conclusion UNSAT : %d\n",proof_id);
            fprintf(proof_fp,"end pseudo-Boolean proof\n");
            fclose(proof_fp);proof_fp=nullptr;
        }
    }
    
    // Failed Literal Probing with hyper-binary learning
    int probe(double time_budget){
        int probed=0,failed=0,new_bins=0;
        double probe_start=elapsed();
        vector<pair<int,int>>cands;
        for(int v=1;v<=nvars;v++){
            if(val[v]!=0)continue;
            int deg=bins[mklit(v,false)].size()+bins[mklit(v,true)].size();
            cands.push_back({-deg,v});
        }
        sort(cands.begin(),cands.end());

        // Temporary storage for implied literals per probe
        vector<int8_t>stamp(2*(nvars+1),0); // 1=pos implied, 2=neg implied

        for(auto&[_,v]:cands){
            if(val[v]!=0)continue;
            if(elapsed()-probe_start>time_budget)break;
            probed++;
            int base_trail=trail.size();

            // Probe positive
            trail_lim.push_back(trail.size());
            int poslit=mklit(v,false);
            enqueue(poslit,Reason_Undef);
            int cp=propagate();

            // Collect implied literals
            vector<int>pos_implied;
            if(cp<0&&cp!=-2){
                for(int i=base_trail+1;i<(int)trail.size();i++){
                    pos_implied.push_back(trail[i]);
                }
            }
            backtrack(0);

            if(cp>=0||cp==-2){
                // Failed literal: v must be negative
                failed++;
                int forced=mklit(v,true);log_probe_unit(forced);
                enqueue(forced,Reason_Undef);
                int c2=propagate();if(c2>=0||c2==-2)return-1;
                continue;
            }

            // Probe negative
            trail_lim.push_back(trail.size());
            int neglit=mklit(v,true);
            enqueue(neglit,Reason_Undef);
            int cn=propagate();

            if(cn>=0||cn==-2){
                // Failed literal: v must be positive
                backtrack(0);
                failed++;
                int forced=mklit(v,false);log_probe_unit(forced);
                enqueue(forced,Reason_Undef);
                int c2=propagate();if(c2>=0||c2==-2)return-1;
                continue;
            }

            // Collect negative implied literals and find common implications
            // Common: implied by both v and ¬v → unit
            // Also learn hyper-binary: v → m becomes (¬v ∨ m), ¬v → m becomes (v ∨ m)
            for(int i=base_trail+1;i<(int)trail.size();i++){
                stamp[trail[i]]=2; // neg-implied
            }

            // Check for common implications
            vector<int>units_to_add;
            for(int m:pos_implied){
                if(stamp[m]==2){
                    // m is implied by both polarities → unit
                    units_to_add.push_back(m);
                }
            }

            // Learn hyper-binary implications (limit to avoid blowup)
            if(new_bins<10000){
                for(int m:pos_implied){
                    if(stamp[m]!=2&&litvar(m)!=v){
                        // v=TRUE → m, so clause (¬v ∨ m) = (neglit ∨ m)
                        // Only add if not already present
                        bool exists=false;
                        for(int x:bins[m]){
                            if(x==neglit){exists=true;break;}
                        }
                        if(!exists){
                            bins[neglit].push_back(m);
                            bins[m].push_back(neglit);
                            // Log to proof (RUP derivable)
                            if(proof_fp){
                                proof_id++;
                                fprintf(proof_fp,"u 1 %sx%d 1 %sx%d >= 1 ;\n",
                                        litsgn(neglit)?"~":"",litvar(neglit),
                                        litsgn(m)?"~":"",litvar(m));
                            }
                            new_bins++;
                        }
                    }
                }
                // Also from negative probe
                for(int i=base_trail+1;i<(int)trail.size();i++){
                    int m=trail[i];
                    if(litvar(m)!=v){
                        // ¬v=TRUE → m, so clause (v ∨ m) = (poslit ∨ m)
                        bool exists=false;
                        for(int x:bins[m]){
                            if(x==poslit){exists=true;break;}
                        }
                        if(!exists){
                            bins[poslit].push_back(m);
                            bins[m].push_back(poslit);
                            if(proof_fp){
                                proof_id++;
                                fprintf(proof_fp,"u 1 %sx%d 1 %sx%d >= 1 ;\n",
                                        litsgn(poslit)?"~":"",litvar(poslit),
                                        litsgn(m)?"~":"",litvar(m));
                            }
                            new_bins++;
                        }
                    }
                }
            }

            // Clear stamps
            for(int i=base_trail+1;i<(int)trail.size();i++)stamp[trail[i]]=0;
            backtrack(0);

            // Add common-implied units
            for(int m:units_to_add){
                if(val[litvar(m)]==0){
                    failed++; // count as a "found"
                    // Before logging the unit, ensure supporting binaries are in proof
                    // m is implied by both v and ¬v, so clauses (¬v ∨ m) and (v ∨ m) are needed
                    if(proof_fp){
                        proof_id++;
                        fprintf(proof_fp,"u 1 %sx%d 1 %sx%d >= 1 ;\n",
                                litsgn(neglit)?"~":"",litvar(neglit),
                                litsgn(m)?"~":"",litvar(m));
                        proof_id++;
                        fprintf(proof_fp,"u 1 %sx%d 1 %sx%d >= 1 ;\n",
                                litsgn(poslit)?"~":"",litvar(poslit),
                                litsgn(m)?"~":"",litvar(m));
                    }
                    log_probe_unit(m);
                    enqueue(m,Reason_Undef);
                    int c2=propagate();if(c2>=0||c2==-2)return-1;
                }
            }
        }
        fprintf(stderr,"c probe: probed=%d failed=%d new_bins=%d fixed=%d/%d t=%.1fs\n",
                probed,failed,new_bins,(int)trail.size(),nvars,elapsed()-probe_start);
        return failed+new_bins; // return nonzero if progress was made
    }
    
    // ====== BVE (Bounded Variable Elimination) ======
    // Builds an explicit clause database from the current (post-probe) state,
    // eliminates variables, then rebuilds the watched-literal structure for CDCL.
    
    void bve_preprocess(){
        double bve_start=elapsed();
        if(bve_start>TIME_LIMIT-300)return; // need at least 300s for CDCL
        
        // Step 1: Build explicit clause set from current state
        // Collect all non-satisfied, non-trivial clauses
        bve_pos.resize(nvars+1);
        bve_neg.resize(nvars+1);
        
        // Add all binary clauses (from bins[])
        // bins[l] contains implied literals: l false => imp true
        // So clause is (l, imp), i.e. (NOT l_false, imp)
        // Wait - bins[np] = imp means clause (-np, imp) = (p, imp)? No.
        // bins stores: bins[l] = list of literals implied when l is false
        // This means clause is (l, imp_lit) for each imp_lit in bins[l]
        // Actually: the clause is (l, imp) where if l is false, imp is true
        // So the clause is {l, imp}
        
        // For binary clauses, we stored bins[a].push_back(b) AND bins[b].push_back(a)
        // so each binary clause {a,b} appears twice. We need to deduplicate.
        
        vector<bool>var_eliminated(nvars+1, false);
        vector<bool>var_fixed(nvars+1, false);
        
        for(int v=1;v<=nvars;v++){
            if(val[v]!=0)var_fixed[v]=true;
        }
        
        // Collect unique binary clauses
        for(int l=2;l<2*(nvars+1);l++){
            int v=litvar(l);
            if(var_fixed[v])continue;
            for(int imp:bins[l]){
                int iv=litvar(imp);
                if(var_fixed[iv])continue;
                if(l<imp){ // deduplicate: only add when l < imp
                    int ci=bve_clauses.size();
                    bve_clauses.push_back({{l,imp},0,true});
                    if(litsgn(l))bve_neg[v].push_back(ci);else bve_pos[v].push_back(ci);
                    if(litsgn(imp))bve_neg[iv].push_back(ci);else bve_pos[iv].push_back(ci);
                }
            }
        }
        
        // Collect longer clauses (original + learned)
        auto add_clause=[&](int cr){
            Clause&c=ca[cr];
            if(c.deleted())return;
            vector<int>lits;
            bool sat=false;
            for(int k=0;k<c.size();k++){
                int l=c[k];int v=litvar(l);
                if(var_fixed[v]){
                    if(litval(l)>0){sat=true;break;}
                    continue; // skip false lits
                }
                lits.push_back(l);
            }
            if(sat)return;
            if(lits.empty())return; // should not happen
            if(lits.size()==1){
                // Unit clause - propagate
                // This shouldn't happen if UP was complete, but just in case
                return;
            }
            int ci=bve_clauses.size();
            bve_clauses.push_back({lits,0,true});
            for(int l:lits){
                int v=litvar(l);
                if(litsgn(l))bve_neg[v].push_back(ci);else bve_pos[v].push_back(ci);
            }
        };
        
        for(int cr:orig_crefs){
            Clause&c=ca[cr];
            if(c.size()<=2)continue; // binaries already handled
            add_clause(cr);
        }
        for(int cr:learnt_crefs){
            Clause&c=ca[cr];
            if(c.size()<=2)continue;
            add_clause(cr);
        }
        
        int init_clauses=0;
        for(auto&bc:bve_clauses)if(bc.alive)init_clauses++;
        int init_vars=0;
        for(int v=1;v<=nvars;v++)if(!var_fixed[v])init_vars++;
        fprintf(stderr,"c BVE: init %d clauses, %d active vars\n", init_clauses, init_vars);
        
        // Step 2: BVE loop
        // Priority queue: eliminate variables with smallest cost first
        // cost = pos_count * neg_count - pos_count - neg_count
        // Only eliminate if cost <= 0 (doesn't increase clause count) or cost is small
        
        vector<pair<int,int>>elim_order; // (cost, var)
        auto compute_cost=[&](int v) -> int{
            if(var_fixed[v]||var_eliminated[v])return 999999;
            int p=0,n=0;
            for(int ci:bve_pos[v])if(bve_clauses[ci].alive)p++;
            for(int ci:bve_neg[v])if(bve_clauses[ci].alive)n++;
            if(p==0||n==0)return-p-n; // pure literal
            return p*n-p-n;
        };
        
        for(int v=1;v<=nvars;v++){
            if(var_fixed[v])continue;
            int cost=compute_cost(v);
            if(cost<=10)elim_order.push_back({cost,v});
        }
        sort(elim_order.begin(),elim_order.end());
        
        int rounds=0;
        while(!elim_order.empty()&&elapsed()<bve_start+min(60.0,TIME_LIMIT-300.0)){
            rounds++;
            vector<pair<int,int>>next_round;
            
            for(auto&[ecost,v]:elim_order){
                if(var_fixed[v]||var_eliminated[v])continue;
                if(elapsed()>bve_start+min(60.0,TIME_LIMIT-300.0))break;
                
                // Recompute actual occurrence counts
                vector<int>pos_cls,neg_cls;
                for(int ci:bve_pos[v])if(bve_clauses[ci].alive)pos_cls.push_back(ci);
                for(int ci:bve_neg[v])if(bve_clauses[ci].alive)neg_cls.push_back(ci);
                int p=pos_cls.size(),n=neg_cls.size();
                
                if(p==0&&n==0){var_eliminated[v]=true;bve_eliminated++;continue;}
                
                // Pure literal
                if(p==0||n==0){
                    // Save for model reconstruction
                    ElimEntry ee;ee.var=v;
                    for(int ci:pos_cls){ee.saved_clauses.push_back(bve_clauses[ci].lits);bve_clauses[ci].alive=false;}
                    for(int ci:neg_cls){ee.saved_clauses.push_back(bve_clauses[ci].lits);bve_clauses[ci].alive=false;}
                    elim_stack.push_back(move(ee));
                    var_eliminated[v]=true;bve_eliminated++;
                    continue;
                }
                
                // Check cost bound
                int cost=p*n-p-n;
                if(cost>10)continue; // too expensive
                
                // Compute all resolvents
                vector<vector<int>>resolvents;
                bool too_many=false;
                for(int pi:pos_cls){
                    if(too_many)break;
                    for(int ni:neg_cls){
                        // Resolve pos_cls[pi] and neg_cls[ni] on v
                        auto&plits=bve_clauses[pi].lits;
                        auto&nlits=bve_clauses[ni].lits;
                        vector<int>res;
                        bool taut=false;
                        for(int l:plits){if(litvar(l)==v)continue;res.push_back(l);}
                        for(int l:nlits){
                            if(litvar(l)==v)continue;
                            // Check for tautology
                            bool dup=false;
                            for(int rl:res){
                                if(rl==l){dup=true;break;}
                                if(rl==litneg(l)){taut=true;break;}
                            }
                            if(taut)break;
                            if(!dup)res.push_back(l);
                        }
                        if(taut)continue;
                        if(res.empty()){
                            // Empty resolvent = UNSAT
                            // This shouldn't happen in BVE normally
                            continue;
                        }
                        if(res.size()>20){
                            // Too large resolvent, skip this elimination
                            too_many=true;break;
                        }
                        resolvents.push_back(move(res));
                    }
                }
                if(too_many)continue;
                
                // Actually eliminate: save clauses, remove originals, add resolvents
                ElimEntry ee;ee.var=v;
                for(int ci:pos_cls){ee.saved_clauses.push_back(bve_clauses[ci].lits);bve_clauses[ci].alive=false;}
                for(int ci:neg_cls){ee.saved_clauses.push_back(bve_clauses[ci].lits);bve_clauses[ci].alive=false;}
                elim_stack.push_back(move(ee));
                var_eliminated[v]=true;bve_eliminated++;
                
                // Add resolvents (also log to proof as RUP)
                for(auto&res:resolvents){
                    // Log resolvent to proof (RUP-derivable since it's a resolution result)
                    if(proof_fp){
                        proof_id++;
                        fputs("u ",proof_fp);
                        for(int l:res)fprintf(proof_fp,"1 %sx%d ",litsgn(l)?"~":"",litvar(l));
                        fputs(">= 1 ;\n",proof_fp);
                    }
                    int ci=bve_clauses.size();
                    bve_clauses.push_back({res,0,true});
                    for(int l:res){
                        int lv=litvar(l);
                        if(litsgn(l))bve_neg[lv].push_back(ci);else bve_pos[lv].push_back(ci);
                    }
                }
            }
            
            // Check if we should do another round
            if(rounds>=5)break;
            elim_order.clear();
            for(int v=1;v<=nvars;v++){
                if(var_fixed[v]||var_eliminated[v])continue;
                int cost=compute_cost(v);
                if(cost<=10)elim_order.push_back({cost,v});
            }
            sort(elim_order.begin(),elim_order.end());
            if(elim_order.empty())break;
        }
        
        // Count remaining
        int rem_clauses=0,rem_vars=0;
        for(auto&bc:bve_clauses)if(bc.alive)rem_clauses++;
        for(int v=1;v<=nvars;v++)if(!var_fixed[v]&&!var_eliminated[v])rem_vars++;
        fprintf(stderr,"c BVE: eliminated %d vars, remaining %d vars, %d clauses, %d rounds, t=%.1fs\n",
                bve_eliminated,rem_vars,rem_clauses,rounds,elapsed()-bve_start);
        
        // Step 3: Rebuild solver state for CDCL
        // Clear old watch/bin structures
        for(int l=0;l<(int)watches.size();l++)watches[l].clear();
        for(int l=0;l<(int)bins.size();l++)bins[l].clear();
        learnt_crefs.clear();
        // Don't clear orig_crefs - we need them for verification
        
        // Build new clause structure from BVE result
        for(auto&bc:bve_clauses){
            if(!bc.alive)continue;
            // Filter out assigned lits
            vector<int>lits;
            bool sat=false;
            for(int l:bc.lits){
                int v=litvar(l);
                if(var_fixed[v]){
                    if(litval(l)>0){sat=true;break;}
                    continue;
                }
                if(var_eliminated[v])continue; // shouldn't happen for alive clauses
                lits.push_back(l);
            }
            if(sat)continue;
            if(lits.empty())continue;
            if(lits.size()==1){
                int v=litvar(lits[0]);
                if(val[v]==0){
                    enqueue(lits[0],Reason_Undef);
                }
                continue;
            }
            if(lits.size()==2){
                bins[lits[0]].push_back(lits[1]);
                bins[lits[1]].push_back(lits[0]);
            }else{
                int cr=ca.alloc(lits.size(),false,0);
                for(int k=0;k<(int)lits.size();k++)ca[cr][k]=lits[k];
                attach_clause(cr);
                learnt_crefs.push_back(cr); // treat as learnt for reduction
            }
        }
        
        // Mark eliminated variables as assigned so CDCL won't decide on them
        // Use a dummy value; actual values will be set during reconstruction
        for(int v=1;v<=nvars;v++){
            if(var_eliminated[v]&&val[v]==0){
                val[v]=-1; // temporary: will be overridden in reconstruction
                dlev_arr[v]=0; // pretend level 0
            }
        }

        // Free BVE data (keep elim_stack for model reconstruction)
        bve_clauses.clear();bve_clauses.shrink_to_fit();
        bve_pos.clear();bve_pos.shrink_to_fit();
        bve_neg.clear();bve_neg.shrink_to_fit();

        // Propagate any new units from BVE
        int confl=propagate();
        if(confl>=0||confl==-2){
            fprintf(stderr,"c BVE: UNSAT from propagation after rebuild\n");
        }
    }
    
    // For SAT: reconstruct eliminated variables using standard BVE reconstruction
    void reconstruct_model(){
        int forced_true=0,forced_false=0;
        // Process elim stack in reverse (last eliminated first)
        for(int i=elim_stack.size()-1;i>=0;i--){
            auto&ee=elim_stack[i];
            int v=ee.var;
            // Default: FALSE (satisfies all ¬v clauses automatically)
            val[v]=-1;
            // Check each saved clause: if it contains positive v and all other lits are false,
            // we MUST set v = TRUE
            for(auto&cls:ee.saved_clauses){
                bool has_pos_v=false;
                for(int l:cls)if(litvar(l)==v&&!litsgn(l)){has_pos_v=true;break;}
                if(!has_pos_v)continue;
                bool other_sat=false;
                for(int l:cls){
                    if(litvar(l)==v)continue;
                    int8_t vv=val[litvar(l)];
                    if(vv!=0){
                        bool lt=litsgn(l)?(vv<0):(vv>0);
                        if(lt){other_sat=true;break;}
                    }
                    // If vv==0, variable is unset - treat as potentially false
                }
                if(!other_sat){val[v]=1;forced_true++;break;}
            }
            if(val[v]==-1)forced_false++;
        }
        fprintf(stderr,"c reconstruct: %d true, %d false out of %d eliminated\n",
                forced_true,forced_false,(int)elim_stack.size());
    }
    
    bool parse(const char*fn){
        FILE*f=fopen(fn,"r");if(!f)return false;
        fseek(f,0,SEEK_END);long fsz=ftell(f);fseek(f,0,SEEK_SET);
        char*data=(char*)malloc(fsz+1);size_t rd=fread(data,1,fsz,f);fclose(f);data[rd]=0;
        int nv=0,nc=0;vector<int>lits;char*p=data;
        while(*p){
            if(*p=='c'||*p=='%'){while(*p&&*p!='\n')p++;if(*p)p++;continue;}
            if(*p=='p'){sscanf(p,"p cnf %d %d",&nv,&nc);init(nv);while(*p&&*p!='\n')p++;if(*p)p++;continue;}
            while(*p==' '||*p=='\t'||*p=='\n'||*p=='\r')p++;if(!*p)break;
            if(*p<'0'&&*p!='-'){p++;continue;}int neg=0;if(*p=='-'){neg=1;p++;}
            if(*p<'0'||*p>'9'){p++;continue;}int x=0;while(*p>='0'&&*p<='9')x=x*10+(*p++-'0');
            if(x==0){if(!lits.empty()){
                if(lits.size()==6){
                    bool all_pos=true;
                    for(int l:lits)if(litsgn(l)){all_pos=false;break;}
                    if(all_pos)for(int l:lits){
                        int v=litvar(l);
                        eo_var[v]=1;
                        if(v>eo_max_var)eo_max_var=v;
                    }
                }
                if(lits.size()==1){
                    int cr=ca.alloc(1,false,0);ca[cr][0]=lits[0];orig_crefs.push_back(cr);
                    int v2=litvar(lits[0]);if(val[v2]==0)enqueue(lits[0],cr);
                    else if(litval(lits[0])<0){free(data);orig_clause_count=orig_crefs.size();return true;}
                }else if(lits.size()==2){
                    bins[lits[0]].push_back(lits[1]);bins[lits[1]].push_back(lits[0]);
                    int cr=ca.alloc(2,false,0);ca[cr][0]=lits[0];ca[cr][1]=lits[1];orig_crefs.push_back(cr);
                }else{
                    int cr=ca.alloc(lits.size(),false,0);for(int k=0;k<(int)lits.size();k++)ca[cr][k]=lits[k];
                    orig_crefs.push_back(cr);attach_clause(cr);
                }lits.clear();}}
            else{lits.push_back(neg?mklit(x,true):mklit(x,false));}}
        free(data);orig_clause_count=orig_crefs.size();return true;
    }
    
    bool verify(){
        int fail=0;
        for(int ci=0;ci<(int)orig_crefs.size();ci++){
            int cr=orig_crefs[ci];Clause&c=ca[cr];
            bool sat=false;
            for(int k=0;k<c.size();k++)if(litval(c[k])>0){sat=true;break;}
            if(!sat){
                fail++;
                if(fail<=5){
                    fprintf(stderr,"c FAIL clause %d:",ci);
                    for(int k=0;k<c.size();k++){
                        int l=c[k];int v=litvar(l);
                        fprintf(stderr," %s%d(val=%d)",litsgn(l)?"-":"",v,val[v]);
                    }
                    fprintf(stderr,"\n");
                }
            }
        }
        if(fail>0)fprintf(stderr,"c VERIFY: %d failures\n",fail);
        return fail==0;
    }
    
    int cdcl_run(long long max_conflicts){
        int reduce_trigger=max((int)learnt_crefs.size()+2000,2000);
        long long start_conflicts=n_conflicts;
        int time_check=0;
        while(true){
            if(++time_check>=4096){time_check=0;
                if(elapsed()>TIME_LIMIT){if(proof_fp){fclose(proof_fp);proof_fp=nullptr;}return 0;}}
            int confl=propagate();
            if(confl>=0||confl==-2){
                n_conflicts++;
                if(dl()==0){
                    // Analyze the level-0 conflict to derive the contradiction in the proof
                    // At level 0, the learned clause should be empty (or a unit that contradicts)
                    if(proof_fp){
                        // Derive empty clause: collect all falsified literals at level 0
                        vector<int>conflict_lits;
                        if(confl==-2){conflict_lits.push_back(conf_np);conflict_lits.push_back(conf_imp);}
                        else{Clause&c=ca[confl];for(int k=0;k<c.size();k++)conflict_lits.push_back(c[k]);}
                        // All these literals are false at level 0, so the clause is a contradiction
                        // Log the empty clause derivation
                        proof_id++;
                        fprintf(proof_fp,"u ");
                        for(int l:conflict_lits)fprintf(proof_fp,"1 %sx%d ",litsgn(l)?"~":"",litvar(l));
                        fprintf(proof_fp,">= 1 ;\n");
                    }
                    close_proof();return 20;
                }
                analyze(confl);backtrack(bt_level);log_learnt_proof(learnt_lits);
                if(learnt_lits.size()==1){enqueue(learnt_lits[0],Reason_Undef);}
                else if(learnt_lits.size()==2){
                    bins[learnt_lits[0]].push_back(learnt_lits[1]);
                    bins[learnt_lits[1]].push_back(learnt_lits[0]);
                    enqueue(learnt_lits[0],make_bin_reason(learnt_lits[1]));
                }else{
                    int cr=ca.alloc(learnt_lits.size(),true,learnt_lbd);
                    for(int k=0;k<(int)learnt_lits.size();k++)ca[cr][k]=learnt_lits[k];
                    learnt_crefs.push_back(cr);attach_clause(cr);enqueue(learnt_lits[0],cr);
                }
                n_learnt++;
                lbd_ema_fast=lbd_ema_fast*0.95+learnt_lbd*0.05;
                lbd_ema_slow=lbd_ema_slow*0.999+learnt_lbd*0.001;
                var_bump/=0.95;
                if(dl()>0&&should_restart()){backtrack(0);n_restarts++;last_restart_confl=n_conflicts;}
                if((int)learnt_crefs.size()>=reduce_trigger){if(dl()>0)backtrack(0);reduce_db();reduce_trigger=learnt_crefs.size()+500;}
                if(max_conflicts>0&&n_conflicts-start_conflicts>=max_conflicts)return-1;
            }else{
                if(!decide()){if(proof_fp){fclose(proof_fp);proof_fp=nullptr;}return 10;}
            }
        }
    }
    
    int solve(const char*proof_fn){
        open_proof(proof_fn);
        int confl=propagate();
        if(confl>=0||confl==-2){close_proof();return 20;}
        
        int fixed_by_up=trail.size();
        fprintf(stderr,"c after UP: %d/%d fixed (%.0f%%)\n",fixed_by_up,nvars,(double)fixed_by_up/nvars*100);
        
        init_activity();
        
        // Phase 1: Quick CDCL (2000 conflicts)
        int res=cdcl_run(2000);
        if(res==10||res==20||res==0)return res;
        fprintf(stderr,"c phase1 CDCL: %lld confl in %.1fs\n",n_conflicts,elapsed());
        
        // Phase 2: Probing (limited time - save time for CDCL)
        if(dl()>0)backtrack(0);
        for(int round=0;round<2;round++){
            double time_budget=10.0;
            if(elapsed()+time_budget>TIME_LIMIT-300)break;
            int found=probe(time_budget);
            if(found<0){close_proof();return 20;}
            if(found==0)break;
            fprintf(stderr,"c after probe round %d: %d/%d fixed\n",round,(int)trail.size(),nvars);
            // Only do second round if first found many failed literals
            if(round==0 && found < 500) break;
        }
        
        // Phase 3: BVE
        fprintf(stderr,"c pre-BVE: %d/%d fixed, t=%.1fs\n",(int)trail.size(),nvars,elapsed());
        bve_preprocess();
        
        // Re-propagate after BVE
        if(dl()>0)backtrack(0);
        confl=propagate();
        if(confl>=0||confl==-2){close_proof();return 20;}
        
        fprintf(stderr,"c post-BVE: %d/%d fixed (elim %d), t=%.1fs\n",
                (int)trail.size(),nvars,bve_eliminated,elapsed());
        
        // Phase 3b: Post-BVE probing (BVE resolvents may create new failed literals)
        {
            int found=probe(5.0);
            if(found<0){close_proof();return 20;}
            if(found>0){
                fprintf(stderr,"c post-BVE probe: fixed %d/%d, t=%.1fs\n",(int)trail.size(),nvars,elapsed());
            }
        }

        // Re-init activity
        memset(act,0,(nvars+1)*sizeof(double));
        heap.clear();memset(hpos_arr,0xff,(nvars+1)*sizeof(int));
        init_activity();

        // Phase 4: Full CDCL with inprocessing
        // Count active vars and clauses
        {int av=0;for(int v=1;v<=nvars;v++)if(val[v]==0)av++;
         int ac=0;for(auto&w:watches)ac+=w.size();ac/=2;
         int ab=0;for(auto&b:bins)ab+=b.size();ab/=2;
         fprintf(stderr,"c starting full CDCL: %d/%d fixed, %d active vars, %d long cls, %d bin cls, t=%.1fs\n",
                 (int)trail.size(),nvars,av,ac,ab,elapsed());}

        // CDCL with inprocessing: alternate CDCL and probing
        long long inprocess_interval=50000;
        for(int inprocess_round=0;;inprocess_round++){
            res=cdcl_run(inprocess_interval);
            if(res==10||res==20||res==0)return res;
            // Inprocess: re-probe with new binary implications
            if(dl()>0)backtrack(0);
            int before=trail.size();
            int found=probe(3.0); // quick probe
            if(found<0){close_proof();return 20;}
            int after=trail.size();
            if(after>before){
                fprintf(stderr,"c inprocess %d: probe fixed %d more (%d/%d total), t=%.1fs\n",
                        inprocess_round,after-before,after,nvars,elapsed());
            }
            if(elapsed()>TIME_LIMIT-30)break;
            // Increase interval each round
            inprocess_interval=min(inprocess_interval*2,(long long)200000);
        }
        if(proof_fp){fclose(proof_fp);proof_fp=nullptr;}
        return 0;
    }
    
    void print_model(){
        printf("s SATISFIABLE\nv ");
        for(int v=1;v<=nvars;v++){printf("%d ",val[v]>0?v:-v);if(v%20==0&&v<nvars)printf("\nv ");}
        printf("0\n");
    }
};

int main(int argc,char**argv){
    if(argc!=3){fprintf(stderr,"usage: solver <formula.cnf> <out.pbp>\n");return 1;}
    Solver S;if(!S.parse(argv[1])){printf("s UNKNOWN\n");return 0;}
    if(getenv("DIAG_NO_PROOF"))S.proof_enabled=false;
    fprintf(stderr,"c %d vars, %d clauses\n",S.nvars,S.orig_clause_count);
    int res=S.solve(argv[2]);
    fprintf(stderr,"c confl=%lld dec=%lld prop=%lld rst=%d learnt=%d reduced=%d t=%.2fs\n",
            S.n_conflicts,S.n_decisions,S.n_props,S.n_restarts,S.n_learnt,S.n_reduced,elapsed());
    if(res==10){
        S.reconstruct_model();
        // Set any remaining unset variables
        for(int v=1;v<=S.nvars;v++)if(S.val[v]==0)S.val[v]=1;
        if(!S.verify()){fprintf(stderr,"c Verify failed!\n");printf("s UNKNOWN\n");return 0;}
        S.print_model();return 10;
    }
    if(res==20&&S.proof_enabled){printf("s UNSATISFIABLE\n");return 20;}
    printf("s UNKNOWN\n");return 0;
}
