#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct Leaf {
    vector<int> vars;
    int c = 0; // XOR value: xor(vars) == c
    map<uint64_t, int> mask_to_clause;
};

struct BinaryEq {
    vector<int> vars;
    int c = 0; // XOR value: xor(vars) == c
    map<uint64_t, int> mask_to_clause;
};

struct Node {
    vector<int> vars;
    int c = 0;
    int left = -1;
    int right = -1;
    int leaf = -1;
};

struct XorConflict {
    vector<int> leaf_indices;
    vector<int> binary_indices;
    int c = 0;
};

static uint64_t parity64(uint64_t x) { return __builtin_popcountll(x) & 1ULL; }

static string lit_term(int var, int bit) {
    return string("+1 ") + (bit ? "~x" : "x") + to_string(var);
}

static string var_name(int var) {
    return string("x") + to_string(var);
}

static string lit_token(const string& var, bool neg) {
    return neg ? string("~") + var : var;
}

static string lit_token_x(int var, bool neg) {
    return lit_token(var_name(var), neg);
}

static string opb_term(const string& var, bool neg, int coeff = 1) {
    return string("+") + to_string(coeff) + " " + lit_token(var, neg);
}

static string opb_term_x(int var, bool neg, int coeff = 1) {
    return opb_term(var_name(var), neg, coeff);
}

static string constraint_from_mask(const vector<int>& vars, uint64_t mask) {
    string s;
    for (size_t i = 0; i < vars.size(); ++i) {
        if (!s.empty()) s += " ";
        s += lit_term(vars[i], (mask >> i) & 1ULL);
    }
    if (s.empty()) s = "";
    s += " >= 1";
    return s;
}

static int find_pos(const vector<int>& vars, int v) {
    auto it = lower_bound(vars.begin(), vars.end(), v);
    if (it == vars.end() || *it != v) return -1;
    return int(it - vars.begin());
}

static vector<int> symdiff_vec(const vector<int>& a, const vector<int>& b) {
    vector<int> out;
    set_symmetric_difference(a.begin(), a.end(), b.begin(), b.end(), back_inserter(out));
    return out;
}

static vector<int> inter_vec(const vector<int>& a, const vector<int>& b) {
    vector<int> out;
    set_intersection(a.begin(), a.end(), b.begin(), b.end(), back_inserter(out));
    return out;
}

struct Solver {
    int nvars = 0, nclauses = 0;
    vector<Clause> clauses; // 1-based externally
    vector<Leaf> leaves;
    vector<BinaryEq> binary_eqs;
    vector<Node> nodes;
    vector<pair<int, vector<int>>> binaries;
    int next_id = 0;
    ofstream proof;
    map<pair<int, uint64_t>, int> memo;
    bool proof_too_large = false;
    static constexpr int MAX_DERIVED = 30000000;
    int global_c = 0;
    int fail_code = 0;

    bool parse(const string& path) {
        ifstream in(path);
        if (!in) return false;
        string tok;
        while (in >> tok) {
            if (tok == "c") {
                string rest;
                getline(in, rest);
            } else if (tok == "p") {
                string cnf;
                in >> cnf >> nvars >> nclauses;
                if (cnf != "cnf") return false;
            } else if (tok == "%") {
                break;
            } else {
                int lit = stoi(tok);
                vector<int> lits;
                while (lit != 0) {
                    lits.push_back(lit);
                    if (!(in >> lit)) return false;
                }
                clauses.push_back({lits});
            }
        }
        if (nclauses == 0) nclauses = (int)clauses.size();
        return nclauses == (int)clauses.size() && nvars > 0;
    }

    bool detect() {
        fail_code = 0;
        map<vector<int>, vector<pair<int, vector<int>>>> tern;
        map<vector<int>, vector<pair<int, vector<int>>>> bin;
        for (int i = 0; i < (int)clauses.size(); ++i) {
            const auto& c = clauses[i].lits;
            vector<int> key;
            for (int l : c) key.push_back(abs(l));
            sort(key.begin(), key.end());
            if (adjacent_find(key.begin(), key.end()) != key.end()) { fail_code = 2; return false; }
            if (c.size() == 3) tern[key].push_back({i + 1, c});
            else if (c.size() == 2) bin[key].push_back({i + 1, c});
            else { fail_code = 1; return false; }
        }
        if (tern.empty()) { fail_code = 3; return false; }
        binaries.clear();
        binary_eqs.clear();

        leaves.clear();
        for (auto& [vars, cs] : tern) {
            if (cs.size() != 4 || vars.size() != 3) { fail_code = 5; return false; }
            Leaf leaf;
            leaf.vars = vars;
            int forbid_parity = -1;
            set<uint64_t> seen;
            for (auto& [id, lits] : cs) {
                uint64_t mask = 0;
                for (int lit : lits) {
                    int p = find_pos(vars, abs(lit));
                    if (p < 0) { fail_code = 5; return false; }
                    if (lit < 0) mask |= (1ULL << p);
                }
                int p = (int)parity64(mask);
                if (forbid_parity < 0) forbid_parity = p;
                if (forbid_parity != p) { fail_code = 5; return false; }
                if (!seen.insert(mask).second) { fail_code = 5; return false; }
                leaf.mask_to_clause[mask] = id;
            }
            leaf.c = 1 - forbid_parity;
            leaves.push_back(leaf);
        }

        for (auto& [bvars, cs] : bin) {
            if ((cs.size() != 2 && cs.size() != 4) || bvars.size() != 2) { fail_code = 4; return false; }
            map<uint64_t, int> mask_to_id;
            for (auto& [id, lits] : cs) {
                uint64_t mask = 0;
                for (int lit : lits) {
                    int p = find_pos(bvars, abs(lit));
                    if (p < 0) { fail_code = 6; return false; }
                    if (lit < 0) mask |= (1ULL << p);
                }
                if (!mask_to_id.emplace(mask, id).second) { fail_code = 6; return false; }
            }
            if (mask_to_id.size() == 2) {
                int b_forbid = -1;
                BinaryEq beq;
                beq.vars = bvars;
                for (auto& [mask, id] : mask_to_id) {
                    int p = (int)parity64(mask);
                    if (b_forbid < 0) b_forbid = p;
                    if (b_forbid != p) { fail_code = 6; return false; }
                    beq.mask_to_clause[mask] = id;
                }
                beq.c = 1 - b_forbid;
                binary_eqs.push_back(beq);
            } else if (mask_to_id.size() == 4) {
                for (int forbid_parity = 0; forbid_parity <= 1; ++forbid_parity) {
                    BinaryEq beq;
                    beq.vars = bvars;
                    for (auto& [mask, id] : mask_to_id) {
                        if ((int)parity64(mask) == forbid_parity) beq.mask_to_clause[mask] = id;
                    }
                    if (beq.mask_to_clause.size() != 2) { fail_code = 6; return false; }
                    beq.c = 1 - forbid_parity;
                    binary_eqs.push_back(beq);
                }
            } else {
                fail_code = 6;
                return false;
            }
        }
        if (binary_eqs.size() == 1) binaries = bin.begin()->second;
        return true;
    }

    int binary_const() const {
        if (binaries.empty()) return 0;
        vector<int> bvars;
        for (int l : binaries[0].second) bvars.push_back(abs(l));
        sort(bvars.begin(), bvars.end());
        uint64_t mask = 0;
        for (int lit : binaries[0].second) {
            int p = find_pos(bvars, abs(lit));
            if (lit < 0) mask |= 1ULL << p;
        }
        return 1 - (int)parity64(mask);
    }

    bool build_tree() {
        nodes.clear();
        vector<int> active;
        for (int i = 0; i < (int)leaves.size(); ++i) {
            Node nd;
            nd.vars = leaves[i].vars;
            nd.c = leaves[i].c;
            nd.leaf = i;
            nodes.push_back(nd);
            active.push_back((int)nodes.size() - 1);
        }
        while (active.size() > 1) {
            int bi = -1, bj = -1, best_len = 1e9, best_inter = 1e9, best_sum = 1e9;
            for (int i = 0; i < (int)active.size(); ++i) {
                for (int j = i + 1; j < (int)active.size(); ++j) {
                    const auto& a = nodes[active[i]].vars;
                    const auto& b = nodes[active[j]].vars;
                    auto inter = inter_vec(a, b);
                    if (inter.empty()) continue;
                    auto sd = symdiff_vec(a, b);
                    int ilen = (int)inter.size();
                    int sum = (int)a.size() + (int)b.size();
                    if ((int)sd.size() < best_len ||
                        ((int)sd.size() == best_len && ilen < best_inter) ||
                        ((int)sd.size() == best_len && ilen == best_inter && sum < best_sum)) {
                        best_len = (int)sd.size();
                        best_inter = ilen;
                        best_sum = sum;
                        bi = i; bj = j;
                    }
                }
            }
            if (bi < 0) return false;
            int aidx = active[bi], bidx = active[bj];
            Node nd;
            nd.left = aidx;
            nd.right = bidx;
            nd.c = nodes[aidx].c ^ nodes[bidx].c;
            nd.vars = symdiff_vec(nodes[aidx].vars, nodes[bidx].vars);
            nodes.push_back(nd);
            if (bi > bj) swap(bi, bj);
            active.erase(active.begin() + bj);
            active.erase(active.begin() + bi);
            active.push_back((int)nodes.size() - 1);
        }
        int root = active[0];
        vector<int> bvars;
        if (binaries.empty()) return nodes[root].vars.empty();
        for (int l : binaries[0].second) bvars.push_back(abs(l));
        sort(bvars.begin(), bvars.end());
        return nodes[root].vars == bvars;
    }

    bool validate_global_xor() {
        vector<int> acc;
        int c = 0;
        for (const Leaf& leaf : leaves) {
            acc = symdiff_vec(acc, leaf.vars);
            c ^= leaf.c;
        }
        vector<int> bvars;
        if (!binaries.empty()) for (int l : binaries[0].second) bvars.push_back(abs(l));
        sort(bvars.begin(), bvars.end());
        if (acc != bvars) return false;
        global_c = c;
        return true;
    }

    int emit_clause(int node_id, uint64_t mask) {
        auto mk = make_pair(node_id, mask);
        if (next_id - nclauses > MAX_DERIVED) {
            proof_too_large = true;
            return -1;
        }
        auto mit = memo.find(mk);
        if (mit != memo.end()) return mit->second;
        const Node& nd = nodes[node_id];
        if (nd.leaf >= 0) {
            auto it = leaves[nd.leaf].mask_to_clause.find(mask);
            if (it == leaves[nd.leaf].mask_to_clause.end()) return -1;
            memo[mk] = it->second;
            return it->second;
        }
        const Node& L = nodes[nd.left];
        const Node& R = nodes[nd.right];
        vector<int> inter = inter_vec(L.vars, R.vars);
        if (inter.empty()) return -1;

        uint64_t lm = 0, rm = 0;
        for (int v : L.vars) {
            int pp = find_pos(nd.vars, v);
            int lp = find_pos(L.vars, v);
            if (pp >= 0 && ((mask >> pp) & 1ULL)) lm |= 1ULL << lp;
        }
        for (int v : R.vars) {
            int pp = find_pos(nd.vars, v);
            int rp = find_pos(R.vars, v);
            if (pp >= 0 && ((mask >> pp) & 1ULL)) rm |= 1ULL << rp;
        }
        if (inter.size() == 1) {
            for (int v : inter) {
                int rp = find_pos(R.vars, v);
                rm |= 1ULL << rp;
            }
            int need_l = 1 - L.c;
            if ((int)parity64(lm) != need_l) {
                int v = inter[0];
                int lp = find_pos(L.vars, v);
                int rp = find_pos(R.vars, v);
                lm ^= 1ULL << lp;
                rm ^= 1ULL << rp;
            }
            if ((int)parity64(lm) != 1 - L.c || (int)parity64(rm) != 1 - R.c) return -1;
            int lid = emit_clause(nd.left, lm);
            int rid = emit_clause(nd.right, rm);
            if (lid < 0 || rid < 0) return -1;
            proof << "pol " << lid << " " << rid << " + ;\n";
            int id = ++next_id;
            memo[mk] = id;
            return id;
        } else {
            if (inter.size() > 20) {
                proof_too_large = true;
                return -1;
            }
            int k = (int)inter.size();
            vector<int> ids(1 << k, -1);
            for (int t = 0; t < (1 << k); ++t) {
                uint64_t lm2 = lm, rm2 = rm;
                for (int i = 0; i < k; ++i) {
                    int v = inter[i];
                    int bit = (t >> i) & 1;
                    int lp = find_pos(L.vars, v);
                    int rp = find_pos(R.vars, v);
                    if (bit) {
                        lm2 |= 1ULL << lp;
                        rm2 |= 1ULL << rp;
                    }
                }
                bool lvalid = ((int)parity64(lm2) == 1 - L.c);
                bool rvalid = ((int)parity64(rm2) == 1 - R.c);
                if (lvalid == rvalid) return -1;
                int id = -1;
                if (lvalid) {
                    id = emit_clause(nd.left, lm2);
                    if (id < 0) return -1;
                    stringstream ss;
                    ss << "pol " << id;
                    bool weakened = false;
                    for (int v : R.vars) {
                        if (find_pos(L.vars, v) >= 0) continue;
                        int pp = find_pos(nd.vars, v);
                        if (pp < 0) continue;
                        ss << " " << (((mask >> pp) & 1ULL) ? "~x" : "x") << v << " +";
                        weakened = true;
                    }
                    if (weakened) {
                        proof << ss.str() << " ;\n";
                        id = ++next_id;
                    }
                } else {
                    id = emit_clause(nd.right, rm2);
                    if (id < 0) return -1;
                    stringstream ss;
                    ss << "pol " << id;
                    bool weakened = false;
                    for (int v : L.vars) {
                        if (find_pos(R.vars, v) >= 0) continue;
                        int pp = find_pos(nd.vars, v);
                        if (pp < 0) continue;
                        ss << " " << (((mask >> pp) & 1ULL) ? "~x" : "x") << v << " +";
                        weakened = true;
                    }
                    if (weakened) {
                        proof << ss.str() << " ;\n";
                        id = ++next_id;
                    }
                }
                ids[t] = id;
            }
            int stride = 1;
            int count = 1 << k;
            for (int dim = 0; dim < k; ++dim) {
                vector<int> next;
                for (int base = 0; base < count; base += 2 * stride) {
                    for (int off = 0; off < stride; ++off) {
                        int a = ids[base + off];
                        int b = ids[base + off + stride];
                        proof << "pol " << a << " " << b << " + s ;\n";
                        next.push_back(++next_id);
                    }
                }
                ids.swap(next);
                count >>= 1;
            }
            if (ids.size() != 1) return -1;
            memo[mk] = ids[0];
            return ids[0];
        }
    }

    int clause_id_for_binary_mask(uint64_t mask) const {
        vector<int> bvars;
        for (int l : binaries[0].second) bvars.push_back(abs(l));
        sort(bvars.begin(), bvars.end());
        for (auto& [id, lits] : binaries) {
            uint64_t m = 0;
            for (int lit : lits) {
                int p = find_pos(bvars, abs(lit));
                if (lit < 0) m |= 1ULL << p;
            }
            if (m == mask) return id;
        }
        return -1;
    }

    bool solve_xor_model(vector<int>& model) const {
        int blocks = (nvars + 63) / 64;
        vector<vector<uint64_t>> mat;
        vector<int> rhs;
        auto add_row = [&](const vector<int>& vars, int c) {
            vector<uint64_t> row(blocks, 0);
            for (int v : vars) {
                int z = v - 1;
                row[z >> 6] ^= 1ULL << (z & 63);
            }
            mat.push_back(std::move(row));
            rhs.push_back(c);
        };
        for (const Leaf& leaf : leaves) add_row(leaf.vars, leaf.c);
        for (const BinaryEq& beq : binary_eqs) add_row(beq.vars, beq.c);

        auto bit = [&](int r, int col) {
            return (mat[r][col >> 6] >> (col & 63)) & 1ULL;
        };
        int rank = 0;
        vector<int> pivot_cols;
        for (int col = 0; col < nvars; ++col) {
            int pivot = -1;
            for (int r = rank; r < (int)mat.size(); ++r) {
                if (bit(r, col)) {
                    pivot = r;
                    break;
                }
            }
            if (pivot < 0) continue;
            swap(mat[rank], mat[pivot]);
            swap(rhs[rank], rhs[pivot]);
            for (int r = 0; r < (int)mat.size(); ++r) {
                if (r == rank || !bit(r, col)) continue;
                for (int b = 0; b < blocks; ++b) mat[r][b] ^= mat[rank][b];
                rhs[r] ^= rhs[rank];
            }
            pivot_cols.push_back(col);
            ++rank;
        }
        for (int r = rank; r < (int)mat.size(); ++r) {
            bool any = false;
            for (uint64_t w : mat[r]) any |= (w != 0);
            if (!any && rhs[r]) return false;
        }

        vector<int> val(nvars + 1, 0);
        for (int i = 0; i < rank; ++i) {
            val[pivot_cols[i] + 1] = rhs[i];
        }
        for (const Clause& clause : clauses) {
            bool sat = false;
            for (int lit : clause.lits) {
                int v = abs(lit);
                if ((lit > 0 && val[v]) || (lit < 0 && !val[v])) {
                    sat = true;
                    break;
                }
            }
            if (!sat) return false;
        }
        model.assign(nvars + 1, 0);
        for (int v = 1; v <= nvars; ++v) model[v] = val[v] ? v : -v;
        return true;
    }

    void print_model(const vector<int>& model) const {
        printf("c detected satisfiable xor-chain: vars=%d clauses=%d xor_gates=%zu\n",
               nvars, nclauses, leaves.size());
        printf("s SATISFIABLE\n");
        int on_line = 0;
        printf("v");
        for (int v = 1; v <= nvars; ++v) {
            printf(" %d", model[v]);
            if (++on_line == 20 && v != nvars) {
                printf(" 0\nv");
                on_line = 0;
            }
        }
        printf(" 0\n");
    }

    bool supported_xor_chain() {
        return validate_global_xor();
    }

    bool find_xor_conflict(XorConflict& conflict) const {
        int eqs = (int)leaves.size() + (int)binary_eqs.size();
        int var_blocks = (nvars + 63) / 64;
        int comb_blocks = (eqs + 63) / 64;
        vector<vector<uint64_t>> mat;
        vector<vector<uint64_t>> comb;
        vector<int> rhs;
        auto add_row = [&](const vector<int>& vars, int c, int eq_id) {
            vector<uint64_t> row(var_blocks, 0), cb(comb_blocks, 0);
            for (int v : vars) {
                int z = v - 1;
                row[z >> 6] ^= 1ULL << (z & 63);
            }
            cb[eq_id >> 6] |= 1ULL << (eq_id & 63);
            mat.push_back(std::move(row));
            comb.push_back(std::move(cb));
            rhs.push_back(c);
        };
        for (int i = 0; i < (int)leaves.size(); ++i) add_row(leaves[i].vars, leaves[i].c, i);
        for (int i = 0; i < (int)binary_eqs.size(); ++i) {
            add_row(binary_eqs[i].vars, binary_eqs[i].c, (int)leaves.size() + i);
        }

        auto bit = [&](int r, int col) {
            return (mat[r][col >> 6] >> (col & 63)) & 1ULL;
        };
        int rank = 0;
        for (int col = 0; col < nvars; ++col) {
            int pivot = -1;
            for (int r = rank; r < (int)mat.size(); ++r) {
                if (bit(r, col)) {
                    pivot = r;
                    break;
                }
            }
            if (pivot < 0) continue;
            swap(mat[rank], mat[pivot]);
            swap(comb[rank], comb[pivot]);
            swap(rhs[rank], rhs[pivot]);
            for (int r = 0; r < (int)mat.size(); ++r) {
                if (r == rank || !bit(r, col)) continue;
                for (int b = 0; b < var_blocks; ++b) mat[r][b] ^= mat[rank][b];
                for (int b = 0; b < comb_blocks; ++b) comb[r][b] ^= comb[rank][b];
                rhs[r] ^= rhs[rank];
            }
            ++rank;
        }
        for (int r = 0; r < (int)mat.size(); ++r) {
            bool any = false;
            for (uint64_t w : mat[r]) any |= (w != 0);
            if (any || !rhs[r]) continue;
            conflict = XorConflict();
            for (int i = 0; i < (int)leaves.size(); ++i) {
                if ((comb[r][i >> 6] >> (i & 63)) & 1ULL) {
                    conflict.leaf_indices.push_back(i);
                    conflict.c ^= leaves[i].c;
                }
            }
            for (int i = 0; i < (int)binary_eqs.size(); ++i) {
                int b = (int)leaves.size() + i;
                if ((comb[r][b >> 6] >> (b & 63)) & 1ULL) {
                    conflict.binary_indices.push_back(i);
                    conflict.c ^= binary_eqs[i].c;
                }
            }
            return true;
        }
        return false;
    }

    int emit_sum_ids(const vector<int>& ids) {
        if (ids.empty()) return -1;
        if (ids.size() == 1) return ids[0];
        proof << "pol " << ids[0];
        for (size_t i = 1; i < ids.size(); ++i) {
            proof << " " << ids[i] << " +";
        }
        proof << " ;\n";
        return ++next_id;
    }

    pair<int, int> emit_leaf_pb(int leaf_idx) {
        const Leaf& leaf = leaves[leaf_idx];
        const string carry = string("y") + to_string(2 * leaf_idx + 1);
        const string par = string("y") + to_string(2 * leaf_idx + 2);

        proof << "red";
        for (int v : leaf.vars) proof << " " << opb_term_x(v, false);
        proof << " " << opb_term(carry, true, 2) << " >= 2 : " << carry << " -> 0 ;\n";
        int maj_low = ++next_id;

        proof << "red";
        for (int v : leaf.vars) proof << " " << opb_term_x(v, true);
        proof << " " << opb_term(carry, false, 2) << " >= 2 : " << carry << " -> 1 ;\n";
        int maj_high = ++next_id;

        proof << "red";
        for (int v : leaf.vars) proof << " " << opb_term_x(v, false);
        proof << " " << opb_term(carry, true, 2) << " " << opb_term(par, true, 3)
              << " >= 3 : " << par << " -> 0 ;\n";
        int par_low_raw = ++next_id;

        proof << "red";
        for (int v : leaf.vars) proof << " " << opb_term_x(v, true);
        proof << " " << opb_term(carry, false, 2) << " " << opb_term(par, false, 3)
              << " >= 3 : " << par << " -> 1 ;\n";
        int par_high_raw = ++next_id;

        proof << "pol " << par_low_raw << " " << maj_low << " 2 * + 3 d ;\n";
        int full_low = ++next_id; // sum(vars) = 2*carry + par, lower side
        proof << "pol " << par_high_raw << " " << maj_high << " 2 * + 3 d ;\n";
        int full_high = ++next_id; // sum(vars) = 2*carry + par, upper side

        const bool unit_neg = (leaf.c == 0);
        const string unit_lit = lit_token(par, unit_neg);
        vector<int> ids(8, -1);
        for (uint64_t mask = 0; mask < 8; ++mask) {
            if ((int)parity64(mask) == leaf.c) {
                proof << "pol " << full_low;
                for (int i = 0; i < 3; ++i) {
                    proof << " " << lit_token_x(leaf.vars[i], (mask >> i) & 1ULL) << " +";
                }
                proof << " " << unit_lit << " + 2 d 2 * " << full_high << " + ;\n";
                ids[mask] = ++next_id;
            } else {
                auto it = leaf.mask_to_clause.find(mask);
                if (it == leaf.mask_to_clause.end()) return {-1, -1};
                ids[mask] = it->second;
            }
        }

        int count = 8;
        int stride = 1;
        while (count > 1) {
            vector<int> next;
            for (int base = 0; base < count; base += 2 * stride) {
                for (int off = 0; off < stride; ++off) {
                    int a = ids[base + off];
                    int b = ids[base + off + stride];
                    proof << "pol " << a << " " << b << " + s ;\n";
                    next.push_back(++next_id);
                }
            }
            ids.swap(next);
            count >>= 1;
        }
        if (ids.size() != 1) return {-1, -1};
        int unit = ids[0]; // par == leaf.c

        int fixed_low = -1, fixed_high = -1;
        if (leaf.c == 0) {
            proof << "pol " << full_low << " " << par << " + ;\n";
            fixed_low = ++next_id;
            proof << "pol " << full_high << " " << unit << " + ;\n";
            fixed_high = ++next_id;
        } else {
            proof << "pol " << full_low << " " << unit << " + ;\n";
            fixed_low = ++next_id;
            proof << "pol " << full_high << " ~" << par << " + ;\n";
            fixed_high = ++next_id;
        }
        return {fixed_low, fixed_high};
    }

    pair<int, int> binary_pb_ids(int binary_idx) const {
        const BinaryEq& beq = binary_eqs[binary_idx];
        if (beq.c == 1) {
            auto lo = beq.mask_to_clause.find(0);
            auto hi = beq.mask_to_clause.find(3);
            if (lo == beq.mask_to_clause.end() || hi == beq.mask_to_clause.end()) return {-1, -1};
            return {lo->second, hi->second};
        }
        auto lo = beq.mask_to_clause.find(2);
        auto hi = beq.mask_to_clause.find(1);
        if (lo == beq.mask_to_clause.end() || hi == beq.mask_to_clause.end()) return {-1, -1};
        return {lo->second, hi->second};
    }

    bool write_proof(const string& out, const XorConflict& conflict) {
        if (conflict.c != 1) return false;
        proof.open(out);
        if (!proof) return false;
        next_id = nclauses;
        memo.clear();
        proof_too_large = false;
        proof << "pseudo-Boolean proof version 3.0\n";
        proof << "f " << nclauses << " ;\n";

        vector<int> lower_ids, upper_ids;
        lower_ids.reserve(conflict.leaf_indices.size());
        upper_ids.reserve(conflict.leaf_indices.size());
        for (int i : conflict.leaf_indices) {
            auto [lo, hi] = emit_leaf_pb(i);
            if (lo < 0 || hi < 0) return false;
            lower_ids.push_back(lo);
            upper_ids.push_back(hi);
        }
        for (int i : conflict.binary_indices) {
            auto [lo, hi] = binary_pb_ids(i);
            if (lo < 0 || hi < 0) return false;
            lower_ids.push_back(lo);
            upper_ids.push_back(hi);
        }

        int all_low = emit_sum_ids(lower_ids);
        int all_high = emit_sum_ids(upper_ids);
        if (all_low < 0 || all_high < 0) return false;

        proof << "pol " << all_low << " 2 d 2 * " << all_high << " + ;\n";
        int empty = ++next_id;
        proof << "output NONE ;\n";
        proof << "conclusion UNSAT : " << empty << " ;\n";
        proof << "end pseudo-Boolean proof ;\n";
        return true;
    }
};

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    Solver s;
    if (!s.parse(argv[1]) || !s.detect()) {
        printf("c xor-chain detector did not certify this instance\n");
        printf("s UNKNOWN\n");
        return 0;
    }
    XorConflict conflict;
    if (s.find_xor_conflict(conflict)) {
        if (!s.write_proof(argv[2], conflict)) {
            printf("c xor-chain proof construction failed\n");
            printf("s UNKNOWN\n");
            return 0;
        }
        printf("c detected xor-chain contradiction: vars=%d clauses=%d xor_gates=%zu binary_xors=%zu conflict_gates=%zu conflict_binary=%zu\n",
               s.nvars, s.nclauses, s.leaves.size(), s.binary_eqs.size(),
               conflict.leaf_indices.size(), conflict.binary_indices.size());
        printf("s UNSATISFIABLE\n");
        return 20;
    }

    vector<int> model;
    if (!s.solve_xor_model(model)) {
        printf("c xor-chain model construction failed verification\n");
        printf("s UNKNOWN\n");
        return 0;
    }
    s.print_model(model);
    return 10;
}
