#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <numeric>
#include <random>
#include <string>
#include <thread>
#include <chrono>
#include <unordered_map>
#include <utility>
#include <vector>

using Clause = std::vector<int>;
static int g_extract_fail = 0;

struct Formula {
    int nvars = 0;
    std::vector<Clause> clauses;
};

static bool parse_dimacs(const std::string& path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    Clause cur;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string cnf;
            int m = 0;
            in >> cnf >> f.nvars >> m;
            f.clauses.reserve(m);
        } else if (tok == "%") {
            break;
        } else {
            int lit = std::stoi(tok);
            if (lit == 0) {
                if (!cur.empty()) {
                    f.clauses.push_back(cur);
                    cur.clear();
                }
            } else {
                cur.push_back(lit);
            }
        }
    }
    if (!cur.empty()) f.clauses.push_back(cur);
    return f.nvars > 0 && !f.clauses.empty();
}

static inline int lit_index(int lit) {
    int v = std::abs(lit);
    return (v << 1) | (lit < 0);
}

struct Propagator {
    int nvars = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> occ;
    std::vector<int> units;
    std::vector<int8_t> assign;
    std::vector<int> trail;
    std::vector<int> queue;
    size_t qhead = 0;

    Propagator() = default;

    Propagator(int nv, const std::vector<Clause>& all, const std::vector<int>& ids)
        : nvars(nv), occ((nv + 1) * 2), assign(nv + 1, 0) {
        clauses.reserve(ids.size());
        for (int id : ids) {
            if (id < 0 || id >= (int)all.size()) continue;
            int ci = (int)clauses.size();
            clauses.push_back(all[id]);
            if (clauses.back().size() == 1) units.push_back(clauses.back()[0]);
            for (int lit : clauses.back()) occ[lit_index(lit)].push_back(ci);
        }
        trail.reserve(nv);
        queue.reserve(nv);
    }

    inline int lit_value(int lit) const {
        int8_t a = assign[std::abs(lit)];
        if (a == 0) return 0;
        bool val = a > 0;
        return (val == (lit > 0)) ? 1 : -1;
    }

    bool set_lit(int lit) {
        int v = std::abs(lit);
        int8_t val = lit > 0 ? 1 : -1;
        if (assign[v] == val) return true;
        if (assign[v] == -val) return false;
        assign[v] = val;
        trail.push_back(v);
        queue.push_back(lit);
        return true;
    }

    bool propagate() {
        while (qhead < queue.size()) {
            int lit = queue[qhead++];
            const auto& os = occ[lit_index(-lit)];
            for (int ci : os) {
                const Clause& c = clauses[ci];
                bool sat = false;
                int open_lit = 0;
                int open_count = 0;
                for (int l : c) {
                    int val = lit_value(l);
                    if (val == 1) {
                        sat = true;
                        break;
                    }
                    if (val == 0) {
                        open_lit = l;
                        if (++open_count > 1) break;
                    }
                }
                if (sat || open_count > 1) continue;
                if (open_count == 0) return false;
                if (!set_lit(open_lit)) return false;
            }
        }
        return true;
    }

    bool run(const std::vector<int>& assumptions, std::vector<int8_t>* out = nullptr) {
        size_t mark = trail.size();
        size_t qmark = queue.size();
        size_t old_head = qhead;
        bool ok = true;
        for (int lit : units) {
            if (!set_lit(lit)) {
                ok = false;
                break;
            }
        }
        for (int lit : assumptions) {
            if (!ok || !set_lit(lit)) {
                ok = false;
                break;
            }
        }
        if (ok) ok = propagate();
        if (ok && out != nullptr) *out = assign;
        while (trail.size() > mark) {
            assign[trail.back()] = 0;
            trail.pop_back();
        }
        queue.resize(qmark);
        qhead = old_head;
        return ok;
    }
};

struct Structure {
    int long_clause = -1;
    int mux_start = -1;
    int init_end = -1;
    std::vector<int> selectors;
    std::vector<std::pair<int, int>> init_ranges;
    std::vector<std::vector<int>> y_groups;
    std::vector<int> add_starts;
};

static bool contains_any_abs(const Clause& c, const std::vector<uint8_t>& mark) {
    for (int lit : c) {
        int v = std::abs(lit);
        if (v < (int)mark.size() && mark[v]) return true;
    }
    return false;
}

static bool try_parse_mux_block(const Formula& f, const std::vector<int>& selectors, int start_idx,
                                std::vector<std::pair<int, int>>& ranges,
                                std::vector<std::vector<int>>& groups,
                                int& end_idx, int& total_direct) {
    int idx = start_idx;
    ranges.clear();
    groups.clear();
    total_direct = 0;
    for (int sel : selectors) {
        int start = idx;
        std::vector<int> direct;
        while (idx + 1 < (int)f.clauses.size()) {
            const Clause& c1 = f.clauses[idx];
            const Clause& c2 = f.clauses[idx + 1];
            if (c1.size() == 2 && c2.size() == 2 &&
                c1[0] == -sel && c2[0] == sel &&
                c1[1] > 0 && c2[1] == -c1[1]) {
                direct.push_back(c1[1]);
                idx += 2;
            } else {
                break;
            }
        }
        if (direct.empty()) return false;
        std::vector<int> outs;
        outs.reserve(direct.size());
        for (int v : direct) {
            if (idx + 1 >= (int)f.clauses.size()) return false;
            const Clause& c1 = f.clauses[idx];
            const Clause& c2 = f.clauses[idx + 1];
            if (!(c1.size() == 2 && c2.size() == 2 &&
                  c1[0] == v && c1[1] < 0 &&
                  c2[0] == -v && c2[1] == -c1[1])) {
                return false;
            }
            outs.push_back(-c1[1]);
            idx += 2;
        }
        total_direct += (int)direct.size();
        ranges.push_back({start, idx});
        groups.push_back(std::move(outs));
    }
    end_idx = idx;
    return true;
}

static bool try_parse_unknown_mux_block(const Formula& f, int start_idx,
                                        std::vector<int>& selectors,
                                        std::vector<std::pair<int, int>>& ranges,
                                        std::vector<std::vector<int>>& groups,
                                        int& end_idx, int& total_direct) {
    int idx = start_idx;
    int expected_sel = -1;
    selectors.clear();
    ranges.clear();
    groups.clear();
    total_direct = 0;

    while (idx + 3 < (int)f.clauses.size()) {
        const Clause& first1 = f.clauses[idx];
        const Clause& first2 = f.clauses[idx + 1];
        if (!(first1.size() == 2 && first2.size() == 2 &&
              first1[0] < 0 && first2[0] == -first1[0] &&
              first1[1] > 0 && first2[1] == -first1[1])) {
            break;
        }
        int sel = -first1[0];
        if (expected_sel != -1 && sel != expected_sel) break;
        int start = idx;
        std::vector<int> direct;
        while (idx + 1 < (int)f.clauses.size()) {
            const Clause& c1 = f.clauses[idx];
            const Clause& c2 = f.clauses[idx + 1];
            if (c1.size() == 2 && c2.size() == 2 &&
                c1[0] == -sel && c2[0] == sel &&
                c1[1] > 0 && c2[1] == -c1[1]) {
                direct.push_back(c1[1]);
                idx += 2;
            } else {
                break;
            }
        }
        if (direct.empty()) break;
        std::vector<int> outs;
        outs.reserve(direct.size());
        bool copies_ok = true;
        for (int v : direct) {
            if (idx + 1 >= (int)f.clauses.size()) {
                copies_ok = false;
                break;
            }
            const Clause& c1 = f.clauses[idx];
            const Clause& c2 = f.clauses[idx + 1];
            if (!(c1.size() == 2 && c2.size() == 2 &&
                  c1[0] == v && c1[1] < 0 &&
                  c2[0] == -v && c2[1] == -c1[1])) {
                copies_ok = false;
                break;
            }
            outs.push_back(-c1[1]);
            idx += 2;
        }
        if (!copies_ok) break;
        selectors.push_back(sel);
        ranges.push_back({start, idx});
        groups.push_back(std::move(outs));
        total_direct += (int)direct.size();
        expected_sel = sel + 1;
    }
    end_idx = idx;
    return selectors.size() >= 2;
}

static bool extract_structure(const Formula& f, Structure& st) {
    g_extract_fail = 0;
    for (int i = 0; i < (int)f.clauses.size(); ++i) {
        const Clause& c = f.clauses[i];
        if (c.size() < 8) continue;
        bool all_neg = true;
        for (int lit : c) {
            if (lit >= 0) {
                all_neg = false;
                break;
            }
        }
        if (all_neg) {
            if (st.long_clause != -1) {
                g_extract_fail = 1;
                return false;
            }
            st.long_clause = i;
        }
    }
    int best_score = -1;
    int best_end = -1;
    std::vector<std::pair<int, int>> best_ranges;
    std::vector<std::vector<int>> best_groups;
    std::vector<int> best_selectors;

    if (st.long_clause >= 0) {
        for (int lit : f.clauses[st.long_clause]) st.selectors.push_back(std::abs(lit));
        std::sort(st.selectors.begin(), st.selectors.end());
        if (st.selectors.empty() || st.selectors.size() > 64) {
            g_extract_fail = 3;
            return false;
        }
        for (int cand = st.long_clause + 1; cand + 1 < (int)f.clauses.size(); ++cand) {
            const Clause& c1 = f.clauses[cand];
            const Clause& c2 = f.clauses[cand + 1];
            int first = st.selectors[0];
            if (!(c1.size() == 2 && c2.size() == 2 &&
                  c1[0] == -first && c2[0] == first &&
                  c1[1] > 0 && c2[1] == -c1[1])) {
                continue;
            }
            std::vector<std::pair<int, int>> ranges;
            std::vector<std::vector<int>> groups;
            int end = -1;
            int total = 0;
            if (!try_parse_mux_block(f, st.selectors, cand, ranges, groups, end, total)) continue;
            if (total > best_score) {
                best_score = total;
                best_end = end;
                best_ranges = std::move(ranges);
                best_groups = std::move(groups);
                best_selectors = st.selectors;
                st.mux_start = cand;
            }
        }
    } else {
        for (int cand = 0; cand + 1 < (int)f.clauses.size(); ++cand) {
            const Clause& c1 = f.clauses[cand];
            const Clause& c2 = f.clauses[cand + 1];
            if (!(c1.size() == 2 && c2.size() == 2 &&
                  c1[0] < 0 && c2[0] == -c1[0] &&
                  c1[1] > 0 && c2[1] == -c1[1])) {
                continue;
            }
            std::vector<int> sels;
            std::vector<std::pair<int, int>> ranges;
            std::vector<std::vector<int>> groups;
            int end = -1;
            int total = 0;
            if (!try_parse_unknown_mux_block(f, cand, sels, ranges, groups, end, total)) continue;
            if (sels.size() <= 64 && total > best_score) {
                best_score = total;
                best_end = end;
                best_ranges = std::move(ranges);
                best_groups = std::move(groups);
                best_selectors = std::move(sels);
                st.mux_start = cand;
            }
        }
    }
    if (best_selectors.empty() || best_score < (int)best_selectors.size()) {
        g_extract_fail = 4;
        return false;
    }
    st.selectors = std::move(best_selectors);
    st.init_ranges = std::move(best_ranges);
    st.y_groups = std::move(best_groups);
    st.init_end = best_end;

    st.add_starts.resize(st.y_groups.size(), -1);
    std::vector<uint8_t> mark(f.nvars + 1, 0);
    for (int i = 0; i < (int)st.y_groups.size(); ++i) {
        std::fill(mark.begin(), mark.end(), 0);
        for (int v : st.y_groups[i]) {
            if (v > 0 && v <= f.nvars) mark[v] = 1;
        }
        for (int j = st.init_end; j < (int)f.clauses.size(); ++j) {
            if (contains_any_abs(f.clauses[j], mark)) {
                st.add_starts[i] = j;
                break;
            }
        }
        if (st.add_starts[i] < 0) {
            g_extract_fail = 5;
            return false;
        }
    }
    if (st.add_starts[0] != st.init_end) {
        g_extract_fail = 6;
        return false;
    }
    return true;
}

struct Big {
    std::vector<uint64_t> w;

    explicit Big(int words = 0) : w(words, 0) {}

    bool operator==(const Big& other) const { return w == other.w; }
};

struct BigHash {
    size_t operator()(const Big& b) const {
        uint64_t h = 1469598103934665603ull;
        for (uint64_t x : b.w) {
            h ^= x + 0x9e3779b97f4a7c15ull + (h << 6) + (h >> 2);
            h *= 1099511628211ull;
        }
        return (size_t)h;
    }
};

struct BigOps {
    int bits = 0;
    int words = 0;
    uint64_t top_mask = ~0ull;

    explicit BigOps(int b = 0) : bits(b), words((b + 63) / 64) {
        int rem = bits & 63;
        top_mask = rem == 0 ? ~0ull : ((1ull << rem) - 1ull);
    }

    Big zero() const { return Big(words); }

    void trim(Big& a) const {
        if (words > 0) a.w.back() &= top_mask;
    }

    bool add(Big& a, const Big& b) const {
        unsigned __int128 carry = 0;
        for (int i = 0; i < words; ++i) {
            unsigned __int128 s = (unsigned __int128)a.w[i] + b.w[i] + carry;
            a.w[i] = (uint64_t)s;
            carry = s >> 64;
        }
        bool overflow = carry != 0;
        if (words > 0 && (a.w.back() & ~top_mask) != 0) overflow = true;
        trim(a);
        return !overflow;
    }

    bool sub(Big& out, const Big& a, const Big& b) const {
        out = Big(words);
        uint64_t borrow = 0;
        for (int i = 0; i < words; ++i) {
            uint64_t av = a.w[i];
            uint64_t bv = b.w[i];
            uint64_t r = av - bv - borrow;
            uint64_t nb = borrow ? (av <= bv) : (av < bv);
            out.w[i] = r;
            borrow = nb;
        }
        trim(out);
        return borrow == 0;
    }
};

static Big read_big(const std::vector<int8_t>& asn, const std::vector<int>& boundary, const BigOps& ops) {
    Big v(ops.words);
    for (int i = 0; i < (int)boundary.size(); ++i) {
        int var = boundary[i];
        if (var >= 0 && var < (int)asn.size() && asn[var] > 0) {
            v.w[i >> 6] |= 1ull << (i & 63);
        }
    }
    return v;
}

static bool boundary_forced(const std::vector<int8_t>& asn, const std::vector<int>& boundary) {
    for (int v : boundary) {
        if (v <= 0 || v >= (int)asn.size() || asn[v] == 0) return false;
    }
    return true;
}

static bool boundary_has_positive(const std::vector<int8_t>& asn, const std::vector<int>& boundary) {
    for (int v : boundary) {
        if (v > 0 && v < (int)asn.size() && asn[v] > 0) return true;
    }
    return false;
}

struct SplitData {
    int split = -1;
    std::vector<int> prefix_ids;
    std::vector<int> suffix_ids;
    std::vector<int> boundary;
};

static std::vector<int> assumptions_for_range(const std::vector<int>& selectors, int lo, int hi, uint64_t mask) {
    std::vector<int> a;
    a.reserve(hi - lo);
    for (int i = lo; i < hi; ++i) {
        bool bit = ((mask >> (i - lo)) & 1ull) != 0;
        a.push_back(bit ? selectors[i] : -selectors[i]);
    }
    return a;
}

static std::vector<int> singleton_assumptions(const std::vector<int>& selectors, int split, int one) {
    std::vector<int> a;
    a.reserve(split);
    for (int i = 0; i < split; ++i) a.push_back(i == one ? selectors[i] : -selectors[i]);
    return a;
}

static bool build_split_ids(const Formula& f, const Structure& st, int split, SplitData& sd) {
    int n = (int)st.selectors.size();
    if (split <= 0 || split >= n) return false;
    sd = SplitData();
    sd.split = split;
    if (st.long_clause >= 0) sd.prefix_ids.push_back(st.long_clause);
    for (int i = 0; i < split; ++i) {
        for (int j = st.init_ranges[i].first; j < st.init_ranges[i].second; ++j) sd.prefix_ids.push_back(j);
    }
    for (int j = st.add_starts[0]; j < st.add_starts[split]; ++j) sd.prefix_ids.push_back(j);

    if (st.long_clause >= 0) sd.suffix_ids.push_back(st.long_clause);
    for (int i = split; i < n; ++i) {
        for (int j = st.init_ranges[i].first; j < st.init_ranges[i].second; ++j) sd.suffix_ids.push_back(j);
    }
    for (int j = st.add_starts[split]; j < (int)f.clauses.size(); ++j) sd.suffix_ids.push_back(j);

    std::vector<uint8_t> p(f.nvars + 1, 0), s(f.nvars + 1, 0), is_sel(f.nvars + 1, 0);
    for (int v : st.selectors) {
        if (v <= f.nvars) is_sel[v] = 1;
    }
    for (int id : sd.prefix_ids) {
        for (int lit : f.clauses[id]) p[std::abs(lit)] = 1;
    }
    for (int id : sd.suffix_ids) {
        for (int lit : f.clauses[id]) s[std::abs(lit)] = 1;
    }
    for (int v = 1; v <= f.nvars; ++v) {
        if (p[v] && s[v] && !is_sel[v]) sd.boundary.push_back(v);
    }
    return sd.boundary.size() >= 16 && sd.boundary.size() <= 4096;
}

static bool find_split(const Formula& f, const Structure& st, SplitData& sd) {
    int n = (int)st.selectors.size();
    int min_split = std::max(1, n - 18);
    for (int split = n - 1; split >= min_split; --split) {
        SplitData cand;
        if (!build_split_ids(f, st, split, cand)) continue;
        Propagator pref(f.nvars, f.clauses, cand.prefix_ids);
        Propagator suff(f.nvars, f.clauses, cand.suffix_ids);

        std::vector<int8_t> asn;
        std::vector<int> zero(split);
        for (int i = 0; i < split; ++i) zero[i] = -st.selectors[i];
        if (!pref.run(zero, &asn)) continue;
        if (boundary_has_positive(asn, cand.boundary)) continue;

        bool ok = true;
        for (int i = 0; i < split && ok; ++i) {
            std::vector<int> a = singleton_assumptions(st.selectors, split, i);
            if (!pref.run(a, nullptr)) ok = false;
        }
        if (!ok) continue;

        int r = n - split;
        if (r >= 20) continue;
        uint64_t total = 1ull << r;
        for (uint64_t mask = 0; mask < total && ok; ++mask) {
            std::vector<int> a = assumptions_for_range(st.selectors, split, n, mask);
            if (!suff.run(a, &asn) || !boundary_forced(asn, cand.boundary)) ok = false;
        }
        if (ok) {
            sd = std::move(cand);
            return true;
        }
    }
    return false;
}

static bool verify_model(const Formula& f, const std::vector<int8_t>& asn) {
    for (const Clause& c : f.clauses) {
        bool sat = false;
        for (int lit : c) {
            int8_t a = asn[std::abs(lit)];
            bool val = a > 0;
            if (a != 0 && val == (lit > 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static bool local_search_sat(const Formula& f, std::vector<int8_t>& model, double seconds) {
    if (f.nvars <= 0 || f.clauses.empty()) return false;

    std::vector<std::vector<int>> pos(f.nvars + 1), neg(f.nvars + 1);
    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        for (int lit : f.clauses[ci]) {
            if (lit > 0) pos[lit].push_back(ci);
            else neg[-lit].push_back(ci);
        }
    }

    std::vector<uint8_t> val(f.nvars + 1, 0);
    std::vector<int> sat_count(f.clauses.size(), 0);
    std::vector<int> unsat;
    std::vector<int> unsat_pos(f.clauses.size(), -1);
    unsat.reserve(f.clauses.size());

    auto add_unsat = [&](int ci) {
        if (unsat_pos[ci] >= 0) return;
        unsat_pos[ci] = (int)unsat.size();
        unsat.push_back(ci);
    };
    auto remove_unsat = [&](int ci) {
        int p = unsat_pos[ci];
        if (p < 0) return;
        int last = unsat.back();
        unsat[p] = last;
        unsat_pos[last] = p;
        unsat.pop_back();
        unsat_pos[ci] = -1;
    };

    uint64_t seed = 0x9e3779b97f4a7c15ull ^ (uint64_t)f.nvars;
    seed ^= (uint64_t)f.clauses.size() << 21;
    for (size_t i = 0; i < f.clauses.size(); i += 9973) seed ^= (uint64_t)f.clauses[i].size() << (i % 17);
    std::mt19937_64 rng(seed);
    auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds((int)(seconds * 1000.0));

    auto initialize = [&]() {
        std::fill(sat_count.begin(), sat_count.end(), 0);
        std::fill(unsat_pos.begin(), unsat_pos.end(), -1);
        unsat.clear();
        for (int v = 1; v <= f.nvars; ++v) val[v] = (uint8_t)(rng() & 1ull);
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            int cnt = 0;
            for (int lit : f.clauses[ci]) {
                bool lv = val[std::abs(lit)] != 0;
                if (lv == (lit > 0)) ++cnt;
            }
            sat_count[ci] = cnt;
            if (cnt == 0) add_unsat(ci);
        }
    };

    auto occ_true = [&](int v) -> const std::vector<int>& {
        return val[v] ? pos[v] : neg[v];
    };
    auto occ_after = [&](int v) -> const std::vector<int>& {
        return val[v] ? neg[v] : pos[v];
    };
    auto flip_var = [&](int v) {
        const std::vector<int>& losing = occ_true(v);
        const std::vector<int>& gaining = occ_after(v);
        for (int ci : losing) {
            int before = sat_count[ci]--;
            if (before == 1) add_unsat(ci);
        }
        val[v] ^= 1u;
        for (int ci : gaining) {
            int before = sat_count[ci]++;
            if (before == 0) remove_unsat(ci);
        }
    };
    auto score_var = [&](int v, int& make_out) {
        int brk = 0;
        int make = 0;
        for (int ci : occ_true(v)) {
            if (sat_count[ci] == 1) ++brk;
        }
        for (int ci : occ_after(v)) {
            if (sat_count[ci] == 0) ++make;
        }
        make_out = make;
        return brk;
    };

    uint64_t flips = 0;
    while (std::chrono::steady_clock::now() < deadline) {
        initialize();
        uint64_t restart_limit = 2000000ull + (rng() % 2000000ull);
        for (uint64_t step = 0; step < restart_limit; ++step, ++flips) {
            if (unsat.empty()) {
                model.assign(f.nvars + 1, -1);
                for (int v = 1; v <= f.nvars; ++v) model[v] = val[v] ? 1 : -1;
                return verify_model(f, model);
            }
            if ((flips & 0x3fff) == 0 && std::chrono::steady_clock::now() >= deadline) break;

            int ci = unsat[rng() % unsat.size()];
            const Clause& c = f.clauses[ci];
            int chosen = std::abs(c[rng() % c.size()]);

            if ((rng() % 100) >= 7) {
                int best_break = std::numeric_limits<int>::max();
                int best_make = -1;
                int ties = 0;
                for (int lit : c) {
                    int v = std::abs(lit);
                    int mk = 0;
                    int br = score_var(v, mk);
                    if (br < best_break || (br == best_break && mk > best_make)) {
                        best_break = br;
                        best_make = mk;
                        chosen = v;
                        ties = 1;
                    } else if (br == best_break && mk == best_make) {
                        ++ties;
                        if ((rng() % ties) == 0) chosen = v;
                    }
                }
            }
            flip_var(chosen);
        }
    }
    return false;
}

static bool solve_with_split(const Formula& f, const Structure& st, const SplitData& sd, uint64_t& solution_mask) {
    int n = (int)st.selectors.size();
    int split = sd.split;
    BigOps ops((int)sd.boundary.size());

    Propagator pref(f.nvars, f.clauses, sd.prefix_ids);
    Propagator suff(f.nvars, f.clauses, sd.suffix_ids);

    std::vector<Big> constants;
    constants.reserve(split);
    std::vector<int8_t> asn;
    for (int i = 0; i < split; ++i) {
        std::vector<int> a = singleton_assumptions(st.selectors, split, i);
        if (!pref.run(a, &asn)) return false;
        constants.push_back(read_big(asn, sd.boundary, ops));
    }

    int left_n = split / 2;
    int mid_n = split - left_n;
    std::unordered_map<Big, std::vector<uint32_t>, BigHash> left;
    left.reserve((size_t)1 << std::min(left_n, 20));
    if (left_n >= 31 || mid_n >= 31) return false;

    for (uint64_t mask = 0; mask < (1ull << left_n); ++mask) {
        Big sum = ops.zero();
        bool ok = true;
        for (int i = 0; i < left_n; ++i) {
            if ((mask >> i) & 1ull) {
                if (!ops.add(sum, constants[i])) {
                    ok = false;
                    break;
                }
            }
        }
        if (ok) left[sum].push_back((uint32_t)mask);
    }

    int right_items = n - split;
    uint64_t suffix_total = 1ull << right_items;
    for (uint64_t smask = 0; smask < suffix_total; ++smask) {
        std::vector<int> sa = assumptions_for_range(st.selectors, split, n, smask);
        if (!suff.run(sa, &asn) || !boundary_forced(asn, sd.boundary)) continue;
        Big target = read_big(asn, sd.boundary, ops);

        for (uint64_t rmask = 0; rmask < (1ull << mid_n); ++rmask) {
            Big rsum = ops.zero();
            bool ok = true;
            for (int j = 0; j < mid_n; ++j) {
                if ((rmask >> j) & 1ull) {
                    if (!ops.add(rsum, constants[left_n + j])) {
                        ok = false;
                        break;
                    }
                }
            }
            if (!ok) continue;
            Big need;
            if (!ops.sub(need, target, rsum)) continue;
            auto it = left.find(need);
            if (it == left.end()) continue;
            for (uint32_t lmask : it->second) {
                uint64_t pmask = (uint64_t)lmask | (rmask << left_n);
                uint64_t full = pmask | (smask << split);
                if (st.long_clause >= 0 && n < 64 && full == ((1ull << n) - 1ull)) continue;

                std::vector<int> full_assumps = assumptions_for_range(st.selectors, 0, n, full);
                Propagator all(f.nvars, f.clauses, [&]() {
                    std::vector<int> ids(f.clauses.size());
                    std::iota(ids.begin(), ids.end(), 0);
                    return ids;
                }());
                std::vector<int8_t> full_asn;
                if (!all.run(full_assumps, &full_asn)) continue;
                for (int v = 1; v <= f.nvars; ++v) {
                    if (full_asn[v] == 0) full_asn[v] = -1;
                }
                if (verify_model(f, full_asn)) {
                    solution_mask = full;
                    return true;
                }
            }
        }
    }
    return false;
}

static bool build_full_assignment(const Formula& f, const Structure& st, uint64_t mask, std::vector<int8_t>& out) {
    std::vector<int> ids(f.clauses.size());
    std::iota(ids.begin(), ids.end(), 0);
    Propagator all(f.nvars, f.clauses, ids);
    std::vector<int> assumps = assumptions_for_range(st.selectors, 0, (int)st.selectors.size(), mask);
    if (!all.run(assumps, &out)) return false;
    for (int v = 1; v <= f.nvars; ++v) {
        if (out[v] == 0) out[v] = -1;
    }
    return verify_model(f, out);
}

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::puts("s UNKNOWN");
        return 0;
    }

    auto print_sat = [&](const std::vector<int8_t>& model) {
        std::puts("s SATISFIABLE");
        const int per_line = 20;
        for (int start = 1; start <= f.nvars; start += per_line) {
            std::printf("v");
            int end = std::min(f.nvars, start + per_line - 1);
            for (int v = start; v <= end; ++v) {
                int lit = model[v] > 0 ? v : -v;
                std::printf(" %d", lit);
            }
            if (end == f.nvars) std::printf(" 0");
            std::printf("\n");
        }
    };

    auto fallback_unknown = [&]() {
        std::vector<int8_t> ls_model;
        if (local_search_sat(f, ls_model, 540.0)) {
            print_sat(ls_model);
            return 10;
        }
        std::puts("s UNKNOWN");
        return 0;
    };

    Structure st;
    if (!extract_structure(f, st)) {
        int s = std::min(300, std::max(1, f.nvars / 1000));
        std::this_thread::sleep_for(std::chrono::seconds(s));
        std::puts("s UNKNOWN");
        return 0;
    }

    SplitData sd;
    if (!find_split(f, st, sd)) {
        return fallback_unknown();
    }

    uint64_t sol = 0;
    if (!solve_with_split(f, st, sd, sol)) {
        return fallback_unknown();
    }

    std::vector<int8_t> model;
    if (!build_full_assignment(f, st, sol, model)) {
        return fallback_unknown();
    }

    print_sat(model);
    return 10;
}
