#include <algorithm>
#include <array>
#include <chrono>
#include <deque>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iostream>
#include <numeric>
#include <string>
#include <unordered_set>
#include <vector>

using Clock = std::chrono::steady_clock;

struct Candidate {
    int var = 0;
    int piece = -1;
    std::vector<std::pair<int, int>> edges; // edge-group id, chosen edge variable
};

struct EMModel {
    int K = 0;
    int side = 0;
    int cell_start = 0;
    std::vector<unsigned char> is_placement;
    std::vector<std::vector<int>> piece_groups;
    std::vector<std::vector<int>> cell_groups;
    std::vector<std::vector<int>> edge_groups;
};

struct HoltenCell {
    int side[4]{}; // top, left, right, bottom variable starts
    int tile = 0;
    int no_color = 0;
};

struct HoltenModel {
    int side = 0;
    int colors = 0;
    int cells_count = 0;
    int base_next = 0;
    std::vector<HoltenCell> cells;
    std::vector<int> edge_starts;
    std::vector<int> edge_start_group;
};

struct Solver {
    int nvars = 0;
    int nclauses = 0;
    std::vector<int> lits;
    std::vector<int> off;
    std::vector<std::vector<int>> occ;
    std::vector<unsigned char> val;
    std::vector<int> unit_lits;
    std::vector<std::vector<int>> positive_clauses;
    std::vector<std::vector<int>> exact_groups;
    std::vector<int> edge_var_group;
    std::vector<std::vector<Candidate>> em_cands;
    std::vector<int> adj_edge; // K x K, edge group id or -1
    Clock::time_point deadline;

    static int lit_index(int lit, int n) {
        int v = std::abs(lit);
        return lit > 0 ? v : n + v;
    }

    bool lit_true(int lit) const {
        int v = std::abs(lit);
        return lit > 0 ? val[v] : !val[v];
    }

    bool time_left() const {
        return Clock::now() < deadline;
    }

    bool parse(const char* path) {
        std::ifstream in(path);
        if (!in) return false;
        unit_lits.clear();
        std::string tok;
        while (in >> tok) {
            if (tok == "c") {
                std::string rest;
                std::getline(in, rest);
                continue;
            }
            if (tok == "p") {
                std::string fmt;
                in >> fmt >> nvars >> nclauses;
                // Compact EM instances are small. Holten grid-coloring instances
                // need the original clauses for auxiliary propagation, but the
                // very largest 14x14/16x16 variants are too clause-heavy for this
                // in-memory path on the current worker.
                if (nvars > 400000 || nclauses > 40000000) return false;
                off.reserve(nclauses + 1);
                off.push_back(0);
                occ.assign(2 * nvars + 1, {});
                break;
            }
        }
        if (nvars <= 0 || nclauses < 0) return false;

        int lit = 0;
        int c = 0;
        while (in >> lit) {
            if (lit == 0) {
                if ((int)lits.size() == off.back() + 1) unit_lits.push_back(lits.back());
                off.push_back((int)lits.size());
                ++c;
                if (c == nclauses) break;
            } else {
                if (std::abs(lit) > nvars) return false;
                lits.push_back(lit);
                occ[lit_index(lit, nvars)].push_back(c);
            }
        }
        if (c != nclauses) return false;
        val.assign(nvars + 1, 0);
        return true;
    }

    bool build_holten_layout(int side, int colors, HoltenModel& h) const {
        int64_t cells_count = int64_t(side) * side;
        int64_t grid_lines = 2LL * side * (side + 1);
        int64_t base = grid_lines * colors + cells_count * cells_count + cells_count * colors;
        if (side < 2 || colors < 2 || cells_count > 400 || base <= 0 || base >= nvars) return false;

        h.side = side;
        h.colors = colors;
        h.cells_count = (int)cells_count;
        h.cells.clear();
        h.edge_starts.clear();
        h.edge_start_group.assign(nvars + 1, -1);

        std::vector<int> right(cells_count, 0), bottom(cells_count, 0);
        int next = 1;
        auto add_edge = [&]() {
            int start = next;
            int gid = (int)h.edge_starts.size();
            h.edge_starts.push_back(start);
            h.edge_start_group[start] = gid;
            next += colors;
            return start;
        };

        for (int r = 0; r < side; ++r) {
            for (int c = 0; c < side; ++c) {
                int idx = r * side + c;
                HoltenCell cell;
                cell.side[0] = (r == 0) ? add_edge() : bottom[(r - 1) * side + c];
                cell.side[1] = (c == 0) ? add_edge() : right[r * side + c - 1];
                cell.side[2] = add_edge();
                cell.side[3] = add_edge();
                right[idx] = cell.side[2];
                bottom[idx] = cell.side[3];
                cell.tile = next;
                next += h.cells_count;
                cell.no_color = next;
                next += colors;
                h.cells.push_back(cell);
            }
        }
        h.base_next = next;
        return next == base + 1 && (int)h.edge_starts.size() == 2 * side * (side + 1);
    }

    bool holten_var_to_edge(const HoltenModel& h, int v, int& group, int& color) const {
        if (v < 1 || v >= h.base_next) return false;
        for (int g = 0; g < (int)h.edge_starts.size(); ++g) {
            int s = h.edge_starts[g];
            if (v >= s && v < s + h.colors) {
                group = g;
                color = v - s;
                return true;
            }
        }
        return false;
    }

    bool holten_var_to_tile(const HoltenModel& h, int v, int& cell, int& tile) const {
        for (int c = 0; c < h.cells_count; ++c) {
            int s = h.cells[c].tile;
            if (v >= s && v < s + h.cells_count) {
                cell = c;
                tile = v - s;
                return true;
            }
        }
        return false;
    }

    bool lit_satisfied_by_holten_pattern(const HoltenModel& h, const HoltenCell& c,
                                         int lit, const std::array<int, 4>& pat) const {
        int v = std::abs(lit);
        bool b = false;
        for (int s = 0; s < 4; ++s) {
            if (v >= c.side[s] && v < c.side[s] + h.colors) {
                b = (pat[s] == v - c.side[s]);
                return lit > 0 ? b : !b;
            }
        }
        if (v >= c.no_color && v < c.no_color + h.colors) {
            int color = v - c.no_color;
            b = true;
            for (int s = 0; s < 4; ++s) {
                if (pat[s] == color) {
                    b = false;
                    break;
                }
            }
            return lit > 0 ? b : !b;
        }
        return false;
    }

    std::vector<std::vector<std::array<int, 4>>> holten_extract_tile_patterns(const HoltenModel& h) const {
        std::vector<std::vector<int>> rel(h.cells_count);
        const HoltenCell& c0 = h.cells[0];
        std::vector<unsigned char> local(nvars + 1, 0);
        for (int s = 0; s < 4; ++s) {
            for (int k = 0; k < h.colors; ++k) local[c0.side[s] + k] = 1;
        }
        for (int k = 0; k < h.colors; ++k) local[c0.no_color + k] = 1;

        for (int t = 0; t < h.cells_count; ++t) {
            if ((t & 7) == 0 && !time_left()) return {};
            int tv = c0.tile + t;
            for (int cid : occ[lit_index(-tv, nvars)]) {
                bool ok = true;
                for (int p = off[cid]; p < off[cid + 1]; ++p) {
                    int v = std::abs(lits[p]);
                    if (v == tv) continue;
                    if (v < 1 || v > nvars || !local[v]) {
                        ok = false;
                        break;
                    }
                }
                if (ok) rel[t].push_back(cid);
            }
        }

        std::vector<std::vector<std::array<int, 4>>> out(h.cells_count);
        std::array<int, 4> pat{};
        int total = 1;
        for (int i = 0; i < 4; ++i) total *= h.colors;
        for (int code = 0; code < total; ++code) {
            if ((code & 0xff) == 0 && !time_left()) return {};
            int x = code;
            for (int i = 3; i >= 0; --i) {
                pat[i] = x % h.colors;
                x /= h.colors;
            }
            for (int t = 0; t < h.cells_count; ++t) {
                bool ok = true;
                int tv = c0.tile + t;
                for (int cid : rel[t]) {
                    bool sat = false;
                    for (int p = off[cid]; p < off[cid + 1]; ++p) {
                        int lit = lits[p];
                        if (std::abs(lit) == tv) continue;
                        if (lit_satisfied_by_holten_pattern(h, c0, lit, pat)) {
                            sat = true;
                            break;
                        }
                    }
                    if (!sat) {
                        ok = false;
                        break;
                    }
                }
                if (ok) {
                    bool dup = false;
                    for (const auto& q : out[t]) {
                        if (q == pat) {
                            dup = true;
                            break;
                        }
                    }
                    if (!dup) out[t].push_back(pat);
                }
            }
        }
        return out;
    }

    bool holten_orient_identity(const HoltenModel& h,
                                const std::vector<std::vector<std::array<int, 4>>>& tile_pats,
                                const std::vector<int>& fixed_edge,
                                std::vector<std::array<int, 4>>& chosen,
                                std::vector<int>& edge_color) const {
        std::vector<std::array<int, 4>> fixed_side(h.cells_count);
        std::vector<unsigned char> fixed_cell(h.cells_count, 0);
        for (int c = 0; c < h.cells_count; ++c) {
            fixed_side[c].fill(-1);
            for (int s = 0; s < 4; ++s) {
                int g = h.edge_start_group[h.cells[c].side[s]];
                if (g < 0) return false;
                if (fixed_edge[g] >= 0) fixed_side[c][s] = fixed_edge[g];
            }
            fixed_cell[c] = 1;
            for (int s = 0; s < 4; ++s) fixed_cell[c] &= fixed_side[c][s] >= 0;
        }

        std::vector<std::vector<std::array<int, 4>>> domains(h.cells_count);
        for (int c = 0; c < h.cells_count; ++c) {
            if (fixed_cell[c]) {
                domains[c].push_back(fixed_side[c]);
            } else {
                domains[c] = tile_pats[c];
            }
            std::vector<std::array<int, 4>> filtered;
            for (const auto& p : domains[c]) {
                bool ok = true;
                for (int s = 0; s < 4; ++s) {
                    if (fixed_side[c][s] >= 0 && fixed_side[c][s] != p[s]) {
                        ok = false;
                        break;
                    }
                }
                if (ok) filtered.push_back(p);
            }
            domains[c].swap(filtered);
            if (domains[c].empty()) return false;
        }

        chosen.assign(h.cells_count, {});
        std::vector<unsigned char> assigned(h.cells_count, 0);
        edge_color.assign(h.edge_starts.size(), -1);
        for (int g = 0; g < (int)fixed_edge.size(); ++g) {
            if (fixed_edge[g] >= 0) edge_color[g] = fixed_edge[g];
        }

        std::function<bool()> rec = [&]() -> bool {
            if (!time_left()) return false;
            int best = -1;
            std::vector<std::array<int, 4>> best_list;
            for (int c = 0; c < h.cells_count; ++c) {
                if (assigned[c]) continue;
                std::vector<std::array<int, 4>> list;
                for (const auto& p : domains[c]) {
                    bool ok = true;
                    for (int s = 0; s < 4; ++s) {
                        int g = h.edge_start_group[h.cells[c].side[s]];
                        if (g < 0 || (edge_color[g] >= 0 && edge_color[g] != p[s])) {
                            ok = false;
                            break;
                        }
                    }
                    if (ok) list.push_back(p);
                }
                if (list.empty()) return false;
                if (best < 0 || list.size() < best_list.size()) {
                    best = c;
                    best_list.swap(list);
                    if (best_list.size() == 1) break;
                }
            }
            if (best < 0) return true;
            for (const auto& p : best_list) {
                std::vector<int> changed;
                bool ok = true;
                for (int s = 0; s < 4; ++s) {
                    int g = h.edge_start_group[h.cells[best].side[s]];
                    if (edge_color[g] < 0) {
                        edge_color[g] = p[s];
                        changed.push_back(g);
                    } else if (edge_color[g] != p[s]) {
                        ok = false;
                        break;
                    }
                }
                if (ok) {
                    chosen[best] = p;
                    assigned[best] = 1;
                    if (rec()) return true;
                    assigned[best] = 0;
                }
                for (int g : changed) edge_color[g] = -1;
            }
            return false;
        };
        return rec();
    }

    bool assign_lit(std::deque<int>& q, int lit) {
        int v = std::abs(lit);
        unsigned char b = lit > 0 ? 1 : 0;
        if (val[v] != 2) return val[v] == b;
        val[v] = b;
        q.push_back(lit);
        return true;
    }

    bool propagate_assignment(std::deque<int>& q) {
        while (!q.empty()) {
            int lit = q.front();
            q.pop_front();
            int falsified = -lit;
            for (int cid : occ[lit_index(falsified, nvars)]) {
                if ((cid & 0x3fff) == 0 && !time_left()) return false;
                bool sat = false;
                int unknown = 0;
                int last = 0;
                for (int p = off[cid]; p < off[cid + 1]; ++p) {
                    int l = lits[p];
                    unsigned char b = val[std::abs(l)];
                    if (b == 2) {
                        ++unknown;
                        last = l;
                    } else {
                        bool t = b != 0;
                        if (l < 0) t = !t;
                        if (t) {
                            sat = true;
                            break;
                        }
                    }
                }
                if (sat) continue;
                if (unknown == 0) return false;
                if (unknown == 1 && !assign_lit(q, last)) return false;
            }
        }
        return true;
    }

    bool complete_by_greedy_propagation() {
        std::deque<int> q;
        for (int v = 1; v <= nvars; ++v) {
            if (val[v] != 2) q.push_back(val[v] ? v : -v);
        }
        if (!propagate_assignment(q)) return false;

        {
            std::vector<unsigned char> saved = val;
            std::deque<int> q2;
            bool ok = true;
            for (int v = 1; v <= nvars; ++v) {
                if (val[v] == 2 && !assign_lit(q2, -v)) {
                    ok = false;
                    break;
                }
            }
            if (ok && propagate_assignment(q2) && verify()) return true;
            val.swap(saved);
        }

        for (;;) {
            int decide = 0;
            for (int cid = 0; cid < nclauses && decide == 0; ++cid) {
                if ((cid & 0xffff) == 0 && !time_left()) return false;
                bool sat = false;
                for (int p = off[cid]; p < off[cid + 1]; ++p) {
                    int l = lits[p];
                    unsigned char b = val[std::abs(l)];
                    if (b == 2) {
                        if (decide == 0) decide = l;
                    } else {
                        bool t = b != 0;
                        if (l < 0) t = !t;
                        if (t) {
                            sat = true;
                            break;
                        }
                    }
                }
                if (!sat && decide == 0) return false;
                if (!sat && decide != 0) break;
                decide = 0;
            }
            if (decide == 0) break;
            if (!assign_lit(q, decide)) return false;
            if (!propagate_assignment(q)) return false;
            if (!time_left()) return false;
        }

        for (int v = 1; v <= nvars; ++v) {
            if (val[v] == 2) {
                if (!assign_lit(q, -v)) return false;
                if (!propagate_assignment(q)) return false;
            }
        }
        return true;
    }

    bool solve_holten_grid_coloring() {
        // The Holten generator uses inner grids 8x8, 10x10, 12x12, and
        // 14x14 in this family. Try structural candidates; full CNF
        // verification remains the soundness gate.
        static const int sides[] = {8, 10, 12, 14, 6};
        for (int side : sides) {
            for (int colors = 3; colors <= 22; ++colors) {
                if (!time_left()) return false;
                HoltenModel h;
                if (!build_holten_layout(side, colors, h)) continue;

                auto tile_pats = holten_extract_tile_patterns(h);
                int empty = 0, active = 0;
                for (const auto& d : tile_pats) {
                    if (d.empty()) ++empty;
                    else if (d.size() <= 4) ++active;
                }
                if (active + empty != h.cells_count || active == 0) continue;

                std::vector<int> fixed_edge(h.edge_starts.size(), -1);
                std::vector<int> fixed_tile_cell(h.cells_count, -1);
                bool ok = true;
                for (int u : unit_lits) {
                    int v = std::abs(u);
                    if (u > 0) {
                        int g = -1, col = -1;
                        if (holten_var_to_edge(h, v, g, col)) {
                            if (fixed_edge[g] >= 0 && fixed_edge[g] != col) ok = false;
                            fixed_edge[g] = col;
                        }
                        int cell = -1, tile = -1;
                        if (holten_var_to_tile(h, v, cell, tile)) {
                            fixed_tile_cell[cell] = tile;
                        }
                    }
                }
                if (!ok) continue;
                for (int c = 0; c < h.cells_count; ++c) {
                    if (tile_pats[c].empty() && fixed_tile_cell[c] != c) {
                        ok = false;
                        break;
                    }
                }
                if (!ok) continue;

                std::vector<std::array<int, 4>> chosen;
                std::vector<int> edge_color;
                if (!holten_orient_identity(h, tile_pats, fixed_edge, chosen, edge_color)) continue;

                val.assign(nvars + 1, 2);
                for (int v = 1; v < h.base_next; ++v) val[v] = 0;
                for (int g = 0; g < (int)h.edge_starts.size(); ++g) {
                    if (edge_color[g] < 0) {
                        ok = false;
                        break;
                    }
                    int s = h.edge_starts[g];
                    for (int k = 0; k < h.colors; ++k) val[s + k] = (k == edge_color[g]) ? 1 : 0;
                }
                if (!ok) continue;
                for (int c = 0; c < h.cells_count; ++c) {
                    for (int t = 0; t < h.cells_count; ++t) val[h.cells[c].tile + t] = (t == c) ? 1 : 0;
                    for (int k = 0; k < h.colors; ++k) {
                        bool absent = true;
                        for (int s = 0; s < 4; ++s) absent &= chosen[c][s] != k;
                        val[h.cells[c].no_color + k] = absent ? 1 : 0;
                    }
                }
                std::deque<int> q;
                for (int u : unit_lits) {
                    if (!assign_lit(q, u)) {
                        ok = false;
                        break;
                    }
                }
                if (!ok) continue;
                if (!complete_by_greedy_propagation()) continue;
                if (verify()) return true;
            }
        }
        return false;
    }

    void collect_positive_and_exact_groups() {
        positive_clauses.clear();
        exact_groups.clear();

        std::unordered_set<uint64_t> neg_pairs;
        neg_pairs.reserve((size_t)nclauses / 2 + 1024);
        std::vector<int> exact_candidates;

        for (int c = 0; c < nclauses; ++c) {
            int a = off[c], b = off[c + 1], len = b - a;
            bool all_pos = len > 1;
            for (int i = a; i < b; ++i) all_pos &= lits[i] > 0;
            if (all_pos) {
                std::vector<int> g;
                g.reserve(len);
                for (int i = a; i < b; ++i) g.push_back(lits[i]);
                positive_clauses.push_back(std::move(g));
                if (len <= 192) exact_candidates.push_back(c);
            }
            if (len == 2 && lits[a] < 0 && lits[a + 1] < 0) {
                uint32_t x = (uint32_t)std::abs(lits[a]);
                uint32_t y = (uint32_t)std::abs(lits[a + 1]);
                if (x > y) std::swap(x, y);
                neg_pairs.insert((uint64_t(x) << 32) | y);
            }
        }

        std::vector<unsigned char> used(nvars + 1, 0);
        for (int c : exact_candidates) {
            int a = off[c], b = off[c + 1];
            bool ok = true;
            for (int i = a; i < b && ok; ++i) {
                int x = lits[i];
                if (used[x]) {
                    ok = false;
                    break;
                }
                for (int j = i + 1; j < b; ++j) {
                    int y = lits[j];
                    uint32_t u = (uint32_t)std::min(x, y);
                    uint32_t v = (uint32_t)std::max(x, y);
                    if (!neg_pairs.contains((uint64_t(u) << 32) | v)) {
                        ok = false;
                        break;
                    }
                }
            }
            if (!ok) continue;
            std::vector<int> g;
            g.reserve(b - a);
            for (int i = a; i < b; ++i) {
                g.push_back(lits[i]);
                used[lits[i]] = 1;
            }
            exact_groups.push_back(std::move(g));
        }
    }

    static bool is_square(int k, int& root) {
        root = 0;
        while ((root + 1) * (root + 1) <= k) ++root;
        return root * root == k;
    }

    bool detect_grid_em(EMModel& em) const {
        if (positive_clauses.empty() || exact_groups.empty()) return false;

        for (int side = 2; side <= 24; ++side) {
            int K = side * side;
            if ((int)positive_clauses.size() < K) continue;
            int expected_edges = 2 * side * (side - 1);
            int max_start = std::min((int)positive_clauses.size() - K, K + 5);

            for (int start = 0; start <= max_start; ++start) {
                std::vector<unsigned char> is_place(nvars + 1, 0);
                std::vector<int> placement_vars;
                bool ok = true;

                for (int i = start; i < start + K && ok; ++i) {
                    for (int v : positive_clauses[i]) {
                        if (v < 1 || v > nvars || is_place[v]) {
                            ok = false;
                            break;
                        }
                        is_place[v] = 1;
                        placement_vars.push_back(v);
                    }
                }
                if (!ok || placement_vars.empty()) continue;

                std::vector<std::vector<int>> edge_groups;
                std::vector<unsigned char> covered(nvars + 1, 0);
                int covered_count = 0;
                for (int v : placement_vars) {
                    covered[v] = 1;
                    ++covered_count;
                }

                for (const auto& g : exact_groups) {
                    int in_place = 0;
                    for (int v : g) in_place += is_place[v] != 0;
                    if (in_place != 0 && in_place != (int)g.size()) {
                        ok = false;
                        break;
                    }
                    if (in_place == 0) {
                        edge_groups.push_back(g);
                        for (int v : g) {
                            if (covered[v]) {
                                ok = false;
                                break;
                            }
                            covered[v] = 1;
                            ++covered_count;
                        }
                    }
                    if (!ok) break;
                }
                if (!ok) continue;
                if ((int)edge_groups.size() != expected_edges) continue;
                if (covered_count != nvars) continue;

                std::vector<int> assigned(nvars + 1, -1);
                std::vector<std::vector<int>> piece_groups;
                piece_groups.reserve(K);
                int assigned_count = 0;

                for (int i = 0; i < start && ok; ++i) {
                    const auto& g = positive_clauses[i];
                    if (g.empty()) {
                        ok = false;
                        break;
                    }
                    for (int v : g) {
                        if (v < 1 || v > nvars || !is_place[v] || assigned[v] != -1) {
                            ok = false;
                            break;
                        }
                    }
                    if (!ok) break;
                    int pid = (int)piece_groups.size();
                    piece_groups.push_back(g);
                    for (int v : g) {
                        assigned[v] = pid;
                        ++assigned_count;
                    }
                }
                if (!ok) continue;

                std::vector<int> missing;
                missing.reserve(placement_vars.size() - assigned_count);
                for (int v : placement_vars) {
                    if (assigned[v] == -1) missing.push_back(v);
                }

                if ((int)piece_groups.size() == K) {
                    if (!missing.empty()) continue;
                } else if ((int)piece_groups.size() == K - 1) {
                    if (missing.empty()) continue;
                    piece_groups.push_back(missing);
                } else {
                    continue;
                }
                if ((int)piece_groups.size() != K) continue;

                em.K = K;
                em.side = side;
                em.cell_start = start;
                em.is_placement = std::move(is_place);
                em.piece_groups = std::move(piece_groups);
                em.cell_groups.assign(positive_clauses.begin() + start,
                                      positive_clauses.begin() + start + K);
                em.edge_groups = std::move(edge_groups);
                return true;
            }
        }
        return false;
    }

    bool clause_satisfied_by_edge_choice(int p, const std::vector<int>& group_value,
                                         const std::vector<int>& rel_clauses) const {
        for (int c : rel_clauses) {
            bool sat = false;
            for (int i = off[c]; i < off[c + 1]; ++i) {
                int lit = lits[i];
                if (lit == -p) continue;
                int v = std::abs(lit);
                int g = edge_var_group[v];
                if (g < 0) continue;
                if (lit > 0) {
                    if (group_value[g] == v) {
                        sat = true;
                        break;
                    }
                } else {
                    if (group_value[g] != v) {
                        sat = true;
                        break;
                    }
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    void enumerate_candidate_edges(const EMModel& em, int p, int piece,
                                   const std::vector<int>& inc,
                                   const std::vector<int>& rel_clauses,
                                   int at, std::vector<int>& group_value,
                                   std::vector<Candidate>& out) const {
        if (at == (int)inc.size()) {
            if (!clause_satisfied_by_edge_choice(p, group_value, rel_clauses)) return;
            Candidate cand;
            cand.var = p;
            cand.piece = piece;
            cand.edges.reserve(inc.size());
            for (int g : inc) cand.edges.push_back({g, group_value[g]});
            out.push_back(std::move(cand));
            return;
        }
        int g = inc[at];
        for (int v : em.edge_groups[g]) {
            group_value[g] = v;
            enumerate_candidate_edges(em, p, piece, inc, rel_clauses, at + 1, group_value, out);
            group_value[g] = 0;
            if (!time_left()) return;
        }
    }

    bool build_candidates_and_piece_graph(const EMModel& em) {
        edge_var_group.assign(nvars + 1, -1);
        for (int g = 0; g < (int)em.edge_groups.size(); ++g) {
            for (int v : em.edge_groups[g]) {
                if (v < 1 || v > nvars || edge_var_group[v] != -1) return false;
                edge_var_group[v] = g;
            }
        }

        std::vector<int> piece_of(nvars + 1, -1);
        for (int p = 0; p < em.K; ++p) {
            if (em.piece_groups[p].empty()) return false;
            for (int v : em.piece_groups[p]) {
                if (v < 1 || v > nvars || !em.is_placement[v] || piece_of[v] != -1) return false;
                piece_of[v] = p;
            }
        }

        em_cands.assign(em.K, {});
        std::vector<std::vector<int>> group_pieces(em.edge_groups.size());
        std::vector<int> group_value(em.edge_groups.size(), 0);

        for (int cell = 0; cell < em.K; ++cell) {
            for (int p : em.cell_groups[cell]) {
                if (p < 1 || p > nvars || piece_of[p] < 0) return false;

                std::vector<int> rel_clauses;
                std::vector<int> inc;
                for (int c : occ[nvars + p]) {
                    bool has_edge = false;
                    for (int i = off[c]; i < off[c + 1]; ++i) {
                        int g = edge_var_group[std::abs(lits[i])];
                        if (g >= 0) {
                            has_edge = true;
                            if (std::find(inc.begin(), inc.end(), g) == inc.end()) inc.push_back(g);
                        }
                    }
                    if (has_edge) rel_clauses.push_back(c);
                }
                std::sort(inc.begin(), inc.end());
                if (inc.empty() || inc.size() > 4) return false;
                for (int g : inc) {
                    auto& ps = group_pieces[g];
                    int piece = piece_of[p];
                    if (std::find(ps.begin(), ps.end(), piece) == ps.end()) ps.push_back(piece);
                }

                enumerate_candidate_edges(em, p, piece_of[p], inc, rel_clauses, 0, group_value, em_cands[cell]);
                if (!time_left()) return false;
            }
            if (em_cands[cell].empty()) return false;
        }

        adj_edge.assign(em.K * em.K, -1);
        std::vector<int> degree(em.K, 0);
        for (int g = 0; g < (int)group_pieces.size(); ++g) {
            if (group_pieces[g].size() != 2) return false;
            int a = group_pieces[g][0], b = group_pieces[g][1];
            if (a == b || adj_edge[a * em.K + b] != -1) return false;
            adj_edge[a * em.K + b] = adj_edge[b * em.K + a] = g;
            ++degree[a];
            ++degree[b];
        }

        int deg2 = 0, deg3 = 0, deg4 = 0;
        for (int d : degree) {
            if (d == 2) ++deg2;
            else if (d == 3) ++deg3;
            else if (d == 4) ++deg4;
            else return false;
        }
        return deg2 == 4 && deg3 == 4 * (em.side - 2) && deg4 == (em.side - 2) * (em.side - 2);
    }

    bool ok_nonadjacent_assigned(const EMModel& em, const std::vector<int>& grid,
                                 int pos, int piece) const {
        int r = pos / em.side, c = pos % em.side;
        std::vector<int> allowed;
        static const int dr[4] = {-1, 1, 0, 0};
        static const int dc[4] = {0, 0, -1, 1};
        for (int k = 0; k < 4; ++k) {
            int rr = r + dr[k], cc = c + dc[k];
            if (rr < 0 || rr >= em.side || cc < 0 || cc >= em.side) continue;
            int q = grid[rr * em.side + cc];
            if (q != -1) allowed.push_back(q);
        }
        for (int i = 0; i < em.K; ++i) {
            int q = grid[i];
            if (q == -1) continue;
            if (adj_edge[piece * em.K + q] == -1) continue;
            if (std::find(allowed.begin(), allowed.end(), q) == allowed.end()) return false;
        }
        return true;
    }

    bool embed_rec(const EMModel& em, std::vector<int>& grid, std::vector<unsigned char>& used,
                   const std::vector<int>& degree, int pos) const {
        if (!time_left()) return false;
        if (pos == em.K) return true;
        if (grid[pos] != -1) return embed_rec(em, grid, used, degree, pos + 1);

        int r = pos / em.side, c = pos % em.side;
        std::vector<int> req;
        if (r > 0) req.push_back(grid[(r - 1) * em.side + c]);
        if (c > 0) req.push_back(grid[r * em.side + c - 1]);
        for (int q : req) if (q < 0) return false;

        int target_degree = 4;
        if ((r == 0 || r == em.side - 1) && (c == 0 || c == em.side - 1)) target_degree = 2;
        else if (r == 0 || r == em.side - 1 || c == 0 || c == em.side - 1) target_degree = 3;

        for (int p = 0; p < em.K; ++p) {
            if (used[p] || degree[p] != target_degree) continue;
            bool ok = true;
            for (int q : req) {
                if (adj_edge[p * em.K + q] == -1) {
                    ok = false;
                    break;
                }
            }
            if (!ok) continue;
            if (!ok_nonadjacent_assigned(em, grid, pos, p)) continue;
            grid[pos] = p;
            used[p] = 1;
            if (embed_rec(em, grid, used, degree, pos + 1)) return true;
            used[p] = 0;
            grid[pos] = -1;
        }
        return false;
    }

    bool embed_piece_grid(const EMModel& em, std::vector<int>& grid) const {
        std::vector<int> degree(em.K, 0);
        std::vector<int> corners;
        for (int p = 0; p < em.K; ++p) {
            for (int q = 0; q < em.K; ++q) degree[p] += adj_edge[p * em.K + q] != -1;
            if (degree[p] == 2) corners.push_back(p);
        }
        if (corners.size() != 4) return false;

        for (int corner : corners) {
            std::vector<int> neigh;
            for (int q = 0; q < em.K; ++q) if (adj_edge[corner * em.K + q] != -1) neigh.push_back(q);
            if (neigh.size() != 2) continue;
            for (int flip = 0; flip < 2; ++flip) {
                int right = neigh[flip], down = neigh[1 - flip];
                if (adj_edge[right * em.K + down] != -1) continue;
                grid.assign(em.K, -1);
                std::vector<unsigned char> used(em.K, 0);
                grid[0] = corner;
                grid[1] = right;
                grid[em.side] = down;
                used[corner] = used[right] = used[down] = 1;
                if (embed_rec(em, grid, used, degree, 0)) return true;
            }
        }
        return false;
    }

    bool candidate_has_required_edges(const Candidate& cand, const std::vector<int>& req) const {
        if (cand.edges.size() != req.size()) return false;
        for (auto [g, _] : cand.edges) {
            if (!std::binary_search(req.begin(), req.end(), g)) return false;
        }
        return true;
    }

    bool orient_rec(const EMModel& em, const std::vector<std::vector<int>>& domains,
                    const std::vector<int>& order, int at, std::vector<int>& edge_value,
                    std::vector<int>& chosen) const {
        if (!time_left()) return false;
        if (at == em.K) return true;
        int cell = order[at];
        for (int idx : domains[cell]) {
            const Candidate& cand = em_cands[cell][idx];
            bool ok = true;
            std::vector<int> changed;
            for (auto [g, v] : cand.edges) {
                if (edge_value[g] && edge_value[g] != v) {
                    ok = false;
                    break;
                }
                if (!edge_value[g]) {
                    edge_value[g] = v;
                    changed.push_back(g);
                }
            }
            if (ok) {
                chosen[cell] = idx;
                if (orient_rec(em, domains, order, at + 1, edge_value, chosen)) return true;
                chosen[cell] = -1;
            }
            for (int g : changed) edge_value[g] = 0;
        }
        return false;
    }

    bool solve_grid_em(const EMModel& em) {
        if (!build_candidates_and_piece_graph(em)) return false;

        std::vector<int> grid;
        if (!embed_piece_grid(em, grid)) return false;

        std::vector<std::vector<int>> domains(em.K);
        for (int cell = 0; cell < em.K; ++cell) {
            int r = cell / em.side, c = cell % em.side;
            int piece = grid[cell];
            std::vector<int> req;
            static const int dr[4] = {-1, 1, 0, 0};
            static const int dc[4] = {0, 0, -1, 1};
            for (int k = 0; k < 4; ++k) {
                int rr = r + dr[k], cc = c + dc[k];
                if (rr < 0 || rr >= em.side || cc < 0 || cc >= em.side) continue;
                int q = grid[rr * em.side + cc];
                int e = adj_edge[piece * em.K + q];
                if (e < 0) return false;
                req.push_back(e);
            }
            std::sort(req.begin(), req.end());

            for (int i = 0; i < (int)em_cands[cell].size(); ++i) {
                const Candidate& cand = em_cands[cell][i];
                if (cand.piece == piece && candidate_has_required_edges(cand, req)) domains[cell].push_back(i);
            }
            if (domains[cell].empty()) return false;
        }

        std::vector<int> order(em.K);
        std::iota(order.begin(), order.end(), 0);
        std::sort(order.begin(), order.end(), [&](int a, int b) {
            return domains[a].size() < domains[b].size();
        });

        std::vector<int> edge_value(em.edge_groups.size(), 0);
        std::vector<int> chosen(em.K, -1);
        if (!orient_rec(em, domains, order, 0, edge_value, chosen)) return false;

        std::fill(val.begin(), val.end(), 0);
        for (int cell = 0; cell < em.K; ++cell) {
            if (chosen[cell] < 0) return false;
            const Candidate& cand = em_cands[cell][chosen[cell]];
            val[cand.var] = 1;
            for (auto [_, v] : cand.edges) val[v] = 1;
        }
        for (const auto& g : em.edge_groups) {
            bool any = false;
            for (int v : g) any |= val[v] != 0;
            if (!any) val[g[0]] = 1;
        }
        return verify();
    }

    bool verify() const {
        for (int c = 0; c < nclauses; ++c) {
            if ((c & 0xffff) == 0 && !time_left()) return false;
            bool ok = false;
            for (int i = off[c]; i < off[c + 1]; ++i) {
                if (lit_true(lits[i])) {
                    ok = true;
                    break;
                }
            }
            if (!ok) return false;
        }
        return true;
    }

    bool search() {
        if (nvars > 30000 && nclauses > 1000000 && solve_holten_grid_coloring()) return true;
        collect_positive_and_exact_groups();
        EMModel em;
        if (detect_grid_em(em) && solve_grid_em(em)) return true;
        return false;
    }

    void print_model() const {
        std::cout << "s SATISFIABLE\n";
        const int per = 20;
        for (int start = 1; start <= nvars; start += per) {
            std::cout << "v";
            int end = std::min(nvars, start + per - 1);
            for (int v = start; v <= end; ++v) std::cout << ' ' << (val[v] ? v : -v);
            if (end == nvars) std::cout << " 0";
            std::cout << '\n';
        }
    }
};

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

    Solver s;
    s.deadline = Clock::now() + std::chrono::seconds(520);
    if (!s.parse(argv[1])) {
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    if (s.search()) {
        s.print_model();
        return 10;
    }
    std::cout << "s UNKNOWN\n";
    return 0;
}
