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

using Clock = std::chrono::steady_clock;

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

static bool parse_dimacs(const char* path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string line;
    while (std::getline(in, line)) {
        if (line.empty()) continue;
        if (line[0] == 'c') continue;
        if (line[0] == 'p') {
            std::stringstream ss(line);
            std::string p, cnf;
            int m = 0;
            ss >> p >> cnf >> f.nvars >> m;
            f.clauses.reserve(std::max(0, m));
            continue;
        }
        std::stringstream ss(line);
        int lit = 0;
        std::vector<int> clause;
        while (ss >> lit) {
            if (lit == 0) break;
            clause.push_back(lit);
        }
        if (!clause.empty()) f.clauses.push_back(std::move(clause));
    }
    return f.nvars > 0;
}

static bool check_model(const Formula& f, const std::vector<int8_t>& val) {
    if ((int)val.size() <= f.nvars) return false;
    for (const auto& c : f.clauses) {
        bool ok = false;
        for (int lit : c) {
            int v = std::abs(lit);
            bool is_true = val[v] > 0;
            if ((lit > 0) == is_true) {
                ok = true;
                break;
            }
        }
        if (!ok) return false;
    }
    return true;
}

static void print_sat_model(const Formula& f, const std::vector<int8_t>& val) {
    std::printf("s SATISFIABLE\n");
    std::printf("v");
    int on_line = 0;
    for (int v = 1; v <= f.nvars; ++v) {
        int lit = val[v] > 0 ? v : -v;
        std::printf(" %d", lit);
        if (++on_line >= 20 && v != f.nvars) {
            std::printf("\nv");
            on_line = 0;
        }
    }
    std::printf(" 0\n");
}

static uint64_t triple_key(int a, int b, int c) {
    if (a > b) std::swap(a, b);
    if (b > c) std::swap(b, c);
    if (a > b) std::swap(a, b);
    return ((uint64_t)a << 42) ^ ((uint64_t)b << 21) ^ (uint64_t)c;
}

static int lit_index(int lit, int n) {
    return lit > 0 ? lit - 1 : n + (-lit - 1);
}

static uint64_t signed_triple_key(int a, int b, int c, int n) {
    int ia = lit_index(a, n) + 1;
    int ib = lit_index(b, n) + 1;
    int ic = lit_index(c, n) + 1;
    if (ia > ib) std::swap(ia, ib);
    if (ib > ic) std::swap(ib, ic);
    if (ia > ib) std::swap(ia, ib);
    return ((uint64_t)ia << 42) ^ ((uint64_t)ib << 21) ^ (uint64_t)ic;
}

struct DSU {
    std::vector<int> p, sz;
    explicit DSU(int n = 0) { reset(n); }
    void reset(int n) {
        p.resize(n);
        sz.assign(n, 1);
        std::iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }
    void unite(int a, int b) {
        int ra = find(a), rb = find(b);
        if (ra == rb) return;
        if (sz[ra] < sz[rb]) std::swap(ra, rb);
        p[rb] = ra;
        sz[ra] += sz[rb];
    }
};

struct CardGroup {
    std::vector<int> vars;
    std::unordered_map<uint64_t, int> triple_id;
};

struct SignedCardGroup {
    std::vector<int> lits;
    std::unordered_map<uint64_t, int> triple_id;
};

static void emit_pol_sum(std::ofstream& out, const std::vector<std::string>& terms) {
    if (terms.empty()) return;
    out << terms[0];
    if (terms.size() >= 2) out << ' ' << terms[1] << " +";
    for (size_t i = 2; i < terms.size(); ++i) out << ' ' << terms[i] << " +";
}

static bool emit_signed_card_group_proof(std::ofstream& out, const SignedCardGroup& g,
                                         const std::string& label, int n) {
    int k = (int)g.lits.size();
    if (k == 4) {
        std::vector<std::string> ids;
        ids.reserve(4);
        for (int i = 0; i < 4; ++i) {
            for (int j = i + 1; j < 4; ++j) {
                for (int l = j + 1; l < 4; ++l) {
                    auto it = g.triple_id.find(signed_triple_key(g.lits[i], g.lits[j], g.lits[l], n));
                    if (it == g.triple_id.end()) return false;
                    ids.push_back(std::to_string(it->second));
                }
            }
        }
        out << label << " pol ";
        emit_pol_sum(out, ids);
        out << " 3 d s ;\n";
        return true;
    }
    if (k != 5) return false;

    std::vector<std::string> sublabels;
    for (int omit = 0; omit < 5; ++omit) {
        std::vector<int> sub;
        for (int i = 0; i < 5; ++i) {
            if (i != omit) sub.push_back(g.lits[i]);
        }
        std::vector<std::string> ids;
        for (int i = 0; i < 4; ++i) {
            for (int j = i + 1; j < 4; ++j) {
                for (int l = j + 1; l < 4; ++l) {
                    auto it = g.triple_id.find(signed_triple_key(sub[i], sub[j], sub[l], n));
                    if (it == g.triple_id.end()) return false;
                    ids.push_back(std::to_string(it->second));
                }
            }
        }
        std::string sublabel = label + "_" + std::to_string(omit);
        sublabels.push_back(sublabel);
        out << sublabel << " pol ";
        emit_pol_sum(out, ids);
        out << " 3 d s ;\n";
    }
    out << label << " pol ";
    emit_pol_sum(out, sublabels);
    out << " 4 d s ;\n";
    return true;
}

static bool prove_signed_ternary_count_unsat(const Formula& f, const char* out_path) {
    int n = f.nvars;
    for (const auto& c : f.clauses) {
        if (c.size() != 3) return false;
        if (std::abs(c[0]) == std::abs(c[1]) || std::abs(c[0]) == std::abs(c[2]) ||
            std::abs(c[1]) == std::abs(c[2])) return false;
    }

    DSU dsu(2 * n);
    std::vector<int> touched(2 * n, 0);
    std::unordered_map<uint64_t, int> all_triples;
    all_triples.reserve(f.clauses.size() * 2);
    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        const auto& c = f.clauses[ci];
        uint64_t key = signed_triple_key(c[0], c[1], c[2], n);
        if (all_triples.count(key)) return false;
        all_triples[key] = ci + 1;
        int a = lit_index(c[0], n), b = lit_index(c[1], n), d = lit_index(c[2], n);
        touched[a] = touched[b] = touched[d] = 1;
        dsu.unite(a, b);
        dsu.unite(a, d);
    }

    std::unordered_map<int, int> comp_index;
    std::vector<SignedCardGroup> groups;
    for (int lit = -n; lit <= n; ++lit) {
        if (lit == 0) continue;
        int idx = lit_index(lit, n);
        if (!touched[idx]) continue;
        int root = dsu.find(idx);
        auto it = comp_index.find(root);
        if (it == comp_index.end()) {
            int gi = (int)groups.size();
            comp_index.emplace(root, gi);
            groups.push_back(SignedCardGroup{});
            it = comp_index.find(root);
        }
        groups[it->second].lits.push_back(lit);
    }
    if (groups.empty()) return false;

    int expected = 0;
    int threshold_sum = 0;
    for (auto& g : groups) {
        std::sort(g.lits.begin(), g.lits.end(), [&](int a, int b) {
            return lit_index(a, n) < lit_index(b, n);
        });
        int k = (int)g.lits.size();
        if (k != 4 && k != 5) return false;
        expected += (k == 4) ? 4 : 10;
        threshold_sum += k - 2;
        for (int i = 0; i < k; ++i) {
            for (int j = i + 1; j < k; ++j) {
                for (int l = j + 1; l < k; ++l) {
                    uint64_t key = signed_triple_key(g.lits[i], g.lits[j], g.lits[l], n);
                    auto it = all_triples.find(key);
                    if (it == all_triples.end()) return false;
                    g.triple_id[key] = it->second;
                }
            }
        }
    }
    if (expected != (int)f.clauses.size()) return false;
    if (threshold_sum <= n) return false;

    for (int v = 1; v <= n; ++v) {
        if (!touched[lit_index(v, n)] || !touched[lit_index(-v, n)]) return false;
    }

    std::ofstream out(out_path);
    if (!out) return false;
    out << "pseudo-Boolean proof version 3.0\n";
    std::vector<std::string> labels;
    labels.reserve(groups.size());
    for (int i = 0; i < (int)groups.size(); ++i) {
        std::string label = "@s" + std::to_string(i);
        if (!emit_signed_card_group_proof(out, groups[i], label, n)) return false;
        labels.push_back(label);
    }
    if (labels.size() < 2) return false;
    out << "@contr pol ";
    emit_pol_sum(out, labels);
    out << " ;\n";
    out << "output NONE ;\n";
    out << "conclusion UNSAT : @contr ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return true;
}

static bool build_monotone_ternary_groups(const Formula& f, int sign,
                                          std::vector<CardGroup>& groups,
                                          int& threshold_sum) {
    int n = f.nvars;
    DSU dsu(n + 1);
    std::vector<int> touched(n + 1, 0);
    std::unordered_map<uint64_t, int> all_triples;
    int clause_count = 0;

    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        const auto& c = f.clauses[ci];
        bool this_sign = c.size() == 3;
        for (int lit : c) this_sign = this_sign && (lit * sign > 0);
        if (!this_sign) continue;
        int a = std::abs(c[0]), b = std::abs(c[1]), d = std::abs(c[2]);
        if (a == b || a == d || b == d) return false;
        uint64_t key = triple_key(a, b, d);
        if (all_triples.count(key)) return false;
        all_triples[key] = ci + 1;
        ++clause_count;
        touched[a] = touched[b] = touched[d] = 1;
        dsu.unite(a, b);
        dsu.unite(a, d);
    }
    if (clause_count == 0) return false;

    std::unordered_map<int, int> comp_index;
    for (int v = 1; v <= n; ++v) {
        if (!touched[v]) continue;
        int root = dsu.find(v);
        auto it = comp_index.find(root);
        if (it == comp_index.end()) {
            int idx = (int)groups.size();
            comp_index.emplace(root, idx);
            groups.push_back(CardGroup{});
            it = comp_index.find(root);
        }
        groups[it->second].vars.push_back(v);
    }

    int expected_clauses = 0;
    threshold_sum = 0;
    for (auto& g : groups) {
        std::sort(g.vars.begin(), g.vars.end());
        int k = (int)g.vars.size();
        if (k != 4 && k != 5) return false;
        threshold_sum += k - 2;
        expected_clauses += (k == 4) ? 4 : 10;
        for (int i = 0; i < k; ++i) {
            for (int j = i + 1; j < k; ++j) {
                for (int l = j + 1; l < k; ++l) {
                    uint64_t key = triple_key(g.vars[i], g.vars[j], g.vars[l]);
                    auto it = all_triples.find(key);
                    if (it == all_triples.end()) return false;
                    g.triple_id[key] = it->second;
                }
            }
        }
    }
    return expected_clauses == clause_count;
}

static bool emit_card_group_proof(std::ofstream& out, const CardGroup& g,
                                  const std::string& label, const std::string& prefix) {
    int k = (int)g.vars.size();
    if (k == 4) {
        std::vector<std::string> ids;
        ids.reserve(4);
        for (int i = 0; i < 4; ++i) {
            for (int j = i + 1; j < 4; ++j) {
                for (int l = j + 1; l < 4; ++l) {
                    auto it = g.triple_id.find(triple_key(g.vars[i], g.vars[j], g.vars[l]));
                    if (it == g.triple_id.end()) return false;
                    ids.push_back(std::to_string(it->second));
                }
            }
        }
        out << label << " pol ";
        emit_pol_sum(out, ids);
        out << " 3 d s ;\n";
        return true;
    }
    if (k != 5) return false;

    std::vector<std::string> sublabels;
    for (int omit = 0; omit < 5; ++omit) {
        std::vector<int> sub;
        for (int i = 0; i < 5; ++i) {
            if (i != omit) sub.push_back(g.vars[i]);
        }
        std::vector<std::string> ids;
        for (int i = 0; i < 4; ++i) {
            for (int j = i + 1; j < 4; ++j) {
                for (int l = j + 1; l < 4; ++l) {
                    auto it = g.triple_id.find(triple_key(sub[i], sub[j], sub[l]));
                    if (it == g.triple_id.end()) return false;
                    ids.push_back(std::to_string(it->second));
                }
            }
        }
        std::string sublabel = label + "_" + std::to_string(omit);
        sublabels.push_back(sublabel);
        out << sublabel << " pol ";
        emit_pol_sum(out, ids);
        out << " 3 d s ;\n";
    }
    out << label << " pol ";
    emit_pol_sum(out, sublabels);
    out << " 4 d s ;\n";
    (void)prefix;
    return true;
}

static bool prove_monotone_ternary_count_unsat(const Formula& f, const char* out_path) {
    for (const auto& c : f.clauses) {
        if (c.size() != 3) return false;
        bool all_pos = true, all_neg = true;
        for (int lit : c) {
            all_pos = all_pos && lit > 0;
            all_neg = all_neg && lit < 0;
        }
        if (!all_pos && !all_neg) return false;
    }

    std::vector<CardGroup> neg_groups, pos_groups;
    int neg_thr = 0, pos_thr = 0;
    if (!build_monotone_ternary_groups(f, -1, neg_groups, neg_thr)) return false;
    if (!build_monotone_ternary_groups(f, +1, pos_groups, pos_thr)) return false;
    if (neg_thr + pos_thr <= f.nvars) return false;

    std::vector<int> seen_neg(f.nvars + 1, 0), seen_pos(f.nvars + 1, 0);
    for (const auto& g : neg_groups) {
        for (int v : g.vars) {
            if (seen_neg[v]++) return false;
        }
    }
    for (const auto& g : pos_groups) {
        for (int v : g.vars) {
            if (seen_pos[v]++) return false;
        }
    }
    for (int v = 1; v <= f.nvars; ++v) {
        if (seen_neg[v] != 1 || seen_pos[v] != 1) return false;
    }

    std::ofstream out(out_path);
    if (!out) return false;
    out << "pseudo-Boolean proof version 3.0\n";
    std::vector<std::string> labels;
    for (int i = 0; i < (int)neg_groups.size(); ++i) {
        std::string label = "@n" + std::to_string(i);
        if (!emit_card_group_proof(out, neg_groups[i], label, "n")) return false;
        labels.push_back(label);
    }
    for (int i = 0; i < (int)pos_groups.size(); ++i) {
        std::string label = "@p" + std::to_string(i);
        if (!emit_card_group_proof(out, pos_groups[i], label, "p")) return false;
        labels.push_back(label);
    }
    if (labels.size() < 2) return false;
    out << "@contr pol ";
    emit_pol_sum(out, labels);
    out << " ;\n";
    out << "output NONE ;\n";
    out << "conclusion UNSAT : @contr ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return true;
}

struct ExactCoverInstance {
    int nvars = 0;
    int primary_cols = 0;
    int ncols = 0;
    std::vector<int> row_lit;
    std::vector<std::array<int, 3>> row_cols;
    std::vector<std::vector<int>> rows_by_col;
    std::vector<std::vector<int>> rows_by_primary;
};

static bool build_signed_sgen_exact_cover(const Formula& f, ExactCoverInstance& ec) {
    int n = f.nvars;
    int count2 = 0, count5 = 0;
    for (const auto& c : f.clauses) {
        if (c.size() == 2) ++count2;
        else if (c.size() == 5) ++count5;
        else return false;
    }
    if (count2 == 0 || count5 == 0) return false;

    DSU dsu(2 * n);
    std::vector<std::array<int, 2>> binary_items;
    binary_items.reserve(count2);
    std::vector<std::array<int, 5>> positive_cols;
    positive_cols.reserve(count5);

    for (const auto& c : f.clauses) {
        if (c.size() == 2) {
            int a = -c[0];
            int b = -c[1];
            dsu.unite(lit_index(a, n), lit_index(b, n));
            binary_items.push_back({a, b});
        } else {
            std::array<int, 5> col{};
            for (int i = 0; i < 5; ++i) col[i] = c[i];
            positive_cols.push_back(col);
        }
    }

    std::vector<int> touched(2 * n, 0);
    for (auto e : binary_items) {
        touched[lit_index(e[0], n)] = 1;
        touched[lit_index(e[1], n)] = 1;
    }

    std::unordered_map<int, int> comp_to_col;
    std::vector<std::vector<int>> groups;
    groups.reserve(n / 5 + 4);
    for (int lit = -n; lit <= n; ++lit) {
        if (lit == 0) continue;
        int idx = lit_index(lit, n);
        if (!touched[idx]) continue;
        int root = dsu.find(idx);
        auto it = comp_to_col.find(root);
        if (it == comp_to_col.end()) {
            int col = (int)groups.size();
            comp_to_col.emplace(root, col);
            groups.push_back({});
            it = comp_to_col.find(root);
        }
        groups[it->second].push_back(lit);
    }

    if (groups.empty()) return false;
    for (const auto& g : groups) {
        if (g.size() != 5) return false;
    }
    int p = (int)groups.size();
    if (count5 != 2 * p) return false;

    std::unordered_map<int, std::vector<int>> lit_cols;
    lit_cols.reserve((size_t)n * 2);
    for (int gi = 0; gi < p; ++gi) {
        for (int lit : groups[gi]) lit_cols[lit].push_back(gi);
    }
    for (int ci = 0; ci < count5; ++ci) {
        int col = p + ci;
        std::array<int, 5> seen = positive_cols[ci];
        std::sort(seen.begin(), seen.end());
        for (int i = 1; i < 5; ++i) {
            if (seen[i] == seen[i - 1]) return false;
        }
        for (int lit : positive_cols[ci]) lit_cols[lit].push_back(col);
    }

    std::vector<int> var_seen(n + 1, 0);
    ec = ExactCoverInstance{};
    ec.nvars = n;
    ec.primary_cols = p;
    ec.ncols = p + count5;
    ec.rows_by_col.assign(ec.ncols, {});
    ec.rows_by_primary.assign(p, {});

    for (const auto& kv : lit_cols) {
        int lit = kv.first;
        const auto& cols = kv.second;
        if (cols.size() != 3) return false;
        int v = std::abs(lit);
        if (var_seen[v] != 0 && var_seen[v] != lit) return false;
        var_seen[v] = lit;

        std::array<int, 3> rc{cols[0], cols[1], cols[2]};
        std::sort(rc.begin(), rc.end());
        if (rc[0] >= p) return false;
        if (rc[0] == rc[1] || rc[1] == rc[2]) return false;
        int ri = (int)ec.row_lit.size();
        ec.row_lit.push_back(lit);
        ec.row_cols.push_back(rc);
        ec.rows_by_primary[rc[0]].push_back(ri);
        for (int col : rc) ec.rows_by_col[col].push_back(ri);
    }

    if ((int)ec.row_lit.size() != n) return false;
    for (int v = 1; v <= n; ++v) {
        if (var_seen[v] == 0) return false;
    }
    for (int c = 0; c < ec.ncols; ++c) {
        if (ec.rows_by_col[c].empty()) return false;
    }
    for (int c = 0; c < p; ++c) {
        if (ec.rows_by_primary[c].size() != 5) return false;
    }
    return true;
}

struct SplitMix64 {
    uint64_t x;
    explicit SplitMix64(uint64_t seed) : x(seed) {}
    uint64_t next() {
        uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
        z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
        return z ^ (z >> 31);
    }
    int randint(int n) { return (int)(next() % (uint64_t)n); }
};

static std::vector<int8_t> exact_cover_assignment(const ExactCoverInstance& ec,
                                                  const std::vector<int>& chosen_rows) {
    std::vector<int8_t> val(ec.nvars + 1, -1);
    std::vector<char> chosen(ec.row_lit.size(), 0);
    for (int r : chosen_rows) chosen[r] = 1;
    for (int r = 0; r < (int)ec.row_lit.size(); ++r) {
        int lit = ec.row_lit[r];
        int v = std::abs(lit);
        bool item_true = chosen[r] != 0;
        bool var_true = lit > 0 ? item_true : !item_true;
        val[v] = var_true ? 1 : -1;
    }
    return val;
}

static bool exact_cover_local_repair(const ExactCoverInstance& ec, Clock::time_point deadline,
                                     std::vector<int>& solution) {
    int p = ec.primary_cols;
    int ncols = ec.ncols;
    int nrows = (int)ec.row_lit.size();
    SplitMix64 rng(0x51f15e5eed1234ULL + (uint64_t)nrows * 1009ULL);

    std::vector<std::vector<int>> nonprimary_rows(ncols);
    for (int c = p; c < ncols; ++c) nonprimary_rows[c] = ec.rows_by_col[c];

    auto bad_cost = [](int cnt) -> int {
        if (cnt == 1) return 0;
        return cnt == 0 ? 1 : cnt - 1;
    };

    std::vector<int> choice(p), cnt(ncols), bad, pos_bad(ncols), selected_by_row(nrows, -1);
    std::vector<int> candidates;
    std::vector<int> best_moves;

    int restarts = 0;
    while (Clock::now() < deadline) {
        ++restarts;
        std::fill(cnt.begin(), cnt.end(), 0);
        std::fill(selected_by_row.begin(), selected_by_row.end(), -1);
        bad.clear();
        std::fill(pos_bad.begin(), pos_bad.end(), -1);

        for (int g = 0; g < p; ++g) {
            const auto& rows = ec.rows_by_primary[g];
            int r = rows[rng.randint((int)rows.size())];
            choice[g] = r;
            selected_by_row[r] = g;
            for (int c : ec.row_cols[r]) ++cnt[c];
        }
        for (int c = p; c < ncols; ++c) {
            if (cnt[c] != 1) {
                pos_bad[c] = (int)bad.size();
                bad.push_back(c);
            }
        }

        int steps_without_check = 0;
        const int max_steps = std::max(200000, 6000 * p);
        for (int step = 0; step < max_steps; ++step) {
            if (bad.empty()) {
                solution.assign(choice.begin(), choice.end());
                return true;
            }
            if (++steps_without_check >= 4096) {
                steps_without_check = 0;
                if (Clock::now() >= deadline) return false;
            }

            int bad_col = bad[rng.randint((int)bad.size())];
            candidates.clear();
            if (cnt[bad_col] == 0) {
                candidates = nonprimary_rows[bad_col];
            } else {
                for (int r : ec.rows_by_col[bad_col]) {
                    if (selected_by_row[r] >= 0) candidates.push_back(r);
                }
            }
            if (candidates.empty()) continue;

            best_moves.clear();
            int best_delta = 1 << 20;
            for (int target : candidates) {
                int g = ec.row_cols[target][0];
                int old = choice[g];
                if (old == target) continue;
                std::array<int, 4> affected{};
                int na = 0;
                for (int k = 1; k < 3; ++k) affected[na++] = ec.row_cols[old][k];
                for (int k = 1; k < 3; ++k) {
                    int c = ec.row_cols[target][k];
                    if (affected[0] != c && affected[1] != c) affected[na++] = c;
                }
                int before = 0, after = 0;
                for (int i = 0; i < na; ++i) {
                    int c = affected[i];
                    before += bad_cost(cnt[c]);
                    int nc = cnt[c];
                    if (ec.row_cols[old][1] == c || ec.row_cols[old][2] == c) --nc;
                    if (ec.row_cols[target][1] == c || ec.row_cols[target][2] == c) ++nc;
                    after += bad_cost(nc);
                }
                int delta = after - before;
                if (delta < best_delta) {
                    best_delta = delta;
                    best_moves.clear();
                    best_moves.push_back(target);
                } else if (delta == best_delta) {
                    best_moves.push_back(target);
                }
            }
            if (best_moves.empty()) continue;
            int target;
            if (best_delta >= 0 && rng.randint(100) < 12) {
                target = candidates[rng.randint((int)candidates.size())];
            } else {
                target = best_moves[rng.randint((int)best_moves.size())];
            }
            int g = ec.row_cols[target][0];
            int old = choice[g];
            if (old == target) continue;

            auto update_bad = [&](int c, int delta) {
                bool was = cnt[c] != 1;
                cnt[c] += delta;
                bool now = cnt[c] != 1;
                if (was && !now) {
                    int pbad = pos_bad[c];
                    int last = bad.back();
                    bad[pbad] = last;
                    pos_bad[last] = pbad;
                    bad.pop_back();
                    pos_bad[c] = -1;
                } else if (!was && now) {
                    pos_bad[c] = (int)bad.size();
                    bad.push_back(c);
                }
            };

            selected_by_row[old] = -1;
            selected_by_row[target] = g;
            choice[g] = target;
            update_bad(ec.row_cols[old][1], -1);
            update_bad(ec.row_cols[old][2], -1);
            update_bad(ec.row_cols[target][1], +1);
            update_bad(ec.row_cols[target][2], +1);
        }
    }
    return false;
}

struct ExactDfsSolver {
    const ExactCoverInstance& ec;
    Clock::time_point deadline;
    SplitMix64 rng;
    std::vector<uint8_t> active;
    std::vector<int> chosen;
    std::vector<int> solution;
    uint64_t nodes = 0;
    uint64_t node_limit = UINT64_MAX;
    bool randomized = false;

    ExactDfsSolver(const ExactCoverInstance& ec_, Clock::time_point deadline_, uint64_t seed)
        : ec(ec_), deadline(deadline_), rng(seed), active(ec_.ncols, 1) {}

    bool valid_row(int r) const {
        const auto& cs = ec.row_cols[r];
        return active[cs[0]] && active[cs[1]] && active[cs[2]];
    }

    bool dfs() {
        if (++nodes >= node_limit) return false;
        if ((nodes & 8191ULL) == 0 && Clock::now() >= deadline) return false;

        int best_col = -1;
        std::vector<int> best_rows;
        int best_count = 1 << 20;
        int ties = 0;
        for (int c = 0; c < ec.ncols; ++c) {
            if (!active[c]) continue;
            int cnt = 0;
            int tmp[8];
            for (int r : ec.rows_by_col[c]) {
                if (valid_row(r)) {
                    if (cnt < 8) tmp[cnt] = r;
                    ++cnt;
                }
            }
            if (cnt == 0) return false;
            bool take = false;
            if (cnt < best_count) {
                take = true;
                ties = 1;
            } else if (randomized && cnt == best_count) {
                ++ties;
                if (rng.randint(ties) == 0) take = true;
            }
            if (take) {
                best_count = cnt;
                best_col = c;
                best_rows.clear();
                for (int r : ec.rows_by_col[c]) {
                    if (valid_row(r)) best_rows.push_back(r);
                }
                if (!randomized && cnt == 1) break;
            }
        }
        if (best_col < 0) {
            solution = chosen;
            return true;
        }

        if (randomized && best_rows.size() > 1) {
            for (int i = (int)best_rows.size() - 1; i > 0; --i) {
                int j = rng.randint(i + 1);
                std::swap(best_rows[i], best_rows[j]);
            }
        } else {
            std::sort(best_rows.begin(), best_rows.end(), [&](int a, int b) {
                int sa = 0, sb = 0;
                for (int c : ec.row_cols[a]) sa += (int)ec.rows_by_col[c].size();
                for (int c : ec.row_cols[b]) sb += (int)ec.rows_by_col[c].size();
                if (sa != sb) return sa < sb;
                return ec.row_lit[a] < ec.row_lit[b];
            });
        }

        for (int r : best_rows) {
            if (!valid_row(r)) continue;
            const auto cols = ec.row_cols[r];
            active[cols[0]] = active[cols[1]] = active[cols[2]] = 0;
            chosen.push_back(r);
            if (dfs()) return true;
            chosen.pop_back();
            active[cols[0]] = active[cols[1]] = active[cols[2]] = 1;
            if ((nodes & 8191ULL) == 0 && Clock::now() >= deadline) return false;
        }
        return false;
    }
};

static bool solve_exact_cover(const Formula& f, const ExactCoverInstance& ec,
                              Clock::time_point deadline, std::vector<int8_t>& model) {
    std::vector<int> sol;
    {
        ExactDfsSolver dfs(ec, std::min(deadline, Clock::now() + std::chrono::seconds(75)),
                           0x123456789ULL);
        dfs.randomized = false;
        if (dfs.dfs()) {
            model = exact_cover_assignment(ec, dfs.solution);
            return check_model(f, model);
        }
    }
    if (Clock::now() >= deadline) return false;

    auto local_deadline = std::min(deadline, Clock::now() + std::chrono::seconds(30));
    if (exact_cover_local_repair(ec, local_deadline, sol)) {
        model = exact_cover_assignment(ec, sol);
        return check_model(f, model);
    }
    if (Clock::now() >= deadline) return false;

    uint64_t seed = 0x9e3779b97f4a7c15ULL;
    while (Clock::now() < deadline) {
        ExactDfsSolver dfs(ec, deadline, seed += 0x100000001b3ULL);
        dfs.randomized = true;
        dfs.node_limit = 2000000;
        if (dfs.dfs()) {
            model = exact_cover_assignment(ec, dfs.solution);
            return check_model(f, model);
        }
    }
    return false;
}

static bool has_clause_len(const Formula& f, int len) {
    for (const auto& c : f.clauses) {
        if ((int)c.size() == len) return true;
    }
    return false;
}

static bool stochastic_sat_search(const Formula& f, Clock::time_point deadline,
                                  std::vector<int8_t>& model) {
    int n = f.nvars;
    int m = (int)f.clauses.size();
    if (n <= 0 || n > 1500 || m > 10000) return false;

    std::vector<std::vector<int>> occ(n + 1);
    for (int ci = 0; ci < m; ++ci) {
        for (int lit : f.clauses[ci]) occ[std::abs(lit)].push_back(ci);
    }

    SplitMix64 rng(0xdecafbad1234567ULL + (uint64_t)n * 1315423911ULL + m);
    std::vector<int8_t> val(n + 1);
    std::vector<int> sat_count(m), unsat, pos_unsat(m, -1);

    auto lit_sat = [&](int lit) {
        int v = std::abs(lit);
        return (lit > 0) == (val[v] > 0);
    };
    auto add_unsat = [&](int ci) {
        pos_unsat[ci] = (int)unsat.size();
        unsat.push_back(ci);
    };
    auto remove_unsat = [&](int ci) {
        int p = pos_unsat[ci];
        if (p < 0) return;
        int last = unsat.back();
        unsat[p] = last;
        pos_unsat[last] = p;
        unsat.pop_back();
        pos_unsat[ci] = -1;
    };

    int restart = 0;
    while (Clock::now() < deadline) {
        ++restart;
        for (int v = 1; v <= n; ++v) val[v] = rng.randint(2) ? 1 : -1;
        std::fill(sat_count.begin(), sat_count.end(), 0);
        unsat.clear();
        std::fill(pos_unsat.begin(), pos_unsat.end(), -1);
        for (int ci = 0; ci < m; ++ci) {
            int cnt = 0;
            for (int lit : f.clauses[ci]) cnt += lit_sat(lit);
            sat_count[ci] = cnt;
            if (cnt == 0) add_unsat(ci);
        }

        int max_steps = std::max(200000, 4000 * n);
        for (int step = 0; step < max_steps; ++step) {
            if (unsat.empty()) {
                model = val;
                return check_model(f, model);
            }
            if ((step & 4095) == 0 && Clock::now() >= deadline) return false;

            int ci = unsat[rng.randint((int)unsat.size())];
            const auto& clause = f.clauses[ci];
            int best_score = 1 << 20;
            int best_vars[16];
            int nbest = 0;
            for (int lit : clause) {
                int v = std::abs(lit);
                int brk = 0, make = 0;
                for (int cj : occ[v]) {
                    bool cur = false;
                    for (int ll : f.clauses[cj]) {
                        if (std::abs(ll) == v) {
                            cur = (ll > 0) == (val[v] > 0);
                            break;
                        }
                    }
                    if (cur) {
                        if (sat_count[cj] == 1) ++brk;
                    } else {
                        if (sat_count[cj] == 0) ++make;
                    }
                }
                int score = brk * 4 - make * 7 + rng.randint(5);
                if (score < best_score) {
                    best_score = score;
                    nbest = 0;
                    best_vars[nbest++] = v;
                } else if (score == best_score && nbest < 16) {
                    best_vars[nbest++] = v;
                }
            }
            int v = best_vars[rng.randint(nbest)];
            int8_t old = val[v];
            val[v] = -val[v];
            for (int cj : occ[v]) {
                bool before = false, after = false;
                for (int ll : f.clauses[cj]) {
                    if (std::abs(ll) == v) {
                        before = (ll > 0) == (old > 0);
                        after = (ll > 0) == (val[v] > 0);
                        break;
                    }
                }
                int oldcnt = sat_count[cj];
                sat_count[cj] += (after ? 1 : 0) - (before ? 1 : 0);
                if (oldcnt == 0 && sat_count[cj] > 0) remove_unsat(cj);
                else if (oldcnt > 0 && sat_count[cj] == 0) add_unsat(cj);
            }
        }
    }
    return false;
}

static void write_empty_unknown_proof(const char* out_path) {
    std::ofstream out(out_path);
    if (out) {
        out << "pseudo-Boolean proof version 3.0\n";
        out << "output NONE ;\n";
        out << "end pseudo-Boolean proof ;\n";
    }
}

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");
        write_empty_unknown_proof(argv[2]);
        return 0;
    }

    auto deadline = Clock::now() + std::chrono::seconds(585);
    std::vector<int8_t> model;

    if (prove_signed_ternary_count_unsat(f, argv[2]) ||
        prove_monotone_ternary_count_unsat(f, argv[2])) {
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }

    ExactCoverInstance ec;
    bool exact_detected = build_signed_sgen_exact_cover(f, ec);
    if (exact_detected) {
        auto exact_deadline = std::min(deadline, Clock::now() + std::chrono::seconds(50));
        if (solve_exact_cover(f, ec, exact_deadline, model)) {
            print_sat_model(f, model);
            return 10;
        }
    }

    if (!exact_detected && has_clause_len(f, 5)) {
        auto sls_deadline = std::min(deadline, Clock::now() + std::chrono::seconds(90));
        if (stochastic_sat_search(f, sls_deadline, model)) {
            print_sat_model(f, model);
            return 10;
        }
    }

    write_empty_unknown_proof(argv[2]);
    std::printf("s UNKNOWN\n");
    return 0;
}
