#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iostream>
#include <numeric>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct Clause { vector<int> lits; };

static string litpb(int l) {
    if (l > 0) return "1 x" + to_string(l);
    return "1 ~x" + to_string(-l);
}
static string mlitpb(int l) {
    int v = abs(l);
    if (l > 0) return "1 $m" + to_string(v);
    return "1 ~$m" + to_string(v);
}

struct Formula {
    int vars = 0, clauses = 0, pairs = 0, n = 0;
    vector<vector<int>> cnf;
};

static bool parse_dimacs(const string& path, Formula& f) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    while (in >> tok) {
        if (tok == "c") {
            string rest; getline(in, rest);
        } else if (tok == "p") {
            string fmt; in >> fmt >> f.vars >> f.clauses;
        } else {
            vector<int> c;
            int x = stoi(tok);
            while (x != 0) { c.push_back(x); in >> x; }
            if (!c.empty()) f.cnf.push_back(c);
        }
    }
    f.pairs = f.vars / 2;
    int disc = 1 + 4 * f.pairs;
    int r = (int)llround(sqrt((double)disc));
    if (f.vars % 2 || r * r != disc || (1 + r) % 2) return false;
    f.n = (1 + r) / 2;
    return f.n >= 4 && f.n * (f.n - 1) == f.pairs;
}

struct High {
    int N = 0;
    vector<vector<int>> clauses;
    vector<array<int,2>> bin_blocks;
    vector<array<int,3>> tri_blocks;
    vector<array<char,3>> tri_types;
};

static bool recover(const Formula& f, High& h) {
    h.N = f.pairs;
    unordered_map<long long,bool> seen2, seen3;
    int bin = 0, trans = 0, extra = 0;
    for (auto& c : f.cnf) {
        if (c.size() == 4) {
            int a = (abs(c[0]) + 1) / 2, b = (abs(c[2]) + 1) / 2;
            long long key = (long long)min(a,b) * (h.N + 1LL) + max(a,b);
            if (seen2.emplace(key, true).second) {
                bool d0 = (c[0] > 0) != (c[1] > 0);
                bool d1 = (c[2] > 0) != (c[3] > 0);
                if (!d0 || !d1) return false;
                h.clauses.push_back({-a, -b});
                h.bin_blocks.push_back({a,b});
                bin++;
            }
        } else if (c.size() == 6) {
            array<int,3> ids{(abs(c[0])+1)/2, (abs(c[2])+1)/2, (abs(c[4])+1)/2};
            long long key = ((long long)ids[0] * (h.N + 1LL) + ids[1]) * (h.N + 1LL) + ids[2];
            if (seen3.emplace(key, true).second) {
                array<char,3> ty;
                vector<int> hc;
                for (int k=0;k<3;k++) {
                    bool same = (c[2*k] > 0) == (c[2*k+1] > 0);
                    ty[k] = same ? 'S' : 'D';
                    hc.push_back(same ? ids[k] : -ids[k]);
                }
                if (ty == array<char,3>{'D','D','S'}) trans++;
                else if (ty == array<char,3>{'S','S','S'}) extra++;
                else return false;
                h.clauses.push_back(hc);
                h.tri_blocks.push_back(ids);
                h.tri_types.push_back(ty);
            }
        } else return false;
    }
    return bin == f.n * (f.n - 1) / 2 &&
           trans == f.n * (f.n - 1) * (f.n - 2) &&
           extra == f.n;
}

struct CDCL {
    int n;
    vector<vector<int>> c;
    vector<vector<int>> watch;
    vector<int> asg, lev, reason, trail, learned_starts;
    vector<vector<int>> proof;
    vector<double> act;
    int q = 0, dl = 0, conflicts = 0;
    bool complete_sat = false;
    CDCL(int n_, const vector<vector<int>>& cls): n(n_), c(cls), watch(2*n_+2), asg(n_+1), lev(n_+1), reason(n_+1,-1), act(n_+1) {
        for (int i=0;i<(int)c.size();i++) {
            if (c[i].empty()) continue;
            watch[idx(c[i][0])].push_back(i);
            if (c[i].size() > 1) watch[idx(c[i][1])].push_back(i);
            for (int l: c[i]) act[abs(l)] += 1.0;
        }
    }
    int idx(int l) const { return 2*abs(l) + (l < 0); }
    int val(int l) const {
        int a = asg[abs(l)];
        if (!a) return 0;
        return ((a > 0) == (l > 0)) ? 1 : -1;
    }
    bool enq(int l, int r) {
        int v = abs(l), a = l > 0 ? 1 : -1;
        if (asg[v]) return asg[v] == a;
        asg[v] = a; lev[v] = dl; reason[v] = r; trail.push_back(l); return true;
    }
    int prop() {
        while (q < (int)trail.size()) {
            int fl = -trail[q++], wi = idx(fl);
            auto old = watch[wi]; watch[wi].clear();
            for (int ci: old) {
                auto& cl = c[ci];
                if (cl.size() > 1 && cl[0] != fl && cl[1] == fl) swap(cl[0], cl[1]);
                if (cl.size() > 1 && val(cl[1]) > 0) { watch[wi].push_back(ci); continue; }
                bool moved = false;
                for (int k=2;k<(int)cl.size();k++) if (val(cl[k]) >= 0) {
                    swap(cl[0], cl[k]); watch[idx(cl[0])].push_back(ci); moved = true; break;
                }
                if (moved) continue;
                watch[wi].push_back(ci);
                int unit = cl.size() == 1 ? cl[0] : cl[1];
                if (val(unit) < 0) {
                    bool all_false = true;
                    for (int l : cl) if (val(l) >= 0) { all_false = false; break; }
                    if (all_false) return ci;
                    continue;
                }
                if (val(unit) == 0 && !enq(unit, ci)) return ci;
            }
        }
        return -1;
    }
    pair<vector<int>,int> analyze(int ci) {
        vector<int> learnt(1, 0), seen(n+1);
        int path = 0, p = 0, bt = 0, ti = (int)trail.size() - 1;
        const vector<int>* cl = &c[ci];
        while (true) {
            for (int ql: *cl) {
                int v = abs(ql);
                if (v == abs(p) || seen[v] || lev[v] == 0) continue;
                seen[v] = 1;
                act[v] += 1.0;
                if (lev[v] == dl) path++;
                else {
                    learnt.push_back(ql);
                    bt = max(bt, lev[v]);
                }
            }
            while (ti >= 0 && !seen[abs(trail[ti])]) ti--;
            if (ti < 0) return {{},0};
            p = trail[ti--];
            seen[abs(p)] = 0;
            if (--path <= 0) break;
            int r = reason[abs(p)];
            if (r < 0) return {{},0};
            cl = &c[r];
        }
        learnt[0] = -p;
        vector<int> compact;
        vector<int> mark(n + 1, 0);
        compact.reserve(learnt.size());
        for (int l : learnt) {
            int v = abs(l), s = l > 0 ? 1 : -1;
            if (mark[v] == s) continue;
            if (mark[v] == -s) continue;
            mark[v] = s;
            compact.push_back(l);
        }
        learnt.swap(compact);
        return {learnt, bt};
    }
    void backtrack(int l) {
        while (!trail.empty() && lev[abs(trail.back())] > l) {
            int v = abs(trail.back()); trail.pop_back(); asg[v] = 0; reason[v] = -1; lev[v] = 0;
        }
        q = min(q, (int)trail.size()); dl = l;
    }
    int add_clause(const vector<int>& cl) {
        int id = c.size(); c.push_back(cl); learned_starts.push_back(id);
        if (!cl.empty()) {
            watch[idx(cl[0])].push_back(id);
            if (cl.size() > 1) watch[idx(cl[1])].push_back(id);
        }
        return id;
    }
    int pick() {
        int b = 0; double ba = -1;
        for (int v=1; v<=n; v++) if (!asg[v] && act[v] > ba) ba = act[v], b = v;
        return b;
    }
    int find_falsified() const {
        for (int i = 0; i < (int)c.size(); i++) {
            bool sat = false, open = false;
            for (int l : c[i]) {
                int a = asg[abs(l)];
                if (!a) open = true;
                else if ((a > 0) == (l > 0)) { sat = true; break; }
            }
            if (!sat && !open) return i;
        }
        return -1;
    }
    bool solve(int limit) {
        int cf = prop();
        if (cf >= 0) { proof.push_back({}); add_clause({}); return true; }
        while (conflicts < limit) {
            int v = pick();
            if (!v) {
                cf = find_falsified();
                if (cf < 0) { complete_sat = true; return false; }
            } else {
                dl++; enq(-v, -1);
                cf = prop();
            }
            while (true) {
                if (cf < 0) break;
                conflicts++;
                if (dl == 0) { proof.push_back({}); add_clause({}); return true; }
                auto [learnt, bt] = analyze(cf);
                if (learnt.empty()) return false;
                backtrack(bt);
                int rid = add_clause(learnt);
                proof.push_back(learnt);
                if (!enq(learnt[0], rid)) { cf = rid; continue; }
                cf = prop();
            }
        }
        return false;
    }
};

struct DPLLProof {
    int n, limit, calls = 0;
    const vector<vector<int>>& init;
    vector<vector<int>> learned, proof;
    DPLLProof(int n_, const vector<vector<int>>& c, int lim): n(n_), limit(lim), init(c) {}
    bool propagate(const vector<int>& assumps, vector<int>& assign) {
        assign.assign(n+1, 0);
        for (int l: assumps) {
            int v=abs(l), a=l>0?1:-1;
            if (assign[v] && assign[v] != a) return false;
            assign[v]=a;
        }
        bool ch=true;
        while (ch) {
            ch=false;
            auto scan = [&](const vector<vector<int>>& cs)->bool {
                for (auto& cl: cs) {
                    bool sat=false; int un=0,last=0;
                    for (int l: cl) {
                        int a=assign[abs(l)];
                        if (!a) { un++; last=l; }
                        else if ((a>0)==(l>0)) { sat=true; break; }
                    }
                    if (sat) continue;
                    if (!un) return false;
                    if (un==1) {
                        int v=abs(last), a=last>0?1:-1;
                        if (assign[v] && assign[v]!=a) return false;
                        if (!assign[v]) assign[v]=a, ch=true;
                    }
                }
                return true;
            };
            if (!scan(init) || !scan(learned)) return false;
        }
        return true;
    }
    int pick(const vector<int>& assign) {
        int best_lit=0, best_un=1000000;
        auto consider = [&](const vector<vector<int>>& cs) {
            for (auto& cl: cs) {
                bool sat=false; int un=0,last=0;
                for (int l: cl) {
                    int a=assign[abs(l)];
                    if (!a) { un++; last=l; }
                    else if ((a>0)==(l>0)) { sat=true; break; }
                }
                if (!sat && un>0 && un<best_un) best_un=un, best_lit=last;
            }
        };
        consider(init); consider(learned);
        return best_lit;
    }
    bool prove(vector<int>& assumps) {
        if (++calls > limit || (int)proof.size() > limit) return false;
        vector<int> assign;
        if (!propagate(assumps, assign)) {
            vector<int> cl; cl.reserve(assumps.size());
            for (int l: assumps) cl.push_back(-l);
            learned.push_back(cl); proof.push_back(cl); return true;
        }
        int l = pick(assign);
        if (!l) return false;
        assumps.push_back(l);
        if (!prove(assumps)) return false;
        assumps.back() = -l;
        if (!prove(assumps)) return false;
        assumps.pop_back();
        vector<int> cl; cl.reserve(assumps.size());
        for (int a: assumps) cl.push_back(-a);
        learned.push_back(cl); proof.push_back(cl);
        return true;
    }
};

static vector<vector<int>> local_proof(const vector<vector<int>>& block, const vector<int>& high, const array<int,6>& xs) {
    vector<vector<int>> base = block;
    for (int m=7;m<=9;m++) {
        int a = xs[2*(m-7)], b = xs[2*(m-7)+1];
        base.push_back({-m,a,b});
        base.push_back({-m,-a,-b});
        base.push_back({m,a,-b});
        base.push_back({m,-a,b});
    }
    for (int l: high) base.push_back({-l});
    vector<vector<int>> learned, proof;
    vector<int> vars;
    for (int x: xs) if (x) vars.push_back(abs(x));
    sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
    function<bool(vector<int>&, vector<int>&)> propagate = [&](vector<int>& assumps, vector<int>& assign)->bool {
        assign.assign(10,0);
        for (int l: high) assign[abs(l)] = l > 0 ? -1 : 1;
        for (int l: assumps) assign[abs(l)] = l > 0 ? 1 : -1;
        bool ch = true;
        while (ch) {
            ch = false;
            for (auto& cl: base) {
                bool sat=false; int un=0, last=0;
                for (int l: cl) {
                    int a = assign[abs(l)];
                    if (!a) { un++; last=l; }
                    else if ((a>0)==(l>0)) { sat=true; break; }
                }
                if (sat) continue;
                if (!un) return false;
                if (un == 1) {
                    int v=abs(last), a=last>0?1:-1;
                    if (assign[v] && assign[v] != a) return false;
                    if (!assign[v]) assign[v]=a, ch=true;
                }
            }
        }
        return true;
    };
    function<void(vector<int>&)> dfs = [&](vector<int>& assumps) {
        vector<int> assign;
        if (!propagate(assumps, assign)) {
            vector<int> cl; for (int l: assumps) cl.push_back(-l);
            learned.push_back(cl); proof.push_back(cl); return;
        }
        int v = 0; for (int x: vars) if (!assign[x]) { v=x; break; }
        if (!v) return;
        assumps.push_back(v); dfs(assumps); assumps.back() = -v; dfs(assumps); assumps.pop_back();
        vector<int> cl; for (int l: assumps) cl.push_back(-l);
        learned.push_back(cl); proof.push_back(cl);
        base.push_back(cl);
    };
    vector<int> a; dfs(a);
    return proof;
}

static void emit_defs(ostream& o, int p) {
    int a = 2*p-1, b = 2*p;
    o << "red 1 ~$m" << p << " 1 x" << a << " 1 x" << b << " >= 1 : $m" << p << " -> 0 ;\n";
    o << "red 1 ~$m" << p << " 1 ~x" << a << " 1 ~x" << b << " >= 1 : $m" << p << " -> 0 ;\n";
    o << "red 1 $m" << p << " 1 x" << a << " 1 ~x" << b << " >= 1 : $m" << p << " -> 1 ;\n";
    o << "red 1 $m" << p << " 1 ~x" << a << " 1 x" << b << " >= 1 : $m" << p << " -> 1 ;\n";
}

static void emit_pbc_clause(ostream& o, const vector<int>& hc, const vector<vector<int>>& block, const vector<int>& ids) {
    o << "pbc ";
    for (int l: hc) o << mlitpb(l) << ' ';
    o << ">= 1 : subproof\n";
    array<int,6> xs{};
    for (int k=0;k<(int)ids.size();k++) { xs[2*k] = 2*ids[k]-1; xs[2*k+1] = 2*ids[k]; }
    vector<vector<int>> local_block;
    for (auto c: block) {
        vector<int> lc;
        for (int l: c) {
            int v = abs(l), loc = 0;
            for (int i=0;i<6;i++) if (xs[i] == v) loc = i+1;
            lc.push_back(l > 0 ? loc : -loc);
        }
        local_block.push_back(lc);
    }
    vector<int> local_high;
    for (int l: hc) {
        int pos = find(ids.begin(), ids.end(), abs(l)) - ids.begin();
        local_high.push_back(l > 0 ? 7 + pos : -(7 + pos));
    }
    auto pr = local_proof(local_block, local_high, array<int,6>{1,2,3,4,5,6});
    for (auto& cl: pr) {
        o << "rup ";
        if (cl.empty()) o << ">= 1";
        else {
            for (int l: cl) {
                int real = xs[abs(l)-1];
                o << litpb(l > 0 ? real : -real) << ' ';
            }
            o << ">= 1";
        }
        o << " ;\n";
    }
    o << "qed pbc ;\n";
}

static bool check_model(const Formula& f, const vector<int>& model) {
    for (auto& cl : f.cnf) {
        bool sat = false;
        for (int l : cl) {
            int v = abs(l);
            if (v <= 0 || v >= (int)model.size() || model[v] == 0) return false;
            if ((model[v] > 0) == (l > 0)) { sat = true; break; }
        }
        if (!sat) return false;
    }
    return true;
}

static vector<int> concrete_model(const Formula& f, const vector<int>& high_asg) {
    vector<int> model(f.vars + 1, 0);
    for (int p = 1; p <= f.pairs; p++) {
        int a = 2 * p - 1, b = 2 * p;
        bool diseq = high_asg[p] > 0;
        model[a] = 1;
        model[b] = diseq ? -1 : 1;
    }
    return model;
}

static void print_model(const vector<int>& model) {
    cout << "s SATISFIABLE\n";
    cout << "v";
    int col = 1;
    for (int v = 1; v < (int)model.size(); v++) {
        int lit = model[v] > 0 ? v : -v;
        cout << ' ' << lit;
        if (++col == 16) {
            cout << " 0\n";
            if (v + 1 < (int)model.size()) cout << "v";
            col = 1;
        }
    }
    if (col != 1) cout << " 0\n";
}

static string clause_key(vector<int> cl) {
    sort(cl.begin(), cl.end());
    cl.erase(unique(cl.begin(), cl.end()), cl.end());
    string s;
    for (int l : cl) {
        s += to_string(l);
        s += ',';
    }
    return s;
}

int main(int argc, char** argv) {
    if (argc != 3) return 1;
    Formula f;
    if (!parse_dimacs(argv[1], f)) { cout << "s UNKNOWN\n"; return 0; }
    High h;
    if (!recover(f, h)) { cout << "s UNKNOWN\n"; return 0; }
    CDCL cdcl(h.N, h.clauses);
    bool unsat = cdcl.solve(5000000);
    if (!unsat && cdcl.complete_sat) {
        vector<int> model = concrete_model(f, cdcl.asg);
        if (check_model(f, model)) {
            print_model(model);
            return 10;
        }
    }
    if (!unsat || cdcl.proof.empty() || !cdcl.proof.back().empty()) {
        cout << "s UNKNOWN\n"; return 0;
    }
    ofstream out(argv[2]);
    out << "pseudo-Boolean proof version 3.0\n";
    for (int p=1;p<=h.N;p++) emit_defs(out, p);
    size_t ti = 0, bi = 0;
    for (auto& hc: h.clauses) {
        if (hc.size() == 2) {
            auto ids = h.bin_blocks[bi++];
            vector<vector<int>> block;
            for (auto& c: f.cnf) if (c.size()==4 && (abs(c[0])+1)/2==ids[0] && (abs(c[2])+1)/2==ids[1]) block.push_back(c);
            emit_pbc_clause(out, hc, block, {ids[0], ids[1]});
        } else {
            auto ids = h.tri_blocks[ti++];
            vector<vector<int>> block;
            for (auto& c: f.cnf) if (c.size()==6 && (abs(c[0])+1)/2==ids[0] && (abs(c[2])+1)/2==ids[1] && (abs(c[4])+1)/2==ids[2]) block.push_back(c);
            emit_pbc_clause(out, hc, block, {ids[0], ids[1], ids[2]});
        }
    }
    unordered_set<string> emitted;
    emitted.reserve(h.clauses.size() + cdcl.proof.size() + 8);
    for (auto& cl : h.clauses) emitted.insert(clause_key(cl));
    for (auto& cl : cdcl.proof) {
        if (!cl.empty()) {
            string key = clause_key(cl);
            if (!emitted.insert(key).second) continue;
        }
        out << "rup ";
        if (cl.empty()) out << ">= 1";
        else { for (int l: cl) out << mlitpb(l) << ' '; out << ">= 1"; }
        out << " ;\n";
    }
    out << "output NONE ;\nconclusion UNSAT ;\nend pseudo-Boolean proof ;\n";
    cout << "s UNSATISFIABLE\n";
    return 20;
}
