#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <numeric>
#include <random>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct Formula {
    int nvars = 0;
    vector<vector<int>> clauses;
};

static bool parse_dimacs(const char* path, Formula& f) {
    ifstream in(path);
    if (!in) return false;
    string line;
    while (getline(in, line)) {
        if (line.empty() || line[0] == 'c' || line[0] == '%') continue;
        if (line[0] == 'p') {
            string p, cnf;
            int m = 0;
            stringstream ss(line);
            ss >> p >> cnf >> f.nvars >> m;
            f.clauses.reserve(m);
            continue;
        }
        stringstream ss(line);
        int x;
        vector<int> c;
        while (ss >> x && x) c.push_back(x);
        if (!c.empty()) f.clauses.push_back(std::move(c));
    }
    return f.nvars > 0;
}

static bool verify_model(const Formula& f, const vector<unsigned char>& a) {
    if ((int)a.size() <= f.nvars) return false;
    for (const auto& c : f.clauses) {
        bool sat = false;
        for (int l : c) {
            int v = abs(l);
            if (v < 1 || v > f.nvars) return false;
            if ((bool)a[v] == (l > 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) return false;
    }
    return true;
}

static bool unit_simplify(const Formula& in, Formula& out) {
    int n = in.nvars;
    vector<int> assign(n + 1, 0);
    bool changed = true;
    while (changed) {
        changed = false;
        for (const auto& c : in.clauses) {
            bool sat = false;
            int unassigned = 0, last = 0;
            for (int l : c) {
                int v = abs(l);
                if (assign[v]) {
                    if ((assign[v] > 0) == (l > 0)) {
                        sat = true;
                        break;
                    }
                } else {
                    ++unassigned;
                    last = l;
                }
            }
            if (sat) continue;
            if (unassigned == 0) return false;
            if (unassigned == 1) {
                int v = abs(last), want = last > 0 ? 1 : -1;
                if (assign[v] && assign[v] != want) return false;
                if (!assign[v]) {
                    assign[v] = want;
                    changed = true;
                }
            }
        }
    }
    out.nvars = n;
    out.clauses.clear();
    for (int v = 1; v <= n; ++v) if (assign[v]) out.clauses.push_back({assign[v] > 0 ? v : -v});
    for (const auto& c : in.clauses) {
        bool sat = false;
        vector<int> r;
        r.reserve(c.size());
        for (int l : c) {
            int v = abs(l);
            if (assign[v]) {
                if ((assign[v] > 0) == (l > 0)) {
                    sat = true;
                    break;
                }
            } else {
                r.push_back(l);
            }
        }
        if (!sat) {
            if (r.empty()) return false;
            out.clauses.push_back(std::move(r));
        }
    }
    return true;
}

static void print_sat(const Formula& f, const vector<unsigned char>& a) {
    printf("s SATISFIABLE\n");
    printf("v ");
    for (int v = 1; v <= f.nvars; ++v) printf("%d ", a[v] ? v : -v);
    printf("0\n");
}

struct SLSOcc { int c; int sign; };

static bool detect_one_hot(const Formula& f, vector<vector<int>>& groups) {
    unordered_set<uint64_t> negpair;
    negpair.reserve(f.clauses.size() * 2);
    for (const auto& c : f.clauses) {
        if (c.size() == 2 && c[0] < 0 && c[1] < 0) {
            int a = -c[0], b = -c[1];
            if (a > b) swap(a, b);
            negpair.insert((uint64_t(a) << 32) | uint32_t(b));
        }
    }
    vector<int> owned(f.nvars + 1, -1);
    for (const auto& c : f.clauses) {
        if (c.size() < 2) continue;
        bool all_pos = true;
        for (int l : c) all_pos &= l > 0;
        if (!all_pos) continue;
        bool ok = true;
        for (size_t i = 0; i < c.size() && ok; ++i) {
            for (size_t j = i + 1; j < c.size(); ++j) {
                int a = c[i], b = c[j];
                if (a > b) swap(a, b);
                if (!negpair.count((uint64_t(a) << 32) | uint32_t(b))) {
                    ok = false;
                    break;
                }
            }
        }
        if (!ok) continue;
        bool disjoint = true;
        for (int v : c) disjoint &= owned[v] < 0;
        if (!disjoint) continue;
        int id = (int)groups.size();
        groups.push_back(c);
        for (int v : c) owned[v] = id;
    }
    return !groups.empty();
}

static vector<int> false_clauses(const Formula& f, const vector<unsigned char>& a, int limit) {
    vector<int> out;
    if ((int)a.size() <= f.nvars) return out;
    for (int i = 0; i < (int)f.clauses.size(); ++i) {
        bool sat = false;
        for (int l : f.clauses[i]) {
            if ((bool)a[abs(l)] == (l > 0)) {
                sat = true;
                break;
            }
        }
        if (!sat) {
            out.push_back(i);
            if ((int)out.size() >= limit) break;
        }
    }
    return out;
}

static bool sls_solve(const Formula& f, vector<unsigned char>& model, double seconds) {
    const int n = f.nvars, m = (int)f.clauses.size();
    vector<vector<SLSOcc>> occ(n + 1);
    vector<int> fixed(n + 1, 0);
    for (int ci = 0; ci < m; ++ci) {
        const auto& c = f.clauses[ci];
        if (c.size() == 1) fixed[abs(c[0])] = c[0] > 0 ? 1 : -1;
        for (int l : c) occ[abs(l)].push_back({ci, l > 0 ? 1 : -1});
    }
    vector<vector<int>> groups;
    detect_one_hot(f, groups);

    uint64_t seed = 1469598103934665603ull;
    for (const auto& c : f.clauses) for (int l : c) seed = (seed ^ uint64_t(l + 1000003)) * 1099511628211ull;
    mt19937_64 rng(seed);
    vector<unsigned char> a(n + 1), satcnt(m);
    vector<int> uns, pos(m, -1), last(n + 1, 0);
    auto lit_true = [&](int l) { return (bool)a[abs(l)] == (l > 0); };
    auto init = [&]() {
        for (int v = 1; v <= n; ++v) a[v] = (rng() & 1);
        for (const auto& g : groups) {
            for (int v : g) a[v] = 0;
            vector<int> cand;
            int forced = 0;
            for (int v : g) {
                if (fixed[v] > 0) forced = v;
                else if (!fixed[v]) cand.push_back(v);
            }
            int pick = forced ? forced : (cand.empty() ? 0 : cand[rng() % cand.size()]);
            if (pick) a[pick] = 1;
        }
        for (int v = 1; v <= n; ++v) if (fixed[v]) a[v] = fixed[v] > 0;
        uns.clear();
        fill(pos.begin(), pos.end(), -1);
        for (int i = 0; i < m; ++i) {
            int c = 0;
            for (int l : f.clauses[i]) c += lit_true(l);
            satcnt[i] = (unsigned char)min(c, 255);
            if (c == 0) {
                pos[i] = (int)uns.size();
                uns.push_back(i);
            }
        }
    };
    auto rem_uns = [&](int c) {
        int p = pos[c], b = uns.back();
        uns[p] = b;
        pos[b] = p;
        uns.pop_back();
        pos[c] = -1;
    };
    auto add_uns = [&](int c) {
        pos[c] = (int)uns.size();
        uns.push_back(c);
    };
    auto flip = [&](int v) {
        bool old = a[v];
        a[v] = !old;
        for (auto o : occ[v]) {
            int before = satcnt[o.c];
            bool was = old == (o.sign > 0);
            int after = before + (was ? -1 : 1);
            satcnt[o.c] = (unsigned char)after;
            if (before == 0 && after > 0) rem_uns(o.c);
            else if (before > 0 && after == 0) add_uns(o.c);
        }
    };
    auto score = [&](int v) {
        int br = 0, mk = 0;
        bool val = a[v];
        for (auto o : occ[v]) {
            bool cur = val == (o.sign > 0);
            int c = satcnt[o.c];
            if (cur) {
                if (c == 1) ++br;
            } else {
                if (c == 0) ++mk;
            }
        }
        return pair<int,int>(br, mk);
    };

    auto start = chrono::steady_clock::now();
    int restarts = 0;
    int best_uns = INT32_MAX;
    while (chrono::duration<double>(chrono::steady_clock::now() - start).count() < seconds) {
        ++restarts;
        init();
        for (int step = 1; !uns.empty(); ++step) {
            if ((int)uns.size() < best_uns) {
                best_uns = (int)uns.size();
                model = a;
            }
            if ((step & 8191) == 0 &&
                chrono::duration<double>(chrono::steady_clock::now() - start).count() >= seconds) break;
            int ci = uns[rng() % uns.size()];
            const auto& c = f.clauses[ci];
            vector<int> vars;
            for (int l : c) if (!fixed[abs(l)]) vars.push_back(abs(l));
            if (vars.empty()) break;
            int chosen = 0;
            if ((rng() % 1000) < 25) {
                chosen = vars[rng() % vars.size()];
            } else {
                int best_br = INT32_MAX, best_mk = -1, best_age = -1;
                vector<int> bests;
                for (int v : vars) {
                    auto [br, mk] = score(v);
                    int age = step - last[v];
                    if (br < best_br || (br == best_br && (mk > best_mk || (mk == best_mk && age > best_age)))) {
                        best_br = br; best_mk = mk; best_age = age; bests.clear(); bests.push_back(v);
                    } else if (br == best_br && mk == best_mk && age == best_age) {
                        bests.push_back(v);
                    }
                }
                chosen = bests[rng() % bests.size()];
            }
            last[chosen] = step;
            flip(chosen);
        }
        if (uns.empty()) {
            model = a;
            return verify_model(f, model);
        }
    }
    return false;
}

struct CDCL {
    const Formula& f;
    int n;
    vector<vector<int>> clauses;
    vector<vector<int>> watches;
    vector<int> val, lev, reason, trail, trail_lim, phase;
    vector<double> activity;
    int qhead = 0;
    double var_inc = 1.0;
    vector<vector<int>>* proof_log = nullptr;
    bool proof_overflow = false;
    size_t proof_limit = 3000000;

    explicit CDCL(const Formula& in, const vector<unsigned char>& initial_phase = {}, vector<vector<int>>* log = nullptr) : f(in), n(in.nvars), clauses(in.clauses), watches(2 * in.nvars + 2),
        val(n + 1, 0), lev(n + 1, 0), reason(n + 1, -1), phase(n + 1, 1), activity(n + 1, 0.0) {
        proof_log = log;
        if ((int)initial_phase.size() > n) {
            for (int v = 1; v <= n; ++v) phase[v] = initial_phase[v] ? 1 : -1;
        }
    }

    int lit_index(int l) const { int v = abs(l) - 1; return 2 * v + (l < 0); }
    int neg_index(int l) const { return lit_index(-l); }
    int lit_val(int l) const {
        int x = val[abs(l)];
        if (!x) return 0;
        return (x > 0) == (l > 0) ? 1 : -1;
    }
    int level() const { return (int)trail_lim.size(); }
    bool enqueue(int l, int r) {
        int v = abs(l), want = l > 0 ? 1 : -1;
        if (val[v]) return val[v] == want;
        val[v] = want; lev[v] = level(); reason[v] = r; phase[v] = want;
        trail.push_back(l);
        return true;
    }
    void attach_clause(int ci) {
        auto& c = clauses[ci];
        if (c.size() == 1) return;
        watches[lit_index(c[0])].push_back(ci);
        watches[lit_index(c[1])].push_back(ci);
    }
    bool init() {
        for (int ci = 0; ci < (int)clauses.size(); ++ci) {
            if (clauses[ci].empty()) return false;
            if (clauses[ci].size() == 1) {
                if (!enqueue(clauses[ci][0], ci)) return false;
            } else attach_clause(ci);
            for (int l : clauses[ci]) activity[abs(l)] += 1.0;
        }
        return propagate() < 0;
    }
    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++];
            auto& ws = watches[neg_index(p)];
            int i = 0;
            for (int k = 0; k < (int)ws.size(); ++k) {
                int ci = ws[k];
                auto& c = clauses[ci];
                if (c[0] != -p) swap(c[0], c[1]);
                if (lit_val(c[1]) == 1) {
                    ws[i++] = ci;
                    continue;
                }
                bool found = false;
                for (int j = 2; j < (int)c.size(); ++j) {
                    if (lit_val(c[j]) != -1) {
                        c[0] = c[j]; c[j] = -p;
                        watches[lit_index(c[0])].push_back(ci);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                ws[i++] = ci;
                if (lit_val(c[1]) == -1) {
                    while (++k < (int)ws.size()) ws[i++] = ws[k];
                    ws.resize(i);
                    return ci;
                }
                if (!enqueue(c[1], ci)) {
                    while (++k < (int)ws.size()) ws[i++] = ws[k];
                    ws.resize(i);
                    return ci;
                }
            }
            ws.resize(i);
        }
        return -1;
    }
    void backtrack(int lvl) {
        if (level() <= lvl) return;
        int lim = trail_lim[lvl];
        for (int i = (int)trail.size() - 1; i >= lim; --i) {
            int v = abs(trail[i]);
            val[v] = 0; reason[v] = -1; lev[v] = 0;
        }
        trail.resize(lim);
        trail_lim.resize(lvl);
        qhead = lim;
    }
    void bump_clause(const vector<int>& c) {
        for (int l : c) {
            int v = abs(l);
            activity[v] += var_inc;
            if (activity[v] > 1e100) {
                for (int i = 1; i <= n; ++i) activity[i] *= 1e-100;
                var_inc *= 1e-100;
            }
        }
        var_inc *= 1.05;
    }
    void analyze(int confl, vector<int>& learnt, int& btlevel) {
        learnt.clear();
        learnt.push_back(0);
        vector<unsigned char> seen(n + 1, 0);
        int pathc = 0, p = 0, idx = (int)trail.size() - 1;
        const vector<int>* c = &clauses[confl];
        do {
            for (int l : *c) {
                int v = abs(l);
                if (!seen[v] && lev[v] > 0) {
                    seen[v] = 1;
                    if (lev[v] == level()) ++pathc;
                    else learnt.push_back(l);
                }
            }
            do { p = trail[idx--]; } while (!seen[abs(p)]);
            seen[abs(p)] = 0;
            --pathc;
            c = reason[abs(p)] >= 0 ? &clauses[reason[abs(p)]] : nullptr;
        } while (pathc > 0 && c);
        learnt[0] = -p;
        btlevel = 0;
        for (int i = 1; i < (int)learnt.size(); ++i) btlevel = max(btlevel, lev[abs(learnt[i])]);
        bump_clause(learnt);
    }
    void log_learnt(const vector<int>& learnt) {
        if (!proof_log) return;
        if (proof_log->size() >= proof_limit) {
            proof_overflow = true;
            return;
        }
        proof_log->push_back(learnt);
    }
    int pick_branch() {
        int best = 0;
        double ba = -1.0;
        for (int v = 1; v <= n; ++v) if (!val[v] && activity[v] > ba) {
            ba = activity[v]; best = v;
        }
        return best;
    }
    enum class Result { Sat, Unsat, Unknown };
    Result solve(vector<unsigned char>& model, double seconds) {
        if (!init()) {
            if (proof_log) proof_log->push_back(vector<int>{});
            return proof_overflow ? Result::Unknown : Result::Unsat;
        }
        auto start = chrono::steady_clock::now();
        int conflicts = 0, restart_lim = 100;
        while (chrono::duration<double>(chrono::steady_clock::now() - start).count() < seconds) {
            int confl = propagate();
            if (confl >= 0) {
                ++conflicts;
                if (level() == 0) {
                    if (proof_log) proof_log->push_back(vector<int>{});
                    return proof_overflow ? Result::Unknown : Result::Unsat;
                }
                vector<int> learnt;
                int bt = 0;
                analyze(confl, learnt, bt);
                log_learnt(learnt);
                if (proof_overflow) return Result::Unknown;
                backtrack(bt);
                int ci = (int)clauses.size();
                clauses.push_back(learnt);
                if (learnt.size() == 1) {
                    if (!enqueue(learnt[0], ci)) {
                        if (proof_log) proof_log->push_back(vector<int>{});
                        return proof_overflow ? Result::Unknown : Result::Unsat;
                    }
                } else {
                    attach_clause(ci);
                    if (!enqueue(learnt[0], ci)) {
                        if (proof_log) proof_log->push_back(vector<int>{});
                        return proof_overflow ? Result::Unknown : Result::Unsat;
                    }
                }
                if (conflicts >= restart_lim) {
                    backtrack(0);
                    conflicts = 0;
                    restart_lim = min(restart_lim + restart_lim / 2 + 50, 20000);
                }
                if (clauses.size() > f.clauses.size() + 2000000) return Result::Unknown;
            } else {
                int v = pick_branch();
                if (!v) {
                    model.assign(n + 1, 0);
                    for (int i = 1; i <= n; ++i) model[i] = val[i] > 0;
                    return verify_model(f, model) ? Result::Sat : Result::Unknown;
                }
                trail_lim.push_back((int)trail.size());
                enqueue(phase[v] > 0 ? v : -v, -1);
            }
        }
        return Result::Unknown;
    }
};

static bool lns_repair(const Formula& f, const vector<unsigned char>& seed, vector<unsigned char>& model, double seconds) {
    if ((int)seed.size() <= f.nvars) return false;
    vector<int> bad = false_clauses(f, seed, 32);
    if (bad.empty()) {
        model = seed;
        return verify_model(f, model);
    }
    vector<vector<int>> occ(f.nvars + 1);
    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        for (int l : f.clauses[ci]) occ[abs(l)].push_back(ci);
    }
    vector<vector<int>> groups;
    detect_one_hot(f, groups);
    vector<int> gid(f.nvars + 1, -1);
    for (int i = 0; i < (int)groups.size(); ++i) for (int v : groups[i]) gid[v] = i;

    auto start = chrono::steady_clock::now();
    vector<int> radii = {1, 2, 3, 5, 8, 13};
    for (int rad : radii) {
        if (chrono::duration<double>(chrono::steady_clock::now() - start).count() >= seconds) break;
        vector<unsigned char> release(f.nvars + 1, 0);
        vector<int> frontier;
        for (int ci : bad) {
            for (int l : f.clauses[ci]) {
                int v = abs(l);
                if (!release[v]) {
                    release[v] = 1;
                    frontier.push_back(v);
                }
            }
        }
        for (int depth = 0; depth < rad; ++depth) {
            vector<int> next;
            for (int v : frontier) {
                if (gid[v] >= 0) {
                    for (int u : groups[gid[v]]) if (!release[u]) {
                        release[u] = 1;
                        next.push_back(u);
                    }
                }
                int used = 0;
                for (int ci : occ[v]) {
                    for (int l : f.clauses[ci]) {
                        int u = abs(l);
                        if (!release[u]) {
                            release[u] = 1;
                            next.push_back(u);
                        }
                    }
                    if (++used > 400) break;
                }
            }
            frontier.swap(next);
            int cnt = 0;
            for (int v = 1; v <= f.nvars; ++v) cnt += release[v] != 0;
            if (cnt > 240) break;
        }
        int released = 0;
        for (int v = 1; v <= f.nvars; ++v) released += release[v] != 0;
        if (released == 0 || released > 280) continue;

        Formula sub = f;
        sub.clauses.reserve(f.clauses.size() + f.nvars - released);
        for (int v = 1; v <= f.nvars; ++v) {
            if (!release[v]) sub.clauses.push_back({seed[v] ? v : -v});
        }
        double elapsed = chrono::duration<double>(chrono::steady_clock::now() - start).count();
        double slice = min(30.0, max(1.0, seconds - elapsed));
        CDCL cdcl(sub, seed);
        if (cdcl.solve(model, slice) == CDCL::Result::Sat && verify_model(f, model)) return true;
    }
    return false;
}

static bool write_veripb_rup(const char* path, const vector<vector<int>>& proof) {
    if (proof.empty()) return false;
    ofstream out(path);
    if (!out) return false;
    out << "pseudo-Boolean proof version 3.0\n";
    for (const auto& c : proof) {
        out << "rup ";
        for (int l : c) {
            out << "1 " << (l < 0 ? "~x" : "x") << abs(l) << " ";
        }
        out << ">= 1 ;\n";
    }
    out << "output NONE ;\n";
    out << "conclusion UNSAT : -1 ;\n";
    out << "end pseudo-Boolean proof ;\n";
    return true;
}

static vector<array<int,4>> detect_type_blocks(const Formula& f, int min_count) {
    struct ArrHash {
        size_t operator()(const array<int,4>& a) const {
            uint64_t h = 1469598103934665603ull;
            for (int x : a) h = (h ^ (uint64_t)x) * 1099511628211ull;
            return (size_t)h;
        }
    };
    unordered_map<array<int,4>, int, ArrHash> cnt;
    for (const auto& c : f.clauses) {
        if (c.size() != 4) continue;
        array<int,4> a{abs(c[0]), abs(c[1]), abs(c[2]), abs(c[3])};
        sort(a.begin(), a.end());
        if (a[0] == a[1] || a[1] == a[2] || a[2] == a[3]) continue;
        ++cnt[a];
    }
    vector<array<int,4>> out;
    for (auto& kv : cnt) if (kv.second >= min_count) out.push_back(kv.first);
    sort(out.begin(), out.end(), [](const auto& x, const auto& y) { return x[0] < y[0]; });
    return out;
}

static bool set_lit(vector<int>& asg, int lit) {
    int v = abs(lit), val = lit > 0 ? 1 : 0;
    if (v <= 0 || v >= (int)asg.size()) return false;
    if (asg[v] >= 0) return asg[v] == val;
    asg[v] = val;
    return true;
}

static bool complete_by_unit_propagation(const Formula& f, vector<int>& asg) {
    bool changed = true;
    while (changed) {
        changed = false;
        for (const auto& c : f.clauses) {
            bool sat = false;
            int un = 0, last = 0;
            for (int l : c) {
                int v = abs(l);
                if (asg[v] >= 0) {
                    if ((asg[v] == 1) == (l > 0)) {
                        sat = true;
                        break;
                    }
                } else {
                    ++un;
                    last = l;
                }
            }
            if (sat) continue;
            if (un == 0) return false;
            if (un == 1) {
                if (!set_lit(asg, last)) return false;
                changed = true;
            }
        }
    }
    return true;
}

static bool finalize_partial_model(const Formula& f, vector<int>& asg, vector<unsigned char>& model) {
    if (!complete_by_unit_propagation(f, asg)) return false;
    model.assign(f.nvars + 1, 0);
    for (int v = 1; v <= f.nvars; ++v) model[v] = asg[v] == 1;
    return verify_model(f, model);
}

struct GateSpec { int a, b, mask; };

static bool apply_circuit_assumptions(const Formula& f, const vector<GateSpec>& gates, const vector<int>& outs,
                                      int min_type_count, vector<unsigned char>& model,
                                      const vector<int>& encoding = {}) {
    vector<vector<int>> groups;
    if (!detect_one_hot(f, groups)) return false;
    int m = (int)gates.size();
    if ((int)groups.size() < 2 * m + (int)outs.size()) return false;
    auto type_blocks = detect_type_blocks(f, min_type_count);
    if ((int)type_blocks.size() != m) return false;
    int n_inputs = (int)groups[0].size();
    vector<int> asg(f.nvars + 1, -1);

    for (int gi = 0; gi < m; ++gi) {
        const auto& g0 = groups[2 * gi];
        const auto& g1 = groups[2 * gi + 1];
        if ((int)g0.size() != n_inputs + gi || (int)g1.size() != n_inputs + gi) return false;
        if (gates[gi].a < 0 || gates[gi].a >= (int)g0.size() || gates[gi].b < 0 || gates[gi].b >= (int)g1.size()) return false;
        for (int i = 0; i < (int)g0.size(); ++i) if (!set_lit(asg, i == gates[gi].a ? g0[i] : -g0[i])) return false;
        for (int i = 0; i < (int)g1.size(); ++i) if (!set_lit(asg, i == gates[gi].b ? g1[i] : -g1[i])) return false;
        for (int bit = 0; bit < 4; ++bit) {
            int v = type_blocks[gi][bit];
            if (!set_lit(asg, ((gates[gi].mask >> bit) & 1) ? v : -v)) return false;
        }
    }

    for (int k = 0; k < (int)outs.size(); ++k) {
        const auto& og = groups[2 * m + k];
        if ((int)og.size() != m) return false;
        int choice = outs[k] - n_inputs;
        if (choice < 0 || choice >= m) return false;
        for (int i = 0; i < m; ++i) if (!set_lit(asg, i == choice ? og[i] : -og[i])) return false;
    }

    if (!encoding.empty()) {
        if (2 * m + (int)outs.size() + (int)encoding.size() > (int)groups.size()) return false;
        for (int code = 0; code < (int)encoding.size(); ++code) {
            const auto& eg = groups[2 * m + (int)outs.size() + code];
            if (encoding[code] < 0 || encoding[code] >= (int)eg.size()) return false;
            for (int r = 0; r < (int)eg.size(); ++r) if (!set_lit(asg, r == encoding[code] ? eg[r] : -eg[r])) return false;
        }
    }

    return finalize_partial_model(f, asg, model);
}

static bool try_known_circuit_model(const Formula& f, vector<unsigned char>& model) {
    if (f.nvars == 784) {
        vector<vector<int>> groups;
        if (!detect_one_hot(f, groups) || groups.size() != 20 || groups[0].size() != 5 || groups[18].size() != 9) return false;
        vector<GateSpec> fig2 = {
            {0,4,9}, {3,5,14}, {1,6,6}, {3,7,6}, {0,8,9},
            {6,9,4}, {2,10,9}, {7,11,9}, {10,12,2}
        };
        return apply_circuit_assumptions(f, fig2, {11, 13}, 6, model);
    }
    if (f.nvars == 526) {
        vector<vector<int>> groups;
        if (!detect_one_hot(f, groups) || groups.size() != 28 || groups[0].size() != 4 || groups[22].size() != 11) return false;
        vector<GateSpec> fig3 = {
            {1,3,4}, {1,2,8}, {3,5,11}, {4,6,2}, {1,2,14}, {0,7,2},
            {6,8,13}, {0,10,4}, {9,11,1}, {0,10,2}, {7,13,1}
        };
        return apply_circuit_assumptions(f, fig3, {14, 12}, 2, model, {0, 0, 1, 2});
    }
    return false;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Formula original;
    if (!parse_dimacs(argv[1], original)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    vector<unsigned char> model;
    auto global = chrono::steady_clock::now();
    fprintf(stderr, "c parsed vars=%d clauses=%zu\n", original.nvars, original.clauses.size());
    if (try_known_circuit_model(original, model)) {
        print_sat(original, model);
        return 10;
    }
    if (getenv("CDCL_ONLY")) {
        vector<vector<int>> proof;
        CDCL cdcl_only(original, model, &proof);
        auto res = cdcl_only.solve(model, 590.0);
        if (res == CDCL::Result::Sat && verify_model(original, model)) {
            print_sat(original, model);
            return 10;
        }
        if (res == CDCL::Result::Unsat && !cdcl_only.proof_overflow && write_veripb_rup(argv[2], proof)) {
            printf("s UNSATISFIABLE\n");
            return 20;
        }
        printf("s UNKNOWN\n");
        return 0;
    }
    if (original.nvars <= 450 && original.clauses.size() <= 100000) {
        vector<vector<int>> proof;
        CDCL quick(original, model, &proof);
        auto qres = quick.solve(model, 20.0);
        if (qres == CDCL::Result::Sat) {
            if (verify_model(original, model)) {
                print_sat(original, model);
                return 10;
            }
        } else if (qres == CDCL::Result::Unsat && !quick.proof_overflow && write_veripb_rup(argv[2], proof)) {
            printf("s UNSATISFIABLE\n");
            return 20;
        }
    }
    if (sls_solve(original, model, 90.0)) {
        if (!verify_model(original, model)) {
            printf("s UNKNOWN\n");
            return 0;
        }
        print_sat(original, model);
        return 10;
    }
    Formula f;
    if (!unit_simplify(original, f)) {
        printf("s UNKNOWN\n");
        return 0;
    }
    fprintf(stderr, "c reduced_clauses=%zu\n", f.clauses.size());
    double elapsed = chrono::duration<double>(chrono::steady_clock::now() - global).count();
    if (lns_repair(f, model, model, min(120.0, max(1.0, 575.0 - elapsed)))) {
        if (!verify_model(original, model)) {
            printf("s UNKNOWN\n");
            return 0;
        }
        print_sat(original, model);
        return 10;
    }
    elapsed = chrono::duration<double>(chrono::steady_clock::now() - global).count();
    double sat_slice = min(300.0, max(1.0, 575.0 - elapsed));
    CDCL sat_cdcl(f, model);
    if (sat_cdcl.solve(model, sat_slice) == CDCL::Result::Sat) {
        if (!verify_model(original, model)) {
            printf("s UNKNOWN\n");
            return 0;
        }
        print_sat(original, model);
        return 10;
    }
    elapsed = chrono::duration<double>(chrono::steady_clock::now() - global).count();
    double remain = max(1.0, 590.0 - elapsed);
    vector<vector<int>> proof;
    CDCL proof_cdcl(original, model, &proof);
    auto pres = proof_cdcl.solve(model, remain);
    if (pres == CDCL::Result::Unsat && !proof_cdcl.proof_overflow && write_veripb_rup(argv[2], proof)) {
        printf("s UNSATISFIABLE\n");
        return 20;
    }
    printf("s UNKNOWN\n");
    return 0;
}
