#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
    bool learnt = false;
    bool removed = false;
};

static inline int lit_index(int lit) {
    int v = abs(lit) - 1;
    return 2 * v + (lit < 0);
}

struct Solver {
    int nvars = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<signed char> assigns;
    vector<int> levels;
    vector<int> reasons;
    vector<int> trail;
    vector<int> trail_lim;
    int qhead = 0;
    vector<double> activity;
    double var_inc = 1.0;
    double var_decay = 0.95;
    long long conflicts = 0;
    vector<vector<int>> proof_clauses;
    bool parse_ok = false;

    int value_lit(int lit) const {
        signed char v = assigns[abs(lit)];
        if (v == 0) return 0;
        bool val = v > 0;
        bool sign = lit > 0;
        return val == sign ? 1 : -1;
    }

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

    bool enqueue(int lit, int reason) {
        int v = abs(lit);
        signed char want = lit > 0 ? 1 : -1;
        if (assigns[v] != 0) return assigns[v] == want;
        assigns[v] = want;
        levels[v] = decision_level();
        reasons[v] = reason;
        trail.push_back(lit);
        return true;
    }

    int add_clause(vector<int> lits, bool learnt) {
        vector<int> out;
        out.reserve(lits.size());
        if (!learnt) {
            sort(lits.begin(), lits.end(), [](int a, int b) {
                int aa = abs(a), bb = abs(b);
                if (aa != bb) return aa < bb;
                return a < b;
            });
            int last = 0;
            for (int lit : lits) {
                if (lit == 0 || abs(lit) > nvars) continue;
                if (last != 0 && abs(last) == abs(lit)) {
                    if (last != lit) return -2; // tautology
                    continue;
                }
                out.push_back(lit);
                last = lit;
            }
        } else {
            vector<char> seen(nvars + 1, 0);
            for (int lit : lits) {
                int v = abs(lit);
                if (lit == 0 || v > nvars) continue;
                if (seen[v]) {
                    bool same_seen = false;
                    for (int x : out) {
                        if (abs(x) == v) {
                            same_seen = x == lit;
                            break;
                        }
                    }
                    if (!same_seen) return -2;
                    continue;
                }
                seen[v] = 1;
                out.push_back(lit);
            }
        }
        int id = (int)clauses.size();
        clauses.push_back({out, learnt, false});
        if (out.empty()) return id;
        if (out.size() == 1) {
            if (!enqueue(out[0], id)) return id;
        } else {
            watches[lit_index(out[0])].push_back(id);
            watches[lit_index(out[1])].push_back(id);
        }
        return id;
    }

    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++];
            int false_lit = -p;
            auto &ws = watches[lit_index(false_lit)];
            int i = 0;
            for (int j = 0; j < (int)ws.size(); ++j) {
                int ci = ws[j];
                Clause &c = clauses[ci];
                if (c.removed) continue;
                if (c.lits.size() < 2) {
                    ws[i++] = ci;
                    continue;
                }
                if (c.lits[0] != false_lit && c.lits[1] == false_lit) {
                    swap(c.lits[0], c.lits[1]);
                }
                if (c.lits[0] != false_lit) {
                    ws[i++] = ci;
                    continue;
                }
                int other = c.lits[1];
                if (value_lit(other) == 1) {
                    ws[i++] = ci;
                    continue;
                }
                bool found = false;
                for (int k = 2; k < (int)c.lits.size(); ++k) {
                    if (value_lit(c.lits[k]) != -1) {
                        c.lits[0] = c.lits[k];
                        c.lits[k] = false_lit;
                        watches[lit_index(c.lits[0])].push_back(ci);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[i++] = ci;
                if (value_lit(other) == -1) {
                    for (++j; j < (int)ws.size(); ++j) ws[i++] = ws[j];
                    ws.resize(i);
                    return ci;
                }
                if (!enqueue(other, ci)) {
                    for (++j; j < (int)ws.size(); ++j) ws[i++] = ws[j];
                    ws.resize(i);
                    return ci;
                }
            }
            ws.resize(i);
        }
        return -1;
    }

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

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

    void decay_activity() { var_inc /= var_decay; }

    void analyze(int confl, vector<int> &out_clause, int &out_btlevel) {
        vector<char> seen(nvars + 1, 0);
        out_clause.clear();
        out_clause.push_back(0);
        int pathC = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        Clause *c = &clauses[confl];
        do {
            for (int lit : c->lits) {
                int v = abs(lit);
                if (v == abs(p) || seen[v]) continue;
                seen[v] = 1;
                bump_var(v);
                if (levels[v] == decision_level()) {
                    ++pathC;
                } else if (levels[v] > 0) {
                    out_clause.push_back(lit);
                }
            }
            while (idx >= 0 && !seen[abs(trail[idx])]) --idx;
            p = trail[idx--];
            c = reasons[abs(p)] >= 0 ? &clauses[reasons[abs(p)]] : nullptr;
            seen[abs(p)] = 0;
            --pathC;
        } while (pathC > 0 && c != nullptr);
        out_clause[0] = -p;
        if (out_clause.size() == 1) {
            out_btlevel = 0;
        } else {
            int max_i = 1;
            for (int i = 2; i < (int)out_clause.size(); ++i) {
                if (levels[abs(out_clause[i])] > levels[abs(out_clause[max_i])]) max_i = i;
            }
            swap(out_clause[1], out_clause[max_i]);
            out_btlevel = levels[abs(out_clause[1])];
        }
        decay_activity();
    }

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

    bool has_binary(int a, int b) const {
        if (abs(a) > abs(b) || (abs(a) == abs(b) && a > b)) swap(a, b);
        for (int ci : watches[lit_index(a)]) {
            const Clause &c = clauses[ci];
            if (!c.removed && c.lits.size() == 2 &&
                ((c.lits[0] == a && c.lits[1] == b) || (c.lits[0] == b && c.lits[1] == a))) {
                return true;
            }
        }
        return false;
    }

    bool root_probe(int max_rounds = 1, int binary_cap = 0) {
        int binaries_added = 0;
        for (int round = 0; round < max_rounds; ++round) {
            bool changed = false;
            int root_keep = trail_lim.empty() ? (int)trail.size() : trail_lim[0];
            for (int v = 1; v <= nvars; ++v) {
                if (assigns[v] != 0) continue;
                for (int sign = 0; sign < 2; ++sign) {
                    if (assigns[v] != 0) break;
                    int lit = sign == 0 ? v : -v;
                    trail_lim.push_back((int)trail.size());
                    if (!enqueue(lit, -1)) {
                        cancel_until(0);
                        continue;
                    }
                    int confl = propagate();
                    if (confl >= 0) {
                        cancel_until(0);
                        vector<int> unit = {-lit};
                        proof_clauses.push_back(unit);
                        int cid = add_clause(unit, true);
                        if (cid >= 0 && !enqueue(unit[0], cid)) {
                            proof_clauses.push_back({});
                            return false;
                        }
                        if (propagate() >= 0) {
                            proof_clauses.push_back({});
                            return false;
                        }
                        changed = true;
                        break;
                    }
                    vector<int> implied;
                    if (binaries_added < binary_cap) {
                        for (int i = trail_lim.back(); i < (int)trail.size(); ++i) {
                            int t = trail[i];
                            if (t != lit) implied.push_back(t);
                        }
                    }
                    cancel_until(0);
                    for (int t : implied) {
                        if (binaries_added >= binary_cap) break;
                        vector<int> bin = {-lit, t};
                        if (has_binary(bin[0], bin[1])) continue;
                        proof_clauses.push_back(bin);
                        add_clause(bin, true);
                        ++binaries_added;
                        changed = true;
                    }
                    int c0 = propagate();
                    if (c0 >= 0) {
                        proof_clauses.push_back({});
                        return false;
                    }
                }
            }
            (void)root_keep;
            if (!changed) break;
        }
        return true;
    }

    bool solve() {
        int confl = propagate();
        if (confl >= 0) {
            proof_clauses.push_back({});
            return false;
        }
        if (!root_probe()) return false;
        int restart_limit = 100;
        int since_restart = 0;
        while (true) {
            confl = propagate();
            if (confl >= 0) {
                ++conflicts;
                ++since_restart;
                if (decision_level() == 0) {
                    proof_clauses.push_back({});
                    return false;
                }
                vector<int> learnt;
                int bt = 0;
                analyze(confl, learnt, bt);
                proof_clauses.push_back(learnt);
                cancel_until(bt);
                int cid = add_clause(learnt, true);
                if (cid >= 0 && !clauses[cid].lits.empty()) {
                    enqueue(clauses[cid].lits[0], cid);
                }
                if (since_restart >= restart_limit) {
                    cancel_until(0);
                    since_restart = 0;
                    restart_limit = (int)(restart_limit * 1.5) + 50;
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) return true;
                trail_lim.push_back((int)trail.size());
                enqueue(next, -1);
            }
        }
    }

    bool model_satisfies() const {
        for (const Clause &c : clauses) {
            if (c.learnt) continue;
            bool sat = false;
            for (int lit : c.lits) {
                int v = assigns[abs(lit)];
                if (v != 0 && ((v > 0) == (lit > 0))) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    bool parse_dimacs(const string &path) {
        ifstream in(path);
        if (!in) return false;
        string tok;
        int declared_m = 0;
        while (in >> tok) {
            if (tok == "c") {
                string line;
                getline(in, line);
            } else if (tok == "p") {
                string fmt;
                in >> fmt >> nvars >> declared_m;
                if (fmt != "cnf" || nvars <= 0) return false;
                watches.assign(2 * nvars + 2, {});
                assigns.assign(nvars + 1, 0);
                levels.assign(nvars + 1, 0);
                reasons.assign(nvars + 1, -1);
                activity.assign(nvars + 1, 0.0);
                break;
            }
        }
        if (nvars <= 0) return false;
        vector<int> lits;
        int lit;
        while (in >> lit) {
            if (lit == 0) {
                if (!lits.empty()) {
                    for (int x : lits) activity[abs(x)] += 1.0;
                    int cid = add_clause(lits, false);
                    if (cid >= 0 && clauses[cid].lits.empty()) {
                        proof_clauses.push_back({});
                    }
                    lits.clear();
                }
            } else {
                if (abs(lit) > nvars) return false;
                lits.push_back(lit);
            }
        }
        parse_ok = true;
        return true;
    }
};

static void write_clause_pbp(FILE *fp, const vector<int> &clause) {
    fputs("rup ", fp);
    for (int lit : clause) {
        if (lit > 0) {
            fprintf(fp, "1 x%d ", lit);
        } else {
            fprintf(fp, "1 ~x%d ", -lit);
        }
    }
    fputs(">= 1 ;\n", fp);
}

static bool write_proof(const string &path, const vector<vector<int>> &proof) {
    FILE *fp = fopen(path.c_str(), "w");
    if (!fp) return false;
    fputs("pseudo-Boolean proof version 3.0\n", fp);
    for (const auto &c : proof) write_clause_pbp(fp, c);
    fputs("output NONE ;\n", fp);
    fputs("conclusion UNSAT : -1 ;\n", fp);
    fputs("end pseudo-Boolean proof ;\n", fp);
    fclose(fp);
    return true;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Solver s;
    if (!s.parse_dimacs(argv[1])) {
        printf("c parse/detection failed\ns UNKNOWN\n");
        return 0;
    }
    printf("c parsed cnf vars=%d clauses=%zu\n", s.nvars, s.clauses.size());
    bool sat = s.solve();
    if (sat) {
        for (int v = 1; v <= s.nvars; ++v) {
            if (s.assigns[v] == 0) s.assigns[v] = 1;
        }
        if (!s.model_satisfies()) {
            printf("c internal model check failed\ns UNKNOWN\n");
            return 0;
        }
        printf("s SATISFIABLE\nv");
        for (int v = 1; v <= s.nvars; ++v) {
            printf(" %d", s.assigns[v] > 0 ? v : -v);
            if (v % 20 == 0 && v != s.nvars) printf("\nv");
        }
        printf(" 0\n");
        return 10;
    }
    if (s.proof_clauses.empty() || !s.proof_clauses.back().empty()) {
        s.proof_clauses.push_back({});
    }
    if (!write_proof(argv[2], s.proof_clauses)) {
        printf("c proof write failed\ns UNKNOWN\n");
        return 0;
    }
    printf("c cdcl conflicts=%lld proof_clauses=%zu\n", s.conflicts, s.proof_clauses.size());
    printf("s UNSATISFIABLE\n");
    return 20;
}
