#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
#include <string>
#include <tuple>
#include <vector>

using Mask = unsigned __int128;

struct Clause {
    std::vector<int> lits;
};

struct Imp {
    int a, b, c;
    Mask ma, mb, mc;
};

struct PureGroup {
    long long count = 0;
    int lo = 1 << 30;
    int hi = 0;
};

static long double choose_ld(int n, int k) {
    if (k < 0 || k > n) return 0.0L;
    k = std::min(k, n - k);
    long double r = 1.0L;
    for (int i = 1; i <= k; ++i) r = r * (n - k + i) / i;
    return r;
}

static long long choose_ll(int n, int k) {
    if (k < 0 || k > n) return 0;
    k = std::min(k, n - k);
    long long r = 1;
    for (int i = 1; i <= k; ++i) {
        r = (r * (n - k + i)) / i;
    }
    return r;
}

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

static int popcount128(Mask x) {
    uint64_t lo = (uint64_t)x;
    uint64_t hi = (uint64_t)(x >> 64);
    return __builtin_popcountll(lo) + __builtin_popcountll(hi);
}

static bool eval_clause(const Clause& cl, Mask true_vars) {
    for (int lit : cl.lits) {
        Mask bit = (Mask)1 << (std::abs(lit) - 1);
        bool val = (true_vars & bit) != 0;
        if ((lit > 0 && val) || (lit < 0 && !val)) return true;
    }
    return false;
}

static bool verify_model(const std::vector<Clause>& clauses, Mask true_vars) {
    for (const Clause& cl : clauses) {
        if (!eval_clause(cl, true_vars)) return false;
    }
    return true;
}

struct Detected {
    int base_hi = 0;
    int pos_k = 0;
    int aux_lo = 0;
    int neg_k = 0;
    int aux_cap = -1;
    std::vector<Imp> imps;
};

static bool detect(int nvars, const std::vector<Clause>& clauses, Detected& d) {
    std::map<int, PureGroup> pos_groups, neg_groups;
    std::vector<std::tuple<int,int,int>> raw_imps;

    for (const Clause& cl : clauses) {
        int pos = 0, neg = 0, pos_lit = 0;
        bool all_pos = true, all_neg = true;
        int lo = 1 << 30, hi = 0;
        for (int lit : cl.lits) {
            if (lit > 0) {
                ++pos;
                pos_lit = lit;
                all_neg = false;
                lo = std::min(lo, lit);
                hi = std::max(hi, lit);
            } else {
                ++neg;
                all_pos = false;
                int v = -lit;
                lo = std::min(lo, v);
                hi = std::max(hi, v);
            }
        }

        if (cl.lits.size() == 3 && pos == 1 && neg == 2) {
            int a = 0, b = 0;
            for (int lit : cl.lits) {
                if (lit < 0) {
                    if (!a) a = -lit;
                    else b = -lit;
                }
            }
            raw_imps.emplace_back(a, b, pos_lit);
        } else if (cl.lits.size() > 1 && all_pos) {
            auto& g = pos_groups[(int)cl.lits.size()];
            ++g.count;
            g.lo = std::min(g.lo, lo);
            g.hi = std::max(g.hi, hi);
        } else if (cl.lits.size() > 1 && all_neg) {
            auto& g = neg_groups[(int)cl.lits.size()];
            ++g.count;
            g.lo = std::min(g.lo, lo);
            g.hi = std::max(g.hi, hi);
        } else {
            return false;
        }
    }

    if (pos_groups.empty()) return false;
    int best_k = 0;
    PureGroup best;
    for (auto [k, g] : pos_groups) {
        if (g.count > best.count) {
            best_k = k;
            best = g;
        }
    }
    if (best.lo != 1 || best.hi <= 0 || best.count != choose_ll(best.hi, best_k)) return false;

    d.base_hi = best.hi;
    d.pos_k = best_k;
    d.aux_lo = d.base_hi + 1;

    if (!neg_groups.empty()) {
        int nk = 0;
        PureGroup ng;
        for (auto [k, g] : neg_groups) {
            if (g.count > ng.count) {
                nk = k;
                ng = g;
            }
        }
        if (ng.lo != d.aux_lo || ng.hi != nvars || ng.count != choose_ll(nvars - d.base_hi, nk)) {
            return false;
        }
        d.neg_k = nk;
        d.aux_cap = nk - 1;
    }

    for (auto [a, b, c] : raw_imps) {
        Imp imp{a, b, c, (Mask)1 << (a - 1), (Mask)1 << (b - 1), (Mask)1 << (c - 1)};
        d.imps.push_back(imp);
    }
    return !d.imps.empty();
}

static Mask close_under_implications(Mask m, const std::vector<Imp>& imps) {
    bool changed = true;
    while (changed) {
        changed = false;
        for (const Imp& imp : imps) {
            if ((m & imp.ma) && (m & imp.mb) && !(m & imp.mc)) {
                m |= imp.mc;
                changed = true;
            }
        }
    }
    return m;
}

struct SearchState {
    int nvars;
    const Detected* d;
    const std::vector<Clause>* clauses;
    Mask base_all = 0;
    Mask aux_mask = 0;
    Mask solution = 0;
    bool found = false;
    long long tried = 0;
};

struct ProofGen {
    int nvars;
    const Detected* d;
    const std::vector<Clause>* clauses;
    std::vector<int> order;
    std::vector<std::pair<int,bool>> assumptions;
    std::ofstream out;
    uint64_t rup_count = 0;
    uint64_t max_rup = 4000000;
    bool aborted = false;

    Mask aux_mask = 0;

    void write_clause_from_assumptions() {
        if (aborted) return;
        if (++rup_count > max_rup) {
            aborted = true;
            return;
        }
        if (assumptions.empty()) {
            out << "rup >= 1 ;\n";
            return;
        }
        out << "rup";
        for (auto [v, val] : assumptions) {
            if (val) out << " 1 ~x" << v;
            else out << " 1 x" << v;
        }
        out << " >= 1 ;\n";
    }

    bool structurally_conflicting(Mask true_mask, Mask false_mask, int false_count) {
        if (false_count >= d->pos_k) return true;
        Mask closed = close_under_implications(true_mask, d->imps);
        if ((closed & false_mask) != 0) return true;
        if (d->aux_cap >= 0 && popcount128(closed & aux_mask) > d->aux_cap) return true;
        return false;
    }

    void rec(int idx, Mask true_mask, Mask false_mask, int false_count) {
        if (aborted) return;
        if (structurally_conflicting(true_mask, false_mask, false_count)) {
            write_clause_from_assumptions();
            return;
        }
        if (idx == (int)order.size()) {
            aborted = true;
            return;
        }

        int v = order[idx];
        assumptions.emplace_back(v, true);
        rec(idx + 1, true_mask | ((Mask)1 << (v - 1)), false_mask, false_count);
        assumptions.pop_back();
        if (aborted) return;

        assumptions.emplace_back(v, false);
        rec(idx + 1, true_mask, false_mask | ((Mask)1 << (v - 1)), false_count + 1);
        assumptions.pop_back();
        if (aborted) return;

        write_clause_from_assumptions();
    }
};

static void enumerate_false_sets(SearchState& st, int next, int remaining, Mask false_mask) {
    if (st.found) return;
    if (remaining == 0) {
        ++st.tried;
        Mask m = st.base_all & ~false_mask;
        m = close_under_implications(m, st.d->imps);
        if (st.d->aux_cap >= 0 && popcount128(m & st.aux_mask) > st.d->aux_cap) return;
        if (verify_model(*st.clauses, m)) {
            st.solution = m;
            st.found = true;
        }
        return;
    }
    for (int v = next; v <= st.d->base_hi - remaining + 1; ++v) {
        enumerate_false_sets(st, v + 1, remaining - 1, false_mask | ((Mask)1 << (v - 1)));
        if (st.found) return;
    }
}

static bool find_sat(int nvars, const Detected& d, const std::vector<Clause>& clauses, Mask& solution, long long& tried) {
    SearchState st;
    st.nvars = nvars;
    st.d = &d;
    st.clauses = &clauses;
    for (int v = 1; v <= d.base_hi; ++v) st.base_all |= (Mask)1 << (v - 1);
    for (int v = d.aux_lo; v <= nvars; ++v) st.aux_mask |= (Mask)1 << (v - 1);

    for (int r = 0; r < d.pos_k && !st.found; ++r) {
        enumerate_false_sets(st, 1, r, 0);
    }
    solution = st.solution;
    tried = st.tried;
    return st.found;
}

static std::vector<int> proof_order(const Detected& d) {
    std::vector<int> order(d.base_hi);
    std::iota(order.begin(), order.end(), 1);
    std::vector<int> score(d.base_hi + 1, 0);
    for (const Imp& imp : d.imps) {
        if (imp.a <= d.base_hi) score[imp.a] += 3;
        if (imp.b <= d.base_hi) score[imp.b] += 3;
        if (imp.c <= d.base_hi) score[imp.c] += 1;
    }
    std::stable_sort(order.begin(), order.end(), [&](int a, int b) {
        return score[a] > score[b];
    });
    return order;
}

static bool emit_unsat_proof(const char* out_path, int nvars, const Detected& d,
                             const std::vector<Clause>& clauses, uint64_t& rup_count) {
    std::string tmp_path = std::string(out_path) + ".tmp";
    ProofGen pg;
    pg.nvars = nvars;
    pg.d = &d;
    pg.clauses = &clauses;
    pg.order = proof_order(d);
    for (int v = d.aux_lo; v <= nvars; ++v) pg.aux_mask |= (Mask)1 << (v - 1);
    pg.out.open(tmp_path);
    if (!pg.out) return false;
    pg.out << "pseudo-Boolean proof version 3.0\n";
    pg.rec(0, 0, 0, 0);
    if (pg.aborted) {
        pg.out.close();
        std::remove(tmp_path.c_str());
        std::ofstream none(out_path);
        if (none) {
            none << "pseudo-Boolean proof version 3.0\n";
            none << "output NONE ;\n";
            none << "conclusion NONE ;\n";
            none << "end pseudo-Boolean proof ;\n";
        }
        return false;
    }
    uint64_t last_id = clauses.size() + pg.rup_count;
    pg.out << "output NONE ;\n";
    pg.out << "conclusion UNSAT : " << last_id << " ;\n";
    pg.out << "end pseudo-Boolean proof ;\n";
    pg.out.close();
    std::remove(out_path);
    if (std::rename(tmp_path.c_str(), out_path) != 0) return false;
    rup_count = pg.rup_count;
    return true;
}

static void print_model(int nvars, Mask m) {
    std::printf("s SATISFIABLE\n");
    std::printf("v");
    for (int v = 1; v <= nvars; ++v) {
        bool val = (m & ((Mask)1 << (v - 1))) != 0;
        std::printf(" %d", val ? v : -v);
        if (v % 20 == 0 && v != nvars) std::printf(" 0\nv");
    }
    std::printf(" 0\n");
}

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

    int nvars = 0;
    std::vector<Clause> clauses;
    if (!parse_dimacs(argv[1], nvars, clauses)) {
        std::printf("c failed to parse DIMACS\ns UNKNOWN\n");
        return 0;
    }

    Detected d;
    if (!detect(nvars, clauses, d)) {
        std::printf("c unsupported clause structure\ns UNKNOWN\n");
        return 0;
    }

    std::printf("c detected closure/cardinality instance: vars=%d base=1..%d pos_k=%d aux=%d..%d",
                nvars, d.base_hi, d.pos_k, d.aux_lo, nvars);
    if (d.aux_cap >= 0) std::printf(" aux_cap=%d", d.aux_cap);
    else std::printf(" aux_cap=none");
    std::printf(" implications=%zu\n", d.imps.size());

    Mask solution = 0;
    long long tried = 0;
    if (find_sat(nvars, d, clauses, solution, tried)) {
        std::printf("c found and verified model after %lld high-level candidates\n", tried);
        print_model(nvars, solution);
        return 10;
    }

    uint64_t rup_count = 0;
    if (emit_unsat_proof(argv[2], nvars, d, clauses, rup_count)) {
        std::printf("c emitted structural RUP proof with %llu derived clauses\n",
                    (unsigned long long)rup_count);
        std::printf("s UNSATISFIABLE\n");
        return 20;
    }
    std::printf("c closure search exhausted %lld candidates; proof generation exceeded limits\n", tried);
    std::printf("s UNKNOWN\n");
    return 0;
}
