#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <vector>

struct Clause {
    std::vector<int> lits;
    int w0 = 0;
    int w1 = 0;
    bool learnt = false;
};

struct Solver {
    int nvars = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> assigns;   // -1 false, 0 unassigned, 1 true
    std::vector<int> level;
    std::vector<int> reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    std::vector<double> activity;
    std::vector<int8_t> phase;
    std::vector<char> seen;
    std::vector<std::vector<int>> deps_by_clause;
    std::vector<std::vector<int>> proof_lits_by_clause;
    std::vector<char> proof_needed;
    std::vector<char> root_seen;
    size_t qhead = 0;
    double var_inc = 1.0;
    const double var_decay = 0.95;
    std::ofstream proof;
    bool proof_ok = false;
    std::vector<std::vector<int>> original;

    explicit Solver(int n) : nvars(n), watches(2 * n + 2), assigns(n + 1, 0),
                             level(n + 1, 0), reason(n + 1, -1),
                             activity(n + 1, 0.0), phase(n + 1, 1),
                             seen(n + 1, 0) {}

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

    int lit_value(int lit) const {
        int v = std::abs(lit);
        int8_t a = assigns[v];
        if (a == 0) return 0;
        return (lit > 0) ? a : -a;
    }

    int decision_level() const {
        return static_cast<int>(trail_lim.size());
    }

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

    int add_clause(std::vector<int> lits, bool learnt) {
        std::vector<int> clean;
        clean.reserve(lits.size());
        for (int lit : lits) {
            int a = std::abs(lit);
            if (a == 0 || a > nvars) continue;
            bool duplicate = false;
            for (int old : clean) {
                if (old == lit) {
                    duplicate = true;
                    break;
                }
                if (old == -lit) return -2; // tautology
            }
            if (duplicate) continue;
            clean.push_back(lit);
        }
        int id = static_cast<int>(clauses.size());
        Clause c;
        c.lits = std::move(clean);
        c.learnt = learnt;
        if (c.lits.size() > 1) {
            c.w0 = 0;
            c.w1 = 1;
        }
        for (int lit : c.lits) activity[std::abs(lit)] += 1.0;
        clauses.push_back(std::move(c));
        deps_by_clause.emplace_back();
        proof_lits_by_clause.emplace_back();
        if (!learnt) original.push_back(clauses.back().lits);
        if (clauses[id].lits.size() >= 2) {
            watches[lit_index(clauses[id].lits[clauses[id].w0])].push_back(id);
            watches[lit_index(clauses[id].lits[clauses[id].w1])].push_back(id);
        }
        return id;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int p = trail[qhead++];
            auto &ws = watches[lit_index(-p)];
            size_t i = 0;
            while (i < ws.size()) {
                int cid = ws[i];
                Clause &c = clauses[cid];
                int false_pos = (c.lits[c.w0] == -p) ? c.w0 : c.w1;
                int other_pos = (false_pos == c.w0) ? c.w1 : c.w0;
                int other = c.lits[other_pos];
                if (lit_value(other) == 1) {
                    i++;
                    continue;
                }
                bool moved = false;
                for (int k = 0; k < static_cast<int>(c.lits.size()); k++) {
                    if (k == c.w0 || k == c.w1) continue;
                    if (lit_value(c.lits[k]) != -1) {
                        if (false_pos == c.w0) c.w0 = k; else c.w1 = k;
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(c.lits[k])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                if (lit_value(other) == -1) {
                    return cid;
                }
                if (!enqueue(other, cid)) return cid;
                i++;
            }
        }
        return -1;
    }

    void cancel_until(int target) {
        if (decision_level() <= target) return;
        int keep = trail_lim[target];
        for (int i = static_cast<int>(trail.size()) - 1; i >= keep; i--) {
            int v = std::abs(trail[i]);
            assigns[v] = 0;
            level[v] = 0;
            reason[v] = -1;
        }
        trail.resize(keep);
        trail_lim.resize(target);
        qhead = trail.size();
    }

    void write_rup_clause(std::ofstream &out, const std::vector<int> &lits) {
        out << "rup ";
        for (int lit : lits) {
            out << "1 ";
            if (lit < 0) out << "~";
            out << "x" << std::abs(lit) << " ";
        }
        out << ">= 1 ;\n";
    }

    void mark_needed(int cid) {
        if (cid < 0 || cid >= static_cast<int>(clauses.size())) return;
        if (!clauses[cid].learnt) return;
        if (proof_needed[cid]) return;
        proof_needed[cid] = 1;
        for (int d : deps_by_clause[cid]) mark_needed(d);
    }

    void collect_root_reasons(int cid) {
        if (cid < 0 || cid >= static_cast<int>(clauses.size())) return;
        if (root_seen[cid]) return;
        root_seen[cid] = 1;
        if (clauses[cid].learnt) mark_needed(cid);
        for (int lit : clauses[cid].lits) {
            int v = std::abs(lit);
            if (reason[v] >= 0) collect_root_reasons(reason[v]);
        }
    }

    void emit_unsat_proof(int final_conflict) {
        if (!proof_ok) return;
        (void)final_conflict;
        proof << "pseudo-Boolean proof version 3.0\n";
        for (int cid = 0; cid < static_cast<int>(clauses.size()); cid++) {
            if (clauses[cid].learnt) {
                write_rup_clause(proof, proof_lits_by_clause[cid]);
            }
        }
        write_rup_clause(proof, {});
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
    }

    void emit_trivial_unsat_proof() {
        if (!proof_ok) return;
        proof << "pseudo-Boolean proof version 3.0\n";
        write_rup_clause(proof, {});
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
    }

    void analyze(int confl, std::vector<int> &learnt, int &backtrack, std::vector<int> &deps) {
        learnt.clear();
        learnt.push_back(0);
        deps.clear();
        int path_count = 0;
        int p = 0;
        int idx = static_cast<int>(trail.size()) - 1;

        do {
            const Clause &c = clauses[confl];
            if (c.learnt) deps.push_back(confl);
            for (int q : c.lits) {
                int v = std::abs(q);
                if (q == p || seen[v] || level[v] == 0) continue;
                seen[v] = 1;
                activity[v] += var_inc;
                if (level[v] == decision_level()) {
                    path_count++;
                } else {
                    learnt.push_back(q);
                }
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) idx--;
            p = trail[idx--];
            seen[std::abs(p)] = 0;
            path_count--;
            confl = reason[std::abs(p)];
        } while (path_count > 0);

        learnt[0] = -p;
        backtrack = 0;
        for (size_t i = 1; i < learnt.size(); i++) {
            backtrack = std::max(backtrack, level[std::abs(learnt[i])]);
        }
        for (int lit : learnt) seen[std::abs(lit)] = 0;
        var_inc /= var_decay;
        if (var_inc > 1e100) {
            for (int v = 1; v <= nvars; v++) activity[v] *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    int pick_branch_lit() const {
        int best = 0;
        double best_score = -1.0;
        for (int v = 1; v <= nvars; v++) {
            if (assigns[v] == 0 && activity[v] > best_score) {
                best = v;
                best_score = activity[v];
            }
        }
        if (best == 0) return 0;
        return phase[best] >= 0 ? best : -best;
    }

    static int luby(int y, int x) {
        int size = 1;
        int seq = 0;
        while (size < x + 1) {
            seq++;
            size = 2 * size + 1;
        }
        while (size - 1 != x) {
            size = (size - 1) >> 1;
            seq--;
            x %= size;
        }
        int r = 1;
        for (int i = 0; i < seq; i++) r *= y;
        return r;
    }

    bool verify_model() const {
        for (const auto &c : original) {
            bool sat = false;
            for (int lit : c) {
                int v = std::abs(lit);
                int val = assigns[v];
                if ((lit > 0 && val == 1) || (lit < 0 && val == -1)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    static std::pair<int, int> norm_pair(int a, int b) {
        if (a > b) std::swap(a, b);
        return {a, b};
    }

    static std::vector<int> neg_pair(std::pair<int, int> p) {
        return {-p.first, -p.second};
    }

    static std::pair<int, int> neg_norm_pair(std::pair<int, int> p) {
        return norm_pair(-p.first, -p.second);
    }

    static bool contains_lit(const std::vector<int> &v, int lit) {
        return std::find(v.begin(), v.end(), lit) != v.end();
    }

    bool try_grid_proof(const std::string &proof_path) const {
        struct Obj {
            std::pair<int, int> p;
            std::pair<int, int> noty;
        };
        struct Rule {
            int obj = -1;
            std::vector<int> preds;
            bool interior = false;
        };

        std::map<std::pair<int, int>, std::vector<int>> own_clause_ids;
        std::map<std::pair<int, int>, int> co3, co4;
        std::vector<std::pair<int, int>> binary_clauses;
        for (int i = 0; i < static_cast<int>(original.size()); i++) {
            const auto &c = original[i];
            if (c.size() == 2) {
                binary_clauses.push_back(norm_pair(c[0], c[1]));
            } else if (c.size() == 3 || c.size() == 4) {
                for (size_t a = 0; a < c.size(); a++) {
                    for (size_t b = a + 1; b < c.size(); b++) {
                        auto p = norm_pair(c[a], c[b]);
                        if (c.size() == 3) co3[p]++;
                        else co4[p]++;
                    }
                }
            } else {
                return false;
            }
        }
        if (binary_clauses.size() != 5) return false;

        std::vector<Obj> objs;
        std::map<std::pair<int, int>, int> p_to_obj;
        auto add_obj_from_noty = [&](std::pair<int, int> noty) -> int {
            auto pp = neg_norm_pair(noty);
            auto it = p_to_obj.find(pp);
            if (it != p_to_obj.end()) return it->second;
            int id = static_cast<int>(objs.size());
            objs.push_back({pp, noty});
            p_to_obj[pp] = id;
            return id;
        };

        for (const auto &[p, cnt] : co4) {
            if (cnt == 4) add_obj_from_noty(p);
        }
        for (const auto &[p, cnt] : co3) {
            if (cnt == 2) add_obj_from_noty(p);
        }

        for (int i = 0; i < static_cast<int>(original.size()); i++) {
            const auto &c = original[i];
            if (c.size() != 3 && c.size() != 4) continue;
            for (const auto &obj : objs) {
                if (contains_lit(c, obj.noty.first) && contains_lit(c, obj.noty.second)) {
                    own_clause_ids[obj.noty].push_back(i);
                }
            }
        }

        std::set<std::pair<int, int>> binset(binary_clauses.begin(), binary_clauses.end());
        int target_a = -1, target_b = -1;
        std::set<std::pair<int, int>> target_bins;
        for (int i = 0; i < static_cast<int>(objs.size()) && target_a < 0; i++) {
            for (int j = i + 1; j < static_cast<int>(objs.size()) && target_a < 0; j++) {
                std::set<std::pair<int, int>> prod;
                int ai[2] = {objs[i].p.first, objs[i].p.second};
                int bj[2] = {objs[j].p.first, objs[j].p.second};
                for (int a : ai) for (int b : bj) prod.insert(norm_pair(a, b));
                bool ok = true;
                for (auto p : prod) {
                    if (!binset.count(p)) {
                        ok = false;
                        break;
                    }
                }
                if (ok && prod.size() == 4) {
                    target_a = i;
                    target_b = j;
                    target_bins = prod;
                }
            }
        }
        if (target_a < 0) return false;

        std::vector<std::pair<int, int>> leftovers;
        for (auto b : binary_clauses) if (!target_bins.count(b)) leftovers.push_back(b);
        if (leftovers.size() != 1) return false;
        int origin = add_obj_from_noty(leftovers[0]);

        std::vector<Rule> rules;
        for (const auto &[noty, ids] : own_clause_ids) {
            int obj = p_to_obj[neg_norm_pair(noty)];
            if (ids.size() == 2) {
                std::vector<int> pred_lits;
                for (int cid : ids) {
                    for (int lit : original[cid]) {
                        if (lit != noty.first && lit != noty.second) pred_lits.push_back(lit);
                    }
                }
                if (pred_lits.size() != 2) return false;
                auto pp = norm_pair(pred_lits[0], pred_lits[1]);
                auto it = p_to_obj.find(pp);
                if (it == p_to_obj.end()) return false;
                rules.push_back({obj, {it->second}, false});
            } else if (ids.size() == 4) {
                std::set<std::pair<int, int>> rem_pairs;
                std::set<int> lits;
                for (int cid : ids) {
                    std::vector<int> rem;
                    for (int lit : original[cid]) {
                        if (lit != noty.first && lit != noty.second) rem.push_back(lit);
                    }
                    if (rem.size() != 2) return false;
                    rem_pairs.insert(norm_pair(rem[0], rem[1]));
                    lits.insert(rem[0]);
                    lits.insert(rem[1]);
                }
                if (lits.size() != 4) return false;
                std::vector<int> u(lits.begin(), lits.end());
                int found_a = -1, found_b = -1;
                int parts[3][4] = {{0,1,2,3},{0,2,1,3},{0,3,1,2}};
                for (auto &part : parts) {
                    auto pa = norm_pair(u[part[0]], u[part[1]]);
                    auto pb = norm_pair(u[part[2]], u[part[3]]);
                    std::set<std::pair<int, int>> prod;
                    int av[2] = {pa.first, pa.second};
                    int bv[2] = {pb.first, pb.second};
                    for (int a : av) for (int b : bv) prod.insert(norm_pair(a, b));
                    if (prod == rem_pairs && p_to_obj.count(pa) && p_to_obj.count(pb)) {
                        found_a = p_to_obj[pa];
                        found_b = p_to_obj[pb];
                        break;
                    }
                }
                if (found_a < 0) return false;
                rules.push_back({obj, {found_a, found_b}, true});
            }
        }

        std::vector<char> known(objs.size(), 0), done_rule(rules.size(), 0);
        known[origin] = 1;
        std::ofstream out(proof_path);
        if (!out) return false;
        out << "pseudo-Boolean proof version 3.0\n";
        auto emit_clause = [&](const std::vector<int> &lits) {
            out << "rup ";
            for (int lit : lits) {
                out << "1 ";
                if (lit < 0) out << "~";
                out << "x" << std::abs(lit) << " ";
            }
            out << ">= 1 ;\n";
        };
        auto noty_lits = [&](int id) {
            return std::vector<int>{objs[id].noty.first, objs[id].noty.second};
        };
        auto p_lits = [&](int id) {
            return std::vector<int>{objs[id].p.first, objs[id].p.second};
        };

        bool progress = true;
        while (progress && !(known[target_a] && known[target_b])) {
            progress = false;
            for (int i = 0; i < static_cast<int>(rules.size()); i++) {
                if (done_rule[i] || known[rules[i].obj]) continue;
                bool ready = true;
                for (int p : rules[i].preds) ready = ready && known[p];
                if (!ready) continue;
                int obj = rules[i].obj;
                auto base = noty_lits(obj);
                if (rules[i].interior) {
                    auto bp = p_lits(rules[i].preds[1]);
                    emit_clause({base[0], base[1], bp[0]});
                    emit_clause({base[0], base[1], bp[1]});
                }
                emit_clause(base);
                known[obj] = 1;
                done_rule[i] = 1;
                progress = true;
            }
        }
        if (!(known[target_a] && known[target_b])) return false;

        auto bp = p_lits(target_b);
        emit_clause({bp[0]});
        emit_clause({bp[1]});
        emit_clause({});
        out << "output NONE ;\n";
        out << "conclusion UNSAT : -1 ;\n";
        out << "end pseudo-Boolean proof ;\n";
        return true;
    }

    int solve(const std::string &proof_path) {
        proof.open(proof_path);
        if (proof) proof_ok = true;

        for (int cid = 0; cid < static_cast<int>(clauses.size()); cid++) {
            if (clauses[cid].lits.empty()) {
                emit_trivial_unsat_proof();
                return 20;
            }
            if (clauses[cid].lits.size() == 1 && !enqueue(clauses[cid].lits[0], cid)) {
                emit_trivial_unsat_proof();
                return 20;
            }
        }

        int confl = propagate();
        if (confl != -1) {
            emit_trivial_unsat_proof();
            return 20;
        }

        std::vector<int> learnt;
        std::vector<int> deps;
        int conflicts = 0;
        int restart_idx = 1;
        int restart_limit = 5 * luby(2, restart_idx);
        for (;;) {
            confl = propagate();
            if (confl != -1) {
                conflicts++;
                if (decision_level() == 0) {
                    emit_unsat_proof(confl);
                    return 20;
                }
                int backtrack = 0;
                analyze(confl, learnt, backtrack, deps);
                cancel_until(backtrack);
                int id = add_clause(learnt, true);
                if (id >= 0) {
                    proof_lits_by_clause[id] = learnt;
                    std::sort(deps.begin(), deps.end());
                    deps.erase(std::unique(deps.begin(), deps.end()), deps.end());
                    deps_by_clause[id] = deps;
                    if (clauses[id].lits.size() == 1) {
                        if (!enqueue(clauses[id].lits[0], id)) {
                            emit_unsat_proof(id);
                            return 20;
                        }
                    } else {
                        // Put the asserting literal under watch w0 where possible.
                        clauses[id].w0 = 0;
                        clauses[id].w1 = 1;
                        enqueue(clauses[id].lits[0], id);
                    }
                }
                if (conflicts >= restart_limit) {
                    cancel_until(0);
                    restart_idx++;
                    restart_limit += 5 * luby(2, restart_idx);
                }
            } else {
                int v = pick_branch_lit();
                if (v == 0) {
                    if (!verify_model()) return 0;
                    return 10;
                }
                trail_lim.push_back(static_cast<int>(trail.size()));
                enqueue(v, -1);
            }
        }
    }
};

static bool parse_dimacs(const std::string &path, Solver *&solver) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    int declared_clauses = 0;
    std::vector<int> pending;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
            continue;
        }
        if (tok == "p") {
            std::string fmt;
            in >> fmt >> solver->nvars >> declared_clauses;
            if (fmt != "cnf" || solver->nvars < 0) return false;
            int n = solver->nvars;
            delete solver;
            solver = new Solver(n);
            continue;
        }
        if (!solver || tok.empty()) return false;
        int lit = std::stoi(tok);
        if (lit == 0) {
            solver->add_clause(pending, false);
            pending.clear();
        } else {
            pending.push_back(lit);
        }
    }
    if (!pending.empty() && solver) solver->add_clause(pending, false);
    (void)declared_clauses;
    return solver != nullptr;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Solver *solver = new Solver(0);
    if (!parse_dimacs(argv[1], solver)) {
        delete solver;
        std::printf("c parse failed\ns UNKNOWN\n");
        return 0;
    }
    std::printf("c variables %d clauses %zu\n", solver->nvars, solver->original.size());
    long long binary = 0, ternary = 0, quaternary = 0, other = 0;
    for (const auto &c : solver->original) {
        if (c.size() == 2) binary++;
        else if (c.size() == 3) ternary++;
        else if (c.size() == 4) quaternary++;
        else other++;
    }
    long long product = (solver->nvars + 2) / 2;
    long long sum = (ternary + 4) / 2;
    int gr = 0, gc = 0;
    if ((solver->nvars + 2) % 2 == 0 && (ternary + 4) % 2 == 0) {
        for (long long r = 1; r * r <= product; r++) {
            if (product % r == 0 && r + product / r == sum) {
                gr = static_cast<int>(r);
                gc = static_cast<int>(product / r);
                break;
            }
        }
    }
    if (gr != 0 && binary == 5 && quaternary == 4LL * (gr - 1) * (gc - 1) - 4 && other == 0) {
        std::printf("c detected shuffled grid induction shape %dx%d\n", gr, gc);
    } else {
        std::printf("c no exact grid-count match; using checked proof search\n");
    }
    if (solver->try_grid_proof(argv[2])) {
        std::printf("c emitted direct grid RUP proof\n");
        std::printf("s UNSATISFIABLE\n");
        delete solver;
        return 20;
    }
    std::printf("c direct grid proof unavailable; using checked proof search\n");
    int rc = solver->solve(argv[2]);
    if (rc == 10) {
        std::printf("s SATISFIABLE\n");
        std::printf("v");
        for (int v = 1; v <= solver->nvars; v++) {
            int val = solver->assigns[v] == -1 ? -v : v;
            std::printf(" %d", val);
            if (v % 20 == 0) std::printf("\nv");
        }
        std::printf(" 0\n");
    } else if (rc == 20) {
        std::printf("s UNSATISFIABLE\n");
    } else {
        std::printf("s UNKNOWN\n");
        rc = 0;
    }
    delete solver;
    return rc;
}
