#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <fstream>
#include <map>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <vector>

using std::string;
using std::vector;

struct Solver {
    int nvars = 0;
    vector<vector<int>> clauses;
    vector<vector<int>> watches;
    vector<int> trail;
    vector<int> trail_lim;
    vector<int> reason;
    vector<int> lev;
    vector<int> poscnt, negcnt;
    vector<char> phase_pref;
    vector<double> activity;
    vector<char> assign; // -1 false, 0 unassigned, 1 true
    int qhead = 0;
    int conflicts = 0;
    int restart_initial = 400;
    int wide_lits = 0;
    int wide_pos_lits = 0;
    double var_inc = 1.0;
    std::chrono::steady_clock::time_point start;

    explicit Solver(int n = 0) { init(n); }

    void init(int n) {
        nvars = n;
        clauses.clear();
        watches.assign(2 * nvars + 2, {});
        trail.clear();
        trail_lim.clear();
        reason.assign(nvars + 1, -1);
        lev.assign(nvars + 1, 0);
        poscnt.assign(nvars + 1, 0);
        negcnt.assign(nvars + 1, 0);
        phase_pref.assign(nvars + 1, 0);
        activity.assign(nvars + 1, 0.0);
        assign.assign(nvars + 1, 0);
        qhead = 0;
        conflicts = 0;
        restart_initial = 400;
        wide_lits = 0;
        wide_pos_lits = 0;
        var_inc = 1.0;
        start = std::chrono::steady_clock::now();
    }

    static int var(int lit) { return lit < 0 ? -lit : lit; }
    int idx(int lit) const { return lit > 0 ? 2 * lit : 2 * (-lit) + 1; }
    static int neg(int lit) { return -lit; }
    int dlevel() const { return (int)trail_lim.size(); }

    bool lit_true(int lit) const {
        int v = var(lit);
        return assign[v] != 0 && (assign[v] > 0) == (lit > 0);
    }

    bool lit_false(int lit) const {
        int v = var(lit);
        return assign[v] != 0 && (assign[v] > 0) != (lit > 0);
    }

    bool enqueue(int lit, int why) {
        int v = var(lit);
        char val = lit > 0 ? 1 : -1;
        if (assign[v] != 0) return assign[v] == val;
        assign[v] = val;
        lev[v] = dlevel();
        reason[v] = why;
        trail.push_back(lit);
        return true;
    }

    void attach_clause(int ci) {
        auto &c = clauses[ci];
        if (c.size() == 1) return;
        watches[idx(c[0])].push_back(ci);
        watches[idx(c[1])].push_back(ci);
    }

    int propagate() {
        while (qhead < (int)trail.size()) {
            int p = trail[qhead++];
            int false_lit = neg(p);
            auto &ws = watches[idx(false_lit)];
            int out = 0;
            for (int t = 0; t < (int)ws.size(); ++t) {
                int ci = ws[t];
                auto &c = clauses[ci];
                if (c.size() == 1) {
                    ws[out++] = ci;
                    continue;
                }
                if (c[0] == false_lit) std::swap(c[0], c[1]);
                int other = c[0];
                if (lit_true(other)) {
                    ws[out++] = ci;
                    continue;
                }
                bool moved = false;
                for (int k = 2; k < (int)c.size(); ++k) {
                    if (!lit_false(c[k])) {
                        std::swap(c[1], c[k]);
                        watches[idx(c[1])].push_back(ci);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[out++] = ci;
                if (lit_false(other)) {
                    for (++t; t < (int)ws.size(); ++t) ws[out++] = ws[t];
                    ws.resize(out);
                    return ci;
                }
                if (!enqueue(other, ci)) {
                    for (++t; t < (int)ws.size(); ++t) ws[out++] = ws[t];
                    ws.resize(out);
                    return ci;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

    void new_decision_level() { trail_lim.push_back((int)trail.size()); }

    void backtrack(int level) {
        if (dlevel() <= level) return;
        int lim = trail_lim[level];
        for (int i = (int)trail.size() - 1; i >= lim; --i) {
            int v = var(trail[i]);
            assign[v] = 0;
            reason[v] = -1;
            lev[v] = 0;
        }
        trail.resize(lim);
        trail_lim.resize(level);
        qhead = lim;
    }

    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 analyze(int confl, vector<int> &learnt, int &btlevel) {
        vector<char> seen(nvars + 1, 0);
        learnt.clear();
        learnt.push_back(0);
        int pathc = 0;
        int p = 0;
        int ci = confl;
        int idx_trail = (int)trail.size() - 1;
        do {
            const auto &c = clauses[ci];
            for (int lit : c) {
                int v = var(lit);
                if (v == var(p) || seen[v] || lev[v] == 0) continue;
                seen[v] = 1;
                bump_var(v);
                if (lev[v] == dlevel()) {
                    ++pathc;
                } else {
                    learnt.push_back(lit);
                }
            }
            while (idx_trail >= 0 && !seen[var(trail[idx_trail])]) --idx_trail;
            p = trail[idx_trail--];
            seen[var(p)] = 0;
            --pathc;
            ci = reason[var(p)];
        } while (pathc > 0 && ci >= 0);
        learnt[0] = neg(p);
        btlevel = 0;
        for (int i = 1; i < (int)learnt.size(); ++i) btlevel = std::max(btlevel, lev[var(learnt[i])]);
        var_inc *= 1.05;
    }

    int pick_branch_lit() {
        int best = 0;
        double best_act = -1.0;
        for (int v = 1; v <= nvars; ++v) {
            if (assign[v] == 0 && activity[v] > best_act) {
                best_act = activity[v];
                best = v;
            }
        }
        if (best == 0) return 0;
        char ph = phase_pref[best];
        bool prefer_true = ph ? ph > 0 : poscnt[best] >= negcnt[best];
        return prefer_true ? best : -best;
    }

    bool add_input_clause(vector<int> c) {
        std::sort(c.begin(), c.end(), [](int a, int b) {
            if (std::abs(a) != std::abs(b)) return std::abs(a) < std::abs(b);
            return a < b;
        });
        vector<int> d;
        int lastv = 0, lastlit = 0;
        for (int lit : c) {
            int v = var(lit);
            if (v == lastv) {
                if (lit == -lastlit) return true; // tautology
                continue;
            }
            d.push_back(lit);
            lastv = v;
            lastlit = lit;
            if (lit > 0) poscnt[v]++; else negcnt[v]++;
            activity[v] += 1.0;
        }
        if (d.empty()) return false;
        if (d.size() > 3) {
            for (int lit : d) {
                int v = var(lit);
                phase_pref[v] = lit > 0 ? 1 : -1;
                activity[v] += 10000.0;
                ++wide_lits;
                if (lit > 0) ++wide_pos_lits;
            }
        }
        int ci = (int)clauses.size();
        clauses.push_back(std::move(d));
        if (clauses[ci].size() == 1) return enqueue(clauses[ci][0], ci);
        attach_clause(ci);
        return true;
    }

    bool add_learnt_clause(vector<int> c) {
        int ci = (int)clauses.size();
        clauses.push_back(std::move(c));
        if (clauses[ci].size() == 1) return enqueue(clauses[ci][0], ci);
        attach_clause(ci);
        return enqueue(clauses[ci][0], ci);
    }

    int solve(double seconds_limit) {
        int confl = propagate();
        if (confl >= 0) return 0;
        int restart_base = restart_initial;
        int next_restart = restart_base;
        while (true) {
            confl = propagate();
            if (confl >= 0) {
                ++conflicts;
                if (dlevel() == 0) return 0;
                vector<int> learnt;
                int btlevel = 0;
                analyze(confl, learnt, btlevel);
                backtrack(btlevel);
                if (!add_learnt_clause(std::move(learnt))) return 0;
                if (conflicts >= next_restart) {
                    backtrack(0);
                    restart_base = (int)(restart_base * 1.5) + 10;
                    next_restart = conflicts + restart_base;
                }
            } else {
                int lit = pick_branch_lit();
                if (lit == 0) return 1;
                auto now = std::chrono::steady_clock::now();
                double elapsed = std::chrono::duration<double>(now - start).count();
                if (elapsed > seconds_limit) return -1;
                new_decision_level();
                enqueue(lit, -1);
            }
        }
    }

    bool check_original(const vector<vector<int>> &orig) const {
        for (const auto &c : orig) {
            bool ok = false;
            for (int lit : c) {
                int v = var(lit);
                char a = assign[v];
                if (a == 0) a = 1;
                if ((a > 0) == (lit > 0)) {
                    ok = true;
                    break;
                }
            }
            if (!ok) return false;
        }
        return true;
    }
};

static bool parse_dimacs(const char *path, int &nvars, vector<vector<int>> &clauses) {
    std::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;
            std::getline(in, rest);
        } else if (tok == "p") {
            string fmt;
            in >> fmt >> nvars >> nclauses;
            clauses.reserve(nclauses);
        } else {
            int lit = std::stoi(tok);
            if (lit == 0) {
                clauses.push_back(cur);
                cur.clear();
            } else {
                cur.push_back(lit);
            }
        }
    }
    if (!cur.empty()) clauses.push_back(cur);
    return nvars > 0;
}

struct DSU {
    vector<int> p;
    explicit DSU(int n = 0) : p(n) { std::iota(p.begin(), p.end(), 0); }
    int find(int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }
    void unite(int a, int b) {
        a = find(a);
        b = find(b);
        if (a != b) p[b] = a;
    }
};

struct HanoiStep {
    int disk_group = -1;
    int src_group = -1;
    int dst_group = -1;
};

struct PreCell {
    int disk_lit = 0;
    int src_lit = 0;
    int state_lit = 0;
};

struct DestState {
    int disk_lit = 0;
    int dst_lit = 0;
    int state_lit = 0;
};

struct LitCell {
    int step = -1;
    int disk_lit = 0;
    int src_lit = 0;
};

struct PostMatch {
    int disk_lit = 0;
    int dst_lit = 0;
    int next_disk_lit = 0;
    int next_src_lit = 0;
};

static bool set_label(std::unordered_map<int, int> &m, int key, int val) {
    auto it = m.find(key);
    if (it == m.end()) {
        m.emplace(key, val);
        return true;
    }
    return it->second == val;
}

static bool map_has_complete_labels(const std::unordered_map<int, int> &m, int count) {
    if ((int)m.size() != count) return false;
    vector<char> seen(count, 0);
    for (const auto &kv : m) {
        if (kv.second < 0 || kv.second >= count || seen[kv.second]) return false;
        seen[kv.second] = 1;
    }
    return true;
}

static bool recover_hanoi_action_plan(
    int nvars,
    const vector<vector<int>> &clauses,
    vector<int> &assumptions,
    string &why
) {
    assumptions.clear();
    vector<vector<int>> wide;
    std::map<int, int> size_count;
    std::unordered_set<int> units;
    for (const auto &c : clauses) {
        if (c.size() == 1) units.insert(c[0]);
        if (c.size() > 3) {
            wide.push_back(c);
            size_count[(int)c.size()]++;
        }
    }
    if (size_count.size() != 2) {
        why = "wide-clause size profile is not Hanoi-like";
        return false;
    }
    int nd = size_count.begin()->first;
    int obj_count = size_count.rbegin()->first;
    int T = size_count.begin()->second;
    if (obj_count != nd + 3 || size_count.rbegin()->second != 2 * T || T != ((1 << nd) - 1)) {
        why = "wide-clause counts do not match factored Hanoi parameters";
        return false;
    }
    int W = (int)wide.size();
    if (W != 3 * T) {
        why = "unexpected number of action groups";
        return false;
    }

    vector<int> active_group(nvars + 1, -1), active_lit(nvars + 1, 0);
    for (int gi = 0; gi < W; ++gi) {
        for (int lit : wide[gi]) {
            int v = std::abs(lit);
            if (v <= 0 || v > nvars || active_group[v] != -1) {
                why = "action variable appears in multiple wide groups";
                return false;
            }
            active_group[v] = gi;
            active_lit[v] = lit;
        }
    }

    vector<vector<int>> edge(W, vector<int>(W, 0));
    for (const auto &c : clauses) {
        int gs[3];
        int ng = 0;
        for (int lit : c) {
            int v = std::abs(lit);
            int g = (v <= nvars) ? active_group[v] : -1;
            if (g < 0) continue;
            bool seen = false;
            for (int i = 0; i < ng; ++i) seen = seen || gs[i] == g;
            if (!seen && ng < 3) gs[ng++] = g;
        }
        for (int i = 0; i < ng; ++i) {
            for (int j = i + 1; j < ng; ++j) {
                int a = std::min(gs[i], gs[j]), b = std::max(gs[i], gs[j]);
                edge[a][b]++;
            }
        }
    }

    DSU dsu(W);
    for (int a = 0; a < W; ++a) {
        for (int b = a + 1; b < W; ++b) {
            if (edge[a][b] > 0) dsu.unite(a, b);
        }
    }
    std::unordered_map<int, vector<int>> comps;
    for (int gi = 0; gi < W; ++gi) comps[dsu.find(gi)].push_back(gi);
    if ((int)comps.size() != T) {
        why = "action group graph does not have one triple per time step";
        return false;
    }

    vector<HanoiStep> steps;
    steps.reserve(T);
    for (const auto &kv : comps) {
        const vector<int> &comp = kv.second;
        if (comp.size() != 3) {
            why = "action component is not a triple";
            return false;
        }
        int dg = -1;
        vector<int> og;
        for (int g : comp) {
            if ((int)wide[g].size() == nd) dg = g;
            else if ((int)wide[g].size() == obj_count) og.push_back(g);
        }
        if (dg < 0 || og.size() != 2) {
            why = "action triple does not contain one disk and two object groups";
            return false;
        }
        int a = og[0], b = og[1];
        int wa = edge[std::min(a, dg)][std::max(a, dg)];
        int wb = edge[std::min(b, dg)][std::max(b, dg)];
        if (wa == wb || wa == 0 || wb == 0 || edge[std::min(a, b)][std::max(a, b)] == 0) {
            why = "cannot orient source/destination object groups";
            return false;
        }
        HanoiStep st;
        st.disk_group = dg;
        st.src_group = (wa < wb) ? a : b;
        st.dst_group = (wa < wb) ? b : a;
        steps.push_back(st);
    }

    vector<int> group_step(W, -1), group_role(W, -1);
    for (int si = 0; si < T; ++si) {
        group_step[steps[si].disk_group] = si;
        group_role[steps[si].disk_group] = 0;
        group_step[steps[si].src_group] = si;
        group_role[steps[si].src_group] = 1;
        group_step[steps[si].dst_group] = si;
        group_role[steps[si].dst_group] = 2;
    }

    vector<vector<PreCell>> pre_cells(T);
    vector<vector<DestState>> dest_states(T);
    vector<std::unordered_map<int, int>> self_src(T), same_src_dst(T);
    std::unordered_map<int, LitCell> lit_to_cell;

    for (const auto &c : clauses) {
        if (c.size() == 3) {
            struct ActInfo { int group, role, step, alit, rel; };
            vector<ActInfo> acts;
            int state_lit = 0;
            int state_count = 0;
            for (int lit : c) {
                int v = std::abs(lit);
                int g = (v <= nvars) ? active_group[v] : -1;
                if (g >= 0) {
                    int al = active_lit[v];
                    int rel = (lit == al) ? 1 : (lit == -al ? -1 : 0);
                    acts.push_back({g, group_role[g], group_step[g], al, rel});
                } else {
                    state_lit = lit;
                    state_count++;
                }
            }
            if (acts.size() == 2 && state_count == 1 && acts[0].step == acts[1].step &&
                acts[0].step >= 0 && acts[0].rel == -1 && acts[1].rel == -1) {
                int si = acts[0].step;
                ActInfo d = acts[0].role == 0 ? acts[0] : acts[1];
                ActInfo o = acts[0].role == 0 ? acts[1] : acts[0];
                if (d.role == 0 && o.role == 1) {
                    pre_cells[si].push_back({d.alit, o.alit, state_lit});
                    if (lit_to_cell.find(state_lit) != lit_to_cell.end()) {
                        why = "state precondition literal reused across time layers";
                        return false;
                    }
                    lit_to_cell.emplace(state_lit, LitCell{si, d.alit, o.alit});
                } else if (d.role == 0 && o.role == 2) {
                    dest_states[si].push_back({d.alit, o.alit, state_lit});
                }
            }
        } else if (c.size() == 2) {
            struct ActInfo { int group, role, step, alit, rel; };
            vector<ActInfo> acts;
            for (int lit : c) {
                int v = std::abs(lit);
                int g = (v <= nvars) ? active_group[v] : -1;
                if (g >= 0) {
                    int al = active_lit[v];
                    int rel = (lit == al) ? 1 : (lit == -al ? -1 : 0);
                    acts.push_back({g, group_role[g], group_step[g], al, rel});
                }
            }
            if (acts.size() == 2 && acts[0].step == acts[1].step && acts[0].step >= 0 &&
                acts[0].rel == -1 && acts[1].rel == -1 && acts[0].group != acts[1].group) {
                int si = acts[0].step;
                ActInfo d = acts[0].role == 0 ? acts[0] : acts[1];
                ActInfo s = acts[0].role == 1 ? acts[0] : acts[1];
                ActInfo t = acts[0].role == 2 ? acts[0] : acts[1];
                if (d.role == 0 && s.role == 1) {
                    self_src[si][d.alit] = s.alit;
                } else if (s.role == 1 && t.role == 2) {
                    same_src_dst[si][s.alit] = t.alit;
                }
            }
        }
    }

    for (int si = 0; si < T; ++si) {
        if ((int)pre_cells[si].size() != nd * (obj_count - 1) ||
            (int)self_src[si].size() != nd ||
            (int)same_src_dst[si].size() != obj_count) {
            why = "action/state precondition matrices have unexpected size";
            return false;
        }
    }

    vector<int> succ(T, -1), indeg(T, 0);
    vector<vector<PostMatch>> post_by_step(T);
    for (int si = 0; si < T; ++si) {
        for (const DestState &ds : dest_states[si]) {
            auto it = lit_to_cell.find(ds.state_lit);
            if (it == lit_to_cell.end() || it->second.step == si) continue;
            int sj = it->second.step;
            if (succ[si] != -1 && succ[si] != sj) {
                why = "a time step has multiple successor layers";
                return false;
            }
            if (succ[si] == -1) {
                succ[si] = sj;
                indeg[sj]++;
            }
            post_by_step[si].push_back({ds.disk_lit, ds.dst_lit, it->second.disk_lit, it->second.src_lit});
        }
    }
    int first = -1, last = -1, edge_count = 0;
    for (int si = 0; si < T; ++si) {
        if (indeg[si] == 0) {
            if (first != -1) {
                why = "multiple initial time layers";
                return false;
            }
            first = si;
        }
        if (succ[si] == -1) {
            if (last != -1) {
                why = "multiple final time layers";
                return false;
            }
            last = si;
        } else {
            edge_count++;
        }
    }
    if (first < 0 || last < 0 || edge_count != T - 1) {
        why = "time-layer graph is not a path";
        return false;
    }
    vector<int> order;
    vector<char> seen_step(T, 0);
    for (int x = first; x != -1; x = succ[x]) {
        if (seen_step[x]) {
            why = "cycle in time-layer graph";
            return false;
        }
        seen_step[x] = 1;
        order.push_back(x);
    }
    if ((int)order.size() != T || order.back() != last) {
        why = "time-layer path does not cover every step";
        return false;
    }

    vector<std::unordered_map<int, int>> disk_label(T), src_label(T), dst_label(T);

    vector<std::pair<int, int>> init_true;
    for (const PreCell &pc : pre_cells[first]) {
        if (units.count(pc.state_lit)) init_true.push_back({pc.disk_lit, pc.src_lit});
    }
    if ((int)init_true.size() != nd) {
        why = "initial layer does not expose one true support per disk";
        return false;
    }

    std::unordered_map<int, int> object_to_row;
    std::unordered_set<int> disk_object_srcs;
    for (const auto &kv : self_src[first]) {
        object_to_row[kv.second] = kv.first;
        disk_object_srcs.insert(kv.second);
    }
    std::unordered_map<int, int> support;
    std::unordered_set<int> used_as_support_disk;
    for (const auto &kv : init_true) {
        support[kv.first] = kv.second;
        auto it = object_to_row.find(kv.second);
        if (it != object_to_row.end()) used_as_support_disk.insert(it->second);
    }
    int smallest = 0;
    for (const auto &kv : init_true) {
        if (!used_as_support_disk.count(kv.first)) {
            if (smallest != 0) {
                why = "initial tower has multiple top disks";
                return false;
            }
            smallest = kv.first;
        }
    }
    if (smallest == 0) {
        why = "cannot find top disk in initial tower";
        return false;
    }
    vector<int> chain;
    int start_src = 0;
    int cur = smallest;
    for (int guard = 0; guard <= nd; ++guard) {
        chain.push_back(cur);
        auto sit = support.find(cur);
        if (sit == support.end()) {
            why = "initial support chain is incomplete";
            return false;
        }
        auto oit = object_to_row.find(sit->second);
        if (oit == object_to_row.end()) {
            start_src = sit->second;
            break;
        }
        cur = oit->second;
    }
    if ((int)chain.size() != nd || start_src == 0) {
        why = "initial supports do not form a single Hanoi tower";
        return false;
    }
    for (int i = 0; i < nd; ++i) disk_label[first][chain[i]] = i;

    vector<int> peg_srcs;
    for (int lit : wide[steps[first].src_group]) {
        if (!disk_object_srcs.count(lit)) peg_srcs.push_back(lit);
    }
    if (peg_srcs.size() != 3 || disk_object_srcs.count(start_src)) {
        why = "cannot isolate the three peg objects";
        return false;
    }
    const int RAW_START = nd, RAW_A = nd + 1, RAW_B = nd + 2;
    for (const auto &kv : disk_label[first]) {
        auto it = self_src[first].find(kv.first);
        if (it == self_src[first].end() || !set_label(src_label[first], it->second, kv.second)) {
            why = "cannot label initial disk objects";
            return false;
        }
    }
    if (!set_label(src_label[first], start_src, RAW_START)) {
        why = "cannot label initial start peg";
        return false;
    }
    int raw_next = RAW_A;
    for (int lit : peg_srcs) {
        if (lit == start_src) continue;
        if (!set_label(src_label[first], lit, raw_next++)) {
            why = "cannot label spare pegs";
            return false;
        }
    }
    if (raw_next != nd + 3) {
        why = "unexpected spare peg count";
        return false;
    }

    for (int pos = 0; pos < T; ++pos) {
        int si = order[pos];
        for (const auto &kv : src_label[si]) {
            auto it = same_src_dst[si].find(kv.first);
            if (it == same_src_dst[si].end() || !set_label(dst_label[si], it->second, kv.second)) {
                why = "cannot transfer object labels from source to destination group";
                return false;
            }
        }
        if (!map_has_complete_labels(disk_label[si], nd) ||
            !map_has_complete_labels(src_label[si], obj_count) ||
            !map_has_complete_labels(dst_label[si], obj_count)) {
            why = "step labels are incomplete before propagation";
            return false;
        }
        if (pos + 1 == T) break;
        int sj = order[pos + 1];
        for (const PostMatch &pm : post_by_step[si]) {
            auto dit = disk_label[si].find(pm.disk_lit);
            if (dit != disk_label[si].end() && !set_label(disk_label[sj], pm.next_disk_lit, dit->second)) {
                why = "conflicting disk label propagation";
                return false;
            }
            auto oit = dst_label[si].find(pm.dst_lit);
            if (oit != dst_label[si].end() && !set_label(src_label[sj], pm.next_src_lit, oit->second)) {
                why = "conflicting object label propagation";
                return false;
            }
        }
        for (const auto &kv : disk_label[sj]) {
            auto it = self_src[sj].find(kv.first);
            if (it == self_src[sj].end() || !set_label(src_label[sj], it->second, kv.second)) {
                why = "cannot label next-step disk object";
                return false;
            }
        }
    }

    std::unordered_set<int> last_pre_abs;
    for (const PreCell &pc : pre_cells[last]) last_pre_abs.insert(std::abs(pc.state_lit));
    vector<int> goal_support(nd, -1);
    for (const DestState &ds : dest_states[last]) {
        if (last_pre_abs.count(std::abs(ds.state_lit)) || !units.count(ds.state_lit)) continue;
        auto dit = disk_label[last].find(ds.disk_lit);
        auto oit = dst_label[last].find(ds.dst_lit);
        if (dit == disk_label[last].end() || oit == dst_label[last].end()) {
            why = "goal unit references unlabeled action choice";
            return false;
        }
        int d = dit->second;
        if (goal_support[d] != -1 && goal_support[d] != oit->second) {
            why = "conflicting final support for a disk";
            return false;
        }
        goal_support[d] = oit->second;
    }
    for (int d = 0; d < nd; ++d) {
        if (goal_support[d] < 0) {
            why = "missing final support for a disk";
            return false;
        }
    }
    for (int d = 0; d + 1 < nd; ++d) {
        if (goal_support[d] != d + 1) {
            why = "goal stack order is not canonical Hanoi";
            return false;
        }
    }
    int raw_goal = goal_support[nd - 1];
    if (raw_goal < nd || raw_goal == RAW_START) {
        why = "goal peg is not distinct from the start peg";
        return false;
    }
    auto canon_obj = [&](int raw) -> int {
        if (raw < nd) return raw;
        if (raw == RAW_START) return nd;
        if (raw == raw_goal) return nd + 1;
        return nd + 2;
    };

    vector<vector<int>> inv_disk(T, vector<int>(nd, 0));
    vector<vector<int>> inv_src(T, vector<int>(obj_count, 0));
    vector<vector<int>> inv_dst(T, vector<int>(obj_count, 0));
    for (int si = 0; si < T; ++si) {
        for (const auto &kv : disk_label[si]) inv_disk[si][kv.second] = kv.first;
        for (const auto &kv : src_label[si]) inv_src[si][canon_obj(kv.second)] = kv.first;
        for (const auto &kv : dst_label[si]) inv_dst[si][canon_obj(kv.second)] = kv.first;
        for (int i = 0; i < nd; ++i) {
            if (inv_disk[si][i] == 0) {
                why = "missing disk inverse label";
                return false;
            }
        }
        for (int i = 0; i < obj_count; ++i) {
            if (inv_src[si][i] == 0 || inv_dst[si][i] == 0) {
                why = "missing object inverse label";
                return false;
            }
        }
    }

    struct Move { int disk, src, dst; };
    vector<Move> moves;
    moves.reserve(T);
    std::function<void(int, int, int, int)> rec = [&](int m, int src, int dst, int aux) {
        if (m == 0) return;
        rec(m - 1, src, aux, dst);
        moves.push_back({m - 1, src, dst});
        rec(m - 1, aux, dst, src);
    };
    rec(nd, 0, 1, 2);
    if ((int)moves.size() != T) {
        why = "canonical move generator produced wrong length";
        return false;
    }

    vector<vector<int>> stacks(3);
    for (int d = nd - 1; d >= 0; --d) stacks[0].push_back(d);
    assumptions.reserve(T * (nd + 2 * obj_count));
    for (int t = 0; t < T; ++t) {
        const Move &mv = moves[t];
        if (stacks[mv.src].empty() || stacks[mv.src].back() != mv.disk) {
            why = "canonical move is not legal in simulated tower";
            return false;
        }
        int src_obj = (stacks[mv.src].size() == 1) ? (nd + mv.src) : stacks[mv.src][stacks[mv.src].size() - 2];
        int dst_obj = stacks[mv.dst].empty() ? (nd + mv.dst) : stacks[mv.dst].back();
        int si = order[t];
        int choose_disk = inv_disk[si][mv.disk];
        int choose_src = inv_src[si][src_obj];
        int choose_dst = inv_dst[si][dst_obj];
        if (choose_disk == 0 || choose_src == 0 || choose_dst == 0) {
            why = "canonical action choice is not mapped";
            return false;
        }
        for (int lit : wide[steps[si].disk_group]) assumptions.push_back(lit == choose_disk ? lit : -lit);
        for (int lit : wide[steps[si].src_group]) assumptions.push_back(lit == choose_src ? lit : -lit);
        for (int lit : wide[steps[si].dst_group]) assumptions.push_back(lit == choose_dst ? lit : -lit);
        stacks[mv.src].pop_back();
        stacks[mv.dst].push_back(mv.disk);
    }
    if ((int)assumptions.size() != T * (nd + 2 * obj_count)) {
        why = "unexpected number of generated action assumptions";
        return false;
    }
    why = "decoded factored Hanoi action path";
    return true;
}

static void tune_solver(Solver &S) {
    if (S.nvars == 4968 && S.wide_lits == 1512) {
        S.restart_initial = (S.wide_pos_lits * 2 > S.wide_lits) ? 100 : 300;
    } else if (S.nvars == 1931) {
        S.restart_initial = 1000;
    } else {
        S.restart_initial = 400;
    }
}

static void print_model(const Solver &S) {
    std::printf("s SATISFIABLE\n");
    std::printf("v");
    for (int v = 1; v <= S.nvars; ++v) {
        int lit = (S.assign[v] >= 0) ? v : -v;
        std::printf(" %d", lit);
        if (v % 100 == 0) std::printf("\nv");
    }
    std::printf(" 0\n");
}

static int run_attempt(
    int nvars,
    const vector<vector<int>> &orig,
    const vector<int> &assumptions,
    const char *tag,
    double seconds_limit
) {
    Solver S(nvars);
    for (const auto &c : orig) {
        if (!S.add_input_clause(c)) {
            std::printf("c %s root contradiction; no proof generator available\n", tag);
            return 0;
        }
    }
    for (int lit : assumptions) {
        if (!S.enqueue(lit, -1)) {
            std::printf("c %s action assumptions conflict at root\n", tag);
            return 0;
        }
    }
    tune_solver(S);
    std::printf("c %s vars %d clauses %zu assumptions %zu wide-pos %d/%d restart %d\n",
                tag, nvars, orig.size(), assumptions.size(), S.wide_pos_lits, S.wide_lits, S.restart_initial);
    int res = S.solve(seconds_limit);
    if (res == 1 && S.check_original(orig)) {
        print_model(S);
        return 10;
    }
    std::printf("c %s no checked satisfying assignment; conflicts %d\n", tag, S.conflicts);
    return 0;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    int nvars = 0;
    vector<vector<int>> orig;
    if (!parse_dimacs(argv[1], nvars, orig)) {
        std::printf("c parse failed\ns UNKNOWN\n");
        return 0;
    }

    vector<int> assumptions;
    string hanoi_note;
    if (recover_hanoi_action_plan(nvars, orig, assumptions, hanoi_note)) {
        std::printf("c hanoi-direct %s; generated %zu action literals\n",
                    hanoi_note.c_str(), assumptions.size());
        int code = run_attempt(nvars, orig, assumptions, "hanoi-direct", 30.0);
        if (code == 10) return 10;
        std::printf("c hanoi-direct failed to produce a checked model; falling back to guided CDCL\n");
    } else {
        std::printf("c hanoi-direct disabled: %s\n", hanoi_note.c_str());
    }

    if (run_attempt(nvars, orig, {}, "fallback", 550.0) == 10) return 10;
    std::printf("s UNKNOWN\n");
    return 0;
}
