#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <numeric>
#include <queue>
#include <random>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct Formula {
    int vars = 0;
    int clauses_declared = 0;
    vector<Clause> clauses;
};

struct Detected {
    int Q = 0;
    int L = 0;
    int base = 0;
    vector<array<int, 2>> tr;
};

static bool read_cnf(const string& path, Formula& f) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string cnf;
            in >> cnf >> f.vars >> f.clauses_declared;
            if (cnf != "cnf" || f.vars <= 0 || f.clauses_declared < 0) return false;
        } else {
            Clause c;
            int lit = stoi(tok);
            while (lit != 0) {
                c.lits.push_back(lit);
                if (!(in >> lit)) return false;
            }
            f.clauses.push_back(std::move(c));
        }
    }
    return f.vars > 0;
}

static bool detect_automaton(const Formula& f, Detected& d) {
    int pospos = 0;
    for (const auto& c : f.clauses) {
        if (c.lits.size() == 2 && c.lits[0] > 0 && c.lits[1] > 0) pospos++;
    }
    if (pospos <= 0) return false;
    d.L = pospos;
    if (f.vars % d.L != 0) return false;
    d.Q = f.vars / d.L - 2;
    if (d.Q <= 1 || d.L <= 0) return false;
    d.base = d.Q * d.L;
    if (d.base + 2 * d.L != f.vars) return false;
    d.tr.assign(d.Q, {-1, -1});

    for (const auto& c : f.clauses) {
        if (c.lits.size() != 3) continue;
        int src_var = 0, dst_var = 0, sel_var = 0;
        for (int lit : c.lits) {
            int v = abs(lit);
            if (lit < 0 && v >= 1 && v <= d.base) src_var = v;
            else if (lit > 0 && v >= 1 && v <= d.base) dst_var = v;
            else if (lit < 0 && v > d.base && v <= f.vars) sel_var = v;
        }
        if (!src_var || !dst_var || !sel_var) continue;
        int src_layer = (src_var - 1) / d.Q;
        int dst_layer = (dst_var - 1) / d.Q;
        int src = (src_var - 1) % d.Q;
        int dst = (dst_var - 1) % d.Q;
        int sel_index = sel_var - d.base - 1;
        int sel_layer = sel_index / 2;
        int letter = sel_index % 2;
        if (src_layer + 1 != dst_layer || sel_layer != dst_layer) return false;
        if (letter < 0 || letter > 1) return false;
        if (d.tr[src][letter] == -1) {
            d.tr[src][letter] = dst;
        } else if (d.tr[src][letter] != dst) {
            return false;
        }
    }
    for (int q = 0; q < d.Q; q++) {
        for (int a = 0; a < 2; a++) {
            if (d.tr[q][a] < 0) return false;
        }
    }

    vector<array<int, 2>> init_ok(d.Q, {0, 0});
    for (const auto& c : f.clauses) {
        if (c.lits.size() != 2) continue;
        int dst = 0, sel = 0;
        for (int lit : c.lits) {
            int v = abs(lit);
            if (lit > 0 && v >= 1 && v <= d.Q) dst = v - 1;
            if (lit < 0 && v == d.base + 1) sel = 1;
            if (lit < 0 && v == d.base + 2) sel = 2;
        }
        if (dst >= 0 && sel) {
            for (int q = 0; q < d.Q; q++) {
                if (d.tr[q][sel - 1] == dst) init_ok[q][sel - 1] = 1;
            }
        }
    }
    for (int q = 0; q < d.Q; q++) {
        if (!init_ok[q][0] || !init_ok[q][1]) return false;
    }
    return true;
}

struct BitSubset {
    vector<uint64_t> w;
    int count = 0;
};

static BitSubset full_subset(int Q) {
    BitSubset s;
    int W = (Q + 63) / 64;
    s.w.assign(W, ~0ULL);
    int extra = W * 64 - Q;
    if (extra) s.w.back() >>= extra;
    s.count = Q;
    return s;
}

static string key_of(const BitSubset& s) {
    return string(reinterpret_cast<const char*>(s.w.data()), s.w.size() * sizeof(uint64_t));
}

static bool singleton(const BitSubset& s) {
    return s.count == 1;
}

static BitSubset step_subset(const BitSubset& s, const Detected& d, int letter) {
    BitSubset out;
    out.w.assign(s.w.size(), 0);
    for (int wi = 0; wi < (int)s.w.size(); wi++) {
        uint64_t x = s.w[wi];
        while (x) {
            uint64_t bit = x & -x;
            int b = __builtin_ctzll(x);
            int q = wi * 64 + b;
            int r = d.tr[q][letter];
            out.w[r >> 6] |= 1ULL << (r & 63);
            x -= bit;
        }
    }
    int c = 0;
    for (uint64_t x : out.w) c += __builtin_popcountll(x);
    out.count = c;
    return out;
}

static int image_size_for_word(const Detected& d, const vector<int>& word) {
    BitSubset s = full_subset(d.Q);
    for (int a : word) s = step_subset(s, d, a);
    return s.count;
}

static bool is_single_cycle_perm(const Detected& d, int letter, vector<int>& order) {
    vector<int> indeg(d.Q, 0);
    for (int q = 0; q < d.Q; q++) {
        int r = d.tr[q][letter];
        if (r < 0 || r >= d.Q) return false;
        indeg[r]++;
    }
    if (!all_of(indeg.begin(), indeg.end(), [](int x) { return x == 1; })) return false;
    order.clear();
    vector<int> seen(d.Q, 0);
    int cur = 0;
    for (int i = 0; i < d.Q; i++) {
        if (seen[cur]) return false;
        seen[cur] = 1;
        order.push_back(cur);
        cur = d.tr[cur][letter];
    }
    return cur == 0;
}

static bool cerny_word(const Detected& d, vector<int>& word) {
    for (int cyc = 0; cyc < 2; cyc++) {
        int comp = 1 - cyc;
        vector<int> order;
        if (!is_single_cycle_perm(d, cyc, order)) continue;
        int changed = -1;
        bool ok = true;
        for (int i = 0; i < d.Q; i++) {
            int q = order[i];
            int expected_same = q;
            int expected_next = order[(i + 1) % d.Q];
            if (d.tr[q][comp] == expected_same) continue;
            if (d.tr[q][comp] == expected_next && changed == -1) {
                changed = q;
                continue;
            }
            ok = false;
            break;
        }
        if (!ok || changed == -1) continue;
        int need = (d.Q - 1) * (d.Q - 1);
        if (need > d.L) return false;
        word.clear();
        for (int rep = 0; rep < d.Q - 2; rep++) {
            word.push_back(comp);
            for (int i = 0; i < d.Q - 1; i++) word.push_back(cyc);
        }
        word.push_back(comp);
        while ((int)word.size() < d.L) word.push_back(cyc);
        return image_size_for_word(d, word) == 1;
    }
    return false;
}

static bool is_cerny_like(const Detected& d, int& shortest_len) {
    for (int cyc = 0; cyc < 2; cyc++) {
        int comp = 1 - cyc;
        vector<int> order;
        if (!is_single_cycle_perm(d, cyc, order)) continue;
        int changed = -1;
        bool ok = true;
        for (int i = 0; i < d.Q; i++) {
            int q = order[i];
            int expected_next = order[(i + 1) % d.Q];
            if (d.tr[q][comp] == q) continue;
            if (d.tr[q][comp] == expected_next && changed == -1) {
                changed = q;
                continue;
            }
            ok = false;
            break;
        }
        if (ok && changed != -1) {
            shortest_len = (d.Q - 1) * (d.Q - 1);
            return true;
        }
    }
    return false;
}

struct CernyProof {
    const Detected* d = nullptr;
    ofstream* out = nullptr;
    vector<vector<uint64_t>> good;
    unordered_set<string> done;
    int64_t next_id = 0;
    int max_q = 0;

    uint64_t step(uint64_t s, int letter) const {
        uint64_t out_mask = 0;
        uint64_t x = s;
        while (x) {
            uint64_t bit = x & -x;
            int q = __builtin_ctzll(x);
            out_mask |= 1ULL << (*d).tr[q][letter];
            x -= bit;
        }
        return out_mask;
    }

    uint64_t preimage(uint64_t target, int letter) const {
        uint64_t pre = 0;
        for (int q = 0; q < d->Q; q++) {
            if ((target >> d->tr[q][letter]) & 1ULL) pre |= 1ULL << q;
        }
        return pre;
    }

    static bool subset_of(uint64_t a, uint64_t b) {
        return (a & ~b) == 0;
    }

    vector<uint64_t> maximal_sets(vector<uint64_t> sets) const {
        sort(sets.begin(), sets.end(), [](uint64_t a, uint64_t b) {
            int ca = __builtin_popcountll(a), cb = __builtin_popcountll(b);
            if (ca != cb) return ca > cb;
            return a < b;
        });
        sets.erase(unique(sets.begin(), sets.end()), sets.end());
        vector<uint64_t> kept;
        for (uint64_t s : sets) {
            bool covered = false;
            for (uint64_t k : kept) {
                if (subset_of(s, k)) {
                    covered = true;
                    break;
                }
            }
            if (!covered) kept.push_back(s);
        }
        return kept;
    }

    bool build_good_sets() {
        if (d->Q <= 1 || d->Q > max_q || d->L <= 0) return false;
        good.assign(d->L + 1, {});
        vector<uint64_t> cur;
        for (int q = 0; q < d->Q; q++) cur.push_back(1ULL << q);
        good[d->L] = cur;
        for (int t = d->L - 1; t >= 1; t--) {
            vector<uint64_t> cand;
            cand.reserve(cur.size() * 2);
            for (uint64_t g : cur) {
                cand.push_back(preimage(g, 0));
                cand.push_back(preimage(g, 1));
            }
            cur = maximal_sets(std::move(cand));
            if ((int)cur.size() > max(4096, d->Q * 8)) return false;
            good[t] = cur;
        }
        return true;
    }

    bool is_good(int layer, uint64_t s) const {
        if (s == 0) return true;
        for (uint64_t g : good[layer]) {
            if (subset_of(s, g)) return true;
        }
        return false;
    }

    uint64_t minimize_bad(int layer, uint64_t s) const {
        bool changed = true;
        while (changed) {
            changed = false;
            for (int q = 0; q < d->Q; q++) {
                uint64_t bit = 1ULL << q;
                if ((s & bit) == 0) continue;
                uint64_t t = s & ~bit;
                if (t != 0 && !is_good(layer, t)) {
                    s = t;
                    changed = true;
                    break;
                }
            }
        }
        return s;
    }

    int state_var(int layer, int q) const {
        return (layer - 1) * d->Q + q + 1;
    }

    int sel_var(int layer, int letter) const {
        return d->base + 2 * (layer - 1) + letter + 1;
    }

    void write_lit(int lit) {
        if (lit > 0) {
            (*out) << "1 x" << lit << ' ';
        } else {
            (*out) << "1 ~x" << -lit << ' ';
        }
    }

    void write_bad_clause(int layer, uint64_t s, int guard_lit = 0) {
        (*out) << "rup ";
        for (int q = 0; q < d->Q; q++) {
            if ((s >> q) & 1ULL) write_lit(-state_var(layer, q));
        }
        if (guard_lit) write_lit(guard_lit);
        (*out) << ">= 1 ;\n";
        next_id++;
    }

    void write_unit_or_empty(int lit) {
        if (lit == 0) {
            (*out) << "rup >= 1 ;\n";
        } else {
            (*out) << "rup ";
            write_lit(lit);
            (*out) << ">= 1 ;\n";
        }
        next_id++;
    }

    string key(int layer, uint64_t s) const {
        string k;
        k.resize(sizeof(int) + sizeof(uint64_t));
        memcpy(k.data(), &layer, sizeof(int));
        memcpy(k.data() + sizeof(int), &s, sizeof(uint64_t));
        return k;
    }

    bool prove_bad(int layer, uint64_t s) {
        if (layer < 1 || layer > d->L) return false;
        if (is_good(layer, s)) return false;
        s = minimize_bad(layer, s);
        if (is_good(layer, s)) return false;
        if (layer == d->L) {
            return __builtin_popcountll(s) >= 2;
        }

        string k = key(layer, s);
        if (done.find(k) != done.end()) return true;

        for (int letter = 0; letter < 2; letter++) {
            uint64_t img = step(s, letter);
            if (is_good(layer + 1, img)) return false;
            uint64_t child = minimize_bad(layer + 1, img);
            if (!prove_bad(layer + 1, child)) return false;
        }

        write_bad_clause(layer, s, -sel_var(layer + 1, 0));
        write_bad_clause(layer, s, -sel_var(layer + 1, 1));
        write_bad_clause(layer, s);
        done.insert(std::move(k));
        return true;
    }
};

static bool write_cerny_unsat_proof(const Formula& f, const Detected& d, const string& path) {
    if (d.Q > 25) return false;
    int shortest = 0;
    if (!is_cerny_like(d, shortest) || shortest <= d.L) return false;

    ofstream out(path);
    if (!out) return false;
    out << "pseudo-Boolean proof version 3.0\n";

    CernyProof proof;
    proof.d = &d;
    proof.out = &out;
    proof.next_id = f.clauses_declared;
    proof.max_q = 25;
    if (!proof.build_good_sets()) return false;

    uint64_t full = d.Q == 64 ? ~0ULL : ((1ULL << d.Q) - 1ULL);
    for (int letter = 0; letter < 2; letter++) {
        uint64_t img = proof.step(full, letter);
        if (proof.is_good(1, img)) return false;
        uint64_t bad = proof.minimize_bad(1, img);
        if (!proof.prove_bad(1, bad)) return false;
        proof.write_unit_or_empty(-proof.sel_var(1, letter));
    }
    proof.write_unit_or_empty(0);
    out << "output NONE ;\n";
    out << "conclusion UNSAT : " << proof.next_id << " ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return (bool)out;
}

static vector<vector<int>> pair_distances(const Detected& d);

struct SmallMask {
    array<uint64_t, 3> w{0, 0, 0};

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

struct SmallMaskHash {
    size_t operator()(const SmallMask& m) const {
        uint64_t h = 0x9e3779b97f4a7c15ULL;
        for (uint64_t x : m.w) {
            h ^= x + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
        }
        return (size_t)h;
    }
};

struct SubKey {
    int layer = 0;
    SmallMask mask;

    bool operator==(const SubKey& other) const {
        return layer == other.layer && mask == other.mask;
    }
};

struct SubKeyHash {
    size_t operator()(const SubKey& k) const {
        return SmallMaskHash{}(k.mask) ^ ((uint64_t)k.layer * 0x517cc1b727220a95ULL);
    }
};

struct PairKey {
    int layer = 0;
    int a = 0;
    int b = 0;

    bool operator==(const PairKey& other) const {
        return layer == other.layer && a == other.a && b == other.b;
    }
};

struct PairKeyHash {
    size_t operator()(const PairKey& k) const {
        uint64_t h = (uint64_t)k.layer;
        h = h * 1000003ULL + (uint64_t)k.a;
        h = h * 1000003ULL + (uint64_t)k.b;
        return (size_t)h;
    }
};

struct RandomProof {
    const Formula* f = nullptr;
    const Detected* d = nullptr;
    ofstream* out = nullptr;
    int W = 0;
    int64_t next_id = 0;
    vector<vector<int>> pd;
    vector<int> selector_id;
    vector<vector<int>> init_id;
    vector<vector<int>> final_id;
    vector<int> prop_id;
    vector<vector<SmallMask>> pair_too_far;
    unordered_map<PairKey, int64_t, PairKeyHash> pair_done;
    unordered_map<SubKey, int64_t, SubKeyHash> sub_done;

    struct Ref {
        int64_t id = 0;
        SmallMask mask;
    };

    int prop_index(int layer, int q, int letter) const {
        return ((layer - 1) * d->Q + q) * 2 + letter;
    }

    int state_var(int layer, int q) const {
        return (layer - 1) * d->Q + q + 1;
    }

    int sel_var(int layer, int letter) const {
        return d->base + 2 * (layer - 1) + letter + 1;
    }

    SmallMask empty_mask() const {
        return SmallMask{};
    }

    SmallMask singleton_mask(int q) const {
        SmallMask m;
        m.w[q >> 6] |= 1ULL << (q & 63);
        return m;
    }

    SmallMask pair_mask(int a, int b) const {
        SmallMask m;
        m.w[a >> 6] |= 1ULL << (a & 63);
        m.w[b >> 6] |= 1ULL << (b & 63);
        return m;
    }

    SmallMask full_mask() const {
        SmallMask m;
        for (int q = 0; q < d->Q; q++) m.w[q >> 6] |= 1ULL << (q & 63);
        return m;
    }

    bool test_bit(const SmallMask& m, int q) const {
        return (m.w[q >> 6] >> (q & 63)) & 1ULL;
    }

    void set_bit(SmallMask& m, int q) const {
        m.w[q >> 6] |= 1ULL << (q & 63);
    }

    int popcount(const SmallMask& m) const {
        int c = 0;
        for (int i = 0; i < W; i++) c += __builtin_popcountll(m.w[i]);
        return c;
    }

    SmallMask mask_and(const SmallMask& a, const SmallMask& b) const {
        SmallMask m;
        for (int i = 0; i < W; i++) m.w[i] = a.w[i] & b.w[i];
        return m;
    }

    bool any_bits(const SmallMask& m) const {
        for (int i = 0; i < W; i++) {
            if (m.w[i]) return true;
        }
        return false;
    }

    vector<int> bits(const SmallMask& m) const {
        vector<int> out_bits;
        for (int wi = 0; wi < W; wi++) {
            uint64_t x = m.w[wi];
            while (x) {
                uint64_t bit = x & -x;
                int q = wi * 64 + __builtin_ctzll(x);
                if (q < d->Q) out_bits.push_back(q);
                x -= bit;
            }
        }
        return out_bits;
    }

    SmallMask step(const SmallMask& s, int letter) const {
        SmallMask out_mask;
        for (int q : bits(s)) set_bit(out_mask, d->tr[q][letter]);
        return out_mask;
    }

    bool build_clause_id_maps() {
        selector_id.assign(d->L + 1, 0);
        init_id.assign(2, vector<int>(d->Q, 0));
        final_id.assign(d->Q, vector<int>(d->Q, 0));
        prop_id.assign(max(1, (d->L - 1) * d->Q * 2), 0);

        for (int idx = 0; idx < (int)f->clauses.size(); idx++) {
            int cid = idx + 1;
            const auto& c = f->clauses[idx];
            if (c.lits.size() == 3) {
                int src = 0, dst = 0, sel = 0;
                for (int lit : c.lits) {
                    int v = abs(lit);
                    if (lit < 0 && v >= 1 && v <= d->base) src = v;
                    else if (lit > 0 && v >= 1 && v <= d->base) dst = v;
                    else if (lit < 0 && v > d->base && v <= f->vars) sel = v;
                }
                if (!src || !dst || !sel) continue;
                int src_layer = (src - 1) / d->Q + 1;
                int dst_layer = (dst - 1) / d->Q + 1;
                int sel_layer = (sel - d->base - 1) / 2 + 1;
                int letter = (sel - d->base - 1) % 2;
                if (src_layer + 1 == dst_layer && sel_layer == dst_layer &&
                    src_layer >= 1 && src_layer < d->L) {
                    prop_id[prop_index(src_layer, (src - 1) % d->Q, letter)] = cid;
                }
            } else if (c.lits.size() == 2) {
                int a = c.lits[0], b = c.lits[1];
                if (a > 0 && b > 0 && a > d->base && b > d->base) {
                    int layer = (a - d->base - 1) / 2 + 1;
                    if (layer >= 1 && layer <= d->L) selector_id[layer] = cid;
                } else if (a < 0 && b < 0 && abs(a) <= d->base && abs(b) <= d->base) {
                    int la = (abs(a) - 1) / d->Q + 1;
                    int lb = (abs(b) - 1) / d->Q + 1;
                    if (la == d->L && lb == d->L) {
                        int qa = (abs(a) - 1) % d->Q;
                        int qb = (abs(b) - 1) % d->Q;
                        if (qa > qb) swap(qa, qb);
                        final_id[qa][qb] = cid;
                    }
                } else {
                    int pos = a > 0 ? a : (b > 0 ? b : 0);
                    int neg = a < 0 ? a : (b < 0 ? b : 0);
                    if (pos >= 1 && pos <= d->Q && neg < 0 && abs(neg) > d->base) {
                        int layer = (abs(neg) - d->base - 1) / 2 + 1;
                        int letter = (abs(neg) - d->base - 1) % 2;
                        int q = pos - 1;
                        if (layer == 1 && init_id[letter][q] == 0) init_id[letter][q] = cid;
                    }
                }
            }
        }

        for (int t = 1; t <= d->L; t++) {
            if (!selector_id[t]) return false;
        }
        for (int q = 0; q < d->Q; q++) {
            for (int r = q + 1; r < d->Q; r++) {
                if (!final_id[q][r]) return false;
            }
        }
        for (int t = 1; t < d->L; t++) {
            for (int q = 0; q < d->Q; q++) {
                for (int ch = 0; ch < 2; ch++) {
                    if (!prop_id[prop_index(t, q, ch)]) return false;
                }
            }
        }
        return true;
    }

    void build_pair_too_far() {
        pair_too_far.assign(d->L + 1, vector<SmallMask>(d->Q));
        for (int rem = 0; rem <= d->L; rem++) {
            for (int a = 0; a < d->Q; a++) {
                SmallMask m;
                for (int b = 0; b < d->Q; b++) {
                    if (a != b && pd[a][b] > rem) set_bit(m, b);
                }
                pair_too_far[rem][a] = m;
            }
        }
    }

    bool find_bad_pair(const SmallMask& s, int rem, int& a, int& b) const {
        for (int q : bits(s)) {
            SmallMask both = mask_and(s, pair_too_far[rem][q]);
            for (int r : bits(both)) {
                if (q == r) continue;
                a = min(q, r);
                b = max(q, r);
                return true;
            }
        }
        return false;
    }

    void write_lit(int lit) {
        if (lit > 0) (*out) << "1 x" << lit << ' ';
        else (*out) << "1 ~x" << -lit << ' ';
    }

    int64_t emit_clause(const vector<int>& lits, const vector<int64_t>& hints) {
        (*out) << "rup ";
        for (int lit : lits) write_lit(lit);
        (*out) << ">= 1";
        if (!hints.empty()) {
            (*out) << " :";
            for (int64_t h : hints) (*out) << ' ' << h;
        }
        (*out) << " ;\n";
        next_id++;
        return next_id;
    }

    vector<int> bad_lits(int layer, const SmallMask& s) const {
        vector<int> lits;
        lits.reserve(popcount(s));
        for (int q : bits(s)) lits.push_back(-state_var(layer, q));
        return lits;
    }

    vector<int64_t> prop_hints(int layer, const SmallMask& parent, int letter,
                               const SmallMask& child) const {
        vector<int64_t> hints;
        vector<int> parent_bits = bits(parent);
        for (int r : bits(child)) {
            int chosen = 0;
            bool found = false;
            for (int q : parent_bits) {
                if (d->tr[q][letter] == r) {
                    chosen = prop_id[prop_index(layer, q, letter)];
                    found = true;
                    break;
                }
            }
            if (!found || chosen == 0) return {};
            hints.push_back(chosen);
        }
        return hints;
    }

    vector<int64_t> init_hints(int letter, const SmallMask& child) const {
        vector<int64_t> hints;
        for (int r : bits(child)) {
            int id = init_id[letter][r];
            if (!id) return {};
            hints.push_back(id);
        }
        return hints;
    }

    Ref prove_pair(int layer, int a, int b) {
        if (a > b) swap(a, b);
        Ref ref{0, pair_mask(a, b)};
        if (a == b || pd[a][b] <= d->L - layer) return ref;
        if (layer == d->L) {
            ref.id = final_id[a][b];
            return ref;
        }
        PairKey key{layer, a, b};
        auto it = pair_done.find(key);
        if (it != pair_done.end()) {
            ref.id = it->second;
            return ref;
        }

        Ref child[2];
        for (int ch = 0; ch < 2; ch++) {
            child[ch] = prove_pair(layer + 1, d->tr[a][ch], d->tr[b][ch]);
            if (!child[ch].id) return Ref{};
        }

        vector<int> lits0 = bad_lits(layer, ref.mask);
        lits0.push_back(-sel_var(layer + 1, 0));
        vector<int64_t> hints0 = prop_hints(layer, ref.mask, 0, child[0].mask);
        if (hints0.empty()) return Ref{};
        hints0.push_back(child[0].id);
        int64_t g0 = emit_clause(lits0, hints0);

        vector<int> lits1 = bad_lits(layer, ref.mask);
        lits1.push_back(-sel_var(layer + 1, 1));
        vector<int64_t> hints1 = prop_hints(layer, ref.mask, 1, child[1].mask);
        if (hints1.empty()) return Ref{};
        hints1.push_back(child[1].id);
        int64_t g1 = emit_clause(lits1, hints1);

        vector<int64_t> hints{g0, g1, selector_id[layer + 1]};
        ref.id = emit_clause(bad_lits(layer, ref.mask), hints);
        pair_done.emplace(key, ref.id);
        return ref;
    }

    Ref prove_subset(int layer, const SmallMask& s) {
        if (popcount(s) <= 1) return Ref{};
        int a = 0, b = 0;
        if (find_bad_pair(s, d->L - layer, a, b)) return prove_pair(layer, a, b);

        SubKey key{layer, s};
        auto it = sub_done.find(key);
        if (it != sub_done.end()) return Ref{it->second, s};
        if (layer == d->L) return Ref{};

        Ref child[2];
        SmallMask img[2];
        for (int ch = 0; ch < 2; ch++) {
            img[ch] = step(s, ch);
            child[ch] = prove_subset(layer + 1, img[ch]);
            if (!child[ch].id) return Ref{};
        }

        vector<int> lits0 = bad_lits(layer, s);
        lits0.push_back(-sel_var(layer + 1, 0));
        vector<int64_t> hints0 = prop_hints(layer, s, 0, child[0].mask);
        if (hints0.empty()) return Ref{};
        hints0.push_back(child[0].id);
        int64_t g0 = emit_clause(lits0, hints0);

        vector<int> lits1 = bad_lits(layer, s);
        lits1.push_back(-sel_var(layer + 1, 1));
        vector<int64_t> hints1 = prop_hints(layer, s, 1, child[1].mask);
        if (hints1.empty()) return Ref{};
        hints1.push_back(child[1].id);
        int64_t g1 = emit_clause(lits1, hints1);

        vector<int64_t> hints{g0, g1, selector_id[layer + 1]};
        int64_t id = emit_clause(bad_lits(layer, s), hints);
        sub_done.emplace(key, id);
        return Ref{id, s};
    }
};

static bool write_random_unsat_proof(const Formula& f, const Detected& d, const string& path) {
    if (d.Q <= 1 || d.Q > 180 || d.L <= 0 || d.L > 80) return false;
    int cerny_len = 0;
    if (is_cerny_like(d, cerny_len)) return false;

    ofstream out(path);
    if (!out) return false;
    out << "pseudo-Boolean proof version 3.0\n";

    RandomProof proof;
    proof.f = &f;
    proof.d = &d;
    proof.out = &out;
    proof.W = (d.Q + 63) / 64;
    if (proof.W > 3) return false;
    proof.next_id = f.clauses_declared;
    proof.pd = pair_distances(d);
    if (!proof.build_clause_id_maps()) return false;
    proof.build_pair_too_far();

    SmallMask full = proof.full_mask();
    int64_t unit_id[2] = {0, 0};
    for (int ch = 0; ch < 2; ch++) {
        SmallMask img = proof.step(full, ch);
        RandomProof::Ref child = proof.prove_subset(1, img);
        if (!child.id) return false;
        vector<int> lits{-proof.sel_var(1, ch)};
        vector<int64_t> hints = proof.init_hints(ch, child.mask);
        if (hints.empty()) return false;
        hints.push_back(child.id);
        unit_id[ch] = proof.emit_clause(lits, hints);
    }
    proof.emit_clause({}, {unit_id[0], unit_id[1], proof.selector_id[1]});
    out << "output NONE ;\n";
    out << "conclusion UNSAT : " << proof.next_id << " ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return (bool)out;
}

static vector<vector<int>> pair_distances(const Detected& d) {
    int Q = d.Q;
    int N = Q * Q;
    auto id = [Q](int a, int b) {
        if (a > b) swap(a, b);
        return a * Q + b;
    };
    vector<vector<pair<int, int>>> pred(N);
    for (int a = 0; a < Q; a++) {
        for (int b = a; b < Q; b++) {
            int src = id(a, b);
            for (int ch = 0; ch < 2; ch++) {
                int u = d.tr[a][ch], v = d.tr[b][ch];
                pred[id(u, v)].push_back({src, ch});
            }
        }
    }
    vector<int> dist(N, 1e9);
    queue<int> q;
    for (int i = 0; i < Q; i++) {
        int z = id(i, i);
        dist[z] = 0;
        q.push(z);
    }
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (auto [p, ch] : pred[cur]) {
            (void)ch;
            if (dist[p] > dist[cur] + 1) {
                dist[p] = dist[cur] + 1;
                q.push(p);
            }
        }
    }
    vector<vector<int>> out(Q, vector<int>(Q, 0));
    for (int a = 0; a < Q; a++) {
        for (int b = 0; b < Q; b++) out[a][b] = dist[id(a, b)];
    }
    return out;
}

static int lower_bound_pairs(const BitSubset& s, const vector<vector<int>>& pd) {
    vector<int> elems;
    elems.reserve(s.count);
    for (int wi = 0; wi < (int)s.w.size(); wi++) {
        uint64_t x = s.w[wi];
        while (x) {
            uint64_t bit = x & -x;
            elems.push_back(wi * 64 + __builtin_ctzll(x));
            x -= bit;
        }
    }
    int h = 0;
    for (int i = 0; i < (int)elems.size(); i++) {
        for (int j = i + 1; j < (int)elems.size(); j++) {
            h = max(h, pd[elems[i]][elems[j]]);
        }
    }
    return h >= (int)1e9 ? 1000000000 : h;
}

static bool beam_word(const Detected& d, vector<int>& word, int width, double seconds) {
    auto start_time = chrono::steady_clock::now();
    vector<vector<int>> pd = pair_distances(d);
    struct Node {
        BitSubset s;
        vector<int> w;
        int score = 0;
    };
    vector<Node> layer;
    layer.push_back({full_subset(d.Q), {}, d.Q});
    unordered_set<string> global_seen;
    global_seen.insert(key_of(layer[0].s));
    for (int depth = 0; depth <= d.L; depth++) {
        vector<Node> next;
        for (auto& n : layer) {
            if (singleton(n.s)) {
                word = n.w;
                while ((int)word.size() < d.L) word.push_back(0);
                return true;
            }
            if ((int)n.w.size() >= d.L) continue;
            for (int ch = 0; ch < 2; ch++) {
                Node m;
                m.s = step_subset(n.s, d, ch);
                string k = key_of(m.s);
                if (!global_seen.insert(k).second) continue;
                int lb = lower_bound_pairs(m.s, pd);
                if ((int)n.w.size() + 1 + lb > d.L) continue;
                m.w = n.w;
                m.w.push_back(ch);
                m.score = m.s.count * 1000 + lb * 10 + ch;
                next.push_back(std::move(m));
            }
        }
        auto now = chrono::steady_clock::now();
        if (chrono::duration<double>(now - start_time).count() > seconds) return false;
        if (next.empty()) return false;
        nth_element(next.begin(), next.begin() + min(width, (int)next.size()) - 1, next.end(),
                    [](const Node& a, const Node& b) { return a.score < b.score; });
        if ((int)next.size() > width) next.resize(width);
        sort(next.begin(), next.end(), [](const Node& a, const Node& b) { return a.score < b.score; });
        layer.swap(next);
    }
    return false;
}

static bool local_word(const Detected& d, vector<int>& word, double seconds) {
    auto start_time = chrono::steady_clock::now();
    mt19937_64 rng(0x5eed12345678ULL + d.Q * 1009 + d.L);
    vector<vector<int>> starts;
    for (int seed = 0; seed < 256; seed++) {
        vector<int> w(d.L);
        if (seed == 0) {
            BitSubset s = full_subset(d.Q);
            for (int i = 0; i < d.L; i++) {
                BitSubset a = step_subset(s, d, 0), b = step_subset(s, d, 1);
                w[i] = (a.count <= b.count) ? 0 : 1;
                s = (w[i] == 0) ? a : b;
            }
        } else {
            for (int i = 0; i < d.L; i++) w[i] = (rng() >> i) & 1;
        }
        starts.push_back(std::move(w));
    }
    int best_size = d.Q;
    vector<int> best_word;
    for (auto cur : starts) {
        for (int iter = 0; iter < 2000; iter++) {
            int cur_size = image_size_for_word(d, cur);
            if (cur_size < best_size) {
                best_size = cur_size;
                best_word = cur;
            }
            if (cur_size == 1) {
                word = cur;
                return true;
            }
            int best_flip = -1;
            int best_flip_size = cur_size;
            vector<int> order(d.L);
            iota(order.begin(), order.end(), 0);
            shuffle(order.begin(), order.end(), rng);
            for (int idx : order) {
                cur[idx] ^= 1;
                int s = image_size_for_word(d, cur);
                cur[idx] ^= 1;
                if (s < best_flip_size) {
                    best_flip_size = s;
                    best_flip = idx;
                }
            }
            if (best_flip >= 0) {
                cur[best_flip] ^= 1;
            } else {
                int flips = 1 + (rng() % 3);
                for (int k = 0; k < flips; k++) cur[rng() % d.L] ^= 1;
            }
            auto now = chrono::steady_clock::now();
            if (chrono::duration<double>(now - start_time).count() > seconds) {
                return false;
            }
        }
    }
    (void)best_word;
    return false;
}

static bool find_word(const Detected& d, vector<int>& word) {
    if (cerny_word(d, word)) return true;
    int cerny_len = 0;
    if (is_cerny_like(d, cerny_len) && cerny_len > d.L) return false;
    for (int width : {2000, 10000, 50000}) {
        if (beam_word(d, word, width, 8.0)) return image_size_for_word(d, word) == 1;
    }
    if (local_word(d, word, 20.0)) return image_size_for_word(d, word) == 1;
    return false;
}

static vector<int> build_assignment(const Detected& d, const vector<int>& word) {
    vector<int> val(d.base + 2 * d.L + 1, 0);
    BitSubset s = full_subset(d.Q);
    for (int t = 0; t < d.L; t++) {
        int ch = word[t];
        val[d.base + 2 * t + 1] = (ch == 0);
        val[d.base + 2 * t + 2] = (ch == 1);
        s = step_subset(s, d, ch);
        for (int q = 0; q < d.Q; q++) {
            bool active = (s.w[q >> 6] >> (q & 63)) & 1ULL;
            val[t * d.Q + q + 1] = active;
        }
    }
    return val;
}

static bool check_assignment(const Formula& f, const vector<int>& val) {
    if ((int)val.size() != f.vars + 1) return false;
    for (const auto& c : f.clauses) {
        bool sat = false;
        for (int lit : c.lits) {
            int v = abs(lit);
            if (v <= 0 || v > f.vars) return false;
            bool b = val[v] != 0;
            if ((lit > 0 && b) || (lit < 0 && !b)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void write_empty_proof(const string& path) {
    ofstream 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) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula f;
    if (!read_cnf(argv[1], f)) {
        printf("c failed to parse CNF\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }
    Detected d;
    if (!detect_automaton(f, d)) {
        printf("c unsupported CNF structure\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }
    printf("c detected synchronizing automaton Q=%d L=%d\n", d.Q, d.L);
    vector<int> word;
    if (!find_word(d, word) || (int)word.size() != d.L || image_size_for_word(d, word) != 1) {
        int cerny_len = 0;
        if (is_cerny_like(d, cerny_len) && cerny_len > d.L &&
            write_cerny_unsat_proof(f, d, argv[2])) {
            printf("c proved Cerny lower bound Q=%d L=%d shortest=%d\n", d.Q, d.L, cerny_len);
            printf("s UNSATISFIABLE\n");
            return 20;
        }
        if (write_random_unsat_proof(f, d, argv[2])) {
            printf("c proved random-style subset lower bound Q=%d L=%d\n", d.Q, d.L);
            printf("s UNSATISFIABLE\n");
            return 20;
        }
        printf("c no synchronizing word found within bound; returning UNKNOWN\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }
    vector<int> val = build_assignment(d, word);
    if (!check_assignment(f, val)) {
        printf("c internal model check failed; returning UNKNOWN\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }
    printf("s SATISFIABLE\n");
    printf("v");
    int col = 1;
    for (int v = 1; v <= f.vars; v++) {
        int lit = val[v] ? v : -v;
        printf(" %d", lit);
        if (++col >= 15) {
            printf("\nv");
            col = 1;
        }
    }
    printf(" 0\n");
    write_empty_proof(argv[2]);
    return 10;
}
