#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <string>
#include <utility>
#include <vector>

struct Clause {
    int off;
    int len;
};

static inline int lit_index(int lit) {
    int v = std::abs(lit) - 1;
    return (v << 1) ^ (lit < 0);
}

static inline int neg_index(int idx) { return idx ^ 1; }

struct SolverProbe {
    int nvars = 0;
    std::vector<int> lits;
    std::vector<Clause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int8_t> val; // -1 false, 0 undef, 1 true
    std::deque<int> q;
    int64_t visits = 0;

    int lit_value(int lit) const {
        int8_t v = val[std::abs(lit)];
        if (v == 0) return 0;
        bool pos = lit > 0;
        return (v == 1) == pos ? 1 : -1;
    }

    bool assign_lit(int lit) {
        int v = std::abs(lit);
        int8_t want = lit > 0 ? 1 : -1;
        if (val[v] == want) return true;
        if (val[v] == -want) return false;
        val[v] = want;
        q.push_back(lit);
        return true;
    }

    bool load(FILE* f) {
        char buf[1 << 20];
        int declared_clauses = 0;
        std::vector<int> cur;
        cur.reserve(16);
        while (std::fgets(buf, sizeof(buf), f)) {
            if (buf[0] == 'c' || buf[0] == '\n' || buf[0] == '\r') continue;
            if (buf[0] == 'p') {
                char tmp1[16], tmp2[16];
                if (std::sscanf(buf, "%15s %15s %d %d", tmp1, tmp2, &nvars, &declared_clauses) != 4) return false;
                val.assign(nvars + 1, 0);
                watches.assign(2 * nvars, {});
                clauses.reserve(declared_clauses);
                continue;
            }
            char* p = buf;
            while (*p) {
                char* end = nullptr;
                long x = std::strtol(p, &end, 10);
                if (end == p) break;
                p = end;
                if (x == 0) {
                    Clause c{(int)lits.size(), (int)cur.size()};
                    clauses.push_back(c);
                    lits.insert(lits.end(), cur.begin(), cur.end());
                    if (c.len == 1) {
                        if (!assign_lit(lits[c.off])) return false;
                    } else if (c.len >= 2) {
                        watches[lit_index(lits[c.off])].push_back((int)clauses.size() - 1);
                        watches[lit_index(lits[c.off + 1])].push_back((int)clauses.size() - 1);
                    }
                    cur.clear();
                } else {
                    cur.push_back((int)x);
                }
            }
        }
        return nvars > 0;
    }

    bool propagate() {
        while (!q.empty()) {
            int assigned = q.front();
            q.pop_front();
            int false_idx = lit_index(-assigned);
            auto& ws = watches[false_idx];
            size_t i = 0;
            while (i < ws.size()) {
                ++visits;
                int ci = ws[i];
                Clause c = clauses[ci];
                int* base = lits.data() + c.off;
                if (lit_index(base[0]) != false_idx) std::swap(base[0], base[1]);
                int other = base[1];
                if (lit_value(other) == 1) {
                    ++i;
                    continue;
                }
                bool moved = false;
                for (int k = 2; k < c.len; ++k) {
                    if (lit_value(base[k]) != -1) {
                        std::swap(base[0], base[k]);
                        ws[i] = ws.back();
                        ws.pop_back();
                        watches[lit_index(base[0])].push_back(ci);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                if (lit_value(other) == -1) {
                    std::printf("conflict clause=%d len=%d\n", ci, c.len);
                    return false;
                }
                if (!assign_lit(other)) {
                    std::printf("conflict assign %d\n", other);
                    return false;
                }
                ++i;
            }
        }
        return true;
    }

    int lit_value_default_false(int lit) const {
        int8_t v = val[std::abs(lit)];
        if (v == 0) v = -1;
        bool pos = lit > 0;
        return (v == 1) == pos ? 1 : -1;
    }

    int64_t count_unsat_default_false() const {
        int64_t bad = 0;
        for (const Clause& c : clauses) {
            bool ok = false;
            for (int k = 0; k < c.len; ++k) {
                if (lit_value_default_false(lits[c.off + k]) == 1) {
                    ok = true;
                    break;
                }
            }
            bad += !ok;
        }
        return bad;
    }
};

int main(int argc, char** argv) {
    if (argc != 2) {
        std::fprintf(stderr, "usage: prop_probe formula.cnf\n");
        return 2;
    }
    auto t0 = std::chrono::steady_clock::now();
    FILE* f = std::fopen(argv[1], "r");
    if (!f) {
        std::perror(argv[1]);
        return 2;
    }
    SolverProbe p;
    bool ok = p.load(f);
    std::fclose(f);
    auto t1 = std::chrono::steady_clock::now();
    if (!ok) {
        std::printf("load-failed\n");
        return 1;
    }
    bool sat_under_units = p.propagate();
    auto t2 = std::chrono::steady_clock::now();
    int64_t bad_default_false = p.count_unsat_default_false();
    int assigned = 0;
    for (int v = 1; v <= p.nvars; ++v) assigned += p.val[v] != 0;
    auto ms1 = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
    auto ms2 = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    std::printf("vars=%d clauses=%zu lits=%zu assigned=%d result=%s default_false_bad=%lld load_ms=%lld prop_ms=%lld visits=%lld\n",
                p.nvars, p.clauses.size(), p.lits.size(), assigned,
                sat_under_units ? "no-conflict" : "conflict",
                (long long)bad_default_false,
                (long long)ms1, (long long)ms2, (long long)p.visits);
    return 0;
}
