#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <queue>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

struct Clause {
    vector<int> lits;
};

struct Move {
    array<int, 130> bit_src{};
    array<int, 24> slot_src{};
    array<int, 24> slot_dst{};
    vector<int> pre_false_bits;
    int aval[2]{0, 0};
};

struct State {
    uint8_t r = 0, c = 0;
    array<uint8_t, 24> a{};
    bool operator==(const State& o) const { return r == o.r && c == o.c && a == o.a; }
};

static const int coord_of_bit[5] = {0, 4, 3, 2, 1};
static const int inv_move[4] = {1, 0, 3, 2};

struct SolverData {
    int nvars = 0;
    vector<Clause> clauses;
    array<int, 130> init_bits{};
    array<int, 130> goal_bits{};
    vector<Move> moves;
    vector<array<int, 2>> action_bits;
    vector<array<int, 4>> action_dir_aux;
    array<array<uint8_t, 24>, 25> pos_of_slot{};
    State init, goal;
};

static int lit_value(int lit, const vector<int>& asg) {
    int v = asg[abs(lit)];
    if (v < 0) return -1;
    bool val = (v == 1);
    if (lit < 0) val = !val;
    return val ? 1 : 0;
}

static bool read_cnf(const string& path, SolverData& d) {
    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 fmt;
            int m;
            in >> fmt >> d.nvars >> m;
        } else {
            Clause c;
            int lit = stoi(tok);
            while (lit != 0) {
                c.lits.push_back(lit);
                in >> lit;
            }
            d.clauses.push_back(std::move(c));
        }
    }
    return d.nvars > 0 && !d.clauses.empty();
}

static bool parse_equiv_const(const vector<Clause>& clauses, int aux, int lo, int hi, array<int, 130>& out, int base) {
    bool any = false;
    for (const auto& c : clauses) {
        if (c.lits.size() == 2 && c.lits[0] == -aux) {
            int v = abs(c.lits[1]);
            if (lo <= v && v <= hi) {
                out[v - base] = c.lits[1] > 0 ? 1 : 0;
                any = true;
            }
        }
    }
    return any;
}

static bool extract_structure(SolverData& d) {
    int init_and = -1;
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 1) {
            init_and = abs(c.lits[0]);
            break;
        }
    }
    if (init_and <= 131) return false;
    int state0 = 1, state_size = 130, state1 = -1;

    vector<int> init_children;
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 2 && c.lits[0] == -init_and && c.lits[1] > 0) {
            init_children.push_back(c.lits[1]);
        }
    }
    if (init_children.size() != 26) return false;
    d.init_bits.fill(0);
    for (int aux : init_children) parse_equiv_const(d.clauses, aux, 1, 130, d.init_bits, 1);

    unordered_map<int, pair<int, int>> xnor;
    unordered_map<int, vector<int>> and5;
    unordered_map<int, vector<const Clause*>> tern_by_first;
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 3) tern_by_first[abs(c.lits[0])].push_back(&c);
        if (c.lits.size() == 6 && c.lits[0] > 0) {
            bool ok = true;
            vector<int> ch;
            for (int i = 1; i < 6; ++i) {
                if (c.lits[i] >= 0) ok = false;
                ch.push_back(-c.lits[i]);
            }
            if (ok) and5[c.lits[0]] = ch;
        }
    }
    for (auto& kv : tern_by_first) {
        int z = kv.first;
        if (kv.second.size() != 4) continue;
        unordered_set<int> vs;
        for (auto* cp : kv.second) {
            for (int l : cp->lits) if (abs(l) != z) vs.insert(abs(l));
        }
        if (vs.size() == 2) {
            auto it = vs.begin();
            int a = *it++;
            int b = *it;
            if (a > b) swap(a, b);
            xnor[z] = {a, b};
        }
    }

    struct DirDef { int z, a, b, va, vb; };
    vector<DirDef> dir_defs;
    unordered_map<int, vector<pair<int,int>>> bin_neg;
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 2 && c.lits[0] < 0) bin_neg[-c.lits[0]].push_back({abs(c.lits[1]), c.lits[1] > 0});
    }
    for (const auto& c : d.clauses) {
        if (c.lits.size() != 3 || c.lits[0] <= 0) continue;
        int z = c.lits[0];
        auto it = bin_neg.find(z);
        if (it == bin_neg.end() || it->second.size() < 2) continue;
        int a = abs(c.lits[1]), b = abs(c.lits[2]);
        int va = c.lits[1] < 0 ? 1 : 0;
        int vb = c.lits[2] < 0 ? 1 : 0;
        bool ha = false, hb = false;
        for (auto [v, s] : it->second) {
            if (v == a && s == va) ha = true;
            if (v == b && s == vb) hb = true;
        }
        if (ha && hb) {
            if (a > b) { swap(a, b); swap(va, vb); }
            dir_defs.push_back({z, a, b, va, vb});
        }
    }
    unordered_map<long long, vector<DirDef>> groups;
    for (auto dd : dir_defs) {
        long long key = ((long long)dd.a << 32) | (unsigned)dd.b;
        groups[key].push_back(dd);
    }
    vector<pair<int, vector<DirDef>>> action_groups;
    for (auto& kv : groups) {
        auto v = kv.second;
        if (v.size() != 4) continue;
        sort(v.begin(), v.end(), [](const DirDef& x, const DirDef& y){ return x.z < y.z; });
        bool combos[2][2] = {};
        for (auto& dd : v) combos[dd.va][dd.vb] = true;
        if (combos[0][0] && combos[0][1] && combos[1][0] && combos[1][1]) action_groups.push_back({v[0].z, v});
    }
    sort(action_groups.begin(), action_groups.end(),
         [](const auto& x, const auto& y){ return x.first < y.first; });
    if (action_groups.empty()) return false;
    d.action_bits.clear();
    d.action_dir_aux.clear();
    for (auto& ag : action_groups) {
        array<int,2> ab{ag.second[0].a, ag.second[0].b};
        array<int,4> dz{};
        for (int i = 0; i < 4; ++i) {
            dz[i] = ag.second[i].z;
        }
        d.action_bits.push_back(ab);
        d.action_dir_aux.push_back(dz);
    }
    state1 = d.action_bits[0][0] - state_size;
    if (state1 <= state0 + state_size || state1 + state_size + 5 > d.nvars) return false;

    d.moves.clear();
    for (int mi = 0; mi < 4; ++mi) {
        int dir = d.action_dir_aux[0][mi];
        Move mv;
        mv.bit_src.fill(-1);
        mv.aval[0] = mi >= 2 ? 1 : 0;
        mv.aval[1] = (mi == 1 || mi == 3) ? 1 : 0;
        for (const auto& c : d.clauses) {
            if (c.lits.size() == 2 && c.lits[0] == -dir) {
                if (c.lits[1] < 0 && abs(c.lits[1]) < state1) mv.pre_false_bits.push_back(abs(c.lits[1]) - 1);
                if (c.lits[1] > 0 && and5.count(c.lits[1])) {
                    for (int z : and5[c.lits[1]]) {
                        if (!xnor.count(z)) continue;
                        auto [a, b] = xnor[z];
                        int oldv = -1, newv = -1;
                        if (state1 <= a && a < state1 + state_size && 1 <= b && b <= state_size) {
                            newv = a - state1; oldv = b - 1;
                        } else if (state1 <= b && b < state1 + state_size && 1 <= a && a <= state_size) {
                            newv = b - state1; oldv = a - 1;
                        }
                        if (oldv >= 0) mv.bit_src[newv] = oldv;
                    }
                }
            }
        }
        for (int i = 0; i < 130; ++i) if (mv.bit_src[i] < 0) return false;
        for (int ng = 2; ng < 26; ++ng) mv.slot_src[ng - 2] = mv.bit_src[ng * 5] / 5 - 2;
        mv.slot_dst.fill(0);
        for (int i = 0; i < 24; ++i) mv.slot_dst[mv.slot_src[i]] = i;
        d.moves.push_back(mv);
    }

    const Clause& last = d.clauses.back();
    if (last.lits.size() < 2) return false;
    int final_goal = 0;
    for (int l : last.lits) final_goal = max(final_goal, abs(l));
    vector<int> goal_children;
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 2 && c.lits[0] == -final_goal && c.lits[1] > 0) goal_children.push_back(c.lits[1]);
    }
    if (goal_children.size() != 26) return false;
    vector<pair<int,int>> gv;
    int minbit = d.nvars + 1;
    for (int aux : goal_children) {
        for (const auto& c : d.clauses) {
            if (c.lits.size() == 2 && c.lits[0] == -aux) {
                int v = abs(c.lits[1]);
                if (v > 1000) {
                    gv.push_back({v, c.lits[1] > 0 ? 1 : 0});
                    minbit = min(minbit, v);
                }
            }
        }
    }
    if (gv.size() != 130) return false;
    d.goal_bits.fill(0);
    for (auto [v, val] : gv) {
        int idx = v - minbit;
        if (idx < 0 || idx >= 130) return false;
        d.goal_bits[idx] = val;
    }
    return true;
}

static string code_group(const array<int,130>& bits, int g) {
    string s;
    for (int i = 0; i < 5; ++i) s.push_back(bits[g * 5 + i] ? '1' : '0');
    return s;
}

static bool decode_states(SolverData& d) {
    array<string, 24> goal_codes;
    unordered_map<string, int> label;
    int gr = -1, gc = -1;
    for (int i = 0; i < 5; ++i) {
        if (d.goal_bits[i]) gr = coord_of_bit[i];
        if (d.goal_bits[5 + i]) gc = coord_of_bit[i];
    }
    if (gr != 0 || gc != 0) return false;
    for (int g = 2; g < 26; ++g) {
        string s = code_group(d.goal_bits, g);
        if (label.count(s)) return false;
        label[s] = g - 2;
        goal_codes[g - 2] = s;
    }
    auto decode = [&](const array<int,130>& bits, State& st) -> bool {
        int r = -1, c = -1;
        for (int i = 0; i < 5; ++i) {
            if (bits[i]) r = coord_of_bit[i];
            if (bits[5 + i]) c = coord_of_bit[i];
        }
        if (r < 0 || c < 0) return false;
        st.r = r; st.c = c;
        for (int g = 2; g < 26; ++g) {
            string s = code_group(bits, g);
            auto it = label.find(s);
            if (it == label.end()) return false;
            st.a[g - 2] = it->second;
        }
        return true;
    };
    if (!decode(d.init_bits, d.init)) return false;
    if (!decode(d.goal_bits, d.goal)) return false;
    for (int i = 0; i < 24; ++i) if (d.goal.a[i] != i) return false;

    for (auto& row : d.pos_of_slot) row.fill(255);
    d.pos_of_slot[0].fill(255);
    d.pos_of_slot[0][0] = 1;
    for (int i = 0; i < 24; ++i) d.pos_of_slot[0][i] = i + 1;
    queue<pair<int,int>> q;
    bool seen[5][5] = {};
    seen[0][0] = true;
    q.push({0,0});
    auto can = [&](int r, int c, int mi) {
        for (int idx : d.moves[mi].pre_false_bits) {
            int g = idx / 5, b = idx % 5;
            if (g == 0 && coord_of_bit[b] == r) return false;
            if (g == 1 && coord_of_bit[b] == c) return false;
        }
        return true;
    };
    auto rc2 = [&](int r, int c, int mi) {
        if (mi == 0) --c; else if (mi == 1) ++c; else if (mi == 2) --r; else ++r;
        return pair<int,int>{r,c};
    };
    while (!q.empty()) {
        auto [r,c] = q.front(); q.pop();
        int key = r * 5 + c;
        int blank = key;
        for (int mi = 0; mi < 4; ++mi) {
            if (!can(r,c,mi)) continue;
            auto [nr,nc] = rc2(r,c,mi);
            if (nr < 0 || nr >= 5 || nc < 0 || nc >= 5) return false;
            int nkey = nr * 5 + nc;
            int neighbor = nkey;
            array<uint8_t,24> nf{};
            for (int s = 0; s < 24; ++s) {
                int pos = d.pos_of_slot[key][s];
                int newpos = (pos == neighbor) ? blank : pos;
                nf[d.moves[mi].slot_dst[s]] = newpos;
            }
            if (!seen[nr][nc]) {
                d.pos_of_slot[nkey] = nf;
                seen[nr][nc] = true;
                q.push({nr,nc});
            } else if (d.pos_of_slot[nkey] != nf) {
                return false;
            }
        }
    }
    return true;
}

static bool can_move(const SolverData& d, const State& s, int mi) {
    for (int idx : d.moves[mi].pre_false_bits) {
        int g = idx / 5, b = idx % 5;
        if (g == 0 && coord_of_bit[b] == s.r) return false;
        if (g == 1 && coord_of_bit[b] == s.c) return false;
    }
    return true;
}

static State apply_move(const SolverData& d, const State& s, int mi) {
    State ns;
    ns.r = s.r; ns.c = s.c;
    if (mi == 0) --ns.c; else if (mi == 1) ++ns.c; else if (mi == 2) --ns.r; else ++ns.r;
    for (int i = 0; i < 24; ++i) ns.a[i] = s.a[d.moves[mi].slot_src[i]];
    return ns;
}

static array<uint8_t,25> board_of(const SolverData& d, const State& s) {
    array<uint8_t,25> b{};
    b.fill(0);
    b[s.r * 5 + s.c] = 0;
    const auto& f = d.pos_of_slot[s.r * 5 + s.c];
    for (int slot = 0; slot < 24; ++slot) b[f[slot]] = s.a[slot] + 1;
    return b;
}

static int heuristic(const SolverData& d, const State& s) {
    auto b = board_of(d, s);
    int h = 0;
    for (int pos = 0; pos < 25; ++pos) {
        int t = b[pos];
        if (!t) continue;
        h += abs(pos / 5 - t / 5) + abs(pos % 5 - t % 5);
    }
    for (int r = 0; r < 5; ++r) {
        vector<int> cols;
        for (int c = 0; c < 5; ++c) {
            int t = b[r * 5 + c];
            if (t && t / 5 == r) cols.push_back(t % 5);
        }
        for (int i = 0; i < (int)cols.size(); ++i)
            for (int j = i + 1; j < (int)cols.size(); ++j)
                if (cols[i] > cols[j]) h += 2;
    }
    for (int c = 0; c < 5; ++c) {
        vector<int> rows;
        for (int r = 0; r < 5; ++r) {
            int t = b[r * 5 + c];
            if (t && t % 5 == c) rows.push_back(t / 5);
        }
        for (int i = 0; i < (int)rows.size(); ++i)
            for (int j = i + 1; j < (int)rows.size(); ++j)
                if (rows[i] > rows[j]) h += 2;
    }
    return h;
}

struct SearchCtx {
    const SolverData* d;
    chrono::steady_clock::time_point deadline;
    vector<int> path, found;
    long long nodes = 0;
    int max_depth = 0;
};

static int dfs(SearchCtx& ctx, const State& s, int g, int bound, int last) {
    if (chrono::steady_clock::now() > ctx.deadline) return 1000000;
    int h = heuristic(*ctx.d, s);
    int f = g + h;
    if (f > bound) return f;
    if (s == ctx.d->goal) {
        ctx.found = ctx.path;
        return -1;
    }
    if (g >= ctx.max_depth) return 1000000;
    ctx.nodes++;
    int best = 1000000;
    array<pair<int,int>,4> order;
    for (int mi = 0; mi < 4; ++mi) {
        int score = 100000;
        if (!(last >= 0 && inv_move[mi] == last) && can_move(*ctx.d, s, mi)) {
            State ns = apply_move(*ctx.d, s, mi);
            score = heuristic(*ctx.d, ns);
        }
        order[mi] = {score, mi};
    }
    sort(order.begin(), order.end());
    for (auto [score, mi] : order) {
        if (score >= 100000) continue;
        State ns = apply_move(*ctx.d, s, mi);
        ctx.path.push_back(mi);
        int r = dfs(ctx, ns, g + 1, bound, mi);
        ctx.path.pop_back();
        if (r == -1) return -1;
        best = min(best, r);
    }
    return best;
}

static bool solve_ida(const SolverData& d, int max_depth, vector<int>& out) {
    SearchCtx ctx;
    ctx.d = &d;
    ctx.max_depth = max_depth;
    ctx.deadline = chrono::steady_clock::now() + chrono::seconds(540);
    int bound = heuristic(d, d.init);
    while (bound <= max_depth) {
        int r = dfs(ctx, d.init, 0, bound, -1);
        cerr << "c ida bound " << bound << " nodes " << ctx.nodes << "\n";
        if (r == -1) {
            out = ctx.found;
            return true;
        }
        if (r >= 1000000) return false;
        bound = r;
    }
    return false;
}

static bool unit_propagate_model(const SolverData& d, const vector<int>& moves, vector<int>& asg) {
    asg.assign(d.nvars + 1, -1);
    vector<vector<int>> occ(2 * (d.nvars + 1));
    auto lit_index = [&](int lit) {
        int v = abs(lit);
        return 2 * v + (lit < 0);
    };
    for (int ci = 0; ci < (int)d.clauses.size(); ++ci) {
        for (int l : d.clauses[ci].lits) occ[lit_index(l)].push_back(ci);
    }
    queue<int> q;
    auto assign_lit = [&](int lit) -> bool {
        int v = abs(lit), val = lit > 0 ? 1 : 0;
        if (asg[v] >= 0) return asg[v] == val;
        asg[v] = val;
        q.push(lit);
        return true;
    };
    for (const auto& c : d.clauses) {
        if (c.lits.size() == 1 && !assign_lit(c.lits[0])) return false;
    }
    int steps = d.action_bits.size();
    for (int t = 0; t < steps; ++t) {
        int mi = moves[t];
        if (!assign_lit(d.moves[mi].aval[0] ? d.action_bits[t][0] : -d.action_bits[t][0])) return false;
        if (!assign_lit(d.moves[mi].aval[1] ? d.action_bits[t][1] : -d.action_bits[t][1])) return false;
    }
    while (!q.empty()) {
        int lit = q.front(); q.pop();
        int false_lit = -lit;
        for (int ci : occ[lit_index(false_lit)]) {
            const auto& c = d.clauses[ci];
            bool sat = false;
            int un = 0, last_un = 0;
            for (int l : c.lits) {
                int lv = lit_value(l, asg);
                if (lv == 1) { sat = true; break; }
                if (lv < 0) { un++; last_un = l; }
            }
            if (sat) continue;
            if (un == 0) return false;
            if (un == 1 && !assign_lit(last_un)) return false;
        }
    }
    for (int v = 1; v <= d.nvars; ++v) if (asg[v] < 0) asg[v] = 0;
    for (const auto& c : d.clauses) {
        bool sat = false;
        for (int l : c.lits) {
            int val = asg[abs(l)];
            bool b = val == 1;
            if (l < 0) b = !b;
            if (b) { sat = true; break; }
        }
        if (!sat) return false;
    }
    return true;
}

int main(int argc, char** argv) {
    if (argc != 3) {
        fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }
    SolverData d;
    if (!read_cnf(argv[1], d)) {
        cout << "c could not read cnf\ns UNKNOWN\n";
        return 0;
    }
    if (!extract_structure(d)) {
        cout << "c unsupported sliding-puzzle structure\ns UNKNOWN\n";
        return 0;
    }
    if (!decode_states(d)) {
        cout << "c unsupported sliding-puzzle state decoding\ns UNKNOWN\n";
        return 0;
    }
    int steps = d.action_bits.size();
    cerr << "c detected compact 24-puzzle steps " << steps << "\n";
    vector<int> path;
    if (!solve_ida(d, steps, path)) {
        cout << "c no SAT path found within encoded bound\ns UNKNOWN\n";
        return 0;
    }
    State st = d.init;
    vector<int> full = path;
    for (int mi : path) st = apply_move(d, st, mi);
    while ((int)full.size() < steps) {
        bool moved = false;
        for (int mi = 0; mi < 4; ++mi) {
            if (can_move(d, st, mi)) {
                full.push_back(mi);
                st = apply_move(d, st, mi);
                moved = true;
                break;
            }
        }
        if (!moved) break;
    }
    if ((int)full.size() != steps) {
        cout << "c could not extend trace to encoded transition count\ns UNKNOWN\n";
        return 0;
    }
    vector<int> asg;
    if (!unit_propagate_model(d, full, asg)) {
        cout << "c recovered trace did not propagate to a verified CNF model\ns UNKNOWN\n";
        return 0;
    }
    ofstream proof(argv[2]);
    proof.close();
    cout << "s SATISFIABLE\n";
    cout << "v";
    for (int v = 1; v <= d.nvars; ++v) {
        if ((v - 1) % 15 == 0 && v != 1) cout << "\nv";
        cout << ' ' << (asg[v] ? v : -v);
    }
    cout << " 0\n";
    return 10;
}
