#include <algorithm>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
#include <string>
#include <unordered_map>
#include <vector>

using Clause = std::vector<int>;

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

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

    std::string tok;
    Clause cur;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
            continue;
        }
        if (tok == "p") {
            std::string fmt;
            int m = 0;
            in >> fmt >> f.nvars >> m;
            f.clauses.reserve(std::max(0, m));
            continue;
        }
        if (!tok.empty() && tok[0] == '%') break;
        int lit = std::stoi(tok);
        if (lit == 0) {
            if (!cur.empty()) {
                f.clauses.push_back(cur);
                cur.clear();
            }
        } else {
            cur.push_back(lit);
        }
    }
    if (!cur.empty()) f.clauses.push_back(cur);
    return f.nvars > 0;
}

static bool check_model(const Formula& f, const std::vector<uint8_t>& val) {
    for (const Clause& c : f.clauses) {
        bool sat = false;
        for (int lit : c) {
            int v = std::abs(lit);
            if (v <= 0 || v >= (int)val.size()) return false;
            bool x = val[v] != 0;
            if ((lit > 0 && x) || (lit < 0 && !x)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

struct XorEq {
    std::vector<int> vars; // 0-based
    uint8_t rhs = 0;
};

struct Relation {
    std::vector<int> vars;       // 0-based
    std::vector<uint32_t> allow; // bit pattern in vars order
};

static bool analyze_blocks(const Formula& f, std::vector<XorEq>& eqs,
                           std::vector<Relation>& rels) {
    std::map<std::vector<int>, std::vector<Clause>> blocks;
    for (const Clause& c : f.clauses) {
        std::vector<int> key;
        key.reserve(c.size());
        for (int lit : c) key.push_back(std::abs(lit));
        std::sort(key.begin(), key.end());
        key.erase(std::unique(key.begin(), key.end()), key.end());
        if (key.size() != c.size()) return false;
        blocks[key].push_back(c);
    }

    for (auto& [key1, cls] : blocks) {
        const int k = (int)key1.size();
        if (k <= 0 || k > 20) return false;
        if ((int)cls.size() != (1 << (k - 1))) return false;

        std::unordered_map<int, int> pos;
        pos.reserve(k * 2);
        for (int i = 0; i < k; ++i) pos[key1[i]] = i;

        std::vector<uint8_t> forbidden(1u << k, 0);
        int parity0 = -1;
        bool xor_like = true;
        for (const Clause& c : cls) {
            uint32_t pat = 0;
            uint8_t par = 0;
            for (int lit : c) {
                int i = pos[std::abs(lit)];
                if (lit < 0) {
                    pat |= (1u << i);
                    par ^= 1;
                }
            }
            if (forbidden[pat]) return false;
            forbidden[pat] = 1;
            if (parity0 < 0) parity0 = par;
            if (par != parity0) xor_like = false;
        }

        if (xor_like) {
            XorEq e;
            e.vars.reserve(k);
            for (int v : key1) e.vars.push_back(v - 1);
            e.rhs = (uint8_t)(1 ^ parity0);
            eqs.push_back(std::move(e));
        } else {
            Relation r;
            r.vars.reserve(k);
            for (int v : key1) r.vars.push_back(v - 1);
            for (uint32_t pat = 0; pat < (1u << k); ++pat) {
                if (!forbidden[pat]) r.allow.push_back(pat);
            }
            if (r.allow.empty()) return false;
            rels.push_back(std::move(r));
        }
    }
    return true;
}

static bool solve_linear(int nvars, const std::vector<XorEq>& base,
                         const std::vector<std::pair<int, uint8_t>>& units,
                         std::vector<uint8_t>& model) {
    const int words = (nvars + 64) / 64; // includes rhs bit at index nvars
    std::vector<std::vector<uint64_t>> rows;
    rows.reserve(base.size() + units.size());

    auto add_row = [&](const std::vector<int>& vars, uint8_t rhs) {
        std::vector<uint64_t> r(words, 0);
        for (int v : vars) r[v >> 6] ^= (1ull << (v & 63));
        if (rhs) r[nvars >> 6] ^= (1ull << (nvars & 63));
        rows.push_back(std::move(r));
    };
    for (const XorEq& e : base) add_row(e.vars, e.rhs);
    for (auto [v, b] : units) {
        std::vector<int> one{v};
        add_row(one, b);
    }

    std::vector<int> pivot_col;
    int rank = 0;
    for (int col = 0; col < nvars; ++col) {
        int piv = -1;
        uint64_t mask = 1ull << (col & 63);
        int w = col >> 6;
        for (int r = rank; r < (int)rows.size(); ++r) {
            if (rows[r][w] & mask) {
                piv = r;
                break;
            }
        }
        if (piv < 0) continue;
        std::swap(rows[rank], rows[piv]);
        for (int r = 0; r < (int)rows.size(); ++r) {
            if (r == rank) continue;
            if (rows[r][w] & mask) {
                for (int j = w; j < words; ++j) rows[r][j] ^= rows[rank][j];
            }
        }
        pivot_col.push_back(col);
        ++rank;
    }

    const int rhs_w = nvars >> 6;
    const uint64_t rhs_m = 1ull << (nvars & 63);
    uint64_t last_mask = rhs_m - 1;
    for (int r = rank; r < (int)rows.size(); ++r) {
        bool any = false;
        for (int w = 0; w < words; ++w) {
            uint64_t x = rows[r][w];
            if (w == rhs_w) x &= last_mask;
            if (x) {
                any = true;
                break;
            }
        }
        bool rhs = (rows[r][rhs_w] & rhs_m) != 0;
        if (!any && rhs) return false;
    }

    model.assign(nvars + 1, 0);
    // Free variables remain false; RREF rows then directly give pivot values.
    for (int i = 0; i < rank; ++i) {
        int col = pivot_col[i];
        model[col + 1] = (rows[i][rhs_w] & rhs_m) ? 1 : 0;
    }
    return true;
}

static bool enumerate_relations(int idx, const std::vector<Relation>& rels,
                                std::vector<int>& assigned,
                                std::vector<std::pair<int, uint8_t>>& units,
                                const std::vector<XorEq>& eqs, const Formula& f,
                                std::vector<uint8_t>& model) {
    if (idx == (int)rels.size()) {
        std::vector<std::pair<int, uint8_t>> u;
        for (int v = 0; v < (int)assigned.size(); ++v) {
            if (assigned[v] >= 0) u.push_back({v, (uint8_t)assigned[v]});
        }
        if (!solve_linear(f.nvars, eqs, u, model)) return false;
        return check_model(f, model);
    }

    const Relation& r = rels[idx];
    for (uint32_t pat : r.allow) {
        bool ok = true;
        std::vector<int> changed;
        for (int i = 0; i < (int)r.vars.size(); ++i) {
            int v = r.vars[i];
            int b = (pat >> i) & 1u;
            if (assigned[v] >= 0 && assigned[v] != b) {
                ok = false;
                break;
            }
            if (assigned[v] < 0) {
                assigned[v] = b;
                changed.push_back(v);
            }
        }
        if (ok && enumerate_relations(idx + 1, rels, assigned, units, eqs, f, model)) return true;
        for (int v : changed) assigned[v] = -1;
    }
    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;
    }

    std::vector<XorEq> eqs;
    std::vector<Relation> rels;
    if (!analyze_blocks(f, eqs, rels)) {
        std::cout << "c unsupported non half-relation block structure\ns UNKNOWN\n";
        return 0;
    }

    int rel_vars = 0;
    {
        std::vector<uint8_t> seen(f.nvars, 0);
        for (const Relation& r : rels) {
            for (int v : r.vars) {
                if (!seen[v]) {
                    seen[v] = 1;
                    ++rel_vars;
                }
            }
        }
    }
    std::cout << "c detected " << eqs.size() << " xor blocks and " << rels.size()
              << " small relation blocks over " << rel_vars << " variables\n";
    if (rel_vars > 28) {
        std::cout << "c relation search cap exceeded\ns UNKNOWN\n";
        return 0;
    }

    std::vector<uint8_t> model;
    bool sat = false;
    if (rels.empty()) {
        std::vector<std::pair<int, uint8_t>> units;
        sat = solve_linear(f.nvars, eqs, units, model) && check_model(f, model);
    } else {
        std::vector<int> assigned(f.nvars, -1);
        std::vector<std::pair<int, uint8_t>> units;
        sat = enumerate_relations(0, rels, assigned, units, eqs, f, model);
    }

    if (!sat) {
        std::cout << "c no verified model found; no VeriPB proof implemented\ns UNKNOWN\n";
        return 0;
    }

    std::cout << "s SATISFIABLE\n";
    const int per_line = 20;
    for (int i = 1; i <= f.nvars; ++i) {
        if ((i - 1) % per_line == 0) std::cout << "v ";
        std::cout << (model[i] ? i : -i) << ' ';
        if (i % per_line == 0) std::cout << "0\n";
    }
    if (f.nvars % per_line != 0) std::cout << "0\n";
    return 10;
}
