#include <algorithm>
#include <cassert>
#include <chrono>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <numeric>
#include <queue>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

using std::string;
using std::vector;

namespace {

constexpr int LIT_POS_SIGN = 0;
constexpr int LIT_NEG_SIGN = 1;

inline int mk_lit(int var, bool neg) { return (var << 1) | (neg ? 1 : 0); }
inline int lit_var(int lit) { return lit >> 1; }
inline bool lit_neg(int lit) { return (lit & 1) != 0; }
inline int lit_negate(int lit) { return lit ^ 1; }

struct Clause {
    vector<int> lits;
    bool learnt = false;
    bool deleted = false;
    double activity = 0.0;
    int lbd = 0;
};

struct Solver {
    int nvars = 0;
    int declared_clauses = 0;
    vector<Clause> clauses;
    vector<int> positive_clause_ids;
    vector<vector<int>> pos_occurs;
    vector<int> conflict_degree;
    vector<std::pair<int, int>> conflict_edges;
    vector<vector<std::pair<int, int>>> conflict_neighbors;

    vector<vector<int>> watches;
    vector<int8_t> assigns; // 0 unassigned, 1 true, -1 false
    vector<int> levels;
    vector<int> reasons;
    vector<int> trail;
    vector<int> trail_lim;
    int qhead = 0;

    vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    double clause_inc = 1.0;
    double clause_decay = 0.999;

    vector<char> seen;
    vector<int> analyze_stack;

    std::ofstream proof;
    bool proof_enabled = false;
    long long conflicts = 0;
    long long decisions = 0;
    long long propagations = 0;
    long long learnt_count = 0;

    std::chrono::steady_clock::time_point start_time;
    double time_limit = 590.0;

    bool parse_dimacs(const char* path);
    bool detect_family() const;
    void init_solver_state();
    int add_clause(vector<int>&& lits, bool learnt);
    bool enqueue(int lit, int reason);
    int value_lit(int lit) const;
    int propagate();
    int decision_level() const { return static_cast<int>(trail_lim.size()); }
    void new_decision_level() { trail_lim.push_back(static_cast<int>(trail.size())); }
    void cancel_until(int lvl);
    void bump_var(int v);
    void decay_vars() { var_inc *= (1.0 / var_decay); }
    void bump_clause(int cid);
    void decay_clauses() { clause_inc *= (1.0 / clause_decay); }
    void analyze(int confl, vector<int>& out_learnt, int& out_btlevel);
    void simplify_learned_clause(vector<int>& lits);
    int pick_branch_lit();
    bool try_local_search(vector<int8_t>& model, double budget_seconds);
    bool all_original_satisfied_with_false_tail(vector<int8_t>& model);
    bool verify_model(const vector<int8_t>& model) const;
    void print_model(const vector<int8_t>& model) const;
    bool timed_out() const;
    void open_proof(const char* path);
    void write_rup_clause(const vector<int>& lits);
    void write_unsat_footer(int final_id);
    int solve(const char* proof_path);
};

bool Solver::parse_dimacs(const char* path) {
    std::ifstream in(path);
    if (!in) return false;

    string tok;
    bool have_header = false;
    int max_seen_var = 0;
    clauses.clear();

    while (in >> tok) {
        if (tok.empty()) continue;
        if (tok[0] == 'c') {
            string rest;
            std::getline(in, rest);
            continue;
        }
        if (tok == "p") {
            string fmt;
            if (!(in >> fmt >> nvars >> declared_clauses)) return false;
            if (fmt != "cnf" || nvars <= 0 || declared_clauses < 0) return false;
            have_header = true;
            continue;
        }
        if (!have_header) return false;

        vector<int> lits;
        int raw = std::stoi(tok);
        while (true) {
            if (raw == 0) break;
            int v = std::abs(raw);
            if (v <= 0 || v > nvars) return false;
            max_seen_var = std::max(max_seen_var, v);
            lits.push_back(mk_lit(v - 1, raw < 0));
            if (!(in >> raw)) return false;
        }
        clauses.push_back({std::move(lits), false, false, 0.0, 0});
    }

    if (!have_header) return false;
    if (declared_clauses != static_cast<int>(clauses.size())) return false;
    (void)max_seen_var;
    return true;
}

bool Solver::detect_family() const {
    if (nvars <= 0 || clauses.empty()) return false;
    bool saw_positive = false;
    bool saw_negative_binary = false;
    for (const Clause& c : clauses) {
        if (c.lits.empty()) return false;
        int pos = 0, neg = 0;
        for (int lit : c.lits) {
            if (lit_neg(lit)) ++neg;
            else ++pos;
        }
        if (pos == static_cast<int>(c.lits.size())) {
            if (c.lits.size() < 3) return false;
            saw_positive = true;
        } else if (neg == 2 && c.lits.size() == 2) {
            saw_negative_binary = true;
        } else {
            return false;
        }
    }
    return saw_positive && saw_negative_binary;
}

void Solver::init_solver_state() {
    positive_clause_ids.clear();
    pos_occurs.assign(nvars, {});
    conflict_degree.assign(nvars, 0);
    conflict_edges.clear();
    conflict_neighbors.assign(nvars, {});

    for (int cid = 0; cid < static_cast<int>(clauses.size()); ++cid) {
        Clause& c = clauses[cid];
        std::sort(c.lits.begin(), c.lits.end());
        c.lits.erase(std::unique(c.lits.begin(), c.lits.end()), c.lits.end());
        bool all_pos = true;
        bool all_neg = true;
        for (int lit : c.lits) {
            all_pos &= !lit_neg(lit);
            all_neg &= lit_neg(lit);
        }
        if (all_pos) {
            positive_clause_ids.push_back(cid);
            for (int lit : c.lits) pos_occurs[lit_var(lit)].push_back(cid);
        } else if (all_neg && c.lits.size() == 2) {
            int a = lit_var(c.lits[0]);
            int b = lit_var(c.lits[1]);
            int eid = static_cast<int>(conflict_edges.size());
            conflict_edges.push_back({a, b});
            conflict_neighbors[a].push_back({b, eid});
            conflict_neighbors[b].push_back({a, eid});
            ++conflict_degree[a];
            ++conflict_degree[b];
        }
    }

    watches.assign(2 * nvars, {});
    assigns.assign(nvars, 0);
    levels.assign(nvars, 0);
    reasons.assign(nvars, -1);
    trail.clear();
    trail_lim.clear();
    qhead = 0;
    activity.assign(nvars, 0.0);
    seen.assign(nvars, 0);

    for (int v = 0; v < nvars; ++v) {
        activity[v] = static_cast<double>(pos_occurs[v].size()) + 0.02 * conflict_degree[v];
    }

    for (int cid = 0; cid < static_cast<int>(clauses.size()); ++cid) {
        Clause& c = clauses[cid];
        if (c.lits.empty()) continue;
        if (c.lits.size() == 1) {
            watches[c.lits[0]].push_back(cid);
        } else {
            watches[c.lits[0]].push_back(cid);
            watches[c.lits[1]].push_back(cid);
        }
    }
}

int Solver::add_clause(vector<int>&& lits, bool learnt) {
    Clause c;
    c.lits = std::move(lits);
    c.learnt = learnt;
    c.deleted = false;
    c.activity = 0.0;
    c.lbd = 0;
    int cid = static_cast<int>(clauses.size());
    clauses.push_back(std::move(c));
    Clause& nc = clauses.back();
    if (nc.lits.size() == 1) {
        watches[nc.lits[0]].push_back(cid);
    } else if (nc.lits.size() >= 2) {
        watches[nc.lits[0]].push_back(cid);
        watches[nc.lits[1]].push_back(cid);
    }
    return cid;
}

int Solver::value_lit(int lit) const {
    int8_t v = assigns[lit_var(lit)];
    if (v == 0) return 0;
    bool lit_is_true = (v == 1 && !lit_neg(lit)) || (v == -1 && lit_neg(lit));
    return lit_is_true ? 1 : -1;
}

bool Solver::enqueue(int lit, int reason) {
    int v = lit_var(lit);
    int val = value_lit(lit);
    if (val == 1) return true;
    if (val == -1) return false;
    assigns[v] = lit_neg(lit) ? -1 : 1;
    levels[v] = decision_level();
    reasons[v] = reason;
    trail.push_back(lit);
    return true;
}

int Solver::propagate() {
    while (qhead < static_cast<int>(trail.size())) {
        int p = trail[qhead++];
        ++propagations;
        int false_lit = lit_negate(p);
        vector<int>& ws = watches[false_lit];
        int out = 0;
        for (int i = 0; i < static_cast<int>(ws.size()); ++i) {
            int cid = ws[i];
            Clause& c = clauses[cid];
            if (c.deleted) continue;

            if (c.lits.size() == 1) {
                if (value_lit(c.lits[0]) == -1) {
                    ws[out++] = cid;
                    for (++i; i < static_cast<int>(ws.size()); ++i) ws[out++] = ws[i];
                    ws.resize(out);
                    return cid;
                }
                ws[out++] = cid;
                continue;
            }

            if (c.lits[0] == false_lit) std::swap(c.lits[0], c.lits[1]);
            assert(c.lits[1] == false_lit);

            if (value_lit(c.lits[0]) == 1) {
                ws[out++] = cid;
                continue;
            }

            bool found_watch = false;
            for (int k = 2; k < static_cast<int>(c.lits.size()); ++k) {
                if (value_lit(c.lits[k]) != -1) {
                    std::swap(c.lits[1], c.lits[k]);
                    watches[c.lits[1]].push_back(cid);
                    found_watch = true;
                    break;
                }
            }
            if (found_watch) continue;

            ws[out++] = cid;
            if (value_lit(c.lits[0]) == -1) {
                for (++i; i < static_cast<int>(ws.size()); ++i) ws[out++] = ws[i];
                ws.resize(out);
                return cid;
            }
            if (!enqueue(c.lits[0], cid)) {
                for (++i; i < static_cast<int>(ws.size()); ++i) ws[out++] = ws[i];
                ws.resize(out);
                return cid;
            }
        }
        ws.resize(out);
    }
    return -1;
}

void Solver::cancel_until(int lvl) {
    if (decision_level() <= lvl) return;
    int keep = trail_lim[lvl];
    for (int i = static_cast<int>(trail.size()) - 1; i >= keep; --i) {
        int v = lit_var(trail[i]);
        assigns[v] = 0;
        levels[v] = 0;
        reasons[v] = -1;
    }
    trail.resize(keep);
    trail_lim.resize(lvl);
    qhead = static_cast<int>(trail.size());
}

void Solver::bump_var(int v) {
    activity[v] += var_inc;
    if (activity[v] > 1e100) {
        for (double& a : activity) a *= 1e-100;
        var_inc *= 1e-100;
    }
}

void Solver::bump_clause(int cid) {
    clauses[cid].activity += clause_inc;
    if (clauses[cid].activity > 1e100) {
        for (Clause& c : clauses) c.activity *= 1e-100;
        clause_inc *= 1e-100;
    }
}

void Solver::simplify_learned_clause(vector<int>& lits) {
    if (lits.size() <= 2) return;
    vector<int> keep;
    keep.reserve(lits.size());
    keep.push_back(lits[0]);

    for (int i = 1; i < static_cast<int>(lits.size()); ++i) {
        int lit = lits[i];
        int v = lit_var(lit);
        int r = reasons[v];
        if (r < 0 || clauses[r].learnt == false) {
            keep.push_back(lit);
            continue;
        }
        bool removable = true;
        for (int q : clauses[r].lits) {
            int qv = lit_var(q);
            if (qv == v) continue;
            if (!seen[qv] && levels[qv] > 0) {
                removable = false;
                break;
            }
        }
        if (!removable) keep.push_back(lit);
    }
    lits.swap(keep);
}

void Solver::analyze(int confl, vector<int>& out_learnt, int& out_btlevel) {
    out_learnt.clear();
    analyze_stack.clear();
    out_learnt.push_back(-1);

    int path_count = 0;
    int p = -1;
    int idx = static_cast<int>(trail.size()) - 1;
    int cid = confl;

    while (true) {
        bump_clause(cid);
        Clause& c = clauses[cid];
        for (int lit : c.lits) {
            int v = lit_var(lit);
            if (v == (p == -1 ? -1 : lit_var(p))) continue;
            if (!seen[v] && levels[v] > 0) {
                seen[v] = 1;
                analyze_stack.push_back(v);
                bump_var(v);
                if (levels[v] == decision_level()) {
                    ++path_count;
                } else {
                    out_learnt.push_back(lit);
                }
            }
        }

        while (idx >= 0 && !seen[lit_var(trail[idx])]) --idx;
        p = trail[idx--];
        int pv = lit_var(p);
        seen[pv] = 0;
        --path_count;

        if (path_count <= 0) break;
        cid = reasons[pv];
        assert(cid >= 0);
    }

    out_learnt[0] = lit_negate(p);

    for (int v : analyze_stack) seen[v] = 0;

    std::sort(out_learnt.begin() + 1, out_learnt.end(), [&](int a, int b) {
        return levels[lit_var(a)] > levels[lit_var(b)];
    });

    out_btlevel = 0;
    for (int i = 1; i < static_cast<int>(out_learnt.size()); ++i) {
        out_btlevel = std::max(out_btlevel, levels[lit_var(out_learnt[i])]);
    }
}

int Solver::pick_branch_lit() {
    int best_clause = -1;
    int best_unassigned = 1 << 30;
    int best_len = 1 << 30;

    for (int cid : positive_clause_ids) {
        const Clause& c = clauses[cid];
        bool satisfied = false;
        int unassigned = 0;
        for (int lit : c.lits) {
            int val = value_lit(lit);
            if (val == 1) {
                satisfied = true;
                break;
            }
            if (val == 0) ++unassigned;
        }
        if (satisfied) continue;
        if (unassigned == 0) return -2;
        if (unassigned < best_unassigned ||
            (unassigned == best_unassigned && static_cast<int>(c.lits.size()) < best_len)) {
            best_unassigned = unassigned;
            best_len = static_cast<int>(c.lits.size());
            best_clause = cid;
            if (unassigned == 1) break;
        }
    }

    if (best_clause < 0) return -1;

    const Clause& row = clauses[best_clause];
    int best_lit = -1;
    double best_score = -1e300;

    for (int lit : row.lits) {
        int v = lit_var(lit);
        if (assigns[v] != 0) continue;

        int residual_cover = 0;
        for (int pcid : pos_occurs[v]) {
            const Clause& pc = clauses[pcid];
            bool sat = false;
            for (int q : pc.lits) {
                if (value_lit(q) == 1) {
                    sat = true;
                    break;
                }
            }
            if (!sat) ++residual_cover;
        }

        double score = 20.0 * residual_cover + activity[v] - 0.03 * conflict_degree[v];
        if (score > best_score) {
            best_score = score;
            best_lit = lit;
        }
    }
    if (best_lit >= 0) return best_lit;

    for (int v = 0; v < nvars; ++v) {
        if (assigns[v] == 0) return mk_lit(v, false);
    }
    return -1;
}

struct LocalSet {
    vector<int> elems;
    vector<int> pos;

    explicit LocalSet(int n = 0) : pos(n, -1) {}

    void reset(int n) {
        elems.clear();
        pos.assign(n, -1);
    }

    bool contains(int x) const { return pos[x] >= 0; }

    void add(int x) {
        if (pos[x] >= 0) return;
        pos[x] = static_cast<int>(elems.size());
        elems.push_back(x);
    }

    void remove(int x) {
        int i = pos[x];
        if (i < 0) return;
        int y = elems.back();
        elems[i] = y;
        pos[y] = i;
        elems.pop_back();
        pos[x] = -1;
    }

    int size() const { return static_cast<int>(elems.size()); }
    bool empty() const { return elems.empty(); }
};

struct SplitMix64 {
    uint64_t state;
    explicit SplitMix64(uint64_t seed) : state(seed) {}
    uint64_t next() {
        uint64_t z = (state += 0x9e3779b97f4a7c15ULL);
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
        z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
        return z ^ (z >> 31);
    }
    int uniform_int(int n) { return static_cast<int>(next() % static_cast<uint64_t>(n)); }
    double uniform01() {
        return (next() >> 11) * (1.0 / 9007199254740992.0);
    }
};

bool Solver::try_local_search(vector<int8_t>& model, double budget_seconds) {
    if (positive_clause_ids.empty() || conflict_edges.empty()) return false;

    const int m = static_cast<int>(positive_clause_ids.size());
    const int e = static_cast<int>(conflict_edges.size());
    uint64_t seed = 0x5eed12345678ULL ^ (static_cast<uint64_t>(nvars) << 32)
                    ^ static_cast<uint64_t>(declared_clauses);
    for (int cid : positive_clause_ids) {
        seed ^= static_cast<uint64_t>(clauses[cid].lits.size() + 17) * 0x9e3779b97f4a7c15ULL;
        seed = (seed << 7) | (seed >> 57);
    }
    SplitMix64 rng(seed);

    vector<char> x(nvars, 0);
    vector<uint16_t> row_count(m, 0);
    vector<int> true_neighbors(nvars, 0);
    vector<int> row_weight(m, 1);
    vector<int> edge_weight(e, 1);
    vector<int> row_id_of_clause(clauses.size(), -1);
    for (int i = 0; i < m; ++i) row_id_of_clause[positive_clause_ids[i]] = i;

    LocalSet bad_rows(m), bad_edges(e);
    vector<int> order(nvars);
    std::iota(order.begin(), order.end(), 0);

    auto deadline = start_time + std::chrono::duration_cast<std::chrono::steady_clock::duration>(
                                    std::chrono::duration<double>(budget_seconds));

    auto initialize = [&](int mode) {
        std::fill(x.begin(), x.end(), 0);
        if (mode == 0) {
            double p = 0.025 + 0.095 * rng.uniform01();
            for (int v = 0; v < nvars; ++v) x[v] = rng.uniform01() < p;
        } else if (mode == 1) {
            vector<char> covered(m, 0), banned(nvars, 0);
            int covered_count = 0;
            for (int i = nvars - 1; i > 0; --i) std::swap(order[i], order[rng.uniform_int(i + 1)]);
            for (int step = 0; step < 90 && covered_count < m; ++step) {
                int best_v = -1;
                int best_gain = 0;
                for (int v : order) {
                    if (x[v] || banned[v]) continue;
                    int gain = 0;
                    for (int cid : pos_occurs[v]) {
                        int rid = row_id_of_clause[cid];
                        if (rid >= 0 && !covered[rid]) ++gain;
                    }
                    if (gain > best_gain || (gain == best_gain && gain > 0 && rng.uniform_int(2) == 0)) {
                        best_gain = gain;
                        best_v = v;
                    }
                }
                if (best_v < 0) break;
                x[best_v] = 1;
                for (auto [u, eid] : conflict_neighbors[best_v]) banned[u] = 1;
                for (int cid : pos_occurs[best_v]) {
                    int rid = row_id_of_clause[cid];
                    if (rid >= 0 && !covered[rid]) {
                        covered[rid] = 1;
                        ++covered_count;
                    }
                }
            }
        } else {
            vector<char> banned(nvars, 0);
            for (int i = nvars - 1; i > 0; --i) std::swap(order[i], order[rng.uniform_int(i + 1)]);
            double p = 0.08 + 0.10 * rng.uniform01();
            for (int v : order) {
                if (!banned[v] && rng.uniform01() < p) {
                    x[v] = 1;
                    for (auto [u, eid] : conflict_neighbors[v]) banned[u] = 1;
                }
            }
        }

        std::fill(row_count.begin(), row_count.end(), 0);
        std::fill(true_neighbors.begin(), true_neighbors.end(), 0);
        std::fill(row_weight.begin(), row_weight.end(), 1);
        std::fill(edge_weight.begin(), edge_weight.end(), 1);
        bad_rows.reset(m);
        bad_edges.reset(e);

        for (int rid = 0; rid < m; ++rid) {
            const Clause& c = clauses[positive_clause_ids[rid]];
            int cnt = 0;
            for (int lit : c.lits) cnt += x[lit_var(lit)] ? 1 : 0;
            row_count[rid] = static_cast<uint16_t>(cnt);
            if (cnt == 0) bad_rows.add(rid);
        }
        for (int v = 0; v < nvars; ++v) {
            int cnt = 0;
            for (auto [u, eid] : conflict_neighbors[v]) cnt += x[u] ? 1 : 0;
            true_neighbors[v] = cnt;
        }
        for (int eid = 0; eid < e; ++eid) {
            auto [a, b] = conflict_edges[eid];
            if (x[a] && x[b]) bad_edges.add(eid);
        }
    };

    auto flip_var = [&](int v, long long& cost) {
        if (!x[v]) {
            x[v] = 1;
            for (int cid : pos_occurs[v]) {
                int rid = row_id_of_clause[cid];
                if (rid < 0) continue;
                if (row_count[rid] == 0) {
                    bad_rows.remove(rid);
                    cost -= row_weight[rid];
                }
                ++row_count[rid];
            }
            for (auto [u, eid] : conflict_neighbors[v]) {
                if (x[u]) {
                    bad_edges.add(eid);
                    cost += edge_weight[eid];
                }
                ++true_neighbors[u];
            }
        } else {
            x[v] = 0;
            for (int cid : pos_occurs[v]) {
                int rid = row_id_of_clause[cid];
                if (rid < 0) continue;
                --row_count[rid];
                if (row_count[rid] == 0) {
                    bad_rows.add(rid);
                    cost += row_weight[rid];
                }
            }
            for (auto [u, eid] : conflict_neighbors[v]) {
                if (x[u]) {
                    bad_edges.remove(eid);
                    cost -= edge_weight[eid];
                }
                --true_neighbors[u];
            }
        }
    };

    const int max_steps_per_try = std::max(250000, 2400 * nvars);
    int tries = 0;
    while (std::chrono::steady_clock::now() < deadline) {
        initialize(tries % 3);
        long long cost = bad_rows.size() + bad_edges.size();
        int best_bad_count = bad_rows.size() + bad_edges.size();
        int stale = 0;

        for (int step = 0; step < max_steps_per_try; ++step) {
            int bad_count = bad_rows.size() + bad_edges.size();
            if (bad_count == 0) {
                model.assign(nvars, -1);
                for (int v = 0; v < nvars; ++v) model[v] = x[v] ? 1 : -1;
                return verify_model(model);
            }
            if ((step & 8191) == 0 && std::chrono::steady_clock::now() >= deadline) return false;
            if ((step % 2000) == 0 && stale > 5000) {
                for (int rid : bad_rows.elems) {
                    ++row_weight[rid];
                    ++cost;
                }
                for (int eid : bad_edges.elems) {
                    ++edge_weight[eid];
                    ++cost;
                }
            }

            bool choose_row = !bad_rows.empty() &&
                              (bad_edges.empty() || rng.uniform_int(100) < 62);
            const vector<int>* candidates = nullptr;
            int edge_u = -1, edge_v = -1;
            if (choose_row) {
                int rid = bad_rows.elems[rng.uniform_int(bad_rows.size())];
                candidates = &clauses[positive_clause_ids[rid]].lits;
            } else {
                int eid = bad_edges.elems[rng.uniform_int(bad_edges.size())];
                edge_u = conflict_edges[eid].first;
                edge_v = conflict_edges[eid].second;
            }

            long long best_delta = (1LL << 60);
            int best_vars[128];
            int best_count = 0;

            auto consider = [&](int v) {
                long long delta = 0;
                if (!x[v]) {
                    for (int cid : pos_occurs[v]) {
                        int rid = row_id_of_clause[cid];
                        if (rid >= 0 && row_count[rid] == 0) delta -= row_weight[rid];
                    }
                    for (auto [u, eid] : conflict_neighbors[v]) {
                        if (x[u]) delta += edge_weight[eid];
                    }
                } else {
                    for (int cid : pos_occurs[v]) {
                        int rid = row_id_of_clause[cid];
                        if (rid >= 0 && row_count[rid] == 1) delta += row_weight[rid];
                    }
                    for (auto [u, eid] : conflict_neighbors[v]) {
                        if (x[u]) delta -= edge_weight[eid];
                    }
                }
                if (delta < best_delta) {
                    best_delta = delta;
                    best_count = 0;
                    best_vars[best_count++] = v;
                } else if (delta == best_delta && best_count < 128) {
                    best_vars[best_count++] = v;
                }
            };

            if (candidates) {
                if (rng.uniform_int(100) < 3) {
                    int lit = (*candidates)[rng.uniform_int(static_cast<int>(candidates->size()))];
                    best_vars[0] = lit_var(lit);
                    best_count = 1;
                } else {
                    for (int lit : *candidates) consider(lit_var(lit));
                }
            } else {
                if (rng.uniform_int(100) < 3) {
                    best_vars[0] = rng.uniform_int(2) == 0 ? edge_u : edge_v;
                    best_count = 1;
                } else {
                    consider(edge_u);
                    consider(edge_v);
                }
            }

            int v = best_vars[rng.uniform_int(best_count)];
            flip_var(v, cost);

            bad_count = bad_rows.size() + bad_edges.size();
            if (bad_count < best_bad_count) {
                best_bad_count = bad_count;
                stale = 0;
            } else if (++stale > 350000) {
                break;
            }
        }
        ++tries;
    }
    return false;
}

bool Solver::all_original_satisfied_with_false_tail(vector<int8_t>& model) {
    model = assigns;
    for (int v = 0; v < nvars; ++v) {
        if (model[v] == 0) model[v] = -1;
    }
    return verify_model(model);
}

bool Solver::verify_model(const vector<int8_t>& model) const {
    if (static_cast<int>(model.size()) != nvars) return false;
    for (int i = 0; i < declared_clauses; ++i) {
        const Clause& c = clauses[i];
        bool sat = false;
        for (int lit : c.lits) {
            int8_t av = model[lit_var(lit)];
            if ((av == 1 && !lit_neg(lit)) || (av == -1 && lit_neg(lit))) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

void Solver::print_model(const vector<int8_t>& model) const {
    std::printf("s SATISFIABLE\n");
    int col = 0;
    std::printf("v");
    for (int v = 0; v < nvars; ++v) {
        int lit = (model[v] == 1) ? (v + 1) : -(v + 1);
        std::printf(" %d", lit);
        if (++col == 20) {
            std::printf(" 0\n");
            if (v + 1 < nvars) std::printf("v");
            col = 0;
        }
    }
    if (col != 0) std::printf(" 0\n");
}

bool Solver::timed_out() const {
    using clock = std::chrono::steady_clock;
    double elapsed = std::chrono::duration<double>(clock::now() - start_time).count();
    return elapsed > time_limit;
}

void Solver::open_proof(const char* path) {
    proof.open(path, std::ios::out | std::ios::trunc);
    if (!proof) return;
    proof_enabled = true;
    proof << "pseudo-Boolean proof version 3.0\n\n";
}

void Solver::write_rup_clause(const vector<int>& lits) {
    if (!proof_enabled) return;
    proof << "rup ";
    for (int lit : lits) {
        proof << "1 ";
        if (lit_neg(lit)) proof << "~";
        proof << "x" << (lit_var(lit) + 1) << " ";
    }
    proof << ">= 1 ;\n";
    ++learnt_count;
}

void Solver::write_unsat_footer(int final_id) {
    if (!proof_enabled) return;
    proof << "\noutput NONE ;\n";
    proof << "conclusion UNSAT : " << final_id << " ;\n";
    proof << "end pseudo-Boolean proof ;\n";
    proof.flush();
}

int Solver::solve(const char* proof_path) {
    start_time = std::chrono::steady_clock::now();
    init_solver_state();

    vector<int8_t> model;
    double local_budget = (nvars <= 600) ? 8.0 : 90.0;
    if (try_local_search(model, local_budget)) {
        print_model(model);
        return 10;
    }

    open_proof(proof_path);

    int confl = propagate();
    if (confl >= 0) {
        vector<int> empty;
        write_rup_clause(empty);
        write_unsat_footer(-1);
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }

    long long next_restart = 100;
    long long restart_inc = 150;

    vector<int> learnt;
    while (true) {
        if ((conflicts & 1023LL) == 0 && timed_out()) {
            std::printf("s UNKNOWN\n");
            return 0;
        }

        confl = propagate();
        if (confl >= 0) {
            ++conflicts;
            if (decision_level() == 0) {
                vector<int> empty;
                write_rup_clause(empty);
                write_unsat_footer(-1);
                std::printf("s UNSATISFIABLE\n");
                return 20;
            }

            int btlevel = 0;
            analyze(confl, learnt, btlevel);

            write_rup_clause(learnt);
            decay_vars();
            decay_clauses();

            cancel_until(btlevel);
            int cid = add_clause(vector<int>(learnt.begin(), learnt.end()), true);
            if (!enqueue(learnt[0], cid)) {
                vector<int> empty;
                write_rup_clause(empty);
                write_unsat_footer(-1);
                std::printf("s UNSATISFIABLE\n");
                return 20;
            }

            if (conflicts >= next_restart) {
                cancel_until(0);
                next_restart += restart_inc;
                restart_inc = static_cast<long long>(restart_inc * 1.25) + 50;
            }
            continue;
        }

        vector<int8_t> model;
        int branch = pick_branch_lit();
        if (branch == -2) {
            // A positive row is empty under the current assignment; propagation should
            // normally find this first, so force another propagation cycle.
            std::printf("s UNKNOWN\n");
            return 0;
        }
        if (branch == -1) {
            if (all_original_satisfied_with_false_tail(model)) {
                print_model(model);
                return 10;
            }
            std::printf("s UNKNOWN\n");
            return 0;
        }

        ++decisions;
        new_decision_level();
        if (!enqueue(branch, -1)) {
            std::printf("s UNKNOWN\n");
            return 0;
        }
    }
}

} // namespace

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

    Solver solver;
    if (!solver.parse_dimacs(argv[1])) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    if (!solver.detect_family()) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    return solver.solve(argv[2]);
}
