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

using std::size_t;

struct Formula {
    int nvars = 0;
    int nclauses_header = 0;
    std::vector<int> lits;
    std::vector<uint32_t> starts;
    std::vector<uint16_t> lens;
};

struct StoneInstance {
    int V = 0; // number of graph vertices and stones
    int sink = -1;
    std::vector<int> sources;

    std::vector<int> p_true_lit;      // by DIMACS variable, 0 for non-placement
    std::vector<int> p_index;         // by DIMACS variable, dense placement index
    std::vector<int> row_of_p;        // by DIMACS variable
    std::vector<int> stone_of_p;      // by DIMACS variable, dense stone index
    std::vector<int> color_index;     // by DIMACS variable, dense stone index or -1
    std::vector<int> color_var;       // by stone
    std::vector<int> red_lit;         // actual DIMACS literal meaning "stone is red"
    std::vector<int> p_lit_at;        // row * V + stone -> actual literal meaning p(row,stone)

    std::vector<int> pred_a;
    std::vector<int> pred_b;
    std::vector<std::vector<int>> succ;
    std::vector<uint8_t> present;     // row * V^3 + predStoneA * V^2 + predStoneB * V + stone
    std::vector<uint8_t> ancestor_of_sink;

    struct Missing {
        int row = -1;
        int pred_a = -1;
        int pred_b = -1;
        int stone_a = -1;
        int stone_b = -1;
        int stone_c = -1;
    };
    std::vector<Missing> missing_ancestors;
};

static bool parse_formula(const char* path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;

    std::string line;
    std::vector<int> tmp;
    while (std::getline(in, line)) {
        if (line.empty()) continue;
        if (line[0] == 'c') continue;
        if (line[0] == 'p') {
            char cnf[16] = {0};
            if (std::sscanf(line.c_str(), "p %15s %d %d", cnf, &f.nvars, &f.nclauses_header) != 3) {
                return false;
            }
            continue;
        }
        tmp.clear();
        const char* p = line.c_str();
        while (*p) {
            while (*p && std::isspace(static_cast<unsigned char>(*p))) ++p;
            if (!*p) break;
            char* end = nullptr;
            long v = std::strtol(p, &end, 10);
            if (end == p) break;
            p = end;
            if (v == 0) break;
            if (v < INT32_MIN || v > INT32_MAX) return false;
            tmp.push_back(static_cast<int>(v));
        }
        if (!tmp.empty()) {
            f.starts.push_back(static_cast<uint32_t>(f.lits.size()));
            f.lens.push_back(static_cast<uint16_t>(tmp.size()));
            f.lits.insert(f.lits.end(), tmp.begin(), tmp.end());
        }
    }
    return f.nvars > 0 && !f.starts.empty();
}

static inline bool lit_satisfied(int lit, const std::vector<uint8_t>& assignment) {
    const int v = std::abs(lit);
    const bool val = assignment[v] != 0;
    return lit > 0 ? val : !val;
}

static bool verify_assignment(const Formula& f, const std::vector<uint8_t>& assignment) {
    if (static_cast<int>(assignment.size()) <= f.nvars) return false;
    for (size_t ci = 0; ci < f.starts.size(); ++ci) {
        bool sat = false;
        const uint32_t st = f.starts[ci];
        const uint16_t len = f.lens[ci];
        for (uint16_t j = 0; j < len; ++j) {
            if (lit_satisfied(f.lits[st + j], assignment)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static inline bool contains_lit(const std::vector<int>& xs, int lit) {
    for (int x : xs) {
        if (x == lit) return true;
    }
    return false;
}

static bool detect_stone(const Formula& f, StoneInstance& s) {
    int max_len = 0;
    for (uint16_t len : f.lens) max_len = std::max(max_len, static_cast<int>(len));
    if (max_len <= 10) return false;
    s.V = max_len;
    const int V = s.V;
    if (f.nvars != V * (V + 1)) return false;

    std::vector<int> long_clause_rows;
    std::vector<int> binary_clauses;
    for (size_t ci = 0; ci < f.lens.size(); ++ci) {
        if (f.lens[ci] == V) long_clause_rows.push_back(static_cast<int>(ci));
        else if (f.lens[ci] == 2) binary_clauses.push_back(static_cast<int>(ci));
        else if (f.lens[ci] != 5 && f.lens[ci] != 6) return false;
    }
    if (static_cast<int>(long_clause_rows.size()) != V) return false;
    if (static_cast<int>(binary_clauses.size()) != 4 * V) return false;

    s.p_true_lit.assign(f.nvars + 1, 0);
    s.p_index.assign(f.nvars + 1, -1);
    s.row_of_p.assign(f.nvars + 1, -1);
    s.stone_of_p.assign(f.nvars + 1, -1);
    s.color_index.assign(f.nvars + 1, -1);

    std::vector<int> p_vars;
    p_vars.reserve(V * V);
    for (int row = 0; row < V; ++row) {
        const int ci = long_clause_rows[row];
        const uint32_t st = f.starts[ci];
        for (int j = 0; j < V; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            if (var < 1 || var > f.nvars) return false;
            if (s.p_true_lit[var] != 0) return false;
            s.p_true_lit[var] = lit;
            s.row_of_p[var] = row;
            s.p_index[var] = static_cast<int>(p_vars.size());
            p_vars.push_back(var);
        }
    }
    if (static_cast<int>(p_vars.size()) != V * V) return false;

    s.color_var.reserve(V);
    for (int var = 1; var <= f.nvars; ++var) {
        if (s.p_true_lit[var] == 0) {
            s.color_index[var] = static_cast<int>(s.color_var.size());
            s.color_var.push_back(var);
        }
    }
    if (static_cast<int>(s.color_var.size()) != V) return false;

    std::vector<int> color_pos(V, 0), color_neg(V, 0);
    std::vector<std::vector<int>> bin_color_lits_by_row(V);
    for (int ci : binary_clauses) {
        int p_lit = 0, c_lit = 0;
        const uint32_t st = f.starts[ci];
        for (int j = 0; j < 2; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            if (s.p_true_lit[var] != 0) p_lit = lit;
            else if (s.color_index[var] >= 0) c_lit = lit;
        }
        if (p_lit == 0 || c_lit == 0) return false;
        const int pvar = std::abs(p_lit);
        if (p_lit != -s.p_true_lit[pvar]) return false;
        const int cidx = s.color_index[std::abs(c_lit)];
        if (c_lit > 0) ++color_pos[cidx];
        else ++color_neg[cidx];
        bin_color_lits_by_row[s.row_of_p[pvar]].push_back(c_lit);
    }

    s.red_lit.assign(V, 0);
    for (int i = 0; i < V; ++i) {
        if (color_pos[i] + color_neg[i] != 4) return false;
        if (color_pos[i] == color_neg[i]) return false;
        s.red_lit[i] = (color_pos[i] > color_neg[i]) ? s.color_var[i] : -s.color_var[i];
    }

    for (int row = 0; row < V; ++row) {
        if (static_cast<int>(bin_color_lits_by_row[row].size()) == V) {
            int red_count = 0;
            for (int lit : bin_color_lits_by_row[row]) {
                const int cidx = s.color_index[std::abs(lit)];
                if (lit == s.red_lit[cidx]) ++red_count;
            }
            if (red_count == V) s.sources.push_back(row);
            else if (red_count == 0) {
                if (s.sink != -1) return false;
                s.sink = row;
            } else {
                return false;
            }
        } else if (!bin_color_lits_by_row[row].empty()) {
            return false;
        }
    }
    if (static_cast<int>(s.sources.size()) != 3 || s.sink < 0) return false;

    std::vector<uint32_t> co(static_cast<size_t>(V) * V * V, 0);
    for (size_t ci = 0; ci < f.lens.size(); ++ci) {
        std::vector<int> colors;
        colors.reserve(3);
        const uint32_t st = f.starts[ci];
        const uint16_t len = f.lens[ci];
        for (uint16_t j = 0; j < len; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            if (s.color_index[var] >= 0) colors.push_back(s.color_index[var]);
        }
        if (colors.empty()) continue;
        for (uint16_t j = 0; j < len; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            if (s.p_index[var] >= 0 && lit == -s.p_true_lit[var]) {
                uint32_t* row = &co[static_cast<size_t>(s.p_index[var]) * V];
                for (int cidx : colors) ++row[cidx];
            }
        }
    }

    s.p_lit_at.assign(V * V, 0);
    for (int var : p_vars) {
        const int pi = s.p_index[var];
        const uint32_t* row = &co[static_cast<size_t>(pi) * V];
        int best = -1;
        uint32_t best_count = 0;
        for (int c = 0; c < V; ++c) {
            if (row[c] > best_count) {
                best_count = row[c];
                best = c;
            }
        }
        if (best < 0 || best_count == 0) return false;
        s.stone_of_p[var] = best;
    }
    for (int row = 0; row < V; ++row) {
        std::vector<uint8_t> seen(V, 0);
        const int ci = long_clause_rows[row];
        const uint32_t st = f.starts[ci];
        for (int j = 0; j < V; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            const int stone = s.stone_of_p[var];
            if (stone < 0 || stone >= V || seen[stone]) return false;
            seen[stone] = 1;
            s.p_lit_at[row * V + stone] = s.p_true_lit[var];
        }
    }

    s.pred_a.assign(V, -1);
    s.pred_b.assign(V, -1);
    s.present.assign(static_cast<size_t>(V) * V * V * V, 0);
    std::vector<uint8_t> row_has_prop(V, 0);

    for (size_t ci = 0; ci < f.lens.size(); ++ci) {
        const uint16_t len = f.lens[ci];
        if (len != 5 && len != 6) continue;
        const uint32_t st = f.starts[ci];
        std::vector<int> color_lits;
        std::vector<int> p_lits;
        color_lits.reserve(3);
        p_lits.reserve(3);
        for (uint16_t j = 0; j < len; ++j) {
            const int lit = f.lits[st + j];
            const int var = std::abs(lit);
            if (s.color_index[var] >= 0) color_lits.push_back(lit);
            else if (s.p_index[var] >= 0) p_lits.push_back(lit);
        }
        if (static_cast<int>(p_lits.size()) != 3) return false;
        if (static_cast<int>(color_lits.size()) != static_cast<int>(len) - 3) return false;

        std::vector<std::pair<int, int>> preds;
        std::vector<std::pair<int, int>> concl;
        for (int plit : p_lits) {
            const int pvar = std::abs(plit);
            if (plit != -s.p_true_lit[pvar]) return false;
            const int stone = s.stone_of_p[pvar];
            const int rl = s.red_lit[stone];
            const bool has_red = contains_lit(color_lits, rl);
            const bool has_not_red = contains_lit(color_lits, -rl);
            if (has_red && !has_not_red) concl.push_back({s.row_of_p[pvar], stone});
            else if (has_not_red && !has_red) preds.push_back({s.row_of_p[pvar], stone});
            else return false;
        }
        if (concl.size() != 1 || preds.size() != 2) return false;
        const int row = concl[0].first;
        const int z = concl[0].second;
        int ra = preds[0].first, rb = preds[1].first;
        int x = preds[0].second, y = preds[1].second;
        if (ra == rb) return false;
        if (rb < ra) {
            std::swap(ra, rb);
            std::swap(x, y);
        }
        if (s.pred_a[row] == -1) {
            s.pred_a[row] = ra;
            s.pred_b[row] = rb;
        } else if (s.pred_a[row] != ra || s.pred_b[row] != rb) {
            return false;
        }
        if (z == x || z == y) return false;
        const size_t idx = (((static_cast<size_t>(row) * V + x) * V + y) * V + z);
        s.present[idx] = 1;
        row_has_prop[row] = 1;
    }

    int prop_rows = 0;
    for (int row = 0; row < V; ++row) {
        if (row_has_prop[row]) ++prop_rows;
        if (row_has_prop[row] && s.pred_a[row] < 0) return false;
    }
    if (prop_rows != V - 3) return false;
    for (int src : s.sources) {
        if (s.pred_a[src] != -1) return false;
    }
    if (s.pred_a[s.sink] == -1) return false;

    s.succ.assign(V, {});
    for (int row = 0; row < V; ++row) {
        if (s.pred_a[row] >= 0) {
            s.succ[s.pred_a[row]].push_back(row);
            s.succ[s.pred_b[row]].push_back(row);
        }
    }

    s.ancestor_of_sink.assign(V, 0);
    std::function<bool(int)> mark_anc = [&](int row) -> bool {
        if (s.ancestor_of_sink[row]) return true;
        s.ancestor_of_sink[row] = 1;
        if (s.pred_a[row] >= 0) {
            mark_anc(s.pred_a[row]);
            mark_anc(s.pred_b[row]);
        }
        return true;
    };
    mark_anc(s.sink);

    for (int row = 0; row < V; ++row) {
        if (s.pred_a[row] < 0) continue;
        for (int x = 0; x < V; ++x) {
            for (int y = 0; y < V; ++y) {
                for (int z = 0; z < V; ++z) {
                    if (z == x || z == y) continue;
                    const size_t idx = (((static_cast<size_t>(row) * V + x) * V + y) * V + z);
                    if (!s.present[idx] && s.ancestor_of_sink[row]) {
                        s.missing_ancestors.push_back({row, s.pred_a[row], s.pred_b[row], x, y, z});
                    }
                }
            }
        }
    }

    return true;
}

static bool build_sat_assignment(const Formula& f, const StoneInstance& s, std::vector<uint8_t>& assignment) {
    if (s.missing_ancestors.empty()) return false;
    const auto miss = s.missing_ancestors.front();
    const int V = s.V;

    std::vector<uint8_t> blue_row(V, 0);
    std::vector<int> stack = {miss.row};
    blue_row[miss.row] = 1;
    while (!stack.empty()) {
        const int u = stack.back();
        stack.pop_back();
        for (int v : s.succ[u]) {
            if (!blue_row[v]) {
                blue_row[v] = 1;
                stack.push_back(v);
            }
        }
    }
    if (!blue_row[s.sink]) return false;
    for (int src : s.sources) {
        if (blue_row[src]) return false;
    }
    if (blue_row[miss.pred_a] || blue_row[miss.pred_b]) return false;

    std::vector<int> row_stone(V, miss.stone_a);
    for (int row = 0; row < V; ++row) {
        if (blue_row[row]) row_stone[row] = miss.stone_c;
    }
    row_stone[miss.pred_a] = miss.stone_a;
    row_stone[miss.pred_b] = miss.stone_b;

    assignment.assign(f.nvars + 1, 0);
    for (int row = 0; row < V; ++row) {
        for (int stone = 0; stone < V; ++stone) {
            const int lit = s.p_lit_at[row * V + stone];
            const bool make_true = (stone == row_stone[row]);
            assignment[std::abs(lit)] = (lit > 0) ? make_true : !make_true;
        }
    }
    for (int stone = 0; stone < V; ++stone) {
        const int lit = s.red_lit[stone];
        const bool red = (stone != miss.stone_c);
        assignment[std::abs(lit)] = (lit > 0) ? red : !red;
    }
    return verify_assignment(f, assignment);
}

static void print_model(const std::vector<uint8_t>& assignment, int nvars) {
    std::printf("s SATISFIABLE\n");
    int on_line = 0;
    std::printf("v");
    for (int v = 1; v <= nvars; ++v) {
        const int lit = assignment[v] ? v : -v;
        std::printf(" %d", lit);
        if (++on_line == 20) {
            std::printf(" 0\n");
            if (v != nvars) std::printf("v");
            on_line = 0;
        }
    }
    if (on_line != 0) std::printf(" 0\n");
}

static void write_pb_lit(FILE* out, int lit) {
    if (lit > 0) std::fprintf(out, "1 x%d ", lit);
    else std::fprintf(out, "1 ~x%d ", -lit);
}

static uint64_t write_rup_clause(FILE* out, const std::vector<int>& lits, uint64_t& next_id) {
    std::fputs("rup ", out);
    for (int lit : lits) write_pb_lit(out, lit);
    std::fputs(">= 1 ;\n", out);
    return next_id++;
}

static void write_del_range(FILE* out, uint64_t first, uint64_t last) {
    if (first > last) return;
    std::fprintf(out, "del range %llu %llu ;\n",
                 static_cast<unsigned long long>(first),
                 static_cast<unsigned long long>(last));
}

static bool topo_from_sink(const StoneInstance& s, std::vector<int>& order) {
    const int V = s.V;
    std::vector<uint8_t> state(V, 0);
    bool ok = true;
    std::function<void(int)> dfs = [&](int row) {
        if (!ok) return;
        if (state[row] == 2) return;
        if (state[row] == 1) {
            ok = false;
            return;
        }
        state[row] = 1;
        if (s.pred_a[row] >= 0) {
            dfs(s.pred_a[row]);
            dfs(s.pred_b[row]);
        }
        state[row] = 2;
        order.push_back(row);
    };
    dfs(s.sink);
    return ok;
}

static bool write_unsat_proof(const char* proof_path, const Formula& f, const StoneInstance& s) {
    const int V = s.V;
    // The proof is direct but intentionally bounded: above this size, proof
    // checking is likely to dominate the benchmark budget.
    if (V > 62) return false;
    if (!s.missing_ancestors.empty()) return false;

    std::vector<int> order;
    if (!topo_from_sink(s, order)) return false;

    FILE* out = std::fopen(proof_path, "w");
    if (!out) return false;
    std::fputs("pseudo-Boolean proof version 3.0\n\n", out);
    uint64_t next_id = static_cast<uint64_t>(f.starts.size()) + 1;

    std::vector<uint8_t> source_row(V, 0);
    for (int src : s.sources) source_row[src] = 1;

    for (int row : order) {
        if (!s.ancestor_of_sink[row]) continue;
        if (source_row[row]) continue; // source redness clauses are original binary clauses
        if (s.pred_a[row] < 0) {
            std::fclose(out);
            return false;
        }
        const int a = s.pred_a[row];
        const int b = s.pred_b[row];
        for (int z = 0; z < V; ++z) {
            const int p_row_z_false = -s.p_lit_at[row * V + z];
            const int red_z = s.red_lit[z];

            for (int x = 0; x < V; ++x) {
                const int p_a_x_false = -s.p_lit_at[a * V + x];
                uint64_t first_d = next_id;
                if (x != z) {
                    for (int y = 0; y < V; ++y) {
                        if (y == z) continue;
                        const int p_b_y_false = -s.p_lit_at[b * V + y];
                        write_rup_clause(out, {p_a_x_false, p_b_y_false, p_row_z_false, red_z}, next_id);
                    }
                }
                uint64_t last_d = next_id - 1;
                write_rup_clause(out, {p_a_x_false, p_row_z_false, red_z}, next_id);
                write_del_range(out, first_d, last_d);
            }
            write_rup_clause(out, {p_row_z_false, red_z}, next_id);
        }
    }

    for (int z = 0; z < V; ++z) {
        const int p_sink_z_false = -s.p_lit_at[s.sink * V + z];
        write_rup_clause(out, {p_sink_z_false}, next_id);
    }
    std::fputs("rup >= 1 ;\n\n", out);
    std::fputs("output NONE ;\n", out);
    std::fputs("conclusion UNSAT : -1 ;\n", out);
    std::fputs("end pseudo-Boolean proof ;\n", out);
    const bool ok = (std::fclose(out) == 0);
    return ok;
}

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

    Formula f;
    if (!parse_formula(argv[1], f)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }

    StoneInstance s;
    if (!detect_stone(f, s)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }

    std::vector<uint8_t> assignment;
    if (build_sat_assignment(f, s, assignment)) {
        print_model(assignment, f.nvars);
        return 10;
    }

    if (write_unsat_proof(argv[2], f, s)) {
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }

    std::printf("s UNKNOWN\n");
    return 0;
}
