#include <algorithm>
#include <array>
#include <atomic>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

struct CNF {
    int nvars = 0;
    std::vector<std::vector<int>> clauses;
    std::vector<int> units;
    std::vector<std::vector<int>> occ;
};

static bool read_cnf(const char* path, CNF& cnf) {
    std::ifstream in(path);
    if (!in) return false;
    std::string tok;
    while (in >> tok) {
        if (tok == "c") {
            std::string rest;
            std::getline(in, rest);
        } else if (tok == "p") {
            std::string fmt;
            int m;
            in >> fmt >> cnf.nvars >> m;
            cnf.clauses.reserve(m);
        } else {
            int lit = std::stoi(tok);
            std::vector<int> c;
            while (lit != 0) {
                c.push_back(lit);
                in >> lit;
            }
            if (c.empty()) return false;
            if (c.size() == 1) cnf.units.push_back(c[0]);
            cnf.clauses.push_back(std::move(c));
        }
    }
    cnf.occ.assign(cnf.nvars + 1, {});
    for (int i = 0; i < (int)cnf.clauses.size(); ++i) {
        for (int lit : cnf.clauses[i]) {
            int v = std::abs(lit);
            if (v <= 0 || v > cnf.nvars) return false;
            cnf.occ[v].push_back(i);
        }
    }
    return true;
}

struct Propagator {
    const CNF& cnf;
    std::vector<signed char> val;
    std::vector<int> trail;
    std::vector<std::array<int, 2>> watch_pos;
    std::vector<std::vector<int>> watches;
    size_t qhead = 0;

    explicit Propagator(const CNF& f) : cnf(f), val(f.nvars + 1, -1) {
        watch_pos.resize(cnf.clauses.size(), {0, 0});
        watches.assign(2 * (cnf.nvars + 1), {});
        for (int ci = 0; ci < (int)cnf.clauses.size(); ++ci) {
            const auto& c = cnf.clauses[ci];
            if (c.size() >= 2) {
                watch_pos[ci] = {0, 1};
                watches[lit_index(c[0])].push_back(ci);
                watches[lit_index(c[1])].push_back(ci);
            }
        }
    }

    int lit_index(int lit) const {
        int v = std::abs(lit);
        return 2 * v + (lit < 0 ? 1 : 0);
    }

    bool lit_true(int lit) const {
        signed char vv = val[std::abs(lit)];
        return vv != -1 && ((lit > 0) == (vv == 1));
    }

    bool lit_false(int lit) const {
        signed char vv = val[std::abs(lit)];
        return vv != -1 && ((lit > 0) != (vv == 1));
    }

    void reset() {
        for (int v : trail) val[v] = -1;
        trail.clear();
        qhead = 0;
    }

    bool assign_var(int v, bool b) {
        signed char nb = b ? 1 : 0;
        if (val[v] != -1) return val[v] == nb;
        val[v] = nb;
        trail.push_back(v);
        return true;
    }

    bool assign_lit(int lit) {
        return assign_var(std::abs(lit), lit > 0);
    }

    bool init_units() {
        for (int lit : cnf.units) {
            if (!assign_lit(lit)) return false;
        }
        return true;
    }

    bool propagate() {
        while (qhead < trail.size()) {
            int vchanged = trail[qhead++];
            bool b = val[vchanged] == 1;
            int false_lit = b ? -vchanged : vchanged;
            auto& wl = watches[lit_index(false_lit)];
            for (size_t wi = 0; wi < wl.size();) {
                int ci = wl[wi];
                const auto& c = cnf.clauses[ci];
                int w0 = watch_pos[ci][0];
                int w1 = watch_pos[ci][1];
                int false_watch_slot;
                int other_slot;
                if (c[w0] == false_lit) {
                    false_watch_slot = 0;
                    other_slot = 1;
                } else if (c[w1] == false_lit) {
                    false_watch_slot = 1;
                    other_slot = 0;
                } else {
                    ++wi;
                    continue;
                }
                int other_lit = c[watch_pos[ci][other_slot]];
                if (lit_true(other_lit)) {
                    ++wi;
                    continue;
                }
                bool moved = false;
                for (int k = 0; k < (int)c.size(); ++k) {
                    if (k == watch_pos[ci][0] || k == watch_pos[ci][1]) continue;
                    if (!lit_false(c[k])) {
                        watch_pos[ci][false_watch_slot] = k;
                        watches[lit_index(c[k])].push_back(ci);
                        wl[wi] = wl.back();
                        wl.pop_back();
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                if (lit_false(other_lit)) return false;
                if (!assign_lit(other_lit)) return false;
                ++wi;
            }
        }
        return true;
    }
};

static std::vector<std::vector<int>> detect_input_blocks(const CNF& cnf) {
    std::vector<int> used;
    int hi = std::min(cnf.nvars, 999);
    for (int v = 300; v <= hi; ++v) {
        if (!cnf.occ[v].empty()) used.push_back(v);
    }
    std::vector<std::vector<int>> blocks;
    for (size_t i = 0; i < used.size();) {
        size_t j = i + 1;
        while (j < used.size() && used[j] == used[j - 1] + 1) ++j;
        size_t len = j - i;
        if (len >= 8 && len % 8 == 0) {
            for (size_t k = i; k < j; k += 8) {
                blocks.emplace_back(used.begin() + k, used.begin() + k + 8);
            }
        }
        i = j;
    }
    return blocks;
}

struct Stats {
    long long tests = 0;
    long long leaves = 0;
    long long full_leaves = 0;
    long long bad_open = 0;
    std::array<long long, 17> depth_leaves{};
};

static bool prefix_conflicts(Propagator& p, const std::vector<int>& vars,
                             int prefix, int len) {
    p.reset();
    if (!p.init_units()) return true;
    for (int k = 0; k < len; ++k) {
        bool bit = ((prefix >> k) & 1) != 0;
        if (!p.assign_var(vars[k], bit)) return true;
    }
    return !p.propagate();
}

static void explore(Propagator& p, const std::vector<int>& vars, int prefix,
                    int len, int start_depth, Stats& st) {
    bool conflict = false;
    if (len >= start_depth || len == (int)vars.size()) {
        ++st.tests;
        conflict = prefix_conflicts(p, vars, prefix, len);
    }
    if (conflict) {
        ++st.leaves;
        if (len == (int)vars.size()) ++st.full_leaves;
        ++st.depth_leaves[len];
        return;
    }
    if (len == (int)vars.size()) {
        ++st.bad_open;
        return;
    }
    explore(p, vars, prefix, len + 1, start_depth, st);
    explore(p, vars, prefix | (1 << len), len + 1, start_depth, st);
}

int main(int argc, char** argv) {
    if (argc < 2 || argc > 3) {
        std::cerr << "usage: prefix_cover <formula.cnf> [start_depth]\n";
        return 1;
    }
    int start_depth = (argc == 3) ? std::atoi(argv[2]) : 0;
    CNF cnf;
    if (!read_cnf(argv[1], cnf)) return 1;
    auto blocks = detect_input_blocks(cnf);
    if (blocks.size() != 2) {
        std::cerr << "blocks=" << blocks.size() << "\n";
        return 1;
    }
    std::vector<int> vars;
    vars.reserve(16);
    vars.insert(vars.end(), blocks[0].begin(), blocks[0].end());
    vars.insert(vars.end(), blocks[1].begin(), blocks[1].end());
    cnf.occ.clear();
    cnf.occ.shrink_to_fit();
    Propagator p(cnf);
    Stats st;
    explore(p, vars, 0, 0, start_depth, st);
    std::cout << "start_depth " << start_depth << "\n";
    std::cout << "tests " << st.tests << "\n";
    std::cout << "leaves " << st.leaves << "\n";
    std::cout << "full_leaves " << st.full_leaves << "\n";
    std::cout << "open " << st.bad_open << "\n";
    for (int d = 0; d <= 16; ++d) {
        if (st.depth_leaves[d]) std::cout << "depth " << d << " " << st.depth_leaves[d] << "\n";
    }
    return 0;
}
