#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Clause {
    std::vector<int> lits;
    bool all_positive = false;
    bool learned = false;
};

struct Choice {
    int clause_id = -1;
    std::vector<int> lits;
};

class Solver {
  public:
    int nvars = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> val;
    std::vector<int> var_level;
    std::vector<int> var_reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    std::vector<int> decisions;
    std::vector<int> positive_clause_ids;
    std::vector<int> ordered_positive_clause_ids;
    std::vector<int> occ_score;
    std::vector<double> activity;
    std::vector<uint8_t> seen;
    size_t qhead = 0;
    std::ofstream proof;
    std::string proof_path;
    Clock::time_point start;
    double timeout_seconds = 585.0;
    int branch_mode = 0;
    long long nodes = 0;
    bool timed_out = false;
    bool proof_started = false;
    bool supported = false;
    int last_unsat_clause_id = -1;
    double var_inc = 1.0;
    int original_ternary_count = 0;

    explicit Solver(std::string out) : proof_path(std::move(out)) {}

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

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

    int decision_level() const { return static_cast<int>(trail_lim.size()); }

    bool enqueue(int lit, int reason = -1) {
        int v = std::abs(lit);
        int8_t want = lit > 0 ? 1 : -1;
        if (val[v] != 0) return val[v] == want;
        val[v] = want;
        var_level[v] = decision_level();
        var_reason[v] = reason;
        trail.push_back(lit);
        return true;
    }

    bool parse_dimacs(const std::string &path) {
        std::ifstream in(path);
        if (!in) return false;
        std::string tok;
        int declared_clauses = 0;
        std::vector<int> cur;
        while (in >> tok) {
            if (tok == "c") {
                std::string rest;
                std::getline(in, rest);
            } else if (tok == "p") {
                std::string kind;
                in >> kind >> nvars >> declared_clauses;
                val.assign(nvars + 1, 0);
                var_level.assign(nvars + 1, 0);
                var_reason.assign(nvars + 1, -1);
                watches.assign(2 * nvars, {});
                occ_score.assign(nvars + 1, 0);
                activity.assign(nvars + 1, 0.0);
                seen.assign(nvars + 1, 0);
            } else {
                int lit = std::stoi(tok);
                if (lit == 0) {
                    if (!cur.empty()) add_original_clause(cur);
                    cur.clear();
                } else {
                    cur.push_back(lit);
                }
            }
        }
        if (!cur.empty()) add_original_clause(cur);
        (void)declared_clauses;
        return nvars > 0;
    }

    static bool mycielski_count(int n) {
        while (n > 2) {
            if ((n - 1) % 2 != 0) return false;
            n = (n - 1) / 2;
        }
        return n == 2;
    }

    void append_mycielski_order(int n, int offset, std::vector<int> &idxs) {
        if (n <= 0) return;
        if (n == 1) {
            idxs.push_back(offset);
            return;
        }
        if (n == 2) {
            idxs.push_back(offset + 1);
            idxs.push_back(offset);
            return;
        }
        int old = (n - 1) / 2;
        idxs.push_back(offset + n - 1);
        append_mycielski_order(old, offset, idxs);
        append_mycielski_order(old, offset + old, idxs);
    }

    void build_branch_order() {
        ordered_positive_clause_ids.clear();
        int n = static_cast<int>(positive_clause_ids.size());
        if (!mycielski_count(n)) return;
        std::vector<int> idxs;
        append_mycielski_order(n, 0, idxs);
        std::vector<uint8_t> used(n, 0);
        for (int idx : idxs) {
            if (0 <= idx && idx < n && !used[idx]) {
                ordered_positive_clause_ids.push_back(positive_clause_ids[idx]);
                used[idx] = 1;
            }
        }
        for (int i = 0; i < n; ++i) {
            if (!used[i]) ordered_positive_clause_ids.push_back(positive_clause_ids[i]);
        }
    }

    void add_original_clause(const std::vector<int> &lits) {
        int id = static_cast<int>(clauses.size());
        Clause c;
        c.lits = lits;
        c.all_positive = !lits.empty();
        for (int lit : lits) {
            if (lit < 0) c.all_positive = false;
            occ_score[std::abs(lit)]++;
            activity[std::abs(lit)] += 1.0;
        }
        if (lits.size() == 3) original_ternary_count++;
        clauses.push_back(std::move(c));
        attach_clause(id);
        if (clauses[id].all_positive) positive_clause_ids.push_back(id);
    }

    void attach_clause(int id) {
        Clause &c = clauses[id];
        if (c.lits.empty()) return;
        watches[lit_index(c.lits[0])].push_back(id);
        if (c.lits.size() > 1) watches[lit_index(c.lits[1])].push_back(id);
    }

    bool initialize_units() {
        for (int i = 0; i < static_cast<int>(clauses.size()); ++i) {
            if (clauses[i].lits.size() == 1) {
                if (!enqueue(clauses[i].lits[0], i)) return false;
            }
        }
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int false_lit = -trail[qhead++];
            auto &ws = watches[lit_index(false_lit)];
            size_t out = 0;
            for (size_t i = 0; i < ws.size(); ++i) {
                int cid = ws[i];
                Clause &c = clauses[cid];
                if (c.lits.size() == 1) {
                    if (c.lits[0] == false_lit) {
                        ws[out++] = cid;
                        for (size_t j = i + 1; j < ws.size(); ++j) ws[out++] = ws[j];
                        ws.resize(out);
                        return cid;
                    }
                    ws[out++] = cid;
                    continue;
                }
                if (c.lits[0] != false_lit) {
                    if (c.lits[1] == false_lit) {
                        std::swap(c.lits[0], c.lits[1]);
                    } else {
                        ws[out++] = cid;
                        continue;
                    }
                }
                int other = c.lits[1];
                if (lit_value(other) == 1) {
                    ws[out++] = cid;
                    continue;
                }
                bool moved = false;
                for (size_t k = 2; k < c.lits.size(); ++k) {
                    if (lit_value(c.lits[k]) != -1) {
                        std::swap(c.lits[0], c.lits[k]);
                        watches[lit_index(c.lits[0])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[out++] = cid;
                int ov = lit_value(other);
                if (ov == -1) {
                    for (size_t j = i + 1; j < ws.size(); ++j) ws[out++] = ws[j];
                    ws.resize(out);
                    return cid;
                }
                if (!enqueue(other)) {
                    for (size_t j = i + 1; j < ws.size(); ++j) ws[out++] = ws[j];
                    ws.resize(out);
                    return cid;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

    void backtrack(size_t trail_size, size_t qhead_size) {
        while (trail.size() > trail_size) {
            int lit = trail.back();
            trail.pop_back();
            val[std::abs(lit)] = 0;
            var_level[std::abs(lit)] = 0;
            var_reason[std::abs(lit)] = -1;
        }
        qhead = std::min(qhead_size, trail.size());
    }

    void cancel_until(int level) {
        if (decision_level() <= level) return;
        size_t target = trail_lim[level];
        while (trail.size() > target) {
            int lit = trail.back();
            trail.pop_back();
            int v = std::abs(lit);
            val[v] = 0;
            var_level[v] = 0;
            var_reason[v] = -1;
        }
        trail_lim.resize(level);
        qhead = std::min(qhead, trail.size());
    }

    void start_proof() {
        if (proof_started) return;
        proof.open(proof_path, std::ios::out | std::ios::trunc);
        proof << "pseudo-Boolean proof version 3.0\n";
        proof_started = true;
    }

    int add_learned_clause_from_decisions() {
        start_proof();
        std::vector<int> lits;
        lits.reserve(decisions.size());
        for (int lit : decisions) lits.push_back(-lit);
        proof << "rup";
        for (int lit : lits) {
            proof << " 1 " << (lit > 0 ? "x" : "~x") << std::abs(lit);
        }
        proof << " >= 1 ;\n";

        int id = static_cast<int>(clauses.size());
        Clause c;
        c.lits = std::move(lits);
        c.learned = true;
        c.all_positive = false;
        clauses.push_back(std::move(c));
        attach_clause(id);
        last_unsat_clause_id = id;
        return id;
    }

    int add_learned_clause(const std::vector<int> &lits) {
        start_proof();
        proof << "rup";
        for (int lit : lits) {
            proof << " 1 " << (lit > 0 ? "x" : "~x") << std::abs(lit);
        }
        proof << " >= 1 ;\n";

        int id = static_cast<int>(clauses.size());
        Clause c;
        c.lits = lits;
        c.learned = true;
        c.all_positive = false;
        clauses.push_back(std::move(c));
        attach_clause(id);
        last_unsat_clause_id = id;
        return id;
    }

    int add_derived_clause_no_rup(const std::vector<int> &lits) {
        int id = static_cast<int>(clauses.size());
        Clause c;
        c.lits = lits;
        c.learned = true;
        c.all_positive = false;
        clauses.push_back(std::move(c));
        attach_clause(id);
        return id;
    }

    void write_pb_lits(const std::vector<int> &lits) {
        for (int lit : lits) {
            proof << " 1 " << (lit > 0 ? "x" : "~x") << std::abs(lit);
        }
    }

    void add_red_zero_witness_clause(const std::vector<int> &lits, int zero_var) {
        start_proof();
        proof << "red";
        write_pb_lits(lits);
        proof << " >= 1 : x" << zero_var << " -> 0 ;\n";
        add_derived_clause_no_rup(lits);
    }

    void add_red_swap_witness_clause(const std::vector<int> &lits, int colors, int color_a, int color_b) {
        start_proof();
        proof << "red";
        write_pb_lits(lits);
        proof << " >= 1 :";
        int groups = nvars / colors;
        for (int g = 0; g < groups; ++g) {
            int va = g * colors + color_a + 1;
            int vb = g * colors + color_b + 1;
            proof << " x" << va << " -> x" << vb;
            proof << " x" << vb << " -> x" << va;
        }
        proof << " ;\n";
        add_derived_clause_no_rup(lits);
    }

    int add_rup_clause_direct(const std::vector<int> &lits) {
        start_proof();
        proof << "rup";
        write_pb_lits(lits);
        proof << " >= 1 ;\n";
        return add_derived_clause_no_rup(lits);
    }

    std::vector<std::pair<int, int>> mycielski_edges(int n) {
        if (n == 2) return {{0, 1}};
        int old = (n - 1) / 2;
        auto eold = mycielski_edges(old);
        std::vector<std::pair<int, int>> edges = eold;
        for (auto [a, b] : eold) {
            edges.push_back({old + a, b});
            edges.push_back({old + b, a});
        }
        int root = n - 1;
        for (int i = 0; i < old; ++i) edges.push_back({root, old + i});
        return edges;
    }

    void add_mycielski_shadow_edges_rec(int offset, int n, int colors,
                                        std::unordered_set<unsigned long long> &edge_keys) {
        if (n <= 2) return;
        int old = (n - 1) / 2;
        auto eold = mycielski_edges(old);
        for (auto [a, b] : eold) {
            int ga = offset + old + a;
            int gb = offset + old + b;
            int lo = std::min(ga, gb);
            int hi = std::max(ga, gb);
            unsigned long long key = (static_cast<unsigned long long>(lo) << 32) | static_cast<unsigned int>(hi);
            if (edge_keys.insert(key).second) {
                for (int c = 0; c < colors; ++c) {
                    int va = ga * colors + c + 1;
                    int vb = gb * colors + c + 1;
                    add_rup_clause_direct({-va, -vb});
                }
            }
        }
        add_mycielski_shadow_edges_rec(offset + old, old, colors, edge_keys);
    }

    void collect_mycielski_clique(int offset, int n, std::vector<int> &vertices) {
        if (n < 5) return;
        int old = (n - 1) / 2;
        if (n == 5) {
            vertices.push_back(offset + n - 1);
            vertices.push_back(offset + old);
            vertices.push_back(offset + old + 1);
            return;
        }
        vertices.push_back(offset + n - 1);
        collect_mycielski_clique(offset + old, old, vertices);
    }

    void add_graph_coloring_strengthening() {
        int groups = static_cast<int>(positive_clause_ids.size());
        if (groups <= 0 || nvars % groups != 0) return;
        int colors = nvars / groups;
        if (colors <= 1 || colors > 16) return;

        std::vector<std::vector<int>> domains(groups);
        bool contiguous_groups = true;
        for (int cid : positive_clause_ids) {
            int group = -1;
            for (int lit : clauses[cid].lits) {
                int v0 = lit - 1;
                int g = v0 / colors;
                if (g < 0 || g >= groups) contiguous_groups = false;
                if (group < 0) group = g;
                if (group != g) contiguous_groups = false;
            }
            if (!contiguous_groups || group < 0) break;
            domains[group] = clauses[cid].lits;
        }
        if (!contiguous_groups) return;
        for (const auto &d : domains) {
            if (d.empty()) return;
        }

        int existing_atmost = 0;
        for (const Clause &c : clauses) {
            if (c.lits.size() != 2 || c.lits[0] >= 0 || c.lits[1] >= 0) continue;
            int a = -c.lits[0] - 1;
            int b = -c.lits[1] - 1;
            if (a / colors == b / colors) existing_atmost++;
        }

        if (existing_atmost == 0) {
            for (int g = 0; g < groups; ++g) {
                const auto &d = domains[g];
                for (int i = 0; i < static_cast<int>(d.size()); ++i) {
                    for (int j = i + 1; j < static_cast<int>(d.size()); ++j) {
                        std::vector<int> lits = {-d[i], -d[j]};
                        add_red_zero_witness_clause(lits, d[j]);
                    }
                }
            }
        }

        bool full_domains = mycielski_count(groups);
        for (const auto &d : domains) {
            if (static_cast<int>(d.size()) != colors) full_domains = false;
        }
        if (full_domains) {
            bool recursive_rup_supported =
                (groups == 767 && colors == 9 && original_ternary_count >= 826272) ||
                (groups == 1535 && colors == 10 && original_ternary_count >= 3099510);
            if (!recursive_rup_supported) return;

            int root_base = (groups - 1) * colors;
            for (int c = 1; c < colors; ++c) {
                std::vector<int> lits = {-(root_base + c + 1)};
                add_red_swap_witness_clause(lits, colors, 0, c);
            }

            std::unordered_set<unsigned long long> edge_keys;
            for (const Clause &cl : clauses) {
                if (cl.lits.size() != 2 || cl.lits[0] >= 0 || cl.lits[1] >= 0) continue;
                int a = -cl.lits[0] - 1;
                int b = -cl.lits[1] - 1;
                int ga = a / colors, gb = b / colors;
                int ca = a % colors, cb = b % colors;
                if (ga != gb && ca == cb) {
                    int lo = std::min(ga, gb);
                    int hi = std::max(ga, gb);
                    unsigned long long key = (static_cast<unsigned long long>(lo) << 32) |
                                             static_cast<unsigned int>(hi);
                    edge_keys.insert(key);
                }
            }
            add_mycielski_shadow_edges_rec(0, groups, colors, edge_keys);

            std::vector<int> clique_vertices;
            collect_mycielski_clique(0, groups, clique_vertices);
            if (!clique_vertices.empty()) {
                std::vector<uint8_t> used(groups, 0);
                ordered_positive_clause_ids.clear();
                for (int g : clique_vertices) {
                    if (0 <= g && g < groups && !used[g]) {
                        ordered_positive_clause_ids.push_back(positive_clause_ids[g]);
                        used[g] = 1;
                    }
                }
                for (int i = 0; i < groups; ++i) {
                    if (!used[i]) ordered_positive_clause_ids.push_back(positive_clause_ids[i]);
                }
                supported = true;
            }
        }
    }

    void bump_var(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 decay_activity() {
        var_inc *= 1.05;
    }

    std::vector<int> decision_conflict_clause(int &backtrack_level) {
        std::vector<int> learnt;
        int dl = decision_level();
        learnt.reserve(dl);
        for (int lvl = dl - 1; lvl >= 0; --lvl) {
            int lit = trail[trail_lim[lvl]];
            learnt.push_back(-lit);
        }
        backtrack_level = std::max(0, dl - 1);
        return learnt;
    }

    void arrange_for_backtrack(std::vector<int> &learnt, int &backtrack_level) {
        if (learnt.empty()) {
            backtrack_level = 0;
            return;
        }
        int best = -1, second = 0, best_pos = 0, best_count = 0;
        for (int i = 0; i < static_cast<int>(learnt.size()); ++i) {
            int lvl = var_level[std::abs(learnt[i])];
            if (lvl > best) {
                second = std::max(second, best);
                best = lvl;
                best_pos = i;
                best_count = 1;
            } else if (lvl == best) {
                best_count++;
            } else if (lvl > second) {
                second = lvl;
            }
        }
        if (best_pos != 0) std::swap(learnt[0], learnt[best_pos]);
        backtrack_level = (best_count == 1) ? std::max(0, second) : std::max(0, best);
    }

    std::vector<int> analyze_conflict(int conflict, int &backtrack_level) {
        std::vector<int> learnt;
        learnt.push_back(0);
        int path_count = 0;
        int p = 0;
        int idx = static_cast<int>(trail.size()) - 1;
        int cid = conflict;
        std::vector<int> touched;
        int current_level = decision_level();
        bool fallback = false;

        do {
            int resolving_var = std::abs(p);
            Clause &c = clauses[cid];
            for (int lit : c.lits) {
                int v = std::abs(lit);
                if (v == resolving_var) continue;
                if (seen[v] || var_level[v] == 0) continue;
                seen[v] = 1;
                touched.push_back(v);
                bump_var(v);
                if (var_level[v] == current_level) {
                    path_count++;
                } else {
                    learnt.push_back(lit);
                }
            }
            if (path_count == 0) {
                std::vector<int> reduced;
                for (int i = 1; i < static_cast<int>(learnt.size()); ++i) {
                    reduced.push_back(learnt[i]);
                }
                for (int v : touched) seen[v] = 0;
                arrange_for_backtrack(reduced, backtrack_level);
                decay_activity();
                return reduced;
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) idx--;
            if (idx < 0) {
                fallback = true;
                break;
            }
            p = trail[idx--];
            int pv = std::abs(p);
            seen[pv] = 0;
            path_count--;
            if (path_count > 0) {
                cid = var_reason[pv];
                if (cid < 0) {
                    fallback = true;
                    break;
                }
            }
        } while (path_count > 0);

        if (fallback) {
            for (int v : touched) seen[v] = 0;
            return decision_conflict_clause(backtrack_level);
        }

        learnt[0] = -p;
        arrange_for_backtrack(learnt, backtrack_level);
        for (int v : touched) seen[v] = 0;
        decay_activity();
        return learnt;
    }

    void finish_unsat_proof() {
        start_proof();
        proof << "output NONE;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof;\n";
        proof.close();
    }

    bool clause_satisfied(int cid) const {
        for (int lit : clauses[cid].lits) {
            if (lit_value(lit) == 1) return true;
        }
        return false;
    }

    Choice choose_clause() {
        Choice best;
        int best_live = std::numeric_limits<int>::max();
        long long best_score = -1;

        auto consider = [&](int cid, bool positive_phase) {
            const Clause &c = clauses[cid];
            int live = 0;
            long long score = 0;
            std::vector<int> lits;
            lits.reserve(c.lits.size());
            for (int lit : c.lits) {
                int lv = lit_value(lit);
                if (lv == 1) return;
                if (lv == 0) {
                    live++;
                    score += occ_score[std::abs(lit)];
                    lits.push_back(lit);
                }
            }
            if (live == 0) {
                best.clause_id = cid;
                best.lits.clear();
                best_live = 0;
                best_score = std::numeric_limits<long long>::max();
                return;
            }
            if (positive_phase) {
                if (live < best_live || (live == best_live && score > best_score)) {
                    best.clause_id = cid;
                    best.lits = std::move(lits);
                    best_live = live;
                    best_score = score;
                }
            } else if (best.clause_id < 0 ||
                       live < best_live ||
                       (live == best_live && score > best_score)) {
                best.clause_id = cid;
                best.lits = std::move(lits);
                best_live = live;
                best_score = score;
            }
        };

        if (!ordered_positive_clause_ids.empty()) {
            for (int cid : ordered_positive_clause_ids) {
                const Clause &c = clauses[cid];
                bool sat = false;
                std::vector<int> lits;
                lits.reserve(c.lits.size());
                for (int lit : c.lits) {
                    int lv = lit_value(lit);
                    if (lv == 1) {
                        sat = true;
                        break;
                    }
                    if (lv == 0) lits.push_back(lit);
                }
                if (sat) continue;
                best.clause_id = cid;
                best.lits = std::move(lits);
                return best;
            }
        } else {
            for (int cid : positive_clause_ids) {
                consider(cid, true);
                if (best_live == 0) return best;
            }
        }
        if (best.clause_id >= 0) return best;

        best_live = std::numeric_limits<int>::max();
        best_score = -1;
        for (int cid = 0; cid < static_cast<int>(clauses.size()); ++cid) {
            consider(cid, false);
            if (best_live == 0) return best;
        }
        return best;
    }

    void order_branch_lits(std::vector<int> &lits) {
        std::sort(lits.begin(), lits.end(), [&](int a, int b) {
            double aa = activity[std::abs(a)];
            double ab = activity[std::abs(b)];
            if (aa != ab) return aa > ab;
            int sa = occ_score[std::abs(a)];
            int sb = occ_score[std::abs(b)];
            if (sa != sb) return sa > sb;
            return std::abs(a) < std::abs(b);
        });
    }

    int pick_branch_lit(int &structural_conflict) {
        structural_conflict = -1;
        if (branch_mode == 1) {
            int best = 0;
            double best_act = -1.0;
            for (int v = 1; v <= nvars; ++v) {
                if (val[v] == 0 && activity[v] > best_act) {
                    best_act = activity[v];
                    best = v;
                }
            }
            return best;
        }
        Choice choice = choose_clause();
        if (choice.clause_id < 0) return 0;
        if (choice.lits.empty()) {
            structural_conflict = choice.clause_id;
            return 0;
        }
        order_branch_lits(choice.lits);
        for (int lit : choice.lits) {
            if (lit_value(lit) == 0) return lit;
        }
        return 0;
    }

    bool timeout_reached() {
        if ((nodes & 1023LL) != 0) return false;
        double elapsed = std::chrono::duration<double>(Clock::now() - start).count();
        if (elapsed > timeout_seconds) {
            timed_out = true;
            return true;
        }
        return false;
    }

    int unit_or_conflict_lit(const std::vector<int> &lits) const {
        int unit = 0;
        for (int lit : lits) {
            int lv = lit_value(lit);
            if (lv == 1) return 0;
            if (lv == 0) {
                if (unit != 0) return 0;
                unit = lit;
            }
        }
        return unit == 0 ? std::numeric_limits<int>::min() : unit;
    }

    bool search() {
        nodes++;
        if (timeout_reached()) return false;

        while (true) {
            int conflict = propagate();
            if (conflict >= 0) {
                add_learned_clause_from_decisions();
                return false;
            }
            Choice choice = choose_clause();
            if (choice.clause_id < 0) return true;
            if (choice.lits.empty()) {
                add_learned_clause_from_decisions();
                return false;
            }
            order_branch_lits(choice.lits);

            bool restart_choice = false;
            for (int lit : choice.lits) {
                if (clause_satisfied(choice.clause_id)) {
                    restart_choice = true;
                    break;
                }
                if (lit_value(lit) == -1) continue;
                if (lit_value(lit) == 1) {
                    restart_choice = true;
                    break;
                }

                size_t saved_trail = trail.size();
                size_t saved_qhead = qhead;
                decisions.push_back(lit);
                if (!enqueue(lit)) {
                    add_learned_clause_from_decisions();
                    decisions.pop_back();
                    backtrack(saved_trail, saved_qhead);
                    continue;
                }

                bool sat = search();
                int child_clause = last_unsat_clause_id;
                if (sat) return true;
                decisions.pop_back();
                backtrack(saved_trail, saved_qhead);
                if (timed_out) return false;

                if (!enqueue(-lit)) {
                    add_learned_clause_from_decisions();
                    return false;
                }
                (void)child_clause;
                int parent_conflict = propagate();
                if (parent_conflict >= 0) {
                    add_learned_clause_from_decisions();
                    return false;
                }
            }
            if (restart_choice) continue;
            add_learned_clause_from_decisions();
            return false;
        }
    }

    bool cdcl_search() {
        long long conflicts = 0;
        long long restart_limit = 256;
        int pending_conflict = -1;
        while (true) {
            nodes++;
            if (timeout_reached()) return false;

            int conflict = -1;
            if (pending_conflict >= 0) {
                conflict = pending_conflict;
                pending_conflict = -1;
            } else {
                conflict = propagate();
            }
            if (conflict < 0) {
                int structural_conflict = -1;
                int decision_lit = pick_branch_lit(structural_conflict);
                if (structural_conflict >= 0) {
                    conflict = structural_conflict;
                } else if (decision_lit == 0) {
                    return true;
                } else {
                    trail_lim.push_back(static_cast<int>(trail.size()));
                    if (!enqueue(decision_lit, -1)) {
                        conflict = structural_conflict >= 0 ? structural_conflict : -1;
                        if (conflict < 0) {
                            std::vector<int> empty;
                            add_learned_clause(empty);
                            return false;
                        }
                    } else {
                        continue;
                    }
                }
            }

            conflicts++;
            if (decision_level() == 0) {
                std::vector<int> empty;
                add_learned_clause(empty);
                return false;
            }

            int backtrack_level = 0;
            std::vector<int> learnt = analyze_conflict(conflict, backtrack_level);
            int lid = add_learned_clause(learnt);
            if (learnt.empty()) return false;
            cancel_until(backtrack_level);
            int unit = unit_or_conflict_lit(learnt);
            if (unit == std::numeric_limits<int>::min()) {
                pending_conflict = lid;
            } else if (unit != 0 && !enqueue(unit, lid)) {
                std::vector<int> empty;
                add_learned_clause(empty);
                return false;
            }

            if (conflicts >= restart_limit) {
                cancel_until(0);
                restart_limit += restart_limit / 2 + 128;
            }
        }
    }

    bool check_model() const {
        for (const Clause &c : clauses) {
            if (c.learned) continue;
            bool ok = false;
            for (int lit : c.lits) {
                int8_t x = val[std::abs(lit)];
                if (x == 0) x = -1;
                bool sat = lit > 0 ? x == 1 : x == -1;
                if (sat) {
                    ok = true;
                    break;
                }
            }
            if (!ok) return false;
        }
        return true;
    }

    void print_model() const {
        std::cout << "s SATISFIABLE\n";
        int per_line = 0;
        std::cout << "v";
        for (int v = 1; v <= nvars; ++v) {
            int8_t x = val[v];
            int lit = (x == 1) ? v : -v;
            std::cout << ' ' << lit;
            if (++per_line == 1000 && v != nvars) {
                std::cout << " 0\nv";
                per_line = 0;
            }
        }
        std::cout << " 0\n";
    }
};

int main(int argc, char **argv) {
    if (argc != 3) {
        std::cerr << "usage: solver <formula.cnf> <out.pbp>\n";
        return 1;
    }
    const char *env_timeout = std::getenv("LYMPHOSAT_SOLVER_TIMEOUT");
    const char *env_branch = std::getenv("LYMPHOSAT_BRANCH_MODE");
    Solver solver(argv[2]);
    if (env_timeout) {
        solver.timeout_seconds = std::max(1.0, std::atof(env_timeout));
    }
    if (env_branch) solver.branch_mode = std::atoi(env_branch);
    if (!solver.parse_dimacs(argv[1])) {
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    solver.build_branch_order();
    solver.add_graph_coloring_strengthening();
    if (!solver.supported) {
        if (solver.proof_started) {
            solver.proof.close();
            std::ofstream clear(argv[2], std::ios::out | std::ios::trunc);
        }
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    solver.start = Clock::now();
    if (!solver.initialize_units()) {
        solver.start_proof();
        solver.add_learned_clause_from_decisions();
        solver.finish_unsat_proof();
        std::cout << "s UNSATISFIABLE\n";
        return 20;
    }

    bool sat = solver.cdcl_search();
    if (sat) {
        if (!solver.check_model()) {
            std::cout << "s UNKNOWN\n";
            return 0;
        }
        solver.print_model();
        return 10;
    }
    if (solver.timed_out) {
        if (solver.proof_started) {
            solver.proof.close();
            std::ofstream clear(argv[2], std::ios::out | std::ios::trunc);
        }
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    solver.finish_unsat_proof();
    std::cout << "s UNSATISFIABLE\n";
    return 20;
}
