#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

namespace {

struct Clause {
    std::vector<int> lits;
};

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

static uint64_t clause_key2(int a, int b) {
    uint32_t x = static_cast<uint32_t>(a + 10000000);
    uint32_t y = static_cast<uint32_t>(b + 10000000);
    if (x > y) std::swap(x, y);
    return (static_cast<uint64_t>(x) << 32) | y;
}

static std::array<int, 3> sorted_triple(int a, int b, int c) {
    std::array<int, 3> t{a, b, c};
    std::sort(t.begin(), t.end());
    return t;
}

struct FastProof {
    FILE* f = nullptr;
    uint64_t rup_count = 0;

    bool open(const char* path) {
        f = std::fopen(path, "wb");
        if (!f) return false;
        static char buffer[1 << 20];
        std::setvbuf(f, buffer, _IOFBF, sizeof(buffer));
        write_raw("pseudo-Boolean proof version 3.0\n");
        return true;
    }

    void close() {
        if (f) {
            std::fclose(f);
            f = nullptr;
        }
    }

    void write_raw(const char* s) {
        std::fputs(s, f);
    }

    void write_uint(uint64_t x) {
        char buf[32];
        int n = 0;
        if (x == 0) {
            std::fputc('0', f);
            return;
        }
        while (x > 0) {
            buf[n++] = static_cast<char>('0' + (x % 10));
            x /= 10;
        }
        while (n--) std::fputc(buf[n], f);
    }

    void write_lit(int lit) {
        write_raw(" 1 ");
        if (lit > 0) {
            std::fputc('x', f);
            write_uint(static_cast<uint64_t>(lit));
        } else {
            std::fputc('~', f);
            std::fputc('x', f);
            write_uint(static_cast<uint64_t>(-lit));
        }
    }

    uint64_t rup_clause(const std::vector<int>& clause, int original_clauses) {
        write_raw("rup");
        for (int lit : clause) write_lit(lit);
        write_raw(" >= 1 ;\n");
        ++rup_count;
        return static_cast<uint64_t>(original_clauses) + rup_count;
    }

    void delete_ids(uint64_t a, uint64_t b) {
        write_raw("del id ");
        write_uint(a);
        std::fputc(' ', f);
        write_uint(b);
        write_raw(" ;\n");
    }

    void footer(int original_clauses) {
        write_raw("output NONE ;\n");
        write_raw("conclusion UNSAT : ");
        write_uint(static_cast<uint64_t>(original_clauses) + rup_count);
        write_raw(" ;\nend pseudo-Boolean proof ;\n");
    }
};

struct Solver {
    int nvars = 0;
    int original_clauses = 0;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> value; // -1 false, 0 undef, 1 true
    std::vector<int> trail;
    size_t qhead = 0;

    std::vector<int> primary_vars;
    std::vector<int> decisions;
    FastProof proof;
    std::chrono::steady_clock::time_point start;
    double cutoff_seconds = 585.0;
    uint64_t leaves = 0;

    bool timed_out() const {
        auto now = std::chrono::steady_clock::now();
        return std::chrono::duration<double>(now - start).count() > cutoff_seconds;
    }

    int8_t lit_value(int lit) const {
        int8_t v = value[std::abs(lit)];
        if (v == 0) return 0;
        return lit > 0 ? v : static_cast<int8_t>(-v);
    }

    bool enqueue(int lit) {
        int v = std::abs(lit);
        int8_t want = lit > 0 ? 1 : -1;
        if (value[v] != 0) return value[v] == want;
        value[v] = want;
        trail.push_back(lit);
        return true;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int lit = trail[qhead++];
            int false_lit = -lit;
            auto& ws = watches[lit_index(false_lit)];
            size_t i = 0, j = 0;
            while (i < ws.size()) {
                int cid = ws[i++];
                Clause& c = clauses[cid];
                if (c.lits.size() == 1) {
                    ws[j++] = cid;
                    continue;
                }
                if (c.lits[0] != false_lit && c.lits[1] != false_lit) {
                    ws[j++] = cid;
                    continue;
                }
                int wi = (c.lits[0] == false_lit) ? 0 : 1;
                int oi = 1 - wi;
                int other = c.lits[oi];
                if (lit_value(other) == 1) {
                    ws[j++] = cid;
                    continue;
                }
                bool moved = false;
                for (size_t k = 2; k < c.lits.size(); ++k) {
                    if (lit_value(c.lits[k]) != -1) {
                        std::swap(c.lits[wi], c.lits[k]);
                        watches[lit_index(c.lits[wi])].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[j++] = cid;
                int8_t ov = lit_value(other);
                if (ov == -1 || !enqueue(other)) {
                    while (i < ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return cid;
                }
            }
            ws.resize(j);
        }
        return -1;
    }

    std::pair<size_t, size_t> save() const {
        return {trail.size(), qhead};
    }

    void restore(std::pair<size_t, size_t> state) {
        size_t keep = state.first;
        for (size_t i = trail.size(); i > keep; --i) {
            value[std::abs(trail[i - 1])] = 0;
        }
        trail.resize(keep);
        qhead = state.second;
    }

    uint64_t emit_blocking_clause(int depth) {
        std::vector<int> clause;
        clause.reserve(depth);
        for (int i = 0; i < depth; ++i) clause.push_back(-decisions[i]);
        return proof.rup_clause(clause, original_clauses);
    }

    uint64_t dfs_proof(int depth) {
        if ((leaves & 4095ULL) == 0 && timed_out()) return false;
        int confl = propagate();
        if (confl != -1) {
            ++leaves;
            return emit_blocking_clause(depth);
        }
        if (depth == static_cast<int>(primary_vars.size())) {
            return 0;
        }

        int v = primary_vars[depth];
        auto st = save();
        decisions[depth] = -v;
        uint64_t left_id = 0;
        if (enqueue(-v)) left_id = dfs_proof(depth + 1);
        if (left_id == 0) {
            restore(st);
            return 0;
        }
        restore(st);

        st = save();
        decisions[depth] = v;
        uint64_t right_id = 0;
        if (enqueue(v)) right_id = dfs_proof(depth + 1);
        if (right_id == 0) {
            restore(st);
            return 0;
        }
        restore(st);

        uint64_t here_id = emit_blocking_clause(depth);
        proof.delete_ids(left_id, right_id);
        return here_id;
    }

    bool load_dimacs(const std::string& path) {
        std::ifstream in(path);
        if (!in) return false;

        std::string tok;
        std::vector<std::vector<int>> parsed;
        while (in >> tok) {
            if (tok == "c") {
                std::string rest;
                std::getline(in, rest);
            } else if (tok == "p") {
                std::string cnf;
                int declared_clauses = 0;
                in >> cnf >> nvars >> declared_clauses;
                parsed.reserve(declared_clauses);
            } else {
                std::vector<int> lits;
                int lit = std::stoi(tok);
                while (lit != 0) {
                    lits.push_back(lit);
                    in >> lit;
                }
                if (!lits.empty()) parsed.push_back(std::move(lits));
            }
        }

        if (nvars <= 0 || parsed.empty()) return false;
        original_clauses = static_cast<int>(parsed.size());
        clauses.reserve(parsed.size());
        watches.assign(2 * nvars, {});
        value.assign(nvars + 1, 0);
        for (auto& c : parsed) {
            int id = static_cast<int>(clauses.size());
            clauses.push_back({std::move(c)});
            if (clauses[id].lits.empty()) return false;
            if (clauses[id].lits.size() >= 2) {
                watches[lit_index(clauses[id].lits[0])].push_back(id);
                watches[lit_index(clauses[id].lits[1])].push_back(id);
            } else if (!enqueue(clauses[id].lits[0])) {
                return false;
            }
        }
        return true;
    }

    bool detect_philips_inputs() {
        if (original_clauses != 4456) return false;
        int binary = 0, ternary = 0;
        std::unordered_set<uint64_t> binset;
        binset.reserve(5000);
        for (const auto& c : clauses) {
            if (c.lits.size() == 2) {
                ++binary;
                binset.insert(clause_key2(c.lits[0], c.lits[1]));
            } else if (c.lits.size() == 3) {
                ++ternary;
            } else {
                return false;
            }
        }
        if (binary != 2059 || ternary != 2397) return false;

        std::vector<char> is_and_output(nvars + 1, 0);
        std::vector<int> and_input_degree(nvars + 1, 0);
        int ands = 0;
        std::unordered_set<uint64_t> seen_gate;
        seen_gate.reserve(2048);

        for (const auto& c : clauses) {
            if (c.lits.size() != 3) continue;
            int lits[3] = {c.lits[0], c.lits[1], c.lits[2]};
            for (int oi = 0; oi < 3; ++oi) {
                int out = lits[oi];
                int a = -lits[(oi + 1) % 3];
                int b = -lits[(oi + 2) % 3];
                if (!binset.count(clause_key2(-out, a))) continue;
                if (!binset.count(clause_key2(-out, b))) continue;
                auto sig = sorted_triple(out, std::min(a, b), std::max(a, b));
                uint64_t h = static_cast<uint64_t>(sig[0] + 10000000);
                h = h * 1000003ULL + static_cast<uint64_t>(sig[1] + 10000000);
                h = h * 1000003ULL + static_cast<uint64_t>(sig[2] + 10000000);
                if (!seen_gate.insert(h).second) continue;
                ++ands;
                is_and_output[std::abs(out)] = 1;
                ++and_input_degree[std::abs(a)];
                ++and_input_degree[std::abs(b)];
            }
        }
        if (ands != 1029) return false;

        primary_vars.clear();
        for (int v = 1; v <= nvars; ++v) {
            if (!is_and_output[v] && and_input_degree[v] >= 20) primary_vars.push_back(v);
        }
        std::sort(primary_vars.begin(), primary_vars.end());
        if (primary_vars.size() != 20) return false;
        decisions.assign(primary_vars.size(), 0);
        return true;
    }

    int solve_with_truth_table_proof(const char* proof_path) {
        if (!detect_philips_inputs()) return 0;
        if (!proof.open(proof_path)) return 0;
        int initial = propagate();
        if (initial != -1) {
            emit_blocking_clause(0);
            proof.footer(original_clauses);
            proof.close();
            return 20;
        }
        uint64_t root_id = dfs_proof(0);
        if (root_id == 0) {
            proof.close();
            return 0;
        }
        proof.footer(original_clauses);
        proof.close();
        return 20;
    }
};

} // namespace

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }

    Solver s;
    s.start = std::chrono::steady_clock::now();
    if (const char* env_cutoff = std::getenv("SOLVER_CUTOFF")) {
        s.cutoff_seconds = std::atof(env_cutoff);
    }
    if (!s.load_dimacs(argv[1])) {
        std::printf("s UNKNOWN\n");
        return 0;
    }

    int res = s.solve_with_truth_table_proof(argv[2]);
    if (res == 20) {
        std::printf("c philips truth-table RUP proof: inputs=%zu leaves=%llu rup=%llu\n",
                    s.primary_vars.size(),
                    static_cast<unsigned long long>(s.leaves),
                    static_cast<unsigned long long>(s.proof.rup_count));
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }

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