#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>

using namespace std;

struct Formula {
    int vars = 0;
    int clauses_decl = 0;
    vector<vector<int>> clauses;
};

struct BPHP {
    int bits = 0;
    int pigeons = 0;
    int holes = 0;
    int codes = 0;
    vector<int> valid_codes;
    vector<int> invalid_codes;
};

static bool read_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_decl;
            if (fmt != "cnf" || f.vars <= 0 || f.clauses_decl < 0) return false;
        } else if (tok == "%") {
            break;
        } else {
            vector<int> cl;
            int lit = stoi(tok);
            while (lit != 0) {
                if (lit == 0 || abs(lit) > f.vars) return false;
                cl.push_back(lit);
                if (!(in >> lit)) return false;
            }
            if (!cl.empty()) f.clauses.push_back(cl);
        }
    }
    return f.vars > 0 && (f.clauses_decl == 0 || (int)f.clauses.size() == f.clauses_decl);
}

static int var_block(int v, int bits) { return (v - 1) / bits; }
static int bit_index(int v, int bits) { return (v - 1) % bits; }
static int block_var(int p, int b, int bits) { return p * bits + b + 1; }

static bool decode_block_code(const vector<int>& lits, int start, int bits, int block, int& code) {
    vector<int> seen(bits, 0);
    code = 0;
    for (int i = 0; i < bits; ++i) {
        int lit = lits[start + i];
        int v = abs(lit);
        if (var_block(v, bits) != block) return false;
        int b = bit_index(v, bits);
        if (seen[b]) return false;
        seen[b] = 1;
        if (lit < 0) code |= (1 << b); // the falsifying assignment of the clause
    }
    return true;
}

static bool detect_bphp(const Formula& f, BPHP& out) {
    if (f.clauses.empty()) return false;
    map<int, int> len_count;
    for (const auto& c : f.clauses) len_count[(int)c.size()]++;
    if (len_count.size() != 1 && len_count.size() != 2) return false;
    int small = len_count.begin()->first;
    int large = len_count.rbegin()->first;
    int bits = 0;
    bool has_range_clauses = len_count.size() == 2;
    if (has_range_clauses) {
        if (small <= 0 || large != 2 * small || small >= 30) return false;
        bits = small;
    } else {
        if (large <= 0 || (large % 2) != 0 || large / 2 >= 30) return false;
        bits = large / 2;
        small = bits;
    }
    if (f.vars % bits != 0) return false;
    int pigeons = f.vars / bits;
    int codes = 1 << bits;
    if (pigeons < 2) return false;

    vector<set<int>> invalid_by_p(pigeons);
    map<pair<int, int>, set<int>> valid_by_pair;
    set<int> all_valid;

    for (const auto& c : f.clauses) {
        if (has_range_clauses && (int)c.size() == bits) {
            int block = var_block(abs(c[0]), bits);
            int code = 0;
            if (block < 0 || block >= pigeons) return false;
            if (!decode_block_code(c, 0, bits, block, code)) return false;
            if (code < 0 || code >= codes) return false;
            invalid_by_p[block].insert(code);
        } else if ((int)c.size() == 2 * bits) {
            int b1 = var_block(abs(c[0]), bits);
            int b2 = var_block(abs(c[bits]), bits);
            if (b1 == b2 || b1 < 0 || b1 >= pigeons || b2 < 0 || b2 >= pigeons) return false;
            int c1 = 0, c2 = 0;
            if (!decode_block_code(c, 0, bits, b1, c1)) return false;
            if (!decode_block_code(c, bits, bits, b2, c2)) return false;
            if (c1 != c2 || c1 < 0 || c1 >= codes) return false;
            if (b2 < b1) swap(b1, b2);
            valid_by_pair[{b1, b2}].insert(c1);
            all_valid.insert(c1);
        } else {
            return false;
        }
    }

    if (all_valid.empty()) return false;
    set<int> all_invalid;
    if (has_range_clauses) {
        all_invalid = invalid_by_p[0];
        for (int p = 1; p < pigeons; ++p) {
            if (invalid_by_p[p] != all_invalid) return false;
        }
    }
    for (int code = 0; code < codes; ++code) {
        bool v = all_valid.count(code);
        bool inv = all_invalid.count(code);
        if (v == inv) return false;
    }
    for (int i = 0; i < pigeons; ++i) {
        for (int j = i + 1; j < pigeons; ++j) {
            auto it = valid_by_pair.find({i, j});
            if (it == valid_by_pair.end() || it->second != all_valid) return false;
        }
    }

    out.bits = bits;
    out.pigeons = pigeons;
    out.codes = codes;
    out.valid_codes.assign(all_valid.begin(), all_valid.end());
    out.invalid_codes.assign(all_invalid.begin(), all_invalid.end());
    out.holes = (int)out.valid_codes.size();
    return true;
}

static bool satisfies(const Formula& f, const vector<int>& val) {
    for (const auto& c : f.clauses) {
        bool ok = false;
        for (int lit : c) {
            int v = abs(lit);
            bool b = val[v] != 0;
            if ((lit > 0 && b) || (lit < 0 && !b)) {
                ok = true;
                break;
            }
        }
        if (!ok) return false;
    }
    return true;
}

static string xlit(int var, bool positive) {
    return positive ? ("x" + to_string(var)) : ("~x" + to_string(var));
}

static string yname(int p, int code) {
    return "y" + to_string(p) + "_" + to_string(code);
}

static string bit_equal_lit(int p, int bit, int code, int bits) {
    int v = block_var(p, bit, bits);
    return xlit(v, ((code >> bit) & 1) != 0);
}

static string bit_mismatch_lit(int p, int bit, int code, int bits) {
    int v = block_var(p, bit, bits);
    return xlit(v, ((code >> bit) & 1) == 0);
}

static void print_terms(ostream& out, const vector<string>& lits) {
    for (const string& lit : lits) out << "1 " << lit << " ";
}

static vector<string> pattern_clause_terms(int p, int prefix, int depth, const BPHP& b) {
    vector<string> terms;
    int free_bits = b.bits - depth;
    int count = 1 << free_bits;
    terms.reserve(count + depth);
    for (int s = 0; s < count; ++s) {
        int code = prefix | (s << depth);
        terms.push_back(yname(p, code));
    }
    for (int bit = 0; bit < depth; ++bit) {
        terms.push_back(bit_mismatch_lit(p, bit, prefix, b.bits));
    }
    return terms;
}

static void emit_pattern_tree(ostream& out, int p, int prefix, int depth, const BPHP& b) {
    if (depth == b.bits) return;
    emit_pattern_tree(out, p, prefix, depth + 1, b);
    emit_pattern_tree(out, p, prefix | (1 << depth), depth + 1, b);
    vector<string> terms = pattern_clause_terms(p, prefix, depth, b);
    if (depth == 0) out << "@all_" << p << " ";
    out << "rup ";
    print_terms(out, terms);
    out << ">= 1;\n";
}

static void emit_proof(const string& out_path, const Formula& f, const BPHP& b) {
    ofstream out(out_path);
    out << "pseudo-Boolean proof version 3.0\n";
    out << "f " << f.clauses.size() << ";\n";

    for (int p = 0; p < b.pigeons; ++p) {
        for (int code = 0; code < b.codes; ++code) {
            string y = yname(p, code);
            for (int bit = 0; bit < b.bits; ++bit) {
                out << "red 1 ~" << y << " 1 " << bit_equal_lit(p, bit, code, b.bits)
                    << " >= 1 : " << y << " -> 0;\n";
            }
            out << "red 1 " << y << " ";
            for (int bit = 0; bit < b.bits; ++bit) {
                out << "1 " << bit_mismatch_lit(p, bit, code, b.bits) << " ";
            }
            out << ">= 1 : " << y << " -> 1;\n";
        }
    }

    for (int p = 0; p < b.pigeons; ++p) {
        emit_pattern_tree(out, p, 0, 0, b);
        for (int code : b.invalid_codes) {
            out << "rup 1 ~" << yname(p, code) << " >= 1;\n";
        }
        out << "@alo_" << p << " rup ";
        for (int code : b.valid_codes) out << "1 " << yname(p, code) << " ";
        out << ">= 1;\n";
    }

    for (int h : b.valid_codes) {
        for (int i = 0; i < b.pigeons; ++i) {
            for (int j = i + 1; j < b.pigeons; ++j) {
                out << "@pair_" << h << "_" << i << "_" << j << " rup 1 ~"
                    << yname(i, h) << " 1 ~" << yname(j, h) << " >= 1;\n";
            }
        }
        out << "@amo_" << h << "_2 pol @pair_" << h << "_0_1;\n";
        for (int k = 2; k < b.pigeons; ++k) {
            out << "@amo_" << h << "_" << (k + 1) << " pol @amo_" << h << "_" << k
                << " " << (k - 1) << " * ";
            for (int i = 0; i < k; ++i) {
                out << "@pair_" << h << "_" << i << "_" << k << " + ";
            }
            out << k << " d;\n";
        }
    }

    out << "pol ";
    bool first = true;
    auto add_item = [&](const string& s) {
        if (first) {
            out << s << " ";
            first = false;
        } else {
            out << s << " + ";
        }
    };
    for (int p = 0; p < b.pigeons; ++p) add_item("@alo_" + to_string(p));
    for (int h : b.valid_codes) add_item("@amo_" + to_string(h) + "_" + to_string(b.pigeons));
    out << ";\n";
    out << "output NONE;\n";
    out << "conclusion UNSAT : -1;\n";
    out << "end pseudo-Boolean proof;\n";
}

static int solve_sat(const Formula& f, const BPHP& b) {
    vector<int> val(f.vars + 1, 0);
    for (int p = 0; p < b.pigeons; ++p) {
        int code = b.valid_codes[p];
        for (int bit = 0; bit < b.bits; ++bit) {
            val[block_var(p, bit, b.bits)] = ((code >> bit) & 1) ? 1 : 0;
        }
    }
    if (!satisfies(f, val)) {
        cout << "c constructed assignment failed internal CNF check\n";
        cout << "s UNKNOWN\n";
        return 0;
    }
    cout << "c detected binary pigeonhole p=" << b.pigeons << " h=" << b.holes
         << " bits=" << b.bits << "\n";
    cout << "s SATISFIABLE\n";
    cout << "v";
    for (int v = 1; v <= f.vars; ++v) cout << ' ' << (val[v] ? v : -v);
    cout << " 0\n";
    return 10;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula f;
    if (!read_dimacs(argv[1], f)) {
        cout << "c failed to parse DIMACS\ns UNKNOWN\n";
        return 0;
    }
    BPHP b;
    if (!detect_bphp(f, b)) {
        cout << "c unsupported CNF structure\ns UNKNOWN\n";
        return 0;
    }
    if (b.pigeons <= b.holes) return solve_sat(f, b);

    cout << "c detected binary pigeonhole p=" << b.pigeons << " h=" << b.holes
         << " bits=" << b.bits << "\n";
    emit_proof(argv[2], f, b);
    cout << "s UNSATISFIABLE\n";
    return 20;
}
