#include <algorithm>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <limits>
#include <climits>
#include <queue>
#include <string>
#include <vector>

using namespace std;

struct FastScanner {
    static constexpr size_t BUFSZ = 1 << 20;
    FILE* f = nullptr;
    vector<char> buf;
    size_t pos = 0, len = 0;

    explicit FastScanner(const char* path) : f(fopen(path, "rb")), buf(BUFSZ) {}
    ~FastScanner() { if (f) fclose(f); }

    int get() {
        if (pos >= len) {
            len = f ? fread(buf.data(), 1, BUFSZ, f) : 0;
            pos = 0;
            if (len == 0) return EOF;
        }
        return (unsigned char)buf[pos++];
    }

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

    int next_non_ws() {
        while (true) {
            int c = get();
            if (c == EOF) return EOF;
            if (isspace(c)) continue;
            if (c == 'c') {
                skip_line();
                continue;
            }
            return c;
        }
    }

    bool read_word(string& out) {
        out.clear();
        int c = next_non_ws();
        if (c == EOF) return false;
        while (c != EOF && !isspace(c)) {
            out.push_back((char)c);
            c = get();
        }
        return true;
    }

    bool read_word_raw(string& out) {
        out.clear();
        int c;
        do {
            c = get();
            if (c == EOF) return false;
        } while (isspace(c));
        while (c != EOF && !isspace(c)) {
            out.push_back((char)c);
            c = get();
        }
        return true;
    }

    bool read_int(int& out) {
        int c = next_non_ws();
        if (c == EOF) return false;
        int sign = 1;
        if (c == '-') {
            sign = -1;
            c = get();
        }
        int x = 0;
        while (c != EOF && isdigit(c)) {
            x = x * 10 + (c - '0');
            c = get();
        }
        out = sign * x;
        return true;
    }

    bool read_header(int& nvars, int& nclauses) {
        int c;
        while ((c = next_non_ws()) != EOF) {
            if (c != 'p') {
                skip_line();
                continue;
            }
            string fmt;
            if (!read_word_raw(fmt)) return false;
            return read_int(nvars) && read_int(nclauses) && fmt == "cnf";
        }
        return false;
    }
};

struct TopoModel {
    int nvars = 0;
    int nclauses = 0;
    vector<uint32_t> starts;
    vector<int> lits;
    vector<int> head;
    vector<int> next;
    vector<int> pos_occ, neg_occ;
    vector<signed char> val;
    vector<signed char> req;
    bool parse_ok = false;
    bool has_empty = false;
    bool too_large = false;

    bool read_dimacs(const char* path) {
        FastScanner fs(path);
        if (!fs.f) return false;
        if (!fs.read_header(nvars, nclauses)) return false;
        if (nvars <= 0 || nclauses < 0) return false;

        starts.clear();
        lits.clear();
        head.assign((size_t)nvars + 1, -1);
        next.assign((size_t)nclauses, -1);
        pos_occ.assign((size_t)nvars + 1, 0);
        neg_occ.assign((size_t)nvars + 1, 0);
        val.assign((size_t)nvars + 1, -1);
        req.assign((size_t)nvars + 1, 0);
        starts.reserve((size_t)nclauses + 1);
        starts.push_back(0);
        if (nclauses > 0) {
            size_t reserve_lits = min<size_t>((size_t)nclauses * 4, (size_t)900000000);
            lits.reserve(reserve_lits);
        }

        int lit;
        int ci = 0;
        int maxv = 0;
        while (fs.read_int(lit)) {
            if (lit == 0) {
                if (ci >= nclauses) return false;
                if (maxv == 0) {
                    has_empty = true;
                } else {
                    next[(size_t)ci] = head[(size_t)maxv];
                    head[(size_t)maxv] = ci;
                }
                if (lits.size() > UINT32_MAX) {
                    too_large = true;
                    return false;
                }
                starts.push_back((uint32_t)lits.size());
                maxv = 0;
                ci++;
                continue;
            }
            int v = abs(lit);
            if (v == 0 || v > nvars) return false;
            if (lit > 0) pos_occ[(size_t)v]++; else neg_occ[(size_t)v]++;
            maxv = max(maxv, v);
            lits.push_back(lit);
        }
        if (ci != nclauses) return false;
        parse_ok = true;
        return true;
    }

    signed char phase_for(int v, int mode) const {
        switch (mode) {
            case 0: return -1;
            case 1: return 1;
            case 2: return (pos_occ[(size_t)v] >= neg_occ[(size_t)v]) ? 1 : -1;
            case 3: return (v & 1) ? 1 : -1;
            default: return (v & 1) ? -1 : 1;
        }
    }

    bool evaluate(int mode) {
        bool debug = getenv("LSAT_TOPO_DEBUG") != nullptr;
        if (!parse_ok || has_empty) {
            if (debug) cerr << "topo mode " << mode << " parse/empty failure\n";
            return false;
        }
        for (int v = 1; v <= nvars; ++v) val[(size_t)v] = phase_for(v, mode);

        for (int v = 1; v <= nvars; ++v) {
            signed char forced = 0;
            for (int ci = head[(size_t)v]; ci != -1; ci = next[(size_t)ci]) {
                uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
                bool other_sat = false;
                bool has_pos_v = false, has_neg_v = false;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    int av = abs(lit);
                    if (av == v) {
                        if (lit > 0) has_pos_v = true; else has_neg_v = true;
                        continue;
                    }
                    signed char a = val[(size_t)av];
                    if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                        other_sat = true;
                        break;
                    }
                }
                if (other_sat) continue;
                if (has_pos_v && has_neg_v) continue; // tautological in v
                signed char need = has_pos_v ? 1 : (has_neg_v ? -1 : 0);
                if (need == 0) {
                    if (debug) cerr << "topo mode " << mode << " empty force at var " << v << " clause " << ci << "\n";
                    return false;
                }
                if (forced != 0 && forced != need) {
                    if (debug) cerr << "topo mode " << mode << " conflict at var " << v << " clause " << ci << "\n";
                    return false;
                }
                forced = need;
            }
            if (forced != 0) val[(size_t)v] = forced;
        }
        if (verify_model()) return true;
        if (debug) {
            int shown = 0;
            long long bad = 0;
            for (int ci = 0; ci < nclauses; ++ci) {
                uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
                bool sat = false;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    signed char a = val[(size_t)abs(lit)];
                    if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                        sat = true;
                        break;
                    }
                }
                if (!sat) {
                    bad++;
                    if (shown < 5) {
                        cerr << "topo mode " << mode << " unsat clause " << ci << ":";
                        for (uint32_t k = b; k < e; ++k) cerr << ' ' << lits[(size_t)k];
                        cerr << "\n";
                        shown++;
                    }
                }
            }
            cerr << "topo mode " << mode << " bad clauses " << bad << "\n";
        }
        return false;
    }

    bool set_req(int lit) {
        int v = abs(lit);
        signed char want = lit > 0 ? 1 : -1;
        if (req[(size_t)v] != 0) return req[(size_t)v] == want;
        req[(size_t)v] = want;
        return true;
    }

    bool seed_unit_requirements() {
        fill(req.begin(), req.end(), 0);
        if (!parse_ok || has_empty) return false;
        for (int ci = 0; ci < nclauses; ++ci) {
            uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
            if (e == b + 1 && !set_req(lits[(size_t)b])) return false;
        }
        return true;
    }

    bool require_literal_from_clause(uint32_t b, uint32_t e, int max_var, int chooser) {
        int first = 0, last = 0, primary = 0, majority = 0;
        for (uint32_t k = b; k < e; ++k) {
            int lit = lits[(size_t)k];
            int v = abs(lit);
            if (v == max_var) continue;
            signed char r = req[(size_t)v];
            signed char want = lit > 0 ? 1 : -1;
            if (r == want) return true;
            if (r == -want) continue;
            if (first == 0) first = lit;
            last = lit;
            if (primary == 0 && head[(size_t)v] == -1) primary = lit;
            if (majority == 0) {
                signed char maj = (pos_occ[(size_t)v] >= neg_occ[(size_t)v]) ? 1 : -1;
                if (maj == want) majority = lit;
            }
        }
        int chosen = 0;
        if (chooser == 0) chosen = primary ? primary : first;
        else if (chooser == 1) chosen = last ? last : first;
        else if (chooser == 2) chosen = majority ? majority : (primary ? primary : first);
        else chosen = first;
        return chosen != 0 && set_req(chosen);
    }

    bool justify_requirements(int chooser) {
        bool debug = getenv("LSAT_TOPO_DEBUG") != nullptr;
        if (!seed_unit_requirements()) return false;
        for (int v = nvars; v >= 1; --v) {
            signed char rv = req[(size_t)v];
            if (rv == 0) continue;
            for (int ci = head[(size_t)v]; ci != -1; ci = next[(size_t)ci]) {
                uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
                bool sat = false;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    int av = abs(lit);
                    if (av == v) {
                        if ((lit > 0 && rv > 0) || (lit < 0 && rv < 0)) {
                            sat = true;
                            break;
                        }
                    } else {
                        signed char r = req[(size_t)av];
                        if (r != 0 && ((lit > 0 && r > 0) || (lit < 0 && r < 0))) {
                            sat = true;
                            break;
                        }
                    }
                }
                if (!sat && !require_literal_from_clause(b, e, v, chooser)) {
                    if (debug) cerr << "justify chooser " << chooser << " conflict at var " << v << " clause " << ci << "\n";
                    return false;
                }
            }
        }
        return true;
    }

    bool evaluate_with_requirements(int mode) {
        bool debug = getenv("LSAT_TOPO_DEBUG") != nullptr;
        if (!parse_ok || has_empty) return false;
        for (int v = 1; v <= nvars; ++v) val[(size_t)v] = phase_for(v, mode);

        for (int v = 1; v <= nvars; ++v) {
            signed char forced = 0;
            for (int ci = head[(size_t)v]; ci != -1; ci = next[(size_t)ci]) {
                uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
                bool other_sat = false;
                bool has_pos_v = false, has_neg_v = false;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    int av = abs(lit);
                    if (av == v) {
                        if (lit > 0) has_pos_v = true; else has_neg_v = true;
                        continue;
                    }
                    signed char a = val[(size_t)av];
                    if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                        other_sat = true;
                        break;
                    }
                }
                if (other_sat) continue;
                if (has_pos_v && has_neg_v) continue;
                signed char need = has_pos_v ? 1 : (has_neg_v ? -1 : 0);
                if (need == 0) {
                    if (debug) cerr << "req eval mode " << mode << " empty force at var " << v << " clause " << ci << "\n";
                    return false;
                }
                if (forced != 0 && forced != need) {
                    if (debug) cerr << "req eval mode " << mode << " force conflict at var " << v << " clause " << ci << "\n";
                    return false;
                }
                forced = need;
            }
            signed char r = req[(size_t)v];
            if (forced != 0 && r != 0 && forced != r) {
                if (debug) cerr << "req eval mode " << mode << " req conflict at var " << v << " req " << (int)r << " forced " << (int)forced << "\n";
                return false;
            }
            if (r != 0) val[(size_t)v] = r;
            else if (forced != 0) val[(size_t)v] = forced;
        }
        if (verify_model()) return true;
        if (debug) {
            long long bad = 0;
            int shown = 0;
            for (int ci = 0; ci < nclauses; ++ci) {
                uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
                bool sat = false;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    signed char a = val[(size_t)abs(lit)];
                    if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) { sat = true; break; }
                }
                if (!sat) {
                    bad++;
                    if (shown < 5) {
                        cerr << "req eval mode " << mode << " unsat clause " << ci << ":";
                        for (uint32_t k = b; k < e; ++k) cerr << ' ' << lits[(size_t)k];
                        cerr << "\n";
                        shown++;
                    }
                }
            }
            cerr << "req eval mode " << mode << " bad clauses " << bad << "\n";
        }
        return false;
    }

    bool justify_and_evaluate(int chooser, int mode) {
        if (!justify_requirements(chooser)) return false;
        return evaluate_with_requirements(mode);
    }

    bool verify_model() const {
        if (!parse_ok || has_empty) return false;
        for (int ci = 0; ci < nclauses; ++ci) {
            uint32_t b = starts[(size_t)ci], e = starts[(size_t)ci + 1];
            bool sat = false;
            for (uint32_t k = b; k < e; ++k) {
                int lit = lits[(size_t)k];
                signed char a = val[(size_t)abs(lit)];
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    void print_model() const {
        cout << "s SATISFIABLE\n";
        string out;
        out.reserve(1 << 20);
        auto flush = [&]() {
            if (!out.empty()) {
                fwrite(out.data(), 1, out.size(), stdout);
                out.clear();
            }
        };
        const int per = 12;
        for (int i = 1; i <= nvars; ++i) {
            if ((i - 1) % per == 0) out += "v";
            signed char a = val[(size_t)i] ? val[(size_t)i] : -1;
            out.push_back(' ');
            out += to_string(a > 0 ? i : -i);
            if (i % per == 0 || i == nvars) {
                out += (i == nvars ? " 0\n" : "\n");
            }
            if (out.size() >= (1 << 20)) flush();
        }
        flush();
    }
};

struct FlatSolver {
    int nvars = 0;
    int nclauses_decl = 0;
    int original_clauses = 0;
    vector<uint32_t> starts;
    vector<uint32_t> lens;
    vector<uint32_t> watch0, watch1;
    vector<int> lits;
    vector<int> watch_head, watch_next;
    vector<signed char> val, phase, base_phase;
    vector<int> pos_occ, neg_occ, lev, reason, trail, trail_lim;
    vector<float> activity;
    priority_queue<pair<float,int>> heap;
    vector<char> seen;
    vector<char> defined_max;
    size_t qhead = 0;
    float var_inc = 1.0f;
    long long conflicts = 0;
    long long decisions = 0;
    long long compact_model_literals = 0;
    long long compact_model_bytes = 0;
    long long partial_model_literals = 0;
    long long partial_model_bytes = 0;
    int branch_scan = 1;
    int primary_count = 0;
    int first_clause_max = 0;
    bool root_conflict = false;
    bool partial_sat = false;
    chrono::steady_clock::time_point start;
    double time_limit = 570.0;
    long long conflict_limit = LLONG_MAX;
    long long decision_limit = LLONG_MAX;

    static int lit_index(int lit) {
        int v = abs(lit);
        return (v << 1) ^ (lit < 0);
    }
    int decision_level() const { return (int)trail_lim.size(); }
    int lit_value(int lit) const {
        signed char x = val[(size_t)abs(lit)];
        if (x == 0) return 0;
        return lit > 0 ? x : -x;
    }
    uint32_t clause_begin(int ci) const { return starts[(size_t)ci]; }
    uint32_t clause_end(int ci) const { return starts[(size_t)ci] + lens[(size_t)ci]; }
    int watched_lit(int ci, int slot) const {
        uint32_t pos = slot == 0 ? watch0[(size_t)ci] : watch1[(size_t)ci];
        return lits[(size_t)starts[(size_t)ci] + pos];
    }

    bool read_dimacs(const char* path) {
        start = chrono::steady_clock::now();
        FastScanner fs(path);
        if (!fs.f) return false;
        if (!fs.read_header(nvars, nclauses_decl)) return false;
        if (nvars <= 0 || nclauses_decl < 0) return false;

        starts.reserve((size_t)nclauses_decl + 200000);
        lens.reserve((size_t)nclauses_decl + 200000);
        watch0.reserve((size_t)nclauses_decl + 200000);
        watch1.reserve((size_t)nclauses_decl + 200000);
        watch_next.reserve((size_t)nclauses_decl * 2 + 400000);
        size_t reserve_lits = min<size_t>((size_t)nclauses_decl * 4, (size_t)900000000);
        lits.reserve(reserve_lits);
        watch_head.assign((size_t)(nvars + 1) * 2 + 2, -1);
        val.assign((size_t)nvars + 1, 0);
        phase.assign((size_t)nvars + 1, 1);
        base_phase.assign((size_t)nvars + 1, 1);
        pos_occ.assign((size_t)nvars + 1, 0);
        neg_occ.assign((size_t)nvars + 1, 0);
        lev.assign((size_t)nvars + 1, 0);
        reason.assign((size_t)nvars + 1, -1);
        activity.assign((size_t)nvars + 1, 0.0f);
        seen.assign((size_t)nvars + 1, 0);
        defined_max.assign((size_t)nvars + 1, 0);

        vector<int> clause;
        clause.reserve(16);
        int lit;
        while (fs.read_int(lit)) {
            if (lit == 0) {
                add_original_clause(clause);
                clause.clear();
            } else {
                int v = abs(lit);
                if (v == 0 || v > nvars) return false;
                clause.push_back(lit);
                activity[(size_t)v] += 1.0f;
                if (lit > 0) pos_occ[(size_t)v]++; else neg_occ[(size_t)v]++;
            }
        }
        original_clauses = (int)starts.size();
        const char* phase_mode = getenv("LSAT_PHASE_MODE");
        for (int v = 1; v <= nvars; ++v) {
            if (phase_mode && strcmp(phase_mode, "pos") == 0) base_phase[(size_t)v] = 1;
            else if (phase_mode && strcmp(phase_mode, "neg") == 0) base_phase[(size_t)v] = -1;
            else if (phase_mode && strcmp(phase_mode, "alt") == 0) base_phase[(size_t)v] = (v & 1) ? 1 : -1;
            else if (phase_mode && strcmp(phase_mode, "ralt") == 0) base_phase[(size_t)v] = (v & 1) ? -1 : 1;
            else if (nvars > 10000000) base_phase[(size_t)v] = -1;
            else base_phase[(size_t)v] = (pos_occ[(size_t)v] >= neg_occ[(size_t)v]) ? 1 : -1;
            phase[(size_t)v] = base_phase[(size_t)v];
            if (!defined_max[(size_t)v]) primary_count++;
        }
        bool branch_all = getenv("LSAT_FLAT_BRANCH_ALL") != nullptr
            || (nvars > 4000000 && nvars <= 10000000 && first_clause_max > 0 && first_clause_max <= 256);
        vector<pair<float,int>> h;
        h.reserve((size_t)max(1, branch_all ? nvars : primary_count));
        for (int v = 1; v <= nvars; ++v) {
            if (branch_all || !defined_max[(size_t)v]) h.push_back({activity[(size_t)v], v});
        }
        if (h.empty()) {
            for (int v = 1; v <= nvars; ++v) h.push_back({activity[(size_t)v], v});
        }
        heap = priority_queue<pair<float,int>>(less<pair<float,int>>(), std::move(h));
        return true;
    }

    void add_watch_entry(int entry, int lit) {
        int idx = lit_index(lit);
        watch_next[(size_t)entry] = watch_head[(size_t)idx];
        watch_head[(size_t)idx] = entry;
    }

    void attach_clause(int ci) {
        uint32_t len = lens[(size_t)ci];
        if (len == 0) return;
        watch0[(size_t)ci] = 0;
        watch1[(size_t)ci] = len > 1 ? 1 : 0;
        int e0 = ci * 2;
        add_watch_entry(e0, watched_lit(ci, 0));
        if (len > 1) {
            int e1 = ci * 2 + 1;
            add_watch_entry(e1, watched_lit(ci, 1));
        }
    }

    int append_clause_raw(const vector<int>& clause) {
        if (lits.size() + clause.size() > UINT32_MAX) return -1;
        int ci = (int)starts.size();
        starts.push_back((uint32_t)lits.size());
        lens.push_back((uint32_t)clause.size());
        watch0.push_back(0);
        watch1.push_back(clause.size() > 1 ? 1 : 0);
        watch_next.push_back(-1);
        watch_next.push_back(-1);
        lits.insert(lits.end(), clause.begin(), clause.end());
        attach_clause(ci);
        return ci;
    }

    bool add_original_clause(vector<int>& clause) {
        if (clause.empty()) {
            root_conflict = true;
            return false;
        }
        sort(clause.begin(), clause.end(), [](int a, int b){
            return abs(a) == abs(b) ? a < b : abs(a) < abs(b);
        });
        vector<int> out;
        out.reserve(clause.size());
        int lastv = 0, lastlit = 0;
        for (int l : clause) {
            int v = abs(l);
            if (v == lastv) {
                if (l == -lastlit) return true;
                continue;
            }
            out.push_back(l);
            lastv = v;
            lastlit = l;
        }
        int maxv = 0;
        for (int l : out) maxv = max(maxv, abs(l));
        if (starts.empty()) first_clause_max = maxv;
        if (maxv > 0) defined_max[(size_t)maxv] = 1;
        int ci = append_clause_raw(out);
        if (ci < 0) return false;
        if (out.size() == 1 && !enqueue(out[0], ci)) root_conflict = true;
        return true;
    }

    bool enqueue(int lit, int from) {
        int v = abs(lit);
        signed char want = lit > 0 ? 1 : -1;
        if (val[(size_t)v] != 0) return val[(size_t)v] == want;
        val[(size_t)v] = want;
        phase[(size_t)v] = want;
        lev[(size_t)v] = decision_level();
        reason[(size_t)v] = from;
        trail.push_back(lit);
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int p = trail[qhead++];
            int idx = lit_index(-p);
            int e = watch_head[(size_t)idx];
            watch_head[(size_t)idx] = -1;
            int kept = -1;
            while (e != -1) {
                int next_entry = watch_next[(size_t)e];
                int ci = e >> 1;
                int slot = e & 1;
                uint32_t len = lens[(size_t)ci];
                bool keep = true;
                if (len == 1) {
                    if (lit_value(watched_lit(ci, 0)) < 0) {
                        watch_next[(size_t)e] = kept;
                        kept = e;
                        while (next_entry != -1) {
                            int rest = next_entry;
                            next_entry = watch_next[(size_t)rest];
                            watch_next[(size_t)rest] = kept;
                            kept = rest;
                        }
                        watch_head[(size_t)idx] = kept;
                        return ci;
                    }
                } else {
                    uint32_t cur_pos = slot == 0 ? watch0[(size_t)ci] : watch1[(size_t)ci];
                    uint32_t other_pos = slot == 0 ? watch1[(size_t)ci] : watch0[(size_t)ci];
                    int other = lits[(size_t)starts[(size_t)ci] + other_pos];
                    if (lit_value(other) <= 0) {
                        bool found = false;
                        uint32_t b = starts[(size_t)ci];
                        for (uint32_t k = 0; k < len; ++k) {
                            if (k == cur_pos || k == other_pos) continue;
                            int cand = lits[(size_t)b + k];
                            if (lit_value(cand) >= 0) {
                                if (slot == 0) watch0[(size_t)ci] = k; else watch1[(size_t)ci] = k;
                                add_watch_entry(e, cand);
                                keep = false;
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            if (lit_value(other) < 0) {
                                watch_next[(size_t)e] = kept;
                                kept = e;
                                while (next_entry != -1) {
                                    int rest = next_entry;
                                    next_entry = watch_next[(size_t)rest];
                                    watch_next[(size_t)rest] = kept;
                                    kept = rest;
                                }
                                watch_head[(size_t)idx] = kept;
                                return ci;
                            }
                            if (!enqueue(other, ci)) {
                                watch_next[(size_t)e] = kept;
                                kept = e;
                                while (next_entry != -1) {
                                    int rest = next_entry;
                                    next_entry = watch_next[(size_t)rest];
                                    watch_next[(size_t)rest] = kept;
                                    kept = rest;
                                }
                                watch_head[(size_t)idx] = kept;
                                return ci;
                            }
                        }
                    }
                }
                if (keep) {
                    watch_next[(size_t)e] = kept;
                    kept = e;
                }
                e = next_entry;
            }
            watch_head[(size_t)idx] = kept;
        }
        return -1;
    }

    void bump_var(int v) {
        activity[(size_t)v] += var_inc;
        heap.push({activity[(size_t)v], v});
        if (activity[(size_t)v] > 1e30f) {
            for (int i = 1; i <= nvars; ++i) activity[(size_t)i] *= 1e-20f;
            var_inc *= 1e-20f;
        }
    }

    int add_learnt(vector<int>& clause) {
        int ci = append_clause_raw(clause);
        return ci;
    }

    void backtrack(int lvl) {
        if (decision_level() <= lvl) return;
        for (int i = (int)trail.size() - 1; i >= trail_lim[(size_t)lvl]; --i) {
            int v = abs(trail[(size_t)i]);
            val[(size_t)v] = 0;
            reason[(size_t)v] = -1;
            lev[(size_t)v] = 0;
        }
        trail.resize((size_t)trail_lim[(size_t)lvl]);
        trail_lim.resize((size_t)lvl);
        qhead = trail.size();
    }

    void analyze(int confl, vector<int>& learnt, int& btlevel) {
        learnt.clear();
        learnt.push_back(0);
        int pathc = 0;
        int p = 0;
        int ci = confl;
        int idx = (int)trail.size() - 1;
        do {
            uint32_t b = clause_begin(ci), e = clause_end(ci);
            for (uint32_t k = b; k < e; ++k) {
                int q = lits[(size_t)k];
                int v = abs(q);
                if (v == abs(p) || seen[(size_t)v] || lev[(size_t)v] == 0) continue;
                seen[(size_t)v] = 1;
                bump_var(v);
                if (lev[(size_t)v] == decision_level()) pathc++;
                else learnt.push_back(q);
            }
            while (idx >= 0 && !seen[(size_t)abs(trail[(size_t)idx])]) idx--;
            if (idx < 0) break;
            p = trail[(size_t)idx--];
            seen[(size_t)abs(p)] = 0;
            pathc--;
            ci = reason[(size_t)abs(p)];
        } while (pathc > 0 && ci >= 0);
        learnt[0] = -p;
        btlevel = 0;
        for (size_t i = 1; i < learnt.size(); ++i) btlevel = max(btlevel, lev[(size_t)abs(learnt[i])]);
        for (int l : learnt) seen[(size_t)abs(l)] = 0;
        var_inc *= 1.05f;
    }

    int pick_branch_lit() {
        while (!heap.empty()) {
            auto [a, v] = heap.top();
            heap.pop();
            if (val[(size_t)v] == 0 && a == activity[(size_t)v]) return v;
        }
        for (int pass = 0; pass < 2; ++pass) {
            for (int checked = 0; checked < nvars; ++checked) {
                int v = branch_scan++;
                if (branch_scan > nvars) branch_scan = 1;
                if (val[(size_t)v] != 0) continue;
                if (pass == 0 && defined_max[(size_t)v]) continue;
                return v;
            }
        }
        return 0;
    }

    bool timeout() const {
        return chrono::duration<double>(chrono::steady_clock::now() - start).count() > time_limit;
    }

    int solve() {
        if (root_conflict) return 0;
        int confl = propagate();
        if (confl >= 0) return 0;
        vector<int> learnt;
        int bt = 0;
        long long next_restart = 1000;
        long long restart_span = 1000;
        int restart_count = 0;
        long long next_partial_check = 0;
        while (true) {
            confl = propagate();
            if (confl >= 0) {
                conflicts++;
                if (conflicts >= conflict_limit) return 0;
                if (decision_level() == 0) return 0;
                analyze(confl, learnt, bt);
                backtrack(bt);
                int ci = add_learnt(learnt);
                if (ci < 0) return 0;
                enqueue(learnt[0], ci);
                if (conflicts >= next_restart) {
                    backtrack(0);
                    restart_count++;
                    int mode = restart_count % 4;
                    for (int v = 1; v <= nvars; ++v) {
                        if (mode == 1) phase[(size_t)v] = 1;
                        else if (mode == 2) phase[(size_t)v] = -1;
                        else if (mode == 3) phase[(size_t)v] = base_phase[(size_t)v];
                    }
                    restart_span = min<long long>(restart_span + restart_span / 2 + 1, 200000);
                    next_restart = conflicts + restart_span;
                }
                if ((conflicts & 4095) == 0 && timeout()) return 0;
            } else {
                if (nvars > 10000000 && getenv("LSAT_FORCE_FULL_MODEL") == nullptr
                    && decisions >= next_partial_check) {
                    if (partial_model_satisfies()) {
                        partial_sat = true;
                        return 10;
                    }
                    next_partial_check = decisions + 8192;
                }
                int next = pick_branch_lit();
                if (next == 0) return verify_model() ? 10 : 0;
                decisions++;
                if (decisions >= decision_limit) return 0;
                if ((decisions & 262143) == 0 && timeout()) return 0;
                trail_lim.push_back((int)trail.size());
                enqueue(phase[(size_t)next] > 0 ? next : -next, -1);
            }
        }
    }

    bool verify_model() const {
        for (int ci = 0; ci < original_clauses; ++ci) {
            uint32_t b = clause_begin(ci), e = clause_end(ci);
            bool sat = false;
            for (uint32_t k = b; k < e; ++k) {
                int lit = lits[(size_t)k];
                signed char a = val[(size_t)abs(lit)];
                if (a == 0) a = -1;
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    bool partial_model_satisfies() const {
        for (int ci = 0; ci < original_clauses; ++ci) {
            uint32_t b = clause_begin(ci), e = clause_end(ci);
            bool sat = false;
            for (uint32_t k = b; k < e; ++k) {
                int lit = lits[(size_t)k];
                signed char a = val[(size_t)abs(lit)];
                if (a != 0 && ((lit > 0 && a > 0) || (lit < 0 && a < 0))) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    int true_lit_for_var(int v) const {
        signed char a = val[(size_t)v] ? val[(size_t)v] : -1;
        return a > 0 ? v : -v;
    }

    int true_occ_count(int v) const {
        signed char a = val[(size_t)v] ? val[(size_t)v] : -1;
        return a > 0 ? pos_occ[(size_t)v] : neg_occ[(size_t)v];
    }

    static int decimal_digits(int x) {
        int d = 1;
        while (x >= 10) {
            x /= 10;
            d++;
        }
        return d;
    }

    long long estimate_model_bytes(const vector<unsigned char>& selected) const {
        long long bytes = 15; // "s SATISFIABLE\n"
        int col = 0;
        for (int v = 1; v <= nvars; ++v) {
            if (!selected[(size_t)v]) continue;
            if (col == 0) bytes += 1; // 'v'
            bytes += 1 + decimal_digits(v);
            if (true_lit_for_var(v) < 0) bytes += 1;
            col++;
            if (col == 12) {
                bytes += 1; // newline
                col = 0;
            }
        }
        bytes += (col == 0 ? 4 : 3); // "v 0\n" after a full line, otherwise " 0\n"
        return bytes;
    }

    bool selected_model_satisfies(const vector<unsigned char>& selected) const {
        for (int ci = 0; ci < original_clauses; ++ci) {
            uint32_t b = clause_begin(ci), e = clause_end(ci);
            bool sat = false;
            for (uint32_t k = b; k < e; ++k) {
                int lit = lits[(size_t)k];
                int v = abs(lit);
                if (!selected[(size_t)v]) continue;
                signed char a = val[(size_t)v] ? val[(size_t)v] : -1;
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    bool build_compact_model(vector<unsigned char>& best, long long byte_limit) {
        best.clear();
        if (!verify_model()) return false;

        vector<unsigned char> mandatory((size_t)nvars + 1, 0);
        long long mandatory_count = 0;
        for (int ci = 0; ci < original_clauses; ++ci) {
            uint32_t b = clause_begin(ci), e = clause_end(ci);
            int sole_true = 0;
            int true_count = 0;
            for (uint32_t k = b; k < e; ++k) {
                int lit = lits[(size_t)k];
                signed char a = val[(size_t)abs(lit)] ? val[(size_t)abs(lit)] : -1;
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    true_count++;
                    sole_true = abs(lit);
                    if (true_count > 1) break;
                }
            }
            if (true_count == 1 && !mandatory[(size_t)sole_true]) {
                mandatory[(size_t)sole_true] = 1;
                mandatory_count++;
            }
        }
        long long mandatory_bytes = estimate_model_bytes(mandatory);
        if (mandatory_bytes > byte_limit) {
            compact_model_literals = mandatory_count;
            compact_model_bytes = mandatory_bytes;
            return false;
        }

        vector<int> thresholds = {
            numeric_limits<int>::max(), 128, 96, 64, 48, 32, 24, 16,
            12, 10, 8, 7, 6, 5, 4, 3, 2
        };
        const char* only = getenv("LSAT_COMPACT_THRESHOLD");
        if (only) {
            thresholds.clear();
            thresholds.push_back(atoi(only));
        }

        vector<unsigned char> selected((size_t)nvars + 1, 0);
        long long best_bytes = numeric_limits<long long>::max();
        long long best_count = 0;

        for (int threshold : thresholds) {
            selected = mandatory;
            long long count = mandatory_count;
            if (threshold != numeric_limits<int>::max()) {
                for (int v = 1; v <= nvars; ++v) {
                    if (!selected[(size_t)v] && true_occ_count(v) >= threshold) {
                        selected[(size_t)v] = 1;
                        count++;
                    }
                }
            }

            bool ok = true;
            for (int ci = 0; ci < original_clauses; ++ci) {
                uint32_t b = clause_begin(ci), e = clause_end(ci);
                bool covered = false;
                int first_true = 0;
                for (uint32_t k = b; k < e; ++k) {
                    int lit = lits[(size_t)k];
                    signed char a = val[(size_t)abs(lit)] ? val[(size_t)abs(lit)] : -1;
                    bool lit_true = (lit > 0 && a > 0) || (lit < 0 && a < 0);
                    if (!lit_true) continue;
                    int v = abs(lit);
                    if (selected[(size_t)v]) {
                        covered = true;
                        break;
                    }
                    if (first_true == 0) first_true = v;
                }
                if (!covered) {
                    if (first_true == 0) {
                        ok = false;
                        break;
                    }
                    if (!selected[(size_t)first_true]) {
                        selected[(size_t)first_true] = 1;
                        count++;
                    }
                }
            }
            if (!ok) continue;
            long long bytes = estimate_model_bytes(selected);
            if (bytes < best_bytes) {
                best = selected;
                best_bytes = bytes;
                best_count = count;
            }
            if (bytes <= byte_limit && threshold == numeric_limits<int>::max()) {
                break;
            }
        }

        if (best.empty()) return false;
        if (!selected_model_satisfies(best)) return false;
        compact_model_literals = best_count;
        compact_model_bytes = best_bytes;
        return best_bytes <= byte_limit;
    }

    bool build_assigned_partial_model(vector<unsigned char>& selected, long long byte_limit) {
        selected.assign((size_t)nvars + 1, 0);
        long long count = 0;
        for (int v = 1; v <= nvars; ++v) {
            if (val[(size_t)v] != 0) {
                selected[(size_t)v] = 1;
                count++;
            }
        }
        long long bytes = estimate_model_bytes(selected);
        partial_model_literals = count;
        partial_model_bytes = bytes;
        if (bytes > byte_limit) return false;
        return selected_model_satisfies(selected);
    }

    void print_model() const {
        cout << "s SATISFIABLE\n";
        string out;
        out.reserve(1 << 20);
        auto flush = [&]() {
            if (!out.empty()) {
                fwrite(out.data(), 1, out.size(), stdout);
                out.clear();
            }
        };
        const int per = 12;
        for (int i = 1; i <= nvars; ++i) {
            if ((i - 1) % per == 0) out += "v";
            signed char a = val[(size_t)i] ? val[(size_t)i] : -1;
            out.push_back(' ');
            out += to_string(a > 0 ? i : -i);
            if (i % per == 0 || i == nvars) {
                out += (i == nvars ? " 0\n" : "\n");
            }
            if (out.size() >= (1 << 20)) flush();
        }
        flush();
    }

    void print_selected_model(const vector<unsigned char>& selected) const {
        cout << "s SATISFIABLE\n";
        string out;
        out.reserve(1 << 20);
        auto flush = [&]() {
            if (!out.empty()) {
                fwrite(out.data(), 1, out.size(), stdout);
                out.clear();
            }
        };
        int col = 0;
        bool any = false;
        for (int i = 1; i <= nvars; ++i) {
            if (!selected[(size_t)i]) continue;
            if (col == 0) out += "v";
            signed char a = val[(size_t)i] ? val[(size_t)i] : -1;
            out.push_back(' ');
            out += to_string(a > 0 ? i : -i);
            any = true;
            col++;
            if (col == 12) {
                out += "\n";
                col = 0;
            }
            if (out.size() >= (1 << 20)) flush();
        }
        if (!any || col == 0) out += "v";
        out += " 0\n";
        flush();
    }
};

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

struct Solver {
    int nvars = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<signed char> val;
    vector<signed char> phase;
    vector<signed char> base_phase;
    vector<int> pos_occ, neg_occ;
    vector<int> lev, reason, trail, trail_lim;
    vector<double> activity;
    priority_queue<pair<double,int>> heap;
    vector<char> seen;
    size_t qhead = 0;
    double var_inc = 1.0;
    ofstream proof;
    bool proof_open = false;
    long long conflicts = 0;
    bool unsupported = false;
    chrono::steady_clock::time_point start;
    double time_limit = 570.0;
    long long conflict_limit = LLONG_MAX;

    static int lit_index(int lit) {
        int v = abs(lit);
        return (v << 1) ^ (lit < 0);
    }
    int lit_value(int lit) const {
        signed char x = val[abs(lit)];
        if (x == 0) return 0;
        return lit > 0 ? x : -x;
    }
    int decision_level() const { return (int)trail_lim.size(); }

    bool read_dimacs(const char* path) {
        FILE* f = fopen(path, "rb");
        if (!f) return false;
        int ch;
        while ((ch = fgetc(f)) != EOF) {
            if (ch == 'c') {
                while ((ch = fgetc(f)) != EOF && ch != '\n') {}
            } else if (ch == 'p') {
                char fmt[16];
                int m;
                if (fscanf(f, "%15s %d %d", fmt, &nvars, &m) != 3) return false;
                if (nvars > 1000000) {
                    unsupported = true;
                    fclose(f);
                    return true;
                }
                clauses.reserve((size_t)m + 100000);
                watches.assign((nvars + 1) * 2 + 2, {});
                val.assign(nvars + 1, 0);
                phase.assign(nvars + 1, 1);
                base_phase.assign(nvars + 1, 1);
                pos_occ.assign(nvars + 1, 0);
                neg_occ.assign(nvars + 1, 0);
                lev.assign(nvars + 1, 0);
                reason.assign(nvars + 1, -1);
                activity.assign(nvars + 1, 0.0);
                seen.assign(nvars + 1, 0);
                break;
            }
        }
        if (nvars <= 0) return false;
        vector<int> lits;
        int lit;
        while (fscanf(f, "%d", &lit) == 1) {
            if (lit == 0) {
                add_original_clause(lits);
                lits.clear();
            } else {
                if (abs(lit) > nvars) return false;
                lits.push_back(lit);
                activity[abs(lit)] += 1.0;
                if (lit > 0) pos_occ[lit]++; else neg_occ[-lit]++;
            }
        }
        fclose(f);
        const char* phase_mode = getenv("LSAT_PHASE_MODE");
        for (int v = 1; v <= nvars; ++v) {
            if (phase_mode && strcmp(phase_mode, "pos") == 0) base_phase[v] = 1;
            else if (phase_mode && strcmp(phase_mode, "neg") == 0) base_phase[v] = -1;
            else if (phase_mode && strcmp(phase_mode, "midneg") == 0 && nvars > 50000) base_phase[v] = -1;
            else base_phase[v] = (pos_occ[v] >= neg_occ[v]) ? 1 : -1;
            phase[v] = base_phase[v];
        }
        for (int v = 1; v <= nvars; ++v) heap.push({activity[v], v});
        return true;
    }

    void attach(int ci) {
        Clause& c = clauses[ci];
        if (c.lits.empty()) return;
        watches[lit_index(c.lits[0])].push_back(ci);
        watches[lit_index(c.lits.size() == 1 ? c.lits[0] : c.lits[1])].push_back(ci);
    }

    bool enqueue(int lit, int from) {
        int v = abs(lit);
        signed char want = lit > 0 ? 1 : -1;
        if (val[v] != 0) return val[v] == want;
        val[v] = want;
        phase[v] = want;
        lev[v] = decision_level();
        reason[v] = from;
        trail.push_back(lit);
        return true;
    }

    bool add_original_clause(vector<int>& lits) {
        if (lits.empty()) {
            Clause c; clauses.push_back(std::move(c));
            return false;
        }
        sort(lits.begin(), lits.end(), [](int a, int b){ return abs(a) == abs(b) ? a < b : abs(a) < abs(b); });
        vector<int> out;
        int lastv = 0, lastlit = 0;
        for (int l : lits) {
            int v = abs(l);
            if (v == lastv) {
                if (l == -lastlit) return true;
                continue;
            }
            out.push_back(l);
            lastv = v; lastlit = l;
        }
        int ci = (int)clauses.size();
        Clause c; c.lits.swap(out); clauses.push_back(std::move(c));
        attach(ci);
        if (clauses[ci].lits.size() == 1) enqueue(clauses[ci].lits[0], ci);
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int p = trail[qhead++];
            vector<int>& ws = watches[lit_index(-p)];
            size_t i = 0, j = 0;
            while (i < ws.size()) {
                int ci = ws[i++];
                Clause& c = clauses[ci];
                if (c.lits.size() == 1) {
                    ws[j++] = ci;
                    if (lit_value(c.lits[0]) < 0) {
                        while (i < ws.size()) ws[j++] = ws[i++];
                        ws.resize(j);
                        return ci;
                    }
                    continue;
                }
                if (c.lits[0] == -p) swap(c.lits[0], c.lits[1]);
                int blocker = c.lits[0];
                if (lit_value(blocker) > 0) {
                    ws[j++] = ci;
                    continue;
                }
                bool found = false;
                for (size_t k = 2; k < c.lits.size(); ++k) {
                    if (lit_value(c.lits[k]) >= 0) {
                        swap(c.lits[1], c.lits[k]);
                        watches[lit_index(c.lits[1])].push_back(ci);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[j++] = ci;
                if (lit_value(blocker) < 0) {
                    while (i < ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return ci;
                }
                if (!enqueue(blocker, ci)) {
                    while (i < ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return ci;
                }
            }
            ws.resize(j);
        }
        return -1;
    }

    void bump_var(int v) {
        activity[v] += var_inc;
        heap.push({activity[v], v});
        if (activity[v] > 1e100) {
            for (int i = 1; i <= nvars; ++i) activity[i] *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    void write_rup(const vector<int>& lits) {
        if (!proof_open) return;
        proof << "rup";
        for (int l : lits) {
            proof << " 1 ";
            if (l < 0) proof << "~";
            proof << "x" << abs(l);
        }
        proof << " >= 1 ;\n";
    }

    int add_learnt(vector<int>& lits) {
        int ci = (int)clauses.size();
        Clause c; c.learnt = true; c.lits = lits; clauses.push_back(std::move(c));
        attach(ci);
        write_rup(lits);
        return ci;
    }

    void backtrack(int lvl) {
        if (decision_level() <= lvl) return;
        for (int i = (int)trail.size() - 1; i >= trail_lim[lvl]; --i) {
            int v = abs(trail[i]);
            val[v] = 0; reason[v] = -1; lev[v] = 0;
        }
        trail.resize(trail_lim[lvl]);
        trail_lim.resize(lvl);
        qhead = trail.size();
    }

    void analyze(int confl, vector<int>& learnt, int& btlevel) {
        learnt.clear();
        learnt.push_back(0);
        int pathc = 0;
        int p = 0;
        int ci = confl;
        int idx = (int)trail.size() - 1;
        do {
            Clause& c = clauses[ci];
            for (int q : c.lits) {
                int v = abs(q);
                if (v == abs(p) || seen[v] || lev[v] == 0) continue;
                seen[v] = 1;
                bump_var(v);
                if (lev[v] == decision_level()) pathc++;
                else learnt.push_back(q);
            }
            while (idx >= 0 && !seen[abs(trail[idx])]) idx--;
            p = trail[idx--];
            seen[abs(p)] = 0;
            pathc--;
            ci = reason[abs(p)];
        } while (pathc > 0 && ci >= 0);
        learnt[0] = -p;
        btlevel = 0;
        for (size_t i = 1; i < learnt.size(); ++i) btlevel = max(btlevel, lev[abs(learnt[i])]);
        for (int l : learnt) seen[abs(l)] = 0;
        var_inc *= 1.05;
    }

    int pick_branch_lit() {
        while (!heap.empty()) {
            auto [a, v] = heap.top(); heap.pop();
            if (val[v] == 0 && a == activity[v]) return v;
        }
        for (int v = 1; v <= nvars; ++v) if (val[v] == 0) return v;
        return 0;
    }

    bool timeout() const {
        return chrono::duration<double>(chrono::steady_clock::now() - start).count() > time_limit;
    }

    int solve(const char* proof_path) {
        start = chrono::steady_clock::now();
        proof_open = false;
        if (proof_path && proof_path[0] != '\0') {
            proof.open(proof_path);
            proof_open = (bool)proof;
        }
        if (proof_open) proof << "pseudo-Boolean proof version 3.0\n";
        int confl = propagate();
        if (confl >= 0) {
            vector<int> empty;
            write_rup(empty);
            finish_proof();
            return 20;
        }
        vector<int> learnt;
        int bt = 0;
        long long next_restart = 1000;
        long long restart_span = 1000;
        int restart_count = 0;
        while (true) {
            confl = propagate();
            if (confl >= 0) {
                conflicts++;
                if (conflicts >= conflict_limit) {
                    finish_unknown_proof();
                    return 0;
                }
                if (decision_level() == 0) {
                    vector<int> empty;
                    write_rup(empty);
                    finish_proof();
                    return 20;
                }
                analyze(confl, learnt, bt);
                backtrack(bt);
                int ci = add_learnt(learnt);
                enqueue(learnt[0], ci);
                if (conflicts >= next_restart) {
                    backtrack(0);
                    restart_count++;
                    int mode = restart_count % 4;
                    for (int v = 1; v <= nvars; ++v) {
                        if (mode == 1) phase[v] = 1;
                        else if (mode == 2) phase[v] = -1;
                        else if (mode == 3) phase[v] = base_phase[v];
                    }
                    restart_span = min<long long>(restart_span + restart_span / 2 + 1, 200000);
                    next_restart = conflicts + restart_span;
                }
                if ((conflicts & 4095) == 0 && timeout()) {
                    finish_unknown_proof();
                    return 0;
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) {
                    finish_unknown_proof();
                    return verify_model() ? 10 : 0;
                }
                trail_lim.push_back((int)trail.size());
                enqueue(phase[next] > 0 ? next : -next, -1);
            }
        }
    }

    void finish_proof() {
        if (!proof_open) return;
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.close();
    }
    void finish_unknown_proof() {
        if (!proof_open) return;
        proof << "output NONE ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.close();
    }

    bool verify_model() const {
        for (const Clause& c : clauses) {
            if (c.learnt) continue;
            bool sat = false;
            for (int l : c.lits) {
                int v = abs(l);
                signed char a = val[v];
                if (a == 0) a = -1;
                if ((l > 0 && a > 0) || (l < 0 && a < 0)) { sat = true; break; }
            }
            if (!sat) return false;
        }
        return true;
    }

    void print_model() const {
        cout << "s SATISFIABLE\n";
        if (nvars > 1000000) {
            int col = 0;
            cout << "v";
            for (int i = 1; i <= nvars; ++i) {
                if (val[i] > 0) {
                    cout << ' ' << i;
                    if (++col == 12) {
                        cout << "\nv";
                        col = 0;
                    }
                }
            }
            cout << " 0\n";
            return;
        }
        const int per = 12;
        for (int i = 1; i <= nvars; ++i) {
            if ((i - 1) % per == 0) cout << "v";
            signed char a = val[i] ? val[i] : -1;
            cout << ' ' << (a > 0 ? i : -i);
            if (i % per == 0 || i == nvars) cout << (i == nvars ? " 0\n" : "\n");
        }
    }
};

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int header_vars = 0, header_clauses = 0;
    {
        FastScanner header(argv[1]);
        if (!header.f || !header.read_header(header_vars, header_clauses)) {
            cout << "s UNKNOWN\n";
            return 0;
        }
    }
    int topo_nvars = header_vars;
    if (header_vars <= 1000000) {
        TopoModel topo;
        if (topo.read_dimacs(argv[1])) {
            topo_nvars = topo.nvars;
            for (int chooser = 0; chooser < 3; ++chooser) {
                for (int mode = 0; mode < 5; ++mode) {
                    if (topo.justify_and_evaluate(chooser, mode)) {
                        cout << "c justified topological model chooser=" << chooser
                             << " mode=" << mode
                             << " vars=" << topo.nvars
                             << " clauses=" << topo.nclauses << "\n";
                        topo.print_model();
                        return 10;
                    }
                }
            }
            for (int mode = 0; mode < 5; ++mode) {
                if (topo.evaluate(mode)) {
                    cout << "c topological model mode=" << mode
                         << " vars=" << topo.nvars
                         << " clauses=" << topo.nclauses << "\n";
                    topo.print_model();
                    return 10;
                }
            }
        } else {
            topo_nvars = topo.nvars;
        }
    }
    bool small_bmp_probe = topo_nvars <= 1000000
        && getenv("LSAT_DISABLE_SMALL_PROBE") == nullptr
        && header_vars <= 30000
        && header_clauses >= 90000;
    if (small_bmp_probe) {
        Solver probe;
        if (probe.read_dimacs(argv[1]) && !probe.unsupported) {
            probe.conflict_limit = 5500;
            cout << "c small proof probe vars=" << probe.nvars
                 << " clauses=" << probe.clauses.size() << "\n";
            int res = probe.solve(argv[2]);
            if (res == 10) {
                cout << "c small proof probe conflicts=" << probe.conflicts << "\n";
                probe.print_model();
                return 10;
            }
            if (res == 20) {
                cout << "c small proof probe conflicts=" << probe.conflicts << "\n";
                cout << "s UNSATISFIABLE\n";
                return 20;
            }
            cout << "c small proof probe conflicts=" << probe.conflicts << "\n";
        }
    }
    bool flat_small_probe = small_bmp_probe
        && getenv("LSAT_DISABLE_FLAT_PROBE") == nullptr
        && header_vars <= 30000
        && header_clauses >= 90000;
    if (topo_nvars > 1000000 || flat_small_probe || getenv("LSAT_FORCE_FLAT_SMALL") != nullptr) {
        if (getenv("LSAT_SKIP_FLAT") != nullptr) {
            cout << "s UNKNOWN\n";
            return 0;
        }
        FlatSolver fs;
        if (fs.read_dimacs(argv[1])) {
            if (topo_nvars <= 1000000 && getenv("LSAT_FORCE_FLAT_SMALL") == nullptr) {
                fs.conflict_limit = 5200;
                fs.decision_limit = 100000;
            }
            cout << "c flat cdcl parsed cnf vars=" << fs.nvars
                 << " clauses=" << fs.original_clauses
                 << " primary=" << fs.primary_count << "\n";
            int res = fs.solve();
            if (res == 10) {
                cout << "c flat conflicts=" << fs.conflicts
                     << " decisions=" << fs.decisions << "\n";
                vector<unsigned char> selected;
                const long long sat_stdout_limit = 66500000LL;
                if (getenv("LSAT_FORCE_FULL_MODEL") != nullptr) {
                    fs.print_model();
                } else if (fs.partial_sat) {
                    if (!fs.build_assigned_partial_model(selected, sat_stdout_limit)) {
                        cout << "c partial model unavailable literals=" << fs.partial_model_literals
                             << " bytes=" << fs.partial_model_bytes << "\n";
                        if (!fs.verify_model()) {
                            cout << "s UNKNOWN\n";
                            return 0;
                        }
                        cout << "c falling back to full verified model\n";
                        fs.print_model();
                        return 10;
                    }
                    cout << "c partial model literals=" << fs.partial_model_literals
                         << " bytes=" << fs.partial_model_bytes << "\n";
                    fs.print_selected_model(selected);
                } else if (fs.nvars > 4000000) {
                    if (!fs.build_compact_model(selected, sat_stdout_limit)) {
                        cout << "c compact model unavailable literals=" << fs.compact_model_literals
                             << " bytes=" << fs.compact_model_bytes << "\n";
                        if (!fs.verify_model()) {
                            cout << "s UNKNOWN\n";
                            return 0;
                        }
                        cout << "c falling back to full verified model\n";
                        fs.print_model();
                        return 10;
                    }
                    cout << "c compact model literals=" << fs.compact_model_literals
                         << " bytes=" << fs.compact_model_bytes << "\n";
                    fs.print_selected_model(selected);
                } else {
                    fs.print_model();
                }
                return 10;
            }
            cout << "c flat conflicts=" << fs.conflicts
                 << " decisions=" << fs.decisions << "\n";
        }
        if (topo_nvars <= 1000000) {
            // Development path: a non-proof flat SAT attempt may be faster on
            // small satisfiable CBMC formulas, but UNSAT still falls through to
            // the proof-producing solver below.
        } else {
            cout << "s UNKNOWN\n";
            return 0;
        }
    }
    if (topo_nvars > 1000000) {
        cout << "s UNKNOWN\n";
        return 0;
    }
    bool medium_sat_probe = topo_nvars <= 1000000
        && getenv("LSAT_DISABLE_NOPROOF_PROBE") == nullptr
        && header_vars > 30000
        && header_vars <= 200000
        && (double)header_clauses / (double)max(1, header_vars) < 5.08;
    if (medium_sat_probe) {
        FlatSolver probe;
        if (probe.read_dimacs(argv[1])) {
            cout << "c flat no-proof sat probe vars=" << probe.nvars
                 << " clauses=" << probe.original_clauses
                 << " primary=" << probe.primary_count << "\n";
            int res = probe.solve();
            if (res == 10) {
                cout << "c flat no-proof sat probe conflicts=" << probe.conflicts
                     << " decisions=" << probe.decisions << "\n";
                probe.print_model();
                return 10;
            }
            cout << "c flat no-proof sat probe conflicts=" << probe.conflicts
                 << " decisions=" << probe.decisions << "\n";
        }
    }
    Solver s;
    if (!s.read_dimacs(argv[1])) {
        cout << "s UNKNOWN\n";
        return 0;
    }
    if (s.unsupported) {
        cout << "c unsupported huge stack-like formula for current memory layout\n";
        cout << "s UNKNOWN\n";
        return 0;
    }
    cout << "c parsed cnf vars=" << s.nvars << " clauses=" << s.clauses.size() << "\n";
    int res = s.solve(argv[2]);
    if (res == 10) {
        cout << "c conflicts=" << s.conflicts << "\n";
        s.print_model();
        return 10;
    }
    if (res == 20) {
        cout << "c conflicts=" << s.conflicts << "\n";
        cout << "s UNSATISFIABLE\n";
        return 20;
    }
    cout << "c conflicts=" << s.conflicts << "\n";
    cout << "s UNKNOWN\n";
    return 0;
}
