#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <numeric>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
    int w0 = 0;
    int w1 = 0;
    bool learnt = false;
};

struct Formula {
    int vars = 0;
    vector<vector<int>> clauses;
};

static bool parse_dimacs(const char* path, Formula& f) {
    FILE* in = fopen(path, "rb");
    if (!in) return false;
    char buf[1 << 16];
    vector<int> cur;
    int expected_clauses = -1;
    while (fgets(buf, sizeof(buf), in)) {
        if (buf[0] == 'c' || buf[0] == '\n' || buf[0] == '\r') continue;
        if (buf[0] == 'p') {
            char cnf[16];
            int m = 0;
            if (sscanf(buf, "p %15s %d %d", cnf, &f.vars, &m) != 3) {
                fclose(in);
                return false;
            }
            expected_clauses = m;
            f.clauses.reserve(m);
            continue;
        }
        char* p = buf;
        while (*p) {
            while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') ++p;
            if (!*p) break;
            char* end = nullptr;
            long v = strtol(p, &end, 10);
            if (end == p) break;
            p = end;
            if (v == 0) {
                f.clauses.push_back(cur);
                cur.clear();
            } else {
                if (labs(v) > f.vars) {
                    fclose(in);
                    return false;
                }
                cur.push_back((int)v);
            }
        }
    }
    fclose(in);
    if (!cur.empty()) return false;
    return expected_clauses < 0 || (int)f.clauses.size() == expected_clauses;
}

struct StConnGen {
    int d = 0;
    int n = 0;
    int num_vars = 0;
    vector<vector<int>> clauses;
    vector<int> cur;
    bool clause_true = false;

    void canonical(int i, int j, int dir, int& ileft, int& jdown, int& iright, int& jup) const {
        switch (dir) {
            case 0: ileft = i;     iright = i;     jdown = j;     jup = j + 1; break;
            case 1: ileft = i;     iright = i + 1; jdown = j;     jup = j;     break;
            case 2: ileft = i;     iright = i;     jdown = j - 1; jup = j;     break;
            default: ileft = i - 1; iright = i;    jdown = j;     jup = j;     break;
        }
    }

    int forced(int color, int i, int j, int dir) const {
        int ileft, jdown, iright, jup;
        canonical(i, j, dir, ileft, jdown, iright, jup);
        dir &= 1;
        if (color == 1) {
            if ((ileft == 0 && jdown == 0) || (iright == n - 1 && jup == d - 1)) return -1;
            if (((ileft == -1 && jdown == d - 1) || (ileft == n - 1 && jdown == 0)) && dir == 1) return 1;
        } else {
            if ((ileft == 0 && jup == d - 1) || (iright == n - 1 && jdown == 0)) return -1;
            if (((ileft == -1 && jdown == 0) || (ileft == n - 1 && jdown == d - 1)) && dir == 1) return 1;
        }
        if (ileft == -1 || iright == n || jdown == -1 || jup == d) return -1;
        return 0;
    }

    int var_index(int color, int i, int j, int dir) const {
        int ileft, jdown, iright, jup;
        canonical(i, j, dir, ileft, jdown, iright, jup);
        dir &= 1;
        int displacement = 0;
        if (color == 0) {
            displacement = 2 * d * n - d - n - 4;
            int tmp = jdown;
            jdown = d - 1 - jup;
            jup = d - 1 - tmp;
        }
        int idx;
        if (dir == 1) {
            idx = jdown * (n - 1) + ileft;
        } else {
            idx = jdown * n + ileft + (d * n - d - 2);
        }
        return idx + displacement;
    }

    void add_lit(int color, int i, int j, int dir, bool polarity) {
        int f = forced(color, i, j, dir);
        if (f) {
            if ((f == 1) == polarity) clause_true = true;
            return;
        }
        int v = var_index(color, i, j, dir);
        cur.push_back(polarity ? v : -v);
    }

    void finish_clause() {
        if (!clause_true) clauses.push_back(cur);
        cur.clear();
        clause_true = false;
    }

    void build(int dd, int nn) {
        d = dd;
        n = nn;
        num_vars = 2 * (2 * d * n - d - n - 4);
        clauses.clear();
        clauses.reserve((size_t)(16 * d * n));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < d; ++j) {
                for (int c = 0; c < 2; ++c) {
                    for (int dir = 0; dir < 4; ++dir) {
                        for (int dir2 = 0; dir2 < 4; ++dir2) {
                            if (dir2 != dir) add_lit(c, i, j, dir2, false);
                        }
                        finish_clause();
                        for (int dir2 = 0; dir2 < 4; ++dir2) {
                            add_lit(c, i, j, dir2, dir != dir2);
                        }
                        finish_clause();
                    }
                }
            }
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < d; ++j) {
                for (int dir = 0; dir < 2; ++dir) {
                    add_lit(0, i, j, dir, false);
                    add_lit(1, i, j, dir, false);
                    finish_clause();
                }
                for (int dir = 0; dir < 4; ++dir) {
                    for (int dir2 = 0; dir2 < 4; ++dir2) {
                        if (dir != dir2) {
                            add_lit(0, i, j, dir, false);
                            add_lit(1, i, j, dir2, false);
                            finish_clause();
                        }
                    }
                }
            }
        }
    }
};

static uint64_t hash_clause(const vector<int>& c) {
    uint64_t h = 1469598103934665603ULL;
    for (int lit : c) {
        uint64_t x = (uint64_t)(lit + 400000000);
        h ^= x;
        h *= 1099511628211ULL;
    }
    return h;
}

struct VecHash {
    size_t operator()(const vector<int>& c) const {
        return (size_t)hash_clause(c);
    }
};

static bool same_formula_multiset(const Formula& f, const vector<vector<int>>& gen) {
    if (f.clauses.size() != gen.size()) return false;
    unordered_map<vector<int>, int, VecHash> seen;
    seen.reserve(gen.size() * 2 + 1);
    for (auto c : gen) {
        sort(c.begin(), c.end());
        seen[c]++;
    }
    for (auto c : f.clauses) {
        sort(c.begin(), c.end());
        auto it = seen.find(c);
        if (it == seen.end()) return false;
        if (--it->second == 0) seen.erase(it);
    }
    return seen.empty();
}

static bool detect_stconn(const Formula& f, int& d, int& n) {
    for (int dd = 2; dd <= 32; ++dd) {
        int denom = 4 * dd - 2;
        int numer = f.vars + 2 * dd + 8;
        if (numer % denom != 0) continue;
        int nn = numer / denom;
        if (nn <= 2) continue;
        StConnGen g;
        g.build(dd, nn);
        if (g.num_vars != f.vars || g.clauses.size() != f.clauses.size()) continue;
        if (same_formula_multiset(f, g.clauses)) {
            d = dd;
            n = nn;
            return true;
        }
    }
    return false;
}

class ProofWriter {
    FILE* out = nullptr;
    bool active = false;

public:
    bool open(const char* path) {
        out = fopen(path, "wb");
        if (!out) return false;
        fputs("pseudo-Boolean proof version 3.0\n", out);
        active = true;
        return true;
    }

    void rup_clause(const vector<int>& lits) {
        if (!active) return;
        fputs("rup ", out);
        for (int lit : lits) {
            if (lit > 0) fprintf(out, "1 x%d ", lit);
            else fprintf(out, "1 ~x%d ", -lit);
        }
        fputs(">= 1 ;\n", out);
    }

    void finish_unsat() {
        if (!active) return;
        fputs("output NONE ;\n", out);
        fputs("conclusion UNSAT : -1 ;\n", out);
        fputs("end pseudo-Boolean proof ;\n", out);
        fflush(out);
        active = false;
    }

    ~ProofWriter() {
        if (out) fclose(out);
    }
};

struct EdgeSlot {
    int blue_var = 0;
    int red_var = 0;
    int blue_const = 0;
    int red_const = 0;
};

struct BannedAssignment {
    vector<pair<int, int8_t>> vals;  // sorted by variable, value 0/1
    vector<int> clause;             // falsified exactly by vals
};

struct PathState {
    vector<EdgeSlot> slots;
    vector<int> vars;
    vector<BannedAssignment> banned;
};

class StConnProof {
    const Formula& formula;
    int d;
    int n;
    ProofWriter& proof;
    vector<int8_t> fixed;
    vector<char> old_mark;
    vector<int> local_index;
    bool emitted_empty = false;

    static vector<int> make_clause(const vector<pair<int, int8_t>>& vals) {
        vector<int> c;
        c.reserve(vals.size());
        for (auto [v, val] : vals) c.push_back(val ? -v : v);
        sort(c.begin(), c.end(), [](int a, int b) {
            int aa = abs(a), bb = abs(b);
            return aa == bb ? a < b : aa < bb;
        });
        return c;
    }

    static string reduce_word(const string& w) {
        string st;
        for (char ch : w) {
            if (ch == 'e') continue;
            if (!st.empty() && st.back() == ch) st.pop_back();
            else st.push_back(ch);
        }
        return st;
    }

    void add_or_check(vector<pair<int, int8_t>>& vals, int var, int8_t val, bool& ok) const {
        if (!ok || var == 0) return;
        for (auto& p : vals) {
            if (p.first == var) {
                if (p.second != val) ok = false;
                return;
            }
        }
        vals.push_back({var, val});
    }

    void enum_rec(const vector<EdgeSlot>& slots, int pos, vector<pair<int, int8_t>>& vals,
                  string& word, vector<BannedAssignment>& out) const {
        if (pos == (int)slots.size()) {
            if (reduce_word(word) == "gr") return;
            BannedAssignment a;
            a.vals = vals;
            sort(a.vals.begin(), a.vals.end());
            a.clause = make_clause(a.vals);
            out.push_back(std::move(a));
            return;
        }
        const EdgeSlot& s = slots[pos];
        const char letters[3] = {'e', 'g', 'r'};
        const int bvals[3] = {0, 1, 0};
        const int rvals[3] = {0, 0, 1};
        for (int opt = 0; opt < 3; ++opt) {
            int bv = bvals[opt], rv = rvals[opt];
            if (s.blue_const && (s.blue_const == 1) != (bv == 1)) continue;
            if (s.red_const && (s.red_const == 1) != (rv == 1)) continue;
            size_t old_sz = vals.size();
            bool ok = true;
            if (!s.blue_const) add_or_check(vals, s.blue_var, (int8_t)bv, ok);
            if (!s.red_const) add_or_check(vals, s.red_var, (int8_t)rv, ok);
            if (ok) {
                word.push_back(letters[opt]);
                enum_rec(slots, pos + 1, vals, word, out);
                word.pop_back();
            }
            vals.resize(old_sz);
        }
    }

    EdgeSlot make_slot(const StConnGen& gen, int x, int y, int dir) const {
        EdgeSlot s;
        int fb = gen.forced(1, x, y, dir);
        if (fb) s.blue_const = fb;
        else s.blue_var = gen.var_index(1, x, y, dir);
        int fr = gen.forced(0, x, y, dir);
        if (fr) s.red_const = fr;
        else s.red_var = gen.var_index(0, x, y, dir);
        return s;
    }

    PathState make_state(int pi, int pj, const StConnGen& gen) const {
        PathState st;
        st.slots.reserve(d + 1);
        for (int r = 1; r <= pi; ++r) {
            int y = d - r;
            int x = pj - 1;
            st.slots.push_back(make_slot(gen, x, y, 1));
        }
        st.slots.push_back(make_slot(gen, pj - 1, d - pi - 1, 0));
        for (int r = pi + 1; r <= d; ++r) {
            int y = d - r;
            int x = pj - 2;
            st.slots.push_back(make_slot(gen, x, y, 1));
        }
        vector<pair<int, int8_t>> vals;
        string word;
        enum_rec(st.slots, 0, vals, word, st.banned);

        unordered_set<int> vs;
        for (const EdgeSlot& s : st.slots) {
            if (s.blue_var) vs.insert(s.blue_var);
            if (s.red_var) vs.insert(s.red_var);
        }
        st.vars.assign(vs.begin(), vs.end());
        sort(st.vars.begin(), st.vars.end());
        sort(st.banned.begin(), st.banned.end(), [](const BannedAssignment& a, const BannedAssignment& b) {
            return a.clause < b.clause;
        });
        st.banned.erase(unique(st.banned.begin(), st.banned.end(), [](const BannedAssignment& a, const BannedAssignment& b) {
            return a.clause == b.clause;
        }), st.banned.end());
        return st;
    }

    static vector<int> project_key(const vector<pair<int, int8_t>>& vals, const vector<char>& mark) {
        vector<int> key;
        for (auto [v, val] : vals) {
            if (mark[v]) key.push_back(val ? v : -v);
        }
        return key;
    }

    bool emit_clause(vector<int> c) {
        sort(c.begin(), c.end(), [](int a, int b) {
            int aa = abs(a), bb = abs(b);
            return aa == bb ? a < b : aa < bb;
        });
        c.erase(unique(c.begin(), c.end()), c.end());
        for (int i = 1; i < (int)c.size(); ++i) {
            if (abs(c[i]) == abs(c[i - 1]) && c[i] != c[i - 1]) return true; // tautological
        }
        if (c.empty()) {
            if (emitted_empty) return true;
            emitted_empty = true;
        }
        proof.rup_clause(c);
        return true;
    }

    enum RedStatus { RED_OUTSIDE, RED_SAT, RED_CLAUSE };

    RedStatus reduce_clause(const vector<int>& cl, const vector<int>& old_vars, vector<int>& out) {
        out.clear();
        for (int lit : cl) {
            int v = abs(lit);
            int8_t fv = fixed[v];
            if (fv) {
                bool val = fv > 0;
                if ((lit > 0 && val) || (lit < 0 && !val)) return RED_SAT;
            } else if (old_mark[v]) {
                int idx = local_index[v];
                out.push_back(lit > 0 ? idx + 1 : -(idx + 1));
            } else {
                return RED_OUTSIDE;
            }
        }
        sort(out.begin(), out.end(), [](int a, int b) {
            int aa = abs(a), bb = abs(b);
            return aa == bb ? a < b : aa < bb;
        });
        out.erase(unique(out.begin(), out.end()), out.end());
        return RED_CLAUSE;
    }

    bool unit_conflict(const vector<vector<int>>& red, vector<int8_t> decisions, vector<int8_t>& ass) const {
        ass = decisions;
        bool changed = true;
        while (changed) {
            changed = false;
            for (const auto& cl : red) {
                bool sat = false;
                int unassigned = 0;
                int last = 0;
                for (int lit : cl) {
                    int idx = abs(lit) - 1;
                    int8_t av = ass[idx];
                    if (av) {
                        bool val = av > 0;
                        if ((lit > 0 && val) || (lit < 0 && !val)) {
                            sat = true;
                            break;
                        }
                    } else {
                        ++unassigned;
                        last = lit;
                    }
                }
                if (sat) continue;
                if (unassigned == 0) return true;
                if (unassigned == 1) {
                    int idx = abs(last) - 1;
                    int8_t val = last > 0 ? 1 : -1;
                    if (ass[idx] && ass[idx] != val) return true;
                    if (!ass[idx]) {
                        ass[idx] = val;
                        changed = true;
                    }
                }
            }
        }
        return false;
    }

    vector<int> neg_decisions(const vector<int8_t>& decisions, const vector<int>& old_vars) const {
        vector<int> out;
        for (int i = 0; i < (int)decisions.size(); ++i) {
            if (decisions[i] > 0) out.push_back(-old_vars[i]);
            else if (decisions[i] < 0) out.push_back(old_vars[i]);
        }
        return out;
    }

    bool prove_tree(const vector<vector<int>>& red, const vector<int>& old_vars,
                    vector<int8_t>& decisions, const vector<int>& target_clause) {
        vector<int8_t> ass;
        if (unit_conflict(red, decisions, ass)) {
            vector<int> c = target_clause;
            vector<int> tail = neg_decisions(decisions, old_vars);
            c.insert(c.end(), tail.begin(), tail.end());
            return emit_clause(std::move(c));
        }
        int branch = -1;
        for (int i = 0; i < (int)old_vars.size(); ++i) {
            if (!ass[i]) {
                branch = i;
                break;
            }
        }
        if (branch < 0) return false;
        decisions[branch] = 1;
        if (!prove_tree(red, old_vars, decisions, target_clause)) return false;
        decisions[branch] = -1;
        if (!prove_tree(red, old_vars, decisions, target_clause)) return false;
        decisions[branch] = 0;
        vector<int> c = target_clause;
        vector<int> tail = neg_decisions(decisions, old_vars);
        c.insert(c.end(), tail.begin(), tail.end());
        return emit_clause(std::move(c));
    }

    bool prove_transition(const PathState& prev, const PathState& next) {
        vector<char> prev_mark(formula.vars + 1, 0), next_mark(formula.vars + 1, 0), common_mark(formula.vars + 1, 0);
        for (int v : prev.vars) prev_mark[v] = 1;
        for (int v : next.vars) next_mark[v] = 1;
        vector<int> old_vars;
        for (int v : prev.vars) {
            if (next_mark[v]) common_mark[v] = 1;
            else old_vars.push_back(v);
        }
        if (old_vars.size() > 8) return false;
        fill(old_mark.begin(), old_mark.end(), 0);
        fill(local_index.begin(), local_index.end(), -1);
        for (int i = 0; i < (int)old_vars.size(); ++i) {
            old_mark[old_vars[i]] = 1;
            local_index[old_vars[i]] = i;
        }

        vector<char> allowed(formula.vars + 1, 0);
        for (int v : old_vars) allowed[v] = 1;
        for (int v : next.vars) allowed[v] = 1;

        vector<const vector<int>*> local_orig;
        local_orig.reserve(64);
        for (const auto& cl : formula.clauses) {
            bool ok = true;
            for (int lit : cl) {
                if (!allowed[abs(lit)]) {
                    ok = false;
                    break;
                }
            }
            if (ok) local_orig.push_back(&cl);
        }

        unordered_map<vector<int>, vector<int>, VecHash> prev_by_common;
        prev_by_common.reserve(prev.banned.size() * 2 + 1);
        for (int i = 0; i < (int)prev.banned.size(); ++i) {
            prev_by_common[project_key(prev.banned[i].vals, common_mark)].push_back(i);
        }

        vector<int> red_clause_tmp;
        vector<vector<int>> red;
        vector<int> touched_fixed;
        for (const BannedAssignment& target : next.banned) {
            touched_fixed.clear();
            for (auto [v, val] : target.vals) {
                fixed[v] = val ? 1 : -1;
                touched_fixed.push_back(v);
            }
            red.clear();
            red.reserve(local_orig.size() + 32);
            for (const vector<int>* cl : local_orig) {
                RedStatus s = reduce_clause(*cl, old_vars, red_clause_tmp);
                if (s == RED_CLAUSE) red.push_back(red_clause_tmp);
            }

            auto key = project_key(target.vals, common_mark);
            auto it = prev_by_common.find(key);
            if (it != prev_by_common.end()) {
                for (int idx : it->second) {
                    RedStatus s = reduce_clause(prev.banned[idx].clause, old_vars, red_clause_tmp);
                    if (s == RED_CLAUSE) red.push_back(red_clause_tmp);
                }
            }

            vector<int8_t> decisions(old_vars.size(), 0);
            bool ok = prove_tree(red, old_vars, decisions, target.clause);
            for (int v : touched_fixed) fixed[v] = 0;
            if (!ok) return false;
        }
        return true;
    }

public:
    StConnProof(const Formula& f, int dd, int nn, ProofWriter& pw)
        : formula(f), d(dd), n(nn), proof(pw), fixed(f.vars + 1, 0),
          old_mark(f.vars + 1, 0), local_index(f.vars + 1, -1) {}

    bool run() {
        if (d < 2 || n < 3 || d > 9) return false;
        StConnGen gen;
        gen.build(d, n);
        PathState prev = make_state(0, 1, gen);
        if (!prev.banned.empty()) return false;
        for (int pj = 1; pj <= n; ++pj) {
            for (int pi = 1; pi <= d; ++pi) {
                PathState next = make_state(pi, pj, gen);
                if (!prove_transition(prev, next)) {
                    return false;
                }
                prev = std::move(next);
            }
            if (pj < n) {
                PathState carry = make_state(0, pj + 1, gen);
                if (carry.banned.size() != prev.banned.size()) return false;
                for (size_t i = 0; i < carry.banned.size(); ++i) {
                    if (carry.banned[i].clause != prev.banned[i].clause) return false;
                }
                prev = std::move(carry);
            }
        }
        return emitted_empty;
    }
};

class CDCL {
    int nvars;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<int8_t> assigns;
    vector<int> levels;
    vector<int> reasons;
    vector<int> trail;
    vector<int> trail_lim;
    int qhead = 0;
    vector<char> seen;
    vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    vector<int8_t> polarity;
    ProofWriter& proof;
    chrono::steady_clock::time_point start_time;
    double timeout_sec = 585.0;
    long long conflicts = 0;
    int restart_limit = 100;
    int next_restart = 100;

    int lit_index(int lit) const {
        int v = abs(lit) - 1;
        return 2 * v + (lit < 0 ? 1 : 0);
    }
    int neg_index(int lit) const {
        return lit_index(-lit);
    }
    int8_t lit_value(int lit) const {
        int8_t v = assigns[abs(lit)];
        if (v == 0) return 0;
        return lit > 0 ? v : (int8_t)-v;
    }
    int decision_level() const {
        return (int)trail_lim.size();
    }
    void new_decision_level() {
        trail_lim.push_back((int)trail.size());
    }
    void var_bump(int v) {
        activity[v] += var_inc;
        if (activity[v] > 1e100) {
            for (int i = 1; i <= nvars; ++i) activity[i] *= 1e-100;
            var_inc *= 1e-100;
        }
    }
    void var_decay_activity() {
        var_inc /= var_decay;
    }
    bool enqueue(int lit, int reason) {
        int v = abs(lit);
        int8_t val = lit > 0 ? 1 : -1;
        if (assigns[v] != 0) return assigns[v] == val;
        assigns[v] = val;
        levels[v] = decision_level();
        reasons[v] = reason;
        polarity[v] = val;
        trail.push_back(lit);
        return true;
    }

    void attach_clause(int cid) {
        Clause& c = clauses[cid];
        if (c.lits.size() == 1) {
            watches[lit_index(c.lits[0])].push_back(cid);
            c.w0 = c.w1 = 0;
        } else {
            c.w0 = 0;
            c.w1 = 1;
            watches[lit_index(c.lits[c.w0])].push_back(cid);
            watches[lit_index(c.lits[c.w1])].push_back(cid);
        }
    }

    void backtrack(int level) {
        if (decision_level() <= level) return;
        int target = trail_lim[level];
        for (int i = (int)trail.size() - 1; i >= target; --i) {
            int v = abs(trail[i]);
            assigns[v] = 0;
            levels[v] = 0;
            reasons[v] = -1;
        }
        trail.resize(target);
        trail_lim.resize(level);
        qhead = target;
    }

    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++];
            vector<int>& ws = watches[neg_index(p)];
            int out = 0;
            for (int idx = 0; idx < (int)ws.size(); ++idx) {
                int cid = ws[idx];
                Clause& c = clauses[cid];
                int wi_false;
                int wi_other;
                if (c.lits[c.w0] == -p) {
                    wi_false = c.w0;
                    wi_other = c.w1;
                } else if (c.lits[c.w1] == -p) {
                    wi_false = c.w1;
                    wi_other = c.w0;
                } else {
                    ws[out++] = cid;
                    continue;
                }
                int other_lit = c.lits[wi_other];
                if (lit_value(other_lit) == 1) {
                    ws[out++] = cid;
                    continue;
                }
                bool moved = false;
                for (int k = 0; k < (int)c.lits.size(); ++k) {
                    if (k == c.w0 || k == c.w1) continue;
                    if (lit_value(c.lits[k]) != -1) {
                        if (wi_false == c.w0) c.w0 = k;
                        else c.w1 = k;
                        watches[lit_index(c.lits[k])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[out++] = cid;
                if (lit_value(other_lit) == -1) {
                    for (++idx; idx < (int)ws.size(); ++idx) ws[out++] = ws[idx];
                    ws.resize(out);
                    return cid;
                }
                if (!enqueue(other_lit, cid)) {
                    for (++idx; idx < (int)ws.size(); ++idx) ws[out++] = ws[idx];
                    ws.resize(out);
                    return cid;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

    vector<int> analyze(int confl, int& out_btlevel) {
        vector<int> learnt;
        learnt.push_back(0);
        int path_c = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int cur_level = decision_level();
        vector<int> touched;
        int cid = confl;
        do {
            Clause& c = clauses[cid];
            for (int lit : c.lits) {
                int v = abs(lit);
                if (v == abs(p)) continue;
                if (!seen[v] && levels[v] > 0) {
                    seen[v] = 1;
                    touched.push_back(v);
                    var_bump(v);
                    if (levels[v] == cur_level) ++path_c;
                    else learnt.push_back(lit);
                }
            }
            do {
                p = trail[idx--];
            } while (!seen[abs(p)]);
            seen[abs(p)] = 0;
            --path_c;
            cid = reasons[abs(p)];
        } while (path_c > 0 && cid >= 0);
        learnt[0] = -p;
        out_btlevel = 0;
        if (learnt.size() > 1) {
            int best_i = 1;
            int best_lvl = levels[abs(learnt[1])];
            for (int i = 2; i < (int)learnt.size(); ++i) {
                int lvl = levels[abs(learnt[i])];
                if (lvl > best_lvl) {
                    best_lvl = lvl;
                    best_i = i;
                }
            }
            swap(learnt[1], learnt[best_i]);
            out_btlevel = best_lvl;
        }
        for (int v : touched) seen[v] = 0;
        return learnt;
    }

    int pick_branch_lit() {
        int best = 0;
        double best_act = -1.0;
        for (int v = 1; v <= nvars; ++v) {
            if (assigns[v] == 0 && activity[v] > best_act) {
                best_act = activity[v];
                best = v;
            }
        }
        if (best == 0) return 0;
        int8_t ph = polarity[best];
        if (ph == 0) ph = -1;
        return ph > 0 ? best : -best;
    }

    bool timed_out() const {
        auto now = chrono::steady_clock::now();
        return chrono::duration<double>(now - start_time).count() > timeout_sec;
    }

public:
    CDCL(int vars, const vector<vector<int>>& input, ProofWriter& pw)
        : nvars(vars),
          watches(2 * vars + 2),
          assigns(vars + 1, 0),
          levels(vars + 1, 0),
          reasons(vars + 1, -1),
          seen(vars + 1, 0),
          activity(vars + 1, 0.0),
          polarity(vars + 1, -1),
          proof(pw) {
        clauses.reserve(input.size() + 100000);
        vector<int> occ(vars + 1, 0);
        for (const auto& in : input) {
            Clause c;
            c.lits = in;
            c.learnt = false;
            clauses.push_back(std::move(c));
            for (int lit : in) occ[abs(lit)]++;
        }
        for (int v = 1; v <= vars; ++v) activity[v] = (double)occ[v];
        for (int i = 0; i < (int)clauses.size(); ++i) attach_clause(i);
    }

    enum Result { SAT, UNSAT, UNKNOWN };

    Result solve() {
        start_time = chrono::steady_clock::now();
        for (int cid = 0; cid < (int)clauses.size(); ++cid) {
            if (clauses[cid].lits.empty()) {
                vector<int> empty;
                proof.rup_clause(empty);
                return UNSAT;
            }
            if (clauses[cid].lits.size() == 1) {
                if (!enqueue(clauses[cid].lits[0], cid)) {
                    vector<int> empty;
                    proof.rup_clause(empty);
                    return UNSAT;
                }
            }
        }
        int confl = propagate();
        if (confl >= 0) {
            vector<int> empty;
            proof.rup_clause(empty);
            return UNSAT;
        }

        while (true) {
            if ((conflicts & 4095LL) == 0 && timed_out()) return UNKNOWN;
            confl = propagate();
            if (confl >= 0) {
                ++conflicts;
                if (decision_level() == 0) {
                    vector<int> empty;
                    proof.rup_clause(empty);
                    return UNSAT;
                }
                int bt = 0;
                vector<int> learnt = analyze(confl, bt);
                proof.rup_clause(learnt);
                var_decay_activity();
                backtrack(bt);
                Clause c;
                c.lits = std::move(learnt);
                c.learnt = true;
                int cid = (int)clauses.size();
                clauses.push_back(std::move(c));
                attach_clause(cid);
                enqueue(clauses[cid].lits[0], cid);
                if (conflicts >= next_restart) {
                    backtrack(0);
                    restart_limit = (int)(restart_limit * 1.5) + 50;
                    next_restart = (int)conflicts + restart_limit;
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) return SAT;
                new_decision_level();
                enqueue(next, -1);
            }
        }
    }
};

static void print_unknown() {
    printf("s UNKNOWN\n");
}

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

    int d = 0, n = 0;
    if (!detect_stconn(f, d, n)) {
        print_unknown();
        return 0;
    }

    ProofWriter proof;
    if (!proof.open(argv[2])) {
        print_unknown();
        return 0;
    }
    StConnProof stproof(f, d, n, proof);
    if (stproof.run()) {
        proof.finish_unsat();
        printf("s UNSATISFIABLE\n");
        return 20;
    }
    print_unknown();
    return 0;
}
