#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

using Clause = std::vector<int>;

struct Formula {
    int vars = 0;
    int declared_clauses = 0;
    std::vector<Clause> clauses;
};

struct Detected {
    int n = 0;
    int k = 0;
    int c = 0;
    int edge_vars = 0;
    int sel_start = 0;
    int color_start = 0;
};

static bool parse_dimacs(const std::string& path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            in >> fmt >> f.vars >> f.declared_clauses;
            if (fmt != "cnf" || f.vars <= 0 || f.declared_clauses < 0) return false;
        } else {
            Clause cl;
            int lit = std::stoi(tok);
            while (lit != 0) {
                if (lit == 0 || std::abs(lit) > f.vars) return false;
                cl.push_back(lit);
                if (!(in >> lit)) return false;
            }
            f.clauses.push_back(std::move(cl));
        }
    }
    return f.vars > 0 && (f.declared_clauses == 0 || (int)f.clauses.size() == f.declared_clauses);
}

static int edge_var(int u, int v) {
    if (u > v) std::swap(u, v);
    return v * (v - 1) / 2 + u + 1; // 0-based vertices, DIMACS vars start at 1.
}

static int sel_var(const Detected& d, int p, int v) {
    return d.sel_start + p * d.n + v;
}

static int color_var(const Detected& d, int v, int a) {
    return d.color_start + v * d.c + a;
}

static bool all_positive_distinct(const Clause& cl) {
    for (int x : cl) if (x <= 0) return false;
    std::vector<int> s = cl;
    std::sort(s.begin(), s.end());
    return std::adjacent_find(s.begin(), s.end()) == s.end();
}

static bool detect_structure(const Formula& f, Detected& d) {
    std::unordered_map<int, int> positive_long;
    for (const auto& cl : f.clauses) {
        if ((int)cl.size() >= 1 && all_positive_distinct(cl)) {
            positive_long[(int)cl.size()]++;
        }
    }
    std::vector<std::pair<int, int>> sizes(positive_long.begin(), positive_long.end());
    std::sort(sizes.begin(), sizes.end());

    for (auto [n, k] : sizes) {
        if (n < 2) continue;
        long long m = 1LL * n * (n - 1) / 2;
        for (auto [c, n_count] : sizes) {
            if (n_count != n || c < 1) continue;
            long long total = m + 1LL * k * n + 1LL * n * c;
            if (total != f.vars) continue;
            Detected cand;
            cand.n = n;
            cand.k = k;
            cand.c = c;
            cand.edge_vars = (int)m;
            cand.sel_start = cand.edge_vars + 1;
            cand.color_start = cand.sel_start + cand.k * cand.n;
            bool ok = true;

            for (int p = 0; ok && p < cand.k; ++p) {
                std::vector<int> expect;
                for (int v = 0; v < cand.n; ++v) expect.push_back(sel_var(cand, p, v));
                ok = false;
                for (const auto& cl : f.clauses) {
                    if (cl == expect) {
                        ok = true;
                        break;
                    }
                }
            }
            for (int v = 0; ok && v < cand.n; ++v) {
                std::vector<int> expect;
                for (int a = 0; a < cand.c; ++a) expect.push_back(color_var(cand, v, a));
                ok = false;
                for (const auto& cl : f.clauses) {
                    if (cl == expect) {
                        ok = true;
                        break;
                    }
                }
            }
            if (ok) {
                d = cand;
                return true;
            }
        }
    }
    return false;
}

static bool check_model(const Formula& f, const std::vector<int>& val) {
    if ((int)val.size() != f.vars + 1) return false;
    for (const auto& cl : f.clauses) {
        bool sat = false;
        for (int lit : cl) {
            int v = std::abs(lit);
            bool b = val[v] > 0;
            if ((lit > 0 && b) || (lit < 0 && !b)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static std::vector<int> construct_sat_model(const Formula& f, const Detected& d) {
    std::vector<int> val(f.vars + 1, -1);

    for (int p = 0; p < d.k; ++p) {
        val[sel_var(d, p, p)] = 1;
    }
    for (int v = 0; v < d.n; ++v) {
        int a = (v < d.k) ? v : 0;
        if (a >= d.c) a = 0;
        val[color_var(d, v, a)] = 1;
    }
    for (int u = 0; u < d.k; ++u) {
        for (int v = u + 1; v < d.k; ++v) {
            val[edge_var(u, v)] = 1;
        }
    }

    for (int i = 1; i <= f.vars; ++i) {
        if (val[i] != 1) val[i] = -1;
    }
    return val;
}

static void write_empty_proof(const std::string& path) {
    std::ofstream out(path);
    out << "pseudo-Boolean proof version 3.0\n";
    out << "output NONE ;\n";
    out << "conclusion NONE ;\n";
    out << "end pseudo-Boolean proof ;\n";
}

static int z_var(const Detected& d, int base, int p, int v, int a) {
    return base + (p * d.n + v) * d.c + a;
}

static void add_lit(std::ostream& out, int var, bool positive, int coef = 1) {
    out << (coef >= 0 ? "+" : "") << coef << " ";
    if (!positive) out << "~";
    out << "x" << var << " ";
}

static bool write_unsat_proof(const std::string& path, const Formula& f, const Detected& d) {
    if (!(d.k > d.c)) return false;

    std::ofstream out(path);
    if (!out) return false;
    int next_id = (int)f.clauses.size();
    std::vector<int> position_ids;
    std::vector<int> color_amo_ids;
    const int base = f.vars + 1;
    const int per_color = d.k * d.n;

    out << "pseudo-Boolean proof version 3.0\n";
    out << "f " << f.clauses.size() << " ;\n";

    for (int p = 0; p < d.k; ++p) {
        for (int v = 0; v < d.n; ++v) {
            int y = sel_var(d, p, v);
            for (int a = 0; a < d.c; ++a) {
                int x = color_var(d, v, a);
                int z = z_var(d, base, p, v, a);
                out << "red ";
                add_lit(out, z, false);
                add_lit(out, y, true);
                out << ">= 1 : x" << z << " -> 0 ;\n";
                ++next_id;

                out << "red ";
                add_lit(out, z, false);
                add_lit(out, x, true);
                out << ">= 1 : x" << z << " -> 0 ;\n";
                ++next_id;

                out << "red ";
                add_lit(out, z, true);
                add_lit(out, y, false);
                add_lit(out, x, false);
                out << ">= 1 : x" << z << " -> 1 ;\n";
                ++next_id;
            }

            out << "rup ";
            for (int a = 0; a < d.c; ++a) add_lit(out, z_var(d, base, p, v, a), true);
            add_lit(out, y, false, d.c);
            out << ">= 1 ;\n";
            ++next_id;
        }

        out << "rup ";
        for (int v = 0; v < d.n; ++v) {
            for (int a = 0; a < d.c; ++a) add_lit(out, z_var(d, base, p, v, a), true);
        }
        out << ">= 1 ;\n";
        ++next_id;
        position_ids.push_back(next_id);
    }

    for (int a = 0; a < d.c; ++a) {
        std::vector<int> zs;
        zs.reserve(per_color);
        for (int p = 0; p < d.k; ++p) {
            for (int v = 0; v < d.n; ++v) zs.push_back(z_var(d, base, p, v, a));
        }

        for (int i = 0; i < (int)zs.size(); ++i) {
            for (int j = i + 1; j < (int)zs.size(); ++j) {
                out << "rup ";
                add_lit(out, zs[i], false);
                add_lit(out, zs[j], false);
                out << ">= 1 ;\n";
                ++next_id;
            }
        }

        out << "pbc ";
        for (int z : zs) add_lit(out, z, false);
        out << ">= " << (per_color - 1) << " : subproof\n";
        for (int z : zs) {
            out << "rup ";
            add_lit(out, z, false);
            out << ">= 1 ;\n";
        }
        out << "rup >= 1 ;\n";
        out << "qed pbc : -1 ;\n";
        next_id += per_color + 3;
        color_amo_ids.push_back(next_id);
    }

    out << "pol ";
    bool first = true;
    auto push_id = [&](int id) {
        if (first) {
            out << id << " ";
            first = false;
        } else {
            out << id << " + ";
        }
    };
    for (int id : position_ids) push_id(id);
    for (int id : color_amo_ids) push_id(id);
    out << ";\n";
    ++next_id;

    out << "output NONE ;\n";
    out << "conclusion UNSAT : " << next_id << " ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return true;
}

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_dimacs(argv[1], f)) {
        std::printf("c failed to parse DIMACS\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }

    Detected d;
    if (!detect_structure(f, d)) {
        std::printf("c unsupported structure\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }
    std::printf("c detected clique-coloring encoding n=%d k=%d c=%d\n", d.n, d.k, d.c);

    if (d.k <= d.c && d.n >= d.k) {
        auto model = construct_sat_model(f, d);
        if (check_model(f, model)) {
            std::printf("s SATISFIABLE\n");
            std::printf("v");
            int col = 1;
            for (int i = 1; i <= f.vars; ++i) {
                std::printf(" %d", model[i] > 0 ? i : -i);
                if (++col == 16 && i != f.vars) {
                    std::printf(" 0\nv");
                    col = 1;
                }
            }
            std::printf(" 0\n");
            write_empty_proof(argv[2]);
            return 10;
        }
        std::printf("c constructed model failed internal CNF check\ns UNKNOWN\n");
        write_empty_proof(argv[2]);
        return 0;
    }

    if (write_unsat_proof(argv[2], f, d)) {
        std::printf("c emitted VeriPB pigeonhole proof with conjunction extension variables\n");
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }

    std::printf("c failed to emit UNSAT proof\ns UNKNOWN\n");
    write_empty_proof(argv[2]);
    return 0;
}
