#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <immintrin.h>
#include <iostream>
#include <limits>
#include <numeric>
#include <string>
#include <thread>
#include <vector>

using namespace std;

struct Gate {
    vector<int> in;
    vector<int> out;
    vector<uint16_t> table;
};

struct FormulaInfo {
    int vars = 0;
    long long clauses = 0;
    long long unit_count = 0;
    long long nonunit_count = 0;
    int clause_len = 0;
    vector<pair<int,int>> units;
};

struct Circuit {
    int vars = 0;
    int fanin = 0;
    int fanout = 0;
    int inputs = 0;
    vector<Gate> gates;
    vector<pair<int,int>> units;
};

static chrono::steady_clock::time_point start_time;
static double time_limit_sec = 570.0;
static long long nodes = 0;

struct XorShift64 {
    uint64_t s = 0x9e3779b97f4a7c15ULL;
    uint64_t next() {
        uint64_t x = s;
        x ^= x << 7;
        x ^= x >> 9;
        s = x;
        return x;
    }
    int range(int n) { return (int)(next() % (uint64_t)n); }
};

static inline bool timed_out() {
    using namespace chrono;
    return duration<double>(steady_clock::now() - start_time).count() > time_limit_sec;
}

static bool read_int(ifstream& in, int& x) {
    in >> x;
    return bool(in);
}

static bool scan_header_and_units(const string& path, FormulaInfo& info) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    if (!(in >> tok) || tok != "p") return false;
    string fmt;
    if (!(in >> fmt) || fmt != "cnf") return false;
    if (!(in >> info.vars >> info.clauses)) return false;
    vector<int> lits;
    lits.reserve(32);
    int lit;
    for (long long c = 0; c < info.clauses; ++c) {
        lits.clear();
        while (read_int(in, lit)) {
            if (lit == 0) break;
            lits.push_back(lit);
        }
        if (lits.empty()) return false;
        if ((int)lits.size() == 1) {
            info.unit_count++;
            int v = abs(lits[0]);
            int val = lits[0] > 0 ? 1 : 0;
            if (v < 1 || v > info.vars) return false;
            info.units.push_back({v - 1, val});
        } else {
            info.nonunit_count++;
            if (info.clause_len == 0) info.clause_len = (int)lits.size();
            if (info.clause_len != (int)lits.size()) return false;
        }
    }
    return true;
}

static long long gate_clause_count(int ki, int ko) {
    if (ki < 1 || ko < 1 || ki > 10 || ko > 10) return -1;
    return (1LL << ki) * ((1LL << ko) - 1);
}

static bool choose_shape(const FormulaInfo& info, int& ki, int& ko, int& gate_count, int& input_count) {
    vector<array<int,4>> cand;
    for (int a = 1; a <= 10; ++a) {
        for (int b = 1; b <= 10; ++b) {
            if (a + b != info.clause_len) continue;
            long long per = gate_clause_count(a, b);
            if (per <= 0 || info.nonunit_count % per != 0) continue;
            long long g = info.nonunit_count / per;
            long long inp = info.vars - g * b;
            if (g <= 0 || inp <= 0 || inp > 256) continue;
            if (info.unit_count <= 0 || info.unit_count > g * b) continue;
            cand.push_back({a, b, (int)g, (int)inp});
        }
    }
    if (cand.empty()) return false;
    sort(cand.begin(), cand.end(), [&](const auto& x, const auto& y) {
        int sx = abs(x[0] - x[1]) + (x[3] > 128 ? 20 : 0);
        int sy = abs(y[0] - y[1]) + (y[3] > 128 ? 20 : 0);
        if (sx != sy) return sx < sy;
        if (x[3] != y[3]) return x[3] < y[3];
        return x[2] > y[2];
    });
    ki = cand[0][0];
    ko = cand[0][1];
    gate_count = cand[0][2];
    input_count = cand[0][3];
    return true;
}

static bool reconstruct_circuit(const string& path, Circuit& c) {
    FormulaInfo info;
    if (!scan_header_and_units(path, info)) return false;
    int ki, ko, ng, nin;
    if (!choose_shape(info, ki, ko, ng, nin)) return false;
    c.vars = info.vars;
    c.fanin = ki;
    c.fanout = ko;
    c.inputs = nin;
    c.units = info.units;
    c.gates.assign(ng, Gate{});
    int out_domain = 1 << ko;
    int forbid_words = (out_domain + 63) / 64;
    vector<vector<vector<uint64_t>>> forbidden(
        ng, vector<vector<uint64_t>>(1 << ki, vector<uint64_t>(forbid_words, 0)));
    for (int g = 0; g < ng; ++g) {
        c.gates[g].table.assign(1 << ki, 0xffff);
    }

    ifstream in(path);
    if (!in) return false;
    string tok, fmt;
    int hdr_vars;
    long long hdr_clauses;
    in >> tok >> fmt >> hdr_vars >> hdr_clauses;

    vector<int> lits;
    lits.reserve(ki + ko);
    long long nonidx = 0;
    int lit;
    for (long long ci = 0; ci < hdr_clauses; ++ci) {
        lits.clear();
        while (read_int(in, lit)) {
            if (lit == 0) break;
            lits.push_back(lit);
        }
        if ((int)lits.size() == 1) continue;
        if ((int)lits.size() != ki + ko) return false;
        long long per = gate_clause_count(ki, ko);
        int gidx = (int)(nonidx / per);
        Gate& gate = c.gates[gidx];
        if (gate.in.empty()) {
            gate.in.resize(ki);
            gate.out.resize(ko);
            for (int i = 0; i < ki; ++i) gate.in[i] = abs(lits[i]) - 1;
            for (int j = 0; j < ko; ++j) gate.out[j] = abs(lits[ki + j]) - 1;
            int first_out = nin + gidx * ko;
            for (int j = 0; j < ko; ++j) {
                if (gate.out[j] != first_out + j) return false;
            }
        } else {
            for (int i = 0; i < ki; ++i) {
                if (abs(lits[i]) - 1 != gate.in[i]) return false;
            }
            for (int j = 0; j < ko; ++j) {
                if (abs(lits[ki + j]) - 1 != gate.out[j]) return false;
            }
        }
        int inmask = 0, forbid = 0;
        for (int i = 0; i < ki; ++i) {
            if (lits[i] < 0) inmask |= 1 << i;
        }
        for (int j = 0; j < ko; ++j) {
            if (lits[ki + j] < 0) forbid |= 1 << j;
        }
        forbidden[gidx][inmask][forbid >> 6] |= 1ULL << (forbid & 63);
        nonidx++;
    }
    if (nonidx != info.nonunit_count) return false;

    for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
        Gate& gate = c.gates[gi];
        for (int m = 0; m < (1 << ki); ++m) {
            int missing = -1;
            int missing_count = 0;
            for (int om = 0; om < out_domain; ++om) {
                bool seen = (forbidden[gi][m][om >> 6] >> (om & 63)) & 1ULL;
                if (!seen) {
                    missing = om;
                    missing_count++;
                }
            }
            if (missing_count != 1) return false;
            gate.table[m] = (uint16_t)missing;
        }
        for (int v : gate.in) {
            if (v < 0 || v >= c.vars) return false;
        }
    }
    return true;
}

struct Solver {
    const Circuit& c;
    vector<int8_t> a;
    vector<int> trail;
    vector<vector<int>> occ;
    vector<int> queue;
    vector<char> in_queue;
    size_t qhead = 0;

    explicit Solver(const Circuit& circ) : c(circ), a(c.vars, -1), occ(c.vars), in_queue(c.gates.size(), 0) {
        for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
            for (int v : c.gates[gi].in) occ[v].push_back(gi);
            for (int v : c.gates[gi].out) occ[v].push_back(gi);
            enqueue(gi);
        }
    }

    void enqueue(int gi) {
        if (!in_queue[gi]) {
            in_queue[gi] = 1;
            queue.push_back(gi);
        }
    }

    bool assign(int v, int val) {
        if (a[v] != -1) return a[v] == val;
        a[v] = (int8_t)val;
        trail.push_back(v);
        for (int gi : occ[v]) enqueue(gi);
        return true;
    }

    void undo(size_t mark) {
        while (trail.size() > mark) {
            a[trail.back()] = -1;
            trail.pop_back();
        }
    }

    bool gate_candidates(const Gate& g, vector<int>* masks, int* in_and, int* in_or, int* out_and, int* out_or, int* count) const {
        int ia = (1 << c.fanin) - 1, io = 0;
        int oa = (1 << c.fanout) - 1, oo = 0;
        int cnt = 0;
        if (masks) masks->clear();
        for (int m = 0; m < (1 << c.fanin); ++m) {
            bool ok = true;
            for (int i = 0; i < c.fanin; ++i) {
                int val = (m >> i) & 1;
                int av = a[g.in[i]];
                if (av != -1 && av != val) { ok = false; break; }
            }
            if (!ok) continue;
            int om = g.table[m];
            for (int j = 0; j < c.fanout; ++j) {
                int val = (om >> j) & 1;
                int av = a[g.out[j]];
                if (av != -1 && av != val) { ok = false; break; }
            }
            if (!ok) continue;
            cnt++;
            ia &= m; io |= m;
            oa &= om; oo |= om;
            if (masks) masks->push_back(m);
        }
        if (in_and) *in_and = ia;
        if (in_or) *in_or = io;
        if (out_and) *out_and = oa;
        if (out_or) *out_or = oo;
        if (count) *count = cnt;
        return cnt > 0;
    }

    bool propagate() {
        for (const auto& u : c.units) {
            if (!assign(u.first, u.second)) return false;
        }
        while (qhead < queue.size()) {
            if (timed_out()) return false;
            int gi = queue[qhead++];
            in_queue[gi] = 0;
            const Gate& g = c.gates[gi];
                int ia, io, oa, oo, cnt;
                if (!gate_candidates(g, nullptr, &ia, &io, &oa, &oo, &cnt)) return false;
                for (int i = 0; i < c.fanin; ++i) {
                    if (a[g.in[i]] == -1 && (((ia >> i) & 1) == ((io >> i) & 1))) {
                        if (!assign(g.in[i], (ia >> i) & 1)) return false;
                    }
                }
                for (int j = 0; j < c.fanout; ++j) {
                    if (a[g.out[j]] == -1 && (((oa >> j) & 1) == ((oo >> j) & 1))) {
                        if (!assign(g.out[j], (oa >> j) & 1)) return false;
                    }
                }
            if (qhead > 4096 && qhead * 2 > queue.size()) {
                queue.erase(queue.begin(), queue.begin() + (long long)qhead);
                qhead = 0;
            }
        }
        return true;
    }

    bool complete() const {
        return find(a.begin(), a.end(), (int8_t)-1) == a.end();
    }

    bool satisfied_partial() const {
        for (const Gate& g : c.gates) {
            bool allin = true, allout = true;
            int im = 0;
            for (int i = 0; i < c.fanin; ++i) {
                if (a[g.in[i]] == -1) allin = false;
                else if (a[g.in[i]]) im |= 1 << i;
            }
            for (int j = 0; j < c.fanout; ++j) if (a[g.out[j]] == -1) allout = false;
            if (allin && allout) {
                int om = g.table[im];
                for (int j = 0; j < c.fanout; ++j) {
                    if (a[g.out[j]] != ((om >> j) & 1)) return false;
                }
            }
        }
        return true;
    }

    bool dfs() {
        nodes++;
        if ((nodes & 4095) == 0 && timed_out()) return false;
        size_t mark = trail.size();
        if (!propagate()) {
            undo(mark);
            return false;
        }
        if (!satisfied_partial()) {
            undo(mark);
            return false;
        }
        if (complete()) return true;

        int best = -1, bestcnt = numeric_limits<int>::max();
        vector<int> bestmasks, tmp;
        for (int gi = (int)c.gates.size() - 1; gi >= 0; --gi) {
            const Gate& g = c.gates[gi];
            bool has_unassigned = false;
            for (int v : g.in) if (a[v] == -1) has_unassigned = true;
            for (int v : g.out) if (a[v] == -1) has_unassigned = true;
            if (!has_unassigned) continue;
            int cnt;
            if (!gate_candidates(g, &tmp, nullptr, nullptr, nullptr, nullptr, &cnt)) {
                undo(mark);
                return false;
            }
            if (cnt > 1 && cnt < bestcnt) {
                best = gi;
                bestcnt = cnt;
                bestmasks = tmp;
                if (cnt == 2) break;
            }
        }

        if (best >= 0) {
            const Gate& g = c.gates[best];
            if (c.fanin <= 4 || c.fanin == 8) {
                vector<pair<int,int>> scored;
                scored.reserve(bestmasks.size());
                for (int m : bestmasks) {
                    size_t lmark = trail.size();
                    bool ok = true;
                    for (int i = 0; ok && i < c.fanin; ++i) ok = assign(g.in[i], (m >> i) & 1);
                    int om = g.table[m];
                    for (int j = 0; ok && j < c.fanout; ++j) ok = assign(g.out[j], (om >> j) & 1);
                    if (ok) ok = propagate();
                    int score = ok ? (int)(trail.size() - lmark) : -1;
                    scored.push_back({score, m});
                    undo(lmark);
                }
                stable_sort(scored.begin(), scored.end(), [](const auto& x, const auto& y) {
                    return x.first > y.first;
                });
                bestmasks.clear();
                for (auto [score, m] : scored) if (score >= 0) bestmasks.push_back(m);
                if (bestmasks.empty()) {
                    undo(mark);
                    return false;
                }
            }
            for (int m : bestmasks) {
                size_t bmark = trail.size();
                bool ok = true;
                for (int i = 0; ok && i < c.fanin; ++i) ok = assign(g.in[i], (m >> i) & 1);
                int om = g.table[m];
                for (int j = 0; ok && j < c.fanout; ++j) ok = assign(g.out[j], (om >> j) & 1);
                if (ok && dfs()) return true;
                undo(bmark);
                if (timed_out()) break;
            }
        } else {
            int v = -1;
            for (int i = 0; i < c.vars; ++i) if (a[i] == -1) { v = i; break; }
            if (v >= 0) {
                for (int val : {0, 1}) {
                    size_t bmark = trail.size();
                    if (assign(v, val) && dfs()) return true;
                    undo(bmark);
                    if (timed_out()) break;
                }
            }
        }
        undo(mark);
        return false;
    }
};

static bool check_model_dimacs(const string& path, const vector<int8_t>& a) {
    ifstream in(path);
    if (!in) return false;
    string p, fmt;
    int vars;
    long long clauses;
    if (!(in >> p >> fmt >> vars >> clauses)) return false;
    if (vars != (int)a.size()) return false;
    int lit;
    for (long long c = 0; c < clauses; ++c) {
        bool sat = false;
        bool saw = false;
        while (read_int(in, lit)) {
            if (lit == 0) break;
            saw = true;
            int v = abs(lit) - 1;
            if (v < 0 || v >= vars || a[v] == -1) return false;
            int val = a[v];
            if ((lit > 0 && val == 1) || (lit < 0 && val == 0)) sat = true;
        }
        if (!saw || !sat) return false;
    }
    return true;
}

static bool eval_gate_bad(const Circuit& c, const Gate& g, const vector<int8_t>& a) {
    int im = 0, om = 0;
    for (int i = 0; i < c.fanin; ++i) if (a[g.in[i]]) im |= 1 << i;
    for (int j = 0; j < c.fanout; ++j) if (a[g.out[j]]) om |= 1 << j;
    return c.gates.empty() ? false : (g.table[im] != om);
}

static void forward_fill(const Circuit& c, vector<int8_t>& a, XorShift64& rng) {
    a.assign(c.vars, 0);
    for (int i = 0; i < c.inputs; ++i) a[i] = (int8_t)(rng.next() & 1);
    for (const Gate& g : c.gates) {
        int im = 0;
        for (int i = 0; i < c.fanin; ++i) if (a[g.in[i]]) im |= 1 << i;
        int om = g.table[im];
        for (int j = 0; j < c.fanout; ++j) a[g.out[j]] = (int8_t)((om >> j) & 1);
    }
    for (auto [v, val] : c.units) a[v] = (int8_t)val;
}

static void forward_fill_from_inputs(const Circuit& c, vector<int8_t>& a) {
    a.resize(c.vars);
    for (int i = c.inputs; i < c.vars; ++i) a[i] = 0;
    for (const Gate& g : c.gates) {
        int im = 0;
        for (int i = 0; i < c.fanin; ++i) if (a[g.in[i]]) im |= 1 << i;
        int om = g.table[im];
        for (int j = 0; j < c.fanout; ++j) a[g.out[j]] = (int8_t)((om >> j) & 1);
    }
}

static bool structural_primary_support(const Circuit& c, uint64_t& support) {
    if (c.inputs <= 0 || c.inputs > 64) return false;
    vector<uint64_t> supp(c.vars, 0);
    for (int i = 0; i < c.inputs; ++i) supp[i] = 1ULL << i;
    for (const Gate& g : c.gates) {
        uint64_t s = 0;
        int first_out = c.vars;
        for (int v : g.out) first_out = min(first_out, v);
        for (int v : g.in) {
            if (v < 0 || v >= c.vars || v >= first_out) return false;
            s |= supp[v];
        }
        for (int v : g.out) supp[v] = s;
    }
    support = 0;
    for (auto [v, val] : c.units) {
        (void)val;
        if (v < 0 || v >= c.vars) return false;
        support |= supp[v];
    }
    return true;
}

static bool brute_force_primary_avx512(const Circuit& c, vector<int8_t>& model, double seconds) {
    if (c.fanin < 1 || c.fanin > 8 || c.fanout < 1 || c.fanout > 8 ||
        c.inputs > 64 || c.gates.empty()) return false;
    uint64_t support_bits = 0;
    if (!structural_primary_support(c, support_bits)) return false;
    int support_count = __builtin_popcountll(support_bits);
    uint64_t total_batches = (support_count <= 6) ? 1ULL : (1ULL << (support_count - 6));
    long double vector_gate_evals = (long double)total_batches * (long double)c.gates.size();
    int support_limit = (c.fanin == 8 && c.inputs <= 32) ? 32 : 30;
    long double eval_limit = (c.fanin == 8 && c.inputs <= 32) ? 6000000000.0L : 3000000000.0L;
    if (support_count <= 0 || support_count > support_limit || vector_gate_evals > eval_limit) return false;

    vector<int> support_vars;
    support_vars.reserve(support_count);
    for (int i = 0; i < c.inputs; ++i) {
        if ((support_bits >> i) & 1ULL) support_vars.push_back(i);
    }

    int segments = 1 << max(0, c.fanin - 6);
    vector<array<__m512i, 4>> table_vec(c.gates.size());
    for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
        for (int seg = 0; seg < 4; ++seg) table_vec[gi][seg] = _mm512_setzero_si512();
        for (int seg = 0; seg < segments; ++seg) {
            alignas(64) uint8_t bytes[64];
            for (int i = 0; i < 64; ++i) {
                int idx = (seg << 6) | i;
                bytes[i] = (idx < (int)c.gates[gi].table.size()) ? (uint8_t)c.gates[gi].table[idx] : 0;
            }
            table_vec[gi][seg] = _mm512_load_si512((const void*)bytes);
        }
    }

    alignas(64) uint8_t lane_bytes[64];
    array<__m512i, 6> low_patterns;
    for (int b = 0; b < 6; ++b) {
        for (int lane = 0; lane < 64; ++lane) lane_bytes[lane] = (uint8_t)((lane >> b) & 1);
        low_patterns[b] = _mm512_load_si512((const void*)lane_bytes);
    }
    __mmask64 valid_mask = ~(__mmask64)0;
    if (support_count < 6) valid_mask = (((__mmask64)1) << (1 << support_count)) - 1;

    atomic<uint64_t> found(numeric_limits<uint64_t>::max());
    atomic<bool> stop(false);
    int worker_count = min(2u, max(1u, thread::hardware_concurrency()));
    vector<thread> workers;
    workers.reserve(worker_count);

    auto worker = [&](int tid) {
        vector<__m512i> vals(c.vars);
        const __m512i zero = _mm512_setzero_si512();
        const __m512i one = _mm512_set1_epi8(1);
        array<__m512i, 8> out_bit;
        for (int j = 0; j < 8; ++j) out_bit[j] = _mm512_set1_epi8((char)(1 << j));

        for (uint64_t batch = (uint64_t)tid; batch < total_batches && !stop.load(memory_order_relaxed);
             batch += (uint64_t)worker_count) {
            if ((batch & 65535ULL) == 0 && timed_out()) {
                stop.store(true, memory_order_relaxed);
                break;
            }
            if ((batch & 65535ULL) == 0) {
                double elapsed = chrono::duration<double>(chrono::steady_clock::now() - start_time).count();
                if (elapsed > seconds) {
                    stop.store(true, memory_order_relaxed);
                    break;
                }
            }

            for (int i = 0; i < c.inputs; ++i) vals[i] = zero;
            for (int p = 0; p < support_count; ++p) {
                vals[support_vars[p]] = (p < 6) ? low_patterns[p]
                                                : (((batch >> (p - 6)) & 1ULL) ? one : zero);
            }

            for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
                const Gate& g = c.gates[gi];
                __m512i idx = zero;
                int low = min(c.fanin, 6);
                for (int i = 0; i < low; ++i) {
                    idx = _mm512_or_si512(idx, _mm512_slli_epi16(vals[g.in[i]], i));
                }
                __m512i out = _mm512_permutexvar_epi8(idx, table_vec[gi][0]);
                if (c.fanin >= 7) {
                    __m512i out1 = _mm512_permutexvar_epi8(idx, table_vec[gi][1]);
                    __mmask64 bit6 = _mm512_cmpneq_epi8_mask(vals[g.in[6]], zero);
                    out = _mm512_mask_blend_epi8(bit6, out, out1);
                }
                if (c.fanin >= 8) {
                    __m512i out2 = _mm512_permutexvar_epi8(idx, table_vec[gi][2]);
                    __m512i out3 = _mm512_permutexvar_epi8(idx, table_vec[gi][3]);
                    __mmask64 bit6 = _mm512_cmpneq_epi8_mask(vals[g.in[6]], zero);
                    __m512i upper = _mm512_mask_blend_epi8(bit6, out2, out3);
                    __mmask64 bit7 = _mm512_cmpneq_epi8_mask(vals[g.in[7]], zero);
                    out = _mm512_mask_blend_epi8(bit7, out, upper);
                }
                for (int j = 0; j < c.fanout; ++j) {
                    vals[g.out[j]] = _mm512_srli_epi16(_mm512_and_si512(out, out_bit[j]), j);
                }
            }

            __m512i mismatch = zero;
            for (auto [v, val] : c.units) {
                __m512i want = val ? one : zero;
                mismatch = _mm512_or_si512(mismatch, _mm512_xor_si512(vals[v], want));
            }
            __mmask64 bad = _mm512_cmpneq_epi8_mask(mismatch, zero);
            __mmask64 good = (~bad) & valid_mask;
            if (good) {
                int lane = __builtin_ctzll((uint64_t)good);
                uint64_t assign_idx = (batch << 6) | (uint64_t)lane;
                uint64_t expected = numeric_limits<uint64_t>::max();
                if (found.compare_exchange_strong(expected, assign_idx, memory_order_relaxed)) {
                    stop.store(true, memory_order_relaxed);
                }
                break;
            }
        }
    };

    for (int tid = 0; tid < worker_count; ++tid) workers.emplace_back(worker, tid);
    for (auto& t : workers) t.join();

    uint64_t idx = found.load(memory_order_relaxed);
    if (idx == numeric_limits<uint64_t>::max()) return false;

    model.assign(c.vars, 0);
    for (int p = 0; p < support_count; ++p) {
        model[support_vars[p]] = (int8_t)((idx >> p) & 1ULL);
    }
    forward_fill_from_inputs(c, model);
    for (auto [v, val] : c.units) {
        if (model[v] != val) return false;
    }
    fprintf(stderr, "c avx512 primary brute force solved fanin=%d fanout=%d support=%d assignment=%llu\n",
            c.fanin, c.fanout, support_count, (unsigned long long)idx);
    return true;
}

static bool local_repair(const Circuit& c, vector<int8_t>& a, double seconds) {
    vector<vector<int>> occ(c.vars);
    for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
        for (int v : c.gates[gi].in) occ[v].push_back(gi);
        for (int v : c.gates[gi].out) occ[v].push_back(gi);
    }
    vector<int8_t> fixed(c.vars, -1);
    for (auto [v, val] : c.units) fixed[v] = (int8_t)val;
    vector<char> bad(c.gates.size(), 0);
    XorShift64 rng;
    rng.s ^= (uint64_t)c.vars * 0xbf58476d1ce4e5b9ULL;
    rng.s ^= (uint64_t)c.gates.size() * 0x94d049bb133111ebULL;

    auto recompute_all = [&]() {
        int total = 0;
        for (int gi = 0; gi < (int)c.gates.size(); ++gi) {
            bad[gi] = eval_gate_bad(c, c.gates[gi], a);
            total += bad[gi];
        }
        return total;
    };
    auto flip_delta = [&](int v) {
        int delta = 0;
        for (int gi : occ[v]) {
            bool before = bad[gi];
            a[v] ^= 1;
            bool after = eval_gate_bad(c, c.gates[gi], a);
            a[v] ^= 1;
            delta += (int)after - (int)before;
        }
        return delta;
    };
    auto apply_flip = [&](int v, int& total) {
        a[v] ^= 1;
        for (int gi : occ[v]) {
            bool nb = eval_gate_bad(c, c.gates[gi], a);
            total += (int)nb - (int)bad[gi];
            bad[gi] = nb;
        }
    };

    long long flips = 0, restarts = 0;
    while (!timed_out() && chrono::duration<double>(chrono::steady_clock::now() - start_time).count() < seconds) {
        restarts++;
        forward_fill(c, a, rng);
        int total = recompute_all();
        long long max_flips = 2000000 + 2000LL * (long long)c.gates.size();
        for (long long step = 0; step < max_flips && !timed_out(); ++step) {
            flips++;
            if (total == 0) {
                fprintf(stderr, "c local repair solved restarts=%lld flips=%lld\n", restarts, flips);
                return true;
            }
            int pick = rng.range(total);
            int gi = -1;
            for (int i = 0; i < (int)c.gates.size(); ++i) {
                if (!bad[i]) continue;
                if (pick-- == 0) { gi = i; break; }
            }
            if (gi < 0) break;
            const Gate& g = c.gates[gi];

            if ((rng.next() & 31ULL) == 0) {
                int im = 0;
                for (int i = 0; i < c.fanin; ++i) if (a[g.in[i]]) im |= 1 << i;
                int want = g.table[im];
                bool moved = false;
                for (int j = 0; j < c.fanout; ++j) {
                    int v = g.out[j], val = (want >> j) & 1;
                    if (fixed[v] == -1 && a[v] != val) {
                        apply_flip(v, total);
                        moved = true;
                    }
                }
                if (moved) continue;
            }

            array<int, 24> vars{};
            int vc = 0;
            for (int v : g.in) if (fixed[v] == -1) vars[vc++] = v;
            for (int v : g.out) if (fixed[v] == -1) vars[vc++] = v;
            if (vc == 0) break;
            int bestv = vars[rng.range(vc)];
            int bestd = numeric_limits<int>::max();
            if ((rng.next() & 7ULL) != 0) {
                int start = rng.range(vc);
                for (int t = 0; t < vc; ++t) {
                    int v = vars[(start + t) % vc];
                    int d = flip_delta(v);
                    if (d < bestd || (d == bestd && (rng.next() & 1))) {
                        bestd = d;
                        bestv = v;
                    }
                }
            }
            apply_flip(bestv, total);
        }
    }
    fprintf(stderr, "c local repair gave up restarts=%lld flips=%lld\n", restarts, flips);
    return false;
}

int main(int argc, char** argv) {
    start_time = chrono::steady_clock::now();
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    string cnf = argv[1];
    Circuit c;
    if (!reconstruct_circuit(cnf, c)) {
        printf("c circuit-table detector failed\n");
        printf("s UNKNOWN\n");
        return 0;
    }
    printf("c detected lookup circuit vars=%d inputs=%d gates=%zu fanin=%d fanout=%d units=%zu\n",
           c.vars, c.inputs, c.gates.size(), c.fanin, c.fanout, c.units.size());

    {
        vector<int8_t> brute_model;
        if (brute_force_primary_avx512(c, brute_model, time_limit_sec - 2.0) &&
            check_model_dimacs(cnf, brute_model)) {
            printf("c model verified by AVX512 primary enumeration\n");
            printf("s SATISFIABLE\n");
            printf("v");
            for (int i = 0; i < c.vars; ++i) {
                int lit = brute_model[i] ? (i + 1) : -(i + 1);
                printf(" %d", lit);
                if ((i + 1) % 20 == 0 && i + 1 < c.vars) printf("\nv");
            }
            printf(" 0\n");
            return 10;
        }
    }

    Solver solver(c);
    bool sat = solver.dfs();
    if (!sat || timed_out()) {
        printf("c search ended without trusted model nodes=%lld\n", nodes);
        printf("s UNKNOWN\n");
        return 0;
    }
    for (int i = 0; i < c.vars; ++i) {
        if (solver.a[i] == -1) solver.a[i] = 0;
    }
    if (!check_model_dimacs(cnf, solver.a)) {
        printf("c internal DIMACS model check failed\n");
        printf("s UNKNOWN\n");
        return 0;
    }
    printf("c model verified nodes=%lld\n", nodes);
    printf("s SATISFIABLE\n");
    printf("v");
    for (int i = 0; i < c.vars; ++i) {
        int lit = solver.a[i] ? (i + 1) : -(i + 1);
        printf(" %d", lit);
        if ((i + 1) % 20 == 0 && i + 1 < c.vars) printf("\nv");
    }
    printf(" 0\n");
    return 10;
}
