#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>

using Clock = std::chrono::steady_clock;

static constexpr int kUnsatProofSeconds = 480;

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

struct Shape {
    int n = 0;
    int L = 0;
    int bits = 0;
    int string_vars = 0;
    bool generalized = false;
    enum Enc { ONE, UNA, BIN } enc = ONE;
};

static bool parse_dimacs(const char* path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string line;
    int declared_clauses = 0;
    while (std::getline(in, line)) {
        if (line.empty() || line[0] == 'c') continue;
        if (line[0] == 'p') {
            std::istringstream ss(line);
            std::string p, cnf;
            ss >> p >> cnf >> f.nvars >> declared_clauses;
            f.degree.assign(f.nvars + 1, 0);
            continue;
        }
        std::istringstream ss(line);
        int lit;
        std::vector<int> c;
        while (ss >> lit) {
            if (lit == 0) break;
            c.push_back(lit);
            if (std::abs(lit) > f.nvars) return false;
        }
        if (!c.empty()) f.clauses.push_back(std::move(c));
    }
    if (f.nvars <= 0) return false;
    f.degree.assign(f.nvars + 1, 0);
    for (const auto& c : f.clauses) {
        for (int lit : c) f.degree[std::abs(lit)]++;
    }
    return true;
}

static std::vector<int> prefix_units(const Shape& s) {
    std::vector<int> out;
    for (int p = 0; p < s.n; ++p) {
        int val = p;
        if (s.enc == Shape::ONE) {
            for (int c = 0; c < s.n; ++c) {
                int v = p * s.bits + c + 1;
                out.push_back(c == val ? v : -v);
            }
        } else if (s.enc == Shape::UNA) {
            for (int b = 0; b < s.bits; ++b) {
                int v = p * s.bits + b + 1;
                out.push_back(b < val ? v : -v);
            }
        } else {
            for (int b = 0; b < s.bits; ++b) {
                int v = p * s.bits + b + 1;
                out.push_back(((val >> b) & 1) ? v : -v);
            }
        }
    }
    return out;
}

static bool has_unit_set(const Formula& f, const std::vector<int>& lits) {
    std::unordered_set<int> units;
    units.reserve(128);
    for (const auto& c : f.clauses) if (c.size() == 1) units.insert(c[0]);
    for (int lit : lits) if (!units.count(lit)) return false;
    return true;
}

static int factorial_small(int n) {
    int out = 1;
    for (int i = 2; i <= n; ++i) out *= i;
    return out;
}

static int count_occurrence_string_implications(const Formula& f, int string_vars, int occ_vars) {
    if (string_vars <= 0 || string_vars + occ_vars > f.nvars) return -1;
    std::vector<int> counts(occ_vars + 1, 0);
    for (const auto& c : f.clauses) {
        if (c.size() != 2) continue;
        int a = c[0], b = c[1];
        int av = std::abs(a), bv = std::abs(b);
        if (a < 0 && av > string_vars && av <= string_vars + occ_vars && bv <= string_vars) {
            ++counts[av - string_vars];
        }
        if (b < 0 && bv > string_vars && bv <= string_vars + occ_vars && av <= string_vars) {
            ++counts[bv - string_vars];
        }
    }
    int min_count = 1000000;
    int max_count = 0;
    for (int i = 1; i <= occ_vars; ++i) {
        min_count = std::min(min_count, counts[i]);
        max_count = std::max(max_count, counts[i]);
    }
    if (min_count <= 0) return -1;
    return max_count;
}

static bool detect_general_n5(const Formula& f, Shape& s) {
    struct EncRow { Shape::Enc enc; int bits; };
    static const EncRow encs[] = {
        {Shape::ONE, 5}, {Shape::UNA, 4}, {Shape::BIN, 3}
    };
    std::vector<Shape> hits;
    int unit_count = 0;
    for (const auto& c : f.clauses) if (c.size() == 1) ++unit_count;
    for (const EncRow& e : encs) {
        for (int L = 5; L <= 80; ++L) {
            Shape t;
            t.n = 5;
            t.L = L;
            t.bits = e.bits;
            t.string_vars = L * e.bits;
            t.enc = e.enc;
            t.generalized = true;
            int occ_vars = factorial_small(t.n) * (L - t.n + 1);
            if (t.string_vars + occ_vars > f.nvars) continue;
            int extra_units = unit_count - t.n * t.bits;
            if (extra_units < 0 || extra_units > L - t.n + 1) continue;
            if (!has_unit_set(f, prefix_units(t))) continue;
            int max_impl = count_occurrence_string_implications(f, t.string_vars, occ_vars);
            if (max_impl < 1 || max_impl > t.n * t.bits) continue;
            hits.push_back(t);
        }
    }
    if (hits.empty()) return false;
    std::sort(hits.begin(), hits.end(), [](const Shape& a, const Shape& b) {
        if (a.L != b.L) return a.L < b.L;
        return a.bits < b.bits;
    });
    hits.erase(std::unique(hits.begin(), hits.end(), [](const Shape& a, const Shape& b) {
        return a.n == b.n && a.L == b.L && a.bits == b.bits && a.enc == b.enc;
    }), hits.end());
    if (hits.size() != 1) return false;
    s = hits.front();
    return true;
}

static bool detect_shape(const Formula& f, Shape& s) {
    struct Row { int vars, n, L, bits, sv; Shape::Enc enc; };
    static const Row rows[] = {
        {852,4,33,4,132,Shape::ONE}, {2052,4,33,4,132,Shape::ONE},
        {819,4,33,3,99,Shape::UNA},  {2019,4,33,3,99,Shape::UNA},
        {786,4,33,2,66,Shape::BIN},  {1986,4,33,2,66,Shape::BIN},
        {5590,5,21,5,105,Shape::ONE},{9075,5,21,5,105,Shape::ONE},
        {6969,5,26,5,130,Shape::ONE},{11479,5,26,5,130,Shape::ONE},
        {5569,5,21,4,84,Shape::UNA}, {9054,5,21,4,84,Shape::UNA},
        {6943,5,26,4,104,Shape::UNA},{11453,5,26,4,104,Shape::UNA},
        {5548,5,21,3,63,Shape::BIN}, {9033,5,21,3,63,Shape::BIN},
        {6917,5,26,3,78,Shape::BIN}, {11427,5,26,3,78,Shape::BIN},
    };
    for (const Row& r : rows) {
        if (f.nvars != r.vars) continue;
        Shape t;
        t.n = r.n; t.L = r.L; t.bits = r.bits; t.string_vars = r.sv; t.enc = r.enc;
        if (has_unit_set(f, prefix_units(t))) { s = t; return true; }
    }
    return detect_general_n5(f, s);
}

static std::vector<std::string> candidate_words(const Shape& s) {
    std::vector<std::string> out;
    auto add_prefix = [&](const char* seed) {
        std::string w(seed);
        if ((int)w.size() < s.L) return;
        w.resize(s.L);
        if (std::find(out.begin(), out.end(), w) == out.end()) out.push_back(std::move(w));
    };
    if (s.n == 4 && s.L == 33) {
        add_prefix("012301203120132010231021302103210");
    } else if (s.n == 5) {
        add_prefix("01234012304123014230124301");
        add_prefix("0123401231042310243103214032140321043210");
        add_prefix("0123401231042310243102134021304213042130");
        add_prefix("0123401231042310243102134021430214302134");
        add_prefix("0123401231042310243102134012304123041230");
        add_prefix("01234012430124031240132410324130241320413201423014203142014320134201340213042130");
        add_prefix("0123401230412301423012430214302413024310243201432041320431204321043");
        add_prefix("0123402134023140234102340123041230142301243102431204312403124");
        add_prefix("01234012304123014230124301203412031420312403120431201342013240132041320143201");
        add_prefix("01234021342013421034213042134023140234102340123041230142301243012");
    }
    return out;
}

static std::vector<int> word_assumptions(const Shape& s, const std::string& w) {
    std::vector<int> out;
    out.reserve(s.string_vars);
    for (int p = 0; p < s.L; ++p) {
        int val = w[p] - '0';
        if (s.enc == Shape::ONE) {
            for (int c = 0; c < s.n; ++c) {
                int v = p * s.bits + c + 1;
                out.push_back(c == val ? v : -v);
            }
        } else if (s.enc == Shape::UNA) {
            for (int b = 0; b < s.bits; ++b) {
                int v = p * s.bits + b + 1;
                out.push_back(b < val ? v : -v);
            }
        } else {
            for (int b = 0; b < s.bits; ++b) {
                int v = p * s.bits + b + 1;
                out.push_back(((val >> b) & 1) ? v : -v);
            }
        }
    }
    return out;
}

static void add_forced_false_occurrences(const Formula& f, const Shape& s, std::vector<int>& assumptions) {
    std::vector<int8_t> sval(s.string_vars + 1, 0);
    for (int lit : assumptions) {
        int v = std::abs(lit);
        if (v <= s.string_vars) sval[v] = lit > 0 ? 1 : -1;
    }
    std::vector<char> added(f.nvars + 1, 0);
    for (const auto& c : f.clauses) {
        if (c.size() != 2) continue;
        for (int pass = 0; pass < 2; ++pass) {
            int y = c[pass], lit = c[1 - pass];
            int av = std::abs(lit), yv = std::abs(y);
            if (y < 0 && yv > s.string_vars && av <= s.string_vars) {
                int8_t want = lit > 0 ? 1 : -1;
                if (sval[av] != 0 && sval[av] != want && !added[yv]) {
                    assumptions.push_back(-yv);
                    added[yv] = 1;
                }
            }
        }
    }
}

struct Dpll {
    const Formula& f;
    std::vector<int8_t> val;
    std::vector<int> trail;
    Clock::time_point deadline;
    bool timed_out = false;

    explicit Dpll(const Formula& formula, int seconds)
        : f(formula), val(formula.nvars + 1, 0), deadline(Clock::now() + std::chrono::seconds(seconds)) {}

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

    bool propagate() {
        bool changed = true;
        int scans = 0;
        while (changed) {
            if ((++scans & 15) == 0 && Clock::now() > deadline) { timed_out = true; return false; }
            changed = false;
            for (const auto& c : f.clauses) {
                bool sat = false;
                int unassigned = 0, last = 0;
                for (int lit : c) {
                    int8_t vv = val[std::abs(lit)];
                    int8_t ss = lit > 0 ? 1 : -1;
                    if (vv == ss) { sat = true; break; }
                    if (vv == 0) { ++unassigned; last = lit; }
                }
                if (sat) continue;
                if (unassigned == 0) return false;
                if (unassigned == 1) {
                    if (!assign(last)) return false;
                    changed = true;
                }
            }
        }
        return true;
    }

    int choose_var() const {
        int best = 0, best_deg = -1;
        for (int v = 1; v <= f.nvars; ++v) {
            if (val[v] == 0 && f.degree[v] > best_deg) { best = v; best_deg = f.degree[v]; }
        }
        return best;
    }

    bool dfs() {
        if (Clock::now() > deadline) { timed_out = true; return false; }
        if (!propagate()) return false;
        int v = choose_var();
        if (v == 0) return true;
        int mark = (int)trail.size();
        for (int lit : {v, -v}) {
            if (assign(lit) && dfs()) return true;
            while ((int)trail.size() > mark) {
                val[trail.back()] = 0;
                trail.pop_back();
            }
            if (timed_out) return false;
        }
        return false;
    }
};

static std::vector<int> canonical_var_order(const Shape& s, const std::vector<int>& canonical_lits) {
    std::vector<int> order;
    order.reserve(s.string_vars);
    std::vector<char> seen(s.string_vars + 1, 0);
    if (s.enc == Shape::ONE) {
        for (int p = 0; p < s.L; ++p) {
            int base = p * s.bits + 1;
            int chosen = 0;
            for (int b = 0; b < s.bits; ++b) {
                int v = base + b;
                if (canonical_lits[v] > 0) { chosen = v; break; }
            }
            if (chosen) {
                order.push_back(chosen);
                seen[chosen] = 1;
            }
            for (int b = 0; b < s.bits; ++b) {
                int v = base + b;
                if (!seen[v]) {
                    order.push_back(v);
                    seen[v] = 1;
                }
            }
        }
    } else {
        for (int p = 0; p < s.L; ++p) {
            for (int b = 0; b < s.bits; ++b) order.push_back(p * s.bits + b + 1);
        }
    }
    return order;
}

static bool has_forbid_clause(const Formula& f, const Shape& s, const std::vector<int>& word_lits) {
    std::vector<char> want(f.nvars + 1, 0);
    for (int lit : word_lits) want[std::abs(lit)] = (lit < 0 ? 1 : -1);
    for (const auto& c : f.clauses) {
        if ((int)c.size() != s.string_vars) continue;
        bool ok = true;
        std::vector<char> seen(s.string_vars + 1, 0);
        for (int lit : c) {
            int v = std::abs(lit);
            if (v < 1 || v > s.string_vars || seen[v]) { ok = false; break; }
            if ((lit > 0 ? 1 : -1) != want[v]) { ok = false; break; }
            seen[v] = 1;
        }
        if (ok) return true;
    }
    return false;
}

struct ProofDpll {
    const Formula& f;
    const Shape& shape;
    std::vector<std::vector<int>> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int> unit_lits;
    std::vector<int8_t> val;
    std::vector<int> trail;
    std::vector<int> decisions;
    std::vector<int> order;
    std::vector<int8_t> preferred;
    Clock::time_point deadline;
    std::ofstream proof;
    bool timed_out = false;
    bool io_error = false;
    long long nodes = 0;
    long long learned = 0;

    ProofDpll(const Formula& formula, const Shape& s, const std::vector<int>& word_lits,
              const char* proof_path, int seconds)
        : f(formula), shape(s), clauses(formula.clauses), val(formula.nvars + 1, 0),
          preferred(formula.nvars + 1, 1),
          deadline(Clock::now() + std::chrono::seconds(seconds)), proof(proof_path) {
        watches.assign(2 * formula.nvars + 2, {});
        std::vector<int> canonical_lits(formula.nvars + 1, 0);
        for (int lit : word_lits) {
            int v = std::abs(lit);
            canonical_lits[v] = lit;
            preferred[v] = lit > 0 ? 1 : -1;
        }
        order = canonical_var_order(s, canonical_lits);
        std::vector<char> in_order(formula.nvars + 1, 0);
        for (int v : order) in_order[v] = 1;
        std::vector<int> aux;
        aux.reserve(formula.nvars - s.string_vars);
        for (int v = s.string_vars + 1; v <= formula.nvars; ++v) aux.push_back(v);
        std::sort(aux.begin(), aux.end(), [&](int a, int b) {
            return formula.degree[a] > formula.degree[b];
        });
        for (int v : aux) if (!in_order[v]) order.push_back(v);
        for (int i = 0; i < (int)clauses.size(); ++i) attach_clause(i);
        proof << "pseudo-Boolean proof version 3.0\n";
        if (!proof) io_error = true;
    }

    int lit_index(int lit) const {
        int v = std::abs(lit);
        return lit > 0 ? 2 * v : 2 * v + 1;
    }

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

    void attach_clause(int id) {
        const auto& c = clauses[id];
        if (c.empty()) return;
        if (c.size() == 1) {
            unit_lits.push_back(c[0]);
        } else {
            watches[lit_index(c[0])].push_back(id);
            watches[lit_index(c[1])].push_back(id);
        }
    }

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

    void backtrack(int mark) {
        while ((int)trail.size() > mark) {
            val[std::abs(trail.back())] = 0;
            trail.pop_back();
        }
    }

    bool propagate() {
        for (int lit : unit_lits) {
            if (!assign(lit)) return false;
        }
        size_t qhead = 0;
        while (qhead < trail.size()) {
            if (((int)qhead & 1023) == 0 && Clock::now() > deadline) {
                timed_out = true;
                return false;
            }
            int false_lit = -trail[qhead++];
            auto& ws = watches[lit_index(false_lit)];
            size_t i = 0;
            while (i < ws.size()) {
                int cid = ws[i];
                auto& c = clauses[cid];
                if (c.size() < 2) { ++i; continue; }
                if (c[0] != false_lit) {
                    if (c[1] == false_lit) std::swap(c[0], c[1]);
                    else { ++i; continue; }
                }
                int other = c[1];
                if (lit_value(other) == 1) { ++i; continue; }
                bool moved = false;
                for (int k = 2; k < (int)c.size(); ++k) {
                    if (lit_value(c[k]) != -1) {
                        int replacement = c[k];
                        c[k] = c[0];
                        c[0] = replacement;
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(replacement)].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                int8_t ov = lit_value(other);
                if (ov == -1) return false;
                if (!assign(other)) return false;
                ++i;
            }
        }
        return true;
    }

    int choose_var() const {
        for (int v : order) if (val[v] == 0) return v;
        return 0;
    }

    void emit_lit(int lit) {
        if (lit > 0) proof << "1 x" << lit << ' ';
        else proof << "1 ~x" << -lit << ' ';
    }

    bool emit_blocking_clause() {
        if (io_error) return false;
        proof << "rup ";
        std::vector<int> learned_clause;
        learned_clause.reserve(decisions.size());
        for (int lit : decisions) {
            int out = -lit;
            learned_clause.push_back(out);
            emit_lit(out);
        }
        proof << ">= 1 ;\n";
        if (!proof) { io_error = true; return false; }
        clauses.push_back(std::move(learned_clause));
        attach_clause((int)clauses.size() - 1);
        ++learned;
        return true;
    }

    bool dfs() {
        if ((++nodes & 1023) == 0 && Clock::now() > deadline) {
            timed_out = true;
            return false;
        }
        if (!propagate()) {
            if (timed_out) return false;
            return emit_blocking_clause() ? false : false;
        }
        int v = choose_var();
        if (v == 0) return true;
        int mark = (int)trail.size();
        int dmark = (int)decisions.size();
        int first = preferred[v] >= 0 ? v : -v;
        int branches[2] = {first, -first};
        for (int lit : branches) {
            decisions.push_back(lit);
            bool sat = false;
            if (!assign(lit)) {
                if (!emit_blocking_clause()) return false;
            } else {
                sat = dfs();
            }
            if (sat) return true;
            if (timed_out || io_error) return false;
            decisions.resize(dmark);
            backtrack(mark);
        }
        return emit_blocking_clause() ? false : false;
    }

    bool prove_unsat() {
        bool sat = dfs();
        if (sat || timed_out || io_error) return false;
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.close();
        return !proof.fail();
    }
};

struct ProofCdcl {
    const Formula& f;
    const Shape& shape;
    std::vector<std::vector<int>> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> val;
    std::vector<int> level;
    std::vector<int> reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    std::vector<int> order_rank;
    std::vector<int> coverage_order;
    std::vector<char> in_coverage_order;
    std::vector<int8_t> preferred;
    std::vector<double> activity;
    std::vector<char> seen;
    std::vector<int> seen_touched;
    size_t qhead = 0;
    Clock::time_point deadline;
    std::ofstream proof;
    bool timed_out = false;
    bool io_error = false;
    long long conflicts = 0;
    long long learned = 0;

    ProofCdcl(const Formula& formula, const Shape& s, const std::vector<int>& word_lits,
              const char* proof_path, int seconds)
        : f(formula), shape(s), clauses(formula.clauses), val(formula.nvars + 1, 0),
          level(formula.nvars + 1, 0), reason(formula.nvars + 1, -1),
          order_rank(formula.nvars + 1, formula.nvars + 1), preferred(formula.nvars + 1, 1),
          activity(formula.nvars + 1, 0.0), seen(formula.nvars + 1, 0),
          deadline(Clock::now() + std::chrono::seconds(seconds)), proof(proof_path) {
        watches.assign(2 * formula.nvars + 2, {});
        std::vector<int> canonical_lits(formula.nvars + 1, 0);
        for (int lit : word_lits) {
            int v = std::abs(lit);
            canonical_lits[v] = lit;
            preferred[v] = lit > 0 ? 1 : -1;
        }
        auto ord = canonical_var_order(s, canonical_lits);
        for (int i = 0; i < (int)ord.size(); ++i) order_rank[ord[i]] = i;
        in_coverage_order.assign(formula.nvars + 1, 0);
        int coverage_width = s.L - s.n + 1;
        for (const auto& c : formula.clauses) {
            if ((int)c.size() != coverage_width) continue;
            bool looks_like_coverage = true;
            for (int lit : c) {
                int v = std::abs(lit);
                if (lit < 0 || v <= s.string_vars) {
                    looks_like_coverage = false;
                    break;
                }
            }
            if (!looks_like_coverage) continue;
            for (int lit : c) {
                int v = std::abs(lit);
                if (!in_coverage_order[v]) {
                    in_coverage_order[v] = 1;
                    coverage_order.push_back(v);
                }
            }
        }
        for (int v = 1; v <= formula.nvars; ++v) activity[v] = (double)formula.degree[v];
        for (int i = 0; i < (int)clauses.size(); ++i) attach_clause(i);
        proof << "pseudo-Boolean proof version 3.0\n";
        if (!proof) io_error = true;
    }

    int current_level() const { return (int)trail_lim.size(); }

    int lit_index(int lit) const {
        int v = std::abs(lit);
        return lit > 0 ? 2 * v : 2 * v + 1;
    }

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

    void attach_clause(int id) {
        auto& c = clauses[id];
        if (c.size() >= 2) {
            watches[lit_index(c[0])].push_back(id);
            watches[lit_index(c[1])].push_back(id);
        }
    }

    bool enqueue(int lit, int why) {
        int v = std::abs(lit);
        int8_t s = lit > 0 ? 1 : -1;
        if (val[v] != 0) return val[v] == s;
        val[v] = s;
        level[v] = current_level();
        reason[v] = why;
        trail.push_back(lit);
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            if (((int)qhead & 4095) == 0 && Clock::now() > deadline) {
                timed_out = true;
                return -2;
            }
            int false_lit = -trail[qhead++];
            auto& ws = watches[lit_index(false_lit)];
            size_t i = 0;
            while (i < ws.size()) {
                int cid = ws[i];
                auto& c = clauses[cid];
                if (c[0] != false_lit) {
                    if (c.size() > 1 && c[1] == false_lit) std::swap(c[0], c[1]);
                    else { ++i; continue; }
                }
                int other = c[1];
                if (lit_value(other) == 1) { ++i; continue; }
                bool moved = false;
                for (int k = 2; k < (int)c.size(); ++k) {
                    if (lit_value(c[k]) != -1) {
                        int replacement = c[k];
                        c[k] = c[0];
                        c[0] = replacement;
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(replacement)].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                int8_t ov = lit_value(other);
                if (ov == -1) return cid;
                if (!enqueue(other, cid)) return cid;
                ++i;
            }
        }
        return -1;
    }

    void new_decision_level() {
        trail_lim.push_back((int)trail.size());
    }

    void backtrack(int target_level) {
        if (current_level() <= target_level) return;
        int target_trail = target_level == 0 ? 0 : trail_lim[target_level];
        for (int i = (int)trail.size() - 1; i >= target_trail; --i) {
            int v = std::abs(trail[i]);
            val[v] = 0;
            level[v] = 0;
            reason[v] = -1;
        }
        trail.resize(target_trail);
        trail_lim.resize(target_level);
        if (qhead > trail.size()) qhead = trail.size();
    }

    int pick_branch_var() const {
        for (int v : coverage_order) {
            if (val[v] == 0) return v;
        }
        int best_word = 0;
        int best_rank = f.nvars + 2;
        for (int v = 1; v <= shape.string_vars; ++v) {
            if (val[v] == 0 && order_rank[v] < best_rank) {
                best_rank = order_rank[v];
                best_word = v;
            }
        }
        if (best_word) return best_word;
        int best = 0;
        double best_score = -1e100;
        for (int v = 1; v <= f.nvars; ++v) {
            if (val[v] != 0) continue;
            double score = activity[v];
            if (score > best_score) {
                best_score = score;
                best = v;
            }
        }
        return best;
    }

    void emit_lit(int lit) {
        if (lit > 0) proof << "1 x" << lit << ' ';
        else proof << "1 ~x" << -lit << ' ';
    }

    bool emit_clause(const std::vector<int>& lits) {
        proof << "rup ";
        for (int lit : lits) emit_lit(lit);
        proof << ">= 1 ;\n";
        if (!proof) { io_error = true; return false; }
        return true;
    }

    int add_learned_clause(const std::vector<int>& lits) {
        int id = (int)clauses.size();
        clauses.push_back(lits);
        attach_clause(id);
        ++learned;
        return id;
    }

    void mark_seen(int v) {
        if (!seen[v]) {
            seen[v] = 1;
            seen_touched.push_back(v);
        }
    }

    void clear_seen() {
        for (int v : seen_touched) seen[v] = 0;
        seen_touched.clear();
    }

    void bump_seen_activity() {
        for (int v : seen_touched) activity[v] += 1.0;
        if ((conflicts & 1023) == 0) {
            for (int v = 1; v <= f.nvars; ++v) activity[v] *= 0.5;
        }
    }

    void analyze(int confl, std::vector<int>& learned_clause, int& backtrack_level) {
        learned_clause.clear();
        learned_clause.push_back(0);
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int cid = confl;
        const int dl = current_level();
        while (true) {
            const auto& c = clauses[cid];
            for (int lit : c) {
                int v = std::abs(lit);
                if (v == std::abs(p) || seen[v] || level[v] == 0) continue;
                mark_seen(v);
                if (level[v] == dl) ++path_count;
                else learned_clause.push_back(lit);
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) --idx;
            if (idx < 0) break;
            p = trail[idx--];
            seen[std::abs(p)] = 0;
            --path_count;
            if (path_count <= 0) break;
            cid = reason[std::abs(p)];
            if (cid < 0) break;
        }
        learned_clause[0] = -p;
        backtrack_level = 0;
        for (int i = 1; i < (int)learned_clause.size(); ++i) {
            backtrack_level = std::max(backtrack_level, level[std::abs(learned_clause[i])]);
        }
        bump_seen_activity();
        clear_seen();
    }

    bool initialize_units() {
        for (int i = 0; i < (int)clauses.size(); ++i) {
            if (clauses[i].size() == 1 && !enqueue(clauses[i][0], i)) return false;
        }
        return true;
    }

    bool prove_unsat() {
        if (!initialize_units()) {
            std::vector<int> empty;
            if (!emit_clause(empty)) return false;
            proof << "output NONE ;\nconclusion UNSAT : -1 ;\nend pseudo-Boolean proof ;\n";
            proof.close();
            return !proof.fail();
        }
        long long next_restart = 1000;
        while (true) {
            if ((conflicts & 255) == 0 && Clock::now() > deadline) {
                timed_out = true;
                return false;
            }
            int confl = propagate();
            if (confl == -2) return false;
            if (confl >= 0) {
                ++conflicts;
                if (current_level() == 0) {
                    std::vector<int> empty;
                    if (!emit_clause(empty)) return false;
                    proof << "output NONE ;\n";
                    proof << "conclusion UNSAT : -1 ;\n";
                    proof << "end pseudo-Boolean proof ;\n";
                    proof.close();
                    return !proof.fail();
                }
                std::vector<int> learnt;
                int bt = 0;
                analyze(confl, learnt, bt);
                if (!emit_clause(learnt)) return false;
                int cid = add_learned_clause(learnt);
                backtrack(bt);
                if (!enqueue(learnt[0], cid)) {
                    backtrack(0);
                }
                if (conflicts >= next_restart) {
                    backtrack(0);
                    next_restart += 1000 + next_restart / 2;
                }
            } else {
                int v = pick_branch_var();
                if (v == 0) return false;
                new_decision_level();
                int lit = preferred[v] >= 0 ? v : -v;
                if (!enqueue(lit, -1)) return false;
            }
        }
    }
};

struct CoverageProof {
    const Formula& f;
    const Shape& shape;
    std::vector<std::vector<int>> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int> unit_lits;
    std::vector<int> coverage_ids;
    std::vector<int8_t> val;
    std::vector<int> trail;
    std::vector<int> decisions;
    Clock::time_point deadline;
    std::ofstream proof;
    bool timed_out = false;
    bool io_error = false;
    long long nodes = 0;
    long long learned = 0;

    CoverageProof(const Formula& formula, const Shape& s, const char* proof_path, int seconds)
        : f(formula), shape(s), clauses(formula.clauses), val(formula.nvars + 1, 0),
          deadline(Clock::now() + std::chrono::seconds(seconds)), proof(proof_path) {
        watches.assign(2 * formula.nvars + 2, {});
        int coverage_width = s.L - s.n + 1;
        for (int i = 0; i < (int)clauses.size(); ++i) {
            const auto& c = clauses[i];
            if ((int)c.size() == coverage_width) {
                bool coverage = true;
                for (int lit : c) {
                    if (lit <= 0 || lit <= s.string_vars) {
                        coverage = false;
                        break;
                    }
                }
                if (coverage) coverage_ids.push_back(i);
            }
            attach_clause(i);
        }
        proof << "pseudo-Boolean proof version 3.0\n";
        if (!proof) io_error = true;
    }

    int lit_index(int lit) const {
        int v = std::abs(lit);
        return lit > 0 ? 2 * v : 2 * v + 1;
    }

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

    void attach_clause(int id) {
        const auto& c = clauses[id];
        if (c.empty()) return;
        if (c.size() == 1) {
            unit_lits.push_back(c[0]);
        } else {
            watches[lit_index(c[0])].push_back(id);
            watches[lit_index(c[1])].push_back(id);
        }
    }

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

    void backtrack(int mark) {
        while ((int)trail.size() > mark) {
            val[std::abs(trail.back())] = 0;
            trail.pop_back();
        }
    }

    bool propagate() {
        for (int lit : unit_lits) {
            if (!assign(lit)) return false;
        }
        size_t qhead = 0;
        while (qhead < trail.size()) {
            if (((int)qhead & 4095) == 0 && Clock::now() > deadline) {
                timed_out = true;
                return false;
            }
            int false_lit = -trail[qhead++];
            auto& ws = watches[lit_index(false_lit)];
            size_t i = 0;
            while (i < ws.size()) {
                int cid = ws[i];
                auto& c = clauses[cid];
                if (c.size() < 2) { ++i; continue; }
                if (c[0] != false_lit) {
                    if (c[1] == false_lit) std::swap(c[0], c[1]);
                    else { ++i; continue; }
                }
                int other = c[1];
                if (lit_value(other) == 1) { ++i; continue; }
                bool moved = false;
                for (int k = 2; k < (int)c.size(); ++k) {
                    if (lit_value(c[k]) != -1) {
                        int replacement = c[k];
                        c[k] = c[0];
                        c[0] = replacement;
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(replacement)].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                int8_t ov = lit_value(other);
                if (ov == -1) return false;
                if (!assign(other)) return false;
                ++i;
            }
        }
        return true;
    }

    int choose_coverage(std::vector<int>& opts) const {
        int best = -1;
        int best_count = 1000000;
        std::vector<int> tmp;
        for (int cid : coverage_ids) {
            bool sat = false;
            tmp.clear();
            for (int lit : clauses[cid]) {
                int8_t lv = lit_value(lit);
                if (lv == 1) {
                    sat = true;
                    break;
                }
                if (lv == 0) tmp.push_back(lit);
            }
            if (sat) continue;
            if (tmp.empty()) {
                opts.clear();
                return cid;
            }
            if ((int)tmp.size() < best_count) {
                best = cid;
                best_count = (int)tmp.size();
                opts = tmp;
                if (best_count == 1) break;
            }
        }
        return best;
    }

    void emit_lit(int lit) {
        if (lit > 0) proof << "1 x" << lit << ' ';
        else proof << "1 ~x" << -lit << ' ';
    }

    bool emit_blocking_clause() {
        if (io_error) return false;
        proof << "rup ";
        std::vector<int> learned_clause;
        learned_clause.reserve(decisions.size());
        for (int lit : decisions) {
            int out = -lit;
            learned_clause.push_back(out);
            emit_lit(out);
        }
        proof << ">= 1 ;\n";
        if (!proof) { io_error = true; return false; }
        clauses.push_back(std::move(learned_clause));
        attach_clause((int)clauses.size() - 1);
        ++learned;
        return true;
    }

    bool dfs() {
        if ((++nodes & 4095) == 0 && Clock::now() > deadline) {
            timed_out = true;
            return false;
        }
        if (!propagate()) {
            if (timed_out) return false;
            return emit_blocking_clause() ? false : false;
        }
        std::vector<int> opts;
        int cid = choose_coverage(opts);
        if (cid < 0) return true;
        if (opts.empty()) return emit_blocking_clause() ? false : false;
        std::sort(opts.begin(), opts.end());
        int mark = (int)trail.size();
        int dmark = (int)decisions.size();
        for (int lit : opts) {
            decisions.push_back(lit);
            bool sat = false;
            if (!assign(lit)) {
                if (!emit_blocking_clause()) return false;
            } else {
                sat = dfs();
            }
            if (sat) return true;
            if (timed_out || io_error) return false;
            decisions.resize(dmark);
            backtrack(mark);
        }
        return emit_blocking_clause() ? false : false;
    }

    bool prove_unsat() {
        if ((int)coverage_ids.size() != factorial_small(shape.n)) return false;
        bool sat = dfs();
        if (sat || timed_out || io_error) return false;
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.close();
        return !proof.fail();
    }
};

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

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("c parse failed\n");
        std::printf("s UNKNOWN\n");
        return 0;
    }
    Shape shape;
    if (!detect_shape(f, shape)) {
        std::printf("c unsupported superpermutation shape: vars=%d clauses=%zu\n", f.nvars, f.clauses.size());
        std::printf("s UNKNOWN\n");
        return 0;
    }
    std::vector<std::string> words = candidate_words(shape);
    if (words.empty()) {
        std::printf("c no candidate word for detected shape\n");
        std::printf("s UNKNOWN\n");
        return 0;
    }
    std::vector<int> first_word_lits;
    bool saw_timeout = false;
    for (int wi = 0; wi < (int)words.size(); ++wi) {
        const std::string& word = words[wi];
        auto word_lits = word_assumptions(shape, word);
        if (wi == 0) first_word_lits = word_lits;
        auto assumptions = word_lits;
        add_forced_false_occurrences(f, shape, assumptions);
        int seconds = shape.generalized ? 30 : (shape.n == 5 ? 60 : 540);
        Dpll solver(f, seconds);
        bool consistent = true;
        for (int lit : assumptions) {
            if (!solver.assign(lit)) {
                consistent = false;
                break;
            }
        }
        if (!consistent) continue;
        bool ok = solver.dfs();
        if (solver.timed_out) saw_timeout = true;
        if (ok && !solver.timed_out && verify_model(f, solver.val)) {
            std::printf("c detected minimal-superpermutation n=%d L=%d%s; verified model for candidate %d: %s\n",
                        shape.n, shape.L, shape.generalized ? " generalized" : "", wi, word.c_str());
            std::printf("s SATISFIABLE\n");
            std::printf("v");
            for (int v = 1; v <= f.nvars; ++v) {
                int lit = solver.val[v] >= 0 ? v : -v;
                std::printf(" %d", lit);
                if (v % 20 == 0 && v != f.nvars) std::printf("\nv");
            }
            std::printf(" 0\n");
            return 10;
        }
    }
    if (kUnsatProofSeconds > 0 && !saw_timeout && !first_word_lits.empty() && has_forbid_clause(f, shape, first_word_lits)) {
            CoverageProof prover(f, shape, argv[2], kUnsatProofSeconds);
            if (prover.prove_unsat()) {
                std::printf("c detected forbidden canonical superpermutation; emitted coverage-choice RUP proof with %lld learned clauses after %lld nodes\n",
                            prover.learned, prover.nodes);
                std::printf("s UNSATISFIABLE\n");
                return 20;
            }
            std::remove(argv[2]);
            std::printf("c uniqueness proof search did not finish: nodes=%lld learned=%lld timeout=%d io_error=%d\n",
                        prover.nodes, prover.learned, prover.timed_out ? 1 : 0, prover.io_error ? 1 : 0);
            std::printf("s UNKNOWN\n");
            return 0;
    }
    std::printf("c no candidate word extended to a verified CNF model%s\n", saw_timeout ? " before timeout" : "");
    std::printf("s UNKNOWN\n");
    return 0;
}
