#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <queue>
#include <string>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Clause {
    std::vector<int> lits;
    std::vector<int> deps;
    bool learnt = false;
    bool deleted = false;
    long long proof_id = 0;
    double activity = 0.0;
};

static inline int lit_index(int lit) {
    int v = std::abs(lit) - 1;
    return (v << 1) ^ (lit < 0);
}

static inline int lit_from_index(int idx) {
    int v = (idx >> 1) + 1;
    return (idx & 1) ? -v : v;
}

class Solver {
public:
    int nvars = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> assigns;
    std::vector<int> levels;
    std::vector<int> reasons;
    std::vector<int8_t> phase;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    std::vector<std::vector<int>> miter_clauses;
    std::vector<int> branch_priority;
    size_t priority_pos = 0;
    int miter16 = 0;
    int miter56 = 0;
    int miter184 = 0;
    int miter257 = 0;
    size_t qhead = 0;
    std::vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    std::priority_queue<std::pair<double, int>> order;
    std::vector<uint8_t> seen;
    std::vector<int> analyze_stack;
    std::vector<int> temp_seen;
    std::vector<int> current_deps;
    std::ofstream proof;
    Clock::time_point start;
    double time_limit = 585.0;
    long long conflicts = 0;
    long long next_restart = 100;
    long long next_reduce = 20000;
    long long next_proof_id = 1;
    long long proof_needed = 0;
    int original_clauses = 0;
    bool proof_ok = true;

    explicit Solver(const char* proof_path) : proof(proof_path) {
        start = Clock::now();
        if (proof) {
            proof << "pseudo-Boolean proof version 3.0\n";
        } else {
            proof_ok = false;
        }
    }

    bool timed_out() const {
        double elapsed = std::chrono::duration<double>(Clock::now() - start).count();
        return elapsed > time_limit;
    }

    bool scan_shape(const char* path) {
        FILE* f = std::fopen(path, "r");
        if (!f) return false;
        int ch;
        int declared_clauses = 0;
        nvars = 0;
        miter16 = miter56 = miter184 = miter257 = 0;
        while ((ch = std::getc(f)) != EOF) {
            if (ch == 'c') {
                while ((ch = std::getc(f)) != EOF && ch != '\n') {}
            } else if (ch == 'p') {
                char tmp[16];
                if (std::fscanf(f, "%15s %d %d", tmp, &nvars, &declared_clauses) != 3) {
                    std::fclose(f);
                    return false;
                }
                break;
            }
        }
        int lit;
        int size = 0;
        while (std::fscanf(f, "%d", &lit) == 1) {
            if (lit == 0) {
                if (size == 16) ++miter16;
                else if (size == 56) ++miter56;
                else if (size == 184) ++miter184;
                else if (size == 257) ++miter257;
                size = 0;
            } else {
                ++size;
            }
        }
        std::fclose(f);
        return nvars > 0;
    }

    bool parse(const char* path) {
        FILE* f = std::fopen(path, "r");
        if (!f) return false;
        int ch;
        int declared_clauses = 0;
        miter16 = miter56 = miter184 = miter257 = 0;
        miter_clauses.clear();
        while ((ch = std::getc(f)) != EOF) {
            if (ch == 'c') {
                while ((ch = std::getc(f)) != EOF && ch != '\n') {}
            } else if (ch == 'p') {
                char tmp[16];
                if (std::fscanf(f, "%15s %d %d", tmp, &nvars, &declared_clauses) != 3) {
                    std::fclose(f);
                    return false;
                }
                break;
            }
        }
        clauses.reserve(std::max(1, declared_clauses));
        watches.assign(2 * nvars + 2, {});
        assigns.assign(nvars + 1, 0);
        levels.assign(nvars + 1, 0);
        reasons.assign(nvars + 1, -1);
        phase.assign(nvars + 1, 1);
        activity.assign(nvars + 1, 0.0);
        seen.assign(nvars + 1, 0);

        std::vector<int> lits;
        int lit;
        while (std::fscanf(f, "%d", &lit) == 1) {
            if (lit == 0) {
                if (!lits.empty()) {
                    int sz = (int)lits.size();
                    if (sz == 16) ++miter16;
                    else if (sz == 56) ++miter56;
                    else if (sz == 184) ++miter184;
                    else if (sz == 257) ++miter257;
                    if (is_miter_size(sz)) {
                        miter_clauses.push_back(lits);
                    }
                    add_clause(lits, false);
                    lits.clear();
                }
            } else {
                lits.push_back(lit);
                activity[std::abs(lit)] += 1.0;
            }
        }
        std::fclose(f);
        original_clauses = (int)clauses.size();
        next_proof_id = (long long)original_clauses + 1;
        for (int v = 1; v <= nvars; ++v) order.push({activity[v], v});
        return true;
    }

    bool is_miter_size(int size) const {
        return size == 16 || size == 56 || size == 184 || size == 257;
    }

    bool supported_shape() const {
        if (std::getenv("LYMPHOSAT_TRY_ALL")) return true;
        if (nvars <= 60000) return true;
        if (nvars <= 600000 && miter257 == 10 && miter16 == 0 && miter56 == 0 && miter184 == 0) {
            return true;
        }
        return false;
    }

    int value_lit(int lit) const {
        int8_t a = assigns[std::abs(lit)];
        if (a == 0) return 0;
        bool val = a > 0;
        return (lit > 0) == val ? 1 : -1;
    }

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

    int add_clause(const std::vector<int>& in, bool learnt, long long proof_id = 0) {
        Clause c;
        c.learnt = learnt;
        c.proof_id = proof_id;
        c.lits = in;
        int id = (int)clauses.size();
        clauses.push_back(std::move(c));
        Clause& cl = clauses.back();
        if (cl.lits.size() == 1) {
            enqueue(cl.lits[0], id);
        } else if (cl.lits.size() >= 2) {
            watches[lit_index(cl.lits[0])].push_back(id);
            watches[lit_index(cl.lits[1])].push_back(id);
        }
        return id;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int lit = trail[qhead++];
            std::vector<int>& ws = watches[lit_index(-lit)];
            size_t i = 0;
            size_t j = 0;
            while (i < ws.size()) {
                int ci = ws[i++];
                Clause& c = clauses[ci];
                if (c.deleted) continue;
                if (c.lits.size() == 1) {
                    ws[j++] = ci;
                    if (value_lit(c.lits[0]) < 0) {
                        while (i < ws.size()) ws[j++] = ws[i++];
                        ws.resize(j);
                        return ci;
                    }
                    continue;
                }
                if (c.lits[0] == -lit) std::swap(c.lits[0], c.lits[1]);
                int other = c.lits[0];
                int other_val = value_lit(other);
                if (other_val > 0) {
                    ws[j++] = ci;
                    continue;
                }
                if (c.lits.size() == 2) {
                    ws[j++] = ci;
                    if (other_val < 0) {
                        while (i < ws.size()) ws[j++] = ws[i++];
                        ws.resize(j);
                        return ci;
                    }
                    if (!enqueue(other, ci)) {
                        while (i < ws.size()) ws[j++] = ws[i++];
                        ws.resize(j);
                        return ci;
                    }
                    continue;
                }
                bool found = false;
                for (size_t k = 2; k < c.lits.size(); ++k) {
                    if (value_lit(c.lits[k]) >= 0) {
                        int nl = c.lits[k];
                        c.lits[k] = c.lits[1];
                        c.lits[1] = nl;
                        watches[lit_index(nl)].push_back(ci);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[j++] = ci;
                if (value_lit(other) < 0) {
                    while (i < ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return ci;
                }
                if (!enqueue(other, ci)) {
                    while (i < ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return ci;
                }
            }
            ws.resize(j);
        }
        return -1;
    }

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

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

    void bump_var(int v) {
        activity[v] += var_inc;
        order.push({activity[v], v});
        if (activity[v] > 1e100) {
            for (int i = 1; i <= nvars; ++i) activity[i] *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    void decay_activity() {
        var_inc *= (1.0 / var_decay);
    }

    void write_clause_rup_now(const std::vector<int>& lits) {
        if (!proof_ok) return;
        proof << "rup ";
        for (int lit : lits) {
            proof << "1 " << (lit < 0 ? "~x" : "x") << std::abs(lit) << ' ';
        }
        proof << ">= 1 ;\n";
        if (!proof) proof_ok = false;
    }

    void write_delete(long long id) {
        if (!proof_ok || id <= 0) return;
        proof << "del id " << id << " ;\n";
        if (!proof) proof_ok = false;
    }

    int analyze(int confl, std::vector<int>& out) {
        out.clear();
        out.push_back(0);
        int path_c = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int current = (int)trail_lim.size();
        int clause_id = confl;
        analyze_stack.clear();
        current_deps.clear();

        do {
            Clause& c = clauses[clause_id];
            if (c.learnt) current_deps.push_back(clause_id);
            for (int q : c.lits) {
                int v = std::abs(q);
                if (v == std::abs(p)) continue;
                if (!seen[v] && levels[v] > 0) {
                    seen[v] = 1;
                    analyze_stack.push_back(v);
                    bump_var(v);
                    if (levels[v] == current) {
                        ++path_c;
                    } else {
                        out.push_back(q);
                    }
                }
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) --idx;
            p = trail[idx--];
            clause_id = reasons[std::abs(p)];
            seen[std::abs(p)] = 0;
            --path_c;
        } while (path_c > 0 && clause_id >= 0);

        out[0] = -p;
        uint32_t abstract = 0;
        for (int lit : out) {
            int lvl = levels[std::abs(lit)];
            abstract |= 1u << (lvl & 31);
        }

        int write = 1;
        for (int i = 1; i < (int)out.size(); ++i) {
            if (!lit_redundant(out[i], abstract)) {
                out[write++] = out[i];
            }
        }
        out.resize(write);

        int back = 0;
        if (out.size() > 1) {
            int best = 1;
            for (int i = 2; i < (int)out.size(); ++i) {
                if (levels[std::abs(out[i])] > levels[std::abs(out[best])]) best = i;
            }
            std::swap(out[1], out[best]);
            back = levels[std::abs(out[1])];
        }
        for (int v : analyze_stack) seen[v] = 0;
        decay_activity();
        return back;
    }

    bool lit_redundant(int lit, uint32_t abstract) {
        int v0 = std::abs(lit);
        int r0 = reasons[v0];
        if (r0 < 0) return false;
        temp_seen.clear();
        std::vector<int> local_deps;
        std::vector<int> stack;
        stack.push_back(v0);
        for (size_t top = 0; top < stack.size(); ++top) {
            int v = stack[top];
            int rid = reasons[v];
            if (rid < 0) {
                clear_temp_seen();
                return false;
            }
            if (clauses[rid].learnt) local_deps.push_back(rid);
            const Clause& c = clauses[rid];
            for (int q : c.lits) {
                int u = std::abs(q);
                if (u == v) continue;
                int lvl = levels[u];
                if (lvl == 0 || seen[u]) continue;
                if (reasons[u] >= 0 && (abstract & (1u << (lvl & 31)))) {
                    seen[u] = 2;
                    temp_seen.push_back(u);
                    stack.push_back(u);
                } else {
                    clear_temp_seen();
                    return false;
                }
            }
        }
        current_deps.insert(current_deps.end(), local_deps.begin(), local_deps.end());
        clear_temp_seen();
        return true;
    }

    void clear_temp_seen() {
        for (int v : temp_seen) {
            if (seen[v] == 2) seen[v] = 0;
        }
        temp_seen.clear();
    }

    int pick_branch_lit() {
        while (priority_pos < branch_priority.size()) {
            int lit = branch_priority[priority_pos++];
            if (value_lit(lit) == 0) return lit;
        }
        while (!order.empty()) {
            int v = order.top().second;
            double a = order.top().first;
            order.pop();
            if (assigns[v] == 0 && std::abs(a - activity[v]) < 1e-9) {
                return phase[v] > 0 ? v : -v;
            }
        }
        for (int v = 1; v <= nvars; ++v) {
            if (assigns[v] == 0) return phase[v] > 0 ? v : -v;
        }
        return 0;
    }

    void reduce_learnts() {
        std::vector<uint8_t> locked(clauses.size(), 0);
        for (int v = 1; v <= nvars; ++v) {
            if (reasons[v] >= 0 && reasons[v] < (int)clauses.size()) {
                locked[reasons[v]] = 1;
            }
        }
        std::vector<int> candidates;
        candidates.reserve(clauses.size() / 8);
        for (int i = original_clauses; i < (int)clauses.size(); ++i) {
            Clause& c = clauses[i];
            if (!c.learnt || c.deleted || locked[i] || c.lits.size() <= 2) continue;
            candidates.push_back(i);
        }
        if (candidates.size() < 20000) return;
        std::sort(candidates.begin(), candidates.end(), [&](int a, int b) {
            const Clause& ca = clauses[a];
            const Clause& cb = clauses[b];
            if (ca.lits.size() != cb.lits.size()) return ca.lits.size() > cb.lits.size();
            return ca.proof_id < cb.proof_id;
        });
        size_t limit = candidates.size() / 2;
        for (size_t k = 0; k < limit; ++k) {
            Clause& c = clauses[candidates[k]];
            c.deleted = true;
        }
    }

    void build_branch_priority() {
        branch_priority.clear();
        priority_pos = 0;
        std::vector<uint8_t> used(nvars + 1, 0);
        std::vector<std::vector<int>> and_inputs;
        bool expand_and_miters = (miter257 == 0);
        if (expand_and_miters) {
            std::vector<uint8_t> candidate(nvars + 1, 0);
            for (const auto& cl : miter_clauses) {
                for (int lit : cl) candidate[std::abs(lit)] = 1;
            }
            std::vector<std::vector<int>> bin_inputs(nvars + 1);
            std::vector<std::vector<int>> bigs(nvars + 1);
            for (int ci = 0; ci < original_clauses; ++ci) {
                const Clause& c = clauses[ci];
                for (int lit : c.lits) {
                    int z = std::abs(lit);
                    if (!candidate[z]) continue;
                    if (lit == -z && c.lits.size() == 2) {
                        int other = c.lits[0] == lit ? c.lits[1] : c.lits[0];
                        bin_inputs[z].push_back(other);
                    } else if (lit == z && c.lits.size() > 2) {
                        bigs[z].push_back(ci);
                    }
                }
            }
            and_inputs.assign(nvars + 1, {});
            for (int z = 1; z <= nvars; ++z) {
                if (!candidate[z] || bin_inputs[z].empty()) continue;
                std::vector<int> sorted_inputs = bin_inputs[z];
                std::sort(sorted_inputs.begin(), sorted_inputs.end());
                sorted_inputs.erase(std::unique(sorted_inputs.begin(), sorted_inputs.end()), sorted_inputs.end());
                for (int ci : bigs[z]) {
                    const Clause& c = clauses[ci];
                    if (c.lits.size() != sorted_inputs.size() + 1) continue;
                    std::vector<int> others;
                    others.reserve(c.lits.size() - 1);
                    bool ok = true;
                    for (int lit : c.lits) {
                        if (lit == z) continue;
                        if (std::abs(lit) == z) {
                            ok = false;
                            break;
                        }
                        others.push_back(lit);
                    }
                    if (!ok) continue;
                    std::sort(others.begin(), others.end());
                    std::vector<int> neg_inputs;
                    neg_inputs.reserve(sorted_inputs.size());
                    for (int lit : sorted_inputs) neg_inputs.push_back(-lit);
                    std::sort(neg_inputs.begin(), neg_inputs.end());
                    if (others == neg_inputs) {
                        and_inputs[z] = std::move(sorted_inputs);
                        break;
                    }
                }
            }
        }
        auto add_priority = [&](int lit) {
            if (value_lit(lit) == 0 && !used[std::abs(lit)]) {
                used[std::abs(lit)] = 1;
                branch_priority.push_back(lit);
            }
        };
        for (const auto& cl : miter_clauses) {
            bool satisfied = false;
            int live = 0;
            for (int lit : cl) {
                int val = value_lit(lit);
                if (val > 0) {
                    satisfied = true;
                    break;
                }
                if (val == 0) ++live;
            }
            if (satisfied || live == 0) continue;
            for (int lit : cl) {
                if (expand_and_miters && lit < 0 && !and_inputs.empty()) {
                    const std::vector<int>& inputs = and_inputs[std::abs(lit)];
                    for (int inp : inputs) add_priority(-inp);
                    if (!inputs.empty()) continue;
                }
                add_priority(lit);
            }
        }
        const char* mode = std::getenv("LYMPHOSAT_PRIORITY_MODE");
        if (!mode) return;
        if (std::strcmp(mode, "reverse") == 0) {
            std::reverse(branch_priority.begin(), branch_priority.end());
        } else if (std::strcmp(mode, "activity-desc") == 0) {
            std::stable_sort(branch_priority.begin(), branch_priority.end(), [&](int a, int b) {
                return activity[std::abs(a)] > activity[std::abs(b)];
            });
        } else if (std::strcmp(mode, "activity-asc") == 0) {
            std::stable_sort(branch_priority.begin(), branch_priority.end(), [&](int a, int b) {
                return activity[std::abs(a)] < activity[std::abs(b)];
            });
        } else if (std::strcmp(mode, "negate") == 0) {
            for (int& lit : branch_priority) lit = -lit;
        }
    }

    bool all_assigned() const {
        for (int v = 1; v <= nvars; ++v) {
            if (assigns[v] == 0) return false;
        }
        return true;
    }

    int solve() {
        int confl = propagate();
        if (confl != -1) {
            write_unsat_proof(confl);
            return 20;
        }
        if (nvars > 60000) build_branch_priority();
        std::vector<int> learnt;
        while (true) {
            if (timed_out()) return 0;
            confl = propagate();
            if (confl != -1) {
                ++conflicts;
                if (trail_lim.empty()) {
                    write_unsat_proof(confl);
                    return 20;
                }
                int back = analyze(confl, learnt);
                std::sort(current_deps.begin(), current_deps.end());
                current_deps.erase(std::unique(current_deps.begin(), current_deps.end()), current_deps.end());
                long long proof_id = next_proof_id++;
                cancel_until(back);
                int cid = add_clause(learnt, true, proof_id);
                clauses[cid].deps = current_deps;
                enqueue(learnt[0], cid);
                if (conflicts >= next_restart) {
                    cancel_until(0);
                    next_restart = conflicts + 100 + conflicts / 2;
                }
            } else {
                int lit = pick_branch_lit();
                if (lit == 0 || all_assigned()) return 10;
                new_decision_level();
                enqueue(lit, -1);
            }
        }
    }

    void mark_needed(int ci, std::vector<uint8_t>& needed) {
        if (ci < original_clauses || ci < 0 || ci >= (int)clauses.size()) return;
        Clause& c = clauses[ci];
        if (!c.learnt || needed[ci]) return;
        needed[ci] = 1;
        for (int dep : c.deps) mark_needed(dep, needed);
    }

    void write_unsat_proof(int confl) {
        if (!proof_ok) return;
        std::vector<uint8_t> needed(clauses.size(), 0);
        mark_needed(confl, needed);
        for (int lit : trail) {
            int r = reasons[std::abs(lit)];
            mark_needed(r, needed);
        }
        for (int i = original_clauses; i < (int)clauses.size(); ++i) {
            if (needed[i]) {
                ++proof_needed;
                write_clause_rup_now(clauses[i].lits);
            }
        }
        proof << "rup >= 1 ;\n";
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.flush();
    }

    bool check_model() const {
        for (const Clause& c : clauses) {
            bool sat = false;
            for (int lit : c.lits) {
                int8_t a = assigns[std::abs(lit)];
                if (a == 0) a = 1;
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    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;
    }
    Solver s(argv[2]);
    if (!s.scan_shape(argv[1])) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    if (!s.supported_shape()) {
        s.proof.close();
        std::ofstream clear(argv[2], std::ios::trunc);
        std::printf("s UNKNOWN\n");
        return 0;
    }
    if (!s.parse(argv[1])) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    int res = s.solve();
    if (std::getenv("LYMPHOSAT_STATS")) {
        std::fprintf(stderr,
                     "stats vars=%d original=%d clauses=%zu learnt=%zu conflicts=%lld proof_needed=%lld trail=%zu\n",
                     s.nvars,
                     s.original_clauses,
                     s.clauses.size(),
                     s.clauses.size() >= (size_t)s.original_clauses ? s.clauses.size() - (size_t)s.original_clauses : 0,
                     s.conflicts,
                     s.proof_needed,
                     s.trail.size());
    }
    if (res == 20) {
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }
    if (res == 10) {
        if (!s.check_model()) {
            std::printf("s UNKNOWN\n");
            return 0;
        }
        std::printf("s SATISFIABLE\n");
        std::printf("v ");
        int col = 2;
        for (int v = 1; v <= s.nvars; ++v) {
            int lit = (s.assigns[v] >= 0) ? v : -v;
            std::printf("%d ", lit);
            if (++col >= 16) {
                std::printf("0\nv ");
                col = 1;
            }
        }
        std::printf("0\n");
        return 10;
    }
    std::printf("s UNKNOWN\n");
    s.proof.close();
    std::ofstream clear(argv[2], std::ios::trunc);
    return 0;
}
