#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <queue>
#include <sstream>
#include <string>
#include <vector>

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

struct Solver {
    int nvars = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> assign;
    std::vector<int8_t> phase;
    std::vector<int> level;
    std::vector<int> reason;
    std::vector<int> trail;
    std::vector<int> trail_lim;
    std::vector<uint8_t> seen;
    std::vector<double> activity;
    std::priority_queue<std::pair<double, int>> heap;
    int qhead = 0;
    int64_t conflicts = 0;
    int64_t decisions = 0;
    std::chrono::steady_clock::time_point deadline;
    std::ofstream proof;

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

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

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

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

    void attach_clause(int ci) {
        const auto& lits = clauses[ci].lits;
        if (lits.size() >= 2) {
            watches[lit_index(lits[0])].push_back(ci);
            watches[lit_index(lits[1])].push_back(ci);
        }
    }

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

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

    void bump_clause_vars(const std::vector<int>& lits) {
        for (int lit : lits) {
            int v = std::abs(lit);
            activity[v] += 1.0;
            heap.push({activity[v], v});
        }
        if ((conflicts & 4095) == 0) {
            for (int v = 1; v <= nvars; ++v) activity[v] *= 0.5;
        }
    }

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

    void analyze(int confl, std::vector<int>& learnt, int& backlevel) {
        learnt.clear();
        learnt.push_back(0);
        std::vector<int> touched;
        int path_count = 0;
        int p = 0;
        int idx = static_cast<int>(trail.size()) - 1;
        int ci = confl;
        do {
            Clause& c = clauses[ci];
            for (int lit : c.lits) {
                if (lit == p) continue;
                int v = std::abs(lit);
                if (!seen[v] && level[v] > 0) {
                    seen[v] = 1;
                    touched.push_back(v);
                    if (level[v] == decision_level()) {
                        ++path_count;
                    } else {
                        learnt.push_back(lit);
                    }
                }
            }
            while (true) {
                p = trail[idx--];
                if (seen[std::abs(p)]) break;
            }
            seen[std::abs(p)] = 0;
            --path_count;
            ci = reason[std::abs(p)];
        } while (path_count > 0);
        learnt[0] = -p;

        backlevel = 0;
        for (int i = 1; i < static_cast<int>(learnt.size()); ++i) {
            backlevel = std::max(backlevel, level[std::abs(learnt[i])]);
        }
        for (int v : touched) seen[v] = 0;
    }

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

    void write_empty_unsat() {
        proof << "rup >= 1 ;\n";
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : -1 ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.flush();
    }

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

    int solve() {
        int confl = propagate();
        if (confl != -1) {
            write_empty_unsat();
            return 20;
        }
        int restart_limit = 128;
        int since_restart = 0;
        std::vector<int> learnt;
        while (!timed_out()) {
            confl = propagate();
            if (confl != -1) {
                ++conflicts;
                ++since_restart;
                if (decision_level() == 0) {
                    write_empty_unsat();
                    return 20;
                }
                int backlevel = 0;
                analyze(confl, learnt, backlevel);
                bump_clause_vars(learnt);
                write_rup(learnt);
                Clause c;
                c.lits = learnt;
                c.learnt = true;
                int ci = static_cast<int>(clauses.size());
                clauses.push_back(std::move(c));
                attach_clause(ci);
                backtrack(backlevel);
                if (!enqueue(clauses[ci].lits[0], ci)) {
                    write_empty_unsat();
                    return 20;
                }
                if (since_restart >= restart_limit) {
                    backtrack(0);
                    since_restart = 0;
                    restart_limit = std::min(20000, restart_limit + restart_limit / 2 + 32);
                }
                continue;
            }
            int next = pick_branch_lit();
            if (next == 0) return 10;
            trail_lim.push_back(static_cast<int>(trail.size()));
            ++decisions;
            enqueue(next, -1);
        }
        return 0;
    }
};

static bool normalize_clause(std::vector<int>& lits) {
    std::sort(lits.begin(), lits.end(), [](int a, int b) {
        int aa = std::abs(a), bb = std::abs(b);
        if (aa != bb) return aa < bb;
        return a < b;
    });
    std::vector<int> out;
    int last_var = 0;
    int last_lit = 0;
    for (int lit : lits) {
        int v = std::abs(lit);
        if (v == last_var) {
            if (lit != last_lit) return false;
            continue;
        }
        out.push_back(lit);
        last_var = v;
        last_lit = lit;
    }
    lits.swap(out);
    return true;
}

int main(int argc, char** argv) {
    if (argc != 4) {
        std::cerr << "usage: cdcl_prover <formula.cnf> <out.pbp> <seconds>\n";
        return 1;
    }
    int seconds = std::max(1, std::atoi(argv[3]));
    Solver s;
    s.deadline = std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
    std::ifstream in(argv[1]);
    if (!in) return 1;
    std::string tok;
    int declared_clauses = 0;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
            continue;
        }
        if (tok == "p") {
            std::string fmt;
            in >> fmt >> s.nvars >> declared_clauses;
            break;
        }
    }
    if (s.nvars <= 0) return 1;
    s.clauses.reserve(declared_clauses + 100000);
    s.watches.assign(2 * s.nvars + 2, {});
    s.assign.assign(s.nvars + 1, 0);
    s.phase.assign(s.nvars + 1, -1);
    s.level.assign(s.nvars + 1, 0);
    s.reason.assign(s.nvars + 1, -1);
    s.seen.assign(s.nvars + 1, 0);
    s.activity.assign(s.nvars + 1, 0.0);
    std::vector<int> pos(s.nvars + 1, 0), neg(s.nvars + 1, 0);

    std::vector<int> lits;
    int lit = 0;
    while (in >> lit) {
        if (lit == 0) {
            if (normalize_clause(lits)) {
                if (lits.empty()) {
                    s.proof.open(argv[2]);
                    s.proof << "pseudo-Boolean proof version 3.0\n";
                    s.write_empty_unsat();
                    return 20;
                }
                int ci = static_cast<int>(s.clauses.size());
                Clause c;
                c.lits = lits;
                s.clauses.push_back(std::move(c));
                for (int x : s.clauses[ci].lits) {
                    int v = std::abs(x);
                    s.activity[v] += 1.0;
                    if (x > 0) ++pos[v]; else ++neg[v];
                }
            }
            lits.clear();
        } else {
            lits.push_back(lit);
        }
    }
    for (int v = 1; v <= s.nvars; ++v) {
        s.phase[v] = pos[v] >= neg[v] ? 1 : -1;
        s.heap.push({s.activity[v], v});
    }
    s.proof.open(argv[2]);
    if (!s.proof) return 1;
    s.proof << "pseudo-Boolean proof version 3.0\n";

    for (int ci = 0; ci < static_cast<int>(s.clauses.size()); ++ci) {
        if (s.clauses[ci].lits.size() == 1) {
            if (!s.enqueue(s.clauses[ci].lits[0], ci)) {
                s.write_empty_unsat();
                return 20;
            }
        } else {
            s.attach_clause(ci);
        }
    }

    int result = s.solve();
    if (result == 20) {
        std::cerr << "c cdcl_prover conflicts " << s.conflicts
                  << " decisions " << s.decisions << "\n";
        return 20;
    }
    std::cerr << "c cdcl_prover result " << result
              << " conflicts " << s.conflicts
              << " decisions " << s.decisions << "\n";
    return 0;
}
