#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <random>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Occ {
    int clause;
    bool positive;
};

struct Formula {
    int nvars = 0;
    std::vector<std::vector<int>> clauses;
};

struct ExactEq {
    std::vector<int> vars;
    std::vector<uint8_t> mask; // y_i = x_i xor mask_i
    int exact = 0;             // sum y_i == exact
};

static bool parse_dimacs(const std::string& path, Formula& f) {
    std::ifstream in(path);
    if (!in) return false;
    std::string line;
    std::vector<int> pending;
    while (std::getline(in, line)) {
        if (line.empty() || line[0] == 'c') continue;
        if (line[0] == 'p') {
            std::istringstream iss(line);
            std::string p, cnf;
            int m = 0;
            iss >> p >> cnf >> f.nvars >> m;
            f.clauses.reserve(std::max(0, m));
            continue;
        }
        std::istringstream iss(line);
        int lit;
        while (iss >> lit) {
            if (lit == 0) {
                if (!pending.empty()) {
                    f.clauses.push_back(pending);
                    pending.clear();
                }
            } else {
                int v = std::abs(lit);
                if (v <= 0) return false;
                if (v > f.nvars) f.nvars = v;
                pending.push_back(lit);
            }
        }
    }
    if (!pending.empty()) f.clauses.push_back(pending);
    return f.nvars > 0;
}

static bool clause_sat(const std::vector<int>& c, const std::vector<uint8_t>& a) {
    for (int lit : c) {
        bool val = a[std::abs(lit)] != 0;
        if (val == (lit > 0)) return true;
    }
    return false;
}

static bool verify_model(const Formula& f, const std::vector<uint8_t>& a) {
    if ((int)a.size() <= f.nvars) return false;
    for (const auto& c : f.clauses) {
        if (!clause_sat(c, a)) return false;
    }
    return true;
}

static void print_model(const Formula& f, const std::vector<uint8_t>& a) {
    std::cout << "s SATISFIABLE\n";
    const int per_line = 12;
    int on_line = 0;
    std::cout << "v";
    for (int v = 1; v <= f.nvars; ++v) {
        if (on_line == per_line) {
            std::cout << " 0\nv";
            on_line = 0;
        }
        std::cout << ' ' << (a[v] ? v : -v);
        ++on_line;
    }
    std::cout << " 0\n";
}

struct Key {
    std::vector<int> vars;
    bool operator==(const Key& o) const { return vars == o.vars; }
};

struct KeyHash {
    size_t operator()(const Key& k) const {
        uint64_t h = 1469598103934665603ull;
        for (int v : k.vars) {
            h ^= (uint64_t)v;
            h *= 1099511628211ull;
        }
        return (size_t)h;
    }
};

static bool find_exact_mask(const std::vector<std::vector<uint8_t>>& signs,
                            int arity, std::vector<uint8_t>& mask_out,
                            int& exact_out, int& rhs_xor) {
    const int total = 1 << arity;
    std::vector<uint8_t> forbidden(total, 0);
    for (const auto& s : signs) {
        int assn = 0;
        for (int i = 0; i < arity; ++i) {
            int bit = 1 - (int)s[i]; // assignment falsifying this literal
            assn |= bit << i;
        }
        forbidden[assn] = 1;
    }
    std::vector<int> allowed;
    for (int a = 0; a < total; ++a) {
        if (!forbidden[a]) allowed.push_back(a);
    }
    if (allowed.empty()) return false;

    for (int mask = 0; mask < total; ++mask) {
        int exact = -1;
        bool ok = true;
        for (int a : allowed) {
            int w = __builtin_popcount((unsigned)(a ^ mask));
            if (exact < 0) exact = w;
            else if (exact != w) {
                ok = false;
                break;
            }
        }
        if (ok) {
            mask_out.assign(arity, 0);
            for (int i = 0; i < arity; ++i) mask_out[i] = (mask >> i) & 1;
            exact_out = exact;
            rhs_xor = (exact ^ (__builtin_popcount((unsigned)mask) & 1)) & 1;
            return true;
        }
    }
    return false;
}

static bool build_exact_system(const Formula& f, bool& recognized,
                               std::vector<ExactEq>& exacts,
                               std::vector<int>& rhs_xors) {
    recognized = false;
    std::unordered_map<Key, std::vector<std::vector<uint8_t>>, KeyHash> groups;
    std::unordered_map<Key, std::vector<std::vector<int>>, KeyHash> binaries;
    int long_clauses = 0;

    for (const auto& c : f.clauses) {
        Key k;
        k.vars.reserve(c.size());
        for (int lit : c) k.vars.push_back(std::abs(lit));
        std::sort(k.vars.begin(), k.vars.end());
        if (std::adjacent_find(k.vars.begin(), k.vars.end()) != k.vars.end()) return false;
        if (c.size() == 2) {
            binaries[k].push_back(c);
        } else if (c.size() == 3 || c.size() == 4) {
            ++long_clauses;
            std::vector<uint8_t> s(k.vars.size(), 0);
            for (int lit : c) {
                int v = std::abs(lit);
                auto it = std::lower_bound(k.vars.begin(), k.vars.end(), v);
                s[it - k.vars.begin()] = lit > 0;
            }
            groups[k].push_back(std::move(s));
        } else {
            return false;
        }
    }
    if (long_clauses == 0) return false;

    for (const auto& it : groups) {
        const auto& vars = it.first.vars;
        int arity = (int)vars.size();
        int rhs = 0, exact = 0;
        std::vector<uint8_t> mask;
        int expected = (arity == 3) ? 5 : 10;
        if ((int)it.second.size() != expected) {
            recognized = false;
            return false;
        }
        if (!find_exact_mask(it.second, arity, mask, exact, rhs)) {
            recognized = false;
            return false;
        }
        exacts.push_back({vars, mask, exact});
        rhs_xors.push_back(rhs);
    }

    for (const auto& it : binaries) {
        if (it.second.size() != 2) return false;
        const auto& vars = it.first.vars;
        std::vector<std::vector<uint8_t>> signs;
        for (const auto& c : it.second) {
            std::vector<uint8_t> s(2, 0);
            for (int lit : c) {
                int idx = (std::abs(lit) == vars[0]) ? 0 : 1;
                s[idx] = lit > 0;
            }
            signs.push_back(std::move(s));
        }
        int rhs = 0, exact = 0;
        std::vector<uint8_t> mask;
        if (!find_exact_mask(signs, 2, mask, exact, rhs) || exact != 1) {
            return false;
        }
        exacts.push_back({vars, mask, exact});
        rhs_xors.push_back(rhs);
    }

    recognized = true;
    return true;
}

static bool gf2_dependency(const Formula& f, const std::vector<ExactEq>& exacts,
                           const std::vector<int>& rhs_xors,
                           std::vector<int>& dependency) {
    std::vector<std::pair<std::vector<uint64_t>, int>> eqs;
    const int words = (f.nvars + 63) / 64;
    for (size_t i = 0; i < exacts.size(); ++i) {
        std::vector<uint64_t> row(words, 0);
        for (int v : exacts[i].vars) row[(v - 1) >> 6] ^= 1ull << ((v - 1) & 63);
        eqs.push_back({std::move(row), rhs_xors[i]});
    }

    int dep_words = ((int)exacts.size() + 63) / 64;
    std::vector<std::vector<uint64_t>> basis(f.nvars);
    std::vector<std::vector<uint64_t>> bdep(f.nvars);
    std::vector<int> brhs(f.nvars, 0);
    for (int i = 0; i < (int)eqs.size(); ++i) {
        auto& eq = eqs[i];
        auto row = std::move(eq.first);
        int rhs = eq.second;
        std::vector<uint64_t> dep(dep_words, 0);
        dep[i >> 6] ^= 1ull << (i & 63);
        while (true) {
            int pivot = -1;
            for (int w = words - 1; w >= 0 && pivot < 0; --w) {
                if (row[w]) pivot = w * 64 + 63 - __builtin_clzll(row[w]);
            }
            if (pivot < 0) {
                if (rhs) {
                    dependency.clear();
                    for (int j = 0; j < (int)exacts.size(); ++j) {
                        if ((dep[j >> 6] >> (j & 63)) & 1ull) dependency.push_back(j);
                    }
                    return true;
                }
                break;
            }
            if (basis[pivot].empty()) {
                basis[pivot] = std::move(row);
                bdep[pivot] = std::move(dep);
                brhs[pivot] = rhs;
                break;
            }
            for (int w = 0; w < words; ++w) row[w] ^= basis[pivot][w];
            for (int w = 0; w < dep_words; ++w) dep[w] ^= bdep[pivot][w];
            rhs ^= brhs[pivot];
        }
    }
    return false;
}

static std::string y_lit(const ExactEq& e, int i, bool negated) {
    bool neg = (e.mask[i] != 0) ^ negated;
    return std::string(neg ? "~x" : "x") + std::to_string(e.vars[i]);
}

static std::string pb_atleast_clause(const std::vector<std::string>& lits) {
    std::ostringstream out;
    for (const auto& lit : lits) out << "1 " << lit << ' ';
    out << ">= 1";
    return out.str();
}

static std::string pol_sum_div(const std::vector<int>& ids, int divisor) {
    std::ostringstream out;
    out << "pol ";
    if (ids.empty()) return "";
    out << ids[0] << ' ';
    for (size_t i = 1; i < ids.size(); ++i) out << ids[i] << " + ";
    out << divisor << " d ;\n";
    return out.str();
}

static std::string pol_sum(const std::vector<int>& ids) {
    std::ostringstream out;
    out << "pol ";
    out << ids[0] << ' ';
    for (size_t i = 1; i < ids.size(); ++i) out << ids[i] << " + ";
    out << ";\n";
    return out.str();
}

static bool emit_exact_constraint_proof(std::ofstream& proof, const ExactEq& e,
                                        bool lower, int& next_id, int& out_id) {
    int arity = (int)e.vars.size();
    if (arity == 2) {
        std::vector<std::string> lits;
        lits.push_back(y_lit(e, 0, !lower));
        lits.push_back(y_lit(e, 1, !lower));
        proof << "rup " << pb_atleast_clause(lits) << " ;\n";
        out_id = next_id++;
        return true;
    }

    if (arity == 4 && e.exact == 2) {
        std::vector<int> triple_ids;
        for (int skip = 0; skip < 4; ++skip) {
            std::vector<std::string> lits;
            for (int i = 0; i < 4; ++i) {
                if (i != skip) lits.push_back(y_lit(e, i, !lower));
            }
            proof << "rup " << pb_atleast_clause(lits) << " ;\n";
            triple_ids.push_back(next_id++);
        }
        proof << pol_sum_div(triple_ids, 3);
        out_id = next_id++;
        return true;
    }

    if (arity != 3 || (e.exact != 1 && e.exact != 2)) return false;

    bool direct = (lower && e.exact == 1) || (!lower && e.exact == 2);
    if (direct) {
        std::vector<std::string> lits;
        for (int i = 0; i < 3; ++i) lits.push_back(y_lit(e, i, !lower));
        proof << "rup " << pb_atleast_clause(lits) << " ;\n";
        out_id = next_id++;
        return true;
    }

    std::vector<int> pair_ids;
    for (int i = 0; i < 3; ++i) {
        for (int j = i + 1; j < 3; ++j) {
            std::vector<std::string> lits;
            lits.push_back(y_lit(e, i, !lower));
            lits.push_back(y_lit(e, j, !lower));
            proof << "rup " << pb_atleast_clause(lits) << " ;\n";
            pair_ids.push_back(next_id++);
        }
    }
    proof << pol_sum_div(pair_ids, 2);
    out_id = next_id++;
    return true;
}

static bool write_veripb_parity_proof(const Formula& f, const std::string& path,
                                      const std::vector<ExactEq>& exacts,
                                      const std::vector<int>& dependency) {
    if (dependency.empty()) return false;
    for (int idx : dependency) {
        int arity = (int)exacts[idx].vars.size();
        if (!(arity == 2 || arity == 3 || (arity == 4 && exacts[idx].exact == 2))) return false;
    }

    std::ofstream proof(path);
    if (!proof) return false;
    proof << "pseudo-Boolean proof version 3.0\n";
    int next_id = (int)f.clauses.size() + 1;
    std::vector<int> lower_ids, upper_ids;
    for (int idx : dependency) {
        int lid = 0, uid = 0;
        if (!emit_exact_constraint_proof(proof, exacts[idx], true, next_id, lid)) return false;
        if (!emit_exact_constraint_proof(proof, exacts[idx], false, next_id, uid)) return false;
        lower_ids.push_back(lid);
        upper_ids.push_back(uid);
    }
    proof << pol_sum_div(lower_ids, 2);
    int lower_sum = next_id++;
    proof << pol_sum_div(upper_ids, 2);
    int upper_sum = next_id++;
    proof << pol_sum({lower_sum, upper_sum});
    int contradiction = next_id++;
    proof << "output NONE ;\n";
    proof << "conclusion UNSAT : " << contradiction << " ;\n";
    proof << "end pseudo-Boolean proof ;\n";
    return true;
}

static bool local_search_sat(const Formula& f, double seconds, std::vector<uint8_t>& assign) {
    std::vector<std::vector<Occ>> occ(f.nvars + 1);
    for (int i = 0; i < (int)f.clauses.size(); ++i) {
        for (int lit : f.clauses[i]) occ[std::abs(lit)].push_back({i, lit > 0});
    }

    uint64_t seed = 0x9e3779b97f4a7c15ull ^ (uint64_t)f.nvars * 1000003ull ^ f.clauses.size();
    for (const auto& c : f.clauses) {
        for (int lit : c) seed = seed * 6364136223846793005ull + (uint64_t)(lit + 1000000);
    }
    std::mt19937_64 rng(seed);
    auto deadline = Clock::now() + std::chrono::milliseconds((int64_t)(seconds * 1000.0));

    std::vector<int> satcnt(f.clauses.size()), unsat, pos(f.clauses.size(), -1);
    assign.assign(f.nvars + 1, 0);

    auto add_unsat = [&](int ci) {
        if (pos[ci] < 0) {
            pos[ci] = (int)unsat.size();
            unsat.push_back(ci);
        }
    };
    auto remove_unsat = [&](int ci) {
        int p = pos[ci];
        if (p >= 0) {
            int last = unsat.back();
            unsat[p] = last;
            pos[last] = p;
            unsat.pop_back();
            pos[ci] = -1;
        }
    };

    const int64_t max_steps_per_restart = std::max<int64_t>(20000, (int64_t)f.nvars * 4000);
    int restart = 0;
    while (Clock::now() < deadline) {
        ++restart;
        for (int v = 1; v <= f.nvars; ++v) assign[v] = (rng() >> 63) & 1;
        std::fill(satcnt.begin(), satcnt.end(), 0);
        std::fill(pos.begin(), pos.end(), -1);
        unsat.clear();
        for (int i = 0; i < (int)f.clauses.size(); ++i) {
            int cnt = 0;
            for (int lit : f.clauses[i]) cnt += (assign[std::abs(lit)] == (uint8_t)(lit > 0));
            satcnt[i] = cnt;
            if (cnt == 0) add_unsat(i);
        }

        for (int64_t step = 0; step < max_steps_per_restart; ++step) {
            if (unsat.empty()) return verify_model(f, assign);
            if ((step & 4095) == 0 && Clock::now() >= deadline) return false;

            int ci = unsat[rng() % unsat.size()];
            const auto& c = f.clauses[ci];
            int chosen = 0;
            bool noise = (rng() % 1000) < 420;
            if (noise) {
                chosen = std::abs(c[rng() % c.size()]);
            } else {
                int best_break = 1 << 30;
                int best_make = -1;
                std::vector<int> best;
                for (int lit : c) {
                    int v = std::abs(lit);
                    int br = 0, mk = 0;
                    for (const Occ& o : occ[v]) {
                        bool cur = assign[v] == (uint8_t)o.positive;
                        int before = satcnt[o.clause];
                        int after = before + (cur ? -1 : 1);
                        if (before > 0 && after == 0) ++br;
                        if (before == 0 && after > 0) ++mk;
                    }
                    if (br < best_break || (br == best_break && mk > best_make)) {
                        best_break = br;
                        best_make = mk;
                        best.clear();
                        best.push_back(v);
                    } else if (br == best_break && mk == best_make) {
                        best.push_back(v);
                    }
                }
                chosen = best[rng() % best.size()];
            }

            uint8_t old = assign[chosen];
            assign[chosen] = old ^ 1;
            for (const Occ& o : occ[chosen]) {
                bool was_sat_lit = old == (uint8_t)o.positive;
                int before = satcnt[o.clause];
                int after = before + (was_sat_lit ? -1 : 1);
                satcnt[o.clause] = after;
                if (before == 0 && after > 0) remove_unsat(o.clause);
                else if (before > 0 && after == 0) add_unsat(o.clause);
            }
        }
    }
    return false;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::cerr << "usage: solver <formula.cnf> <out.pbp>\n";
        return 1;
    }
    Formula f;
    if (!parse_dimacs(argv[1], f)) {
        std::cout << "c parse failed\ns UNKNOWN\n";
        return 0;
    }

    bool recognized = false;
    std::vector<ExactEq> exacts;
    std::vector<int> rhs_xors;
    bool system_ok = build_exact_system(f, recognized, exacts, rhs_xors);
    std::vector<int> dependency;
    bool xor_unsat = system_ok && gf2_dependency(f, exacts, rhs_xors, dependency);
    if (recognized) {
        std::cout << "c detected signed exact-cardinality blocks";
        if (xor_unsat) std::cout << " with inconsistent GF(2) shadow";
        std::cout << "\n";
    }
    if (xor_unsat) {
        if (write_veripb_parity_proof(f, argv[2], exacts, dependency)) {
            std::cout << "s UNSATISFIABLE\n";
            return 20;
        }
        std::cout << "c parity proof generation unsupported for this structure; returning UNKNOWN\n";
        std::cout << "s UNKNOWN\n";
        return 0;
    }

    double budget = 575.0;
    if (const char* e = std::getenv("SOLVER_TIME_LIMIT")) {
        budget = std::max(0.01, std::atof(e));
    }
    std::vector<uint8_t> assign;
    if (local_search_sat(f, budget, assign) && verify_model(f, assign)) {
        print_model(f, assign);
        return 10;
    }

    std::cout << "c no satisfying assignment found within local-search budget\n";
    std::cout << "s UNKNOWN\n";
    return 0;
}
