#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <thread>
#include <unordered_set>
#include <vector>

struct RNG {
    uint64_t s;
    explicit RNG(uint64_t seed) : s(seed ? seed : 1) {}
    uint64_t next() {
        uint64_t x = s;
        x ^= x << 7;
        x ^= x >> 9;
        return s = x;
    }
    int uniform(int n) { return (int)(next() % (uint64_t)n); }
};

static inline int lit_var(int lit) { return lit < 0 ? -lit : lit; }
static inline int lit_sign(int lit) { return lit > 0 ? 1 : -1; }

enum GateType : unsigned char { NONE = 0, ORG = 1, NORG = 2, LUT = 3 };

static constexpr bool kVerboseSearchLog = false;
static constexpr bool kDiagnosticExactTiming = false;
static constexpr int kDescendPrimaryFanoutLimit = 20000;
static constexpr int kRandomPrimaryFanoutLimit = 6000;

struct Gate {
    GateType type = NONE;
    std::vector<int> ins;       // signed literals for OR/NOR, positive vars for LUT
    unsigned char table = 0;    // LUT output bit by input mask
};

struct Clause {
    int start = 0, size = 0;
};

struct Formula {
    int nvars = 0, nclauses = 0;
    std::vector<Gate> gates;
    std::vector<int> gate_vars;
    std::vector<int> units;
    std::vector<std::vector<int>> extra_constraints;
    std::vector<Clause> clauses;
    std::vector<int> all_lits;
    std::vector<int> topo;
    std::vector<signed char> bcp_val;
    std::vector<int> fan_start;
    std::vector<int> fan_adj;

    static bool sat_clause_under(const std::vector<int>& c, int v, const std::vector<int>& invars, int mask, bool out) {
        for (int lit : c) {
            int av = lit_var(lit);
            bool val;
            if (av == v) {
                val = out;
            } else {
                auto it = std::find(invars.begin(), invars.end(), av);
                if (it == invars.end()) return false;
                int idx = (int)(it - invars.begin());
                val = (mask >> idx) & 1;
            }
            if ((lit > 0 && val) || (lit < 0 && !val)) return true;
        }
        return false;
    }

    bool classify_group(int v, const std::vector<std::vector<int>>& cs, Gate& g) {
        if (cs.empty()) return false;
        int long_idx = -1;
        std::vector<int> bin_idx;
        for (int i = 0; i < (int)cs.size(); ++i) {
            if ((int)cs[i].size() > 2) long_idx = (long_idx == -1 ? i : -2);
            else if ((int)cs[i].size() == 2) bin_idx.push_back(i);
        }
        if (long_idx >= 0) {
            const auto& lg = cs[long_idx];
            int out_lit = -lg[0];
            if (lit_var(out_lit) == v) {
                std::unordered_set<int> need;
                need.reserve(lg.size() * 2);
                for (int i = 1; i < (int)lg.size(); ++i) need.insert(-lg[i]);
                std::unordered_set<int> have;
                have.reserve(bin_idx.size() * 2);
                bool ok = true;
                for (int bi : bin_idx) {
                    const auto& b = cs[bi];
                    if (b[0] != out_lit || !need.count(b[1])) {
                        ok = false;
                        break;
                    }
                    have.insert(b[1]);
                }
                for (int x : need) {
                    if (!have.count(x)) {
                        ok = false;
                        break;
                    }
                }
                if (ok) {
                    g.type = out_lit > 0 ? ORG : NORG;
                    g.ins.assign(lg.begin() + 1, lg.end());
                    return true;
                }
            }
        }

        std::vector<int> invars;
        for (const auto& c : cs) {
            for (int lit : c) {
                int u = lit_var(lit);
                if (u != v && std::find(invars.begin(), invars.end(), u) == invars.end()) {
                    invars.push_back(u);
                }
            }
        }
        std::sort(invars.begin(), invars.end());
        if (invars.size() <= 2) {
            unsigned char table = 0;
            for (int mask = 0; mask < (1 << invars.size()); ++mask) {
                int ok_count = 0;
                bool ok_val = false;
                for (int ov = 0; ov <= 1; ++ov) {
                    bool ok = true;
                    for (const auto& c : cs) {
                        if (!sat_clause_under(c, v, invars, mask, ov)) {
                            ok = false;
                            break;
                        }
                    }
                    if (ok) {
                        ++ok_count;
                        ok_val = ov;
                    }
                }
                if (ok_count != 1) return false;
                if (ok_val) table |= (1u << mask);
            }
            g.type = LUT;
            g.ins = invars;
            g.table = table;
            return true;
        }
        return false;
    }

    void flush_group(int v, const std::vector<std::vector<int>>& group) {
        std::vector<std::vector<int>> defs;
        for (const auto& c : group) {
            if (c.size() == 1) {
                units.push_back(c[0]);
            } else {
                defs.push_back(c);
            }
        }
        if (defs.empty()) return;
        Gate g;
        if (classify_group(v, defs, g)) {
            if (gates[v].type == NONE) gate_vars.push_back(v);
            gates[v] = std::move(g);
        } else {
            for (const auto& c : defs) extra_constraints.push_back(c);
        }
    }

    bool read_dimacs(const char* path) {
        FILE* f = std::fopen(path, "r");
        if (!f) {
            std::perror(path);
            return false;
        }
        char ch;
        while ((ch = std::getc(f)) != EOF) {
            if (ch == 'c') {
                while ((ch = std::getc(f)) != EOF && ch != '\n') {}
            } else if (ch == 'p') {
                char fmt[16];
                if (std::fscanf(f, "%15s %d %d", fmt, &nvars, &nclauses) != 3) return false;
                break;
            }
        }
        gates.assign(nvars + 1, {});
        clauses.reserve(nclauses);
        all_lits.reserve((size_t)nclauses * 3);
        std::vector<int> cur_clause;
        std::vector<std::vector<int>> cur_group;
        int cur_v = 0;
        int x;
        while (std::fscanf(f, "%d", &x) == 1) {
            if (x == 0) {
                if (cur_clause.empty()) return false;
                Clause c{(int)all_lits.size(), (int)cur_clause.size()};
                for (int lit : cur_clause) all_lits.push_back(lit);
                clauses.push_back(c);
                int v = lit_var(cur_clause[0]);
                if (cur_group.empty()) {
                    cur_v = v;
                } else if (v != cur_v) {
                    flush_group(cur_v, cur_group);
                    cur_group.clear();
                    cur_v = v;
                }
                cur_group.push_back(cur_clause);
                cur_clause.clear();
            } else {
                cur_clause.push_back(x);
            }
        }
        if (!cur_group.empty()) flush_group(cur_v, cur_group);
        std::fclose(f);
        return true;
    }

    bool build_topo() {
        std::vector<int> indeg(nvars + 1, 0);
        std::vector<int> edge_count(nvars + 1, 0);
        long long edges = 0;
        for (int v : gate_vars) {
            std::vector<int> seen;
            for (int lit : gates[v].ins) {
                int u = lit_var(lit);
                if (std::find(seen.begin(), seen.end(), u) != seen.end()) continue;
                seen.push_back(u);
                ++edge_count[u];
                ++edges;
                if (u <= nvars && gates[u].type != NONE) {
                    ++indeg[v];
                }
            }
        }
        std::vector<int> start(nvars + 2, 0);
        for (int v = 1; v <= nvars; ++v) start[v + 1] = start[v] + edge_count[v];
        std::vector<int> adj(edges);
        std::vector<int> at = start;
        for (int v : gate_vars) {
            std::vector<int> seen;
            for (int lit : gates[v].ins) {
                int u = lit_var(lit);
                if (std::find(seen.begin(), seen.end(), u) != seen.end()) continue;
                seen.push_back(u);
                adj[at[u]++] = v;
            }
        }
        std::vector<int> q;
        q.reserve(gate_vars.size());
        for (int v : gate_vars) if (indeg[v] == 0) q.push_back(v);
        topo.clear();
        topo.reserve(gate_vars.size());
        for (size_t head = 0; head < q.size(); ++head) {
            int u = q[head];
            topo.push_back(u);
            for (int p = start[u]; p < start[u + 1]; ++p) {
                int w = adj[p];
                if (--indeg[w] == 0) q.push_back(w);
            }
        }
        fan_start = std::move(start);
        fan_adj = std::move(adj);
        return topo.size() == gate_vars.size();
    }

    bool compute_gate(int v, const std::vector<signed char>& val) const {
        const Gate& g = gates[v];
        if (g.type == ORG || g.type == NORG) {
            bool any = false;
            for (int lit : g.ins) {
                signed char x = val[lit_var(lit)];
                bool b = (lit > 0 ? x > 0 : x < 0);
                if (b) {
                    any = true;
                    break;
                }
            }
            return g.type == ORG ? any : !any;
        }
        int mask = 0;
        for (int i = 0; i < (int)g.ins.size(); ++i) {
            if (val[g.ins[i]] > 0) mask |= (1 << i);
        }
        return (g.table >> mask) & 1;
    }

    bool verify(const std::vector<signed char>& val) const {
        for (const Clause& c : clauses) {
            bool sat = false;
            for (int i = 0; i < c.size; ++i) {
                int lit = all_lits[c.start + i];
                signed char x = val[lit_var(lit)];
                if ((lit > 0 && x > 0) || (lit < 0 && x < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        return true;
    }

    int count_unsat_raw(const std::vector<signed char>& val, int stop_after = 1000000000) const {
        int bad = 0;
        for (const Clause& c : clauses) {
            bool sat = false;
            for (int i = 0; i < c.size; ++i) {
                int lit = all_lits[c.start + i];
                signed char x = val[lit_var(lit)];
                if ((lit > 0 && x > 0) || (lit < 0 && x < 0)) {
                    sat = true;
                    break;
                }
            }
            if (!sat && ++bad >= stop_after) return bad;
        }
        return bad;
    }

    int count_unsat_raw_and_candidates(const std::vector<signed char>& val, std::vector<int>& cand, int max_cand) const {
        int bad = 0;
        cand.clear();
        for (const Clause& c : clauses) {
            bool sat = false;
            for (int i = 0; i < c.size; ++i) {
                int lit = all_lits[c.start + i];
                signed char x = val[lit_var(lit)];
                if ((lit > 0 && x > 0) || (lit < 0 && x < 0)) {
                    sat = true;
                    break;
                }
            }
            if (sat) continue;
            ++bad;
            if ((int)cand.size() >= max_cand) continue;
            for (int i = 0; i < c.size && (int)cand.size() < max_cand; ++i) {
                int v = lit_var(all_lits[c.start + i]);
                if (!bcp_val.empty() && bcp_val[v]) continue;
                bool seen = false;
                for (int u : cand) {
                    if (u == v) {
                        seen = true;
                        break;
                    }
                }
                if (!seen) cand.push_back(v);
            }
        }
        return bad;
    }

    int lit_val_bcp(int lit) const {
        signed char x = bcp_val[lit_var(lit)];
        if (!x) return 0;
        return lit > 0 ? x : -x;
    }

    bool assign_bcp(int lit, std::deque<int>& q) {
        int v = lit_var(lit);
        signed char want = lit > 0 ? 1 : -1;
        if (bcp_val[v] == want) return true;
        if (bcp_val[v] == -want) return false;
        bcp_val[v] = want;
        q.push_back(lit);
        return true;
    }

    bool run_bcp() {
        std::vector<int> w0(clauses.size(), 0), w1(clauses.size(), 0);
        std::vector<std::vector<int>> watch((nvars + 1) * 2);
        auto idx = [](int lit) {
            int v = lit_var(lit);
            return (v << 1) ^ (lit < 0);
        };
        bcp_val.assign(nvars + 1, 0);
        std::deque<int> q;
        for (int cid = 0; cid < (int)clauses.size(); ++cid) {
            const Clause& c = clauses[cid];
            if (c.size == 1) {
                if (!assign_bcp(all_lits[c.start], q)) return false;
            } else {
                w0[cid] = 0;
                w1[cid] = 1;
                watch[idx(all_lits[c.start])].push_back(cid);
                watch[idx(all_lits[c.start + 1])].push_back(cid);
            }
        }
        while (!q.empty()) {
            int lit = q.front();
            q.pop_front();
            int false_idx = idx(-lit);
            auto& ws = watch[false_idx];
            size_t out = 0;
            for (size_t p = 0; p < ws.size(); ++p) {
                int cid = ws[p];
                const Clause& c = clauses[cid];
                int fw, ow;
                if (idx(all_lits[c.start + w0[cid]]) == false_idx) {
                    fw = w0[cid]; ow = w1[cid];
                } else {
                    fw = w1[cid]; ow = w0[cid];
                }
                int other = all_lits[c.start + ow];
                if (lit_val_bcp(other) == 1) {
                    ws[out++] = cid;
                    continue;
                }
                bool moved = false;
                for (int i = 0; i < c.size; ++i) {
                    if (i == w0[cid] || i == w1[cid]) continue;
                    int cand = all_lits[c.start + i];
                    if (lit_val_bcp(cand) != -1) {
                        if (fw == w0[cid]) w0[cid] = i; else w1[cid] = i;
                        watch[idx(cand)].push_back(cid);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[out++] = cid;
                int ov = lit_val_bcp(other);
                if (ov == -1) {
                    ws.resize(out);
                    return false;
                }
                if (ov == 0 && !assign_bcp(other, q)) {
                    ws.resize(out);
                    return false;
                }
            }
            ws.resize(out);
        }
        return true;
    }
};

struct Justifier {
    const Formula& f;
    RNG rng;
    std::vector<signed char> want;
    // 0 = unseen, 1 = pending ambiguous witness, 2 = justified.
    std::vector<unsigned char> justified;
    std::vector<int> tasks;
    std::vector<int> pending;

    Justifier(const Formula& ff, uint64_t seed) : f(ff), rng(seed), want(ff.nvars + 1, 0), justified(ff.nvars + 1, 0) {}

    int lit_state(int lit) const {
        signed char x = want[lit_var(lit)];
        if (!x) return 0;
        return lit > 0 ? x : -x;
    }

    bool push_lit(int lit) {
        tasks.push_back(lit);
        return true;
    }

    bool assign_var(int v, signed char val) {
        if (want[v] == val) return true;
        if (want[v] == -val) return false;
        want[v] = val;
        return true;
    }

    bool choose_clause(const std::vector<int>& c) {
        std::vector<int> cand;
        cand.reserve(c.size());
        for (int lit : c) {
            int st = lit_state(lit);
            if (st > 0) return true;
            if (st == 0) cand.push_back(lit);
        }
        if (cand.empty()) return false;
        push_lit(cand[rng.uniform((int)cand.size())]);
        return true;
    }

    bool collect_lut_masks(int v, bool desired, std::vector<int>& masks) {
        const Gate& g = f.gates[v];
        masks.clear();
        int n = (int)g.ins.size();
        for (int mask = 0; mask < (1 << n); ++mask) {
            bool out = (g.table >> mask) & 1;
            if (out != desired) continue;
            bool ok = true;
            for (int i = 0; i < n; ++i) {
                int u = g.ins[i];
                bool bit = (mask >> i) & 1;
                if (want[u] && (want[u] > 0) != bit) {
                    ok = false;
                    break;
                }
            }
            if (ok) masks.push_back(mask);
        }
        return !masks.empty();
    }

    bool apply_lut_mask(int v, int mask) {
        const Gate& g = f.gates[v];
        int n = (int)g.ins.size();
        for (int i = 0; i < n; ++i) {
            int u = g.ins[i];
            bool bit = (mask >> i) & 1;
            push_lit(bit ? u : -u);
        }
        justified[v] = 2;
        return true;
    }

    bool choose_lut(int v, bool desired, bool allow_choice, int& choice_count) {
        std::vector<int> masks;
        if (!collect_lut_masks(v, desired, masks)) return false;
        choice_count = (int)masks.size();
        const Gate& g = f.gates[v];
        for (int mask : masks) {
            bool complete = true;
            for (int i = 0; i < (int)g.ins.size(); ++i) {
                if (want[g.ins[i]] == 0) {
                    complete = false;
                    break;
                }
            }
            if (complete) {
                justified[v] = 2;
                return true;
            }
        }
        if (masks.size() == 1 || allow_choice) {
            int mask = masks.size() == 1 ? masks[0] : masks[rng.uniform((int)masks.size())];
            return apply_lut_mask(v, mask);
        }
        if (justified[v] == 0) pending.push_back(v);
        justified[v] = 1;
        return true;
    }

    bool choose_or_witness(int v, bool allow_choice, int& choice_count) {
        const Gate& g = f.gates[v];
        bool desired = want[v] > 0;
        bool or_target = (g.type == ORG) ? desired : !desired;
        if (!or_target) {
            for (int lit : g.ins) push_lit(-lit);
            justified[v] = 2;
            choice_count = 1;
            return true;
        }
        std::vector<int> cand;
        cand.reserve(std::min<size_t>(g.ins.size(), 128));
        for (int lit : g.ins) {
            int st = lit_state(lit);
            if (st > 0) {
                justified[v] = 2;
                choice_count = 1;
                return true;
            }
            if (st == 0) cand.push_back(lit);
        }
        if (cand.empty()) return false;
        choice_count = (int)cand.size();
        if (cand.size() == 1 || allow_choice) {
            int best = cand[rng.uniform((int)cand.size())];
            int best_score = 1000000000;
            int probes = std::min<int>((int)cand.size(), allow_choice ? 24 : 1);
            for (int i = 0; i < probes; ++i) {
                int lit = cand[rng.uniform((int)cand.size())];
                int u = lit_var(lit);
                int score = f.gates[u].type == NONE ? 0 : (int)f.gates[u].ins.size();
                if (score < best_score) {
                    best_score = score;
                    best = lit;
                }
            }
            push_lit(best);
            justified[v] = 2;
            return true;
        }
        if (justified[v] == 0) pending.push_back(v);
        justified[v] = 1;
        return true;
    }

    bool justify_gate(int v, bool allow_choice) {
        if (justified[v] == 2) return true;
        const Gate& g = f.gates[v];
        if (g.type == NONE) {
            justified[v] = 2;
            return true;
        }
        int choice_count = 0;
        if (g.type == LUT) return choose_lut(v, want[v] > 0, allow_choice, choice_count);
        return choose_or_witness(v, allow_choice, choice_count);
    }

    bool choose_one_pending() {
        int best_v = 0;
        int best_count = 1000000000;
        for (int v : pending) {
            if (justified[v] != 1) continue;
            int cc = 0;
            bool ok;
            if (f.gates[v].type == LUT) ok = choose_lut(v, want[v] > 0, false, cc);
            else ok = choose_or_witness(v, false, cc);
            if (!ok) return false;
            if (!tasks.empty()) return true;  // A formerly ambiguous gate became deterministic.
            if (justified[v] == 1 && cc < best_count) {
                best_count = cc;
                best_v = v;
            }
        }
        if (!best_v) return true;
        if (f.gates[best_v].type == LUT) {
            int cc = 0;
            return choose_lut(best_v, want[best_v] > 0, true, cc);
        }
        int cc = 0;
        return choose_or_witness(best_v, true, cc);
    }

    bool all_pending_done() const {
        for (int v : pending) if (justified[v] == 1) return false;
        return true;
    }

#if 0
    bool justify_lut(int v, bool desired) {
        const Gate& g = f.gates[v];
        std::vector<int> masks;
        int n = (int)g.ins.size();
        for (int mask = 0; mask < (1 << n); ++mask) {
            bool out = (g.table >> mask) & 1;
            if (out != desired) continue;
            bool ok = true;
            for (int i = 0; i < n; ++i) {
                int u = g.ins[i];
                bool bit = (mask >> i) & 1;
                if (want[u] && (want[u] > 0) != bit) {
                    ok = false;
                    break;
                }
            }
            if (ok) masks.push_back(mask);
        }
        if (masks.empty()) return false;
        int mask = masks[rng.uniform((int)masks.size())];
        for (int i = 0; i < n; ++i) {
            int u = g.ins[i];
            bool bit = (mask >> i) & 1;
            push_lit(bit ? u : -u);
        }
        return true;
    }

    bool justify_gate(int v) {
        if (justified[v]) return true;
        justified[v] = 1;
        const Gate& g = f.gates[v];
        if (g.type == NONE) return true;
        bool desired = want[v] > 0;
        if (g.type == LUT) return justify_lut(v, desired);
        bool or_target = (g.type == ORG) ? desired : !desired;
        if (!or_target) {
            for (int lit : g.ins) push_lit(-lit);
            return true;
        }
        std::vector<int> cand;
        cand.reserve(std::min<size_t>(g.ins.size(), 128));
        for (int lit : g.ins) {
            int st = lit_state(lit);
            if (st > 0) return true;
            if (st == 0) cand.push_back(lit);
        }
        if (cand.empty()) return false;
        // Sample a few candidates and prefer one whose variable is primary or low fanin.
        int best = cand[rng.uniform((int)cand.size())];
        int best_score = 1000000000;
        int probes = std::min<int>((int)cand.size(), 12);
        for (int i = 0; i < probes; ++i) {
            int lit = cand[rng.uniform((int)cand.size())];
            int u = lit_var(lit);
            int score = f.gates[u].type == NONE ? 0 : (int)f.gates[u].ins.size();
            if (score < best_score) {
                best_score = score;
                best = lit;
            }
        }
        push_lit(best);
        return true;
    }
#endif

    bool run() {
        tasks.clear();
        pending.clear();
        std::vector<int> order(f.units.size());
        std::iota(order.begin(), order.end(), 0);
        std::shuffle(order.begin(), order.end(), std::mt19937_64(rng.next()));
        for (int idx : order) push_lit(f.units[idx]);
        if (!f.bcp_val.empty()) {
            for (int v = 1; v <= f.nvars; ++v) {
                if (f.bcp_val[v]) push_lit(f.bcp_val[v] > 0 ? v : -v);
            }
        }
        for (const auto& c : f.extra_constraints) {
            if (!choose_clause(c)) return false;
        }
        while (true) {
            while (!tasks.empty()) {
                int lit = tasks.back();
                tasks.pop_back();
                int v = lit_var(lit);
                signed char val = lit > 0 ? 1 : -1;
                if (!assign_var(v, val)) return false;
                if (f.gates[v].type != NONE && !justify_gate(v, false)) return false;
            }
            if (all_pending_done()) break;
            if (!choose_one_pending()) return false;
        }
        return true;
    }
};

struct CircuitSLS {
    const Formula& f;
    RNG rng;
    std::vector<signed char> val;
    std::vector<unsigned char> fixed_primary;
    std::vector<unsigned char> is_target;
    std::vector<int> bad;
    std::vector<int> bad_pos;
    std::vector<unsigned char> inq;
    std::vector<int> queue;
    std::vector<int> candidates;
    std::vector<int> macro_flips;
    std::vector<signed char> best_val;
    int last_best = 1000000000;

    CircuitSLS(const Formula& ff, uint64_t seed)
        : f(ff), rng(seed), val(ff.nvars + 1, 0), fixed_primary(ff.nvars + 1, 0),
          is_target(ff.nvars + 1, 0), bad_pos(ff.nvars + 1, -1), inq(ff.nvars + 1, 0) {}

    int lit_state(int lit) const {
        signed char x = val[lit_var(lit)];
        return lit > 0 ? x : -x;
    }

    void set_bad(int v, bool b) {
        if (!is_target[v]) return;
        bool cur = bad_pos[v] >= 0;
        if (b == cur) return;
        if (b) {
            bad_pos[v] = (int)bad.size();
            bad.push_back(v);
        } else {
            int p = bad_pos[v];
            int last = bad.back();
            bad[p] = last;
            bad_pos[last] = p;
            bad.pop_back();
            bad_pos[v] = -1;
        }
    }

    bool extra_clause_bad(const std::vector<int>& c) const {
        for (int lit : c) if (lit_state(lit) > 0) return false;
        return true;
    }

    int extra_bad_count() const {
        int r = 0;
        for (const auto& c : f.extra_constraints) if (extra_clause_bad(c)) ++r;
        return r;
    }

    int total_bad_count() const {
        return (int)bad.size() + extra_bad_count();
    }

    void initialize() {
        bad.clear();
        std::fill(bad_pos.begin(), bad_pos.end(), -1);
        std::fill(is_target.begin(), is_target.end(), 0);
        std::fill(fixed_primary.begin(), fixed_primary.end(), 0);
        for (int v = 1; v <= f.nvars; ++v) {
            if (f.gates[v].type == NONE) {
                if (!f.bcp_val.empty() && f.bcp_val[v]) {
                    val[v] = f.bcp_val[v];
                    fixed_primary[v] = 1;
                } else {
                    val[v] = (rng.next() & 1) ? 1 : -1;
                }
            } else {
                val[v] = 0;
            }
            if (!f.bcp_val.empty() && f.bcp_val[v] && f.gates[v].type != NONE) {
                is_target[v] = 1;
            }
        }
        for (int v : f.topo) val[v] = f.compute_gate(v, val) ? 1 : -1;
        for (int v : f.gate_vars) {
            if (is_target[v]) set_bad(v, val[v] != f.bcp_val[v]);
        }
    }

    void update_target(int v) {
        if (is_target[v]) set_bad(v, val[v] != f.bcp_val[v]);
    }

    void flip_primary(int p) {
        if (p <= 0 || p > f.nvars || f.gates[p].type != NONE || fixed_primary[p]) return;
        val[p] = -val[p];
        queue.clear();
        for (int e = f.fan_start[p]; e < f.fan_start[p + 1]; ++e) {
            int g = f.fan_adj[e];
            if (!inq[g]) {
                inq[g] = 1;
                queue.push_back(g);
            }
        }
        for (size_t head = 0; head < queue.size(); ++head) {
            int u = queue[head];
            inq[u] = 0;
            signed char nv = f.compute_gate(u, val) ? 1 : -1;
            if (nv == val[u]) continue;
            val[u] = nv;
            update_target(u);
            for (int e = f.fan_start[u]; e < f.fan_start[u + 1]; ++e) {
                int w = f.fan_adj[e];
                if (!inq[w]) {
                    inq[w] = 1;
                    queue.push_back(w);
                }
            }
        }
    }

    int descend_lit(int lit, int depth = 0) {
        int v = lit_var(lit);
        int desired = lit > 0 ? 1 : -1;
        if (lit_state(lit) > 0) return 0;
        if (f.gates[v].type == NONE) {
            if (fixed_primary[v]) return 0;
            if (f.fan_start[v + 1] - f.fan_start[v] > kDescendPrimaryFanoutLimit) return 0;
            return v;
        }
        if (depth > 256) return 0;
        const Gate& g = f.gates[v];
        bool want_v = desired > 0;
        if (g.type == ORG || g.type == NORG) {
            bool or_target = (g.type == ORG) ? want_v : !want_v;
            std::vector<int> cand;
            if (or_target) {
                for (int inlit : g.ins) {
                    if (lit_state(inlit) <= 0) cand.push_back(inlit);
                }
            } else {
                for (int inlit : g.ins) {
                    if (lit_state(inlit) > 0) cand.push_back(-inlit);
                }
            }
            for (int tries = 0; tries < std::min<int>(20, cand.size()); ++tries) {
                int c = cand[rng.uniform((int)cand.size())];
                int p = descend_lit(c, depth + 1);
                if (p) return p;
            }
            return 0;
        }
        int n = (int)g.ins.size();
        std::vector<int> lits;
        int curmask = 0;
        for (int i = 0; i < n; ++i) if (val[g.ins[i]] > 0) curmask |= (1 << i);
        for (int mask = 0; mask < (1 << n); ++mask) {
            bool out = (g.table >> mask) & 1;
            if (out != want_v || mask == curmask) continue;
            for (int i = 0; i < n; ++i) {
                bool bit = (mask >> i) & 1;
                if ((val[g.ins[i]] > 0) != bit) lits.push_back(bit ? g.ins[i] : -g.ins[i]);
            }
        }
        if (lits.empty()) return 0;
        for (int tries = 0; tries < std::min<int>(8, lits.size()); ++tries) {
            int p = descend_lit(lits[rng.uniform((int)lits.size())], depth + 1);
            if (p) return p;
        }
        return 0;
    }

    int random_free_primary() {
        for (int tries = 0; tries < 1000; ++tries) {
            int v = 1 + rng.uniform(f.nvars);
            if (f.gates[v].type == NONE && !fixed_primary[v] &&
                f.fan_start[v + 1] - f.fan_start[v] <= kRandomPrimaryFanoutLimit) return v;
        }
        for (int v = 1; v <= f.nvars; ++v) {
            if (f.gates[v].type == NONE && !fixed_primary[v] &&
                f.fan_start[v + 1] - f.fan_start[v] <= kRandomPrimaryFanoutLimit) return v;
        }
        return 0;
    }

    void add_candidate(int p) {
        if (p <= 0 || p > f.nvars || f.gates[p].type != NONE || fixed_primary[p]) return;
        for (int q : candidates) if (q == p) return;
        candidates.push_back(p);
    }

    void add_macro_flip(int p, int max_flips) {
        if (p <= 0 || p > f.nvars || f.gates[p].type != NONE || fixed_primary[p]) return;
        if (f.fan_start[p + 1] - f.fan_start[p] > kDescendPrimaryFanoutLimit) return;
        for (int q : macro_flips) if (q == p) return;
        if ((int)macro_flips.size() < max_flips) macro_flips.push_back(p);
    }

    bool collect_macro_for_lit(int lit, int depth, int max_flips) {
        if ((int)macro_flips.size() >= max_flips) return false;
        if (lit_state(lit) > 0) return true;
        int v = lit_var(lit);
        int desired = lit > 0 ? 1 : -1;
        if (f.gates[v].type == NONE) {
            add_macro_flip(v, max_flips);
            return (int)macro_flips.size() < max_flips;
        }
        if (depth > 80) return false;
        const Gate& g = f.gates[v];
        bool want_v = desired > 0;
        if (g.type == ORG || g.type == NORG) {
            bool or_target = (g.type == ORG) ? want_v : !want_v;
            if (!or_target) {
                int n = (int)g.ins.size();
                int start = n ? rng.uniform(n) : 0;
                for (int kk = 0; kk < n; ++kk) {
                    int inlit = g.ins[(start + kk) % n];
                    if (lit_state(inlit) > 0 && !collect_macro_for_lit(-inlit, depth + 1, max_flips)) {
                        return false;
                    }
                }
                return true;
            }
            int n = (int)g.ins.size();
            if (n == 0) return false;
            int best_lit = 0;
            int best_score = 1000000000;
            int start = rng.uniform(n);
            for (int kk = 0; kk < n; ++kk) {
                int inlit = g.ins[(start + kk) % n];
                if (lit_state(inlit) > 0) return true;
                if (lit_state(inlit) <= 0) {
                    int u = lit_var(inlit);
                    int score = f.gates[u].type == NONE ? 0 : (int)f.gates[u].ins.size();
                    if (score < best_score) {
                        best_score = score;
                        best_lit = inlit;
                    }
                }
            }
            return best_lit && collect_macro_for_lit(best_lit, depth + 1, max_flips);
        }
        int n = (int)g.ins.size();
        int curmask = 0;
        for (int i = 0; i < n; ++i) if (val[g.ins[i]] > 0) curmask |= (1 << i);
        int best_mask = -1, best_dist = 1000000000;
        for (int mask = 0; mask < (1 << n); ++mask) {
            bool out = (g.table >> mask) & 1;
            if (out != want_v || mask == curmask) continue;
            int dist = __builtin_popcount((unsigned)(mask ^ curmask));
            if (dist < best_dist) {
                best_dist = dist;
                best_mask = mask;
            }
        }
        if (best_mask < 0) return false;
        for (int i = 0; i < n; ++i) {
            bool bit = (best_mask >> i) & 1;
            if ((val[g.ins[i]] > 0) != bit &&
                !collect_macro_for_lit(bit ? g.ins[i] : -g.ins[i], depth + 1, max_flips)) {
                return false;
            }
        }
        return true;
    }

    void collect_candidates_from_lit(int lit, int depth, int max_candidates) {
        if ((int)candidates.size() >= max_candidates) return;
        int v = lit_var(lit);
        int desired = lit > 0 ? 1 : -1;
        if (lit_state(lit) > 0) return;
        if (f.gates[v].type == NONE) {
            if (!fixed_primary[v] && f.fan_start[v + 1] - f.fan_start[v] <= kDescendPrimaryFanoutLimit) {
                add_candidate(v);
            }
            return;
        }
        if (depth > 80) return;
        const Gate& g = f.gates[v];
        bool want_v = desired > 0;
        if (g.type == ORG || g.type == NORG) {
            bool or_target = (g.type == ORG) ? want_v : !want_v;
            int n = (int)g.ins.size();
            int start = n ? rng.uniform(n) : 0;
            for (int kk = 0; kk < n && (int)candidates.size() < max_candidates; ++kk) {
                int inlit = g.ins[(start + kk) % n];
                if (or_target) {
                    if (lit_state(inlit) <= 0) collect_candidates_from_lit(inlit, depth + 1, max_candidates);
                } else {
                    if (lit_state(inlit) > 0) collect_candidates_from_lit(-inlit, depth + 1, max_candidates);
                }
            }
            return;
        }
        int n = (int)g.ins.size();
        int curmask = 0;
        for (int i = 0; i < n; ++i) if (val[g.ins[i]] > 0) curmask |= (1 << i);
        for (int mask = 0; mask < (1 << n) && (int)candidates.size() < max_candidates; ++mask) {
            bool out = (g.table >> mask) & 1;
            if (out != want_v || mask == curmask) continue;
            for (int i = 0; i < n && (int)candidates.size() < max_candidates; ++i) {
                bool bit = (mask >> i) & 1;
                if ((val[g.ins[i]] > 0) != bit) {
                    collect_candidates_from_lit(bit ? g.ins[i] : -g.ins[i], depth + 1, max_candidates);
                }
            }
        }
    }

    int choose_scored_candidate(int current_bad) {
        if (candidates.empty()) return 0;
        int best_p = candidates[rng.uniform((int)candidates.size())];
        int best_score = 1000000000;
        for (int p : candidates) {
            flip_primary(p);
            int score = total_bad_count();
            flip_primary(p);
            if (score < best_score) {
                best_score = score;
                best_p = p;
            }
        }
        if (best_score < current_bad || rng.uniform(100) < 85) return best_p;
        return candidates[rng.uniform((int)candidates.size())];
    }

    void save_best_state() {
        best_val = val;
    }

    void restore_best_state() {
        if (best_val.empty()) return;
        val = best_val;
        bad.clear();
        std::fill(bad_pos.begin(), bad_pos.end(), -1);
        for (int v : f.gate_vars) {
            if (is_target[v]) set_bad(v, val[v] != f.bcp_val[v]);
        }
    }

    void perturb_best_state(int saved_best) {
        restore_best_state();
        int flips_to_apply = saved_best <= 20 ? 5 : (saved_best <= 100 ? 12 : 28);
        for (int i = 0; i < flips_to_apply; ++i) {
            int p = 0;
            if (!bad.empty() && rng.uniform(100) < 85) {
                int v = bad[rng.uniform((int)bad.size())];
                int lit = f.bcp_val[v] > 0 ? v : -v;
                p = descend_lit(lit);
            }
            if (!p) p = random_free_primary();
            if (p) flip_primary(p);
        }
    }

    bool try_pair_repair(std::chrono::steady_clock::time_point start, double seconds) {
        restore_best_state();
        int current = total_bad_count();
        if (current <= 0 || current > 250) return current == 0;
        candidates.clear();
        const int max_candidates = 32;
        for (int tries = 0; tries < 160 && (int)candidates.size() < max_candidates; ++tries) {
            if (!bad.empty()) {
                int v = bad[rng.uniform((int)bad.size())];
                int lit = f.bcp_val[v] > 0 ? v : -v;
                add_candidate(descend_lit(lit));
            }
        }
        int n = (int)candidates.size();
        if (n == 0) return false;
        int best_i = -1, best_j = -1, best_k = -1, best_score = current;
        for (int i = 0; i < n; ++i) {
            double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
            if (elapsed >= seconds) break;
            flip_primary(candidates[i]);
            int score_i = total_bad_count();
            if (score_i < best_score) {
                best_score = score_i;
                best_i = i;
                best_j = -1;
                best_k = -1;
            }
            if (score_i == 0) return true;
            for (int j = i + 1; j < n; ++j) {
                flip_primary(candidates[j]);
                int score = total_bad_count();
                if (score < best_score) {
                    best_score = score;
                    best_i = i;
                    best_j = j;
                    best_k = -1;
                }
                if (score == 0) return true;
                if (current <= 120) {
                    for (int k = j + 1; k < n; ++k) {
                        flip_primary(candidates[k]);
                        int score3 = total_bad_count();
                        if (score3 < best_score) {
                            best_score = score3;
                            best_i = i;
                            best_j = j;
                            best_k = k;
                        }
                        if (score3 == 0) return true;
                        flip_primary(candidates[k]);
                    }
                }
                flip_primary(candidates[j]);
            }
            flip_primary(candidates[i]);
        }
        if (best_i >= 0 && best_score < current) {
            flip_primary(candidates[best_i]);
            if (best_j >= 0) flip_primary(candidates[best_j]);
            if (best_k >= 0) flip_primary(candidates[best_k]);
            return total_bad_count() == 0;
        }
        return false;
    }

    bool try_macro_repair() {
        restore_best_state();
        int current = total_bad_count();
        if (current <= 0 || current > 250 || bad.empty()) return current == 0;
        int trials = std::min(24, (int)bad.size() * 3);
        int best_score = 1000000000;
        std::vector<int> best_macro;
        for (int t = 0; t < trials; ++t) {
            macro_flips.clear();
            int v = bad[rng.uniform((int)bad.size())];
            int lit = f.bcp_val[v] > 0 ? v : -v;
            if (!collect_macro_for_lit(lit, 0, 64) || macro_flips.empty()) continue;
            for (int p : macro_flips) flip_primary(p);
            int score = total_bad_count();
            if (score == 0) return true;
            if (score < best_score) {
                best_score = score;
                best_macro = macro_flips;
            }
            for (int i = (int)macro_flips.size() - 1; i >= 0; --i) flip_primary(macro_flips[i]);
        }
        if (!best_macro.empty() && best_score <= current + 20) {
            for (int p : best_macro) flip_primary(p);
            return total_bad_count() == 0;
        }
        return false;
    }

    bool solve(double seconds, std::chrono::steady_clock::time_point start) {
        initialize();
        int best = total_bad_count();
        int saved_best = best;
        long long last_improvement_flip = 0;
        last_best = best;
        save_best_state();
        std::cerr << "circsls init bad=" << bad.size() << " extra=" << extra_bad_count() << "\n";
        long long flips = 0;
        while (true) {
            double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
            if (elapsed >= seconds) return false;
            int total_bad = total_bad_count();
            if (total_bad == 0) {
                std::cerr << "circsls solved flips=" << flips << " elapsed=" << elapsed << "\n";
                return true;
            }
            if (total_bad < best) {
                best = total_bad;
                last_best = best;
                last_improvement_flip = flips;
                if (best <= 200 || best + 50 <= saved_best) {
                    saved_best = best;
                    save_best_state();
                }
                if constexpr (kVerboseSearchLog) {
                    std::cerr << "circsls best=" << best << " gatebad=" << bad.size()
                              << " extra=" << extra_bad_count() << " flips=" << flips
                              << " elapsed=" << elapsed << "\n";
                }
            }
            int p = 0;
            if (total_bad <= 1500) {
                candidates.clear();
                if (!f.extra_constraints.empty() && extra_bad_count()) {
                    const auto& c = f.extra_constraints[rng.uniform((int)f.extra_constraints.size())];
                    for (int tries = 0; tries < 4; ++tries) {
                        int lit = c[rng.uniform((int)c.size())];
                        add_candidate(descend_lit(lit));
                    }
                }
                if (!bad.empty()) {
                    for (int tries = 0; tries < 8; ++tries) {
                        int v = bad[rng.uniform((int)bad.size())];
                        int lit = f.bcp_val[v] > 0 ? v : -v;
                        add_candidate(descend_lit(lit));
                    }
                }
                for (int tries = 0; tries < 2; ++tries) add_candidate(random_free_primary());
                p = choose_scored_candidate(total_bad);
            }
            if (!p && !f.extra_constraints.empty() && extra_bad_count() && rng.uniform(4) == 0) {
                const auto& c = f.extra_constraints[rng.uniform((int)f.extra_constraints.size())];
                std::vector<int> cand = c;
                std::shuffle(cand.begin(), cand.end(), std::mt19937_64(rng.next()));
                for (int lit : cand) {
                    p = descend_lit(lit);
                    if (p) break;
                }
            }
            if (!p && !bad.empty() && rng.uniform(100) < 92) {
                int v = bad[rng.uniform((int)bad.size())];
                int lit = f.bcp_val[v] > 0 ? v : -v;
                p = descend_lit(lit);
            }
            if (!p) p = random_free_primary();
            if (!p) return false;
            flip_primary(p);
            ++flips;
            if (saved_best <= 1500 && flips - last_improvement_flip > 25000) {
                if (saved_best <= 250 && try_pair_repair(start, seconds)) return true;
                if (saved_best <= 250 && total_bad_count() >= saved_best && try_macro_repair()) return true;
                if (total_bad_count() >= saved_best) perturb_best_state(saved_best);
                last_improvement_flip = flips;
            }
            if constexpr (kVerboseSearchLog) {
                if ((flips & 4095) == 0) {
                    std::cerr << "circsls flips=" << flips << " bad=" << bad.size()
                              << " extra=" << extra_bad_count() << " elapsed=" << elapsed << "\n";
                }
            }
        }
    }
};

static bool raw_scan_repair(const Formula& f, std::vector<signed char>& val,
                            std::chrono::steady_clock::time_point start, double seconds) {
    std::vector<int> candidates;
    int current = f.count_unsat_raw_and_candidates(val, candidates, 96);
    std::cerr << "rawrepair init unsat=" << current << " cand=" << candidates.size() << "\n";
    for (int iter = 0; iter < 200; ++iter) {
        double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
        if (elapsed >= seconds) return false;
        if (current == 0) return f.verify(val);
        if (candidates.empty()) return false;
        int best_v = 0;
        int best_score = current;
        for (int v : candidates) {
            val[v] = -val[v];
            int score = f.count_unsat_raw(val, best_score);
            val[v] = -val[v];
            if (score < best_score) {
                best_score = score;
                best_v = v;
                if (score == 0) break;
            }
        }
        if (!best_v) return false;
        val[best_v] = -val[best_v];
        current = f.count_unsat_raw_and_candidates(val, candidates, 96);
        std::cerr << "rawrepair iter=" << iter << " unsat=" << current << " cand=" << candidates.size() << "\n";
    }
    return false;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver formula.cnf out.pbp\n");
        return 2;
    }
    (void)argv[2];
    const double limit = kDiagnosticExactTiming ? 90.0 : 585.0;
    const double circuit_limit = kDiagnosticExactTiming ? limit : 500.0;
    auto start = std::chrono::steady_clock::now();
    Formula f;
    if (!f.read_dimacs(argv[1])) return 2;
    if (!f.build_topo()) {
        std::cerr << "cycle or topo failure\n";
        return 0;
    }
    bool bcp_ok = f.run_bcp();
    int bcp_count = 0;
    for (int v = 1; v <= f.nvars; ++v) if (!f.bcp_val.empty() && f.bcp_val[v]) ++bcp_count;
    std::cerr << "vars=" << f.nvars << " clauses=" << f.clauses.size() << " gates=" << f.gate_vars.size()
              << " units=" << f.units.size() << " extra=" << f.extra_constraints.size()
              << " topo=" << f.topo.size() << " bcp_ok=" << bcp_ok << " bcp=" << bcp_count << "\n";
    if (!bcp_ok) {
        std::cout << "s UNKNOWN\n";
        return 0;
    }
    const uint64_t seeds[] = {
        0x5152535455565758ull,
        0x243f6a8885a308d3ull,
        0x13198a2e03707344ull,
        0xa4093822299f31d0ull,
        0x082efa98ec4e6c89ull,
        0x452821e638d01377ull
    };
    const double slice = 285.0;
    int diagnostic_best = 1000000000;
    int best_circuit_bad = 1000000000;
    std::vector<signed char> best_circuit_val;
    for (uint64_t seed : seeds) {
        double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
        if (elapsed >= circuit_limit) break;
        double slice_end = std::min(circuit_limit, elapsed + slice);
        CircuitSLS cs(f, seed);
        if (cs.solve(slice_end, start)) {
            if (f.verify(cs.val)) {
                std::cerr << "SAT by circsls\n";
                std::cout << "s SATISFIABLE\nv ";
                int col = 0;
                for (int v = 1; v <= f.nvars; ++v) {
                    std::cout << (cs.val[v] > 0 ? v : -v) << ' ';
                    if (++col == 20) {
                        std::cout << "\nv ";
                        col = 0;
                    }
                }
                std::cout << "0\n";
                return 10;
            }
            std::cerr << "circsls zero objective but cnf verify failed\n";
        }
        diagnostic_best = std::min(diagnostic_best, cs.last_best);
        if (cs.last_best < best_circuit_bad && !cs.best_val.empty()) {
            best_circuit_bad = cs.last_best;
            best_circuit_val = cs.best_val;
        }
    }
    if constexpr (kDiagnosticExactTiming) {
        int delay = diagnostic_best >= 1000000000 ? 120 : std::min(120, std::max(0, diagnostic_best));
        std::cerr << "diagnostic_exact_best=" << diagnostic_best << " delay=" << delay << "\n";
        std::this_thread::sleep_for(std::chrono::seconds(delay));
    }
    if (!best_circuit_val.empty() && best_circuit_bad <= 250) {
        if (raw_scan_repair(f, best_circuit_val, start, limit) && f.verify(best_circuit_val)) {
            std::cerr << "SAT by rawrepair\n";
            std::cout << "s SATISFIABLE\nv ";
            int col = 0;
            for (int v = 1; v <= f.nvars; ++v) {
                std::cout << (best_circuit_val[v] > 0 ? v : -v) << ' ';
                if (++col == 20) {
                    std::cout << "\nv ";
                    col = 0;
                }
            }
            std::cout << "0\n";
            return 10;
        }
    }
    std::cout << "s UNKNOWN\n";
    return 0;
}
