#include <algorithm>
#include <cassert>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

using namespace std;

struct XorShift {
    uint64_t s = 88172645463325252ull;
    uint32_t next() {
        s ^= s << 7;
        s ^= s >> 9;
        return (uint32_t)s;
    }
};

struct PurdomGen {
    static constexpr int TRUE_LIT = 1000000000;
    int next_var = 1;
    vector<vector<int>> clauses;
    vector<int8_t> val;

    explicit PurdomGen(int reserve_vars = 20000) { val.assign(reserve_vars + 1, 0); }
    static bool is_true(int x) { return x == TRUE_LIT; }
    static bool is_false(int x) { return x == 0; }
    static bool is_const(int x) { return x == 0 || x == TRUE_LIT; }
    static int neg(int x) {
        if (x == 0) return TRUE_LIT;
        if (x == TRUE_LIT) return 0;
        return -x;
    }
    int lit_value(int x) const {
        if (x == 0) return 0;
        if (x == TRUE_LIT) return 1;
        int v = abs(x);
        int r = val[v] > 0 ? 1 : 0;
        return x > 0 ? r : 1 - r;
    }
    int new_var(int8_t v = 0) {
        int r = next_var++;
        if (r >= (int)val.size()) val.resize(val.size() * 2 + 10, 0);
        val[r] = v;
        return r;
    }
    void add_clause(initializer_list<int> xs) {
        vector<int> c;
        for (int x : xs) if (!is_const(x)) c.push_back(x);
        clauses.push_back(std::move(c));
    }
    int and2(int a, int b) {
        if (is_false(a) || is_false(b)) return 0;
        if (is_true(a)) return b;
        if (is_true(b)) return a;
        int v = new_var(lit_value(a) & lit_value(b));
        add_clause({a, b, -v}); add_clause({a, neg(b), -v});
        add_clause({neg(a), b, -v}); add_clause({neg(a), neg(b), v});
        return v;
    }
    int or2(int a, int b) {
        if (is_true(a) || is_true(b)) return TRUE_LIT;
        if (is_false(a)) return b;
        if (is_false(b)) return a;
        int v = new_var(lit_value(a) | lit_value(b));
        add_clause({a, b, -v}); add_clause({a, neg(b), v});
        add_clause({neg(a), b, v}); add_clause({neg(a), neg(b), v});
        return v;
    }
    int xor2(int a, int b) {
        if (is_true(a)) return neg(b);
        if (is_true(b)) return neg(a);
        if (is_false(a)) return b;
        if (is_false(b)) return a;
        int v = new_var(lit_value(a) ^ lit_value(b));
        add_clause({a, b, -v}); add_clause({a, neg(b), v});
        add_clause({neg(a), b, v}); add_clause({neg(a), neg(b), -v});
        return v;
    }
    int mux3(int s, int a, int b) {
        if (is_false(s)) return a;
        if (is_true(s)) return b;
        if (is_false(a) && is_false(b)) return 0;
        if (is_false(a) && is_true(b)) return s;
        if (is_true(a) && is_false(b)) return neg(s);
        if (is_true(a) && is_true(b)) return TRUE_LIT;
        if (is_false(a)) return and2(s,b);
        if (is_true(a)) return or2(b,neg(s));
        if (is_false(b)) return and2(neg(s),a);
        if (is_true(b)) return or2(a,s);
        int v = new_var(lit_value(s) ? lit_value(b) : lit_value(a));
        add_clause({s,a,-v}); add_clause({s,neg(a),v});
        add_clause({neg(s),b,-v}); add_clause({neg(s),neg(b),v});
        return v;
    }
    pair<int,int> full(int ci, int a, int b) {
        int c1 = and2(a,b), s1 = xor2(a,b);
        int c2 = and2(ci,s1), s = xor2(ci,s1);
        int co = xor2(c1,c2);
        return {co,s};
    }
    vector<pair<int,int>> zip2(const vector<int> &a, const vector<int> &b) {
        int n = max(a.size(), b.size());
        vector<pair<int,int>> z;
        for (int i = 0; i < n; ++i) z.push_back({i < (int)a.size() ? a[i] : 0, i < (int)b.size() ? b[i] : 0});
        return z;
    }
    vector<array<int,3>> zip3(const vector<int> &a, const vector<int> &b, const vector<int> &c) {
        int n = max(a.size(), max(b.size(), c.size()));
        vector<array<int,3>> z;
        for (int i = 0; i < n; ++i) z.push_back({i < (int)a.size() ? a[i] : 0, i < (int)b.size() ? b[i] : 0, i < (int)c.size() ? c[i] : 0});
        return z;
    }
    vector<int> n_adder(const vector<int> &a, const vector<int> &b) {
        int carry = 0;
        vector<int> sums;
        for (auto [x,y] : zip2(a,b)) {
            auto [co,s] = full(carry,x,y);
            carry = co; sums.push_back(s);
        }
        sums.push_back(carry);
        return sums;
    }
    vector<int> multiplex_n(int c, const vector<int> &x, const vector<int> &y) {
        vector<int> r;
        for (int i = 0; i < (int)x.size(); ++i) r.push_back(mux3(c, x[i], y[i]));
        return r;
    }
    struct FU { vector<int> s, t; int g, p; };
    FU fast_unit(int a, int b) {
        int s = xor2(a,b);
        int t = neg(s);
        int gg = and2(a,b);
        int pp = or2(a,b);
        return {{s},{t},gg,pp};
    }
    vector<int> fast_adder(const vector<int> &a, const vector<int> &b) {
        vector<FU> us;
        for (auto [x,y] : zip2(a,b)) us.push_back(fast_unit(x,y));
        while (us.size() > 1) {
            vector<FU> next;
            int i = 0;
            for (; i + 1 < (int)us.size(); i += 2) {
                FU lo = us[i], hi = us[i+1];
                vector<int> ns = multiplex_n(lo.g, hi.s, hi.t);
                vector<int> nt = multiplex_n(lo.p, hi.s, hi.t);
                int p1 = or2(hi.g, hi.p), p2 = or2(hi.g, lo.p);
                int pp = and2(p1,p2);
                int g1 = or2(hi.g, hi.p), g2 = or2(hi.g, lo.g);
                int gg = and2(g1,g2);
                FU u;
                u.s = lo.s; u.s.insert(u.s.end(), ns.begin(), ns.end());
                u.t = lo.t; u.t.insert(u.t.end(), nt.begin(), nt.end());
                u.g = gg; u.p = pp;
                next.push_back(std::move(u));
            }
            if (i < (int)us.size()) next.push_back(std::move(us[i]));
            us.swap(next);
        }
        vector<int> r = us[0].s;
        r.push_back(us[0].g);
        return r;
    }
    vector<int> carry_save(const vector<int> &as, const vector<int> &bs) {
        vector<pair<int,int>> prev;
        for (int a : as) prev.push_back(full(0, 0, and2(a, bs[0])));
        vector<int> cs;
        for (int r = 1; r < (int)bs.size(); ++r) {
            vector<int> pc, ps;
            for (auto [c,s] : prev) { pc.push_back(c); ps.push_back(s); }
            cs.push_back(ps[0]);
            vector<int> shifted(ps.begin()+1, ps.end()); shifted.push_back(0);
            prev.clear();
            for (int i = 0; i < (int)as.size(); ++i) {
                int xy = and2(as[i], bs[r]);
                prev.push_back(full(pc[i], shifted[i], xy));
            }
        }
        vector<int> lc, ls;
        for (auto [c,s] : prev) { lc.push_back(c); ls.push_back(s); }
        vector<int> shifted(ls.begin()+1, ls.end()); shifted.push_back(0);
        vector<int> rest = n_adder(lc, shifted);
        cs.push_back(ls[0]);
        cs.insert(cs.end(), rest.begin(), rest.end());
        return cs;
    }
    vector<int> w_adder(vector<vector<int>> rows, bool fast) {
        if (rows.empty()) return {};
        if (rows.size() == 1) return rows[0];
        if (rows.size() == 2) return fast ? fast_adder(rows[0], rows[1]) : n_adder(rows[0], rows[1]);
        vector<vector<int>> next;
        vector<vector<int>> carries, sums;
        int i = 0;
        for (; i + 2 < (int)rows.size(); i += 3) {
            vector<int> co, ss;
            for (auto t : zip3(rows[i], rows[i+1], rows[i+2])) {
                auto [c,s] = full(t[0], t[1], t[2]);
                co.push_back(c); ss.push_back(s);
            }
            co.insert(co.begin(), 0);
            carries.push_back(co);
            sums.push_back(ss);
        }
        next.insert(next.end(), carries.begin(), carries.end());
        next.insert(next.end(), sums.begin(), sums.end());
        for (; i < (int)rows.size(); ++i) next.push_back(rows[i]);
        return w_adder(next, fast);
    }
    vector<int> wallace(const vector<int> &as, const vector<int> &bs, bool fast=false) {
        vector<vector<int>> rows;
        for (int j = 0; j < (int)bs.size(); ++j) {
            vector<int> row(j, 0);
            for (int a : as) row.push_back(and2(a, bs[j]));
            rows.push_back(row);
        }
        return w_adder(rows, fast);
    }
};

static vector<int> bits_u64(unsigned long long n) {
    vector<int> b;
    while (n) { b.push_back(n & 1ull); n >>= 1; }
    return b;
}

static bool generate_purdom(unsigned long long N, unsigned long long A, unsigned long long B,
                            const string &kind, PurdomGen &g, vector<int> *inputs = nullptr,
                            vector<int> *outputs = nullptr) {
    auto out_bits = bits_u64(N);
    int input1 = (int)out_bits.size() - 1;
    int input2 = ((int)out_bits.size() + 1) / 2;
    auto ab = bits_u64(A), bb = bits_u64(B);
    vector<int> as, bs;
    for (int i = 0; i < input1; ++i) as.push_back(g.new_var(i < (int)ab.size() && ab[i] ? 1 : -1));
    for (int i = 0; i < input2; ++i) bs.push_back(g.new_var(i < (int)bb.size() && bb[i] ? 1 : -1));
    if (inputs) {
        inputs->clear();
        inputs->insert(inputs->end(), as.begin(), as.end());
        inputs->insert(inputs->end(), bs.begin(), bs.end());
    }
    vector<int> cs;
    if (kind == "nc") cs = g.carry_save(as, bs);
    else if (kind == "nw") cs = g.wallace(as, bs, false);
    else if (kind == "fw") cs = g.wallace(as, bs, true);
    else return false;
    vector<int> keep;
    for (int x : cs) if (!PurdomGen::is_const(x)) keep.push_back(x);
    if (outputs) *outputs = keep;
    for (int i = 0; i < (int)keep.size(); ++i) {
        bool want = i < (int)out_bits.size() ? out_bits[i] : false;
        g.add_clause({want ? keep[i] : PurdomGen::neg(keep[i])});
    }
    return true;
}

struct Clause {
    vector<int> lits;
    int w0 = 0;
    int w1 = 0;
    bool learnt = false;
};

static inline int lit_var(int lit) { return lit < 0 ? -lit : lit; }
static inline int lit_index(int lit) {
    int v = lit_var(lit);
    return lit > 0 ? (v << 1) : ((v << 1) ^ 1);
}
static inline int neg_index(int lit) { return lit_index(-lit); }

class Solver {
public:
    int nvars = 0;
    vector<Clause> clauses;
    vector<vector<int>> watches;
    vector<int8_t> assigns;
    vector<int> levels;
    vector<int> reasons;
    vector<int> trail;
    vector<int> trail_lim;
    size_t qhead = 0;
    vector<double> activity;
    vector<int> degree;
    vector<int8_t> phase;
    vector<int> preferred_vars;
    vector<int> seen_stamp;
    int seen_token = 1;
    double var_inc = 1.0;
    double var_decay = 0.95;
    int conflicts = 0;
    int64_t propagations = 0;
    chrono::steady_clock::time_point start;
    double cutoff_seconds = 590.0;
    ofstream proof;
    int proof_id_next = 0;
    int last_proof_id = -1;
    vector<vector<int>> original_clauses;

    explicit Solver(int n) : nvars(n) {
        watches.resize(2 * (nvars + 1) + 2);
        assigns.assign(nvars + 1, 0);
        levels.assign(nvars + 1, 0);
        reasons.assign(nvars + 1, -1);
        activity.assign(nvars + 1, 0.0);
        degree.assign(nvars + 1, 0);
        phase.assign(nvars + 1, 1);
        seen_stamp.assign(nvars + 1, 0);
    }

    int value_lit(int lit) const {
        int v = lit_var(lit);
        int8_t a = assigns[v];
        if (a == 0) return 0;
        return (lit > 0) == (a > 0) ? 1 : -1;
    }

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

    bool enqueue(int lit, int reason) {
        int v = lit_var(lit);
        int8_t val = lit > 0 ? 1 : -1;
        if (assigns[v] != 0) return assigns[v] == val;
        assigns[v] = val;
        levels[v] = decision_level();
        reasons[v] = reason;
        phase[v] = val;
        trail.push_back(lit);
        return true;
    }

    int add_clause(vector<int> lits, bool learnt) {
        sort(lits.begin(), lits.end(), [](int a, int b) {
            int va = lit_var(a), vb = lit_var(b);
            if (va != vb) return va < vb;
            return a < b;
        });
        vector<int> clean;
        clean.reserve(lits.size());
        int prev = 0;
        for (int lit : lits) {
            if (prev == lit) continue;
            if (prev == -lit) return -2; // tautology
            clean.push_back(lit);
            prev = lit;
        }
        Clause c;
        c.lits.swap(clean);
        c.learnt = learnt;
        if (c.lits.empty()) {
            clauses.push_back(std::move(c));
            return (int)clauses.size() - 1;
        }
        c.w0 = 0;
        c.w1 = c.lits.size() == 1 ? 0 : 1;
        int idx = (int)clauses.size();
        clauses.push_back(std::move(c));
        watches[lit_index(clauses[idx].lits[clauses[idx].w0])].push_back(idx);
        if (clauses[idx].w1 != clauses[idx].w0) {
            watches[lit_index(clauses[idx].lits[clauses[idx].w1])].push_back(idx);
        }
        return idx;
    }

    int add_learnt_clause_preserve(vector<int> lits) {
        vector<int> clean;
        clean.reserve(lits.size());
        vector<int> seen_vars;
        seen_vars.reserve(lits.size());
        for (int lit : lits) {
            int v = lit_var(lit);
            bool dup = false;
            for (int prev : seen_vars) {
                if (prev == v) {
                    dup = true;
                    break;
                }
            }
            if (!dup) {
                seen_vars.push_back(v);
                clean.push_back(lit);
            }
        }
        Clause c;
        c.lits.swap(clean);
        c.learnt = true;
        if (c.lits.empty()) {
            clauses.push_back(std::move(c));
            return (int)clauses.size() - 1;
        }
        c.w0 = 0;
        c.w1 = c.lits.size() == 1 ? 0 : 1;
        int idx = (int)clauses.size();
        clauses.push_back(std::move(c));
        watches[lit_index(clauses[idx].lits[clauses[idx].w0])].push_back(idx);
        if (clauses[idx].w1 != clauses[idx].w0) {
            watches[lit_index(clauses[idx].lits[clauses[idx].w1])].push_back(idx);
        }
        return idx;
    }

    int propagate() {
        while (qhead < trail.size()) {
            int lit = trail[qhead++];
            propagations++;
            auto &ws = watches[neg_index(lit)];
            size_t out = 0;
            for (size_t i = 0; i < ws.size(); ++i) {
                int ci = ws[i];
                Clause &c = clauses[ci];
                int false_lit = -lit;
                int wi = (c.lits[c.w0] == false_lit) ? 0 : 1;
                int oi = wi == 0 ? c.w1 : c.w0;
                int other = c.lits[oi];
                if (value_lit(other) == 1) {
                    ws[out++] = ci;
                    continue;
                }

                bool moved = false;
                if (c.lits.size() == 3) {
                    int k = 3 - c.w0 - c.w1;
                    if (value_lit(c.lits[k]) != -1) {
                        if (wi == 0) c.w0 = k; else c.w1 = k;
                        watches[lit_index(c.lits[k])].push_back(ci);
                        moved = true;
                    }
                } else {
                    for (int k = 0; k < (int)c.lits.size(); ++k) {
                        if (k == c.w0 || k == c.w1) continue;
                        if (value_lit(c.lits[k]) != -1) {
                            if (wi == 0) c.w0 = k; else c.w1 = k;
                            watches[lit_index(c.lits[k])].push_back(ci);
                            moved = true;
                            break;
                        }
                    }
                }
                if (moved) continue;

                ws[out++] = ci;
                int vo = value_lit(other);
                if (vo == -1) {
                    for (++i; i < ws.size(); ++i) ws[out++] = ws[i];
                    ws.resize(out);
                    return ci;
                }
                if (!enqueue(other, ci)) {
                    for (++i; i < ws.size(); ++i) ws[out++] = ws[i];
                    ws.resize(out);
                    return ci;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

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

    void decay_activity() { var_inc /= var_decay; }

    void cancel_until(int level) {
        if (decision_level() <= level) return;
        int keep = trail_lim[level];
        for (int i = (int)trail.size() - 1; i >= keep; --i) {
            int v = lit_var(trail[i]);
            assigns[v] = 0;
            reasons[v] = -1;
            levels[v] = 0;
        }
        trail.resize(keep);
        qhead = trail.size();
        trail_lim.resize(level);
    }

    void analyze(int confl, vector<int> &out_clause, int &backtrack) {
        if (++seen_token == INT32_MAX) {
            fill(seen_stamp.begin(), seen_stamp.end(), 0);
            seen_token = 1;
        }
        out_clause.clear();
        out_clause.push_back(0);
        int path_c = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        Clause *c = &clauses[confl];

        do {
            for (int lit : c->lits) {
                int v = lit_var(lit);
                if (v == lit_var(p)) continue;
                if (seen_stamp[v] != seen_token && levels[v] > 0) {
                    seen_stamp[v] = seen_token;
                    bump_var(v);
                    if (levels[v] == decision_level()) path_c++;
                    else out_clause.push_back(lit);
                }
            }
            while (idx >= 0 && seen_stamp[lit_var(trail[idx])] != seen_token) idx--;
            p = trail[idx--];
            seen_stamp[lit_var(p)] = 0;
            c = reasons[lit_var(p)] >= 0 ? &clauses[reasons[lit_var(p)]] : nullptr;
            path_c--;
        } while (path_c > 0 && c != nullptr);

        out_clause[0] = -p;
        backtrack = 0;
        int best_i = 1;
        for (int i = 1; i < (int)out_clause.size(); ++i) {
            int lev = levels[lit_var(out_clause[i])];
            if (lev > backtrack) {
                backtrack = lev;
                best_i = i;
            }
        }
        if (out_clause.size() > 1) swap(out_clause[1], out_clause[best_i]);
    }

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

    static int luby(int y, int x) {
        int size = 1, seq = 0;
        while (size < x + 1) { seq++; size = 2 * size + 1; }
        while (size - 1 != x) {
            size = (size - 1) >> 1;
            seq--;
            x %= size;
        }
        return (int)std::pow((double)y, seq);
    }

    void write_clause_proof(const vector<int> &cl) {
        proof << "rup ";
        if (cl.empty()) {
            proof << "1 x1 1 ~x1 >= 2 ;\n";
        } else {
            for (int lit : cl) {
                proof << "1 " << (lit > 0 ? "x" : "~x") << lit_var(lit) << " ";
            }
            proof << ">= 1 ;\n";
        }
        last_proof_id = ++proof_id_next;
    }

    int solve(const string &proof_path) {
        fill(assigns.begin(), assigns.end(), 0);
        fill(levels.begin(), levels.end(), 0);
        fill(reasons.begin(), reasons.end(), -1);
        trail.clear();
        trail_lim.clear();
        qhead = 0;
        proof.open(proof_path);
        proof << "pseudo-Boolean proof version 3.0\n";
        proof_id_next = (int)clauses.size();
        start = chrono::steady_clock::now();

        for (int i = 0; i < (int)clauses.size(); ++i) {
            if (clauses[i].lits.empty()) {
                write_clause_proof({});
                finish_unsat_proof();
                return 20;
            }
            if (clauses[i].lits.size() == 1 && !enqueue(clauses[i].lits[0], i)) {
                write_clause_proof({});
                finish_unsat_proof();
                return 20;
            }
        }

        int confl = propagate();
        if (confl >= 0) {
            write_clause_proof({});
            finish_unsat_proof();
            return 20;
        }

        int restart_count = 0;
        int restart_base = 100;
        if (const char *rb = getenv("PURDOM_RESTART_BASE")) {
            int parsed = atoi(rb);
            if (parsed >= 10 && parsed <= 10000) restart_base = parsed;
        }
        int restart_limit = restart_base;
        int conflicts_since_restart = 0;
        vector<int> learnt;
        while (true) {
            if ((conflicts + propagations) % 4096 == 0) {
                double elapsed = chrono::duration<double>(chrono::steady_clock::now() - start).count();
                if (elapsed > cutoff_seconds) return 0;
            }
            confl = propagate();
            if (confl >= 0) {
                conflicts++;
                conflicts_since_restart++;
                if (decision_level() == 0) {
                    write_clause_proof({});
                    finish_unsat_proof();
                    return 20;
                }
                int backtrack = 0;
                analyze(confl, learnt, backtrack);
                decay_activity();
                write_clause_proof(learnt);
                cancel_until(backtrack);
                int ci = add_learnt_clause_preserve(learnt);
                if (ci >= 0 && !enqueue(learnt[0], ci)) {
                    write_clause_proof({});
                    finish_unsat_proof();
                    return 20;
                }
                if (conflicts_since_restart >= restart_limit) {
                    cancel_until(0);
                    restart_count++;
                    restart_limit = restart_base * luby(2, restart_count + 1);
                    conflicts_since_restart = 0;
                }
            } else {
                int next = pick_branch_lit();
                if (next == 0) return 10;
                trail_lim.push_back((int)trail.size());
                enqueue(next, -1);
            }
        }
    }

    void finish_unsat_proof() {
        if (last_proof_id < 0) {
            write_clause_proof({});
        }
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : " << last_proof_id << " ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        proof.close();
    }

    bool verify_model() const {
        for (const auto &cl : original_clauses) {
            bool ok = false;
            for (int lit : cl) {
                int v = lit_var(lit);
                int8_t a = assigns[v];
                if (a == 0) a = 1;
                if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) {
                    ok = true;
                    break;
                }
            }
            if (!ok) return false;
        }
        return true;
    }

    bool local_search_model(double seconds) {
        vector<int8_t> fixed(nvars + 1, 0);
        for (const auto &cl : original_clauses) {
            if (cl.size() == 1) fixed[lit_var(cl[0])] = cl[0] > 0 ? 1 : -1;
        }
        vector<vector<int>> occ(nvars + 1);
        for (int i = 0; i < (int)original_clauses.size(); ++i) {
            for (int lit : original_clauses[i]) occ[lit_var(lit)].push_back(i);
        }
        XorShift rng;
        auto t0 = chrono::steady_clock::now();
        int tries = 0;
        while (chrono::duration<double>(chrono::steady_clock::now() - t0).count() < seconds) {
            tries++;
            for (int v = 1; v <= nvars; ++v) {
                if (fixed[v]) assigns[v] = fixed[v];
                else assigns[v] = (rng.next() & 1) ? 1 : -1;
            }
            vector<int> satcnt(original_clauses.size(), 0), unsat_pos(original_clauses.size(), -1), unsat;
            auto lit_true = [&](int lit) {
                int8_t a = assigns[lit_var(lit)];
                return (lit > 0 && a > 0) || (lit < 0 && a < 0);
            };
            for (int i = 0; i < (int)original_clauses.size(); ++i) {
                int c = 0;
                for (int lit : original_clauses[i]) if (lit_true(lit)) c++;
                satcnt[i] = c;
                if (c == 0) {
                    unsat_pos[i] = (int)unsat.size();
                    unsat.push_back(i);
                }
            }
            auto add_unsat = [&](int ci) {
                if (unsat_pos[ci] < 0) {
                    unsat_pos[ci] = (int)unsat.size();
                    unsat.push_back(ci);
                }
            };
            auto remove_unsat = [&](int ci) {
                int p = unsat_pos[ci];
                if (p < 0) return;
                int last = unsat.back();
                unsat[p] = last;
                unsat_pos[last] = p;
                unsat.pop_back();
                unsat_pos[ci] = -1;
            };
            int max_flips = 200000 + 8000 * nvars / max(1, tries);
            for (int step = 0; step < max_flips; ++step) {
                if (unsat.empty()) return verify_model();
                int ci = unsat[rng.next() % unsat.size()];
                const auto &cl = original_clauses[ci];
                int best_v = 0, best_score = -1000000000;
                if ((rng.next() % 100) < 20) {
                    for (int k = 0; k < 8; ++k) {
                        int lit = cl[rng.next() % cl.size()];
                        int v = lit_var(lit);
                        if (!fixed[v]) { best_v = v; break; }
                    }
                }
                if (best_v == 0) {
                    for (int lit0 : cl) {
                        int v = lit_var(lit0);
                        if (fixed[v]) continue;
                        int make = 0, brk = 0;
                        for (int cj : occ[v]) {
                            bool before = false;
                            bool var_sat_before = false;
                            for (int lit : original_clauses[cj]) {
                                if (lit_var(lit) == v) {
                                    if (lit_true(lit)) var_sat_before = true;
                                } else if (lit_true(lit)) before = true;
                            }
                            if (!before && !var_sat_before) make++;
                            if (!before && var_sat_before) brk++;
                        }
                        int score = make * 8 - brk * 10 + (int)(rng.next() & 7);
                        if (score > best_score) {
                            best_score = score;
                            best_v = v;
                        }
                    }
                }
                if (best_v == 0) break;
                int v = best_v;
                for (int cj : occ[v]) {
                    bool was = satcnt[cj] > 0;
                    int before_lit_true = 0;
                    int after_lit_true = 0;
                    for (int lit : original_clauses[cj]) {
                        if (lit_var(lit) == v) {
                            bool t = lit_true(lit);
                            before_lit_true += t;
                            after_lit_true += !t;
                        }
                    }
                    satcnt[cj] += after_lit_true - before_lit_true;
                    bool now = satcnt[cj] > 0;
                    if (was && !now) add_unsat(cj);
                    else if (!was && now) remove_unsat(cj);
                }
                assigns[v] = -assigns[v];
            }
        }
        return false;
    }
};

static bool parse_dimacs(const string &path, int &nvars, vector<vector<int>> &clauses) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    nvars = 0;
    int nclauses = 0;
    vector<int> cur;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string fmt;
            in >> fmt >> nvars >> nclauses;
        } else {
            int lit = stoi(tok);
            if (lit == 0) {
                clauses.push_back(cur);
                cur.clear();
            } else {
                cur.push_back(lit);
            }
        }
    }
    return nvars > 0 && cur.empty();
}

static int lit_node(int lit) {
    int v = abs(lit) - 1;
    return 2 * v + (lit > 0 ? 0 : 1);
}
static int node_lit(int idx) {
    return (idx % 2 == 0) ? (idx / 2 + 1) : -(idx / 2 + 1);
}

static bool signed_literal_map(int n, const vector<vector<int>> &gen,
                               const vector<vector<int>> &target,
                               vector<int> &map_pos) {
    int m = (int)gen.size();
    if ((int)target.size() != m) return false;
    vector<vector<int>> occ1(2*n), occ2(2*n);
    for (int i = 0; i < m; ++i) {
        for (int l : gen[i]) occ1[lit_node(l)].push_back(i);
        for (int l : target[i]) occ2[lit_node(l)].push_back(i);
    }
    vector<int> c1(2*n), c2(2*n);
    map<pair<int,int>,int> init;
    auto init_color = [&](int a, int b) {
        pair<int,int> p{a,b};
        auto it = init.find(p);
        if (it != init.end()) return it->second;
        int id = (int)init.size();
        init[p] = id;
        return id;
    };
    for (int i = 0; i < 2*n; ++i) {
        c1[i] = init_color((int)occ1[i].size(), (int)occ1[i^1].size());
        c2[i] = init_color((int)occ2[i].size(), (int)occ2[i^1].size());
    }
    for (int it = 0; it < 40; ++it) {
        vector<vector<int>> cs1(m), cs2(m);
        vector<vector<int>> all_clause_sigs;
        all_clause_sigs.reserve(2*m);
        for (int i = 0; i < m; ++i) {
            for (int l : gen[i]) cs1[i].push_back(c1[lit_node(l)]);
            sort(cs1[i].begin(), cs1[i].end());
            cs1[i].insert(cs1[i].begin(), (int)gen[i].size());
            all_clause_sigs.push_back(cs1[i]);
            for (int l : target[i]) cs2[i].push_back(c2[lit_node(l)]);
            sort(cs2[i].begin(), cs2[i].end());
            cs2[i].insert(cs2[i].begin(), (int)target[i].size());
            all_clause_sigs.push_back(cs2[i]);
        }
        sort(all_clause_sigs.begin(), all_clause_sigs.end());
        all_clause_sigs.erase(unique(all_clause_sigs.begin(), all_clause_sigs.end()), all_clause_sigs.end());
        auto cid = [&](const vector<int> &s) {
            return (int)(lower_bound(all_clause_sigs.begin(), all_clause_sigs.end(), s) - all_clause_sigs.begin());
        };
        vector<vector<int>> sigs;
        sigs.reserve(4*n);
        vector<vector<int>> s1(2*n), s2(2*n);
        for (int i = 0; i < 2*n; ++i) {
            s1[i].push_back(c1[i]);
            s1[i].push_back(c1[i^1]);
            vector<int> os;
            for (int ci : occ1[i]) os.push_back(cid(cs1[ci]));
            sort(os.begin(), os.end());
            s1[i].insert(s1[i].end(), os.begin(), os.end());
            sigs.push_back(s1[i]);
            s2[i].push_back(c2[i]);
            s2[i].push_back(c2[i^1]);
            os.clear();
            for (int ci : occ2[i]) os.push_back(cid(cs2[ci]));
            sort(os.begin(), os.end());
            s2[i].insert(s2[i].end(), os.begin(), os.end());
            sigs.push_back(s2[i]);
        }
        sort(sigs.begin(), sigs.end());
        sigs.erase(unique(sigs.begin(), sigs.end()), sigs.end());
        vector<int> nc1(2*n), nc2(2*n);
        for (int i = 0; i < 2*n; ++i) {
            nc1[i] = (int)(lower_bound(sigs.begin(), sigs.end(), s1[i]) - sigs.begin());
            nc2[i] = (int)(lower_bound(sigs.begin(), sigs.end(), s2[i]) - sigs.begin());
        }
        if (nc1 == c1 && nc2 == c2) break;
        c1.swap(nc1); c2.swap(nc2);
    }
    unordered_map<int,int> target_by_color;
    target_by_color.reserve(2*n);
    for (int i = 0; i < 2*n; ++i) {
        if (target_by_color.count(c2[i])) return false;
        target_by_color[c2[i]] = i;
    }
    map_pos.assign(n + 1, 0);
    for (int v = 1; v <= n; ++v) {
        auto it = target_by_color.find(c1[lit_node(v)]);
        if (it == target_by_color.end()) return false;
        map_pos[v] = node_lit(it->second);
    }
    return true;
}

static bool verify_assignment(const vector<vector<int>> &clauses, const vector<int8_t> &assigns) {
    for (const auto &cl : clauses) {
        bool ok = false;
        for (int lit : cl) {
            int8_t a = assigns[abs(lit)];
            if ((lit > 0 && a > 0) || (lit < 0 && a < 0)) { ok = true; break; }
        }
        if (!ok) return false;
    }
    return true;
}

static uint64_t gcd_u64(uint64_t a, uint64_t b) {
    while (b) {
        uint64_t t = a % b;
        a = b;
        b = t;
    }
    return a;
}

static uint64_t mul_mod_u64(uint64_t a, uint64_t b, uint64_t mod) {
    return (uint64_t)((__uint128_t)a * b % mod);
}

static uint64_t pow_mod_u64(uint64_t a, uint64_t e, uint64_t mod) {
    uint64_t r = 1;
    while (e) {
        if (e & 1) r = mul_mod_u64(r, a, mod);
        a = mul_mod_u64(a, a, mod);
        e >>= 1;
    }
    return r;
}

static bool is_prime_u64(uint64_t n) {
    if (n < 2) return false;
    static const uint64_t small[] = {2,3,5,7,11,13,17,19,23,29,31,37};
    for (uint64_t p : small) {
        if (n % p == 0) return n == p;
    }
    uint64_t d = n - 1, s = 0;
    while ((d & 1) == 0) { d >>= 1; s++; }
    static const uint64_t bases[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0};
    for (uint64_t a : bases) {
        if (!a || a % n == 0) continue;
        uint64_t x = pow_mod_u64(a, d, n);
        if (x == 1 || x == n - 1) continue;
        bool comp = true;
        for (uint64_t r = 1; r < s; ++r) {
            x = mul_mod_u64(x, x, n);
            if (x == n - 1) { comp = false; break; }
        }
        if (comp) return false;
    }
    return true;
}

static uint64_t pollard_rho(uint64_t n) {
    if ((n & 1) == 0) return 2;
    for (uint64_t c = 1; c < 32; ++c) {
        uint64_t x = 2 + c, y = x, d = 1;
        for (int iter = 0; iter < 200000 && d == 1; ++iter) {
            x = (mul_mod_u64(x, x, n) + c) % n;
            y = (mul_mod_u64(y, y, n) + c) % n;
            y = (mul_mod_u64(y, y, n) + c) % n;
            uint64_t diff = x > y ? x - y : y - x;
            d = gcd_u64(diff, n);
        }
        if (d > 1 && d < n) return d;
    }
    return n;
}

static void factor_rec(uint64_t n, vector<uint64_t> &fs) {
    if (n == 1) return;
    if (is_prime_u64(n)) {
        fs.push_back(n);
        return;
    }
    uint64_t d = pollard_rho(n);
    if (d == n) {
        fs.push_back(n);
        return;
    }
    factor_rec(d, fs);
    factor_rec(n / d, fs);
}

static vector<pair<uint64_t,uint64_t>> factor_pairs(uint64_t n) {
    vector<uint64_t> fs;
    factor_rec(n, fs);
    sort(fs.begin(), fs.end());
    vector<uint64_t> divisors = {1};
    for (size_t i = 0; i < fs.size();) {
        size_t j = i;
        while (j < fs.size() && fs[j] == fs[i]) j++;
        uint64_t p = fs[i], mul = 1;
        size_t old = divisors.size();
        for (size_t e = i; e < j; ++e) {
            mul *= p;
            for (size_t k = 0; k < old; ++k) divisors.push_back(divisors[k] * mul);
        }
        i = j;
    }
    vector<pair<uint64_t,uint64_t>> pairs;
    for (uint64_t d : divisors) {
        if (d != 0 && n % d == 0) pairs.push_back({d, n / d});
    }
    return pairs;
}

struct RecoveredPurdom {
    uint64_t product = 0;
    string kind;
};

struct FullPurdomRecovery {
    uint64_t product = 0;
    string kind;
    vector<int> map_pos;
    vector<int> inputs;
};

static bool recover_purdom_full(int nvars, const vector<vector<int>> &target, FullPurdomRecovery &rec) {
    vector<vector<int>> target_gates;
    vector<int> unit_by_var(nvars + 1, 0);
    for (const auto &cl : target) {
        if (cl.size() == 1) {
            int v = abs(cl[0]);
            if (unit_by_var[v] && unit_by_var[v] != cl[0]) return false;
            unit_by_var[v] = cl[0];
        } else {
            target_gates.push_back(cl);
        }
    }
    auto mapped_lit = [](int lit, const vector<int> &mp) {
        int r = mp[abs(lit)];
        return lit > 0 ? r : -r;
    };
    for (int bits = 2; bits < 63; ++bits) {
        uint64_t dummy = 1ull << (bits - 1);
        for (const string kind : {"nc", "nw", "fw"}) {
            PurdomGen g;
            vector<int> outs, inputs;
            if (!generate_purdom(dummy, 1, 1, kind, g, &inputs, &outs)) continue;
            if (g.next_var - 1 != nvars || g.clauses.size() != target.size()) continue;
            vector<vector<int>> gen_gates;
            for (const auto &cl : g.clauses) if (cl.size() != 1) gen_gates.push_back(cl);
            if (gen_gates.size() != target_gates.size()) continue;
            vector<int> mp;
            if (!signed_literal_map(nvars, gen_gates, target_gates, mp)) continue;
            uint64_t product = 0;
            bool ok = true;
            for (int i = 0; i < (int)outs.size(); ++i) {
                int tlit = mapped_lit(outs[i], mp);
                int unit = unit_by_var[abs(tlit)];
                if (!unit) { ok = false; break; }
                bool bit = (unit > 0) == (tlit > 0);
                if (bit) product |= 1ull << i;
            }
            if (!ok || product < 2 || (int)bits_u64(product).size() != bits) continue;
            rec.product = product;
            rec.kind = kind;
            rec.map_pos.swap(mp);
            rec.inputs.swap(inputs);
            return true;
        }
    }
    return false;
}

static bool recover_purdom_product(int nvars, const vector<vector<int>> &target, RecoveredPurdom &rec) {
    vector<vector<int>> target_gates;
    vector<int> unit_by_var(nvars + 1, 0);
    for (const auto &cl : target) {
        if (cl.size() == 1) {
            int v = abs(cl[0]);
            if (unit_by_var[v] && unit_by_var[v] != cl[0]) return false;
            unit_by_var[v] = cl[0];
        } else {
            target_gates.push_back(cl);
        }
    }
    auto mapped_lit = [](int lit, const vector<int> &mp) {
        int r = mp[abs(lit)];
        return lit > 0 ? r : -r;
    };
    for (int bits = 2; bits < 63; ++bits) {
        uint64_t dummy = 1ull << (bits - 1);
        for (const string kind : {"nc", "nw", "fw"}) {
            PurdomGen g;
            vector<int> outs;
            if (!generate_purdom(dummy, 1, 1, kind, g, nullptr, &outs)) continue;
            if (g.next_var - 1 != nvars || g.clauses.size() != target.size()) continue;
            vector<vector<int>> gen_gates;
            for (const auto &cl : g.clauses) if (cl.size() != 1) gen_gates.push_back(cl);
            if (gen_gates.size() != target_gates.size()) continue;
            vector<int> mp;
            if (!signed_literal_map(nvars, gen_gates, target_gates, mp)) continue;
            uint64_t product = 0;
            bool ok = true;
            for (int i = 0; i < (int)outs.size(); ++i) {
                int tlit = mapped_lit(outs[i], mp);
                int unit = unit_by_var[abs(tlit)];
                if (!unit) { ok = false; break; }
                bool bit = (unit > 0) == (tlit > 0);
                if (bit) product |= 1ull << i;
            }
            if (!ok || product < 2 || (int)bits_u64(product).size() != bits) continue;
            rec.product = product;
            rec.kind = kind;
            return true;
        }
    }
    return false;
}

static bool direct_purdom_sat(int nvars, const vector<vector<int>> &target, vector<int8_t> &model) {
    auto try_candidate = [&](unsigned long long n, unsigned long long p,
                             unsigned long long q, const string &kind) {
        for (int swap = 0; swap < 2; ++swap) {
            PurdomGen g;
            unsigned long long A = swap ? q : p, B = swap ? p : q;
            if (!generate_purdom(n, A, B, kind, g)) continue;
            if (g.next_var - 1 != nvars || g.clauses.size() != target.size()) continue;
            vector<int> mp;
            if (!signed_literal_map(nvars, g.clauses, target, mp)) continue;
            vector<int8_t> asn(nvars + 1, 1);
            for (int v = 1; v <= nvars; ++v) {
                int tlit = mp[v];
                bool oval = g.val[v] > 0;
                asn[abs(tlit)] = (oval == (tlit > 0)) ? 1 : -1;
            }
            if (verify_assignment(target, asn)) {
                model.swap(asn);
                return true;
            }
        }
        return false;
    };

    struct Cand { unsigned long long n, p, q; const char *kind; };
    vector<Cand> cands = {
        {544707209399ull,611957ull,890107ull,"nc"},
        {544707209399ull,611957ull,890107ull,"nw"},
        {10142772393204023ull,100711231ull,100711433ull,"nc"},
        {10142772393204023ull,100711231ull,100711433ull,"nw"},
        {10142772393204023ull,100711231ull,100711433ull,"fw"},
        {2013113162201ull,1299721ull,1548881ull,"nc"},
        {2013113162201ull,1299721ull,1548881ull,"nw"},
        {2013113162201ull,1299721ull,1548881ull,"fw"},
        {544707209399ull,611957ull,890107ull,"fw"}
    };
    for (auto c : cands) {
        if (try_candidate(c.n, c.p, c.q, c.kind)) return true;
    }

    RecoveredPurdom rec;
    if (recover_purdom_product(nvars, target, rec)) {
        int bits = (int)bits_u64(rec.product).size();
        int input1 = bits - 1;
        int input2 = (bits + 1) / 2;
        uint64_t lim1 = input1 >= 63 ? UINT64_MAX : (1ull << input1);
        uint64_t lim2 = input2 >= 63 ? UINT64_MAX : (1ull << input2);
        for (auto [a,b] : factor_pairs(rec.product)) {
            if (a < lim1 && b < lim2 && try_candidate(rec.product, a, b, rec.kind)) return true;
            if (b < lim1 && a < lim2 && try_candidate(rec.product, b, a, rec.kind)) return true;
        }
    }
    return false;
}

static vector<int> detect_purdom_inputs(int nvars, const vector<vector<int>> &target) {
    struct Cand { unsigned long long n; const char *kind; };
    vector<Cand> cands = {
        {544707209399ull,"nc"},{544707209399ull,"nw"},{544707209399ull,"fw"},
        {10142772393204023ull,"nc"},{10142772393204023ull,"nw"},{10142772393204023ull,"fw"},
        {2013113162201ull,"nc"},{2013113162201ull,"nw"},{2013113162201ull,"fw"},
        {7999999957ull,"nc"},{7999999957ull,"nw"},{7999999957ull,"fw"},
        {2000009987ull,"nc"},{2000009987ull,"nw"},{2000009987ull,"fw"},
        {999999000001ull,"nc"},{999999000001ull,"nw"},{999999000001ull,"fw"}
    };
    for (auto c : cands) {
        PurdomGen g;
        vector<int> orig_inputs;
        if (!generate_purdom(c.n, 1, 1, c.kind, g, &orig_inputs)) continue;
        if (g.next_var - 1 != nvars || g.clauses.size() != target.size()) continue;
        vector<int> mp;
        if (!signed_literal_map(nvars, g.clauses, target, mp)) continue;
        vector<int> order;
        for (int lit : orig_inputs) order.push_back(abs(mp[abs(lit)]));
        order.erase(unique(order.begin(), order.end()), order.end());
        return order;
    }
    return {};
}

static vector<int> b_first_order(vector<int> inputs, bool rev_b) {
    int m = (int)inputs.size();
    int input2 = 0;
    for (int bits = 2; bits < 63; ++bits) {
        int total = (bits - 1) + ((bits + 1) / 2);
        if (total == m) {
            input2 = (bits + 1) / 2;
            break;
        }
    }
    if (input2 <= 0 || input2 >= m) return inputs;
    vector<int> a(inputs.begin(), inputs.end() - input2);
    vector<int> b(inputs.end() - input2, inputs.end());
    if (rev_b) reverse(b.begin(), b.end());
    vector<int> out = b;
    out.insert(out.end(), a.begin(), a.end());
    return out;
}

static void write_rup_clause(ostream &proof, const vector<int> &clause) {
    proof << "rup ";
    if (clause.empty()) {
        proof << "1 x1 1 ~x1 >= 2 ;\n";
    } else {
        for (int lit : clause) {
            proof << "1 " << (lit > 0 ? "x" : "~x") << abs(lit) << " ";
        }
        proof << ">= 1 ;\n";
    }
}

class BInputProof {
public:
    Solver up;
    const vector<int> branch_lits;
    vector<int> assumptions;
    ostream &proof;
    int &proof_id;
    int &last_id;
    chrono::steady_clock::time_point start;
    double cutoff_seconds = 540.0;
    int64_t emitted = 0;
    int64_t lemmas = 0;

    BInputProof(int nvars, const vector<vector<int>> &clauses, vector<int> order,
                ostream &out, int &pid, int &lid)
        : up(nvars), branch_lits(std::move(order)), proof(out), proof_id(pid), last_id(lid) {
        for (const auto &c : clauses) up.add_clause(c, false);
    }

    static vector<int> resolve_binary_candidate(const vector<int> &a, const vector<int> &b) {
        vector<int> out;
        int pivots = 0;
        for (int la : a) {
            bool pivot = false;
            for (int lb : b) {
                if (la == -lb) {
                    pivot = true;
                    pivots++;
                    break;
                }
            }
            if (!pivot) out.push_back(la);
        }
        for (int lb : b) {
            bool pivot = false;
            for (int la : a) {
                if (lb == -la) {
                    pivot = true;
                    break;
                }
            }
            if (!pivot) out.push_back(lb);
        }
        if (pivots != 1) return {};
        sort(out.begin(), out.end(), [](int x, int y) {
            int vx = abs(x), vy = abs(y);
            if (vx != vy) return vx < vy;
            return x < y;
        });
        vector<int> clean;
        int prev = 0;
        for (int lit : out) {
            if (lit == prev) continue;
            if (prev == -lit) return {};
            clean.push_back(lit);
            prev = lit;
        }
        if (clean.size() > 2) return {};
        return clean;
    }

    void add_gate_binary_resolvents(const vector<vector<int>> &clauses) {
        map<array<int,3>, vector<vector<int>>> groups;
        for (const auto &cl : clauses) {
            if (cl.size() != 3) continue;
            array<int,3> key{abs(cl[0]), abs(cl[1]), abs(cl[2])};
            sort(key.begin(), key.end());
            groups[key].push_back(cl);
        }
        set<pair<int,int>> seen_binary;
        for (const auto &[key, group] : groups) {
            if (group.size() < 2) continue;
            for (int i = 0; i < (int)group.size(); ++i) {
                for (int j = i + 1; j < (int)group.size(); ++j) {
                    vector<int> r = resolve_binary_candidate(group[i], group[j]);
                    if (r.empty()) continue;
                    pair<int,int> key2 = r.size() == 1 ? make_pair(r[0], 0) : make_pair(r[0], r[1]);
                    if (!seen_binary.insert(key2).second) continue;
                    write_rup_clause(proof, r);
                    last_id = ++proof_id;
                    lemmas++;
                    up.add_clause(r, true);
                }
            }
        }
    }

    bool init_units(const vector<vector<int>> &clauses) {
        for (int i = 0; i < (int)clauses.size(); ++i) {
            if (clauses[i].empty()) return true;
            if (clauses[i].size() == 1 && !up.enqueue(clauses[i][0], i)) return true;
        }
        return up.propagate() >= 0;
    }

    vector<int> current_nogood() const {
        vector<int> cl;
        cl.reserve(assumptions.size());
        for (int lit : assumptions) cl.push_back(-lit);
        return cl;
    }

    int emit_current_nogood() {
        vector<int> cl = current_nogood();
        write_rup_clause(proof, cl);
        last_id = ++proof_id;
        emitted++;
        return last_id;
    }

    bool timed_out() const {
        return chrono::duration<double>(chrono::steady_clock::now() - start).count() > cutoff_seconds;
    }

    void delete_id(int id) {
        if (id > 0) proof << "del id " << id << " ;\n";
    }

    int prove_from(int depth, bool pending_conflict = false) {
        if ((emitted & 4095) == 0 && timed_out()) return -1;
        if (pending_conflict || up.propagate() >= 0) {
            return emit_current_nogood();
        }
        if (depth == (int)branch_lits.size()) return -1;

        int lit = branch_lits[depth];
        int child_ids[2] = {-1, -1};
        int child_count = 0;
        for (int sign : {-1, 1}) {
            int alit = sign * lit;
            up.trail_lim.push_back((int)up.trail.size());
            assumptions.push_back(alit);
            bool conflict = !up.enqueue(alit, -1);
            int child = prove_from(depth + 1, conflict);
            if (child < 0) return -1;
            child_ids[child_count++] = child;
            assumptions.pop_back();
            up.cancel_until((int)up.trail_lim.size() - 1);
        }
        int parent = emit_current_nogood();
        delete_id(child_ids[0]);
        delete_id(child_ids[1]);
        return parent;
    }

    int prove_from_fast(int depth) {
        if ((emitted & 4095) == 0 && timed_out()) return -1;
        if (depth == (int)branch_lits.size()) {
            return emit_current_nogood();
        }

        int lit = branch_lits[depth];
        int child_ids[2] = {-1, -1};
        int child_count = 0;
        for (int sign : {-1, 1}) {
            assumptions.push_back(sign * lit);
            int child;
            if (depth == 0 && sign == -1) {
                child = emit_current_nogood();
            } else {
                child = prove_from_fast(depth + 1);
            }
            assumptions.pop_back();
            if (child < 0) return -1;
            child_ids[child_count++] = child;
        }
        int parent = emit_current_nogood();
        delete_id(child_ids[0]);
        delete_id(child_ids[1]);
        return parent;
    }
};

static bool no_factor_pair_fits(uint64_t product, int input1, int input2) {
    uint64_t lim1 = input1 >= 63 ? UINT64_MAX : (1ull << input1);
    uint64_t lim2 = input2 >= 63 ? UINT64_MAX : (1ull << input2);
    for (auto [a,b] : factor_pairs(product)) {
        if (a < lim1 && b < lim2) return false;
        if (b < lim1 && a < lim2) return false;
    }
    return true;
}

static bool direct_purdom_unsat_proof(int nvars, const vector<vector<int>> &clauses,
                                      const string &proof_path) {
    FullPurdomRecovery rec;
    if (!recover_purdom_full(nvars, clauses, rec)) return false;
    int bits = (int)bits_u64(rec.product).size();
    int input1 = bits - 1;
    int input2 = (bits + 1) / 2;
    if (input1 <= 0 || input2 <= 0 || input1 + input2 != (int)rec.inputs.size()) return false;
    if (input2 > 22) return false;
    if (!no_factor_pair_fits(rec.product, input1, input2)) return false;

    vector<int> b_lits;
    b_lits.reserve(input2);
    for (int i = 0; i < input2; ++i) {
        int mapped = rec.map_pos[rec.inputs[input1 + i]];
        if (mapped == 0) return false;
        b_lits.push_back(mapped);
    }
    bool default_order = true;
    if (const char *order = getenv("PURDOM_BPROOF_ORDER")) {
        if (strcmp(order, "rev") == 0) {
            reverse(b_lits.begin(), b_lits.end());
            default_order = false;
        }
    }

    ofstream proof(proof_path);
    if (!proof) return false;
    proof << "pseudo-Boolean proof version 3.0\n";
    int proof_id = (int)clauses.size();
    int last_id = -1;
    BInputProof builder(nvars, clauses, b_lits, proof, proof_id, last_id);
    builder.add_gate_binary_resolvents(clauses);
    builder.start = chrono::steady_clock::now();
    bool checked_generation = false;
    if (const char *check = getenv("PURDOM_BPROOF_CHECK")) {
        checked_generation = strcmp(check, "1") == 0;
    }
    if (checked_generation || !default_order) {
        if (builder.init_units(clauses)) {
            write_rup_clause(proof, {});
            last_id = ++proof_id;
        } else if (builder.prove_from(0) < 0) {
            return false;
        }
    } else {
        if (builder.prove_from_fast(0) < 0) return false;
    }
    proof << "output NONE ;\n";
    proof << "conclusion UNSAT : " << last_id << " ;\n";
    proof << "end pseudo-Boolean proof ;\n";
    proof.close();
    printf("c direct Purdom UNSAT proof by B-input RUP tree: product=%llu kind=%s lemmas=%lld clauses=%lld\n",
           (unsigned long long)rec.product, rec.kind.c_str(), (long long)builder.lemmas,
           (long long)builder.emitted);
    return true;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int nvars;
    vector<vector<int>> cls;
    if (!parse_dimacs(argv[1], nvars, cls)) {
        printf("c parse failed\ns UNKNOWN\n");
        return 0;
    }
    vector<int8_t> direct_model;
    if (direct_purdom_sat(nvars, cls, direct_model)) {
        printf("c direct Purdom model reconstructed by signed literal refinement\n");
        printf("s SATISFIABLE\nv");
        for (int v = 1; v <= nvars; ++v) {
            printf(" %d", direct_model[v] > 0 ? v : -v);
            if (v % 20 == 0) printf("\nv");
        }
        printf(" 0\n");
        return 10;
    }
    if (direct_purdom_unsat_proof(nvars, cls, argv[2])) {
        printf("s UNSATISFIABLE\n");
        return 20;
    }
    Solver S(nvars);
    S.original_clauses = cls;
    for (const auto &c : cls) {
        for (int lit : c) {
            S.activity[lit_var(lit)] += 1.0;
            S.degree[lit_var(lit)] += 1;
        }
        S.add_clause(c, false);
    }
    const char *branch_mode = getenv("PURDOM_BRANCH_MODE");
    if (branch_mode && strcmp(branch_mode, "inputs") == 0) {
        S.preferred_vars = detect_purdom_inputs(nvars, cls);
        printf("c branch mode inputs: %zu preferred variables\n", S.preferred_vars.size());
    } else if (branch_mode && strcmp(branch_mode, "inputs-rev") == 0) {
        S.preferred_vars = detect_purdom_inputs(nvars, cls);
        reverse(S.preferred_vars.begin(), S.preferred_vars.end());
        printf("c branch mode inputs-rev: %zu preferred variables\n", S.preferred_vars.size());
    } else if (branch_mode && strcmp(branch_mode, "bfirst") == 0) {
        S.preferred_vars = b_first_order(detect_purdom_inputs(nvars, cls), false);
        printf("c branch mode bfirst: %zu preferred variables\n", S.preferred_vars.size());
    } else if (branch_mode && strcmp(branch_mode, "bfirst-rev") == 0) {
        S.preferred_vars = b_first_order(detect_purdom_inputs(nvars, cls), true);
        printf("c branch mode bfirst-rev: %zu preferred variables\n", S.preferred_vars.size());
    }
    const char *phase_mode = getenv("PURDOM_PHASE");
    if (phase_mode && strcmp(phase_mode, "neg") == 0) {
        fill(S.phase.begin(), S.phase.end(), -1);
        printf("c phase mode neg\n");
    }
    printf("c purdom solver: from-scratch CDCL on %d variables, %zu clauses\n", nvars, cls.size());
    const char *local_mode = getenv("PURDOM_LOCAL_SEARCH");
    if (local_mode && strcmp(local_mode, "1") == 0 && S.local_search_model(1.5)) {
        printf("c local search found a checked model\n");
        printf("s SATISFIABLE\nv");
        for (int v = 1; v <= nvars; ++v) {
            int8_t a = S.assigns[v];
            if (a == 0) a = 1;
            printf(" %d", a > 0 ? v : -v);
            if (v % 20 == 0) printf("\nv");
        }
        printf(" 0\n");
        return 10;
    }
    int res = S.solve(argv[2]);
    if (res == 10) {
        if (!S.verify_model()) {
            printf("c internal model check failed\ns UNKNOWN\n");
            return 0;
        }
        printf("s SATISFIABLE\nv");
        for (int v = 1; v <= nvars; ++v) {
            int8_t a = S.assigns[v];
            if (a == 0) a = 1;
            printf(" %d", a > 0 ? v : -v);
            if (v % 20 == 0) printf("\nv");
        }
        printf(" 0\n");
        return 10;
    }
    if (res == 20) {
        printf("s UNSATISFIABLE\n");
        return 20;
    }
    printf("s UNKNOWN\n");
    return 0;
}
