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

using Clock = std::chrono::steady_clock;

struct Formula {
    int n = 0;
    std::vector<std::vector<int>> clauses;
    std::vector<std::vector<int>> occ_pos, occ_neg;
};

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

static bool parse_dimacs(const char* path, Formula& F) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    int declared_m = -1;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            in >> fmt >> F.n >> declared_m;
            if (fmt != "cnf" || F.n < 0 || declared_m < 0) return false;
        } else if (tok == "%") {
            break;
        } else {
            int lit = std::stoi(tok);
            std::vector<int> clause;
            while (lit != 0) {
                if (lit_index(lit) > F.n) return false;
                clause.push_back(lit);
                if (!(in >> lit)) return false;
            }
            F.clauses.push_back(std::move(clause));
        }
    }
    if (F.n <= 0) return false;
    F.occ_pos.assign(F.n + 1, {});
    F.occ_neg.assign(F.n + 1, {});
    for (int i = 0; i < (int)F.clauses.size(); ++i) {
        for (int lit : F.clauses[i]) {
            if (lit > 0) F.occ_pos[lit].push_back(i);
            else F.occ_neg[-lit].push_back(i);
        }
    }
    return true;
}

static bool check_model(const Formula& F, const std::vector<int8_t>& val) {
    for (const auto& cl : F.clauses) {
        bool sat = false;
        for (int lit : cl) {
            int v = lit_index(lit);
            if (val[v] == lit_sign(lit)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void print_model(const Formula& F, std::vector<int8_t> val) {
    for (int v = 1; v <= F.n; ++v) {
        if (val[v] == 0) val[v] = 1;
    }
    std::printf("s SATISFIABLE\n");
    std::printf("v ");
    int col = 2;
    for (int v = 1; v <= F.n; ++v) {
        int lit = val[v] > 0 ? v : -v;
        std::printf("%d ", lit);
        if (++col >= 12) {
            std::printf("0\nv ");
            col = 1;
        }
    }
    std::printf("0\n");
}

struct ProofDPLL {
    const Formula& F;
    std::vector<int8_t> val;
    std::vector<int> trail;
    std::vector<int> decisions;
    std::vector<int> order;
    std::vector<std::vector<int>> proof_clauses;
    Clock::time_point start;
    double time_limit = 520.0;
    int64_t nodes = 0;
    int64_t node_limit = 2000000;
    int64_t proof_limit = 2500000;
    bool cutoff = false;
    bool sat_found = false;
    std::vector<int8_t> sat_model;

    explicit ProofDPLL(const Formula& formula) : F(formula), val(F.n + 1, 0) {
        std::vector<int> deg(F.n + 1, 0);
        for (int v = 1; v <= F.n; ++v) {
            deg[v] = (int)F.occ_pos[v].size() + (int)F.occ_neg[v].size();
        }
        order.resize(F.n);
        std::iota(order.begin(), order.end(), 1);
        std::stable_sort(order.begin(), order.end(), [&](int a, int b) {
            return deg[a] > deg[b];
        });
    }

    bool assign_lit(int lit) {
        int v = lit_index(lit), s = lit_sign(lit);
        if (val[v] != 0) return val[v] == s;
        val[v] = (int8_t)s;
        trail.push_back(lit);
        return true;
    }

    void backtrack(size_t mark) {
        while (trail.size() > mark) {
            int v = lit_index(trail.back());
            trail.pop_back();
            val[v] = 0;
        }
    }

    bool propagate_from(size_t head) {
        while (head < trail.size()) {
            int lit = trail[head++];
            const auto& occ = lit > 0 ? F.occ_neg[lit] : F.occ_pos[-lit];
            for (int ci : occ) {
                const auto& cl = F.clauses[ci];
                bool sat = false;
                int unassigned = 0;
                int last_unassigned = 0;
                for (int l : cl) {
                    int v = lit_index(l);
                    int8_t a = val[v];
                    if (a == lit_sign(l)) {
                        sat = true;
                        break;
                    }
                    if (a == 0) {
                        ++unassigned;
                        last_unassigned = l;
                    }
                }
                if (sat) continue;
                if (unassigned == 0) return false;
                if (unassigned == 1) {
                    if (!assign_lit(last_unassigned)) return false;
                }
            }
        }
        return true;
    }

    bool all_satisfied() const {
        for (const auto& cl : F.clauses) {
            bool sat = false;
            for (int lit : cl) {
                if (val[lit_index(lit)] == lit_sign(lit)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

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

    void emit_decision_clause() {
        std::vector<int> cl;
        cl.reserve(decisions.size());
        for (int lit : decisions) cl.push_back(-lit);
        proof_clauses.push_back(std::move(cl));
        if ((int64_t)proof_clauses.size() > proof_limit) cutoff = true;
    }

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

    bool search(size_t prop_head) {
        if (++nodes > node_limit || time_exceeded()) {
            cutoff = true;
            return false;
        }
        size_t mark = trail.size();
        if (!propagate_from(prop_head)) {
            emit_decision_clause();
            backtrack(mark);
            return true;
        }
        if (all_satisfied()) {
            sat_found = true;
            sat_model = val;
            backtrack(mark);
            return false;
        }
        int v = choose_var();
        if (v == 0) {
            sat_found = true;
            sat_model = val;
            backtrack(mark);
            return false;
        }

        for (int lit : {v, -v}) {
            size_t child_mark = trail.size();
            decisions.push_back(lit);
            bool ok_assign = assign_lit(lit);
            bool child_unsat = ok_assign && search(child_mark);
            decisions.pop_back();
            backtrack(child_mark);
            if (!child_unsat) {
                backtrack(mark);
                return false;
            }
            if (cutoff) {
                backtrack(mark);
                return false;
            }
        }
        emit_decision_clause();
        backtrack(mark);
        return !cutoff;
    }

    enum Result { UNSAT, SAT, UNKNOWN };

    Result run(double seconds) {
        time_limit = seconds;
        start = Clock::now();
        size_t mark = trail.size();
        for (int i = 0; i < (int)F.clauses.size(); ++i) {
            if (F.clauses[i].empty()) {
                proof_clauses.push_back({});
                return UNSAT;
            }
            if (F.clauses[i].size() == 1) {
                if (!assign_lit(F.clauses[i][0])) {
                    proof_clauses.push_back({});
                    return UNSAT;
                }
            }
        }
        bool root_unsat = search(mark);
        if (sat_found) return SAT;
        if (root_unsat && !cutoff) return UNSAT;
        return UNKNOWN;
    }
};

static void write_proof(const char* path, const std::vector<std::vector<int>>& clauses) {
    std::ofstream out(path);
    out << "pseudo-Boolean proof version 3.0\n";
    for (const auto& cl : clauses) {
        out << "rup";
        for (int lit : cl) {
            out << " 1 " << (lit > 0 ? "x" : "~x") << (lit > 0 ? lit : -lit);
        }
        out << " >= 1 ;\n";
    }
    out << "output NONE ;\n";
    out << "conclusion UNSAT : -1 ;\n";
    out << "end pseudo-Boolean proof ;\n";
}

static bool greedy_sls(const Formula& F, std::vector<int8_t>& model, double seconds) {
    auto start = Clock::now();
    std::vector<int8_t> val(F.n + 1, 1);
    for (const auto& cl : F.clauses) {
        if (cl.size() == 1) val[lit_index(cl[0])] = (int8_t)lit_sign(cl[0]);
    }
    std::mt19937_64 rng(0x5eed12345678ULL ^ (uint64_t)F.n ^ ((uint64_t)F.clauses.size() << 21));
    std::vector<int> unsat;
    auto rebuild_unsat = [&]() {
        unsat.clear();
        for (int i = 0; i < (int)F.clauses.size(); ++i) {
            bool sat = false;
            for (int lit : F.clauses[i]) {
                if (val[lit_index(lit)] == lit_sign(lit)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) unsat.push_back(i);
        }
    };
    rebuild_unsat();
    if (unsat.empty()) {
        model = val;
        return true;
    }
    int64_t flips = 0;
    while (std::chrono::duration<double>(Clock::now() - start).count() < seconds) {
        if (unsat.empty()) {
            model = val;
            return true;
        }
        int ci = unsat[rng() % unsat.size()];
        const auto& cl = F.clauses[ci];
        int best_lit = cl[rng() % cl.size()];
        int best_score = -1000000000;
        for (int lit : cl) {
            int v = lit_index(lit);
            if (F.clauses[ci].size() == 1) continue;
            int score = 0;
            const auto& pos = F.occ_pos[v];
            const auto& neg = F.occ_neg[v];
            auto eval_clause_after = [&](int cj) {
                bool before = false, after = false;
                for (int l : F.clauses[cj]) {
                    int vv = lit_index(l);
                    int8_t a = val[vv];
                    if (a == lit_sign(l)) before = true;
                    if (vv == v) a = (int8_t)-a;
                    if (a == lit_sign(l)) after = true;
                }
                return (after ? 1 : 0) - (before ? 1 : 0);
            };
            for (int cj : pos) score += eval_clause_after(cj);
            for (int cj : neg) score += eval_clause_after(cj);
            if (score > best_score || (score == best_score && (rng() & 1))) {
                best_score = score;
                best_lit = lit;
            }
        }
        int v = lit_index(best_lit);
        val[v] = (int8_t)-val[v];
        if ((++flips & 1023) == 0) {
            rebuild_unsat();
            if (unsat.empty() && check_model(F, val)) {
                model = val;
                return true;
            }
        }
    }
    return false;
}

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\ns UNKNOWN\n");
        return 0;
    }
    std::printf("c parsed vars=%d clauses=%zu\n", F.n, F.clauses.size());

    auto try_dpll = [&](double seconds) -> int {
        ProofDPLL dpll(F);
        if (F.n > 1000) {
            dpll.node_limit = 300000;
            dpll.proof_limit = 400000;
        }
        auto res = dpll.run(seconds);
        std::printf("c dpll nodes=%lld proof_lines=%zu cutoff=%d\n",
                    (long long)dpll.nodes, dpll.proof_clauses.size(), dpll.cutoff ? 1 : 0);
        if (res == ProofDPLL::SAT && check_model(F, dpll.sat_model)) {
            print_model(F, dpll.sat_model);
            return 10;
        }
        if (res == ProofDPLL::UNSAT) {
            write_proof(argv[2], dpll.proof_clauses);
            std::printf("s UNSATISFIABLE\n");
            return 20;
        }
        return 0;
    };

    if (F.n <= 1000) {
        int rc = try_dpll(120.0);
        if (rc != 0) return rc;
    }

    std::vector<int8_t> sls_model;
    double sls_seconds = F.n <= 100000 ? 12.0 : 6.0;
    if (greedy_sls(F, sls_model, sls_seconds) && check_model(F, sls_model)) {
        std::printf("c stochastic/local repair found a checked model\n");
        print_model(F, sls_model);
        return 10;
    }

    if (F.n > 1000 && F.n <= 30000) {
        int rc = try_dpll(540.0);
        if (rc != 0) return rc;
    } else {
        std::printf("c proof dpll skipped for large instance\n");
    }

    std::printf("s UNKNOWN\n");
    return 0;
}
