#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <fstream>
#include <numeric>
#include <random>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using Clock = std::chrono::steady_clock;

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

struct CSP {
    int n = 0;
    int max_d = 0;
    int dimacs_vars = 0;
    std::vector<int> dom_size;
    std::vector<std::vector<int>> group_vars;       // group -> DIMACS variables
    std::vector<std::vector<uint32_t>> domains;     // group -> truth pattern over group_vars
    std::vector<int> lit_group, lit_pos;            // indexed by DIMACS var
    std::vector<uint32_t> forbid;               // ((i*d+a)*n+j) -> bit mask of j values
    std::vector<std::vector<int>> neighbors;
    std::vector<int> edge_u, edge_v;
    std::vector<int> edge_index;                // i*n+j -> edge id, or -1

    uint32_t mask(int i, int a, int j) const {
        return forbid[(i * max_d + a) * n + j];
    }
};

static bool parse_dimacs(const char* path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            int m = 0;
            in >> fmt >> f.vars >> m;
            f.clauses.reserve(m);
        } else {
            std::vector<int> clause;
            int lit = std::stoi(tok);
            while (lit != 0) {
                clause.push_back(lit);
                in >> lit;
            }
            if (!clause.empty()) f.clauses.push_back(std::move(clause));
        }
    }
    return f.vars > 0 && !f.clauses.empty();
}

static bool lit_true_in_pattern(int lit, const CSP& csp, uint32_t pat) {
    int pos = csp.lit_pos[std::abs(lit)];
    bool truth = ((pat >> pos) & 1U) != 0;
    return lit > 0 ? truth : !truth;
}

static bool clause_satisfied_by_pattern(const std::vector<int>& clause, const CSP& csp, uint32_t pat) {
    for (int lit : clause) {
        if (lit_true_in_pattern(lit, csp, pat)) return true;
    }
    return false;
}

static uint32_t false_value_mask(const CSP& csp, int g, const std::vector<int>& lits) {
    uint32_t m = 0;
    for (int a = 0; a < csp.dom_size[g]; ++a) {
        uint32_t pat = csp.domains[g][a];
        bool all_false = true;
        for (int lit : lits) {
            if (lit_true_in_pattern(lit, csp, pat)) {
                all_false = false;
                break;
            }
        }
        if (all_false) m |= (uint32_t(1) << a);
    }
    return m;
}

static bool split_clause_by_groups(const Formula& f, const CSP& csp, const std::vector<int>& cl,
                                   std::vector<int>& groups, std::vector<std::vector<int>>& parts) {
    groups.clear();
    parts.clear();
    for (int lit : cl) {
        int v = std::abs(lit);
        if (v <= 0 || v > f.vars) return false;
        int g = csp.lit_group[v];
        if (g < 0) return false;
        int idx = -1;
        for (int k = 0; k < (int)groups.size(); ++k) {
            if (groups[k] == g) {
                idx = k;
                break;
            }
        }
        if (idx < 0) {
            idx = (int)groups.size();
            groups.push_back(g);
            parts.push_back({});
        }
        parts[idx].push_back(lit);
    }
    return true;
}

static bool finalize_binary_csp(const Formula& f, CSP& csp) {
    std::vector<std::vector<int>> local(csp.n);
    std::vector<int> groups;
    std::vector<std::vector<int>> parts;

    for (const auto& cl : f.clauses) {
        if (!split_clause_by_groups(f, csp, cl, groups, parts)) return false;
        if (groups.size() == 1) local[groups[0]].push_back(&cl - &f.clauses[0]);
    }

    csp.dom_size.assign(csp.n, 0);
    csp.max_d = 0;
    for (int g = 0; g < csp.n; ++g) {
        std::vector<uint32_t> kept;
        kept.reserve(csp.domains[g].size());
        for (uint32_t pat : csp.domains[g]) {
            bool ok = true;
            for (int ci : local[g]) {
                if (!clause_satisfied_by_pattern(f.clauses[ci], csp, pat)) {
                    ok = false;
                    break;
                }
            }
            if (ok) kept.push_back(pat);
        }
        if (kept.empty() || kept.size() > 32) return false;
        csp.domains[g].swap(kept);
        csp.dom_size[g] = (int)csp.domains[g].size();
        csp.max_d = std::max(csp.max_d, csp.dom_size[g]);
    }

    csp.forbid.assign((size_t)csp.n * csp.max_d * csp.n, 0);
    csp.neighbors.assign(csp.n, {});
    csp.edge_index.assign(csp.n * csp.n, -1);
    std::vector<char> adjacent(csp.n * csp.n, 0);

    auto add_edge = [&](int i, int j) {
        if (!adjacent[i * csp.n + j]) {
            adjacent[i * csp.n + j] = adjacent[j * csp.n + i] = 1;
            csp.neighbors[i].push_back(j);
            csp.neighbors[j].push_back(i);
            int id = (int)csp.edge_u.size();
            csp.edge_u.push_back(i);
            csp.edge_v.push_back(j);
            csp.edge_index[i * csp.n + j] = csp.edge_index[j * csp.n + i] = id;
        }
    };

    for (const auto& cl : f.clauses) {
        if (!split_clause_by_groups(f, csp, cl, groups, parts)) return false;
        if (groups.size() == 1) {
            uint32_t fm = false_value_mask(csp, groups[0], parts[0]);
            if (fm != 0) return false;
            continue;
        }
        if (groups.size() != 2) {
            bool always_satisfied = false;
            for (int k = 0; k < (int)groups.size(); ++k) {
                if (false_value_mask(csp, groups[k], parts[k]) == 0) {
                    always_satisfied = true;
                    break;
                }
            }
            if (!always_satisfied) return false;
            continue;
        }

        int i = groups[0], j = groups[1];
        uint32_t mi = false_value_mask(csp, i, parts[0]);
        uint32_t mj = false_value_mask(csp, j, parts[1]);
        if (mi == 0 || mj == 0) continue;
        for (int a = 0; a < csp.dom_size[i]; ++a) {
            if ((mi >> a) & 1U) csp.forbid[(i * csp.max_d + a) * csp.n + j] |= mj;
        }
        for (int b = 0; b < csp.dom_size[j]; ++b) {
            if ((mj >> b) & 1U) csp.forbid[(j * csp.max_d + b) * csp.n + i] |= mi;
        }
        add_edge(i, j);
    }

    return !csp.edge_u.empty();
}

static bool init_onehot_candidate(const Formula& f, int d, CSP& csp) {
    std::vector<std::vector<int>> groups;
    std::vector<char> seen(f.vars + 1, 0);
    for (const auto& cl : f.clauses) {
        if ((int)cl.size() != d) continue;
        std::vector<int> vars;
        vars.reserve(d);
        for (int lit : cl) {
            int v = std::abs(lit);
            if (v <= 0 || v > f.vars || seen[v]) return false;
            seen[v] = 1;
            vars.push_back(v);
        }
        groups.push_back(cl);
    }
    if ((int)groups.size() * d != f.vars) return false;
    for (int v = 1; v <= f.vars; ++v) if (!seen[v]) return false;

    csp = CSP();
    csp.n = (int)groups.size();
    csp.dimacs_vars = f.vars;
    csp.group_vars.assign(csp.n, {});
    csp.domains.assign(csp.n, {});
    csp.lit_group.assign(f.vars + 1, -1);
    csp.lit_pos.assign(f.vars + 1, -1);

    for (int i = 0; i < csp.n; ++i) {
        csp.group_vars[i].resize(d);
        csp.domains[i].reserve(d);
        for (int a = 0; a < d; ++a) {
            int lit = groups[i][a];
            int v = std::abs(lit);
            csp.group_vars[i][a] = v;
            csp.lit_group[v] = i;
            csp.lit_pos[v] = a;
        }
        for (int a = 0; a < d; ++a) {
            uint32_t pat = 0;
            for (int b = 0; b < d; ++b) {
                bool selected = (a == b);
                bool var_truth = selected ? (groups[i][b] > 0) : (groups[i][b] < 0);
                if (var_truth) pat |= (uint32_t(1) << b);
            }
            csp.domains[i].push_back(pat);
        }
    }
    return finalize_binary_csp(f, csp);
}

static bool detect_onehot_csp(const Formula& f, CSP& csp) {
    std::vector<int> sizes;
    for (const auto& cl : f.clauses) {
        int sz = (int)cl.size();
        if (sz > 2 && f.vars % sz == 0) sizes.push_back(sz);
    }
    std::sort(sizes.begin(), sizes.end());
    sizes.erase(std::unique(sizes.begin(), sizes.end()), sizes.end());
    std::sort(sizes.rbegin(), sizes.rend());

    for (int d : sizes) {
        int count = 0;
        for (const auto& cl : f.clauses) if ((int)cl.size() == d) ++count;
        if (count * d != f.vars || d > 31) continue;
        CSP cand;
        if (init_onehot_candidate(f, d, cand)) {
            csp = std::move(cand);
            return true;
        }
    }
    return false;
}

static bool detect_compact_5bit_csp(const Formula& f, CSP& csp) {
    if (f.vars <= 0 || f.vars % 5 != 0) return false;
    int n = f.vars / 5;
    csp = CSP();
    csp.n = n;
    csp.dimacs_vars = f.vars;
    csp.group_vars.assign(n, {});
    csp.domains.assign(n, {});
    csp.lit_group.assign(f.vars + 1, -1);
    csp.lit_pos.assign(f.vars + 1, -1);
    for (int g = 0; g < n; ++g) {
        csp.group_vars[g].resize(5);
        for (int p = 0; p < 5; ++p) {
            int v = g * 5 + p + 1;
            csp.group_vars[g][p] = v;
            csp.lit_group[v] = g;
            csp.lit_pos[v] = p;
        }
        for (uint32_t pat = 0; pat < 32; ++pat) csp.domains[g].push_back(pat);
    }
    if (!finalize_binary_csp(f, csp)) return false;
    for (int g = 0; g < n; ++g) {
        if (csp.dom_size[g] != 17) return false;
    }
    return true;
}

static bool detect_recovered_csp(const Formula& f, CSP& csp) {
    if (detect_onehot_csp(f, csp)) return true;
    if (detect_compact_5bit_csp(f, csp)) return true;
    return false;
}

static bool verify_assignment(const Formula& f, const CSP& csp, const std::vector<int>& val, std::vector<int>& model) {
    model.assign(f.vars + 1, 0);
    for (int i = 0; i < csp.n; ++i) {
        if (val[i] < 0 || val[i] >= csp.dom_size[i]) return false;
        uint32_t pat = csp.domains[i][val[i]];
        for (int p = 0; p < (int)csp.group_vars[i].size(); ++p) {
            int v = csp.group_vars[i][p];
            bool truth = ((pat >> p) & 1U) != 0;
            model[v] = truth ? v : -v;
        }
    }
    for (int v = 1; v <= f.vars; ++v) if (model[v] == 0) return false;

    for (const auto& cl : f.clauses) {
        bool sat = false;
        for (int lit : cl) {
            int v = std::abs(lit);
            bool truth = model[v] > 0;
            if ((lit > 0 && truth) || (lit < 0 && !truth)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static bool verify_bool_model(const Formula& f, const std::vector<char>& asg, std::vector<int>& model) {
    model.assign(f.vars + 1, 0);
    for (int v = 1; v <= f.vars; ++v) model[v] = asg[v] ? v : -v;
    for (const auto& cl : f.clauses) {
        bool sat = false;
        for (int lit : cl) {
            bool truth = asg[std::abs(lit)];
            if ((lit > 0 && truth) || (lit < 0 && !truth)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model(const std::vector<int>& model, int vars) {
    std::printf("s SATISFIABLE\n");
    const int per_line = 12;
    for (int v = 1; v <= vars; ++v) {
        if ((v - 1) % per_line == 0) std::printf("v");
        std::printf(" %d", model[v]);
        if (v % per_line == 0) std::printf(" 0\n");
    }
    if (vars % per_line != 0) std::printf(" 0\n");
}

static uint32_t full_mask_for_size(int d) {
    return d >= 32 ? 0xffffffffU : ((uint32_t(1) << d) - 1U);
}

static int first_bit_index(uint32_t x) {
    return __builtin_ctz(x);
}

struct BoolLocalSearch {
    const Formula& f;
    std::mt19937_64 rng;
    std::vector<char> asg;
    std::vector<int> sat_count;
    std::vector<int> unsat, unsat_pos;
    std::vector<std::vector<int>> pos_occ, neg_occ;

    BoolLocalSearch(const Formula& formula, uint64_t seed)
        : f(formula), rng(seed), asg(formula.vars + 1), sat_count(formula.clauses.size()),
          unsat_pos(formula.clauses.size(), -1), pos_occ(formula.vars + 1), neg_occ(formula.vars + 1) {
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            for (int lit : f.clauses[ci]) {
                if (lit > 0) pos_occ[lit].push_back(ci);
                else neg_occ[-lit].push_back(ci);
            }
        }
    }

    bool lit_true(int lit) const {
        bool t = asg[std::abs(lit)];
        return lit > 0 ? t : !t;
    }

    void add_unsat(int ci) {
        if (unsat_pos[ci] >= 0) return;
        unsat_pos[ci] = (int)unsat.size();
        unsat.push_back(ci);
    }

    void 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;
    }

    void random_init() {
        std::uniform_int_distribution<int> bit(0, 1);
        for (int v = 1; v <= f.vars; ++v) asg[v] = bit(rng);
        unsat.clear();
        std::fill(unsat_pos.begin(), unsat_pos.end(), -1);
        for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
            int cnt = 0;
            for (int lit : f.clauses[ci]) if (lit_true(lit)) ++cnt;
            sat_count[ci] = cnt;
            if (cnt == 0) add_unsat(ci);
        }
    }

    int flip_delta(int v) const {
        int delta = 0;
        for (int ci : pos_occ[v]) {
            bool cur = asg[v];
            if (cur && sat_count[ci] == 1) ++delta;
            else if (!cur && sat_count[ci] == 0) --delta;
        }
        for (int ci : neg_occ[v]) {
            bool cur = !asg[v];
            if (cur && sat_count[ci] == 1) ++delta;
            else if (!cur && sat_count[ci] == 0) --delta;
        }
        return delta;
    }

    void flip_var(int v) {
        bool old = asg[v];
        asg[v] = !asg[v];
        for (int ci : pos_occ[v]) {
            int before = sat_count[ci];
            sat_count[ci] += old ? -1 : 1;
            if (before == 0 && sat_count[ci] > 0) remove_unsat(ci);
            else if (before > 0 && sat_count[ci] == 0) add_unsat(ci);
        }
        for (int ci : neg_occ[v]) {
            int before = sat_count[ci];
            sat_count[ci] += old ? 1 : -1;
            if (before == 0 && sat_count[ci] > 0) remove_unsat(ci);
            else if (before > 0 && sat_count[ci] == 0) add_unsat(ci);
        }
    }

    bool run_until(const Clock::time_point& deadline, std::vector<char>& answer) {
        std::uniform_real_distribution<double> real01(0.0, 1.0);
        while (Clock::now() < deadline) {
            random_init();
            int max_steps = 200000 + 2000 * f.vars;
            for (int step = 0; step < max_steps && Clock::now() < deadline; ++step) {
                if (unsat.empty()) {
                    answer = asg;
                    return true;
                }
                int ci = unsat[std::uniform_int_distribution<int>(0, (int)unsat.size() - 1)(rng)];
                const auto& cl = f.clauses[ci];
                int chosen = std::abs(cl[std::uniform_int_distribution<int>(0, (int)cl.size() - 1)(rng)]);
                if (real01(rng) >= 0.08) {
                    int best = 1 << 30;
                    std::vector<int> choices;
                    for (int lit : cl) {
                        int v = std::abs(lit);
                        int d = flip_delta(v);
                        if (d < best) {
                            best = d;
                            choices.clear();
                            choices.push_back(v);
                        } else if (d == best) {
                            choices.push_back(v);
                        }
                    }
                    chosen = choices[std::uniform_int_distribution<int>(0, (int)choices.size() - 1)(rng)];
                }
                flip_var(chosen);
            }
        }
        return false;
    }
};

static bool try_bool_fallback(const Formula& f, const Clock::time_point& deadline, uint64_t seed) {
    std::vector<char> bool_answer;
    BoolLocalSearch bls(f, seed);
    if (bls.run_until(deadline, bool_answer)) {
        std::vector<int> model;
        if (verify_bool_model(f, bool_answer, model)) {
            print_model(model, f.vars);
            return true;
        }
    }
    return false;
}

struct LocalSearch {
    const CSP& c;
    std::mt19937_64 rng;
    std::vector<int> val;
    std::vector<int> weights;
    std::vector<int> conf;
    std::vector<std::vector<int>> static_score;
    std::vector<int> bad;
    std::vector<char> is_bad;
    int total_conf = 0;

    explicit LocalSearch(const CSP& csp, uint64_t seed)
        : c(csp), rng(seed), val(csp.n), weights(csp.edge_u.size(), 1),
          conf(csp.n), static_score(csp.n), is_bad(csp.n) {
        for (int i = 0; i < c.n; ++i) {
            static_score[i].assign(c.dom_size[i], 0);
            for (int a = 0; a < c.dom_size[i]; ++a) {
                int s = 0;
                for (int j : c.neighbors[i]) s += __builtin_popcount(c.mask(i, a, j));
                static_score[i][a] = s;
            }
        }
    }

    int edge_weight(int i, int j) const {
        int id = c.edge_index[i * c.n + j];
        return id >= 0 ? weights[id] : 0;
    }

    int actual_conflicts(const std::vector<int>& values, std::vector<int>* bad_vars = nullptr) const {
        int count = 0;
        std::vector<char> seen;
        if (bad_vars) {
            bad_vars->clear();
            seen.assign(c.n, 0);
        }
        for (size_t e = 0; e < c.edge_u.size(); ++e) {
            int i = c.edge_u[e], j = c.edge_v[e];
            if ((c.mask(i, values[i], j) >> values[j]) & 1U) {
                ++count;
                if (bad_vars) {
                    if (!seen[i]) {
                        seen[i] = 1;
                        bad_vars->push_back(i);
                    }
                    if (!seen[j]) {
                        seen[j] = 1;
                        bad_vars->push_back(j);
                    }
                }
            }
        }
        return count;
    }

    int score_value(int i, int a) const {
        int s = 0;
        for (int j : c.neighbors[i]) {
            if ((c.mask(i, a, j) >> val[j]) & 1U) s += edge_weight(i, j);
        }
        return s;
    }

    void add_bad(int i) {
        if (is_bad[i]) return;
        is_bad[i] = 1;
        bad.push_back(i);
    }

    void remove_bad(int i) {
        if (!is_bad[i]) return;
        for (int p = 0; p < (int)bad.size(); ++p) {
            if (bad[p] == i) {
                bad[p] = bad.back();
                bad.pop_back();
                break;
            }
        }
        is_bad[i] = 0;
    }

    void update_bad(int i) {
        if (conf[i] > 0) add_bad(i);
        else remove_bad(i);
    }

    void recompute_conflicts() {
        std::fill(conf.begin(), conf.end(), 0);
        total_conf = 0;
        for (size_t e = 0; e < c.edge_u.size(); ++e) {
            int i = c.edge_u[e], j = c.edge_v[e];
            if ((c.mask(i, val[i], j) >> val[j]) & 1U) {
                int w = weights[e];
                conf[i] += w;
                conf[j] += w;
                total_conf += w;
            }
        }
        bad.clear();
        std::fill(is_bad.begin(), is_bad.end(), 0);
        for (int i = 0; i < c.n; ++i) {
            if (conf[i] > 0) {
                bad.push_back(i);
                is_bad[i] = 1;
            }
        }
    }

    void apply_move(int i, int chosen) {
        int old = val[i];
        if (old == chosen) return;
        for (int j : c.neighbors[i]) {
            int id = c.edge_index[i * c.n + j];
            int w = weights[id];
            bool before = ((c.mask(i, old, j) >> val[j]) & 1U) != 0;
            bool after = ((c.mask(i, chosen, j) >> val[j]) & 1U) != 0;
            if (before == after) continue;
            if (before) {
                total_conf -= w;
                conf[i] -= w;
                conf[j] -= w;
            } else {
                total_conf += w;
                conf[i] += w;
                conf[j] += w;
            }
            update_bad(j);
        }
        val[i] = chosen;
        update_bad(i);
    }

    void greedy_init() {
        std::vector<int> order(c.n);
        std::iota(order.begin(), order.end(), 0);
        std::shuffle(order.begin(), order.end(), rng);
        for (int i = 0; i < c.n; ++i) {
            val[i] = std::uniform_int_distribution<int>(0, c.dom_size[i] - 1)(rng);
        }
        static const int bias_options[] = {0, 1, 2, 4, 8};
        int bias = bias_options[std::uniform_int_distribution<int>(0, 4)(rng)];
        std::vector<char> assigned(c.n, 0);
        for (int i : order) {
            int64_t best = (int64_t)1 << 60;
            std::vector<int> choices;
            for (int a = 0; a < c.dom_size[i]; ++a) {
                int s = 0;
                for (int j : c.neighbors[i]) {
                    if (assigned[j] && ((c.mask(i, a, j) >> val[j]) & 1U)) ++s;
                }
                int64_t rank = (int64_t)s * 100000 + (int64_t)bias * static_score[i][a];
                if (rank < best) {
                    best = rank;
                    choices.clear();
                    choices.push_back(a);
                } else if (rank == best) {
                    choices.push_back(a);
                }
            }
            val[i] = choices[std::uniform_int_distribution<int>(0, (int)choices.size() - 1)(rng)];
            assigned[i] = 1;
        }
        recompute_conflicts();
    }

    void static_init() {
        for (int i = 0; i < c.n; ++i) {
            int best = 1 << 30;
            std::vector<int> choices;
            for (int a = 0; a < c.dom_size[i]; ++a) {
                int s = static_score[i][a];
                if (s < best) {
                    best = s;
                    choices.clear();
                    choices.push_back(a);
                } else if (s == best) {
                    choices.push_back(a);
                }
            }
            val[i] = choices[std::uniform_int_distribution<int>(0, (int)choices.size() - 1)(rng)];
        }
        recompute_conflicts();
    }

    bool shake_conflict_neighborhood() {
        std::vector<int> bad_vars;
        int bad_edges = actual_conflicts(val, &bad_vars);
        if (bad_edges == 0) return true;
        if (bad_edges > 4 || bad_vars.empty()) return false;

        std::vector<char> in(c.n, 0), assigned(c.n, 0);
        std::vector<int> subset;
        auto add_var = [&](int x) {
            if (!in[x]) {
                in[x] = 1;
                subset.push_back(x);
            }
        };
        for (int i : bad_vars) {
            add_var(i);
            for (int j : c.neighbors[i]) add_var(j);
        }
        if (bad_edges <= 2) {
            std::vector<int> fringe;
            for (int i : subset) {
                for (int j : c.neighbors[i]) {
                    if (!in[j]) fringe.push_back(j);
                }
            }
            std::shuffle(fringe.begin(), fringe.end(), rng);
            for (int j : fringe) {
                if ((int)subset.size() >= 52) break;
                add_var(j);
            }
        }
        if ((int)subset.size() > 58) return false;

        std::shuffle(subset.begin(), subset.end(), rng);
        for (int i : subset) val[i] = std::uniform_int_distribution<int>(0, c.dom_size[i] - 1)(rng);

        static const int bias_options[] = {1, 2, 4, 8};
        int bias = bias_options[std::uniform_int_distribution<int>(0, 3)(rng)];
        for (int i : subset) {
            int64_t best = (int64_t)1 << 60;
            std::vector<int> choices;
            for (int a = 0; a < c.dom_size[i]; ++a) {
                int s = 0;
                for (int j : c.neighbors[i]) {
                    if (!in[j] || assigned[j]) {
                        if ((c.mask(i, a, j) >> val[j]) & 1U) s += edge_weight(i, j);
                    }
                }
                int64_t rank = (int64_t)s * 100000 + (int64_t)bias * static_score[i][a];
                if (rank < best) {
                    best = rank;
                    choices.clear();
                    choices.push_back(a);
                } else if (rank == best) {
                    choices.push_back(a);
                }
            }
            val[i] = choices[std::uniform_int_distribution<int>(0, (int)choices.size() - 1)(rng)];
            assigned[i] = 1;
        }
        recompute_conflicts();
        return total_conf == 0;
    }

    bool repair_subset(const std::vector<int>& base, const std::vector<int>& subset,
                       int node_limit, const Clock::time_point& deadline,
                       std::vector<int>& answer) const {
        int s = (int)subset.size();
        if (s <= 0 || s > c.n) return false;
        std::vector<int> local_of(c.n, -1);
        for (int x = 0; x < s; ++x) local_of[subset[x]] = x;

        std::vector<std::vector<int>> lneigh(s);
        std::vector<uint32_t> dom(s);
        for (int x = 0; x < s; ++x) {
            int i = subset[x];
            uint32_t m = full_mask_for_size(c.dom_size[i]);
            for (int j : c.neighbors[i]) {
                int y = local_of[j];
                if (y >= 0) {
                    lneigh[x].push_back(y);
                } else {
                    int bj = base[j];
                    uint32_t bad = 0;
                    for (int a = 0; a < c.dom_size[i]; ++a) {
                        if ((c.mask(i, a, j) >> bj) & 1U) bad |= (uint32_t(1) << a);
                    }
                    m &= ~bad;
                }
            }
            if (m == 0) return false;
            dom[x] = m;
        }

        int nodes = 0;
        auto revise = [&](int x, int y, std::vector<std::pair<int, uint32_t>>& trail) -> bool {
            int i = subset[x], j = subset[y];
            uint32_t dx = dom[x], dy = dom[y], rem = 0;
            uint32_t mm = dx;
            while (mm) {
                uint32_t lb = mm & -mm;
                int a = first_bit_index(lb);
                mm -= lb;
                if (((~c.mask(i, a, j)) & dy) == 0) rem |= lb;
            }
            if (rem == 0) return true;
            uint32_t nd = dx & ~rem;
            if (nd == 0) return false;
            trail.push_back({x, dx});
            dom[x] = nd;
            return true;
        };

        auto ac3 = [&](std::vector<std::pair<int, int>> queue,
                       std::vector<std::pair<int, uint32_t>>& trail) -> bool {
            while (!queue.empty()) {
                if (Clock::now() >= deadline) return false;
                auto [x, y] = queue.back();
                queue.pop_back();
                uint32_t before = dom[x];
                if (!revise(x, y, trail)) return false;
                if (dom[x] != before) {
                    for (int z : lneigh[x]) {
                        if (z != y) queue.push_back({z, x});
                    }
                }
            }
            return true;
        };

        std::vector<std::pair<int, int>> initial_queue;
        for (int x = 0; x < s; ++x) {
            for (int y : lneigh[x]) initial_queue.push_back({x, y});
        }
        std::vector<std::pair<int, uint32_t>> initial_trail;
        if (!ac3(initial_queue, initial_trail)) return false;

        std::function<bool()> dfs = [&]() -> bool {
            if (++nodes > node_limit || Clock::now() >= deadline) return false;
            int xbest = -1;
            int best_count = 1000;
            int best_degree = -1;
            for (int x = 0; x < s; ++x) {
                int pc = __builtin_popcount(dom[x]);
                if (pc > 1) {
                    int deg = (int)lneigh[x].size();
                    if (pc < best_count || (pc == best_count && deg > best_degree)) {
                        best_count = pc;
                        best_degree = deg;
                        xbest = x;
                    }
                }
            }
            if (xbest < 0) {
                answer = base;
                for (int x = 0; x < s; ++x) answer[subset[x]] = first_bit_index(dom[x]);
                return true;
            }

            int gi = subset[xbest];
            std::vector<int> values;
            uint32_t mm = dom[xbest];
            while (mm) {
                uint32_t lb = mm & -mm;
                values.push_back(first_bit_index(lb));
                mm -= lb;
            }
            std::sort(values.begin(), values.end(), [&](int a, int b) {
                if (a == base[gi]) return true;
                if (b == base[gi]) return false;
                int ca = static_score[gi][a], cb = static_score[gi][b];
                for (int y : lneigh[xbest]) {
                    ca += __builtin_popcount(dom[y] & c.mask(gi, a, subset[y]));
                    cb += __builtin_popcount(dom[y] & c.mask(gi, b, subset[y]));
                }
                return ca < cb;
            });

            for (int a : values) {
                std::vector<std::pair<int, uint32_t>> trail;
                uint32_t old = dom[xbest];
                trail.push_back({xbest, old});
                dom[xbest] = (uint32_t(1) << a);
                std::vector<std::pair<int, int>> queue;
                for (int y : lneigh[xbest]) queue.push_back({y, xbest});
                if (ac3(queue, trail) && dfs()) return true;
                for (auto it = trail.rbegin(); it != trail.rend(); ++it) dom[it->first] = it->second;
            }
            return false;
        };

        bool ok = dfs();
        for (auto it = initial_trail.rbegin(); it != initial_trail.rend(); ++it) dom[it->first] = it->second;
        return ok;
    }

    int actual_score_value(const std::vector<int>& values, int i, int a) const {
        int s = 0;
        for (int j : c.neighbors[i]) {
            if ((c.mask(i, a, j) >> values[j]) & 1U) ++s;
        }
        return s;
    }

    bool hamming_repair(const std::vector<int>& base, int max_changed, int node_limit,
                        const Clock::time_point& deadline, std::vector<int>& answer) const {
        std::vector<int> cur = base;
        std::vector<char> changed(c.n, 0);
        int nodes = 0;

        std::function<bool(int)> dfs = [&](int budget) -> bool {
            if (++nodes > node_limit || Clock::now() >= deadline) return false;
            int ei = -1, ej = -1;
            int conflicts = 0;
            for (size_t e = 0; e < c.edge_u.size(); ++e) {
                int i = c.edge_u[e], j = c.edge_v[e];
                if ((c.mask(i, cur[i], j) >> cur[j]) & 1U) {
                    if (ei < 0) {
                        ei = i;
                        ej = j;
                    }
                    ++conflicts;
                }
            }
            if (conflicts == 0) {
                answer = cur;
                return true;
            }
            if (budget <= 0) return false;

            int endpoints[2] = {ei, ej};
            for (int side = 0; side < 2; ++side) {
                int x = endpoints[side];
                int y = endpoints[1 - side];
                int cost = 1;
                if (cost > budget) continue;
                std::vector<int> values;
                for (int a = 0; a < c.dom_size[x]; ++a) {
                    if (a == cur[x]) continue;
                    if (((c.mask(x, a, y) >> cur[y]) & 1U) == 0) values.push_back(a);
                }
                std::sort(values.begin(), values.end(), [&](int a, int b) {
                    int sa = actual_score_value(cur, x, a);
                    int sb = actual_score_value(cur, x, b);
                    if (sa != sb) return sa < sb;
                    return static_score[x][a] < static_score[x][b];
                });
                if ((int)values.size() > 9) values.resize(9);
                int old = cur[x];
                bool was_changed = changed[x];
                for (int a : values) {
                    cur[x] = a;
                    changed[x] = 1;
                    if (dfs(budget - cost)) return true;
                    cur[x] = old;
                    changed[x] = was_changed;
                }
            }
            return false;
        };

        return dfs(max_changed);
    }

    bool try_exact_repair(const std::vector<int>& base, const Clock::time_point& deadline,
                          std::vector<int>& answer) const {
        std::vector<int> bad_vars;
        int bad_edges = actual_conflicts(base, &bad_vars);
        if (bad_edges == 0) {
            answer = base;
            return true;
        }
        if (bad_edges > 12 || bad_vars.empty()) return false;

        std::sort(bad_vars.begin(), bad_vars.end());
        bad_vars.erase(std::unique(bad_vars.begin(), bad_vars.end()), bad_vars.end());
        if ((int)bad_vars.size() <= 26 &&
            repair_subset(base, bad_vars, 30000, deadline, answer) &&
            actual_conflicts(answer, nullptr) == 0) {
            return true;
        }
        std::vector<char> in(c.n, 0);
        std::vector<int> expanded;
        for (int i : bad_vars) {
            if (!in[i]) {
                in[i] = 1;
                expanded.push_back(i);
            }
            for (int j : c.neighbors[i]) {
                if (!in[j]) {
                    in[j] = 1;
                    expanded.push_back(j);
                }
            }
        }
        if ((int)expanded.size() <= 38 &&
            repair_subset(base, expanded, 80000, deadline, answer) &&
            actual_conflicts(answer, nullptr) == 0) {
            return true;
        }
        return false;
    }

    bool run_until(const Clock::time_point& deadline, std::vector<int>& answer) {
        std::uniform_real_distribution<double> real01(0.0, 1.0);
        int restart = 0;
        int best_actual = 1 << 30;
        bool debug = std::getenv("LYMPHO_DEBUG") != nullptr;
        while (Clock::now() < deadline) {
            ++restart;
            std::fill(weights.begin(), weights.end(), 1);
            if (restart % 17 == 0) static_init();
            else greedy_init();
            int stagnant = 0;
            int best_seen = total_conf;
            int max_steps = 20000 + 4000 * c.n;
            for (int step = 0; step < max_steps && Clock::now() < deadline; ++step) {
                if (total_conf == 0) {
                    answer = val;
                    return true;
                }
                if ((step & 511) == 0) {
                    int actual = actual_conflicts(val, nullptr);
                    if (actual < best_actual) {
                        best_actual = actual;
                        if (debug) std::fprintf(stderr, "best_actual=%d restart=%d step=%d weighted=%d\n", best_actual, restart, step, total_conf);
                        if (debug && best_actual <= 2) {
                            std::fprintf(stderr, "vals");
                            for (int x : val) std::fprintf(stderr, " %d", x);
                            std::fprintf(stderr, "\n");
                        }
                    }
                    if (actual <= 8 && try_exact_repair(val, deadline, answer)) return true;
                }
                if (total_conf < best_seen) {
                    best_seen = total_conf;
                    stagnant = 0;
                } else {
                    ++stagnant;
                }

                if (stagnant > 800 + 20 * c.n) {
                    if (try_exact_repair(val, deadline, answer)) return true;
                    // Breakout: penalize exactly the currently violated high-level constraints.
                    for (size_t e = 0; e < c.edge_u.size(); ++e) {
                        int i = c.edge_u[e], j = c.edge_v[e];
                        if ((c.mask(i, val[i], j) >> val[j]) & 1U && weights[e] < 1000000) {
                            weights[e] += 1;
                        }
                    }
                    recompute_conflicts();
                    stagnant = 0;
                    continue;
                }

                int i;
                if (real01(rng) < 0.20) {
                    int probes = std::min<int>(8, bad.size());
                    i = bad[std::uniform_int_distribution<int>(0, (int)bad.size() - 1)(rng)];
                    for (int k = 1; k < probes; ++k) {
                        int cand = bad[std::uniform_int_distribution<int>(0, (int)bad.size() - 1)(rng)];
                        if (conf[cand] > conf[i]) i = cand;
                    }
                } else {
                    i = bad[std::uniform_int_distribution<int>(0, (int)bad.size() - 1)(rng)];
                }

                int cur = val[i];
                int64_t best = (int64_t)1 << 60;
                std::vector<int> choices;
                int move_bias = (step & 7) == 0 ? 1 : 0;
                for (int a = 0; a < c.dom_size[i]; ++a) {
                    int s = score_value(i, a);
                    if (a == cur) s += 1; // weakly prefer movement on ties
                    int64_t rank = (int64_t)s * 100000 + (int64_t)move_bias * static_score[i][a];
                    if (rank < best) {
                        best = rank;
                        choices.clear();
                        choices.push_back(a);
                    } else if (rank == best) {
                        choices.push_back(a);
                    }
                }
                int chosen;
                if (real01(rng) < 0.015) {
                    chosen = std::uniform_int_distribution<int>(0, c.dom_size[i] - 1)(rng);
                } else {
                    chosen = choices[std::uniform_int_distribution<int>(0, (int)choices.size() - 1)(rng)];
                }
                if (chosen == val[i]) continue;
                apply_move(i, chosen);
            }
        }
        return false;
    }
};

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;
    }
    auto deadline = Clock::now() + std::chrono::seconds(590);
    CSP csp;
    if (!detect_recovered_csp(f, csp)) {
        uint64_t seed = 0xa0761d6478bd642fULL ^ (uint64_t)f.vars ^ ((uint64_t)f.clauses.size() << 17);
        if (try_bool_fallback(f, deadline, seed)) return 10;
        std::printf("s UNKNOWN\n");
        return 0;
    }
    std::vector<int> assignment;
    uint64_t seed = 0x9e3779b97f4a7c15ULL ^ (uint64_t)f.vars ^ ((uint64_t)f.clauses.size() << 32);
    LocalSearch ls(csp, seed);
    auto csp_deadline = Clock::now() + std::chrono::seconds(500);
    if (csp_deadline > deadline) csp_deadline = deadline;
    if (!ls.run_until(csp_deadline, assignment)) {
        if (try_bool_fallback(f, deadline, seed ^ 0xe7037ed1a0b428dbULL)) return 10;
        std::printf("s UNKNOWN\n");
        return 0;
    }
    std::vector<int> model;
    if (!verify_assignment(f, csp, assignment, model)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    print_model(model, f.vars);
    return 10;
}
