#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <queue>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct Value {
    int var = 0;
    int lit = 0;      // the literal appearing in the block ALO
    int group = -1;
    int resource = -1;
};

static string lit_name_from_signed_var(int lit) {
    int v = abs(lit);
    if (lit > 0) return "x" + to_string(v);
    return "~x" + to_string(v);
}

static string pol_sum(const vector<string>& terms) {
    if (terms.empty()) return "";
    string s = terms[0];
    for (size_t i = 1; i < terms.size(); ++i) {
        s += " " + terms[i] + " +";
    }
    return s;
}

struct DSU {
    vector<int> p, r;
    explicit DSU(int n = 0) { reset(n); }
    void reset(int n) {
        p.resize(n + 1);
        r.assign(n + 1, 0);
        iota(p.begin(), p.end(), 0);
    }
    int find(int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }
    void unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return;
        if (r[a] < r[b]) swap(a, b);
        p[b] = a;
        if (r[a] == r[b]) ++r[a];
    }
};

struct Solver {
    int nvars = 0, nclauses_decl = 0;
    vector<Clause> clauses;
    vector<int> clause_id; // 1-based original ID for each parsed clause
    vector<vector<int>> groups; // value ids
    vector<int> group_clause_id;
    vector<Value> values;
    vector<int> var_to_value, var_block_sign;
    vector<pair<int,int>> conflict_pairs_values;
    unordered_map<unsigned long long, int> pair_clause;
    vector<vector<int>> resource_values;
    vector<vector<int>> adj; // group -> resources
    vector<vector<int>> value_for_gr; // group/resource -> value id or -1 flattened
    vector<int> matchR, matchL, dist;
    vector<int> hall_groups, hall_resources;
    int next_proof_id = 0;

    static unsigned long long key_pair(int a, int b) {
        if (a > b) swap(a, b);
        return (static_cast<unsigned long long>(static_cast<unsigned int>(a)) << 32) |
               static_cast<unsigned int>(b);
    }

    bool parse(const string& path) {
        ifstream in(path);
        if (!in) return false;
        string tok;
        int current_clause = 0;
        while (in >> tok) {
            if (tok == "c") {
                string rest;
                getline(in, rest);
                continue;
            }
            if (tok == "p") {
                string cnf;
                in >> cnf >> nvars >> nclauses_decl;
                continue;
            }
            int lit = stoi(tok);
            vector<int> lits;
            while (lit != 0) {
                lits.push_back(lit);
                in >> lit;
            }
            clauses.push_back({lits});
            clause_id.push_back(++current_clause);
        }
        if (nvars <= 0 || clauses.empty()) return false;
        next_proof_id = static_cast<int>(clauses.size());
        return true;
    }

    bool detect() {
        var_to_value.assign(nvars + 1, -1);
        var_block_sign.assign(nvars + 1, 0);
        values.clear();
        groups.clear();
        group_clause_id.clear();

        for (size_t ci = 0; ci < clauses.size(); ++ci) {
            const auto& c = clauses[ci].lits;
            if (c.size() <= 2) continue;
            int g = static_cast<int>(groups.size());
            vector<int> group;
            int prev = 0;
            for (int lit : c) {
                int v = abs(lit);
                if (v < 1 || v > nvars) return false;
                if (var_to_value[v] != -1) return false;
                if (prev && v != prev + 1) return false;
                prev = v;
                int id = static_cast<int>(values.size());
                values.push_back({v, lit, g, -1});
                var_to_value[v] = id;
                var_block_sign[v] = (lit > 0 ? 1 : -1);
                group.push_back(id);
            }
            groups.push_back(group);
            group_clause_id.push_back(clause_id[ci]);
        }
        if (groups.size() < 4 || values.size() < 100) return false;

        bool changed = true;
        while (changed) {
            changed = false;
            for (const auto& cl : clauses) {
                const auto& c = cl.lits;
                if (c.size() != 2) continue;
                int va = abs(c[0]), vb = abs(c[1]);
                int sa = (c[0] > 0 ? 1 : -1);
                int sb = (c[1] > 0 ? 1 : -1);
                if (var_block_sign[va] && !var_block_sign[vb]) {
                    var_block_sign[vb] = -sb;
                    changed = true;
                } else if (var_block_sign[vb] && !var_block_sign[va]) {
                    var_block_sign[va] = -sa;
                    changed = true;
                }
            }
        }

        for (size_t ci = 0; ci < clauses.size(); ++ci) {
            const auto& c = clauses[ci].lits;
            if (c.size() != 1) continue;
            int v = abs(c[0]);
            if (var_block_sign[v] == 0) var_block_sign[v] = (c[0] > 0 ? 1 : -1);
            int rel = (c[0] > 0 ? 1 : -1) * var_block_sign[v];
            if (rel != 1) continue;
            if (var_to_value[v] != -1) continue;
            int g = static_cast<int>(groups.size());
            int id = static_cast<int>(values.size());
            values.push_back({v, c[0], g, -1});
            var_to_value[v] = id;
            groups.push_back(vector<int>{id});
            group_clause_id.push_back(clause_id[ci]);
        }

        for (int v = 1; v <= nvars; ++v) {
            if (var_block_sign[v] == 0) return false;
        }

        DSU dsu(nvars);
        conflict_pairs_values.clear();
        pair_clause.clear();
        for (size_t ci = 0; ci < clauses.size(); ++ci) {
            const auto& c = clauses[ci].lits;
            if (c.size() != 2) continue;
            int va = abs(c[0]), vb = abs(c[1]);
            int rela = (c[0] > 0 ? 1 : -1) * var_block_sign[va];
            int relb = (c[1] > 0 ? 1 : -1) * var_block_sign[vb];
            if (rela != -1 || relb != -1) return false;
            dsu.unite(va, vb);
            pair_clause[key_pair(va, vb)] = clause_id[ci];
        }

        unordered_map<int,int> root_to_res;
        for (int i = 0; i < static_cast<int>(values.size()); ++i) {
            int root = dsu.find(values[i].var);
            auto [it, inserted] = root_to_res.emplace(root, static_cast<int>(root_to_res.size()));
            values[i].resource = it->second;
        }
        resource_values.assign(root_to_res.size(), {});
        for (int i = 0; i < static_cast<int>(values.size()); ++i) {
            resource_values[values[i].resource].push_back(i);
        }

        vector<vector<int>> seen(resource_values.size(), vector<int>());
        for (int r = 0; r < static_cast<int>(resource_values.size()); ++r) {
            vector<int> gs;
            for (int id : resource_values[r]) gs.push_back(values[id].group);
            sort(gs.begin(), gs.end());
            if (adjacent_find(gs.begin(), gs.end()) != gs.end()) return false;
            int k = static_cast<int>(resource_values[r].size());
            if (k > 1) {
                long long have = 0;
                for (int i = 0; i < k; ++i) {
                    for (int j = i + 1; j < k; ++j) {
                        if (!pair_clause.count(key_pair(values[resource_values[r][i]].var,
                                                       values[resource_values[r][j]].var))) return false;
                        ++have;
                    }
                }
                if (have != 1LL * k * (k - 1) / 2) return false;
            }
        }

        int G = static_cast<int>(groups.size());
        int R = static_cast<int>(resource_values.size());
        adj.assign(G, {});
        value_for_gr.assign(G, vector<int>(R, -1));
        for (int id = 0; id < static_cast<int>(values.size()); ++id) {
            int g = values[id].group, r = values[id].resource;
            adj[g].push_back(r);
            value_for_gr[g][r] = id;
        }
        for (auto& a : adj) {
            sort(a.begin(), a.end());
            a.erase(unique(a.begin(), a.end()), a.end());
        }
        return true;
    }

    bool bfs_matching() {
        int G = static_cast<int>(groups.size());
        queue<int> q;
        for (int g = 0; g < G; ++g) {
            if (matchL[g] < 0) {
                dist[g] = 0;
                q.push(g);
            } else {
                dist[g] = -1;
            }
        }
        bool found = false;
        while (!q.empty()) {
            int g = q.front(); q.pop();
            for (int r : adj[g]) {
                int ng = matchR[r];
                if (ng < 0) {
                    found = true;
                } else if (dist[ng] < 0) {
                    dist[ng] = dist[g] + 1;
                    q.push(ng);
                }
            }
        }
        return found;
    }

    bool dfs_matching(int g) {
        for (int r : adj[g]) {
            int ng = matchR[r];
            if (ng < 0 || (dist[ng] == dist[g] + 1 && dfs_matching(ng))) {
                matchL[g] = r;
                matchR[r] = g;
                return true;
            }
        }
        dist[g] = -1;
        return false;
    }

    int max_matching() {
        int G = static_cast<int>(groups.size());
        int R = static_cast<int>(resource_values.size());
        matchL.assign(G, -1);
        matchR.assign(R, -1);
        dist.assign(G, -1);
        int m = 0;
        while (bfs_matching()) {
            for (int g = 0; g < G; ++g) {
                if (matchL[g] < 0 && dfs_matching(g)) ++m;
            }
        }
        return m;
    }

    void compute_hall_set() {
        int G = static_cast<int>(groups.size());
        int R = static_cast<int>(resource_values.size());
        vector<char> visL(G, 0), visR(R, 0);
        queue<int> q;
        for (int g = 0; g < G; ++g) {
            if (matchL[g] < 0) {
                visL[g] = 1;
                q.push(g);
            }
        }
        while (!q.empty()) {
            int g = q.front(); q.pop();
            for (int r : adj[g]) {
                if (matchL[g] == r) continue; // unmatched edges from left to right
                if (visR[r]) continue;
                visR[r] = 1;
                int ng = matchR[r];
                if (ng >= 0 && !visL[ng]) {
                    visL[ng] = 1;
                    q.push(ng);
                }
            }
        }
        hall_groups.clear();
        hall_resources.clear();
        for (int g = 0; g < G; ++g) if (visL[g]) hall_groups.push_back(g);
        for (int r = 0; r < R; ++r) if (visR[r]) hall_resources.push_back(r);
    }

    int emit_pol(ofstream& out, const string& expr) {
        out << "pol " << expr << " ;\n";
        return ++next_proof_id;
    }

    int emit_amo_for_values(ofstream& out, const vector<int>& vals) {
        int k = static_cast<int>(vals.size());
        if (k < 2) return -1;
        auto pair_id = [&](int a, int b) -> int {
            auto it = pair_clause.find(key_pair(values[a].var, values[b].var));
            return it == pair_clause.end() ? -1 : it->second;
        };
        int current = pair_id(vals[0], vals[1]);
        if (current < 0) return -1;
        for (int i = 3; i <= k; ++i) {
            vector<string> pair_terms;
            for (int j = 0; j < i - 1; ++j) {
                int pid = pair_id(vals[j], vals[i - 1]);
                if (pid < 0) return -1;
                pair_terms.push_back(to_string(pid));
            }
            int pair_sum = emit_pol(out, pol_sum(pair_terms));
            int d = emit_pol(out, to_string(current) + " " + to_string(pair_sum) + " + 2 d");
            int q = i / 2; // ceil((i - 1) / 2)
            if (q <= 1) {
                current = d;
            } else {
                current = emit_pol(out, to_string(d) + " " + to_string(current) + " " +
                                        to_string(q - 1) + " * + " + to_string(q) + " d");
            }
        }
        return current;
    }

    bool write_hall_proof(const string& out_path) {
        vector<char> in_hall_group(groups.size(), 0);
        for (int g : hall_groups) in_hall_group[g] = 1;

        vector<vector<int>> vals_by_res(resource_values.size());
        for (int g : hall_groups) {
            for (int id : groups[g]) vals_by_res[values[id].resource].push_back(id);
        }
        int used_resources = 0, occurrences = 0;
        for (auto& v : vals_by_res) {
            if (!v.empty()) {
                ++used_resources;
                occurrences += static_cast<int>(v.size());
            }
        }
        if (static_cast<int>(hall_groups.size()) <= used_resources) return false;

        ofstream out(out_path);
        if (!out) return false;
        out << "pseudo-Boolean proof version 3.0\n";

        vector<string> final_terms;
        for (int g : hall_groups) final_terms.push_back(to_string(group_clause_id[g]));
        for (auto& vals : vals_by_res) {
            if (vals.empty()) continue;
            if (vals.size() == 1) {
                int ylit = values[vals[0]].lit;
                final_terms.push_back(lit_name_from_signed_var(-ylit));
            } else {
                int id = emit_amo_for_values(out, vals);
                if (id < 0) return false;
                final_terms.push_back(to_string(id));
            }
        }
        int contradiction = emit_pol(out, pol_sum(final_terms));
        out << "output NONE ;\n";
        out << "conclusion UNSAT : " << contradiction << " ;\n";
        out << "end pseudo-Boolean proof ;\n";
        return true;
    }

    int run(const string& cnf_path, const string& proof_path) {
        if (!parse(cnf_path)) return 0;
        if (!detect()) return 0;
        cerr << "c detected interval-matching encoding: groups=" << groups.size()
             << " values=" << values.size()
             << " resources=" << resource_values.size() << "\n";
        int m = max_matching();
        if (m == static_cast<int>(groups.size())) {
            cout << "c matching exists in recovered core; returning UNKNOWN for soundness\n";
            cout << "s UNKNOWN\n";
            return 0;
        }
        compute_hall_set();
        cerr << "c Hall set groups=" << hall_groups.size()
             << " resources=" << hall_resources.size() << "\n";
        if (!write_hall_proof(proof_path)) {
            cout << "s UNKNOWN\n";
            return 0;
        }
        cout << "s UNSATISFIABLE\n";
        return 20;
    }
};

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