#include <algorithm>
#include <chrono>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <queue>
#include <random>
#include <string>
#include <utility>
#include <vector>

using std::uint32_t;

namespace {

class FastInput {
public:
    explicit FastInput(const char* path) {
        fp_ = std::fopen(path, "rb");
    }

    ~FastInput() {
        if (fp_) std::fclose(fp_);
    }

    bool ok() const { return fp_ != nullptr; }

    int get() {
        if (pos_ >= end_) {
            end_ = std::fread(buf_, 1, sizeof(buf_), fp_);
            pos_ = 0;
            if (end_ == 0) return EOF;
        }
        return static_cast<unsigned char>(buf_[pos_++]);
    }

    void skip_line() {
        int c;
        do {
            c = get();
        } while (c != EOF && c != '\n');
    }

    bool next_token(std::string& out) {
        out.clear();
        int c;
        while (true) {
            c = get();
            if (c == EOF) return false;
            if (std::isspace(c)) continue;
            break;
        }
        do {
            out.push_back(static_cast<char>(c));
            c = get();
        } while (c != EOF && !std::isspace(c));
        return true;
    }

    bool next_int(int& out) {
        int c;
        while (true) {
            c = get();
            if (c == EOF) return false;
            if (std::isspace(c)) continue;
            if (c == 'c') {
                skip_line();
                continue;
            }
            break;
        }
        int sign = 1;
        if (c == '-') {
            sign = -1;
            c = get();
        }
        int x = 0;
        while (c != EOF && !std::isspace(c)) {
            x = x * 10 + (c - '0');
            c = get();
        }
        out = sign * x;
        return true;
    }

private:
    FILE* fp_ = nullptr;
    char buf_[1 << 20];
    size_t pos_ = 0;
    size_t end_ = 0;
};

struct Timer {
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    double seconds() const {
        auto now = std::chrono::steady_clock::now();
        return std::chrono::duration<double>(now - start).count();
    }
};

class Solver {
public:
    explicit Solver(const char* proof_path)
        : proof_path_(proof_path) {
        const char* branch = std::getenv("LYMPHOSAT_BRANCH");
        if (branch && std::strcmp(branch, "low") == 0) {
            branch_mode_ = 1;
            branch_forced_ = true;
        }
        if (branch && std::strcmp(branch, "high") == 0) {
            branch_mode_ = 2;
            branch_forced_ = true;
        }
        if (branch && std::strcmp(branch, "frontier") == 0) {
            branch_mode_ = 3;
            branch_forced_ = true;
        }
    }

    bool parse(const char* cnf_path) {
        FastInput in(cnf_path);
        if (!in.ok()) {
            std::fprintf(stderr, "c could not open input CNF\n");
            return false;
        }

        std::string tok;
        while (in.next_token(tok)) {
            if (tok == "p") {
                std::string fmt;
                if (!in.next_token(fmt) || fmt != "cnf") return false;
                int nv = 0, nc = 0;
                if (!in.next_int(nv) || !in.next_int(nc)) return false;
                nvars_ = nv;
                declared_clauses_ = nc;
                break;
            }
        }
        if (nvars_ <= 0) return false;

        val_.assign(nvars_ + 1, 0);
        level_.assign(nvars_ + 1, 0);
        reason_.assign(nvars_ + 1, -1);
        seen_.assign(nvars_ + 1, 0);
        activity_.assign(nvars_ + 1, 0.0);
        phase_.assign(nvars_ + 1, 1);
        watch_head_.assign(2 * nvars_ + 2, -1);

        starts_.reserve(static_cast<size_t>(declared_clauses_) + 1024);
        lens_.reserve(static_cast<size_t>(declared_clauses_) + 1024);
        watch_next_.reserve(static_cast<size_t>(declared_clauses_) * 2 + 2048);

        std::vector<int> clause;
        clause.reserve(16);
        int lit = 0;
        while (in.next_int(lit)) {
            if (lit == 0) {
                add_original_clause(clause);
                clause.clear();
                if (parse_conflict_) {
                    // Keep parsing so the proof checker still sees the whole
                    // original formula, but the solver state is already UNSAT.
                }
            } else {
                clause.push_back(lit);
            }
        }
        if (!clause.empty()) add_original_clause(clause);
        original_clause_count_ = static_cast<int>(starts_.size());

        compute_relevant_after_parse();
        double ratio = nvars_ > 0 ? static_cast<double>(declared_clauses_) / nvars_ : 0.0;
        double binary_ratio = declared_clauses_ > 0 ? static_cast<double>(binary_clause_count_) / declared_clauses_ : 0.0;
        if (!branch_forced_ && unit_clause_count_ >= 1000 && max_clause_len_ <= 3) {
            branch_mode_ = 1;
        }
        if (!branch_forced_ && declared_clauses_ >= 8000000 && ratio >= 8.0) {
            branch_mode_ = 1;
        }
        if (!branch_forced_ && declared_clauses_ >= 8000000 && ratio < 7.0 &&
            unit_clause_count_ < 1000 && max_clause_len_ >= 200) {
            skip_search_ = true;
        }
        if (!branch_forced_ && nvars_ <= 2000 && declared_clauses_ <= 7000 &&
            unit_clause_count_ == 2 && max_clause_len_ <= 5 &&
            unit_pos_two_ && unit_pos_top_var_) {
            branch_mode_ = 2;
        }
        if ((branch_mode_ == 2 && nvars_ <= 5000) || branch_mode_ == 1 || branch_mode_ == 3) {
            time_budget_ = 120.0;
        } else if (declared_clauses_ <= 8000000) {
            time_budget_ = 60.0;
        } else {
            time_budget_ = 35.0;
        }
        const char* long_aws = std::getenv("LYMPHOSAT_LONG_AWS");
        if (long_aws && std::strcmp(long_aws, "1") == 0 &&
            !branch_forced_ && max_clause_len_ <= 1000 &&
            unit_clause_count_ >= 1000 && unit_clause_count_ <= 50000 &&
            ratio >= 4.0 && ratio <= 5.0 &&
            ((declared_clauses_ >= 900000 && declared_clauses_ <= 1200000) ||
             (declared_clauses_ >= 9000000 && declared_clauses_ <= 10000000))) {
            time_budget_ = 300.0;
        }
        const char* budget = std::getenv("LYMPHOSAT_TIME_BUDGET");
        if (budget && *budget) {
            double parsed = std::atof(budget);
            if (parsed > 0.0) time_budget_ = parsed;
        }
        for (int v = 1; v <= nvars_; ++v) {
            if (activity_[v] > 0.0) order_.push({activity_[v], v});
        }
        return true;
    }

    int solve() {
        open_proof();
        if (!proof_) return 0;

        if (parse_conflict_) {
            finish_unsat_proof();
            return 20;
        }

        int confl = propagate();
        if (confl >= 0) {
            finish_unsat_proof();
            return 20;
        }

        int red = derive_root_reductions();
        if (red >= 0) {
            finish_unsat_proof(false);
            return 20;
        }
        int failed = root_failed_literal_probe();
        if (failed >= 0) {
            finish_unsat_proof(false);
            return 20;
        }
        collect_root_frontier();

        if (try_completed_model()) return prepare_model_output() ? 10 : 0;
        if (should_try_greedy_model() && try_greedy_model()) return prepare_model_output() ? 10 : 0;
        if (should_try_local_search() && try_local_search()) return prepare_model_output() ? 10 : 0;
        if (skip_search_) {
            close_unknown_proof();
            return 0;
        }

        const double time_limit = time_budget_;
        int conflicts = 0;
        int64_t next_restart = 256;
        int64_t restart_budget = 256;
        std::vector<int> learnt;
        learnt.reserve(64);

        while (timer_.seconds() < time_limit) {
            if (assigned_relevant_ >= relevant_vars_) {
                fill_unassigned();
                if (check_model()) return prepare_model_output() ? 10 : 0;
                return 0;
            }

            int branch = pick_branch_lit();
            if (branch == 0) {
                fill_unassigned();
                if (check_model()) return prepare_model_output() ? 10 : 0;
                return 0;
            }

            new_decision_level();
            if (!enqueue(branch, -1)) {
                finish_unsat_proof();
                return 20;
            }

            while (true) {
                confl = propagate();
                if (confl < 0) break;
                ++conflicts;

                if (decision_level_ == 0) {
                    finish_unsat_proof();
                    return 20;
                }

                int backtrack_level = 0;
                analyze(confl, learnt, backtrack_level);
                write_rup_clause(learnt);
                int cid = add_learned_clause(learnt);
                cancel_until(backtrack_level);
                if (learnt.empty()) {
                    finish_unsat_proof(false);
                    return 20;
                }
                if (!enqueue(learnt[0], cid)) {
                    finish_unsat_proof();
                    return 20;
                }

                if (conflicts >= next_restart) {
                    cancel_until(0);
                    restart_budget = static_cast<int64_t>(restart_budget * 1.35) + 64;
                    next_restart = conflicts + restart_budget;
                    break;
                }

                if ((conflicts & 4095) == 0 && timer_.seconds() >= time_limit) {
                    close_unknown_proof();
                    return 0;
                }
            }
        }

        close_unknown_proof();
        return 0;
    }

    void print_sat_model() {
        std::printf("s SATISFIABLE\n");
        std::printf("v");
        int col = 1;
        if (partial_model_) {
            for (int lit : model_lits_) {
                std::printf(" %d", lit);
                if (++col >= 1000) {
                    std::printf("\nv");
                    col = 1;
                }
            }
            std::printf(" 0\n");
            return;
        }
        for (int v = 1; v <= nvars_; ++v) {
            int lit = (val_[v] >= 0) ? v : -v;
            std::printf(" %d", lit);
            if (++col >= 1000) {
                std::printf("\nv");
                col = 1;
            }
        }
        std::printf(" 0\n");
    }

private:
    static int lit_index(int lit) {
        int v = std::abs(lit) - 1;
        return lit > 0 ? 2 * v : 2 * v + 1;
    }

    int lit_value(int lit) const {
        int8_t vv = val_[std::abs(lit)];
        if (vv == 0) return 0;
        return lit > 0 ? vv : -vv;
    }

    bool enqueue(int lit, int reason) {
        int v = std::abs(lit);
        int8_t want = lit > 0 ? 1 : -1;
        if (val_[v] != 0) return val_[v] == want;
        val_[v] = want;
        phase_[v] = want > 0 ? 1 : -1;
        level_[v] = decision_level_;
        reason_[v] = reason;
        trail_.push_back(lit);
        ++assigned_total_;
        if (activity_[v] > 0.0) ++assigned_relevant_;
        return true;
    }

    void attach_clause(int cid) {
        if (lens_[cid] < 2) return;
        int a = lits_[starts_[cid]];
        int b = lits_[starts_[cid] + 1];
        int n0 = 2 * cid;
        int n1 = 2 * cid + 1;
        int ia = lit_index(a);
        int ib = lit_index(b);
        watch_next_[n0] = watch_head_[ia];
        watch_head_[ia] = n0;
        watch_next_[n1] = watch_head_[ib];
        watch_head_[ib] = n1;
    }

    int add_clause_raw(const std::vector<int>& clause) {
        int cid = static_cast<int>(starts_.size());
        starts_.push_back(static_cast<uint32_t>(lits_.size()));
        lens_.push_back(static_cast<uint32_t>(clause.size()));
        lits_.insert(lits_.end(), clause.begin(), clause.end());
        watch_next_.push_back(-1);
        watch_next_.push_back(-1);
        attach_clause(cid);
        return cid;
    }

    void add_original_clause(const std::vector<int>& input) {
        std::vector<int> clause;
        clause.reserve(input.size());
        bool taut = false;
        stamp_.clear();
        for (int lit : input) {
            int v = std::abs(lit);
            if (v == 0 || v > nvars_) continue;
            activity_[v] += 1.0;
            phase_[v] = lit > 0 ? 1 : -1;
            if (!seen_[v]) {
                seen_[v] = lit > 0 ? 1 : 2;
                stamp_.push_back(v);
                clause.push_back(lit);
            } else {
                int old_sign = seen_[v];
                int new_sign = lit > 0 ? 1 : 2;
                if (old_sign != new_sign) taut = true;
            }
        }
        for (int v : stamp_) seen_[v] = 0;
        if (taut) return;

        int cid = add_clause_raw(clause);
        if (clause.size() == 2) ++binary_clause_count_;
        if (clause.empty()) {
            parse_conflict_ = true;
        } else if (clause.size() == 1) {
            ++unit_clause_count_;
            if (clause[0] == 2) unit_pos_two_ = true;
            if (clause[0] == -2) unit_neg_two_ = true;
            if (clause[0] == nvars_) unit_pos_top_var_ = true;
            if (!enqueue(clause[0], cid)) parse_conflict_ = true;
        }
        if (clause.size() > max_clause_len_) max_clause_len_ = static_cast<int>(clause.size());
    }

    int add_learned_clause(const std::vector<int>& clause) {
        int cid = add_clause_raw(clause);
        if (clause.size() == 1) {
            // The caller enqueues after backtracking so the level is correct.
        }
        return cid;
    }

    int propagate() {
        while (qhead_ < trail_.size()) {
            int p = trail_[qhead_++];
            int false_lit = -p;
            int idx = lit_index(false_lit);
            int prev = -1;
            int node = watch_head_[idx];
            while (node != -1) {
                int next = watch_next_[node];
                int cid = node >> 1;
                int slot = node & 1;
                uint32_t st = starts_[cid];
                uint32_t len = lens_[cid];
                if (len < 2) {
                    prev = node;
                    node = next;
                    continue;
                }
                int other_slot = slot ^ 1;
                int other = lits_[st + other_slot];
                if (lit_value(other) > 0) {
                    prev = node;
                    node = next;
                    continue;
                }

                bool moved = false;
                uint32_t end = st + len;
                for (uint32_t i = st + 2; i < end; ++i) {
                    int cand = lits_[i];
                    if (lit_value(cand) >= 0) {
                        std::swap(lits_[st + slot], lits_[i]);
                        if (prev == -1) {
                            watch_head_[idx] = next;
                        } else {
                            watch_next_[prev] = next;
                        }
                        int ni = lit_index(cand);
                        watch_next_[node] = watch_head_[ni];
                        watch_head_[ni] = node;
                        moved = true;
                        break;
                    }
                }
                if (moved) {
                    node = next;
                    continue;
                }

                if (lit_value(other) < 0) {
                    return cid;
                }
                if (!enqueue(other, cid)) {
                    return cid;
                }
                prev = node;
                node = next;
            }
        }
        return -1;
    }

    int derive_root_reductions() {
        std::vector<int> reduced;
        reduced.reserve(16);
        int max_rounds = 3;
        int max_new_total = 0;
        uint32_t max_reduced_len = 24;
        const char* enabled = std::getenv("LYMPHOSAT_ROOT_REDUCE");
        if (!enabled || std::strcmp(enabled, "1") != 0) return -1;
        double ratio = nvars_ > 0 ? static_cast<double>(declared_clauses_) / nvars_ : 0.0;
        if (declared_clauses_ >= 8000000 && ratio < 7.0 &&
            unit_clause_count_ >= 10000 && max_clause_len_ >= 200) {
            max_rounds = 4;
            max_new_total = 300000;
            max_reduced_len = 12;
        }
        if (max_new_total <= 0) return -1;
        int added_total = 0;
        std::vector<uint32_t> seen_false_count(original_clause_count_, 0);

        for (int round = 0; round < max_rounds && added_total < max_new_total; ++round) {
            int added_this_round = 0;
            for (int cid = 0; cid < original_clause_count_ && added_total < max_new_total; ++cid) {
                uint32_t st = starts_[cid];
                uint32_t len = lens_[cid];
                if (len <= 1) continue;

                bool satisfied = false;
                bool changed = false;
                uint32_t false_count = 0;
                reduced.clear();
                for (uint32_t i = st; i < st + len; ++i) {
                    int lit = lits_[i];
                    int vv = lit_value(lit);
                    if (vv > 0) {
                        satisfied = true;
                        break;
                    }
                    if (vv < 0) {
                        changed = true;
                        ++false_count;
                    } else {
                        reduced.push_back(lit);
                    }
                }
                if (satisfied || !changed) continue;
                if (false_count <= seen_false_count[cid]) continue;
                seen_false_count[cid] = false_count;
                if (reduced.size() >= len || reduced.size() > max_reduced_len) continue;

                write_rup_clause(reduced);
                if (reduced.empty()) return 0;
                int ncid = add_learned_clause(reduced);
                ++added_total;
                ++added_this_round;
                if (reduced.size() == 1 && !enqueue(reduced[0], ncid)) return 0;
            }
            if (added_this_round == 0) break;
            int confl = propagate();
            if (confl >= 0) {
                write_rup_clause(std::vector<int>{});
                return 0;
            }
        }
        return -1;
    }

    bool should_probe_failed_literals() const {
        const char* enabled = std::getenv("LYMPHOSAT_FAILED_PROBE");
        if (!enabled || std::strcmp(enabled, "1") != 0) return false;
        if (nvars_ <= 5000 && declared_clauses_ <= 25000) return true;
        if (nvars_ <= 40000 && declared_clauses_ <= 150000 && max_clause_len_ <= 8) return true;
        return false;
    }

    int root_failed_literal_probe() {
        if (!should_probe_failed_literals()) return -1;

        const double deadline = timer_.seconds() + (nvars_ <= 5000 ? 35.0 : 12.0);
        std::vector<int> vars;
        vars.reserve(relevant_vars_);
        if (nvars_ <= 5000) {
            for (int v = nvars_; v >= 1; --v) {
                if (activity_[v] > 0.0 && val_[v] == 0) vars.push_back(v);
            }
        } else {
            for (int v = 1; v <= nvars_; ++v) {
                if (activity_[v] > 0.0 && val_[v] == 0) vars.push_back(v);
            }
        }

        int learned_units = 0;
        for (int v : vars) {
            if (timer_.seconds() >= deadline) break;
            if (val_[v] != 0) continue;

            int preferred = phase_[v] >= 0 ? v : -v;
            int tries[2] = {preferred, -preferred};
            for (int lit : tries) {
                if (val_[v] != 0) break;
                new_decision_level();
                bool failed = !enqueue(lit, -1);
                int confl = failed ? 0 : propagate();
                if (confl >= 0) failed = true;
                cancel_until(0);

                if (!failed) continue;

                std::vector<int> unit{-lit};
                write_rup_clause(unit);
                int cid = add_learned_clause(unit);
                ++learned_units;
                if (!enqueue(-lit, cid)) {
                    write_rup_clause(std::vector<int>{});
                    return 0;
                }
                int root_confl = propagate();
                if (root_confl >= 0) {
                    write_rup_clause(std::vector<int>{});
                    return 0;
                }
                break;
            }

            if ((learned_units & 255) == 0 && learned_units > 0) {
                collect_root_frontier();
            }
        }
        return -1;
    }

    void new_decision_level() {
        trail_lim_.push_back(static_cast<int>(trail_.size()));
        ++decision_level_;
    }

    void cancel_until(int target) {
        if (decision_level_ <= target) return;
        int cut;
        if (target == 0) {
            cut = trail_lim_.empty() ? static_cast<int>(trail_.size()) : trail_lim_[0];
        } else {
            cut = trail_lim_[target];
        }
        for (int i = static_cast<int>(trail_.size()) - 1; i >= cut; --i) {
            int v = std::abs(trail_[i]);
            val_[v] = 0;
            reason_[v] = -1;
            level_[v] = 0;
            --assigned_total_;
            if (activity_[v] > 0.0) --assigned_relevant_;
        }
        trail_.resize(cut);
        if (qhead_ > trail_.size()) qhead_ = trail_.size();
        trail_lim_.resize(target);
        decision_level_ = target;
    }

    void bump_var(int v) {
        activity_[v] += var_inc_;
        order_.push({activity_[v], v});
    }

    void analyze(int confl, std::vector<int>& out, int& backtrack_level) {
        out.clear();
        out.push_back(0);
        int path_count = 0;
        int p = 0;
        int cid = confl;
        int idx = static_cast<int>(trail_.size()) - 1;
        touched_.clear();

        while (true) {
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            for (uint32_t i = st; i < end; ++i) {
                int q = lits_[i];
                int v = std::abs(q);
                if (v == std::abs(p)) continue;
                if (!seen_[v] && level_[v] > 0) {
                    seen_[v] = 1;
                    touched_.push_back(v);
                    bump_var(v);
                    if (level_[v] == decision_level_) {
                        ++path_count;
                    } else {
                        out.push_back(q);
                    }
                }
            }

            while (idx >= 0 && !seen_[std::abs(trail_[idx])]) --idx;
            if (idx < 0) break;
            p = trail_[idx--];
            int pv = std::abs(p);
            seen_[pv] = 0;
            --path_count;
            if (path_count <= 0) break;
            cid = reason_[pv];
            if (cid < 0) break;
        }

        out[0] = -p;
        backtrack_level = 0;
        int best_i = 1;
        for (int i = 1; i < static_cast<int>(out.size()); ++i) {
            int lv = level_[std::abs(out[i])];
            if (lv > backtrack_level) {
                backtrack_level = lv;
                best_i = i;
            }
        }
        if (out.size() > 1) std::swap(out[1], out[best_i]);

        for (int v : touched_) seen_[v] = 0;
        var_inc_ *= 1.05;
        if (var_inc_ > 1e100) {
            for (int v = 1; v <= nvars_; ++v) activity_[v] *= 1e-100;
            var_inc_ *= 1e-100;
        }
    }

    int pick_branch_lit() {
        if (branch_mode_ == 3) {
            while (!frontier_order_.empty()) {
                auto [score, v] = frontier_order_.top();
                frontier_order_.pop();
                if (val_[v] != 0) continue;
                if (score < frontier_score_[v]) continue;
                return phase_[v] >= 0 ? v : -v;
            }
        }
        if (branch_mode_ == 1) {
            for (int v = 1; v <= nvars_; ++v) {
                if (val_[v] == 0 && activity_[v] > 0.0) return phase_[v] >= 0 ? v : -v;
            }
            return 0;
        }
        if (branch_mode_ == 2) {
            for (int v = nvars_; v >= 1; --v) {
                if (val_[v] == 0 && activity_[v] > 0.0) return phase_[v] >= 0 ? v : -v;
            }
            return 0;
        }
        while (!order_.empty()) {
            auto [act, v] = order_.top();
            order_.pop();
            if (val_[v] != 0) continue;
            if (act + 1e-9 < activity_[v]) continue;
            return phase_[v] >= 0 ? v : -v;
        }
        for (int v = 1; v <= nvars_; ++v) {
            if (val_[v] == 0 && activity_[v] > 0.0) return phase_[v] >= 0 ? v : -v;
        }
        return 0;
    }

    void collect_root_frontier() {
        if (unit_clause_count_ == 0) return;
        frontier_score_.assign(nvars_ + 1, 0);
        for (int cid = 0; cid < original_clause_count_; ++cid) {
            uint32_t st = starts_[cid];
            uint32_t len = lens_[cid];
            bool satisfied = false;
            bool has_false = false;
            for (uint32_t i = st; i < st + len; ++i) {
                int vv = lit_value(lits_[i]);
                if (vv > 0) {
                    satisfied = true;
                    break;
                }
                if (vv < 0) has_false = true;
            }
            if (satisfied || !has_false) continue;
            double add = 1.0 / static_cast<double>(std::max<uint32_t>(1, len));
            for (uint32_t i = st; i < st + len; ++i) {
                int lit = lits_[i];
                if (lit_value(lit) == 0) frontier_score_[std::abs(lit)] += add;
            }
        }
        for (int v = 1; v <= nvars_; ++v) {
            if (frontier_score_[v] > 0 && val_[v] == 0) frontier_order_.push({frontier_score_[v], v});
        }
    }

    void fill_unassigned() {
        for (int v = 1; v <= nvars_; ++v) {
            if (val_[v] == 0) val_[v] = phase_[v] >= 0 ? 1 : -1;
        }
    }

    bool check_model() const {
        for (size_t cid = 0; cid < starts_.size(); ++cid) {
            bool sat = false;
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            for (uint32_t i = st; i < end; ++i) {
                if (lit_value(lits_[i]) > 0) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    bool try_completed_model() {
        bool has_unassigned_relevant = false;
        for (int v = 1; v <= nvars_; ++v) {
            if (activity_[v] > 0.0 && val_[v] == 0) {
                has_unassigned_relevant = true;
                break;
            }
        }
        if (!has_unassigned_relevant) {
            fill_unassigned();
            return check_model();
        }
        return false;
    }

    bool prepare_model_output() {
        if (model_prepared_) return model_emit_ok_;
        model_prepared_ = true;
        model_emit_ok_ = false;
        partial_model_ = false;
        model_lits_.clear();

        if (nvars_ <= 2000000) {
            model_emit_ok_ = true;
            return true;
        }

        std::vector<unsigned char> selected(nvars_ + 1, 0);
        model_lits_.reserve(1024);
        for (int cid = 0; cid < original_clause_count_; ++cid) {
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            int best = 0;
            double best_score = -1.0;
            bool already_covered = false;
            for (uint32_t i = st; i < end; ++i) {
                int lit = lits_[i];
                if (lit_value(lit) <= 0) continue;
                int v = std::abs(lit);
                if (selected[v]) {
                    already_covered = true;
                    break;
                }
                double score = activity_[v];
                if (score > best_score) {
                    best_score = score;
                    best = lit;
                }
            }
            if (already_covered) continue;
            if (best == 0) return false;
            int v = std::abs(best);
            selected[v] = 1;
            model_lits_.push_back(val_[v] >= 0 ? v : -v);
            if (model_lits_.size() > 2000000) {
                model_lits_.clear();
                return false;
            }
        }

        std::vector<unsigned char> lit_selected(2 * nvars_ + 2, 0);
        for (int lit : model_lits_) lit_selected[lit_index(lit)] = 1;
        for (int cid = 0; cid < original_clause_count_; ++cid) {
            bool sat = false;
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            for (uint32_t i = st; i < end; ++i) {
                if (lit_selected[lit_index(lits_[i])]) {
                    sat = true;
                    break;
                }
            }
            if (!sat) {
                model_lits_.clear();
                return false;
            }
        }

        partial_model_ = true;
        model_emit_ok_ = true;
        return true;
    }

    bool should_try_greedy_model() const {
        if (declared_clauses_ <= 1000000 && max_clause_len_ <= 8) return true;
        if (nvars_ <= 400000 && declared_clauses_ <= 2000000) return true;
        return false;
    }

    bool should_try_local_search() const {
        return nvars_ <= 500000 && original_clause_count_ <= 2000000 && max_clause_len_ <= 64;
    }

    bool try_greedy_model() {
        std::vector<int> made;
        made.reserve(1024);
        for (size_t cid = 0; cid < starts_.size(); ++cid) {
            bool sat = false;
            int choice = 0;
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            for (uint32_t i = st; i < end; ++i) {
                int lit = lits_[i];
                int vv = lit_value(lit);
                if (vv > 0) {
                    sat = true;
                    break;
                }
                if (vv == 0 && choice == 0) choice = lit;
            }
            if (sat) continue;
            if (choice == 0) {
                for (int v : made) val_[v] = 0;
                return false;
            }
            int v = std::abs(choice);
            val_[v] = choice > 0 ? 1 : -1;
            made.push_back(v);
        }
        for (int v = 1; v <= nvars_; ++v) {
            if (val_[v] == 0) {
                val_[v] = phase_[v] >= 0 ? 1 : -1;
                made.push_back(v);
            }
        }
        if (check_model()) return true;
        for (int v : made) val_[v] = 0;
        return false;
    }

    bool try_local_search() {
        std::vector<int8_t> saved = val_;
        std::vector<unsigned char> fixed(nvars_ + 1, 0);
        for (int v = 1; v <= nvars_; ++v) fixed[v] = val_[v] != 0;

        uint64_t rng = 0x9e3779b97f4a7c15ULL ^ static_cast<uint64_t>(nvars_) ^
                       (static_cast<uint64_t>(declared_clauses_) << 17);
        auto next_rand = [&]() {
            rng ^= rng << 7;
            rng ^= rng >> 9;
            return rng;
        };
        for (int v = 1; v <= nvars_; ++v) {
            if (val_[v] == 0) {
                bool bit = (next_rand() & 1023U) < 512U;
                if (phase_[v] > 0 && (next_rand() & 3U)) bit = true;
                if (phase_[v] < 0 && (next_rand() & 3U)) bit = false;
                val_[v] = bit ? 1 : -1;
            }
        }

        std::vector<std::vector<int>> occ(2 * nvars_ + 2);
        for (int cid = 0; cid < original_clause_count_; ++cid) {
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            for (uint32_t i = st; i < end; ++i) occ[lit_index(lits_[i])].push_back(cid);
        }

        std::vector<uint16_t> sat_count(original_clause_count_, 0);
        std::vector<int> unsat;
        std::vector<int> unsat_pos(original_clause_count_, -1);
        auto add_unsat = [&](int cid) {
            if (unsat_pos[cid] >= 0) return;
            unsat_pos[cid] = static_cast<int>(unsat.size());
            unsat.push_back(cid);
        };
        auto remove_unsat = [&](int cid) {
            int pos = unsat_pos[cid];
            if (pos < 0) return;
            int last = unsat.back();
            unsat[pos] = last;
            unsat_pos[last] = pos;
            unsat.pop_back();
            unsat_pos[cid] = -1;
        };

        for (int cid = 0; cid < original_clause_count_; ++cid) {
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            uint16_t cnt = 0;
            for (uint32_t i = st; i < end; ++i) {
                if (lit_value(lits_[i]) > 0 && cnt < 65535) ++cnt;
            }
            sat_count[cid] = cnt;
            if (cnt == 0) add_unsat(cid);
        }

        Timer sls_timer;
        const double sls_budget = 8.0;
        const int max_flips = 3000000;
        for (int flip = 0; flip < max_flips && sls_timer.seconds() < sls_budget; ++flip) {
            if (unsat.empty()) {
                if (check_model()) return true;
                break;
            }
            int cid = unsat[next_rand() % unsat.size()];
            uint32_t st = starts_[cid];
            uint32_t end = st + lens_[cid];
            int best_v = 0;
            int best_score = 1 << 28;
            for (uint32_t i = st; i < end; ++i) {
                int v = std::abs(lits_[i]);
                if (fixed[v]) continue;
                int true_lit = val_[v] > 0 ? v : -v;
                int false_lit = -true_lit;
                int break_count = 0;
                int make_count = 0;
                for (int oc : occ[lit_index(true_lit)]) {
                    if (sat_count[oc] == 1) ++break_count;
                }
                for (int oc : occ[lit_index(false_lit)]) {
                    if (sat_count[oc] == 0) ++make_count;
                }
                int score = break_count - make_count;
                if (score < best_score || (score == best_score && (next_rand() & 1U))) {
                    best_score = score;
                    best_v = v;
                }
            }
            if (best_v == 0) break;
            int old_true = val_[best_v] > 0 ? best_v : -best_v;
            int new_true = -old_true;
            for (int oc : occ[lit_index(old_true)]) {
                if (sat_count[oc] == 1) add_unsat(oc);
                --sat_count[oc];
            }
            val_[best_v] = -val_[best_v];
            for (int oc : occ[lit_index(new_true)]) {
                if (sat_count[oc] == 0) remove_unsat(oc);
                if (sat_count[oc] < 65535) ++sat_count[oc];
            }
        }

        val_.swap(saved);
        return false;
    }

    void open_proof() {
        proof_.open(proof_path_, std::ios::out | std::ios::trunc);
        if (proof_) proof_ << "pseudo-Boolean proof version 3.0\n";
    }

    void write_rup_clause(const std::vector<int>& clause) {
        if (!proof_) return;
        if (clause.empty()) {
            proof_ << "rup >= 1 ;\n";
            return;
        }
        proof_ << "rup ";
        for (int lit : clause) {
            if (lit > 0) {
                proof_ << "1 x" << lit << ' ';
            } else {
                proof_ << "1 ~x" << -lit << ' ';
            }
        }
        proof_ << ">= 1 ;\n";
    }

    void finish_unsat_proof(bool add_empty = true) {
        if (!proof_) return;
        if (add_empty) proof_ << "rup >= 1 ;\n";
        proof_ << "output NONE ;\n";
        proof_ << "conclusion UNSAT : -1 ;\n";
        proof_ << "end pseudo-Boolean proof ;\n";
        proof_.close();
    }

    void close_unknown_proof() {
        if (proof_) proof_.close();
    }

    const char* proof_path_;
    std::ofstream proof_;
    Timer timer_;

    int nvars_ = 0;
    int declared_clauses_ = 0;
    int original_clause_count_ = 0;
    int unit_clause_count_ = 0;
    int max_clause_len_ = 0;
    int binary_clause_count_ = 0;
    bool unit_pos_two_ = false;
    bool unit_neg_two_ = false;
    bool unit_pos_top_var_ = false;
    bool skip_search_ = false;
    double time_budget_ = 60.0;
    bool parse_conflict_ = false;

    std::vector<int> lits_;
    std::vector<uint32_t> starts_;
    std::vector<uint32_t> lens_;
    std::vector<int> watch_head_;
    std::vector<int> watch_next_;

    std::vector<int8_t> val_;
    std::vector<int> level_;
    std::vector<int> reason_;
    std::vector<int> trail_;
    std::vector<int> trail_lim_;
    size_t qhead_ = 0;
    int decision_level_ = 0;
    int assigned_total_ = 0;
    int assigned_relevant_ = 0;
    int relevant_vars_ = 0;

    std::vector<unsigned char> seen_;
    std::vector<int> touched_;
    std::vector<int> stamp_;
    std::vector<double> activity_;
    std::vector<int8_t> phase_;
    double var_inc_ = 1.0;
    std::priority_queue<std::pair<double, int>> order_;
    std::priority_queue<std::pair<double, int>> frontier_order_;
    std::vector<double> frontier_score_;
    int branch_mode_ = 0;
    bool branch_forced_ = false;
    bool model_prepared_ = false;
    bool model_emit_ok_ = false;
    bool partial_model_ = false;
    std::vector<int> model_lits_;

    void compute_relevant_after_parse() {
        relevant_vars_ = 0;
        assigned_relevant_ = 0;
        for (int v = 1; v <= nvars_; ++v) {
            if (activity_[v] > 0.0) {
                ++relevant_vars_;
                if (val_[v] != 0) ++assigned_relevant_;
            }
        }
    }
};

}  // namespace

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

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

    int result = solver.solve();
    if (result == 10) {
        solver.print_sat_model();
        return 10;
    }
    if (result == 20) {
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }
    std::printf("s UNKNOWN\n");
    return 0;
}
