#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct Eq {
    int p[3];      // high-level OR-pair indices, 1-based
    int rhs;       // XOR rhs over OR-pair values
    int start_id;  // first original CNF constraint id, 1-based
    int count;
};

static string lit_pb(int v) {
    if (v > 0) return "+1 x" + to_string(v);
    return "+1 ~x" + to_string(-v);
}

static string ylit(const string &name, bool positive) {
    return string("+1 ") + (positive ? "" : "~") + name;
}

static string clause_constraint(const vector<int> &lits, const string &extra = "") {
    string s;
    if (!extra.empty()) s += extra;
    for (int lit : lits) {
        if (!s.empty()) s += " ";
        s += lit_pb(lit);
    }
    s += " >= 1";
    return s;
}

static vector<int> clause_for_assignment(const int vars[3], int a) {
    vector<int> lits;
    for (int i = 0; i < 3; ++i) {
        bool bit = (a >> i) & 1;
        lits.push_back(bit ? -vars[i] : vars[i]);
    }
    return lits;
}

static int parity3(int a) {
    return __builtin_popcount((unsigned)a) & 1;
}

static bool read_cnf(const string &path, int &nvars, int &nclauses, vector<Clause> &clauses) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    nvars = nclauses = 0;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string fmt;
            in >> fmt >> nvars >> nclauses;
            if (fmt != "cnf") return false;
        } else {
            Clause c;
            int lit = stoi(tok);
            while (lit != 0) {
                c.lits.push_back(lit);
                if (!(in >> lit)) return false;
            }
            clauses.push_back(std::move(c));
        }
    }
    return nvars > 0 && (int)clauses.size() == nclauses;
}

static bool detect_blocks(int nvars, const vector<Clause> &clauses, vector<Eq> &eqs) {
    if (nvars % 2 != 0) return false;
    int i = 0;
    while (i < (int)clauses.size()) {
        set<int> ps_set;
        for (int lit : clauses[i].lits) {
            int v = abs(lit);
            if (v < 1 || v > nvars) return false;
            ps_set.insert((v + 1) / 2);
        }
        if (ps_set.size() != 3) return false;
        vector<int> ps(ps_set.begin(), ps_set.end());
        int j = i + 1;
        while (j < (int)clauses.size()) {
            set<int> qs;
            for (int lit : clauses[j].lits) qs.insert((abs(lit) + 1) / 2);
            if (vector<int>(qs.begin(), qs.end()) != ps) break;
            ++j;
        }
        int cnt = j - i;
        int rhs = -1;
        if (cnt == 13) rhs = 1;
        if (cnt == 14) rhs = 0;
        if (rhs < 0) return false;
        Eq e;
        for (int k = 0; k < 3; ++k) e.p[k] = ps[k];
        e.rhs = rhs;
        e.start_id = i + 1;
        e.count = cnt;
        eqs.push_back(e);
        i = j;
    }
    return !eqs.empty();
}

struct GaussianResult {
    bool inconsistent = false;
    vector<int> dep_rows;
    vector<int> solution;
};

static GaussianResult gaussian(int n, const vector<Eq> &eqs) {
    int m = (int)eqs.size();
    int w = (n + 63) / 64;
    int dw = (m + 63) / 64;
    struct Row {
        vector<uint64_t> a, dep;
        int rhs = 0;
    };
    vector<Row> basis(n + 1);
    vector<int> piv_has(n + 1, 0);
    GaussianResult gr;
    for (int r = 0; r < m; ++r) {
        Row row;
        row.a.assign(w, 0);
        row.dep.assign(dw, 0);
        for (int k = 0; k < 3; ++k) {
            int v = eqs[r].p[k] - 1;
            row.a[v >> 6] ^= 1ULL << (v & 63);
        }
        row.dep[r >> 6] ^= 1ULL << (r & 63);
        row.rhs = eqs[r].rhs;
        while (true) {
            int piv = -1;
            for (int word = w - 1; word >= 0 && piv < 0; --word) {
                uint64_t x = row.a[word];
                if (x) piv = word * 64 + 63 - __builtin_clzll(x);
            }
            if (piv < 0) {
                if (row.rhs) {
                    gr.inconsistent = true;
                    for (int i = 0; i < m; ++i)
                        if ((row.dep[i >> 6] >> (i & 63)) & 1ULL) gr.dep_rows.push_back(i);
                    return gr;
                }
                break;
            }
            if (!piv_has[piv]) {
                basis[piv] = std::move(row);
                piv_has[piv] = 1;
                break;
            }
            for (int i = 0; i < w; ++i) row.a[i] ^= basis[piv].a[i];
            for (int i = 0; i < dw; ++i) row.dep[i] ^= basis[piv].dep[i];
            row.rhs ^= basis[piv].rhs;
        }
    }
    gr.solution.assign(n, 0);
    for (int piv = 0; piv < n; ++piv) {
        if (!piv_has[piv]) continue;
        int val = basis[piv].rhs;
        for (int j = 0; j < n; ++j) {
            if (j == piv) continue;
            if ((basis[piv].a[j >> 6] >> (j & 63)) & 1ULL) val ^= gr.solution[j];
        }
        gr.solution[piv] = val;
    }
    return gr;
}

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

struct ProofWriter {
    ofstream out;
    int next_id;
    explicit ProofWriter(const string &path, int original_constraints)
        : out(path), next_id(original_constraints + 1) {}
    void line(const string &s) { out << s << "\n"; }
    int add(const string &s) {
        out << s << "\n";
        return next_id++;
    }
};

static string plus_chain(const vector<int> &ids) {
    string s = to_string(ids[0]);
    for (size_t i = 1; i < ids.size(); ++i) s += " " + to_string(ids[i]) + " +";
    return s;
}

static bool write_unsat_proof(const string &path, int nvars, int nclauses,
                              const vector<Eq> &eqs, const vector<int> &dep) {
    ProofWriter pw(path, nclauses);
    if (!pw.out) return false;
    pw.line("pseudo-Boolean proof version 3.0");
    pw.line("f " + to_string(nclauses) + " ;");

    for (int v = 2; v <= nvars; v += 2) {
        int odd = v - 1;
        pw.add("red +1 ~x" + to_string(v) + " >= 1 : x" + to_string(v) +
               " -> 0 x" + to_string(odd) + " -> 1 ;");
    }

    vector<int> lowers, uppers;
    for (int rid : dep) {
        const Eq &e = eqs[rid];
        int xs[3] = {2 * e.p[0] - 1, 2 * e.p[1] - 1, 2 * e.p[2] - 1};
        map<int, int> input_clause_id;
        for (int a = 0; a < 8; ++a) {
            if (parity3(a) == e.rhs) continue;
            int id = pw.add("rup " + clause_constraint(clause_for_assignment(xs, a)) + " ;");
            input_clause_id[a] = id;
        }

        string c = "y" + to_string(rid) + "c";
        string y = "y" + to_string(rid) + "p";
        int r1 = pw.add("red +2 ~" + c + " +1 x" + to_string(xs[0]) + " +1 x" +
                        to_string(xs[1]) + " +1 x" + to_string(xs[2]) +
                        " >= 2 : " + c + " -> 0 ;");
        int r2 = pw.add("red +2 " + c + " +1 ~x" + to_string(xs[0]) + " +1 ~x" +
                        to_string(xs[1]) + " +1 ~x" + to_string(xs[2]) +
                        " >= 2 : " + c + " -> 1 ;");
        int r3 = pw.add("red +3 ~" + y + " +1 x" + to_string(xs[0]) + " +1 x" +
                        to_string(xs[1]) + " +1 x" + to_string(xs[2]) +
                        " +2 ~" + c + " >= 3 : " + y + " -> 0 ;");
        int r4 = pw.add("red +3 " + y + " +1 ~x" + to_string(xs[0]) + " +1 ~x" +
                        to_string(xs[1]) + " +1 ~x" + to_string(xs[2]) +
                        " +2 " + c + " >= 3 : " + y + " -> 1 ;");
        int p1 = pw.add("pol " + to_string(r3) + " " + to_string(r1) + " 2 * + 3 d ;");
        int p2 = pw.add("pol " + to_string(r4) + " " + to_string(r2) + " 2 * + 3 d ;");

        map<int, int> fix_clause_id;
        for (int a = 0; a < 8; ++a) {
            if (parity3(a) != e.rhs) continue;
            vector<int> lits = clause_for_assignment(xs, a);
            string extra = ylit(y, e.rhs == 1);
            int id = pw.add("rup " + clause_constraint(lits, extra) + " ;");
            fix_clause_id[a] = id;
        }

        auto id_for_input = [&](int a) -> int { return input_clause_id.at(a); };
        auto id_for_fix = [&](int a) -> int { return fix_clause_id.at(a); };
        vector<pair<int, int>> first;
        if (e.rhs == 0) {
            first = {{id_for_fix(0), id_for_input(1)},
                     {id_for_input(2), id_for_fix(3)},
                     {id_for_input(4), id_for_fix(5)},
                     {id_for_fix(6), id_for_input(7)}};
        } else {
            first = {{id_for_input(0), id_for_fix(1)},
                     {id_for_fix(2), id_for_input(3)},
                     {id_for_fix(4), id_for_input(5)},
                     {id_for_input(6), id_for_fix(7)}};
        }
        string pol = "pol ";
        pol += to_string(first[0].first) + " " + to_string(first[0].second) + " + 2 d ";
        pol += to_string(first[1].first) + " " + to_string(first[1].second) + " + 2 d + 2 d ";
        pol += to_string(first[2].first) + " " + to_string(first[2].second) + " + 2 d ";
        pol += to_string(first[3].first) + " " + to_string(first[3].second) + " + 2 d + 2 d + 2 d ;";
        int fixed = pw.add(pol);

        int lower, upper;
        if (e.rhs == 0) {
            lower = pw.add("pol " + to_string(p1) + " " + y + " + ;");
            upper = pw.add("pol " + to_string(p2) + " " + to_string(fixed) + " + ;");
        } else {
            lower = pw.add("pol " + to_string(p1) + " " + to_string(fixed) + " + ;");
            upper = pw.add("pol " + to_string(p2) + " ~" + y + " + ;");
        }
        lowers.push_back(lower);
        uppers.push_back(upper);
    }

    if (lowers.empty()) return false;
    int lower_sum = pw.add("pol " + plus_chain(lowers) + " ;");
    int upper_sum = pw.add("pol " + plus_chain(uppers) + " ;");
    int contradiction = pw.add("pol " + to_string(lower_sum) + " 2 d 2 * " +
                               to_string(upper_sum) + " + ;");
    pw.line("output NONE ;");
    pw.line("conclusion UNSAT : " + to_string(contradiction) + " ;");
    pw.line("end pseudo-Boolean proof;");
    return true;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int nvars, nclauses;
    vector<Clause> clauses;
    if (!read_cnf(argv[1], nvars, nclauses, clauses)) {
        cout << "c could not parse DIMACS\ns UNKNOWN\n";
        return 0;
    }
    vector<Eq> eqs;
    if (!detect_blocks(nvars, clauses, eqs)) {
        cout << "c unsupported structure\ns UNKNOWN\n";
        return 0;
    }
    int high_n = nvars / 2;
    GaussianResult gr = gaussian(high_n, eqs);
    cout << "c detected or-randxor pairs=" << high_n << " equations=" << eqs.size() << "\n";
    if (!gr.inconsistent) {
        vector<int> assign(nvars + 1, -1);
        for (int i = 1; i <= high_n; ++i) {
            assign[2 * i] = -1;
            assign[2 * i - 1] = gr.solution[i - 1] ? 1 : -1;
        }
        if (!check_model(clauses, assign)) {
            cout << "c internal model check failed\ns UNKNOWN\n";
            return 0;
        }
        cout << "s SATISFIABLE\nv ";
        for (int v = 1; v <= nvars; ++v) cout << (assign[v] > 0 ? v : -v) << " ";
        cout << "0\n";
        return 10;
    }
    if (!write_unsat_proof(argv[2], nvars, nclauses, eqs, gr.dep_rows)) {
        cout << "c proof generation failed\ns UNKNOWN\n";
        return 0;
    }
    cout << "c gaussian dependency size=" << gr.dep_rows.size() << "\n";
    cout << "s UNSATISFIABLE\n";
    return 20;
}
