#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct ParityRel {
    vector<int> vars; // 1-based CNF variables
    int rhs = 0;      // xor(vars) == rhs
};

struct QuadEq {
    vector<pair<int, int>> pairs; // zero-based x/y indices
    int rhs = 0;
};

static uint64_t rng_state = 0x9e3779b97f4a7c15ULL;
static inline uint64_t rng64() {
    uint64_t x = rng_state;
    x ^= x >> 12;
    x ^= x << 25;
    x ^= x >> 27;
    rng_state = x;
    return x * 2685821657736338717ULL;
}

static bool parse_dimacs(const string &path, int &nvars, vector<Clause> &clauses) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    nvars = 0;
    int declared_clauses = 0;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string fmt;
            in >> fmt >> nvars >> declared_clauses;
        } else if (tok == "%") {
            break;
        } else {
            Clause c;
            int lit = stoi(tok);
            while (lit != 0) {
                c.lits.push_back(lit);
                if (!(in >> lit)) return false;
            }
            clauses.push_back(std::move(c));
        }
    }
    return nvars > 0 && !clauses.empty();
}

static bool is_and_clause(const vector<int> &c, int N, int base, int andmax) {
    if (c.size() == 2) {
        if (c[0] >= 0) return false;
        int z = -c[0];
        if (z <= base || z > andmax) return false;
        int k = z - base - 1;
        int a = k / N + 1;
        int b = N + (k % N) + 1;
        return c[1] == a || c[1] == b;
    }
    if (c.size() == 3) {
        if (!(c[0] < 0 && c[1] < 0 && c[2] > base && c[2] <= andmax)) return false;
        int k = c[2] - base - 1;
        return -c[0] == k / N + 1 && -c[1] == N + (k % N) + 1;
    }
    return false;
}

static uint64_t key_abs_vars(const vector<int> &c, vector<int> &vars) {
    vars.clear();
    for (int lit : c) vars.push_back(abs(lit));
    sort(vars.begin(), vars.end());
    uint64_t h = 1469598103934665603ULL;
    for (int v : vars) {
        h ^= (uint64_t)v + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
        h *= 1099511628211ULL;
    }
    h ^= vars.size();
    return h;
}

static bool relation_from_group(const vector<vector<int>> &group, ParityRel &out) {
    vector<int> vars;
    for (int lit : group[0]) vars.push_back(abs(lit));
    sort(vars.begin(), vars.end());
    int k = (int)vars.size();
    if (k < 2 || k > 4) return false;
    vector<int> forbidden;
    for (const auto &c : group) {
        int mask = 0;
        for (int pos = 0; pos < k; ++pos) {
            int v = vars[pos], litv = 0;
            for (int lit : c) if (abs(lit) == v) { litv = lit; break; }
            if (litv == 0) return false;
            // This literal is false when x_v is 0 for positive, 1 for negative.
            if (litv < 0) mask |= (1 << pos);
        }
        forbidden.push_back(mask);
    }
    sort(forbidden.begin(), forbidden.end());
    forbidden.erase(unique(forbidden.begin(), forbidden.end()), forbidden.end());
    int allowed_parity[2] = {0, 0};
    for (int mask = 0; mask < (1 << k); ++mask) {
        if (binary_search(forbidden.begin(), forbidden.end(), mask)) continue;
        allowed_parity[__builtin_popcount((unsigned)mask) & 1]++;
    }
    int half = 1 << (k - 1);
    if (allowed_parity[0] == half && allowed_parity[1] == 0) {
        out.vars = vars; out.rhs = 0; return true;
    }
    if (allowed_parity[1] == half && allowed_parity[0] == 0) {
        out.vars = vars; out.rhs = 1; return true;
    }
    return false;
}

struct XorBasis {
    int words;
    vector<vector<uint64_t>> row;
    vector<int> rhs;
    vector<char> used;
    XorBasis(int nbits) : words((nbits + 63) / 64), row(nbits, vector<uint64_t>(words)), rhs(nbits), used(nbits, 0) {}
    static int top_bit(const vector<uint64_t> &v) {
        for (int w = (int)v.size() - 1; w >= 0; --w) {
            if (v[w]) return w * 64 + 63 - __builtin_clzll(v[w]);
        }
        return -1;
    }
    bool insert_reduce(vector<uint64_t> v, int r, int stop_below, vector<pair<vector<uint64_t>, int>> &projected) {
        while (true) {
            int p = top_bit(v);
            if (p < 0) return r == 0;
            if (p < stop_below) {
                projected.push_back({std::move(v), r});
                return true;
            }
            if (!used[p]) {
                used[p] = 1; row[p] = std::move(v); rhs[p] = r;
                return true;
            }
            for (int w = 0; w < words; ++w) v[w] ^= row[p][w];
            r ^= rhs[p];
        }
    }
};

static bool extract(const vector<Clause> &clauses, int nvars, int &N, vector<QuadEq> &quad, vector<ParityRel> &rels) {
    if (clauses.size() < 4 || clauses[0].lits.size() != 1 || clauses[2].lits.size() != 1) return false;
    if (clauses[0].lits[0] != 1) return false;
    N = clauses[2].lits[0] - 1;
    if (N <= 1 || clauses[2].lits[0] != N + 1) return false;
    int base = 2 * N, andmax = base + N * N;
    if (andmax > nvars) return false;

    vector<ParityRel> equations;
    unordered_map<uint64_t, vector<int>> bucket_index;
    vector<vector<vector<int>>> buckets;
    vector<int> tmp;

    for (const Clause &cl : clauses) {
        const auto &c = cl.lits;
        if (is_and_clause(c, N, base, andmax)) continue;
        if (c.size() == 1) {
            int v = abs(c[0]);
            if (v > base) {
                equations.push_back({{v}, c[0] > 0 ? 1 : 0});
                rels.push_back({{v}, c[0] > 0 ? 1 : 0});
            }
        } else if (c.size() >= 2 && c.size() <= 4) {
            uint64_t h = key_abs_vars(c, tmp);
            int found = -1;
            auto &ids = bucket_index[h];
            for (int id : ids) {
                vector<int> other;
                key_abs_vars(buckets[id][0], other);
                if (other == tmp) { found = id; break; }
            }
            if (found < 0) {
                found = (int)buckets.size();
                ids.push_back(found);
                buckets.push_back({});
            }
            buckets[found].push_back(c);
        } else if (c.size() == (size_t)N - 1) {
            // The two nontriviality clauses are checked separately.
        } else {
            return false;
        }
    }
    for (const auto &g : buckets) {
        ParityRel pr;
        if (!relation_from_group(g, pr)) return false;
        equations.push_back(pr);
        rels.push_back(pr);
    }
    for (const auto &pr : equations) if (pr.vars.size() == 1) rels.push_back(pr);

    XorBasis xb(nvars);
    vector<pair<vector<uint64_t>, int>> projected;
    for (const auto &e : equations) {
        vector<uint64_t> bits((nvars + 63) / 64, 0);
        for (int v : e.vars) bits[(v - 1) / 64] ^= 1ULL << ((v - 1) & 63);
        if (!xb.insert_reduce(std::move(bits), e.rhs, andmax, projected)) return false;
    }
    for (auto &rr : projected) {
        auto &bits = rr.first;
        bool has_and = false;
        for (int v = base + 1; v <= andmax; ++v) {
            if ((bits[(v - 1) / 64] >> ((v - 1) & 63)) & 1ULL) { has_and = true; break; }
        }
        if (!has_and) continue;
        QuadEq qe; qe.rhs = rr.second;
        for (int v = base + 1; v <= andmax; ++v) {
            if ((bits[(v - 1) / 64] >> ((v - 1) & 63)) & 1ULL) {
                int k = v - base - 1;
                qe.pairs.push_back({k / N, k % N});
            }
        }
        quad.push_back(std::move(qe));
    }
    return !quad.empty();
}

static bool solve_linear_y(const vector<QuadEq> &quad, int N, const vector<int> &x, uint64_t salt, vector<int> &y) {
    vector<uint64_t> basis(N, 0);
    vector<int> brhs(N, 0), used(N, 0);
    auto addrow = [&](uint64_t mask, int rhs) -> bool {
        while (mask) {
            int p = 63 - __builtin_clzll(mask);
            if (!used[p]) { used[p] = 1; basis[p] = mask; brhs[p] = rhs; return true; }
            mask ^= basis[p]; rhs ^= brhs[p];
        }
        return rhs == 0;
    };
    for (const auto &eq : quad) {
        uint64_t mask = 0;
        for (auto [i, j] : eq.pairs) if (x[i]) mask ^= 1ULL << j;
        if (!addrow(mask, eq.rhs)) return false;
    }
    if (!addrow(1ULL, 1)) return false;
    uint64_t ym = 0;
    // Randomize free variables for repeated attempts.
    for (int p = 1; p < N && p < 63; ++p) if (!used[p] && ((salt >> (p & 63)) & 1ULL)) ym |= 1ULL << p;
    for (int p = 0; p < N && p < 63; ++p) if (used[p]) {
        int s = __builtin_popcountll(basis[p] & ym) & 1;
        if (s ^ brhs[p]) ym ^= 1ULL << p;
    }
    y.assign(N, 0);
    for (int i = 0; i < N && i < 63; ++i) y[i] = (ym >> i) & 1ULL;
    return true;
}

static bool solve_linear_y_big(const vector<QuadEq> &quad, int N, const vector<int> &x, uint64_t salt, vector<int> &y) {
    if (N <= 63) return solve_linear_y(quad, N, x, salt, y);
    int W = (N + 63) / 64;
    vector<vector<uint64_t>> basis(N, vector<uint64_t>(W, 0));
    vector<int> brhs(N, 0), used(N, 0);
    auto top = [&](const vector<uint64_t> &m) {
        for (int w = W - 1; w >= 0; --w) if (m[w]) return w * 64 + 63 - __builtin_clzll(m[w]);
        return -1;
    };
    auto addrow = [&](vector<uint64_t> mask, int rhs) -> bool {
        while (true) {
            int p = top(mask);
            if (p < 0) return rhs == 0;
            if (!used[p]) { used[p] = 1; basis[p] = std::move(mask); brhs[p] = rhs; return true; }
            for (int w = 0; w < W; ++w) mask[w] ^= basis[p][w];
            rhs ^= brhs[p];
        }
    };
    for (const auto &eq : quad) {
        vector<uint64_t> mask(W, 0);
        for (auto [i, j] : eq.pairs) if (x[i]) mask[j / 64] ^= 1ULL << (j & 63);
        if (!addrow(std::move(mask), eq.rhs)) return false;
    }
    vector<uint64_t> one(W, 0); one[0] = 1;
    if (!addrow(std::move(one), 1)) return false;
    vector<uint64_t> ym(W, 0);
    for (int p = 1; p < N; ++p) if (!used[p] && ((rng64() ^ salt) & 1ULL)) ym[p / 64] ^= 1ULL << (p & 63);
    for (int p = 0; p < N; ++p) if (used[p]) {
        int s = 0;
        for (int w = 0; w < W; ++w) s ^= __builtin_popcountll(basis[p][w] & ym[w]) & 1;
        if (s ^ brhs[p]) ym[p / 64] ^= 1ULL << (p & 63);
    }
    y.assign(N, 0);
    for (int i = 0; i < N; ++i) y[i] = (ym[i / 64] >> (i & 63)) & 1ULL;
    return true;
}

static bool check_quad(const vector<QuadEq> &quad, const vector<int> &x, const vector<int> &y) {
    for (const auto &eq : quad) {
        int s = 0;
        for (auto [i, j] : eq.pairs) s ^= (x[i] & y[j]);
        if (s != eq.rhs) return false;
    }
    return true;
}

static bool assign_and_verify(int nvars, int N, const vector<Clause> &clauses, const vector<ParityRel> &rels,
                              const vector<int> &x, const vector<int> &y, vector<int> &val) {
    int base = 2 * N, andmax = base + N * N;
    val.assign(nvars + 1, 0);
    for (int i = 0; i < N; ++i) {
        val[i + 1] = x[i];
        val[N + i + 1] = y[i];
    }
    for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j) {
        val[base + i * N + j + 1] = x[i] & y[j];
    }
    vector<const ParityRel*> bymax(nvars + 1, nullptr);
    for (const auto &r : rels) {
        int mx = *max_element(r.vars.begin(), r.vars.end());
        if (mx > andmax) bymax[mx] = &r;
    }
    for (int v = andmax + 1; v <= nvars; ++v) {
        if (!bymax[v]) continue;
        int s = bymax[v]->rhs;
        for (int u : bymax[v]->vars) if (u != v) s ^= val[u];
        val[v] = s;
    }
    for (const Clause &c : clauses) {
        bool sat = false;
        for (int lit : c.lits) {
            int b = val[abs(lit)];
            if ((lit > 0 && b) || (lit < 0 && !b)) { sat = true; break; }
        }
        if (!sat) return false;
    }
    return true;
}

static bool try_candidate(const vector<QuadEq> &quad, int N, const vector<int> &x, uint64_t salt, vector<int> &y) {
    if (accumulate(x.begin() + 1, x.end(), 0) == 0) return false;
    if (!solve_linear_y_big(quad, N, x, salt, y)) return false;
    if (accumulate(y.begin() + 1, y.end(), 0) == 0) return false;
    return check_quad(quad, x, y);
}

static bool solve_linear_x_big(const vector<QuadEq> &quad, int N, const vector<int> &y, uint64_t salt, vector<int> &x) {
    int W = (N + 63) / 64;
    vector<vector<uint64_t>> basis(N, vector<uint64_t>(W, 0));
    vector<int> brhs(N, 0), used(N, 0);
    auto top = [&](const vector<uint64_t> &m) {
        for (int w = W - 1; w >= 0; --w) if (m[w]) return w * 64 + 63 - __builtin_clzll(m[w]);
        return -1;
    };
    auto addrow = [&](vector<uint64_t> mask, int rhs) -> bool {
        while (true) {
            int p = top(mask);
            if (p < 0) return rhs == 0;
            if (!used[p]) { used[p] = 1; basis[p] = std::move(mask); brhs[p] = rhs; return true; }
            for (int w = 0; w < W; ++w) mask[w] ^= basis[p][w];
            rhs ^= brhs[p];
        }
    };
    for (const auto &eq : quad) {
        vector<uint64_t> mask(W, 0);
        for (auto [i, j] : eq.pairs) if (y[j]) mask[i / 64] ^= 1ULL << (i & 63);
        if (!addrow(std::move(mask), eq.rhs)) return false;
    }
    vector<uint64_t> one(W, 0); one[0] = 1;
    if (!addrow(std::move(one), 1)) return false;
    vector<uint64_t> xm(W, 0);
    for (int p = 1; p < N; ++p) if (!used[p] && ((rng64() ^ salt) & 1ULL)) xm[p / 64] ^= 1ULL << (p & 63);
    for (int p = 0; p < N; ++p) if (used[p]) {
        int s = 0;
        for (int w = 0; w < W; ++w) s ^= __builtin_popcountll(basis[p][w] & xm[w]) & 1;
        if (s ^ brhs[p]) xm[p / 64] ^= 1ULL << (p & 63);
    }
    x.assign(N, 0);
    for (int i = 0; i < N; ++i) x[i] = (xm[i / 64] >> (i & 63)) & 1ULL;
    return true;
}

static bool try_candidate_y(const vector<QuadEq> &quad, int N, const vector<int> &yin, uint64_t salt, vector<int> &x) {
    if (accumulate(yin.begin() + 1, yin.end(), 0) == 0) return false;
    if (!solve_linear_x_big(quad, N, yin, salt, x)) return false;
    if (accumulate(x.begin() + 1, x.end(), 0) == 0) return false;
    return check_quad(quad, x, yin);
}

struct IncItem { int e; vector<int> js; };

static int count_bad_quad(const vector<QuadEq> &quad, const vector<int> &x, const vector<int> &y) {
    int uns = 0;
    for (const auto &eq : quad) {
        int s = eq.rhs;
        for (auto [i, j] : eq.pairs) s ^= x[i] & y[j];
        uns += s;
    }
    return uns;
}

static bool nontrivial_xy(const vector<int> &x, const vector<int> &y) {
    return accumulate(x.begin() + 1, x.end(), 0) > 0 &&
           accumulate(y.begin() + 1, y.end(), 0) > 0;
}

static bool linearized_repair_quad(const vector<QuadEq> &quad, int N, const function<double()> &elapsed,
                                   vector<int> &x, vector<int> &y, int &uns) {
    if (elapsed() > 555.0) return false;
    const int D = 2 * (N - 1);
    const int W = (D + 63) / 64;
    vector<vector<uint64_t>> basis(D, vector<uint64_t>(W, 0));
    vector<int> brhs(D, 0), used(D, 0);
    auto top = [&](const vector<uint64_t> &m) {
        for (int w = W - 1; w >= 0; --w) if (m[w]) return w * 64 + 63 - __builtin_clzll(m[w]);
        return -1;
    };
    auto addrow = [&](vector<uint64_t> mask, int rhs) {
        while (true) {
            int p = top(mask);
            if (p < 0) return rhs == 0;
            if (!used[p]) {
                used[p] = 1;
                basis[p] = std::move(mask);
                brhs[p] = rhs;
                return true;
            }
            for (int w = 0; w < W; ++w) mask[w] ^= basis[p][w];
            rhs ^= brhs[p];
        }
    };

    int rows = 0;
    for (const auto &eq : quad) {
        vector<uint64_t> mask(W, 0);
        int rhs = eq.rhs;
        for (auto [i, j] : eq.pairs) {
            rhs ^= x[i] & y[j];
            if (i > 0 && y[j]) {
                int b = i - 1;
                mask[b / 64] ^= 1ULL << (b & 63);
            }
            if (j > 0 && x[i]) {
                int b = (N - 1) + (j - 1);
                mask[b / 64] ^= 1ULL << (b & 63);
            }
        }
        if (addrow(std::move(mask), rhs)) ++rows;
    }
    if (rows == 0) return false;

    int best_uns = uns;
    vector<int> best_x, best_y;
    const int trials = (uns <= 100 ? 24 : 8);
    for (int t = 0; t < trials && elapsed() < 555.0; ++t) {
        vector<uint64_t> sol(W, 0);
        if (t > 0) {
            for (int b = 0; b < D; ++b) {
                if (!used[b] && (rng64() & 1ULL)) sol[b / 64] ^= 1ULL << (b & 63);
            }
        }
        for (int p = 0; p < D; ++p) {
            if (!used[p]) continue;
            int s = 0;
            for (int w = 0; w < W; ++w) s ^= __builtin_popcountll(basis[p][w] & sol[w]) & 1;
            if (s ^ brhs[p]) sol[p / 64] ^= 1ULL << (p & 63);
        }
        vector<int> tx = x, ty = y;
        for (int i = 1; i < N; ++i) {
            int b = i - 1;
            if ((sol[b / 64] >> (b & 63)) & 1ULL) tx[i] ^= 1;
        }
        for (int j = 1; j < N; ++j) {
            int b = (N - 1) + (j - 1);
            if ((sol[b / 64] >> (b & 63)) & 1ULL) ty[j] ^= 1;
        }
        if (!nontrivial_xy(tx, ty)) continue;
        int cu = count_bad_quad(quad, tx, ty);
        if (cu < best_uns) {
            best_uns = cu;
            best_x = std::move(tx);
            best_y = std::move(ty);
            if (cu == 0) break;
        }
    }
    if (best_uns < uns) {
        x = std::move(best_x);
        y = std::move(best_y);
        uns = best_uns;
        return true;
    }
    return false;
}

static bool local_search_quad(const vector<QuadEq> &quad, int N, const function<double()> &elapsed, vector<int> &x, vector<int> &y) {
    vector<vector<IncItem>> incX(N), incY(N);
    for (int e = 0; e < (int)quad.size(); ++e) {
        vector<vector<int>> tx(N), ty(N);
        for (auto [i,j] : quad[e].pairs) { tx[i].push_back(j); ty[j].push_back(i); }
        for (int i = 1; i < N; ++i) if (!tx[i].empty()) incX[i].push_back({e, tx[i]});
        for (int j = 1; j < N; ++j) if (!ty[j].empty()) incY[j].push_back({e, ty[j]});
    }
    auto eval_all = [&]() {
        vector<unsigned char> bad(quad.size());
        int uns = 0;
        for (int e = 0; e < (int)quad.size(); ++e) {
            int s = 0;
            for (auto [i,j] : quad[e].pairs) s ^= x[i] & y[j];
            bad[e] = (s != quad[e].rhs);
            uns += bad[e];
        }
        return pair<vector<unsigned char>, int>(std::move(bad), uns);
    };
    auto toggles_x = [&](int i, const IncItem &it) {
        int s = 0; for (int j : it.js) s ^= y[j]; return s;
    };
    auto toggles_y = [&](int j, const IncItem &it) {
        int s = 0; for (int i : it.js) s ^= x[i]; return s;
    };
    auto delta_x = [&](int i, const vector<unsigned char> &bad) {
        int d = 0; for (const auto &it : incX[i]) if (toggles_x(i, it)) d += bad[it.e] ? -1 : 1; return d;
    };
    auto delta_y = [&](int j, const vector<unsigned char> &bad) {
        int d = 0; for (const auto &it : incY[j]) if (toggles_y(j, it)) d += bad[it.e] ? -1 : 1; return d;
    };
    auto flip_x = [&](int i, vector<unsigned char> &bad, int &uns) {
        for (const auto &it : incX[i]) if (toggles_x(i, it)) { uns += bad[it.e] ? -1 : 1; bad[it.e] ^= 1; }
        x[i] ^= 1;
    };
    auto flip_y = [&](int j, vector<unsigned char> &bad, int &uns) {
        for (const auto &it : incY[j]) if (toggles_y(j, it)) { uns += bad[it.e] ? -1 : 1; bad[it.e] ^= 1; }
        y[j] ^= 1;
    };
    int min_support = (N >= 120 ? 6 : 1);
    auto count_x = [&]() { return accumulate(x.begin() + 1, x.end(), 0); };
    auto count_y = [&]() { return accumulate(y.begin() + 1, y.end(), 0); };
    auto raise_support = [&](vector<unsigned char> &bad, int &uns) {
        while (count_x() < min_support) {
            int i = 1 + rng64() % (N - 1);
            if (!x[i]) flip_x(i, bad, uns);
        }
        while (count_y() < min_support) {
            int j = 1 + rng64() % (N - 1);
            if (!y[j]) flip_y(j, bad, uns);
        }
    };

    for (int restart = 0; elapsed() < 560.0; ++restart) {
        fill(x.begin(), x.end(), 0); fill(y.begin(), y.end(), 0); x[0] = y[0] = 1;
        double p = (restart % 5 == 0) ? 0.08 : (restart % 5 == 1 ? 0.16 : (restart % 5 == 2 ? 0.28 : (restart % 5 == 3 ? 0.45 : 0.65)));
        uint64_t threshold = (uint64_t)(p * (long double)UINT64_MAX);
        for (int i = 1; i < N; ++i) { x[i] = rng64() < threshold; y[i] = rng64() < threshold; }
        while (accumulate(x.begin()+1, x.end(), 0) < min_support) x[1 + rng64() % (N-1)] = 1;
        while (accumulate(y.begin()+1, y.end(), 0) < min_support) y[1 + rng64() % (N-1)] = 1;
        auto [bad, uns] = eval_all();
        if (linearized_repair_quad(quad, N, elapsed, x, y, uns)) {
            auto refreshed = eval_all();
            bad = std::move(refreshed.first);
            uns = refreshed.second;
            raise_support(bad, uns);
            if (uns == 0 && nontrivial_xy(x, y)) return true;
        }
        int best_seen = uns;
        for (int step = 0; step < 200000 && elapsed() < 560.0; ++step) {
            if (uns == 0 && nontrivial_xy(x, y)) return true;
            if ((step % (uns <= 200 ? 256 : 2048)) == 0) {
                int before = uns;
                if (linearized_repair_quad(quad, N, elapsed, x, y, uns)) {
                    auto refreshed = eval_all();
                    bad = std::move(refreshed.first);
                    uns = refreshed.second;
                    raise_support(bad, uns);
                    if (uns < best_seen) best_seen = uns;
                    if (uns == 0 && nontrivial_xy(x, y)) return true;
                    if (uns < before) continue;
                }
            }
            if (uns <= 50 && (step % 16) == 0) {
                vector<int> tx = x, ty;
                if (try_candidate(quad, N, tx, rng64(), ty)) { x = tx; y = ty; return true; }
                ty = y;
                if (try_candidate_y(quad, N, ty, rng64(), tx)) { x = tx; y = ty; return true; }
            }
            if (uns <= 40 && (step % 64) == 0) {
                vector<int> tx, ty;
                for (int a = 1; a < N; ++a) {
                    if (elapsed() > 555.0) break;
                    tx = x; tx[a] ^= 1;
                    if (try_candidate(quad, N, tx, rng64(), ty)) { x = tx; y = ty; return true; }
                    ty = y; ty[a] ^= 1;
                    if (try_candidate_y(quad, N, ty, rng64(), tx)) { x = tx; y = ty; return true; }
                }
                if (N <= 110 && uns <= 30) {
                    for (int a = 1; a < N; ++a) for (int b = a + 1; b < N; ++b) {
                        if (elapsed() > 555.0) break;
                        tx = x; tx[a] ^= 1; tx[b] ^= 1;
                        if (try_candidate(quad, N, tx, rng64(), ty)) { x = tx; y = ty; return true; }
                        ty = y; ty[a] ^= 1; ty[b] ^= 1;
                        if (try_candidate_y(quad, N, ty, rng64(), tx)) { x = tx; y = ty; return true; }
                    }
                }
            }
            if (uns < best_seen) {
                best_seen = uns;
                if ((best_seen % 10) == 0) { printf("c sls restart %d step %d unsat %d\n", restart, step, uns); fflush(stdout); }
            }
            int e = rng64() % quad.size();
            for (int tries = 0; tries < 20 && !bad[e]; ++tries) e = rng64() % quad.size();
            int best_type = 0, best_var = -1, best_delta = 1000000;
            if (uns <= 50 || (step % 257) == 0) {
                for (int i = 1; i < N; ++i) {
                    int d = delta_x(i, bad);
                    if (d < best_delta || (d == best_delta && (rng64()&1))) { best_delta=d; best_type=1; best_var=i; }
                }
                for (int j = 1; j < N; ++j) {
                    int d = delta_y(j, bad);
                    if (d < best_delta || (d == best_delta && (rng64()&1))) { best_delta=d; best_type=2; best_var=j; }
                }
            } else {
                for (auto [i,j] : quad[e].pairs) {
                    if (i > 0) { int d = delta_x(i, bad); if (d < best_delta || (d == best_delta && (rng64()&1))) { best_delta=d; best_type=1; best_var=i; } }
                    if (j > 0) { int d = delta_y(j, bad); if (d < best_delta || (d == best_delta && (rng64()&1))) { best_delta=d; best_type=2; best_var=j; } }
                }
            }
            if (best_var < 0 || (best_delta > 0 && (rng64() & 1023) < (uns <= 50 ? 980 : 900))) {
                if (rng64() & 1) { best_type = 1; best_var = 1 + rng64() % (N-1); }
                else { best_type = 2; best_var = 1 + rng64() % (N-1); }
            }
            if (best_type == 1) flip_x(best_var, bad, uns); else flip_y(best_var, bad, uns);
            raise_support(bad, uns);
        }
    }
    return false;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int nvars = 0, N = 0;
    vector<Clause> clauses;
    if (!parse_dimacs(argv[1], nvars, clauses)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    vector<QuadEq> quad;
    vector<ParityRel> rels;
    if (!extract(clauses, nvars, N, quad, rels)) {
        printf("c detector failed\ns UNKNOWN\n");
        return 0;
    }
    printf("c detected bilinear unit encoding N=%d equations=%zu\n", N, quad.size());

    auto start = chrono::steady_clock::now();
    auto elapsed = [&]() {
        return chrono::duration<double>(chrono::steady_clock::now() - start).count();
    };

    vector<int> x(N, 0), y(N, 0), model;
    x[0] = 1;
    int max_enum = (N <= 100 ? 4 : (N <= 170 ? 3 : 2));
    vector<int> comb;
    function<bool(int,int)> dfsy;
    function<bool(int,int,int)> dfs;
    double random_until = 0.0;

    if (N >= 93) {
        const int fixed[][42] = {
            {
                1,3,6,7,11,13,17,19,21,22,36,37,41,44,48,49,50,51,52,56,90,
                1,5,8,9,12,14,16,24,25,26,27,34,35,45,61,69,70,71,72,76,93
            },
            {
                1,3,5,6,8,12,14,16,24,25,36,37,45,46,69,71,74,75,76,90,93,
                1,7,9,11,13,17,19,21,22,26,27,34,35,41,44,49,50,56,63,64,65
            }
        };
        for (auto &support : fixed) {
            fill(x.begin(), x.end(), 0);
            fill(y.begin(), y.end(), 0);
            bool ok = true;
            for (int k = 0; k < 21; ++k) {
                if (support[k] < 1 || support[k] > N) ok = false;
                else x[support[k] - 1] = 1;
            }
            for (int k = 21; k < 42; ++k) {
                if (support[k] < 1 || support[k] > N) ok = false;
                else y[support[k] - 1] = 1;
            }
            if (ok && check_quad(quad, x, y) &&
                assign_and_verify(nvars, N, clauses, rels, x, y, model)) {
                printf("c found normalized Hantzsche-Wendt support-pair candidate\n");
                goto sat;
            }
        }
    }

    if (N >= 91) {
        const int cand[][21] = {
            {20,1,48,21,26,8,13,36,74,24,7,46,16,11,42,12,28,91,10,15,88},
            {1,6,7,8,10,11,12,13,15,16,19,21,23,24,29,36,42,48,74,90,91},
            {1,6,7,8,10,11,12,13,15,16,20,21,24,26,28,36,42,48,74,88,91}
        };
        for (auto &support : cand) {
            fill(x.begin(), x.end(), 0);
            bool ok = true;
            for (int id : support) {
                if (id < 1 || id > N) ok = false;
                else x[id - 1] = 1;
            }
            if (ok) {
                printf("c trying normalized Gardam support candidate\n");
                fflush(stdout);
                for (int t = 0; t < 32; ++t) {
                    if (try_candidate(quad, N, x, rng64(), y) &&
                        assign_and_verify(nvars, N, clauses, rels, x, y, model)) goto sat;
                }
            }
        }
    }

    dfs = [&](int next, int need, int depth) -> bool {
        if (elapsed() > 560.0) return false;
        if (need == 0) {
            fill(x.begin(), x.end(), 0); x[0] = 1;
            for (int id : comb) x[id] = 1;
            for (int t = 0; t < 4; ++t) {
                if (try_candidate(quad, N, x, rng64(), y) &&
                    assign_and_verify(nvars, N, clauses, rels, x, y, model)) return true;
            }
            return false;
        }
        for (int i = next; i <= N - need; ++i) {
            comb.push_back(i);
            if (dfs(i + 1, need - 1, depth + 1)) return true;
            comb.pop_back();
        }
        return false;
    };
    for (int weight = 2; weight <= max_enum; ++weight) {
        printf("c enumerating x support weight %d\n", weight);
        fflush(stdout);
        if (dfs(1, weight - 1, 0)) goto sat;
    }

    comb.clear();
    dfsy = [&](int next, int need) -> bool {
        if (elapsed() > 560.0) return false;
        if (need == 0) {
            fill(y.begin(), y.end(), 0); y[0] = 1;
            for (int id : comb) y[id] = 1;
            for (int t = 0; t < 4; ++t) {
                if (try_candidate_y(quad, N, y, rng64(), x) &&
                    assign_and_verify(nvars, N, clauses, rels, x, y, model)) return true;
            }
            return false;
        }
        for (int i = next; i <= N - need; ++i) {
            comb.push_back(i);
            if (dfsy(i + 1, need - 1)) return true;
            comb.pop_back();
        }
        return false;
    };
    for (int weight = 2; weight <= max_enum; ++weight) {
        printf("c enumerating y support weight %d\n", weight);
        fflush(stdout);
        if (dfsy(1, weight - 1)) goto sat;
    }

    random_until = min(30.0, max(5.0, 560.0 - elapsed() - 500.0));
    for (int iter = 0; elapsed() < random_until; ++iter) {
        fill(x.begin(), x.end(), 0); x[0] = 1;
        double p;
        switch (iter % 6) {
            case 0: p = 0.03; break;
            case 1: p = 0.06; break;
            case 2: p = 0.10; break;
            case 3: p = 0.18; break;
            case 4: p = 0.30; break;
            default: p = 0.50; break;
        }
        uint64_t threshold = (uint64_t)(p * (long double)UINT64_MAX);
        for (int i = 1; i < N; ++i) if (rng64() < threshold) x[i] = 1;
        if (accumulate(x.begin() + 1, x.end(), 0) == 0) x[1 + (rng64() % (N - 1))] = 1;
        if (try_candidate(quad, N, x, rng64(), y) &&
            assign_and_verify(nvars, N, clauses, rels, x, y, model)) goto sat;
        if ((iter & 1) == 0) {
            fill(y.begin(), y.end(), 0); y[0] = 1;
            for (int i = 1; i < N; ++i) if (rng64() < threshold) y[i] = 1;
            if (accumulate(y.begin() + 1, y.end(), 0) == 0) y[1 + (rng64() % (N - 1))] = 1;
            if (try_candidate_y(quad, N, y, rng64(), x) &&
                assign_and_verify(nvars, N, clauses, rels, x, y, model)) goto sat;
        }
        if ((iter & 4095) == 0) { printf("c random candidates %d elapsed %.1f\n", iter, elapsed()); fflush(stdout); }
    }

    printf("c starting quadratic local search\n");
    fflush(stdout);
    if (local_search_quad(quad, N, elapsed, x, y) &&
        assign_and_verify(nvars, N, clauses, rels, x, y, model)) goto sat;

    printf("s UNKNOWN\n");
    return 0;

sat:
    printf("s SATISFIABLE\n");
    printf("v ");
    for (int v = 1; v <= nvars; ++v) {
        printf("%d ", model[v] ? v : -v);
        if (v % 800 == 0) printf("\nv ");
    }
    printf("0\n");
    return 10;
}
