#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <random>
#include <string>
#include <unordered_set>
#include <vector>

using std::int8_t;

struct FastScanner {
    static constexpr size_t BUFSZ = 1 << 20;
    FILE* f = nullptr;
    char buf[BUFSZ];
    size_t pos = 0, len = 0;

    explicit FastScanner(const char* path) {
        f = std::fopen(path, "rb");
    }
    ~FastScanner() {
        if (f) std::fclose(f);
    }
    int get() {
        if (pos >= len) {
            len = std::fread(buf, 1, BUFSZ, f);
            pos = 0;
            if (len == 0) return EOF;
        }
        return buf[pos++];
    }
    void skipLine() {
        int c;
        while ((c = get()) != EOF && c != '\n') {}
    }
    bool nextInt(int& out) {
        int c;
        do {
            c = get();
            if (c == EOF) return false;
            if (c == 'c') {
                skipLine();
                continue;
            }
            if (c == 'p') {
                skipLine();
                continue;
            }
        } while ((c < '0' || c > '9') && c != '-');

        int sign = 1;
        if (c == '-') {
            sign = -1;
            c = get();
        }
        int v = 0;
        while (c >= '0' && c <= '9') {
            v = v * 10 + (c - '0');
            c = get();
        }
        out = sign * v;
        return true;
    }
};

struct Formula {
    int vars = 0;
    int clauses_declared = 0;
    std::vector<int> lits;
    std::vector<uint32_t> start;
    std::vector<int> unit_lits;
};

static bool parseHeader(const char* path, int& vars, int& clauses) {
    FILE* f = std::fopen(path, "rb");
    if (!f) return false;
    char line[4096];
    bool ok = false;
    while (std::fgets(line, sizeof(line), f)) {
        if (line[0] == 'p') {
            if (std::sscanf(line, "p cnf %d %d", &vars, &clauses) == 2) ok = true;
            break;
        }
    }
    std::fclose(f);
    return ok;
}

static bool loadFormula(const char* path, Formula& F) {
    if (!parseHeader(path, F.vars, F.clauses_declared)) return false;
    F.start.reserve(static_cast<size_t>(F.clauses_declared) + 1);
    F.lits.reserve(static_cast<size_t>(F.clauses_declared) * 2);
    FastScanner sc(path);
    if (!sc.f) return false;

    F.start.push_back(0);
    int x;
    int clause_len = 0;
    while (sc.nextInt(x)) {
        if (x == 0) {
            if (clause_len == 1) F.unit_lits.push_back(F.lits.back());
            F.start.push_back(static_cast<uint32_t>(F.lits.size()));
            clause_len = 0;
        } else {
            F.lits.push_back(x);
            ++clause_len;
        }
    }
    if (clause_len != 0) return false;
    return true;
}

static int ipow3(int k) {
    int r = 1;
    for (int i = 0; i < k; ++i) r *= 3;
    return r;
}

static std::vector<int> ternaryVec(int ix, int W) {
    std::vector<int> v(W);
    for (int i = W - 1; i >= 0; --i) {
        v[i] = (ix % 3) - 1;  // '<', '=', '>' -> -1, 0, +1
        ix /= 3;
    }
    return v;
}

struct Shape {
    int N = 0;
    int W = 0;
    int P = 0;
    int B = 0;
    int boundary = 0;
    bool forced_ttf = false;
};

static bool inferShape(const Formula& F, Shape& S) {
    std::vector<int> first_neg;
    first_neg.reserve(8);
    for (int lit : F.unit_lits) {
        if (lit < 0) {
            first_neg.push_back(-lit);
            if (first_neg.size() >= 4) break;
        }
    }
    if (first_neg.size() < 2 || first_neg[0] != 1) return false;
    S.N = first_neg[1] - first_neg[0] - 1;
    if (S.N < 3 || S.N > 256) return false;

    std::vector<int8_t> unit(F.vars + 1, 0);
    for (int lit : F.unit_lits) {
        int v = std::abs(lit);
        if (v <= F.vars) unit[v] = lit > 0 ? 1 : -1;
    }

    for (int W = 1; W <= 8; ++W) {
        int P = (ipow3(W) - 1) / 2;
        int B = W * S.N * S.N;
        int boundary = B + 2 * S.N * P;
        if (boundary <= 0 || boundary >= F.vars) continue;
        bool diag = true;
        for (int w = 0; w < W && diag; ++w) {
            for (int b = 0; b < S.N; ++b) {
                int var = 1 + w * S.N * S.N + b * S.N + b;
                if (var > F.vars || unit[var] != -1) {
                    diag = false;
                    break;
                }
            }
        }
        if (!diag) continue;
        if (boundary + 1 <= F.vars && unit[boundary + 1] != 1) continue;
        S.W = W;
        S.P = P;
        S.B = B;
        S.boundary = boundary;
    }
    if (S.W == 0) return false;

    std::vector<uint8_t> forced(S.N, 0);
    for (size_t ci = 0; ci + 1 < F.start.size(); ++ci) {
        uint32_t a = F.start[ci], b = F.start[ci + 1];
        if (b - a != 2 && b - a != 3) continue;
        int l1 = 0, l2 = 0;
        bool guarded = false;
        for (uint32_t p = a; p < b; ++p) {
            int lit = F.lits[p];
            if (lit > S.B && lit <= S.boundary) {
                if (l1 == 0) l1 = lit;
                else l2 = lit;
            } else if (lit < 0 && -lit <= F.vars && unit[-lit] == 1) {
                guarded = true;
            }
        }
        if (l1 <= 0 || l2 <= 0) continue;
        if (b - a == 3 && !guarded) continue;
        int o1 = l1 - S.B - 1, o2 = l2 - S.B - 1;
        int ix1 = o1 / (2 * S.N), ix2 = o2 / (2 * S.N);
        int r1 = o1 % (2 * S.N), r2 = o2 % (2 * S.N);
        int ball1 = r1 / 2, ball2 = r2 / 2, e1 = r1 & 1, e2 = r2 & 1;
        if (ball1 == ball2 && ix1 == ix2 && ix1 == ball1 + 1 && e1 != e2) {
            forced[ball1] = 1;
        }
    }
    S.forced_ttf = std::all_of(forced.begin(), forced.end(), [](uint8_t x) { return x != 0; });
    return true;
}

static int scoreSums(const std::vector<int>& sums) {
    int s = 0;
    for (int x : sums) s += x * x;
    return s;
}

static bool solveSignsFixedRows(const std::vector<int>& rows, int W, std::vector<int>& signs) {
    const int m = static_cast<int>(rows.size());
    std::vector<std::vector<int>> vecs;
    vecs.reserve(m);
    for (int r : rows) vecs.push_back(ternaryVec(r, W));
    std::mt19937_64 rng(0x9e3779b97f4a7c15ULL ^ (uint64_t)m * 1315423911ULL ^ (uint64_t)W);

    for (int restart = 0; restart < 240; ++restart) {
        signs.assign(m, 1);
        for (int i = 0; i < m; ++i) signs[i] = (rng() & 1) ? 1 : -1;
        std::vector<int> sums(W, 0);
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < W; ++j) sums[j] += signs[i] * vecs[i][j];
        int obj = scoreSums(sums);
        double temp = std::max(0.1, obj / 5.0);
        for (int step = 0; step < 70000; ++step) {
            if (obj == 0) return true;
            int best_i = -1, best_obj = 1 << 30;
            std::vector<int> best_sums;
            for (int t = 0; t < 24; ++t) {
                int i = static_cast<int>(rng() % m);
                std::vector<int> ns = sums;
                for (int j = 0; j < W; ++j) ns[j] -= 2 * signs[i] * vecs[i][j];
                int no = scoreSums(ns);
                if (no < best_obj) {
                    best_obj = no;
                    best_i = i;
                    best_sums.swap(ns);
                }
            }
            double accept = std::exp((obj - best_obj) / std::max(0.01, temp));
            if (best_obj <= obj || std::generate_canonical<double, 32>(rng) < accept) {
                signs[best_i] = -signs[best_i];
                sums.swap(best_sums);
                obj = best_obj;
            }
            temp *= 0.99972;
        }
    }
    return false;
}

static bool solveSelectRows(int N, int W, std::vector<int>& rows, std::vector<int>& signs) {
    int P = (ipow3(W) - 1) / 2;
    std::vector<std::vector<int>> vecs(P);
    for (int i = 0; i < P; ++i) vecs[i] = ternaryVec(i, W);

    std::mt19937_64 rng(0xabcdef1234567890ULL ^ (uint64_t)N * 1000003ULL ^ W);
    std::vector<int> all;
    all.reserve(P - 1);
    for (int i = 1; i < P; ++i) all.push_back(i);

    for (int restart = 0; restart < 160; ++restart) {
        std::shuffle(all.begin(), all.end(), rng);
        std::vector<uint8_t> selected(P, 0);
        std::vector<int> sel;
        sel.reserve(N);
        selected[0] = 1;
        sel.push_back(0);
        for (int i = 0; i < N - 1; ++i) {
            selected[all[i]] = 1;
            sel.push_back(all[i]);
        }

        std::vector<int> sign_by_row(P, 0);
        for (int r : sel) sign_by_row[r] = (rng() & 1) ? 1 : -1;
        sign_by_row[0] = 1; // ZeroPlus: the first selected row maps to ball 0 heavy.

        std::vector<int> sums(W, 0);
        for (int r : sel)
            for (int j = 0; j < W; ++j) sums[j] += sign_by_row[r] * vecs[r][j];
        int obj = scoreSums(sums);
        double temp = std::max(0.1, obj / 5.0);

        for (int step = 0; step < 140000; ++step) {
            if (obj == 0) {
                std::sort(sel.begin(), sel.end());
                rows = sel;
                signs.resize(N);
                for (int i = 0; i < N; ++i) signs[i] = sign_by_row[rows[i]];
                return true;
            }
            int best_obj = 1 << 30;
            int best_kind = 0, best_i = -1, best_o = -1, best_sg = 0;
            std::vector<int> best_sums;
            for (int t = 0; t < 36; ++t) {
                bool do_flip = (rng() & 1) != 0;
                int r = sel[static_cast<int>(rng() % sel.size())];
                if (r == 0) continue;
                if (do_flip) {
                    std::vector<int> ns = sums;
                    for (int j = 0; j < W; ++j) ns[j] -= 2 * sign_by_row[r] * vecs[r][j];
                    int no = scoreSums(ns);
                    if (no < best_obj) {
                        best_obj = no; best_kind = 1; best_i = r; best_sums.swap(ns);
                    }
                } else {
                    int o = static_cast<int>(rng() % P);
                    if (selected[o]) continue;
                    int sg = (rng() & 1) ? 1 : -1;
                    std::vector<int> ns = sums;
                    for (int j = 0; j < W; ++j) ns[j] += -sign_by_row[r] * vecs[r][j] + sg * vecs[o][j];
                    int no = scoreSums(ns);
                    if (no < best_obj) {
                        best_obj = no; best_kind = 2; best_i = r; best_o = o; best_sg = sg; best_sums.swap(ns);
                    }
                }
            }
            if (best_kind == 0) continue;
            double accept = std::exp((obj - best_obj) / std::max(0.01, temp));
            if (best_obj <= obj || std::generate_canonical<double, 32>(rng) < accept) {
                if (best_kind == 1) {
                    sign_by_row[best_i] = -sign_by_row[best_i];
                } else {
                    selected[best_i] = 0;
                    selected[best_o] = 1;
                    sign_by_row[best_o] = best_sg;
                    sign_by_row[best_i] = 0;
                    auto it = std::find(sel.begin(), sel.end(), best_i);
                    *it = best_o;
                }
                sums.swap(best_sums);
                obj = best_obj;
            }
            temp *= 0.99996;
        }
    }
    return false;
}

static bool buildHighLevelAssignment(const Shape& S, std::vector<int8_t>& assign) {
    std::vector<int> rows, signs;
    if (S.forced_ttf) {
        rows.resize(S.N);
        for (int b = 0; b < S.N; ++b) rows[b] = b + 1;
        for (int w = 0; w < S.W; ++w) {
            int nonzero = 0;
            for (int r : rows) {
                if (ternaryVec(r, S.W)[w] != 0) ++nonzero;
            }
            if (nonzero & 1) return false;
        }
        if (!solveSignsFixedRows(rows, S.W, signs)) return false;
    } else {
        if (!solveSelectRows(S.N, S.W, rows, signs)) return false;
    }

    for (int v = 1; v <= S.boundary; ++v) assign[v] = -1;

    std::vector<std::vector<int>> codes(S.N, std::vector<int>(S.W, 0));
    for (int b = 0; b < S.N; ++b) {
        std::vector<int> v = ternaryVec(rows[b], S.W);
        for (int w = 0; w < S.W; ++w) codes[b][w] = signs[b] * v[w];
    }

    for (int w = 0; w < S.W; ++w) {
        std::vector<int> left, right;
        for (int b = 0; b < S.N; ++b) {
            if (codes[b][w] == 1) left.push_back(b);
            else if (codes[b][w] == -1) right.push_back(b);
        }
        if (left.size() != right.size()) return false;
        for (size_t i = 0; i < left.size(); ++i) {
            int var = 1 + w * S.N * S.N + left[i] * S.N + right[i];
            assign[var] = 1;
        }
    }

    for (int b = 0; b < S.N; ++b) {
        int err = signs[b] == 1 ? 1 : 0; // formulation order: '-' then '+'
        int var = S.B + rows[b] * (2 * S.N) + b * 2 + err + 1;
        if (var <= S.B || var > S.boundary) return false;
        assign[var] = 1;
    }
    return true;
}

static bool assignLiteral(std::vector<int8_t>& assign, int lit) {
    int v = std::abs(lit);
    int8_t val = lit > 0 ? 1 : -1;
    if (assign[v] != 0 && assign[v] != val) return false;
    assign[v] = val;
    return true;
}

static bool propagate(const Formula& F, std::vector<int8_t>& assign) {
    for (int lit : F.unit_lits) {
        if (!assignLiteral(assign, lit)) return false;
    }

    bool changed = true;
    int rounds = 0;
    while (changed && rounds++ < 64) {
        changed = false;
        for (size_t ci = 0; ci + 1 < F.start.size(); ++ci) {
            uint32_t a = F.start[ci], b = F.start[ci + 1];
            bool sat = false;
            int unassigned = 0, last = 0;
            for (uint32_t p = a; p < b; ++p) {
                int lit = F.lits[p];
                int8_t av = assign[std::abs(lit)];
                if (av == 0) {
                    ++unassigned;
                    last = lit;
                } else if ((lit > 0 && av == 1) || (lit < 0 && av == -1)) {
                    sat = true;
                    break;
                }
            }
            if (sat) continue;
            if (unassigned == 0) return false;
            if (unassigned == 1) {
                int v = std::abs(last);
                int8_t val = last > 0 ? 1 : -1;
                if (assign[v] == 0) {
                    assign[v] = val;
                    changed = true;
                } else if (assign[v] != val) {
                    return false;
                }
            }
        }
    }
    return true;
}

static bool verifyComplete(const Formula& F, std::vector<int8_t>& assign) {
    for (int v = 1; v <= F.vars; ++v) {
        if (assign[v] == 0) assign[v] = -1;
    }
    for (size_t ci = 0; ci + 1 < F.start.size(); ++ci) {
        bool sat = false;
        for (uint32_t p = F.start[ci]; p < F.start[ci + 1]; ++p) {
            int lit = F.lits[p];
            int8_t av = assign[std::abs(lit)];
            if ((lit > 0 && av == 1) || (lit < 0 && av == -1)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static void printModel(const std::vector<int8_t>& assign, int vars) {
    std::puts("s SATISFIABLE");
    const int per_line = 16;
    for (int v = 1; v <= vars; ++v) {
        if ((v - 1) % per_line == 0) std::printf("v");
        std::printf(" %d", assign[v] == 1 ? v : -v);
        if (v % per_line == 0) std::printf(" 0\n");
    }
    if (vars % per_line != 0) std::printf(" 0\n");
}

static void printUnknown() {
    std::puts("s UNKNOWN");
}

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

    Formula F;
    if (!loadFormula(argv[1], F)) {
        if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "load failed\n");
        printUnknown();
        return 0;
    }

    Shape S;
    if (!inferShape(F, S)) {
        if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "shape failed vars=%d clauses=%zu units=%zu\n", F.vars, F.start.size() ? F.start.size() - 1 : 0, F.unit_lits.size());
        printUnknown();
        return 0;
    }
    if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "shape N=%d W=%d P=%d B=%d boundary=%d forced=%d vars=%d clauses=%zu\n", S.N, S.W, S.P, S.B, S.boundary, S.forced_ttf ? 1 : 0, F.vars, F.start.size() - 1);

    std::vector<int8_t> assign(F.vars + 1, 0);
    if (!buildHighLevelAssignment(S, assign)) {
        if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "high level failed\n");
        printUnknown();
        return 0;
    }
    if (!propagate(F, assign)) {
        if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "propagate failed\n");
        printUnknown();
        return 0;
    }
    if (!verifyComplete(F, assign)) {
        if (std::getenv("ODDBALL_DEBUG")) std::fprintf(stderr, "verify failed\n");
        printUnknown();
        return 0;
    }

    printModel(assign, F.vars);
    return 10;
}
