#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using Clock = std::chrono::steady_clock;

static inline int lit_index(int lit) {
    int v = lit > 0 ? lit : -lit;
    return (v << 1) ^ (lit < 0);
}

struct Formula {
    int vars = 0;
    int declared_clauses = 0;
    std::vector<int> starts;
    std::vector<int> lens;
    std::vector<int> lits;
};

struct FastParser {
    std::vector<char> buf;
    const char* p = nullptr;
    const char* e = nullptr;

    explicit FastParser(const char* path) {
        FILE* f = std::fopen(path, "rb");
        if (!f) return;
        std::fseek(f, 0, SEEK_END);
        long sz = std::ftell(f);
        std::fseek(f, 0, SEEK_SET);
        if (sz < 0) sz = 0;
        buf.resize(static_cast<size_t>(sz) + 1);
        size_t got = std::fread(buf.data(), 1, static_cast<size_t>(sz), f);
        std::fclose(f);
        buf[got] = 0;
        p = buf.data();
        e = buf.data() + got;
    }

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

    void skip_line() {
        while (p < e && *p != '\n') ++p;
        if (p < e) ++p;
    }

    void skip_ws() {
        while (p < e && static_cast<unsigned char>(*p) <= ' ') ++p;
    }

    bool read_int(int& out) {
        skip_ws();
        while (p < e && (*p == 'c' || *p == 'p')) return false;
        if (p >= e) return false;
        int sign = 1;
        if (*p == '-') {
            sign = -1;
            ++p;
        }
        int v = 0;
        bool any = false;
        while (p < e && *p >= '0' && *p <= '9') {
            any = true;
            v = v * 10 + (*p - '0');
            ++p;
        }
        out = sign * v;
        return any;
    }
};

static bool parse_dimacs(const char* path, Formula& f) {
    FastParser fp(path);
    if (!fp.ok()) return false;
    std::vector<int> cur;
    while (fp.p < fp.e) {
        fp.skip_ws();
        if (fp.p >= fp.e) break;
        if (*fp.p == 'c') {
            fp.skip_line();
            continue;
        }
        if (*fp.p == 'p') {
            const char* header = fp.p;
            fp.skip_line();
            std::sscanf(header, "p cnf %d %d", &f.vars, &f.declared_clauses);
            continue;
        }
        int x = 0;
        if (!fp.read_int(x)) return false;
        if (x == 0) {
            f.starts.push_back(static_cast<int>(f.lits.size()));
            f.lens.push_back(static_cast<int>(cur.size()));
            f.lits.insert(f.lits.end(), cur.begin(), cur.end());
            cur.clear();
        } else {
            cur.push_back(x);
        }
    }
    if (!cur.empty()) return false;
    return f.vars > 0;
}

struct CSP {
    int rows = 0;
    std::vector<int> dom_size;
    std::vector<uint64_t> full;
    std::vector<std::vector<uint64_t>> allowed; // (i * rows + j) -> masks per value of i.

    std::vector<std::vector<int>> direct_lits;
    bool is_log = false;
    std::vector<std::array<int, 5>> log_vars;
    std::vector<std::vector<int>> log_codes;
};

static inline uint64_t full_mask(int d) {
    return d >= 64 ? ~0ULL : ((1ULL << d) - 1ULL);
}

static inline std::vector<uint64_t>& allow_ref(CSP& c, int i, int j) {
    return c.allowed[i * c.rows + j];
}

static inline const std::vector<uint64_t>& allow_cref(const CSP& c, int i, int j) {
    return c.allowed[i * c.rows + j];
}

static void init_allowed(CSP& c) {
    c.full.resize(c.rows);
    for (int i = 0; i < c.rows; ++i) c.full[i] = full_mask(c.dom_size[i]);
    c.allowed.assign(c.rows * c.rows, {});
    for (int i = 0; i < c.rows; ++i) {
        for (int j = 0; j < c.rows; ++j) {
            allow_ref(c, i, j).assign(c.dom_size[i], c.full[j]);
        }
    }
}

static bool build_direct_csp(const Formula& f, CSP& c) {
    std::vector<int> long_ids;
    std::vector<int> bin_ids;
    for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
        int len = f.lens[ci];
        if (len == 2) {
            bin_ids.push_back(ci);
        } else if (len > 2) {
            if (len > 64) return false;
            long_ids.push_back(ci);
        } else {
            return false;
        }
    }
    if (long_ids.empty() || long_ids.size() > 256) return false;

    std::vector<int> seen_var(f.vars + 1, 0);
    std::vector<int> option_of_lit(2 * (f.vars + 1) + 2, -1);
    std::vector<int> opt_row;
    std::vector<int> opt_val;

    c.rows = static_cast<int>(long_ids.size());
    c.dom_size.assign(c.rows, 0);
    c.direct_lits.assign(c.rows, {});
    c.is_log = false;

    int option = 0;
    for (int r = 0; r < c.rows; ++r) {
        int ci = long_ids[r];
        int st = f.starts[ci], len = f.lens[ci];
        c.dom_size[r] = len;
        c.direct_lits[r].reserve(len);
        std::vector<int> local_abs;
        local_abs.reserve(len);
        for (int k = 0; k < len; ++k) {
            int lit = f.lits[st + k];
            int v = lit > 0 ? lit : -lit;
            if (v <= 0 || v > f.vars || seen_var[v]) return false;
            seen_var[v] = 1;
            c.direct_lits[r].push_back(lit);
            option_of_lit[lit_index(lit)] = option;
            opt_row.push_back(r);
            opt_val.push_back(k);
            ++option;
            local_abs.push_back(v);
        }
        std::sort(local_abs.begin(), local_abs.end());
        if (std::adjacent_find(local_abs.begin(), local_abs.end()) != local_abs.end()) return false;
    }
    for (int v = 1; v <= f.vars; ++v) {
        if (!seen_var[v]) return false;
    }

    init_allowed(c);
    for (int ci : bin_ids) {
        int st = f.starts[ci];
        int a = f.lits[st], b = f.lits[st + 1];
        int ia = option_of_lit[lit_index(-a)];
        int ib = option_of_lit[lit_index(-b)];
        if (ia < 0 || ib < 0) return false;
        int ra = opt_row[ia], va = opt_val[ia];
        int rb = opt_row[ib], vb = opt_val[ib];
        if (ra == rb) continue;
        allow_ref(c, ra, rb)[va] &= ~(1ULL << vb);
        allow_ref(c, rb, ra)[vb] &= ~(1ULL << va);
    }
    return true;
}

struct ArrHash {
    size_t operator()(const std::array<int, 5>& a) const {
        uint64_t h = 1469598103934665603ULL;
        for (int x : a) {
            h ^= static_cast<uint64_t>(x + 0x9e3779b9);
            h *= 1099511628211ULL;
        }
        return static_cast<size_t>(h);
    }
};

static int code_from_lits(const int* lits, int count, const std::array<int, 5>& vars) {
    if (count != 5) return -1;
    int code = 0;
    for (int i = 0; i < count; ++i) {
        int lit = lits[i];
        int v = lit > 0 ? lit : -lit;
        auto it = std::find(vars.begin(), vars.end(), v);
        if (it == vars.end()) return -1;
        int pos = static_cast<int>(it - vars.begin());
        if (lit < 0) code |= (1 << pos);
    }
    return code;
}

static bool build_log_csp(const Formula& f, CSP& c) {
    std::vector<int> five_ids;
    std::vector<int> ten_ids;
    for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
        if (f.lens[ci] == 5) five_ids.push_back(ci);
        else if (f.lens[ci] == 10) ten_ids.push_back(ci);
        else return false;
    }
    if (five_ids.empty() || f.vars % 5 != 0) return false;

    std::unordered_map<std::array<int, 5>, std::vector<int>, ArrHash> groups_map;
    for (int ci : five_ids) {
        std::array<int, 5> key{};
        int st = f.starts[ci];
        for (int k = 0; k < 5; ++k) key[k] = std::abs(f.lits[st + k]);
        std::sort(key.begin(), key.end());
        groups_map[key].push_back(ci);
    }
    if (groups_map.empty()) return false;

    c.rows = static_cast<int>(groups_map.size());
    if (c.rows * 5 != f.vars) return false;
    c.is_log = true;
    c.dom_size.assign(c.rows, 30);
    c.log_vars.clear();
    c.log_codes.assign(c.rows, {});

    std::vector<int> var_group(f.vars + 1, -1), var_pos(f.vars + 1, -1);
    std::vector<std::array<int, 32>> code_to_val(c.rows);
    int row = 0;
    for (auto& kv : groups_map) {
        if (kv.second.size() != 2) return false;
        auto vars = kv.first;
        c.log_vars.push_back(vars);
        for (int p = 0; p < 5; ++p) {
            int v = vars[p];
            if (v <= 0 || v > f.vars || var_group[v] >= 0) return false;
            var_group[v] = row;
            var_pos[v] = p;
        }
        std::array<int, 32> invalid{};
        invalid.fill(0);
        for (int ci : kv.second) {
            int code = code_from_lits(&f.lits[f.starts[ci]], 5, vars);
            if (code < 0) return false;
            invalid[code] = 1;
        }
        code_to_val[row].fill(-1);
        for (int code = 0; code < 32; ++code) {
            if (!invalid[code]) {
                code_to_val[row][code] = static_cast<int>(c.log_codes[row].size());
                c.log_codes[row].push_back(code);
            }
        }
        if (c.log_codes[row].size() != 30) return false;
        ++row;
    }
    for (int v = 1; v <= f.vars; ++v) {
        if (var_group[v] < 0) return false;
    }

    init_allowed(c);
    std::array<int, 5> part_lits_a{}, part_lits_b{};
    for (int ci : ten_ids) {
        int st = f.starts[ci];
        int g0 = -1, g1 = -1, n0 = 0, n1 = 0;
        for (int k = 0; k < 10; ++k) {
            int lit = f.lits[st + k];
            int v = std::abs(lit);
            int g = var_group[v];
            if (g < 0) return false;
            if (g0 < 0 || g == g0) {
                g0 = g;
                if (n0 >= 5) return false;
                part_lits_a[n0++] = lit;
            } else if (g1 < 0 || g == g1) {
                g1 = g;
                if (n1 >= 5) return false;
                part_lits_b[n1++] = lit;
            } else {
                return false;
            }
        }
        if (g0 < 0 || g1 < 0 || g0 == g1 || n0 != 5 || n1 != 5) return false;
        int code0 = code_from_lits(part_lits_a.data(), 5, c.log_vars[g0]);
        int code1 = code_from_lits(part_lits_b.data(), 5, c.log_vars[g1]);
        if (code0 < 0 || code1 < 0) return false;
        int v0 = code_to_val[g0][code0];
        int v1 = code_to_val[g1][code1];
        if (v0 < 0 || v1 < 0) continue;
        allow_ref(c, g0, g1)[v0] &= ~(1ULL << v1);
        allow_ref(c, g1, g0)[v1] &= ~(1ULL << v0);
    }
    return true;
}

struct XorShift64 {
    uint64_t s;
    explicit XorShift64(uint64_t seed) : s(seed ? seed : 88172645463393265ULL) {}
    uint64_t next() {
        uint64_t x = s;
        x ^= x << 7;
        x ^= x >> 9;
        s = x;
        return x;
    }
    int uniform(int n) { return static_cast<int>(next() % static_cast<uint64_t>(n)); }
};

struct Searcher {
    const CSP& c;
    Clock::time_point deadline;
    uint64_t nodes = 0;
    bool timed_out = false;
    std::vector<int> row_weight;

    explicit Searcher(const CSP& csp, double seconds)
        : c(csp), deadline(Clock::now() + std::chrono::milliseconds(static_cast<int64_t>(seconds * 1000.0))) {
        row_weight.assign(c.rows, 0);
        for (int i = 0; i < c.rows; ++i) {
            for (int j = 0; j < c.rows; ++j) {
                if (i == j) continue;
                int allowed_pairs = 0;
                for (int a = 0; a < c.dom_size[i]; ++a) {
                    allowed_pairs += __builtin_popcountll(allow_cref(c, i, j)[a]);
                }
                row_weight[i] += c.dom_size[i] * c.dom_size[j] - allowed_pairs;
            }
        }
    }

    bool time_left() {
        if ((nodes & 4095ULL) == 0 && Clock::now() >= deadline) {
            timed_out = true;
            return false;
        }
        return true;
    }

    bool propagate(std::vector<uint64_t>& dom) {
        bool changed = true;
        while (changed) {
            changed = false;
            for (int i = 0; i < c.rows; ++i) {
                uint64_t m = dom[i];
                if (m == 0) return false;
                uint64_t nm = m;
                while (m) {
                    uint64_t bit = m & -m;
                    int a = __builtin_ctzll(m);
                    m -= bit;
                    bool supported = true;
                    for (int j = 0; j < c.rows; ++j) {
                        if (i == j) continue;
                        if ((dom[j] & allow_cref(c, i, j)[a]) == 0) {
                            supported = false;
                            break;
                        }
                    }
                    if (!supported) nm &= ~bit;
                }
                if (nm != dom[i]) {
                    if (nm == 0) return false;
                    dom[i] = nm;
                    changed = true;
                }
            }
        }
        return true;
    }

    int conflict_count(const std::vector<int>& asg, int row = -1, int val = -1) const {
        int conflicts = 0;
        for (int i = 0; i < c.rows; ++i) {
            int vi = (i == row) ? val : asg[i];
            for (int j = i + 1; j < c.rows; ++j) {
                int vj = (j == row) ? val : asg[j];
                if (((allow_cref(c, i, j)[vi] >> vj) & 1ULL) == 0) ++conflicts;
            }
        }
        return conflicts;
    }

    bool min_conflicts(const std::vector<uint64_t>& domains, std::vector<uint64_t>& sol) {
        uint64_t seed = 0x9e3779b97f4a7c15ULL ^ static_cast<uint64_t>(c.rows);
        for (int d : c.dom_size) seed = seed * 1315423911ULL + static_cast<uint64_t>(d);
        XorShift64 rng(seed);
        std::vector<int> values(c.rows, 0);
        std::vector<int> conflicted;
        const int restarts = c.rows <= 35 ? 80 : 140;
        const int steps_per_restart = std::max(2000, c.rows * c.rows * 12);

        for (int restart = 0; restart < restarts && Clock::now() < deadline; ++restart) {
            for (int i = 0; i < c.rows; ++i) {
                std::vector<int> vals;
                uint64_t m = domains[i];
                while (m) {
                    int a = __builtin_ctzll(m);
                    vals.push_back(a);
                    m &= m - 1;
                }
                values[i] = vals[rng.uniform(static_cast<int>(vals.size()))];
            }
            for (int step = 0; step < steps_per_restart; ++step) {
                conflicted.clear();
                int total = 0;
                std::vector<int> row_conf(c.rows, 0);
                for (int i = 0; i < c.rows; ++i) {
                    for (int j = i + 1; j < c.rows; ++j) {
                        if (((allow_cref(c, i, j)[values[i]] >> values[j]) & 1ULL) == 0) {
                            ++total;
                            ++row_conf[i];
                            ++row_conf[j];
                        }
                    }
                }
                if (total == 0) {
                    sol.assign(c.rows, 0);
                    for (int i = 0; i < c.rows; ++i) sol[i] = 1ULL << values[i];
                    return true;
                }
                for (int i = 0; i < c.rows; ++i) {
                    if (row_conf[i]) conflicted.push_back(i);
                }
                int r = conflicted[rng.uniform(static_cast<int>(conflicted.size()))];
                int best = 1 << 30;
                std::vector<int> best_vals;
                uint64_t m = domains[r];
                while (m) {
                    int a = __builtin_ctzll(m);
                    m &= m - 1;
                    int sc = 0;
                    for (int j = 0; j < c.rows; ++j) {
                        if (j == r) continue;
                        if (((allow_cref(c, r, j)[a] >> values[j]) & 1ULL) == 0) ++sc;
                    }
                    if (sc < best) {
                        best = sc;
                        best_vals.clear();
                        best_vals.push_back(a);
                    } else if (sc == best) {
                        best_vals.push_back(a);
                    }
                }
                if (!best_vals.empty()) values[r] = best_vals[rng.uniform(static_cast<int>(best_vals.size()))];
            }
        }
        return false;
    }

    bool dfs(std::vector<uint64_t>& dom, std::vector<uint64_t>& sol) {
        ++nodes;
        if (!time_left()) return false;
        if (!propagate(dom)) return false;

        int best_row = -1;
        int best_count = 1000000;
        for (int i = 0; i < c.rows; ++i) {
            int bc = __builtin_popcountll(dom[i]);
            if (bc > 1 && (bc < best_count ||
                           (bc == best_count && row_weight[i] > row_weight[best_row]))) {
                best_count = bc;
                best_row = i;
            }
        }
        if (best_row < 0) {
            sol = dom;
            return true;
        }

        struct Candidate {
            int val;
            int score;
        };
        std::vector<Candidate> vals;
        uint64_t m = dom[best_row];
        while (m) {
            int a = __builtin_ctzll(m);
            m &= m - 1;
            int score = 0;
            for (int j = 0; j < c.rows; ++j) {
                if (j == best_row) continue;
                score += __builtin_popcountll(dom[j] & allow_cref(c, best_row, j)[a]);
            }
            vals.push_back({a, score});
        }
        std::sort(vals.begin(), vals.end(), [](const Candidate& a, const Candidate& b) {
            if (a.score != b.score) return a.score > b.score;
            return a.val < b.val;
        });

        for (const Candidate& cand : vals) {
            std::vector<uint64_t> next = dom;
            next[best_row] = 1ULL << cand.val;
            if (dfs(next, sol)) return true;
            if (timed_out) return false;
        }
        return false;
    }

    bool solve(std::vector<uint64_t>& sol) {
        std::vector<uint64_t> dom = c.full;
        if (!propagate(dom)) return false;
        if (min_conflicts(dom, sol)) return true;
        return dfs(dom, sol);
    }
};

static bool build_assignment(const CSP& c, const std::vector<uint64_t>& sol, int vars, std::vector<int8_t>& truth) {
    truth.assign(vars + 1, 0);
    if (!c.is_log) {
        for (int r = 0; r < c.rows; ++r) {
            if (__builtin_popcountll(sol[r]) != 1) return false;
            int chosen = __builtin_ctzll(sol[r]);
            for (int a = 0; a < c.dom_size[r]; ++a) {
                int lit = c.direct_lits[r][a];
                int v = std::abs(lit);
                bool lit_true = (a == chosen);
                bool var_true = lit > 0 ? lit_true : !lit_true;
                truth[v] = static_cast<int8_t>(var_true);
            }
        }
    } else {
        for (int r = 0; r < c.rows; ++r) {
            if (__builtin_popcountll(sol[r]) != 1) return false;
            int val = __builtin_ctzll(sol[r]);
            int code = c.log_codes[r][val];
            for (int p = 0; p < 5; ++p) {
                int v = c.log_vars[r][p];
                truth[v] = static_cast<int8_t>((code >> p) & 1);
            }
        }
    }
    return true;
}

static bool try_ordered_deletion_embedding(const CSP& c, std::vector<uint64_t>& sol) {
    if (c.is_log || c.rows <= 0) return false;
    int h = 0;
    for (int d : c.dom_size) h = std::max(h, d);
    int deleted = h - c.rows;
    if (h <= 0 || h > 63 || deleted < 0 || deleted > 8) return false;

    int previous_max = 0;
    for (int r = 0; r < c.rows; ++r) {
        int last = previous_max;
        for (int lit : c.direct_lits[r]) {
            if (lit <= 0 || lit <= last) return false;
            last = lit;
        }
        if (!c.direct_lits[r].empty() && c.direct_lits[r].front() <= previous_max) return false;
        previous_max = last;
    }

    std::vector<int> full_rows;
    for (int r = 0; r < c.rows; ++r) {
        if (c.dom_size[r] == h) full_rows.push_back(r);
    }
    if (full_rows.empty()) return false;

    std::vector<std::vector<uint64_t>> possible_labels(c.rows);
    for (int r = 0; r < c.rows; ++r) {
        possible_labels[r].assign(c.dom_size[r], 0);
        if (c.dom_size[r] == h) {
            for (int a = 0; a < h; ++a) possible_labels[r][a] = 1ULL << a;
        } else {
            for (int a = 0; a < c.dom_size[r]; ++a) {
                uint64_t inter = full_mask(h);
                for (int fr : full_rows) {
                    if (fr == r) continue;
                    uint64_t conflict = 0;
                    const auto& ar = allow_cref(c, r, fr);
                    for (int b = 0; b < h; ++b) {
                        if (((ar[a] >> b) & 1ULL) == 0) conflict |= (1ULL << b);
                    }
                    inter &= conflict;
                }
                int lb = a;
                int ub = h - (c.dom_size[r] - a);
                uint64_t bounds = 0;
                for (int label = lb; label <= ub; ++label) bounds |= 1ULL << label;
                inter &= bounds;
                if (inter == 0) return false;
                possible_labels[r][a] = inter;
            }
        }
    }

    std::vector<int> kept;
    kept.reserve(c.rows);

    auto check_kept = [&]() -> bool {
        std::vector<uint64_t> dom(c.rows, 0);
        for (int r = 0; r < c.rows; ++r) {
            uint64_t want = 1ULL << kept[r];
            for (int a = 0; a < c.dom_size[r]; ++a) {
                if (possible_labels[r][a] & want) dom[r] |= 1ULL << a;
            }
            if (dom[r] == 0) return false;
        }

        auto propagate_small = [&]() -> bool {
            bool changed = true;
            while (changed) {
                changed = false;
                for (int i = 0; i < c.rows; ++i) {
                    uint64_t m = dom[i];
                    if (m == 0) return false;
                    uint64_t nm = m;
                    while (m) {
                        uint64_t bit = m & -m;
                        int a = __builtin_ctzll(m);
                        m -= bit;
                        bool ok = true;
                        for (int j = 0; j < c.rows; ++j) {
                            if (i == j) continue;
                            if ((dom[j] & allow_cref(c, i, j)[a]) == 0) {
                                ok = false;
                                break;
                            }
                        }
                        if (!ok) nm &= ~bit;
                    }
                    if (nm != dom[i]) {
                        if (nm == 0) return false;
                        dom[i] = nm;
                        changed = true;
                    }
                }
            }
            return true;
        };

        int nodes = 0;
        auto dfs_small = [&](auto&& self) -> bool {
            if (++nodes > 20000) return false;
            if (!propagate_small()) return false;
            int best = -1, best_count = 1000000;
            for (int i = 0; i < c.rows; ++i) {
                int pc = __builtin_popcountll(dom[i]);
                if (pc > 1 && pc < best_count) {
                    best_count = pc;
                    best = i;
                }
            }
            if (best < 0) {
                sol = dom;
                return true;
            }
            struct Cand { int val; int score; };
            std::vector<Cand> vals;
            uint64_t m = dom[best];
            while (m) {
                int a = __builtin_ctzll(m);
                m &= m - 1;
                int score = 0;
                for (int j = 0; j < c.rows; ++j) {
                    if (j != best) score += __builtin_popcountll(dom[j] & allow_cref(c, best, j)[a]);
                }
                vals.push_back({a, score});
            }
            std::sort(vals.begin(), vals.end(), [](const Cand& x, const Cand& y) {
                if (x.score != y.score) return x.score > y.score;
                return x.val < y.val;
            });
            std::vector<uint64_t> saved = dom;
            for (const auto& cand : vals) {
                dom = saved;
                dom[best] = 1ULL << cand.val;
                if (self(self)) return true;
            }
            dom = saved;
            return false;
        };

        return dfs_small(dfs_small);
    };

    bool found = false;
    auto rec = [&](auto&& self, int label, int kept_count, int del_count) -> void {
        if (found) return;
        if (kept_count == c.rows) {
            if (del_count + (h - label) == deleted && check_kept()) found = true;
            return;
        }
        if (label >= h) return;
        int remaining = h - label;
        if (kept_count + remaining < c.rows) return;
        if (del_count > deleted) return;

        // Keep this host label for the next pattern row.
        kept.push_back(label);
        self(self, label + 1, kept_count + 1, del_count);
        kept.pop_back();
        if (found) return;

        // Delete this host label from the ordered embedding.
        if (del_count < deleted) self(self, label + 1, kept_count, del_count + 1);
    };
    rec(rec, 0, 0, 0);
    return found;
}

static bool verify_formula(const Formula& f, const std::vector<int8_t>& truth) {
    for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
        int st = f.starts[ci], len = f.lens[ci];
        bool sat = false;
        for (int k = 0; k < len; ++k) {
            int lit = f.lits[st + k];
            int v = std::abs(lit);
            bool val = truth[v] != 0;
            if ((lit > 0 && val) || (lit < 0 && !val)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model(const std::vector<int8_t>& truth) {
    std::printf("s SATISFIABLE\n");
    int col = 0;
    std::printf("v");
    for (int v = 1; v < static_cast<int>(truth.size()); ++v) {
        if (col >= 20) {
            std::printf(" 0\nv");
            col = 0;
        }
        std::printf(" %d", truth[v] ? v : -v);
        ++col;
    }
    std::printf(" 0\n");
}

static void fprint_pb_lit(FILE* out, int lit) {
    if (lit > 0) std::fprintf(out, "1 x%d ", lit);
    else std::fprintf(out, "1 ~x%d ", -lit);
}

struct DirectProofMAC {
    const CSP& c;
    Clock::time_point deadline;
    FILE* out = nullptr;
    bool timed_out = false;
    bool failed = false;
    uint64_t nodes = 0;
    uint64_t rup_count = 0;
    std::vector<std::pair<int, int>> path;
    std::vector<int> row_weight;

    struct State {
        std::vector<uint64_t> dom;
        std::vector<std::vector<uint64_t>> reason;
        std::vector<int> assigned_val;
        std::vector<int> assigned_bit;
    };

    explicit DirectProofMAC(const CSP& csp, double seconds)
        : c(csp), deadline(Clock::now() + std::chrono::milliseconds(static_cast<int64_t>(seconds * 1000.0))) {
        row_weight.assign(c.rows, 0);
        for (int i = 0; i < c.rows; ++i) {
            for (int j = 0; j < c.rows; ++j) {
                if (i == j) continue;
                for (int a = 0; a < c.dom_size[i]; ++a) {
                    row_weight[i] += c.dom_size[j] - __builtin_popcountll(allow_cref(c, i, j)[a]);
                }
            }
        }
    }

    bool time_left() {
        if ((nodes & 1023ULL) == 0 && Clock::now() >= deadline) {
            timed_out = true;
            return false;
        }
        return true;
    }

    bool write_rup(uint64_t mask, int extra_row = -1, int extra_val = -1) {
        if (!out) return false;
        std::fprintf(out, "rup ");
        uint64_t m = mask;
        while (m) {
            uint64_t bit = m & -m;
            int idx = __builtin_ctzll(m);
            m -= bit;
            int r = path[idx].first;
            int a = path[idx].second;
            fprint_pb_lit(out, -c.direct_lits[r][a]);
        }
        if (extra_row >= 0) fprint_pb_lit(out, -c.direct_lits[extra_row][extra_val]);
        std::fprintf(out, ">= 1 ;\n");
        ++rup_count;
        if ((rup_count & 8191ULL) == 0 && std::fflush(out) != 0) {
            failed = true;
            return false;
        }
        return true;
    }

    uint64_t domain_conflict_mask(const State& s, int row) const {
        uint64_t mask = 0;
        for (int a = 0; a < c.dom_size[row]; ++a) mask |= s.reason[row][a];
        return mask;
    }

    bool remove_value(State& s, int row, int val, uint64_t why, bool log_clause) {
        uint64_t bit = 1ULL << val;
        if ((s.dom[row] & bit) == 0) return true;
        s.dom[row] &= ~bit;
        s.reason[row][val] = why;
        if (log_clause && !write_rup(why, row, val)) return false;
        return true;
    }

    bool propagate(State& s, uint64_t& conflict, bool& has_conflict) {
        has_conflict = false;
        bool changed = true;
        while (changed) {
            changed = false;
            for (int i = 0; i < c.rows; ++i) {
                if (s.assigned_val[i] >= 0) continue;
                if (s.dom[i] == 0) {
                    conflict = domain_conflict_mask(s, i);
                    has_conflict = true;
                    return write_rup(conflict);
                }
            }

            for (int i = 0; i < c.rows; ++i) {
                if (s.assigned_val[i] >= 0) continue;
                uint64_t m = s.dom[i];
                while (m) {
                    uint64_t bit = m & -m;
                    int a = __builtin_ctzll(m);
                    m -= bit;

                    bool remove = false;
                    uint64_t why = 0;
                    bool needs_logged_clause = false;
                    for (int j = 0; j < c.rows; ++j) {
                        if (i == j) continue;
                        const uint64_t allowed = allow_cref(c, i, j)[a];
                        if (s.assigned_val[j] >= 0) {
                            int b = s.assigned_val[j];
                            if (((allowed >> b) & 1ULL) == 0) {
                                remove = true;
                                why = 1ULL << s.assigned_bit[j];
                                needs_logged_clause = false; // Original binary clause explains this deletion.
                                break;
                            }
                        } else if ((s.dom[j] & allowed) == 0) {
                            remove = true;
                            uint64_t supports = allowed & c.full[j];
                            uint64_t reason = 0;
                            while (supports) {
                                uint64_t sb = supports & -supports;
                                int b = __builtin_ctzll(supports);
                                supports -= sb;
                                reason |= s.reason[j][b];
                            }
                            why = reason;
                            needs_logged_clause = true;
                            break;
                        }
                    }
                    if (remove) {
                        if (!remove_value(s, i, a, why, needs_logged_clause)) return false;
                        changed = true;
                        if (s.dom[i] == 0) {
                            conflict = domain_conflict_mask(s, i);
                            has_conflict = true;
                            return write_rup(conflict);
                        }
                    }
                }
            }
        }
        return true;
    }

    bool assign_choice(State& s, int row, int val, int bit_index) {
        s.assigned_val[row] = val;
        s.assigned_bit[row] = bit_index;
        s.dom[row] = 1ULL << val;
        for (int j = 0; j < c.rows; ++j) {
            if (j == row || s.assigned_val[j] >= 0) continue;
            uint64_t bad = s.dom[j] & ~allow_cref(c, row, j)[val];
            while (bad) {
                uint64_t bb = bad & -bad;
                int b = __builtin_ctzll(bad);
                bad -= bb;
                if (!remove_value(s, j, b, 1ULL << bit_index, false)) return false;
            }
        }
        return true;
    }

    int choose_row(const State& s) const {
        int zero_row = -1;
        int best = -1;
        int best_count = 1000000;
        for (int i = 0; i < c.rows; ++i) {
            if (s.assigned_val[i] >= 0) continue;
            int pc = __builtin_popcountll(s.dom[i]);
            if (pc == 0) return i;
            if (pc < best_count || (pc == best_count && (best < 0 || row_weight[i] > row_weight[best]))) {
                best_count = pc;
                best = i;
            }
        }
        return zero_row >= 0 ? zero_row : best;
    }

    bool prove(State& s, uint64_t& conflict, std::vector<uint64_t>& sol) {
        ++nodes;
        if (!time_left()) return false;
        bool has_conflict = false;
        if (!propagate(s, conflict, has_conflict)) return false;
        if (has_conflict) return true;
        if (failed) return false;

        int row = choose_row(s);
        if (row < 0) {
            sol = s.dom;
            return false; // Found a high-level solution, not an UNSAT proof.
        }
        if (s.dom[row] == 0) {
            conflict = domain_conflict_mask(s, row);
            return write_rup(conflict);
        }

        while (s.dom[row]) {
            struct Cand { int val; int score; };
            std::vector<Cand> vals;
            uint64_t m = s.dom[row];
            while (m) {
                uint64_t bit = m & -m;
                int a = __builtin_ctzll(m);
                m -= bit;
                int score = 0;
                for (int j = 0; j < c.rows; ++j) {
                    if (j != row && s.assigned_val[j] < 0) {
                        score += __builtin_popcountll(s.dom[j] & allow_cref(c, row, j)[a]);
                    }
                }
                vals.push_back({a, score});
            }
            std::sort(vals.begin(), vals.end(), [](const Cand& x, const Cand& y) {
                if (x.score != y.score) return x.score < y.score; // Fail-first for proofs.
                return x.val < y.val;
            });

            bool progressed = false;
            for (const Cand& cand : vals) {
                if (((s.dom[row] >> cand.val) & 1ULL) == 0) continue;
                progressed = true;
                if (path.size() >= 63) {
                    timed_out = true;
                    return false;
                }
                int bit_index = static_cast<int>(path.size());
                path.push_back({row, cand.val});
                State child = s;
                if (!assign_choice(child, row, cand.val, bit_index)) {
                    path.pop_back();
                    return false;
                }

                uint64_t child_conflict = 0;
                std::vector<uint64_t> child_sol;
                bool child_unsat = prove(child, child_conflict, child_sol);
                path.pop_back();
                if (!child_unsat) {
                    if (!child_sol.empty()) {
                        sol = child_sol;
                        return false;
                    }
                    return false;
                }
                uint64_t decision_bit = 1ULL << bit_index;
                if ((child_conflict & decision_bit) == 0) {
                    conflict = child_conflict;
                    return true;
                }
                uint64_t why = child_conflict & ~decision_bit;
                if (!remove_value(s, row, cand.val, why, false)) return false;
                bool parent_conflict = false;
                if (!propagate(s, conflict, parent_conflict)) return false;
                if (parent_conflict) return true;
                if (s.dom[row] == 0) {
                    conflict = domain_conflict_mask(s, row);
                    return write_rup(conflict);
                }
                break;
            }
            if (!progressed) break;
        }

        conflict = domain_conflict_mask(s, row);
        return write_rup(conflict);
    }

    enum Result { PROVED_UNSAT, FOUND_SAT, GAVE_UP };

    Result run(const char* proof_path, std::vector<uint64_t>& sol) {
        out = std::fopen(proof_path, "wb");
        if (!out) return GAVE_UP;
        std::fprintf(out, "pseudo-Boolean proof version 3.0\n");
        State s;
        s.dom = c.full;
        s.reason.assign(c.rows, {});
        for (int i = 0; i < c.rows; ++i) s.reason[i].assign(c.dom_size[i], 0);
        s.assigned_val.assign(c.rows, -1);
        s.assigned_bit.assign(c.rows, -1);

        uint64_t conflict = 0;
        bool ok = prove(s, conflict, sol);
        Result res = GAVE_UP;
        if (ok && conflict == 0 && !failed && !timed_out) {
            std::fprintf(out, "output NONE ;\n");
            std::fprintf(out, "conclusion UNSAT : -1 ;\n");
            std::fprintf(out, "end pseudo-Boolean proof ;\n");
            res = PROVED_UNSAT;
        } else if (!sol.empty()) {
            res = FOUND_SAT;
        }
        std::fclose(out);
        out = nullptr;
        return res;
    }
};

static bool generic_sls(const Formula& f, double seconds, std::vector<int8_t>& truth) {
    if (f.vars <= 0 || f.vars > 200000) return false;
    auto deadline = Clock::now() + std::chrono::milliseconds(static_cast<int64_t>(seconds * 1000.0));

    std::vector<std::vector<int>> occ_pos(f.vars + 1), occ_neg(f.vars + 1);
    for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
        int st = f.starts[ci], len = f.lens[ci];
        for (int k = 0; k < len; ++k) {
            int lit = f.lits[st + k];
            int v = std::abs(lit);
            if (v <= 0 || v > f.vars) return false;
            if (lit > 0) occ_pos[v].push_back(ci);
            else occ_neg[v].push_back(ci);
        }
    }

    std::vector<int8_t> fixed(f.vars + 1, -1);
    for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
        if (f.lens[ci] != 1) continue;
        int lit = f.lits[f.starts[ci]];
        int v = std::abs(lit);
        int8_t want = lit > 0 ? 1 : 0;
        if (fixed[v] >= 0 && fixed[v] != want) return false;
        fixed[v] = want;
    }

    uint64_t seed = 0x517cc1b727220a95ULL ^ static_cast<uint64_t>(f.vars);
    seed ^= static_cast<uint64_t>(f.lens.size()) << 21;
    XorShift64 rng(seed);

    std::vector<int8_t> asg(f.vars + 1, 0);
    std::vector<int> sat_count(f.lens.size(), 0);
    std::vector<int> unsat;
    std::vector<int> unsat_pos(f.lens.size(), -1);

    auto add_unsat = [&](int ci) {
        if (unsat_pos[ci] >= 0) return;
        unsat_pos[ci] = static_cast<int>(unsat.size());
        unsat.push_back(ci);
    };
    auto remove_unsat = [&](int ci) {
        int pos = unsat_pos[ci];
        if (pos < 0) return;
        int last = unsat.back();
        unsat[pos] = last;
        unsat_pos[last] = pos;
        unsat.pop_back();
        unsat_pos[ci] = -1;
    };
    auto init_counts = [&]() {
        std::fill(sat_count.begin(), sat_count.end(), 0);
        unsat.clear();
        std::fill(unsat_pos.begin(), unsat_pos.end(), -1);
        for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
            int st = f.starts[ci], len = f.lens[ci];
            int sc = 0;
            for (int k = 0; k < len; ++k) {
                int lit = f.lits[st + k];
                bool val = asg[std::abs(lit)] != 0;
                if ((lit > 0 && val) || (lit < 0 && !val)) ++sc;
            }
            sat_count[ci] = sc;
            if (sc == 0) add_unsat(ci);
        }
    };
    auto flip_var = [&](int v) {
        bool old = asg[v] != 0;
        const std::vector<int>& true_occ = old ? occ_pos[v] : occ_neg[v];
        const std::vector<int>& false_occ = old ? occ_neg[v] : occ_pos[v];
        for (int ci : true_occ) {
            --sat_count[ci];
            if (sat_count[ci] == 0) add_unsat(ci);
        }
        for (int ci : false_occ) {
            if (sat_count[ci] == 0) remove_unsat(ci);
            ++sat_count[ci];
        }
        asg[v] = old ? 0 : 1;
    };
    auto break_count = [&](int v) {
        bool val = asg[v] != 0;
        const std::vector<int>& true_occ = val ? occ_pos[v] : occ_neg[v];
        int b = 0;
        for (int ci : true_occ) {
            if (sat_count[ci] == 1) ++b;
        }
        return b;
    };
    auto make_count = [&](int v) {
        bool val = asg[v] != 0;
        const std::vector<int>& false_occ = val ? occ_neg[v] : occ_pos[v];
        int m = 0;
        for (int ci : false_occ) {
            if (sat_count[ci] == 0) ++m;
        }
        return m;
    };

    int restart = 0;
    while (Clock::now() < deadline) {
        ++restart;
        for (int v = 1; v <= f.vars; ++v) {
            if (fixed[v] >= 0) {
                asg[v] = fixed[v];
            } else {
                int pos = static_cast<int>(occ_pos[v].size());
                int neg = static_cast<int>(occ_neg[v].size());
                bool bias = pos >= neg;
                asg[v] = (rng.uniform(100) < 65) ? static_cast<int8_t>(bias) : static_cast<int8_t>(rng.uniform(2));
            }
        }
        init_counts();
        if (unsat.empty()) {
            truth = asg;
            return true;
        }

        uint64_t flips = 0;
        while (Clock::now() < deadline) {
            if (unsat.empty()) {
                truth = asg;
                return true;
            }
            if ((++flips & 262143ULL) == 0 && flips > static_cast<uint64_t>(std::max(200000, f.vars * 60))) break;
            int ci = unsat[rng.uniform(static_cast<int>(unsat.size()))];
            int st = f.starts[ci], len = f.lens[ci];
            int chosen = 0;
            if (rng.uniform(100) < 35) {
                for (int tries = 0; tries < len * 2; ++tries) {
                    int lit = f.lits[st + rng.uniform(len)];
                    int v = std::abs(lit);
                    if (fixed[v] < 0) {
                        chosen = v;
                        break;
                    }
                }
            }
            if (chosen == 0) {
                int best_score = 1 << 30;
                std::vector<int> ties;
                for (int k = 0; k < len; ++k) {
                    int v = std::abs(f.lits[st + k]);
                    if (fixed[v] >= 0) continue;
                    int score = break_count(v) * 8 - make_count(v) * 3;
                    if (score < best_score) {
                        best_score = score;
                        ties.clear();
                        ties.push_back(v);
                    } else if (score == best_score) {
                        ties.push_back(v);
                    }
                }
                if (ties.empty()) break;
                chosen = ties[rng.uniform(static_cast<int>(ties.size()))];
            }
            flip_var(chosen);
        }
    }
    return false;
}

struct MiniCDCL {
    struct Clause {
        int start;
        int len;
        bool learnt;
    };

    const Formula& f;
    Clock::time_point deadline;
    int vars = 0;
    std::vector<int> lits;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> assigns;
    std::vector<int8_t> phase;
    std::vector<int> level;
    std::vector<int> reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    int qhead = 0;
    std::vector<double> activity;
    double var_inc = 1.0;
    int conflicts = 0;
    bool timed_out = false;
    bool proved_unsat = false;
    std::vector<std::vector<int>> proof_clauses;
    const std::vector<int8_t>* preferred_phase = nullptr;

    MiniCDCL(const Formula& formula, double seconds, const std::vector<int8_t>* pref = nullptr)
        : f(formula),
          deadline(Clock::now() + std::chrono::milliseconds(static_cast<int64_t>(seconds * 1000.0))),
          vars(formula.vars),
          preferred_phase(pref) {}

    int value_lit(int lit) const {
        int v = std::abs(lit);
        int8_t a = assigns[v];
        if (a == 0) return 0;
        bool tv = a > 0;
        bool sat = lit > 0 ? tv : !tv;
        return sat ? 1 : -1;
    }

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

    bool enqueue(int lit, int why) {
        int v = std::abs(lit);
        int val = value_lit(lit);
        if (val == 1) return true;
        if (val == -1) return false;
        assigns[v] = lit > 0 ? 1 : -1;
        phase[v] = assigns[v];
        level[v] = decision_level();
        reason[v] = why;
        trail.push_back(lit);
        return true;
    }

    void attach_clause(int cid) {
        Clause& c = clauses[cid];
        int st = c.start;
        if (c.len == 1) {
            watches[lit_index(lits[st])].push_back(cid);
        } else {
            watches[lit_index(lits[st])].push_back(cid);
            watches[lit_index(lits[st + 1])].push_back(cid);
        }
    }

    bool build() {
        watches.assign(2 * (vars + 1) + 2, {});
        assigns.assign(vars + 1, 0);
        phase.assign(vars + 1, 1);
        level.assign(vars + 1, 0);
        reason.assign(vars + 1, -1);
        activity.assign(vars + 1, 0.0);
        std::vector<int> pos(vars + 1, 0), neg(vars + 1, 0);

        clauses.reserve(f.lens.size() + 10000);
        lits.reserve(f.lits.size() + 100000);
        for (int ci = 0; ci < static_cast<int>(f.lens.size()); ++ci) {
            int st = f.starts[ci], len = f.lens[ci];
            if (len <= 0) return false;
            int out_st = static_cast<int>(lits.size());
            for (int k = 0; k < len; ++k) {
                int lit = f.lits[st + k];
                int v = std::abs(lit);
                if (v <= 0 || v > vars) return false;
                lits.push_back(lit);
                activity[v] += 1.0;
                if (lit > 0) ++pos[v];
                else ++neg[v];
            }
            clauses.push_back({out_st, len, false});
            attach_clause(static_cast<int>(clauses.size()) - 1);
        }
        for (int v = 1; v <= vars; ++v) {
            if (preferred_phase && v < static_cast<int>(preferred_phase->size()) && (*preferred_phase)[v] != 0) {
                phase[v] = (*preferred_phase)[v];
            } else {
                phase[v] = pos[v] >= neg[v] ? 1 : -1;
            }
        }
        for (int cid = 0; cid < static_cast<int>(clauses.size()); ++cid) {
            if (clauses[cid].len == 1 && !enqueue(lits[clauses[cid].start], cid)) {
                proved_unsat = true;
                return false;
            }
        }
        return true;
    }

    int propagate() {
        while (qhead < static_cast<int>(trail.size())) {
            int p = trail[qhead++];
            int false_lit = -p;
            auto& ws = watches[lit_index(false_lit)];
            int i = 0;
            while (i < static_cast<int>(ws.size())) {
                int cid = ws[i];
                Clause& c = clauses[cid];
                int st = c.start;
                if (c.len > 1 && lits[st] != false_lit) {
                    if (lits[st + 1] == false_lit) std::swap(lits[st], lits[st + 1]);
                }
                if (lits[st] != false_lit) {
                    ++i;
                    continue;
                }
                if (c.len == 1) return cid;
                int other = lits[st + 1];
                if (value_lit(other) == 1) {
                    ++i;
                    continue;
                }
                bool found = false;
                for (int k = 2; k < c.len; ++k) {
                    int lit = lits[st + k];
                    if (value_lit(lit) != -1) {
                        lits[st] = lit;
                        lits[st + k] = false_lit;
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(lit)].push_back(cid);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                int oval = value_lit(other);
                if (oval == -1) return cid;
                if (!enqueue(other, cid)) return cid;
                ++i;
            }
            if ((qhead & 65535) == 0 && Clock::now() >= deadline) {
                timed_out = true;
                return -2;
            }
        }
        return -1;
    }

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

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

    void analyze(int confl, std::vector<int>& learnt, int& back_lvl) {
        learnt.clear();
        learnt.push_back(0);
        std::vector<char> seen(vars + 1, 0);
        int path_c = 0;
        int idx = static_cast<int>(trail.size()) - 1;
        int p = 0;
        int cid = confl;
        do {
            Clause& c = clauses[cid];
            for (int j = 0; j < c.len; ++j) {
                int q = lits[c.start + j];
                int v = std::abs(q);
                if (!seen[v] && level[v] > 0) {
                    seen[v] = 1;
                    bump_var(v);
                    if (level[v] == decision_level()) ++path_c;
                    else learnt.push_back(q);
                }
            }
            while (idx >= 0) {
                p = trail[idx--];
                if (seen[std::abs(p)]) break;
            }
            seen[std::abs(p)] = 0;
            --path_c;
            if (path_c <= 0) break;
            cid = reason[std::abs(p)];
            if (cid < 0) break;
        } while (true);
        learnt[0] = -p;
        back_lvl = 0;
        int best_pos = 1;
        for (int i = 1; i < static_cast<int>(learnt.size()); ++i) {
            int l = level[std::abs(learnt[i])];
            if (l > back_lvl) {
                back_lvl = l;
                best_pos = i;
            }
        }
        if (learnt.size() > 1) std::swap(learnt[1], learnt[best_pos]);
        var_inc *= 1.05;
    }

    int add_learned(const std::vector<int>& learnt) {
        int st = static_cast<int>(lits.size());
        lits.insert(lits.end(), learnt.begin(), learnt.end());
        clauses.push_back({st, static_cast<int>(learnt.size()), true});
        int cid = static_cast<int>(clauses.size()) - 1;
        attach_clause(cid);
        proof_clauses.push_back(learnt);
        return cid;
    }

    bool write_veripb_proof(const char* path) const {
        FILE* out = std::fopen(path, "wb");
        if (!out) return false;
        std::fprintf(out, "pseudo-Boolean proof version 3.0\n");
        for (const auto& clause : proof_clauses) {
            std::fprintf(out, "rup ");
            for (int lit : clause) {
                if (lit > 0) std::fprintf(out, "1 x%d ", lit);
                else std::fprintf(out, "1 ~x%d ", -lit);
            }
            std::fprintf(out, ">= 1 ;\n");
        }
        std::fprintf(out, "rup >= 1 ;\n");
        std::fprintf(out, "output NONE ;\n");
        std::fprintf(out, "conclusion UNSAT : -1 ;\n");
        std::fprintf(out, "end pseudo-Boolean proof ;\n");
        std::fclose(out);
        return true;
    }

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

    bool solve(std::vector<int8_t>& truth) {
        if (!build()) return false;
        int restart_limit = 128;
        int since_restart = 0;
        int pending_conflict = -1;
        std::vector<int> learnt;
        while (Clock::now() < deadline) {
            int confl = pending_conflict >= 0 ? pending_conflict : propagate();
            pending_conflict = -1;
            if (confl == -2) return false;
            if (confl >= 0) {
                ++conflicts;
                ++since_restart;
                if (decision_level() == 0) {
                    proved_unsat = true;
                    return false;
                }
                int back_lvl = 0;
                analyze(confl, learnt, back_lvl);
                backtrack(back_lvl);
                int cid = add_learned(learnt);
                if (!enqueue(learnt[0], cid)) {
                    pending_conflict = cid;
                    continue;
                }
                if (since_restart >= restart_limit) {
                    backtrack(0);
                    since_restart = 0;
                    restart_limit = std::min(restart_limit + restart_limit / 2 + 32, 20000);
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) {
                    truth.assign(vars + 1, 0);
                    for (int v = 1; v <= vars; ++v) truth[v] = assigns[v] > 0 ? 1 : 0;
                    return true;
                }
                trail_lim.push_back(static_cast<int>(trail.size()));
                enqueue(next, -1);
            }
        }
        timed_out = true;
        return false;
    }
};

static double default_seconds() {
    const char* env = std::getenv("LYMPHOSAT_SOLVER_SECONDS");
    if (env && *env) {
        double v = std::atof(env);
        if (v > 0.01) return v;
    }
    return 180.0;
}

static bool env_flag(const char* name) {
    const char* env = std::getenv(name);
    return env && *env && std::strcmp(env, "0") != 0;
}

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

    CSP csp;
    bool supported = build_direct_csp(f, csp);
    if (!supported) supported = build_log_csp(f, csp);
    if (!supported || env_flag("LYMPHOSAT_FORCE_CDCL")) {
        std::vector<int8_t> truth;
        MiniCDCL cdcl(f, default_seconds());
        if (cdcl.solve(truth) && verify_formula(f, truth)) {
            print_model(truth);
            return 10;
        }
        if (cdcl.proved_unsat && cdcl.write_veripb_proof(argv[2])) {
            std::printf("s UNSATISFIABLE\n");
            return 20;
        }
        if (generic_sls(f, default_seconds(), truth) && verify_formula(f, truth)) {
            print_model(truth);
            return 10;
        }
        std::printf("s UNKNOWN\n");
        return 0;
    }

    std::vector<uint64_t> sol;
    bool found = try_ordered_deletion_embedding(csp, sol);
    if (!found) {
        Searcher search(csp, default_seconds());
        found = search.solve(sol);
    }
    if (!found) {
        if (!csp.is_log && env_flag("LYMPHOSAT_MAC_PROOF")) {
            DirectProofMAC proof(csp, default_seconds());
            std::vector<uint64_t> proof_sol;
            DirectProofMAC::Result pres = proof.run(argv[2], proof_sol);
            if (pres == DirectProofMAC::PROVED_UNSAT) {
                std::printf("s UNSATISFIABLE\n");
                return 20;
            }
            if (pres == DirectProofMAC::FOUND_SAT) {
                sol = proof_sol;
                found = true;
            }
        }
    }
    if (!found) {
        std::vector<int8_t> cdcl_truth;
        std::vector<int8_t> preferred;
        if (!csp.is_log) {
            preferred.assign(f.vars + 1, 0);
            for (const auto& row : csp.direct_lits) {
                for (int lit : row) preferred[std::abs(lit)] = lit > 0 ? 1 : -1;
            }
        }
        MiniCDCL cdcl(f, default_seconds(), preferred.empty() ? nullptr : &preferred);
        if (cdcl.solve(cdcl_truth) && verify_formula(f, cdcl_truth)) {
            print_model(cdcl_truth);
            return 10;
        }
        if (cdcl.proved_unsat && cdcl.write_veripb_proof(argv[2])) {
            std::printf("s UNSATISFIABLE\n");
            return 20;
        }
        std::printf("s UNKNOWN\n");
        return 0;
    }

    std::vector<int8_t> truth;
    if (!build_assignment(csp, sol, f.vars, truth) || !verify_formula(f, truth)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    print_model(truth);
    return 10;
}
