// Random k-SAT Specialized Solver v8
// Major improvements over v7:
// - Heap-based VSIDS for O(log n) variable selection
// - Clause minimization with separate proof writing (unminimized clause to proof)
// - LBD-based clause deletion (protect glue clauses LBD<=2)
// - Phase saving
// - Ratio-based time budget
// - Capped reduceDBThreshold

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <random>
#include <numeric>
#include <chrono>

using namespace std;
using Clock = chrono::steady_clock;

static auto g_start = Clock::now();
static double g_timeout = 585.0;
static inline double elapsed() {
    return chrono::duration<double>(Clock::now() - g_start).count();
}

struct CNF {
    int nVars, nClauses;
    vector<vector<int>> clauses;
    vector<vector<int>> posOcc, negOcc;
    int maxK;
    void build() {
        posOcc.assign(nVars + 1, {});
        negOcc.assign(nVars + 1, {});
        maxK = 0;
        for (int i = 0; i < nClauses; i++) {
            for (int lit : clauses[i]) {
                int v = abs(lit);
                if (lit > 0) posOcc[v].push_back(i);
                else negOcc[v].push_back(i);
            }
            maxK = max(maxK, (int)clauses[i].size());
        }
    }
};

bool parseDIMACS(const char* filename, CNF& cnf) {
    FILE* f = fopen(filename, "r");
    if (!f) return false;
    fseek(f, 0, SEEK_END);
    long sz = ftell(f);
    fseek(f, 0, SEEK_SET);
    vector<char> buf(sz + 1);
    fread(buf.data(), 1, sz, f);
    buf[sz] = 0;
    fclose(f);
    cnf.nVars = 0; cnf.nClauses = 0;
    char* ptr = buf.data();
    while (*ptr) {
        if (*ptr == 'c') { while (*ptr && *ptr != '\n') ptr++; if (*ptr) ptr++; continue; }
        if (*ptr == 'p') { sscanf(ptr, "p cnf %d %d", &cnf.nVars, &cnf.nClauses); while (*ptr && *ptr != '\n') ptr++; if (*ptr) ptr++; continue; }
        vector<int> clause;
        while (*ptr) {
            while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r') ptr++;
            if (!*ptr) break;
            if (*ptr == 'c') { while (*ptr && *ptr != '\n') ptr++; if (*ptr) ptr++; continue; }
            char* end;
            long lit = strtol(ptr, &end, 10);
            if (end == ptr) { ptr++; continue; }
            ptr = end;
            if (lit == 0) { if (!clause.empty()) cnf.clauses.push_back(std::move(clause)); break; }
            clause.push_back((int)lit);
        }
    }
    cnf.nClauses = cnf.clauses.size();
    cnf.build();
    return true;
}

bool verify(const CNF& cnf, const int* asgn) {
    for (const auto& cl : cnf.clauses) {
        bool sat = false;
        for (int lit : cl) { int v=abs(lit); if ((lit>0&&asgn[v]==1)||(lit<0&&asgn[v]==0)){sat=true;break;} }
        if (!sat) return false;
    }
    return true;
}

// ============== probSAT ==============
struct ProbSAT {
    const CNF& cnf;
    int n, m;
    vector<int> asgn;
    vector<int> numTrue;
    vector<int> unsatList;
    vector<int> unsatPos;
    int nUnsat;
    mt19937_64 rng;
    vector<double> brkWeight;
    double cb;
    
    ProbSAT(const CNF& c, uint64_t seed) : cnf(c), rng(seed) {
        n = cnf.nVars; m = cnf.nClauses;
        asgn.resize(n + 1);
        numTrue.resize(m);
        unsatPos.resize(m);
        int k = cnf.maxK;
        if (k <= 3) cb = 2.06; else if (k==4) cb = 2.85; else if (k==5) cb = 3.7;
        else if (k==6) cb = 5.1; else if (k==7) cb = 7.0; else if (k==8) cb = 9.5;
        else if (k==9) cb = 13.0; else cb = 18.0;
        int maxB = min(m, 2000);
        brkWeight.resize(maxB + 1);
        for (int i = 0; i <= maxB; i++) brkWeight[i] = pow(cb, -(double)i);
    }
    void init() {
        for (int v = 1; v <= n; v++) asgn[v] = rng() & 1;
        nUnsat = 0; unsatList.clear();
        for (int ci = 0; ci < m; ci++) {
            numTrue[ci] = 0;
            for (int lit : cnf.clauses[ci]) {
                int v = abs(lit);
                if ((lit>0) ? (asgn[v]==1) : (asgn[v]==0)) numTrue[ci]++;
            }
            if (numTrue[ci] == 0) { unsatPos[ci] = nUnsat; unsatList.push_back(ci); nUnsat++; }
            else unsatPos[ci] = -1;
        }
    }
    void flip(int v) {
        asgn[v] ^= 1;
        const auto& mt = (asgn[v]==1) ? cnf.posOcc[v] : cnf.negOcc[v];
        const auto& mf = (asgn[v]==0) ? cnf.posOcc[v] : cnf.negOcc[v];
        for (int ci : mt) {
            if (++numTrue[ci] == 1) {
                int pos = unsatPos[ci]; nUnsat--;
                if (pos < nUnsat) { int last = unsatList[nUnsat]; unsatList[pos] = last; unsatPos[last] = pos; }
                unsatList.pop_back(); unsatPos[ci] = -1;
            }
        }
        for (int ci : mf) {
            if (--numTrue[ci] == 0) { unsatPos[ci] = nUnsat; unsatList.push_back(ci); nUnsat++; }
        }
    }
    int brk(int v) {
        int b = 0;
        const auto& crit = (asgn[v]==1) ? cnf.posOcc[v] : cnf.negOcc[v];
        for (int ci : crit) if (numTrue[ci] == 1) b++;
        return b;
    }
    bool run(double timeLimit) {
        double t0 = elapsed();
        int64_t maxFlips = std::max((int64_t)n * 3000, (int64_t)500000);
        double scores[128];
        int restarts = 0;
        while (elapsed() - t0 < timeLimit) {
            init(); restarts++;
            for (int64_t f = 0; f < maxFlips; f++) {
                if (nUnsat == 0) { fprintf(stderr, "c probSAT: solved, %d restarts, %.1fs\n", restarts, elapsed()); return true; }
                if ((f & 0x3FFF) == 0 && elapsed() - t0 > timeLimit) break;
                int ci = unsatList[rng() % nUnsat];
                const auto& cl = cnf.clauses[ci];
                int clen = cl.size();
                double total = 0;
                for (int j = 0; j < clen; j++) {
                    int b = brk(abs(cl[j]));
                    double w = (b < (int)brkWeight.size()) ? brkWeight[b] : pow(cb, -(double)b);
                    scores[j] = w; total += w;
                }
                double r = uniform_real_distribution<double>(0, total)(rng);
                int chosen = clen - 1;
                double cum = 0;
                for (int j = 0; j < clen; j++) { cum += scores[j]; if (cum >= r) { chosen = j; break; } }
                flip(abs(cl[chosen]));
            }
        }
        fprintf(stderr, "c probSAT: no solution, %d restarts, %.1fs\n", restarts, elapsed());
        return false;
    }
};

// ============== Variable activity heap ==============
struct VarHeap {
    int n;
    vector<double>& act;
    vector<int> heap;    // heap[0] is the max
    vector<int> pos;     // pos[v] = position in heap, -1 if not in heap
    
    VarHeap(int n, vector<double>& act) : n(n), act(act), pos(n+1, -1) {}
    
    void build() {
        heap.clear();
        for (int v = 1; v <= n; v++) { heap.push_back(v); pos[v] = v-1; }
        // Build max-heap
        for (int i = heap.size()/2 - 1; i >= 0; i--) siftDown(i);
    }
    
    bool empty() const { return heap.empty(); }
    
    bool inHeap(int v) const { return pos[v] >= 0; }
    
    void siftUp(int i) {
        int v = heap[i];
        while (i > 0) {
            int parent = (i-1)/2;
            if (act[heap[parent]] >= act[v]) break;
            heap[i] = heap[parent]; pos[heap[i]] = i;
            i = parent;
        }
        heap[i] = v; pos[v] = i;
    }
    
    void siftDown(int i) {
        int v = heap[i];
        int sz = heap.size();
        while (true) {
            int child = 2*i+1;
            if (child >= sz) break;
            if (child+1 < sz && act[heap[child+1]] > act[heap[child]]) child++;
            if (act[v] >= act[heap[child]]) break;
            heap[i] = heap[child]; pos[heap[i]] = i;
            i = child;
        }
        heap[i] = v; pos[v] = i;
    }
    
    void increase(int v) {
        if (pos[v] >= 0) siftUp(pos[v]);
    }
    
    void insert(int v) {
        if (pos[v] >= 0) return;
        pos[v] = heap.size();
        heap.push_back(v);
        siftUp(pos[v]);
    }
    
    int removeMax() {
        if (heap.empty()) return -1;
        int v = heap[0];
        pos[v] = -1;
        if (heap.size() > 1) {
            heap[0] = heap.back();
            pos[heap[0]] = 0;
            heap.pop_back();
            siftDown(0);
        } else {
            heap.pop_back();
        }
        return v;
    }
};

// ============== CDCL ==============
struct CDCL {
    int n, m;

    // Flat clause pool: all literals stored contiguously
    vector<int> pool;        // flat literal storage
    struct CRef { int off; short sz; short lbd; int proofId; bool isLearned, deleted; float act; };
    vector<CRef> cr;         // clause metadata
    int nOriginal;

    // val[v]: -1=undef, 0=false, 1=true
    // litval[li(l)]: 1=true, 0=false, -1=undef (cached for fast BCP)
    vector<int> val;
    vector<int8_t> litval;  // indexed by li(lit)
    vector<int> lev, rsn;
    vector<int> trail;
    vector<int> trailLim;
    int qhead;

    vector<int> phase;

    struct WE { int ci, blocker; };
    vector<vector<WE>> watches;

    vector<double> act;
    double actInc, actDecay;
    double clauseActInc;
    VarHeap varHeap;

    int64_t conflicts, propagations, decisions;
    int nLearned;
    int reduceDBThreshold;
    static const int MAX_REDUCE_THRESHOLD = 50000;

    FILE* pf;
    int proofLines;

    vector<bool> seen;

    // Access clause literals
    inline int* cdata(int ci) { return pool.data() + cr[ci].off; }
    inline const int* cdata(int ci) const { return pool.data() + cr[ci].off; }
    inline int csz(int ci) const { return cr[ci].sz; }

    CDCL(const CNF& cnf, FILE* proof) : pf(proof), act(cnf.nVars+1, 0.0), varHeap(cnf.nVars, act) {
        n = cnf.nVars; m = cnf.nClauses;
        nOriginal = m;

        // Copy clauses into flat pool
        int totalLits = 0;
        for (int ci = 0; ci < m; ci++) totalLits += cnf.clauses[ci].size();
        pool.reserve(totalLits * 3);  // room for learned clauses
        cr.reserve(m + m);

        for (int ci = 0; ci < m; ci++) {
            int off = pool.size();
            int sz = cnf.clauses[ci].size();
            for (int lit : cnf.clauses[ci]) pool.push_back(lit);
            cr.push_back({off, (short)sz, 0, 0, false, false, 0.0f});
        }

        val.assign(n+1, -1);
        litval.assign(2*(n+1)+2, -1);
        lev.assign(n+1, -1);
        rsn.assign(n+1, -2);
        phase.assign(n+1, 1);
        qhead = 0;

        watches.resize(2*(n+1)+2);
        for (int ci = 0; ci < m; ci++) {
            int* cl = cdata(ci);
            int sz = csz(ci);
            if (sz >= 2) {
                watches[li(cl[0])].push_back({ci, cl[1]});
                watches[li(cl[1])].push_back({ci, cl[0]});
            }
        }

        actInc = 1.0; actDecay = 0.95;
        for (int ci = 0; ci < m; ci++) {
            int* cl = cdata(ci);
            for (int k = 0; k < csz(ci); k++) act[abs(cl[k])] += 1.0;
        }
        varHeap.build();

        clauseActInc = 1.0;

        conflicts = propagations = decisions = 0;
        nLearned = 0;
        reduceDBThreshold = std::max(m / 2, 2000);
        if (reduceDBThreshold > MAX_REDUCE_THRESHOLD)
            reduceDBThreshold = MAX_REDUCE_THRESHOLD;
        proofLines = 0;

        seen.assign(n+1, false);
    }
    
    static inline int li(int lit) { return lit > 0 ? 2*lit : 2*(-lit)+1; }
    inline bool isT(int lit) const { return litval[li(lit)] == 1; }
    inline bool isF(int lit) const { return litval[li(lit)] == 0; }

    void assign(int lit, int level, int reason) {
        int v=abs(lit);
        val[v]=(lit>0)?1:0;
        litval[li(lit)] = 1;   // lit is true
        litval[li(-lit)] = 0;  // ~lit is false
        phase[v] = val[v];
        lev[v]=level; rsn[v]=reason; trail.push_back(lit);
    }
    
    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++]; propagations++;
            int fl = -p, fli = li(fl);
            auto& wl = watches[fli];
            int i=0, j=0, conflict=-1;
            while (i < (int)wl.size()) {
                WE& we = wl[i];
                if (cr[we.ci].deleted) { i++; continue; }
                if (isT(we.blocker)) { wl[j++]=wl[i++]; continue; }
                int ci = we.ci;
                int* cl = cdata(ci);
                int sz = csz(ci);
                if (cl[0] == fl) { int t=cl[0]; cl[0]=cl[1]; cl[1]=t; }
                if (isT(cl[0])) { we.blocker=cl[0]; wl[j++]=wl[i++]; continue; }
                bool found = false;
                for (int k=2; k<sz; k++) {
                    if (!isF(cl[k])) { int t=cl[1]; cl[1]=cl[k]; cl[k]=t; watches[li(cl[1])].push_back({ci,cl[0]}); found=true; break; }
                }
                if (found) { i++; continue; }
                if (isF(cl[0])) { wl[j++]=wl[i++]; conflict=ci; break; }
                assign(cl[0], trailLim.size(), ci);
                wl[j++]=wl[i++];
            }
            while (i<(int)wl.size()) {
                if (!cr[wl[i].ci].deleted) wl[j++]=wl[i];
                i++;
            }
            wl.resize(j);
            if (conflict!=-1) return conflict;
        }
        return -1;
    }
    
    void cancelUntil(int level) {
        int target = level < (int)trailLim.size() ? trailLim[level] : (int)trail.size();
        while ((int)trail.size() > target) {
            int lit = trail.back(); trail.pop_back();
            int v = abs(lit);
            val[v] = -1;
            litval[li(lit)] = -1;
            litval[li(-lit)] = -1;
            lev[v] = -1; rsn[v] = -2;
            varHeap.insert(v);
        }
        trailLim.resize(level);
        qhead = trail.size();
    }
    
    int computeLBD(const vector<int>& cl) {
        static vector<int> lbdStamp;
        static int lbdCounter = 0;
        if ((int)lbdStamp.size() < n+1) lbdStamp.assign(n+1, 0);
        lbdCounter++;
        int lbd = 0;
        for (int lit : cl) {
            int v = abs(lit);
            int l = lev[v];
            if (l >= 0 && lbdStamp[l] != lbdCounter) {
                lbdStamp[l] = lbdCounter;
                lbd++;
            }
        }
        return lbd;
    }
    
    // Check if literal can be removed from learned clause (recursive check)
    bool canRemove(int v, int abstractLevels) {
        if (rsn[v] < 0) return false;
        if (cr[rsn[v]].deleted) return false;
        int* cl = cdata(rsn[v]);
        int sz = csz(rsn[v]);
        for (int k = 0; k < sz; k++) {
            int u = abs(cl[k]);
            if (u == v) continue;
            if (!seen[u] && lev[u] > 0) {
                if ((abstractLevels & (1 << (lev[u] & 31))) == 0) return false;
                if (rsn[u] < 0) return false;
                if (cr[rsn[u]].deleted) return false;
                seen[u] = true;
                if (!canRemove(u, abstractLevels)) return false;
            }
        }
        return true;
    }
    
    void bumpVar(int v) {
        act[v] += actInc;
        if (act[v] > 1e100) {
            for (int i = 1; i <= n; i++) act[i] /= 1e100;
            actInc /= 1e100;
        }
        varHeap.increase(v);
    }
    
    int analyze(int confl, vector<int>& learned, vector<int>& learnedFull, int& btLevel) {
        learned.clear();
        learnedFull.clear();
        int curLev = trailLim.size();
        if (curLev == 0) { btLevel = -1; return 0; }

        int counter = 0, trailIdx = trail.size()-1;

        { int* cl = cdata(confl); int sz = csz(confl);
          for (int k = 0; k < sz; k++) {
            int v = abs(cl[k]);
            if (!seen[v] && lev[v] > 0) {
                seen[v] = true; bumpVar(v);
                if (lev[v] == curLev) counter++;
                else learned.push_back(cl[k]);
            }
          }
        }

        int uipLit = 0;
        while (counter > 1) {
            while (trailIdx >= 0 && !seen[abs(trail[trailIdx])]) trailIdx--;
            if (trailIdx < 0) break;
            int p = trail[trailIdx--]; int v = abs(p);
            seen[v] = false; counter--;
            if (rsn[v] >= 0 && !cr[rsn[v]].deleted) {
                cr[rsn[v]].act += (float)clauseActInc;
                int* rl = cdata(rsn[v]); int rsz = csz(rsn[v]);
                for (int k = 0; k < rsz; k++) {
                    int u = abs(rl[k]);
                    if (u != v && !seen[u] && lev[u] > 0) {
                        seen[u] = true; bumpVar(u);
                        if (lev[u] == curLev) counter++;
                        else learned.push_back(rl[k]);
                    }
                }
            }
        }

        while (trailIdx >= 0 && !seen[abs(trail[trailIdx])]) trailIdx--;
        if (trailIdx >= 0) uipLit = trail[trailIdx];
        else {
            for (int i = trail.size()-1; i >= 0; i--)
                if (seen[abs(trail[i])] && lev[abs(trail[i])] == curLev) { uipLit = trail[i]; break; }
        }

        seen[abs(uipLit)] = false;
        learned.push_back(-uipLit);

        // Clear seen
        for (int lit : learned) seen[abs(lit)] = false;

        // Put UIP first
        for (int i = 0; i < (int)learned.size(); i++)
            if (learned[i] == -uipLit) { swap(learned[0], learned[i]); break; }

        // Save full (unminimized) clause for proof
        learnedFull = learned;

        // Simple clause minimization
        if (learned.size() > 2) {
            for (int lit : learned) seen[abs(lit)] = true;
            int j = 1;
            for (int i = 1; i < (int)learned.size(); i++) {
                int v = abs(learned[i]);
                bool removable = false;
                if (rsn[v] >= 0 && !cr[rsn[v]].deleted) {
                    removable = true;
                    int* reason = cdata(rsn[v]); int rsz = csz(rsn[v]);
                    for (int k = 0; k < rsz; k++) {
                        int u = abs(reason[k]);
                        if (u == v) continue;
                        if (lev[u] == 0) continue;
                        if (!seen[u]) { removable = false; break; }
                    }
                }
                if (!removable) learned[j++] = learned[i];
            }
            for (int lit : learnedFull) seen[abs(lit)] = false;
            learned.resize(j);
        }

        // Sort non-UIP literals by level (highest first for second watch)
        btLevel = 0;
        for (int lit : learned) {
            int v = abs(lit);
            if (lit != learned[0] && lev[v] > btLevel) btLevel = lev[v];
        }
        
        if (learned.size() >= 2) {
            int mxL = -1, mxI = 1;
            for (int i = 1; i < (int)learned.size(); i++)
                if (lev[abs(learned[i])] > mxL) { mxL = lev[abs(learned[i])]; mxI = i; }
            swap(learned[1], learned[mxI]);
        }
        
        actInc /= actDecay;
        
        return 1;
    }
    
    // Write RUP line and return the VeriPB constraint ID
    int writeRUP(const vector<int>& cl) {
        if (!pf) return -1;
        fprintf(pf, "rup ");
        for (int lit : cl) {
            if (lit > 0) fprintf(pf, "1 x%d ", lit);
            else fprintf(pf, "1 ~x%d ", -lit);
        }
        fprintf(pf, ">= 1 ;\n");
        proofLines++;
        return nOriginal + proofLines;  // 1-based: originals are 1..nOriginal, learned start at nOriginal+1
    }
    
    int addClause(const vector<int>& cl, int lbd, int proofId = -1) {
        int ci = cr.size();
        int off = pool.size();
        for (int lit : cl) pool.push_back(lit);
        cr.push_back({off, (short)cl.size(), (short)lbd, proofId, true, false, (float)clauseActInc});
        if (cl.size() >= 2) {
            watches[li(cl[0])].push_back({ci, cl[1]});
            watches[li(cl[1])].push_back({ci, cl[0]});
        }
        nLearned++;
        return ci;
    }
    
    void reduceDB() {
        cancelUntil(0);
        
        vector<int> candidates;
        int ncr = cr.size();
        for (int ci = nOriginal; ci < ncr; ci++) {
            auto& c = cr[ci];
            if (c.deleted || !c.isLearned) continue;
            if (c.sz <= 2) continue;
            if (c.lbd <= 2) continue;
            bool locked = false;
            if (c.sz > 0) {
                int v = abs(cdata(ci)[0]);
                if (val[v] != -1 && rsn[v] == ci) locked = true;
            }
            if (!locked) candidates.push_back(ci);
        }

        if (candidates.empty()) return;

        sort(candidates.begin(), candidates.end(), [&](int a, int b) {
            return cr[a].act < cr[b].act;
        });

        int toDelete = candidates.size() / 2;
        for (int i = 0; i < toDelete; i++) {
            int ci = candidates[i];
            if (pf && cr[ci].proofId > 0) {
                fprintf(pf, "del id %d ;\n", cr[ci].proofId);
            }
            cr[ci].deleted = true;
            nLearned--;
        }

        for (auto& wl : watches) {
            int j = 0;
            for (int i = 0; i < (int)wl.size(); i++) {
                if (!cr[wl[i].ci].deleted) wl[j++] = wl[i];
            }
            wl.resize(j);
        }

        reduceDBThreshold += reduceDBThreshold / 4;
        if (reduceDBThreshold > MAX_REDUCE_THRESHOLD)
            reduceDBThreshold = MAX_REDUCE_THRESHOLD;

        fprintf(stderr, "c reduceDB: deleted %d, remaining %d, next at %d\n",
                toDelete, nLearned, reduceDBThreshold);
    }
    
    int solve(double timeLimit) {
        double t0 = elapsed();

        for (int ci = 0; ci < nOriginal; ci++) {
            if (csz(ci) == 0) {
                if (pf) { fprintf(pf, "rup >= 1 ;\n"); proofLines++; }
                return 0;
            }
            if (csz(ci) == 1) {
                int lit = cdata(ci)[0]; int v = abs(lit);
                if (val[v] == -1) assign(lit, 0, ci);
                else if (isF(lit)) {
                    if (pf) { fprintf(pf, "rup >= 1 ;\n"); proofLines++; }
                    return 0;
                }
            }
        }
        
        if (propagate() != -1) {
            if (pf) { fprintf(pf, "rup >= 1 ;\n"); proofLines++; }
            return 0;
        }
        
        int restartConflicts = 0;
        int restartLimit = 512;
        
        while (true) {
            if ((conflicts & 0xFF) == 0) {
                if (elapsed() - t0 > timeLimit) return -1;
                // Memory limit: pool + crefs should fit in ~2GB
                if (pool.size() > 200000000 || cr.size() > 30000000) {
                    fprintf(stderr, "c CDCL memory limit (pool=%zu, cr=%zu)\n", pool.size(), cr.size());
                    return -1;
                }
            }

            if (nLearned >= reduceDBThreshold) {
                reduceDB();
            }
            
            if (restartConflicts >= restartLimit) {
                cancelUntil(0);
                restartConflicts = 0;
            }
            
            int confl = propagate();
            
            if (confl != -1) {
                conflicts++; restartConflicts++;
                
                cr[confl].act += (float)clauseActInc;
                clauseActInc *= 1.001;
                if (clauseActInc > 1e15) {
                    int ncr2 = cr.size();
                    for (int ci2 = 0; ci2 < ncr2; ci2++) cr[ci2].act /= (float)1e15;
                    clauseActInc /= 1e15;
                }
                
                if (trailLim.empty()) {
                    if (pf) { fprintf(pf, "rup >= 1 ;\n"); proofLines++; }
                    return 0;
                }
                vector<int> learned, learnedFull;
                int btLevel;
                if (analyze(confl, learned, learnedFull, btLevel) == 0) {
                    if (pf) { fprintf(pf, "rup >= 1 ;\n"); proofLines++; }
                    return 0;
                }
                
                int lbd = computeLBD(learned);

                // Write clause to proof
                int pid = writeRUP(learned);

                cancelUntil(btLevel);
                if (learned.size() == 1) assign(learned[0], 0, -1);
                else { int ci = addClause(learned, lbd, pid); assign(learned[0], btLevel, ci); }
            } else {
                // Pick variable from heap
                int v = -1;
                while (!varHeap.empty()) {
                    v = varHeap.removeMax();
                    if (val[v] == -1) break;
                    v = -1;
                }
                if (v == -1) return 1; // SAT
                decisions++;
                trailLim.push_back(trail.size());
                if (phase[v] == 1)
                    assign(v, trailLim.size(), -1);
                else
                    assign(-v, trailLim.size(), -1);
            }
        }
    }
};

void printSAT(int nv, const int* a) {
    printf("s SATISFIABLE\nv ");
    for (int v = 1; v <= nv; v++) { if (v > 1) printf(" "); printf("%d", a[v] == 1 ? v : -v); }
    printf(" 0\n");
}

static double kSATthreshold(int k) {
    switch (k) {
        case 3: return 4.267;
        case 4: return 9.93;
        case 5: return 21.12;
        case 6: return 43.37;
        case 7: return 87.79;
        case 8: return 176.54;
        case 9: return 354.01;
        case 10: return 708.94;
        default: return k >= 11 ? pow(2.0, k) * log(2) : 4.267;
    }
}

// Run CDCL. Returns 10 (SAT), 20 (UNSAT), or 0 (unknown/timeout)
int runCDCL(const CNF& cnf, const char* proofPath, double budget) {
    int n = cnf.nVars, m = cnf.nClauses;
    fprintf(stderr, "c CDCL (budget=%.1fs, vars=%d, clauses=%d)\n", budget, n, m);

    FILE* proof = fopen(proofPath, "w");
    if (proof) fprintf(proof, "pseudo-Boolean proof version 3.0\n");

    CDCL cdcl(cnf, proof);
    int result = cdcl.solve(budget);

    if (result == 1) {
        if (proof) fclose(proof);
        if (verify(cnf, cdcl.val.data())) {
            printSAT(n, cdcl.val.data());
            return 10;
        }
        fprintf(stderr, "c CDCL SAT verify fail!\n");
        return 0;
    } else if (result == 0) {
        if (proof) {
            fprintf(proof, "output NONE ;\n");
            fprintf(proof, "conclusion UNSAT : %d ;\n", m + cdcl.proofLines);
            fprintf(proof, "end pseudo-Boolean proof ;\n");
            fclose(proof);
        }
        fprintf(stderr, "c CDCL UNSAT (%lld confl, %lld prop)\n",
                (long long)cdcl.conflicts, (long long)cdcl.propagations);
        printf("s UNSATISFIABLE\n");
        return 20;
    } else {
        if (proof) fclose(proof);
        fprintf(stderr, "c CDCL timeout/limit (%lld confl, %d learned)\n",
                (long long)cdcl.conflicts, cdcl.nLearned);
        return 0;
    }
}

int main(int argc, char** argv) {
    if (argc != 3) { fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n"); return 1; }

    CNF cnf;
    if (!parseDIMACS(argv[1], cnf)) { fprintf(stderr, "c parse failed\n"); return 0; }

    int n = cnf.nVars, m = cnf.nClauses, k = cnf.maxK;
    double ratio = (n > 0) ? (double)m / n : 0;
    double thresh = kSATthreshold(k);
    double relRatio = (thresh > 0) ? ratio / thresh : 1.0;

    fprintf(stderr, "c %d vars, %d clauses, maxK=%d, ratio=%.2f, threshold=%.2f, rel=%.2f\n",
            n, m, k, ratio, thresh, relRatio);

    if (m == 0) { vector<int> a(n+1, 1); printSAT(n, a.data()); return 10; }

    // Strategy depends on instance size and ratio
    // For tiny instances (n <= 200): CDCL first - it's cheap and handles UNSAT well
    // For medium (200 < n <= 3000): ratio-based split between SLS and CDCL
    // For large (n > 3000): mostly SLS (CDCL too expensive)

    if (n <= 200) {
        // Tiny instance strategy:
        // 1. Quick CDCL probe (10s) - catches easy UNSAT instantly (5cnf/6cnf/7cnf)
        // 2. SLS phase - catches SAT instances (gencnf near-threshold)
        // 3. Full CDCL - for harder UNSAT
        double rem = g_timeout - elapsed();

        // Phase 1: Quick CDCL probe
        double cdclProbe = (relRatio > 1.1) ? 60.0 : 5.0;
        int result = runCDCL(cnf, argv[2], cdclProbe);
        if (result == 10 || result == 20) return result;

        // Phase 2: SLS
        rem = g_timeout - elapsed();
        if (rem > 10.0) {
            double slsBudget = (relRatio > 1.15) ? rem * 0.05 : rem * 0.85;
            if (slsBudget > 3.0) {
                fprintf(stderr, "c probSAT (budget=%.1fs)\n", slsBudget);
                ProbSAT sls(cnf, Clock::now().time_since_epoch().count());
                if (sls.run(slsBudget) && verify(cnf, sls.asgn.data())) {
                    printSAT(n, sls.asgn.data());
                    return 10;
                }
            }
        }

        // Phase 3: Full CDCL with remaining time
        rem = g_timeout - elapsed();
        if (rem > 10.0) {
            result = runCDCL(cnf, argv[2], rem - 3.0);
            if (result == 10 || result == 20) return result;
        }
    } else if (n <= 3000) {
        // Medium instance: ratio-based strategy
        double rem = g_timeout - elapsed();

        double slsFraction;
        if (relRatio > 1.15) {
            slsFraction = 0.02;
        } else if (relRatio > 1.05) {
            slsFraction = 0.08;
        } else if (relRatio > 0.97) {
            slsFraction = 0.50;
        } else if (relRatio > 0.85) {
            slsFraction = 0.80;
        } else {
            slsFraction = 0.95;
        }

        double slsBudget = rem * slsFraction;
        fprintf(stderr, "c slsFraction=%.2f, slsBudget=%.1fs\n", slsFraction, slsBudget);

        // SLS phase
        if (slsBudget > 1.0) {
            fprintf(stderr, "c probSAT (budget=%.1fs)\n", slsBudget);
            ProbSAT sls(cnf, Clock::now().time_since_epoch().count());
            if (sls.run(slsBudget) && verify(cnf, sls.asgn.data())) {
                printSAT(n, sls.asgn.data());
                return 10;
            }
        }

        // CDCL phase with remaining time
        rem = g_timeout - elapsed();
        if (rem > 10.0) {
            int result = runCDCL(cnf, argv[2], rem - 3.0);
            if (result == 10 || result == 20) return result;
        }
    } else {
        // Large instance: mostly SLS (CDCL impractical)
        double rem = g_timeout - elapsed();
        double slsBudget = rem - 5.0;

        fprintf(stderr, "c probSAT (budget=%.1fs)\n", slsBudget);
        ProbSAT sls(cnf, Clock::now().time_since_epoch().count());
        if (sls.run(slsBudget) && verify(cnf, sls.asgn.data())) {
            printSAT(n, sls.asgn.data());
            return 10;
        }
    }

    fprintf(stderr, "c UNKNOWN (%.1fs)\n", elapsed());
    printf("s UNKNOWN\n");
    return 0;
}
