#include <algorithm>
#include <array>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct VecHash {
    size_t operator()(const vector<int>& v) const {
        uint64_t h = 1469598103934665603ULL;
        for (int x : v) {
            h ^= (uint64_t)(x + 1000000007);
            h *= 1099511628211ULL;
        }
        return (size_t)h;
    }
};

static vector<int> norm_clause(vector<int> c) {
    sort(c.begin(), c.end());
    return c;
}

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

static void write_rup(ofstream& out, const vector<int>& lits) {
    out << "rup " << pb_clause(lits);
}

static void unknown(const char* why) {
    cout << "c " << why << "\n";
    cout << "s UNKNOWN\n";
}

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

    ifstream in(argv[1]);
    if (!in) {
        unknown("could not open CNF");
        return 0;
    }

    int nvars = 0, declared_clauses = 0;
    vector<vector<int>> clauses;
    string tok;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
            continue;
        }
        if (tok == "p") {
            string fmt;
            in >> fmt >> nvars >> declared_clauses;
            continue;
        }
        int lit = stoi(tok);
        vector<int> c;
        while (lit != 0) {
            c.push_back(lit);
            if (!(in >> lit)) break;
        }
        if (!c.empty()) clauses.push_back(c);
    }

    if (nvars <= 0 || clauses.empty()) {
        unknown("empty or invalid CNF");
        return 0;
    }

    vector<vector<int>> binaries, ternaries, longs;
    for (auto& c : clauses) {
        if (c.size() == 2) binaries.push_back(c);
        else if (c.size() == 3) ternaries.push_back(c);
        else if (c.size() > 3) longs.push_back(c);
        else {
            unknown("unit/empty clauses are outside the ordering-principle detector");
            return 0;
        }
    }

    int n = (int)longs.size();
    if (n < 2) {
        unknown("no ordering-principle long clauses detected");
        return 0;
    }
    for (auto& c : longs) {
        if ((int)c.size() != n - 1) {
            unknown("long clauses do not have uniform n-1 width");
            return 0;
        }
    }
    if ((int)binaries.size() != n * (n - 1) / 2) {
        unknown("binary clause count is not n choose 2");
        return 0;
    }

    vector<int> row_of_var(nvars + 1, -1), row_lit(nvars + 1, 0);
    for (int r = 0; r < n; r++) {
        for (int lit : longs[r]) {
            int v = abs(lit);
            if (v < 1 || v > nvars || row_of_var[v] != -1) {
                unknown("variable appears in multiple long clauses");
                return 0;
            }
            row_of_var[v] = r;
            row_lit[v] = lit;
        }
    }

    vector<vector<int>> edge(n, vector<int>(n, 0));
    unordered_set<vector<int>, VecHash> clause_set;
    clause_set.reserve(clauses.size() * 2 + 10);
    for (const auto& c : clauses) clause_set.insert(norm_clause(c));

    for (auto& c : binaries) {
        int a = c[0], b = c[1];
        int va = abs(a), vb = abs(b);
        if (va < 1 || va > nvars || vb < 1 || vb > nvars) {
            unknown("literal variable outside declared range");
            return 0;
        }
        int ra = row_of_var[va], rb = row_of_var[vb];
        if (ra < 0 || rb < 0 || ra == rb) {
            unknown("binary clause does not pair two distinct rows");
            return 0;
        }
        if (a != -row_lit[va] || b != -row_lit[vb]) {
            unknown("binary clause is not row-literal antisymmetry");
            return 0;
        }
        if (edge[ra][rb] != 0 || edge[rb][ra] != 0) {
            unknown("duplicate binary row pair");
            return 0;
        }
        edge[ra][rb] = row_lit[va];
        edge[rb][ra] = row_lit[vb];
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i != j && edge[i][j] == 0) {
                unknown("not all directed row pairs were recovered");
                return 0;
            }
        }
    }

    vector<array<int,3>> missing;
    long long present_trans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) if (j != i) {
            for (int k = 0; k < n; k++) if (k != i && k != j) {
                vector<int> t = norm_clause({-edge[i][j], -edge[j][k], edge[i][k]});
                if (clause_set.find(t) == clause_set.end()) missing.push_back({i, j, k});
                else present_trans++;
            }
        }
    }

    vector<int> order(n);
    iota(order.begin(), order.end(), 0);
    auto order_ok = [&](const vector<int>& ord) {
        vector<int> pos(n);
        for (int p = 0; p < n; p++) pos[ord[p]] = p;
        for (auto [i, mid, k] : missing) {
            if (pos[mid] < pos[i] && pos[mid] < pos[k]) return false;
        }
        return true;
    };

    if (!order_ok(order)) {
        bool changed = true;
        int guard = 0;
        while (changed && guard++ < n * n * 4) {
            changed = false;
            vector<int> pos(n);
            for (int p = 0; p < n; p++) pos[order[p]] = p;
            for (auto [i, mid, k] : missing) {
                if (pos[mid] < pos[i] && pos[mid] < pos[k]) {
                    int target = min(pos[i], pos[k]);
                    swap(order[pos[mid]], order[target]);
                    changed = true;
                    break;
                }
            }
        }
    }
    if (!order_ok(order)) {
        unknown("could not find an elimination order avoiding missing transitivity clauses");
        return 0;
    }

    ofstream out(argv[2]);
    if (!out) {
        unknown("could not open proof output");
        return 0;
    }
    out << "pseudo-Boolean proof version 3.0\n";

    cout << "c detected ordering-principle instance with " << n
         << " vertices, " << missing.size() << " missing transitivity clauses\n";

    vector<int> active = order;
    while ((int)active.size() > 2) {
        int z = active.back();
        int m = (int)active.size();
        vector<int> rest(active.begin(), active.end() - 1);
        for (int ii = 0; ii < (int)rest.size(); ii++) {
            int i = rest[ii];
            vector<int> cur;
            cur.reserve(2 * m);
            for (int j : rest) if (j != i) cur.push_back(edge[z][j]);
            for (int j : rest) if (j != i) cur.push_back(edge[i][j]);
            write_rup(out, cur);

            for (int j : rest) if (j != i) {
                auto it = find(cur.begin(), cur.end(), edge[z][j]);
                if (it == cur.end()) {
                    unknown("internal proof generation error");
                    return 0;
                }
                cur.erase(it);
                write_rup(out, cur);
            }
        }
        active.pop_back();
    }

    write_rup(out, {});
    out << "output NONE ;\n";
    out << "conclusion UNSAT : -1 ;\n";
    out << "end pseudo-Boolean proof ;\n";
    out.close();

    cout << "s UNSATISFIABLE\n";
    return 20;
}
