#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <limits>
#include <queue>
#include <random>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using std::vector;

struct Clause {
    vector<int> lits;
    bool learnt = false;
    double activity = 0.0;
};

struct Solver {
    int nvars = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<signed char> assigns;  // -1 false, 0 undef, 1 true
    vector<signed char> phase;
    vector<int> level;
    vector<int> reason;
    vector<int> trail;
    vector<int> trail_lim;
    int qhead = 0;
    vector<double> activity;
    double var_inc = 1.0;
    double clause_inc = 1.0;
    std::chrono::steady_clock::time_point deadline;
    std::mt19937 rng{1234567};

    static int lit_index(int lit) {
        int v = std::abs(lit);
        return (v << 1) ^ (lit < 0);
    }

    int lit_value(int lit) const {
        int v = std::abs(lit);
        signed char a = assigns[v];
        if (a == 0) return 0;
        bool val = a > 0;
        if (lit < 0) val = !val;
        return val ? 1 : -1;
    }

    int decision_level() const { return (int)trail_lim.size(); }

    bool timed_out() const {
        return std::chrono::steady_clock::now() >= deadline;
    }

    void init(int nv) {
        nvars = nv;
        watches.assign((nvars + 1) * 2 + 2, {});
        assigns.assign(nvars + 1, 0);
        phase.assign(nvars + 1, 1);
        level.assign(nvars + 1, 0);
        reason.assign(nvars + 1, -1);
        activity.assign(nvars + 1, 0.0);
    }

    bool enqueue(int lit, int why) {
        int v = std::abs(lit);
        signed char val = lit > 0 ? 1 : -1;
        if (assigns[v] != 0) return assigns[v] == val;
        assigns[v] = val;
        phase[v] = val;
        level[v] = decision_level();
        reason[v] = why;
        trail.push_back(lit);
        return true;
    }

    bool add_clause(vector<int> lits, bool learnt, int *out_id = nullptr) {
        if (lits.empty()) return false;
        vector<int> compact;
        compact.reserve(lits.size());
        if (!learnt) {
            std::sort(lits.begin(), lits.end(), [](int a, int b) {
                if (std::abs(a) != std::abs(b)) return std::abs(a) < std::abs(b);
                return a < b;
            });
            int last = 0;
            for (int lit : lits) {
                if (last == lit) continue;
                if (last == -lit) return true;  // tautology
                compact.push_back(lit);
                last = lit;
            }
        } else {
            for (int lit : lits) {
                bool skip = false;
                for (int old : compact) {
                    if (old == lit) {
                        skip = true;
                        break;
                    }
                    if (old == -lit) return true;
                }
                if (!skip) compact.push_back(lit);
            }
        }
        int id = (int)clauses.size();
        Clause c;
        c.lits = std::move(compact);
        c.learnt = learnt;
        clauses.push_back(std::move(c));
        if (out_id) *out_id = id;
        if (clauses[id].lits.size() == 1) {
            return enqueue(clauses[id].lits[0], id);
        }
        watches[lit_index(clauses[id].lits[0])].push_back(id);
        watches[lit_index(clauses[id].lits[1])].push_back(id);
        for (int lit : clauses[id].lits) activity[std::abs(lit)] += 1.0;
        return true;
    }

    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++];
            int false_idx = lit_index(-p);
            auto &ws = watches[false_idx];
            int i = 0;
            int j = 0;
            while (i < (int)ws.size()) {
                int cid = ws[i++];
                Clause &c = clauses[cid];
                if (c.lits.size() == 1) {
                    ws[j++] = cid;
                    continue;
                }
                if (c.lits[0] != -p) std::swap(c.lits[0], c.lits[1]);
                if (c.lits[0] != -p) {
                    ws[j++] = cid;
                    continue;
                }
                int other = c.lits[1];
                if (lit_value(other) == 1) {
                    ws[j++] = cid;
                    continue;
                }
                bool found = false;
                for (int k = 2; k < (int)c.lits.size(); ++k) {
                    if (lit_value(c.lits[k]) != -1) {
                        std::swap(c.lits[0], c.lits[k]);
                        watches[lit_index(c.lits[0])].push_back(cid);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[j++] = cid;
                if (lit_value(other) == -1) {
                    while (i < (int)ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return cid;
                }
                if (!enqueue(other, cid)) {
                    while (i < (int)ws.size()) ws[j++] = ws[i++];
                    ws.resize(j);
                    return cid;
                }
            }
            ws.resize(j);
        }
        return -1;
    }

    void new_decision_level() { trail_lim.push_back((int)trail.size()); }

    void cancel_until(int target_level) {
        if (decision_level() <= target_level) return;
        int keep = target_level == 0 ? trail_lim[0] : trail_lim[target_level];
        for (int i = (int)trail.size() - 1; i >= keep; --i) {
            int v = std::abs(trail[i]);
            assigns[v] = 0;
            reason[v] = -1;
            level[v] = 0;
        }
        trail.resize(keep);
        trail_lim.resize(target_level);
        qhead = (int)trail.size();
    }

    void bump_var(int v) {
        activity[v] += var_inc;
        if (activity[v] > 1e100) {
            for (int i = 1; i <= nvars; ++i) activity[i] *= 1e-100;
            var_inc *= 1e-100;
        }
    }

    void decay_vars() { var_inc *= 1.0 / 0.95; }

    void analyze(int confl, vector<int> &learnt, int &backtrack) {
        learnt.clear();
        learnt.push_back(0);
        vector<unsigned char> seen(nvars + 1, 0);
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int cid = confl;

        do {
            Clause &c = clauses[cid];
            c.activity += clause_inc;
            for (int lit : c.lits) {
                int v = std::abs(lit);
                if (seen[v] || level[v] == 0) continue;
                seen[v] = 1;
                bump_var(v);
                if (level[v] == decision_level()) {
                    ++path_count;
                } else {
                    learnt.push_back(lit);
                }
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) --idx;
            p = trail[idx--];
            cid = reason[std::abs(p)];
            seen[std::abs(p)] = 0;
            --path_count;
        } while (path_count > 0 && cid >= 0);

        learnt[0] = -p;
        backtrack = 0;
        for (int i = 1; i < (int)learnt.size(); ++i)
            backtrack = std::max(backtrack, level[std::abs(learnt[i])]);
        decay_vars();
        clause_inc *= 1.001;
    }

    int pick_branch_lit() {
        int best = 0;
        double best_act = -1.0;
        for (int v = 1; v <= nvars; ++v) {
            if (assigns[v] == 0 && activity[v] > best_act) {
                best_act = activity[v];
                best = v;
            }
        }
        if (best == 0) return 0;
        int sign = phase[best] >= 0 ? 1 : -1;
        return sign > 0 ? best : -best;
    }

    static int luby(int y, int x) {
        int size = 1;
        int seq = 0;
        while (size < x + 1) {
            size = 2 * size + 1;
            seq++;
        }
        while (size - 1 != x) {
            size = (size - 1) >> 1;
            seq--;
            x %= size;
        }
        int res = 1;
        for (int i = 0; i < seq; ++i) res *= y;
        return res;
    }

    enum Result { SAT, UNSAT_NO_PROOF, UNKNOWN };

    Result solve(double seconds) {
        deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds((int)(seconds * 1000));
        int confl = propagate();
        if (confl != -1) {
            if (std::getenv("DEBUG_SOLVER")) {
                std::fprintf(stderr, "root conflict clause %d:", confl);
                for (int lit : clauses[confl].lits) std::fprintf(stderr, " %d(v=%d)", lit, lit_value(lit));
                std::fprintf(stderr, "\ntrail %zu qhead %d\n", trail.size(), qhead);
            }
            return UNSAT_NO_PROOF;
        }

        long long conflicts = 0;
        int restart_idx = 1;
        int restart_limit = 100;
        vector<int> learnt;

        while (!timed_out()) {
            confl = propagate();
            if (confl != -1) {
                ++conflicts;
                if (decision_level() == 0) return UNSAT_NO_PROOF;
                int backtrack = 0;
                analyze(confl, learnt, backtrack);
                cancel_until(backtrack);
                int cid = -1;
                if (!add_clause(learnt, true, &cid)) {
                    if (std::getenv("DEBUG_SOLVER")) std::fprintf(stderr, "learned empty/taut at conflict %lld\n", conflicts);
                    return UNSAT_NO_PROOF;
                }
                if (!enqueue(clauses[cid].lits[0], cid)) {
                    if (std::getenv("DEBUG_SOLVER")) {
                        std::fprintf(stderr, "enqueue learned asserting lit failed cid=%d lit=%d backtrack=%d level=%d\n",
                                     cid, clauses[cid].lits[0], backtrack, decision_level());
                        std::fprintf(stderr, "clause:");
                        for (int lit : clauses[cid].lits) std::fprintf(stderr, " %d(v=%d,lvl=%d)", lit, lit_value(lit), level[std::abs(lit)]);
                        std::fprintf(stderr, "\n");
                    }
                    return UNSAT_NO_PROOF;
                }

                if (conflicts >= restart_limit) {
                    cancel_until(0);
                    restart_idx++;
                    restart_limit += 100 * luby(2, restart_idx);
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) return SAT;
                new_decision_level();
                enqueue(next, -1);
            }
        }
        return UNKNOWN;
    }

    bool verify_model(const vector<Clause> &original) const {
        for (int ci = 0; ci < (int)original.size(); ++ci) {
            const Clause &c = original[ci];
            bool sat = false;
            for (int lit : c.lits) {
                int v = std::abs(lit);
                bool val = assigns[v] > 0;
                if (lit < 0) val = !val;
                if (val) {
                    sat = true;
                    break;
                }
            }
            if (!sat) {
                if (std::getenv("DEBUG_SOLVER")) {
                    std::fprintf(stderr, "verify failed clause %d:", ci);
                    for (int lit : c.lits) {
                        std::fprintf(stderr, " %d(assign=%d,lvl=%d)", lit, (int)assigns[std::abs(lit)], level[std::abs(lit)]);
                    }
                    std::fprintf(stderr, "\n");
                }
                return false;
            }
        }
        return true;
    }
};

struct TensorTerm {
    int u = 0;
    int v = 0;
    int w = 0;
};

struct AndGate {
    int out_lit = 0;
    int in[3] = {0, 0, 0};
    bool valid = false;
};

struct XorGate {
    int vars[3] = {0, 0, 0};
    int constant = 0;
};

struct PartInfo {
    int rank = -1;
    int part = -1;
    int pos = -1;
};

struct TensorInfo {
    bool ok = false;
    int dims[3] = {0, 0, 0};
    int rank = 0;
    unsigned __int128 target = 0;
    vector<vector<vector<int>>> rank_lit;   // [rank][dim][pos] -> signed literal
    vector<vector<int>> component_lits;     // signed literals in comparator-like order
};

static uint64_t pair_key(int a, int b, int nvars) {
    int ca = a + nvars + 1;
    int cb = b + nvars + 1;
    if (ca > cb) std::swap(ca, cb);
    return (uint64_t)(uint32_t)ca << 32 | (uint32_t)cb;
}

static uint64_t triple_key(int a, int b, int c, int nvars) {
    int x[3] = {std::abs(a), std::abs(b), std::abs(c)};
    std::sort(x, x + 3);
    uint64_t base = (uint64_t)nvars + 1;
    return ((uint64_t)x[0] * base + x[1]) * base + x[2];
}

static bool xor_group_constant(const vector<vector<int>> &cls, const int vars[3], int &constant) {
    vector<int> good;
    for (int mask = 0; mask < 8; ++mask) {
        bool ok = true;
        for (const auto &cl : cls) {
            bool sat = false;
            for (int lit : cl) {
                int idx = (std::abs(lit) == vars[0]) ? 0 : (std::abs(lit) == vars[1] ? 1 : 2);
                bool val = (mask >> idx) & 1;
                if (lit < 0) val = !val;
                if (val) {
                    sat = true;
                    break;
                }
            }
            if (!sat) {
                ok = false;
                break;
            }
        }
        if (ok) good.push_back(mask);
    }
    if (good.size() != 4) return false;
    int p = __builtin_popcount((unsigned)good[0]) & 1;
    for (int m : good)
        if ((__builtin_popcount((unsigned)m) & 1) != p) return false;
    constant = p;
    return true;
}

static TensorInfo reconstruct_tensor(const vector<Clause> &original, int nvars) {
    TensorInfo info;
    std::unordered_set<uint64_t> binaries;
    binaries.reserve(original.size() * 2);
    for (const Clause &c : original) {
        if (c.lits.size() == 2) binaries.insert(pair_key(c.lits[0], c.lits[1], nvars));
    }

    vector<AndGate> ands;
    vector<AndGate> out_gate(nvars + 1);
    std::unordered_set<int> base_lit_set;
    for (const Clause &c : original) {
        if (c.lits.size() != 4) continue;
        vector<AndGate> candidates;
        for (int out : c.lits) {
            AndGate g;
            g.out_lit = out;
            int k = 0;
            for (int lit : c.lits)
                if (lit != out) g.in[k++] = -lit;
            bool ok = true;
            for (int i = 0; i < 3; ++i) {
                if (!binaries.count(pair_key(g.in[i], -out, nvars))) {
                    ok = false;
                    break;
                }
            }
            if (ok) {
                g.valid = true;
                candidates.push_back(g);
            }
        }
        if (candidates.size() != 1) return info;
        AndGate g = candidates[0];
        int ov = std::abs(g.out_lit);
        if (out_gate[ov].valid) return info;
        out_gate[ov] = g;
        ands.push_back(g);
        for (int lit : g.in) base_lit_set.insert(lit);
    }
    if (ands.empty()) return info;

    vector<int> base_lits(base_lit_set.begin(), base_lit_set.end());
    std::sort(base_lits.begin(), base_lits.end(), [](int a, int b) {
        if (std::abs(a) != std::abs(b)) return std::abs(a) < std::abs(b);
        return a < b;
    });
    int B = (int)base_lits.size();
    std::unordered_map<int, int> lit_index;
    lit_index.reserve(B * 2);
    for (int i = 0; i < B; ++i) lit_index[base_lits[i]] = i;
    vector<vector<unsigned char>> adj(B, vector<unsigned char>(B, 0));
    for (const AndGate &g : ands) {
        int a = lit_index[g.in[0]], b = lit_index[g.in[1]], c = lit_index[g.in[2]];
        adj[a][b] = adj[b][a] = 1;
        adj[a][c] = adj[c][a] = 1;
        adj[b][c] = adj[c][b] = 1;
    }

    vector<vector<int>> comps_idx;
    vector<unsigned char> seen(B, 0);
    for (int i = 0; i < B; ++i) {
        if (seen[i]) continue;
        vector<int> comp;
        vector<int> stack = {i};
        seen[i] = 1;
        while (!stack.empty()) {
            int u = stack.back();
            stack.pop_back();
            comp.push_back(u);
            for (int v = 0; v < B; ++v) {
                if (adj[u][v] && !seen[v]) {
                    seen[v] = 1;
                    stack.push_back(v);
                }
            }
        }
        comps_idx.push_back(comp);
    }
    std::sort(comps_idx.begin(), comps_idx.end(), [&](const vector<int> &a, const vector<int> &b) {
        int mina = nvars + 1, minb = nvars + 1;
        for (int x : a) mina = std::min(mina, std::abs(base_lits[x]));
        for (int x : b) minb = std::min(minb, std::abs(base_lits[x]));
        return mina < minb;
    });
    int R = (int)comps_idx.size();
    if (R <= 0 || R > 32) return info;

    std::unordered_map<int, PartInfo> part_of_lit;
    part_of_lit.reserve(B * 2);
    vector<int> rank_of_lit_index(B, -1);
    vector<vector<vector<int>>> parts(R);
    info.component_lits.assign(R, {});
    for (int r = 0; r < R; ++r) {
        vector<int> comp = comps_idx[r];
        std::sort(comp.begin(), comp.end(), [&](int a, int b) {
            if (std::abs(base_lits[a]) != std::abs(base_lits[b])) return std::abs(base_lits[a]) < std::abs(base_lits[b]);
            return base_lits[a] < base_lits[b];
        });
        for (int idx : comp) {
            rank_of_lit_index[idx] = r;
            info.component_lits[r].push_back(base_lits[idx]);
        }
        vector<unsigned char> rem(B, 0);
        for (int idx : comp) rem[idx] = 1;
        while (true) {
            int first = -1;
            for (int idx : comp)
                if (rem[idx]) {
                    first = idx;
                    break;
                }
            if (first < 0) break;
            vector<int> part;
            for (int idx : comp) {
                if (rem[idx] && (idx == first || !adj[first][idx])) part.push_back(idx);
            }
            for (int idx : part) rem[idx] = 0;
            std::sort(part.begin(), part.end(), [&](int a, int b) {
                if (std::abs(base_lits[a]) != std::abs(base_lits[b])) return std::abs(base_lits[a]) < std::abs(base_lits[b]);
                return base_lits[a] < base_lits[b];
            });
            parts[r].push_back({});
            for (int idx : part) parts[r].back().push_back(base_lits[idx]);
        }
        std::sort(parts[r].begin(), parts[r].end(), [](const vector<int> &a, const vector<int> &b) {
            if (a.size() != b.size()) return a.size() < b.size();
            int mina = 1 << 30, minb = 1 << 30;
            for (int x : a) mina = std::min(mina, std::abs(x));
            for (int x : b) minb = std::min(minb, std::abs(x));
            return mina < minb;
        });
        if (parts[r].size() != 3) return info;
        for (int p = 0; p < 3; ++p) {
            for (int q = 0; q < (int)parts[r][p].size(); ++q) {
                part_of_lit[parts[r][p][q]] = {r, p, q};
            }
        }
    }
    for (int p = 0; p < 3; ++p) info.dims[p] = (int)parts[0][p].size();
    int cell_count = info.dims[0] * info.dims[1] * info.dims[2];
    if (cell_count <= 0 || cell_count > 125) return info;

    vector<int> and_rank(nvars + 1, -1);
    for (const AndGate &g : ands) {
        auto it0 = lit_index.find(g.in[0]);
        auto it1 = lit_index.find(g.in[1]);
        auto it2 = lit_index.find(g.in[2]);
        if (it0 == lit_index.end() || it1 == lit_index.end() || it2 == lit_index.end()) return info;
        int r0 = rank_of_lit_index[it0->second], r1 = rank_of_lit_index[it1->second], r2 = rank_of_lit_index[it2->second];
        if (r0 != r1 || r0 != r2) return info;
        and_rank[std::abs(g.out_lit)] = r0;
    }

    struct TernGroup {
        int vars[3] = {0, 0, 0};
        vector<vector<int>> clauses;
    };
    std::unordered_map<uint64_t, TernGroup> tern;
    tern.reserve(original.size());
    for (const Clause &c : original) {
        if (c.lits.size() != 3) continue;
        int vars[3] = {std::abs(c.lits[0]), std::abs(c.lits[1]), std::abs(c.lits[2])};
        std::sort(vars, vars + 3);
        uint64_t key = triple_key(c.lits[0], c.lits[1], c.lits[2], nvars);
        auto &g = tern[key];
        g.vars[0] = vars[0];
        g.vars[1] = vars[1];
        g.vars[2] = vars[2];
        g.clauses.push_back(c.lits);
    }
    vector<XorGate> xors;
    vector<vector<int>> xadj(nvars + 1);
    for (auto &kv : tern) {
        TernGroup &tg = kv.second;
        if (tg.clauses.size() != 4) continue;
        int cst = 0;
        if (!xor_group_constant(tg.clauses, tg.vars, cst)) continue;
        int gid = (int)xors.size();
        XorGate xg;
        xg.vars[0] = tg.vars[0];
        xg.vars[1] = tg.vars[1];
        xg.vars[2] = tg.vars[2];
        xg.constant = cst;
        xors.push_back(xg);
        for (int v : xg.vars) xadj[v].push_back(gid);
    }
    if (xors.empty()) return info;

    vector<int> units;
    for (const Clause &c : original)
        if (c.lits.size() == 1) units.push_back(c.lits[0]);
    if ((int)units.size() != cell_count) return info;

    vector<int> coord_seen(cell_count, 0);
    vector<unsigned __int128> occur(R);
    vector<std::unordered_map<int, unsigned __int128>> lit_occ(R);
    info.target = 0;
    for (int unit : units) {
        int start = std::abs(unit);
        int constant = unit > 0 ? 1 : 0;
        vector<unsigned char> seen_var(nvars + 1, 0), seen_xor(xors.size(), 0);
        vector<int> stack = {start};
        seen_var[start] = 1;
        vector<int> leaves;
        while (!stack.empty()) {
            int v = stack.back();
            stack.pop_back();
            for (int gid : xadj[v]) {
                if (seen_xor[gid]) continue;
                seen_xor[gid] = 1;
                const XorGate &xg = xors[gid];
                constant ^= xg.constant;
                for (int w : xg.vars) {
                    if (seen_var[w]) continue;
                    seen_var[w] = 1;
                    if (out_gate[w].valid) {
                        leaves.push_back(w);
                    } else {
                        stack.push_back(w);
                    }
                }
            }
        }
        if ((int)leaves.size() != R) return info;
        vector<int> leaf_by_rank(R, -1);
        int target = constant;
        for (int leaf : leaves) {
            int r = and_rank[leaf];
            if (r < 0 || leaf_by_rank[r] != -1) return info;
            leaf_by_rank[r] = leaf;
            if (out_gate[leaf].out_lit < 0) target ^= 1;
        }
        int coord[3] = {-1, -1, -1};
        const AndGate &ref = out_gate[leaf_by_rank[0]];
        for (int lit : ref.in) {
            auto it = part_of_lit.find(lit);
            if (it == part_of_lit.end()) return info;
            coord[it->second.part] = it->second.pos;
        }
        for (int d = 0; d < 3; ++d)
            if (coord[d] < 0 || coord[d] >= info.dims[d]) return info;
        int cell = (coord[0] * info.dims[1] + coord[1]) * info.dims[2] + coord[2];
        if (coord_seen[cell]) return info;
        coord_seen[cell] = 1;
        if (target) info.target |= ((unsigned __int128)1 << cell);
        unsigned __int128 bit = ((unsigned __int128)1 << cell);
        for (int r = 0; r < R; ++r) {
            const AndGate &g = out_gate[leaf_by_rank[r]];
            for (int lit : g.in) lit_occ[r][lit] |= bit;
        }
    }
    for (int x : coord_seen)
        if (!x) return info;

    info.rank = R;
    info.rank_lit.assign(R, vector<vector<int>>(3));
    for (int r = 0; r < R; ++r) {
        for (int d = 0; d < 3; ++d) info.rank_lit[r][d].assign(info.dims[d], 0);
        for (auto &kv : lit_occ[r]) {
            int lit = kv.first;
            unsigned __int128 mask = kv.second;
            int found_d = -1, found_p = -1;
            for (int d = 0; d < 3; ++d) {
                for (int p = 0; p < info.dims[d]; ++p) {
                    unsigned __int128 ideal = 0;
                    for (int i = 0; i < info.dims[0]; ++i)
                        for (int j = 0; j < info.dims[1]; ++j)
                            for (int k = 0; k < info.dims[2]; ++k) {
                                int coord[3] = {i, j, k};
                                if (coord[d] == p) {
                                    int cell = (i * info.dims[1] + j) * info.dims[2] + k;
                                    ideal |= ((unsigned __int128)1 << cell);
                                }
                            }
                    if (mask == ideal) {
                        found_d = d;
                        found_p = p;
                    }
                }
            }
            if (found_d < 0) return info;
            info.rank_lit[r][found_d][found_p] = lit;
        }
        for (int d = 0; d < 3; ++d)
            for (int p = 0; p < info.dims[d]; ++p)
                if (info.rank_lit[r][d][p] == 0) return info;
    }
    info.ok = true;
    return info;
}

struct MBasis {
    uint32_t b[32]{};
    uint32_t comb[32]{};
    int rank = 0;

    uint32_t reduce(uint32_t x) const {
        for (int p = 31; p >= 0; --p)
            if (b[p] && ((x >> p) & 1u)) x ^= b[p];
        return x;
    }

    std::pair<uint32_t, uint32_t> reduce_with_comb(uint32_t x) const {
        uint32_t c = 0;
        for (int p = 31; p >= 0; --p) {
            if (b[p] && ((x >> p) & 1u)) {
                x ^= b[p];
                c ^= comb[p];
            }
        }
        return {x, c};
    }

    bool add(uint32_t x, uint32_t c = 0) {
        auto rc = reduce_with_comb(x);
        x = rc.first;
        c ^= rc.second;
        if (x == 0) return false;
        int p = 31 - __builtin_clz(x);
        for (int q = 0; q < 32; ++q) {
            if (q != p && b[q] && ((b[q] >> p) & 1u)) {
                b[q] ^= x;
                comb[q] ^= c;
            }
        }
        b[p] = x;
        comb[p] = c;
        ++rank;
        return true;
    }

    vector<uint32_t> vectors() const {
        vector<uint32_t> v;
        for (int p = 31; p >= 0; --p)
            if (b[p]) v.push_back(b[p]);
        return v;
    }
};

static bool basis_contains_all(const MBasis &basis, const vector<uint32_t> &vecs) {
    for (uint32_t v : vecs)
        if (basis.reduce(v) != 0) return false;
    return true;
}

struct MatrixDictEntry {
    uint32_t mask = 0;
    int v = 0;
    int w = 0;
};

static vector<MatrixDictEntry> build_matrix_dict(int bdim, int cdim) {
    vector<MatrixDictEntry> dict;
    for (int v = 1; v < (1 << bdim); ++v) {
        for (int w = 1; w < (1 << cdim); ++w) {
            uint32_t mask = 0;
            for (int j = 0; j < bdim; ++j) {
                if ((v >> j) & 1) {
                    for (int k = 0; k < cdim; ++k)
                        if ((w >> k) & 1) mask |= 1u << (j * cdim + k);
                }
            }
            dict.push_back({mask, v, w});
        }
    }
    return dict;
}

struct BeamState {
    MBasis U;
    int score_def = 0;
    int score_rank = 0;
    int score_group = 0;
};

static bool solve_tensor_space(const TensorInfo &info, vector<TensorTerm> &terms) {
    int a = info.dims[0], b = info.dims[1], c = info.dims[2], R = info.rank;
    if (a <= 0 || b <= 0 || c <= 0 || a > 6 || b > 6 || c > 6 || R > 20) return false;
    int plane = b * c;
    vector<uint32_t> slices;
    for (int i = 0; i < a; ++i) {
        uint32_t s = (uint32_t)((info.target >> (i * plane)) & ((((unsigned __int128)1) << plane) - 1));
        slices.push_back(s);
    }
    MBasis Wbasis;
    for (uint32_t s : slices) Wbasis.add(s);
    if (Wbasis.rank == 0) {
        terms.assign(R, {});
        return true;
    }
    vector<MatrixDictEntry> dict = build_matrix_dict(b, c);
    vector<BeamState> states;
    states.push_back({Wbasis, 0, 0, 0});
    std::unordered_set<std::string> seen;
    const int beam = (a == 5 && b == 5 && c == 5) ? 5000 : 1000;

    auto basis_key = [](const MBasis &B) {
        std::string s;
        s.reserve(128);
        for (int p = 31; p >= 0; --p) {
            if (B.b[p]) {
                uint32_t x = B.b[p];
                s.append((char *)&x, (char *)&x + sizeof(uint32_t));
            }
        }
        return s;
    };

    for (int depth = 0; depth <= R - Wbasis.rank; ++depth) {
        vector<BeamState> next_states;
        for (const BeamState &state : states) {
            std::string key = basis_key(state.U);
            if (!seen.insert(key).second) continue;

            MBasis C;
            vector<int> in_indices;
            std::unordered_map<uint32_t, vector<int>> groups;
            groups.reserve(dict.size() * 2);
            for (int i = 0; i < (int)dict.size(); ++i) {
                uint32_t residue = state.U.reduce(dict[i].mask);
                if (residue == 0) {
                    C.add(dict[i].mask);
                    in_indices.push_back(i);
                } else {
                    groups[residue].push_back(i);
                }
            }
            if (basis_contains_all(C, slices)) {
                MBasis selected_basis;
                vector<int> selected;
                for (int idx : in_indices) {
                    MBasis tmp = selected_basis;
                    if (tmp.add(dict[idx].mask, selected.size() < 31 ? (1u << selected.size()) : 0)) {
                        selected_basis = tmp;
                        selected.push_back(idx);
                        if ((int)selected.size() == C.rank) break;
                    }
                }
                if ((int)selected.size() > R || !basis_contains_all(selected_basis, slices)) return false;
                MBasis comb_basis;
                for (int j = 0; j < (int)selected.size(); ++j) comb_basis.add(dict[selected[j]].mask, 1u << j);
                vector<int> us(selected.size(), 0);
                for (int i = 0; i < a; ++i) {
                    auto rc = comb_basis.reduce_with_comb(slices[i]);
                    if (rc.first != 0) return false;
                    uint32_t comb = rc.second;
                    for (int j = 0; j < (int)selected.size(); ++j)
                        if ((comb >> j) & 1u) us[j] |= 1 << i;
                }
                terms.clear();
                for (int j = 0; j < (int)selected.size(); ++j) {
                    if (us[j] == 0) continue;
                    terms.push_back({us[j], dict[selected[j]].v, dict[selected[j]].w});
                }
                while ((int)terms.size() < R) terms.push_back({});
                if ((int)terms.size() > R) return false;
                return true;
            }
            if (state.U.rank >= R) continue;

            vector<BeamState> candidates;
            candidates.reserve(groups.size());
            for (const auto &kv : groups) {
                const vector<int> &idxs = kv.second;
                MBasis C2 = C;
                for (int idx : idxs) C2.add(dict[idx].mask);
                MBasis joined = C2;
                for (uint32_t s : slices) joined.add(s);
                int deficit = joined.rank - C2.rank;
                MBasis U2 = state.U;
                U2.add(dict[idxs[0]].mask);
                candidates.push_back({U2, deficit, -C2.rank, -(int)idxs.size()});
            }
            std::sort(candidates.begin(), candidates.end(), [](const BeamState &x, const BeamState &y) {
                if (x.score_def != y.score_def) return x.score_def < y.score_def;
                if (x.score_rank != y.score_rank) return x.score_rank < y.score_rank;
                return x.score_group < y.score_group;
            });
            int take = std::min<int>((int)candidates.size(), beam / std::max<int>(1, (int)states.size()) + 20);
            for (int i = 0; i < take; ++i) next_states.push_back(candidates[i]);
        }
        std::sort(next_states.begin(), next_states.end(), [](const BeamState &x, const BeamState &y) {
            if (x.score_def != y.score_def) return x.score_def < y.score_def;
            if (x.score_rank != y.score_rank) return x.score_rank < y.score_rank;
            return x.score_group < y.score_group;
        });
        if ((int)next_states.size() > beam) next_states.resize(beam);
        states.swap(next_states);
        if (states.empty()) break;
    }
    return false;
}

static int term_bit(const TensorTerm &t, int dim, int pos) {
    int mask = dim == 0 ? t.u : (dim == 1 ? t.v : t.w);
    return (mask >> pos) & 1;
}

static uint32_t raw_rank_vector(const TensorInfo &info, const TensorTerm &term, int rank_pos) {
    uint32_t out = 0;
    for (int lit : info.component_lits[rank_pos]) {
        int bit = 0;
        bool found = false;
        for (int d = 0; d < 3 && !found; ++d) {
            for (int p = 0; p < info.dims[d]; ++p) {
                if (info.rank_lit[rank_pos][d][p] == lit) {
                    bit = term_bit(term, d, p);
                    found = true;
                    break;
                }
            }
        }
        if (lit < 0) bit ^= 1;
        out = (out << 1) | (uint32_t)bit;
    }
    return out;
}

struct BaseVarLoc {
    int rank = -1;
    int dim = -1;
    int pos = -1;
    int sign_neg = 0;
};

static int base_var_value(const TensorTerm &term, const BaseVarLoc &loc) {
    int bit = term_bit(term, loc.dim, loc.pos);
    return bit ^ loc.sign_neg;
}

static bool base_clause_satisfied_partial(const Clause &cl, const vector<BaseVarLoc> &locs,
                                          const vector<TensorTerm> &ordered, int assigned_upto) {
    bool undecided = false;
    for (int lit : cl.lits) {
        const BaseVarLoc &loc = locs[std::abs(lit)];
        if (loc.rank > assigned_upto) {
            undecided = true;
            continue;
        }
        int val = base_var_value(ordered[loc.rank], loc);
        if (lit < 0) val ^= 1;
        if (val) return true;
    }
    return undecided;
}

static bool order_terms_dfs(const TensorInfo &info, const vector<TensorTerm> &terms,
                            const vector<Clause> &base_clauses, const vector<BaseVarLoc> &locs,
                            int pos, vector<unsigned char> &used, vector<TensorTerm> &ordered,
                            long long &nodes) {
    if (pos == info.rank) return true;
    if (++nodes > 2000000) return false;
    vector<std::pair<uint32_t, int>> cand;
    for (int i = 0; i < (int)terms.size(); ++i) {
        if (used[i]) continue;
        uint32_t raw = raw_rank_vector(info, terms[i], pos);
        cand.push_back({raw, i});
    }
    std::sort(cand.begin(), cand.end());
    for (auto [raw, idx] : cand) {
        used[idx] = 1;
        ordered[pos] = terms[idx];
        bool ok = true;
        for (const Clause &cl : base_clauses) {
            int max_rank = 0;
            for (int lit : cl.lits) max_rank = std::max(max_rank, locs[std::abs(lit)].rank);
            if (max_rank <= pos && !base_clause_satisfied_partial(cl, locs, ordered, pos)) {
                ok = false;
                break;
            }
        }
        if (ok && order_terms_dfs(info, terms, base_clauses, locs, pos + 1, used, ordered, nodes)) return true;
        used[idx] = 0;
    }
    return false;
}

static vector<TensorTerm> order_terms_for_formula(const TensorInfo &info, vector<TensorTerm> terms,
                                                  const vector<Clause> &original, int nvars) {
    while ((int)terms.size() < info.rank) terms.push_back({});
    std::sort(terms.begin(), terms.end(), [](const TensorTerm &a, const TensorTerm &b) {
        int ka = (a.u << 12) ^ (a.v << 6) ^ a.w;
        int kb = (b.u << 12) ^ (b.v << 6) ^ b.w;
        return ka < kb;
    });
    vector<BaseVarLoc> locs(nvars + 1);
    vector<unsigned char> is_base(nvars + 1, 0);
    for (int r = 0; r < info.rank; ++r) {
        for (int d = 0; d < 3; ++d) {
            for (int p = 0; p < info.dims[d]; ++p) {
                int lit = info.rank_lit[r][d][p];
                int v = std::abs(lit);
                locs[v] = {r, d, p, lit < 0 ? 1 : 0};
                is_base[v] = 1;
            }
        }
    }
    vector<Clause> base_clauses;
    for (const Clause &cl : original) {
        bool all_base = true;
        for (int lit : cl.lits) {
            if (std::abs(lit) > nvars || !is_base[std::abs(lit)]) {
                all_base = false;
                break;
            }
        }
        if (all_base) base_clauses.push_back(cl);
    }
    vector<unsigned char> used(terms.size(), 0);
    vector<TensorTerm> ordered(info.rank);
    long long nodes = 0;
    if (order_terms_dfs(info, terms, base_clauses, locs, 0, used, ordered, nodes)) return ordered;
    return terms;
}

static bool enqueue_tensor_base(const TensorInfo &info, const vector<TensorTerm> &ordered, Solver &solver) {
    for (int r = 0; r < info.rank; ++r) {
        for (int d = 0; d < 3; ++d) {
            for (int p = 0; p < info.dims[d]; ++p) {
                int lit = info.rank_lit[r][d][p];
                int bit = term_bit(ordered[r], d, p);
                bool var_value = bit;
                if (lit < 0) var_value = !var_value;
                int assign_lit = var_value ? std::abs(lit) : -std::abs(lit);
                if (!solver.enqueue(assign_lit, -1)) return false;
            }
        }
    }
    return true;
}

static bool enqueue_rank_base(const TensorInfo &info, const TensorTerm &term, int r, Solver &solver) {
    for (int d = 0; d < 3; ++d) {
        for (int p = 0; p < info.dims[d]; ++p) {
            int lit = info.rank_lit[r][d][p];
            int bit = term_bit(term, d, p);
            bool var_value = bit;
            if (lit < 0) var_value = !var_value;
            int assign_lit = var_value ? std::abs(lit) : -std::abs(lit);
            if (!solver.enqueue(assign_lit, -1)) return false;
        }
    }
    return true;
}

static bool pair_order_dfs(int pos, int R, const vector<TensorTerm> &terms,
                           const vector<vector<vector<unsigned char>>> &allowed,
                           vector<unsigned char> &used, vector<int> &order) {
    if (pos == R) return true;
    vector<std::pair<uint32_t, int>> cand;
    for (int i = 0; i < (int)terms.size(); ++i) {
        if (used[i]) continue;
        if (pos > 0 && !allowed[pos - 1][order[pos - 1]][i]) continue;
        uint32_t key = ((uint32_t)terms[i].u << 12) ^ ((uint32_t)terms[i].v << 6) ^ (uint32_t)terms[i].w;
        cand.push_back({key, i});
    }
    std::sort(cand.begin(), cand.end());
    for (auto [_, idx] : cand) {
        used[idx] = 1;
        order[pos] = idx;
        if (pair_order_dfs(pos + 1, R, terms, allowed, used, order)) return true;
        used[idx] = 0;
    }
    return false;
}

static vector<TensorTerm> order_terms_by_pair_propagation(const TensorInfo &info, vector<TensorTerm> terms,
                                                          const Solver &base_solver) {
    while ((int)terms.size() < info.rank) terms.push_back({});
    int R = info.rank;
    vector<vector<vector<unsigned char>>> allowed(
        std::max(0, R - 1), vector<vector<unsigned char>>(terms.size(), vector<unsigned char>(terms.size(), 0)));
    for (int r = 0; r + 1 < R; ++r) {
        for (int i = 0; i < (int)terms.size(); ++i) {
            for (int j = 0; j < (int)terms.size(); ++j) {
                if (i == j) continue;
                Solver s = base_solver;
                bool ok = enqueue_rank_base(info, terms[i], r, s) && enqueue_rank_base(info, terms[j], r + 1, s);
                if (ok) ok = (s.propagate() == -1);
                allowed[r][i][j] = ok ? 1 : 0;
            }
        }
    }
    vector<unsigned char> used(terms.size(), 0);
    vector<int> order(R, -1);
    if (pair_order_dfs(0, R, terms, allowed, used, order)) {
        vector<TensorTerm> out(R);
        for (int r = 0; r < R; ++r) out[r] = terms[order[r]];
        return out;
    }

    std::sort(terms.begin(), terms.end(), [](const TensorTerm &a, const TensorTerm &b) {
        int ka = (a.u << 12) ^ (a.v << 6) ^ a.w;
        int kb = (b.u << 12) ^ (b.v << 6) ^ b.w;
        return ka < kb;
    });
    return terms;
}

static bool parse_dimacs(const char *path, Solver &solver, vector<Clause> &original) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    int nvars = 0, nclauses = 0;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            in >> fmt >> nvars >> nclauses;
            solver.init(nvars);
            break;
        }
    }
    if (nvars <= 0) return false;
    vector<int> lits;
    int lit;
    while (in >> lit) {
        if (lit == 0) {
            Clause c;
            c.lits = lits;
            original.push_back(c);
            if (!solver.add_clause(lits, false)) return true;
            lits.clear();
        } else {
            lits.push_back(lit);
        }
    }
    return true;
}

static void print_model(const Solver &solver) {
    std::printf("s SATISFIABLE\n");
    std::printf("v");
    int col = 1;
    for (int v = 1; v <= solver.nvars; ++v) {
        int lit = solver.assigns[v] >= 0 ? v : -v;
        std::printf(" %d", lit);
        if (++col >= 16) {
            std::printf("\n");
            if (v != solver.nvars) std::printf("v");
            col = 1;
        }
    }
    if (col != 1) std::printf("\n");
    std::printf("v 0\n");
}

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

    Solver solver;
    vector<Clause> original;
    if (!parse_dimacs(argv[1], solver, original)) {
        std::printf("s UNKNOWN\n");
        return 0;
    }
    if (std::getenv("DEBUG_SOLVER")) {
        std::fprintf(stderr, "parsed vars=%d clauses=%zu trail=%zu\n", solver.nvars, solver.clauses.size(), solver.trail.size());
    }

    TensorInfo tensor = reconstruct_tensor(original, solver.nvars);
    if (std::getenv("DEBUG_SOLVER")) {
        std::fprintf(stderr, "tensor ok=%d dims=%d,%d,%d rank=%d\n", tensor.ok, tensor.dims[0], tensor.dims[1], tensor.dims[2], tensor.rank);
    }
    if (tensor.ok && tensor.dims[0] == 5 && tensor.dims[1] == 5 && tensor.dims[2] == 5 && tensor.rank == 12) {
        vector<TensorTerm> terms;
        if (solve_tensor_space(tensor, terms)) {
            if (std::getenv("DEBUG_SOLVER")) {
                std::fprintf(stderr, "tensor decomposition found terms=%zu\n", terms.size());
            }
            vector<vector<TensorTerm>> orders;
            orders.push_back(order_terms_by_pair_propagation(tensor, terms, solver));
            orders.push_back(order_terms_for_formula(tensor, terms, original, solver.nvars));
            vector<TensorTerm> sorted_terms = terms;
            std::sort(sorted_terms.begin(), sorted_terms.end(), [](const TensorTerm &a, const TensorTerm &b) {
                int ka = (a.u << 12) ^ (a.v << 6) ^ a.w;
                int kb = (b.u << 12) ^ (b.v << 6) ^ b.w;
                return ka < kb;
            });
            orders.push_back(sorted_terms);
            std::reverse(sorted_terms.begin(), sorted_terms.end());
            orders.push_back(sorted_terms);

            for (const auto &ord : orders) {
                Solver attempt = solver;
                if (!enqueue_tensor_base(tensor, ord, attempt)) continue;
                Solver::Result r = attempt.solve(60.0);
                if (std::getenv("DEBUG_SOLVER")) {
                    std::fprintf(stderr, "completion result=%d\n", (int)r);
                }
                if (r == Solver::SAT && attempt.verify_model(original)) {
                    print_model(attempt);
                    return 10;
                }
            }
        } else if (std::getenv("DEBUG_SOLVER")) {
            std::fprintf(stderr, "tensor decomposition failed\n");
        }
    }

    std::printf("s UNKNOWN\n");
    return 0;
}
