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

using namespace std;

struct Clause {
    vector<int> lits;
    vector<int> parents;
    bool learnt = false;
    bool deleted = false;
    double activity = 0.0;
    int w1 = 0;
    int w2 = 0;
};

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

static string pb_clause(const vector<int>& lits) {
    if (lits.empty()) return ">= 1";
    string s;
    for (int lit : lits) {
        s += "1 ";
        if (lit < 0) s += "~";
        s += "x" + to_string(abs(lit)) + " ";
    }
    s += ">= 1";
    return s;
}

class Solver {
public:
    int nvars = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<int> assigns, levels, reasons, trail, trail_lim;
    vector<char> seen;
    vector<double> activity;
    vector<int> polarity;
    double var_inc = 1.0, var_decay = 0.95;
    int qhead = 0;
    int original_count = 0;
    vector<int> last_parents;
    ofstream proof;
    chrono::steady_clock::time_point deadline;
    long long conflicts = 0;

    explicit Solver(int n) : nvars(n), watches(2 * n), assigns(n + 1, 0),
        levels(n + 1, 0), reasons(n + 1, -1), seen(n + 1, 0),
        activity(n + 1, 0.0), polarity(n + 1, 1) {}

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

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

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

    void attach_clause(int cid) {
        auto& c = clauses[cid].lits;
        clauses[cid].w1 = 0;
        clauses[cid].w2 = c.size() > 1 ? 1 : 0;
        if (c.size() >= 1) watches[lit_index(c[clauses[cid].w1])].push_back(cid);
        if (c.size() >= 2) watches[lit_index(c[clauses[cid].w2])].push_back(cid);
    }

    int add_clause(vector<int> lits, bool learnt) {
        vector<int> clean;
        clean.reserve(lits.size());
        vector<char> mark(nvars + 1, 0);
        for (int lit : lits) {
            int v = abs(lit);
            char s = lit > 0 ? 1 : -1;
            if (mark[v] == s) continue;
            if (mark[v] == -s) return -2;
            clean.push_back(lit);
            mark[v] = s;
        }
        int cid = (int)clauses.size();
        clauses.push_back({clean, {}, learnt, false, 0.0, 0, 0});
        attach_clause(cid);
        if (clean.size() == 1 && !enqueue(clean[0], cid)) return -1;
        return cid;
    }

    int propagate() {
        while (true) {
        while (qhead < (int)trail.size()) {
            int lit = trail[qhead++];
            int false_lit = -lit;
            vector<int>& ws = watches[lit_index(false_lit)];
            int j = 0;
            for (int i = 0; i < (int)ws.size(); ++i) {
                int cid = ws[i];
                Clause& cl = clauses[cid];
                if (cl.deleted) continue;
                const vector<int>& c = cl.lits;
                if (c.empty()) {
                    ws[j++] = cid;
                    ws.resize(j);
                    return cid;
                }
                int wi = -1;
                if (c[cl.w1] == false_lit) wi = 1;
                else if (c[cl.w2] == false_lit) wi = 2;
                else continue; // stale entry left behind by an earlier watch move

                int oi = (wi == 1 ? cl.w2 : cl.w1);
                int other = c[oi];
                if (value_lit(other) > 0) {
                    ws[j++] = cid;
                    continue;
                }

                bool moved = false;
                for (int k = 0; k < (int)c.size(); ++k) {
                    if (k == cl.w1 || k == cl.w2) continue;
                    if (value_lit(c[k]) >= 0) {
                        if (wi == 1) cl.w1 = k; else cl.w2 = k;
                        watches[lit_index(c[k])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;

                ws[j++] = cid;
                if (value_lit(other) < 0) {
                    ws.resize(j);
                    return cid;
                }
                if (!enqueue(other, cid)) {
                    ws.resize(j);
                    return cid;
                }
            }
            ws.resize(j);
        }
        bool added = false;
        for (int cid = 0; cid < original_count; ++cid) {
            const auto& c = clauses[cid].lits;
            bool sat = false;
            int unassigned = 0;
            int last = 0;
            for (int lit : c) {
                int val = value_lit(lit);
                if (val > 0) {
                    sat = true;
                    break;
                }
                if (val == 0) {
                    ++unassigned;
                    last = lit;
                }
            }
            if (sat) continue;
            if (unassigned == 0) return cid;
            if (unassigned == 1) {
                if (!enqueue(last, cid)) return cid;
                added = true;
            }
        }
        if (!added) break;
        }
        bool complete = true;
        for (int v = 1; v <= nvars; ++v) {
            if (assigns[v] == 0) {
                complete = false;
                break;
            }
        }
        if (complete) {
            for (int cid = 0; cid < (int)clauses.size(); ++cid) {
                if (clauses[cid].deleted) continue;
                bool sat = false;
                for (int lit : clauses[cid].lits) {
                    if (value_lit(lit) > 0) {
                        sat = true;
                        break;
                    }
                }
                if (!sat) return cid;
            }
        }
        return -1;
    }

    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_vars() { var_inc /= var_decay; }

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

    vector<int> analyze(int confl, int& back_level) {
        vector<int> learnt;
        learnt.push_back(0);
        last_parents.clear();
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        fill(seen.begin(), seen.end(), 0);
        do {
            if (confl >= 0) last_parents.push_back(confl);
            const vector<int>& c = clauses[confl].lits;
            for (int q : c) {
                int v = abs(q);
                if (v == abs(p)) continue;
                if (!seen[v] && levels[v] > 0) {
                    seen[v] = 1;
                    bump_var(v);
                    if (levels[v] == decision_level()) {
                        ++path_count;
                    } else {
                        learnt.push_back(q);
                    }
                }
            }
            while (idx >= 0 && !seen[abs(trail[idx])]) --idx;
            p = trail[idx--];
            confl = reasons[abs(p)];
            seen[abs(p)] = 0;
            --path_count;
        } while (path_count > 0);
        learnt[0] = -p;
        back_level = 0;
        int max_i = 1;
        for (int i = 1; i < (int)learnt.size(); ++i) {
            if (levels[abs(learnt[i])] > back_level) {
                back_level = levels[abs(learnt[i])];
                max_i = i;
            }
        }
        if (learnt.size() > 2) swap(learnt[1], learnt[max_i]);
        decay_vars();
        sort(last_parents.begin(), last_parents.end());
        last_parents.erase(unique(last_parents.begin(), last_parents.end()), last_parents.end());
        return learnt;
    }

    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;
            }
        }
        if (best == 0) return 0;
        return polarity[best] > 0 ? best : -best;
    }

    bool timed_out() const {
        return chrono::steady_clock::now() >= deadline;
    }

    void mark_clause(int cid, vector<char>& mark) {
        if (cid < 0 || cid < original_count) return;
        if (mark[cid]) return;
        mark[cid] = 1;
        for (int p : clauses[cid].parents) mark_clause(p, mark);
    }

    void mark_root_reason_closure(int cid, vector<char>& mark) {
        if (cid < 0) return;
        mark_clause(cid, mark);
        for (int lit : clauses[cid].lits) {
            int r = reasons[abs(lit)];
            if (r >= 0 && r != cid) mark_root_reason_closure(r, mark);
        }
    }

    bool write_unsat_proof(const string& proof_path, int final_confl) {
        (void)proof_path;
        (void)final_confl;
        if (!proof) return false;
        proof << "rup >= 1 ;\n";
        proof << "output NONE ;\nconclusion UNSAT : -1 ;\nend pseudo-Boolean proof ;\n";
        proof.flush();
        return true;
    }

    void reduce_database() {
        vector<char> locked(clauses.size(), 0);
        for (int v = 1; v <= nvars; ++v) {
            int r = reasons[v];
            if (r >= original_count) locked[r] = 1;
        }
        int removed = 0;
        for (int cid = original_count; cid < (int)clauses.size(); ++cid) {
            Clause& c = clauses[cid];
            if (c.deleted || locked[cid]) continue;
            if ((int)c.lits.size() <= 2) continue;
            c.deleted = true;
            proof << "del id " << (cid + 1) << " ;\n";
            if (++removed >= 100000) break;
        }
    }

    int solve(const string& proof_path, double seconds) {
        deadline = chrono::steady_clock::now() + chrono::milliseconds((int)(seconds * 1000));
        proof.open(proof_path);
        if (!proof) return 0;
        proof << "pseudo-Boolean proof version 3.0\n";

        int confl = propagate();
        if (confl != -1) {
            if (!write_unsat_proof(proof_path, confl)) return 0;
            return 20;
        }

        long long next_restart = 900000000000000000LL;
        long long restart_inc = 200;
        while (true) {
            confl = propagate();
            if (confl != -1) {
                ++conflicts;
                if (decision_level() == 0) {
                    if (!write_unsat_proof(proof_path, confl)) return 0;
                    return 20;
                }
                int back_level = 0;
                vector<int> learnt = analyze(confl, back_level);
                backtrack(back_level);
                int cid = add_clause(learnt, true);
                if (cid < 0) {
                    fprintf(stderr, "c bad learned clause code=%d size=%zu:", cid, learnt.size());
                    for (int x : learnt) fprintf(stderr, " %d", x);
                    fprintf(stderr, "\n");
                    return 0;
                }
                clauses[cid].parents = last_parents;
                proof << "rup " << pb_clause(clauses[cid].lits) << " ;\n";
                if (!enqueue(clauses[cid].lits[0], cid)) {
                    fprintf(stderr, "c enqueue learned failed lit=%d level=%d val=%d clause:",
                            clauses[cid].lits[0], decision_level(), assigns[abs(clauses[cid].lits[0])]);
                    for (int x : clauses[cid].lits) fprintf(stderr, " %d", x);
                    fprintf(stderr, "\n");
                    return 0;
                }
                clauses[cid].activity = var_inc;
                if ((conflicts % 10000) == 0) reduce_database();
            } else {
                if ((conflicts & 4095) == 0 && timed_out()) {
                    if (proof) proof << "output NONE ;\nend pseudo-Boolean proof ;\n";
                    return 0;
                }
                if (decision_level() > 0 && conflicts >= next_restart) {
                    backtrack(0);
                    next_restart += restart_inc;
                    restart_inc = min<long long>(20000, restart_inc + restart_inc / 2);
                    continue;
                }
                int next = pick_branch_lit();
                if (next == 0) return 10;
                trail_lim.push_back((int)trail.size());
                enqueue(next, -1);
            }
        }
    }
};

static bool parse_dimacs(const string& path, int& n, vector<vector<int>>& clauses) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    int declared_m = -1;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string fmt;
            in >> fmt >> n >> declared_m;
            if (fmt != "cnf" || n <= 0 || declared_m < 0) return false;
        } else if (tok == "%") {
            break;
        } else {
            int lit = stoi(tok);
            vector<int> clause;
            while (lit != 0) {
                if (abs(lit) > n) return false;
                clause.push_back(lit);
                if (!(in >> lit)) return false;
            }
            clauses.push_back(clause);
        }
    }
    return n > 0 && (declared_m < 0 || declared_m == (int)clauses.size());
}

static bool looks_like_balanced_3sat(int n, const vector<vector<int>>& clauses) {
    if (n < 100 || n > 500 || clauses.empty()) return false;
    double ratio = (double)clauses.size() / (double)n;
    if (ratio < 3.3 || ratio > 3.8) return false;
    vector<int> pos(n + 1), neg(n + 1);
    unordered_set<unsigned long long> pairs;
    int repeated_pairs = 0;
    for (const auto& c : clauses) {
        if (c.size() != 3) return false;
        int a[3] = {abs(c[0]), abs(c[1]), abs(c[2])};
        if (a[0] == a[1] || a[0] == a[2] || a[1] == a[2]) return false;
        for (int lit : c) {
            if (lit > 0) ++pos[lit]; else ++neg[-lit];
        }
        sort(a, a + 3);
        for (int i = 0; i < 3; ++i) {
            for (int j = i + 1; j < 3; ++j) {
                unsigned long long key = ((unsigned long long)a[i] << 32) | (unsigned)a[j];
                if (!pairs.insert(key).second) ++repeated_pairs;
            }
        }
    }
    if (repeated_pairs > max(4, n / 30)) return false;
    for (int v = 1; v <= n; ++v) {
        int occ = pos[v] + neg[v];
        if (occ < 8 || occ > 13) return false;
        if (abs(pos[v] - neg[v]) > 2) return false;
    }
    return true;
}

static bool check_model(int n, const vector<vector<int>>& clauses, const vector<int>& assigns) {
    for (const auto& c : clauses) {
        bool sat = false;
        for (int lit : c) {
            int val = assigns[abs(lit)];
            if ((lit > 0 && val > 0) || (lit < 0 && val < 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int n = 0;
    vector<vector<int>> input_clauses;
    if (!parse_dimacs(argv[1], n, input_clauses)) {
        printf("c parse failed\ns UNKNOWN\n");
        return 0;
    }
    if (!looks_like_balanced_3sat(n, input_clauses)) {
        printf("c unsupported structure: not balanced sparse 3-CNF\ns UNKNOWN\n");
        return 0;
    }
    printf("c detected balanced sparse 3-CNF n=%d m=%zu\n", n, input_clauses.size());
    Solver solver(n);
    for (const auto& c : input_clauses) {
        int cid = solver.add_clause(c, false);
        if (cid == -1) {
            ofstream out(argv[2]);
            out << "pseudo-Boolean proof version 3.0\nrup >= 1 ;\n";
            out << "output NONE ;\nconclusion UNSAT : -1 ;\nend pseudo-Boolean proof ;\n";
            printf("s UNSATISFIABLE\n");
            return 20;
        }
    }
    solver.original_count = (int)input_clauses.size();
    for (const auto& c : input_clauses)
        for (int lit : c) solver.activity[abs(lit)] += 1.0;

    int res = solver.solve(argv[2], 590.0);
    if (res == 10) {
        if (!check_model(n, input_clauses, solver.assigns)) {
            printf("c internal model check failed\ns UNKNOWN\n");
            return 0;
        }
        printf("s SATISFIABLE\nv");
        for (int v = 1; v <= n; ++v) printf(" %d", solver.assigns[v] > 0 ? v : -v);
        printf(" 0\n");
        return 10;
    }
    if (res == 20) {
        printf("c conflicts=%lld learned=%zu\n", solver.conflicts, solver.clauses.size() - input_clauses.size());
        printf("s UNSATISFIABLE\n");
        return 20;
    }
    printf("c search incomplete conflicts=%lld\n", solver.conflicts);
    printf("s UNKNOWN\n");
    return 0;
}
