#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <vector>

using namespace std;

struct CNF {
    int vars = 0;
    vector<int> lits;
    vector<uint32_t> start;
    vector<uint16_t> len;
    vector<int> occ_lit;
    vector<uint32_t> occ_start;
};

static bool parse_dimacs(const string& path, CNF& f) {
    ifstream in(path);
    if (!in) return false;
    string tok;
    int clauses = 0;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string cnf;
            in >> cnf >> f.vars >> clauses;
            f.start.reserve((size_t)clauses + 1);
            f.len.reserve(clauses);
            break;
        }
    }
    if (f.vars <= 0 || clauses <= 0) return false;
    vector<uint32_t> occ_count((size_t)2 * f.vars + 2, 0);
    int lit;
    vector<int> cur;
    while (in >> lit) {
        if (lit == 0) {
            f.start.push_back((uint32_t)f.lits.size());
            f.len.push_back((uint16_t)cur.size());
            for (int x : cur) {
                f.lits.push_back(x);
                int idx = (abs(x) - 1) * 2 + (x > 0);
                occ_count[idx]++;
            }
            cur.clear();
        } else {
            cur.push_back(lit);
        }
    }
    f.start.push_back((uint32_t)f.lits.size());
    f.occ_start.assign((size_t)2 * f.vars + 3, 0);
    for (int i = 0; i < 2 * f.vars + 2; ++i) f.occ_start[i + 1] = f.occ_start[i] + occ_count[i];
    f.occ_lit.assign(f.lits.size(), 0);
    vector<uint32_t> pos = f.occ_start;
    for (uint32_t c = 0; c < f.len.size(); ++c) {
        for (uint32_t p = f.start[c]; p < f.start[c + 1]; ++p) {
            int x = f.lits[p];
            int idx = (abs(x) - 1) * 2 + (x > 0);
            f.occ_lit[pos[idx]++] = (int)c;
        }
    }
    return (int)f.len.size() == clauses;
}

static inline bool lit_true(int lit, const vector<uint8_t>& val) {
    bool v = val[abs(lit)];
    return lit > 0 ? v : !v;
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "usage: sls formula.cnf [seconds] [seed-model]\n");
        return 1;
    }
    double seconds = argc >= 3 ? atof(argv[2]) : 30.0;
    CNF f;
    auto t0 = chrono::steady_clock::now();
    if (!parse_dimacs(argv[1], f)) {
        fprintf(stderr, "parse failed\n");
        return 1;
    }
    int C = (int)f.len.size();
    vector<int8_t> fixed(f.vars + 1, 0);
    for (int c = 0; c < C; ++c) {
        if (f.len[c] == 1) {
            int l = f.lits[f.start[c]];
            int8_t want = l > 0 ? 1 : -1;
            int v = abs(l);
            if (fixed[v] && fixed[v] != want) return 2;
            fixed[v] = want;
        }
    }
    uint64_t seed = 1469598103934665603ull ^ (uint64_t)f.vars * 1099511628211ull ^ (uint64_t)C;
    mt19937_64 rng(seed);
    vector<uint8_t> val(f.vars + 1, 0);
    vector<uint8_t> best_val;
    vector<uint16_t> sat(C, 0);
    vector<uint32_t> weight(C, 1);
    vector<int> unsat;
    vector<int> pos_unsat(C, -1);

    auto add_unsat = [&](int c) {
        if (pos_unsat[c] < 0) {
            pos_unsat[c] = (int)unsat.size();
            unsat.push_back(c);
        }
    };
    auto rem_unsat = [&](int c) {
        int p = pos_unsat[c];
        if (p >= 0) {
            int last = unsat.back();
            unsat[p] = last;
            pos_unsat[last] = p;
            unsat.pop_back();
            pos_unsat[c] = -1;
        }
    };

    vector<int8_t> seed_val(f.vars + 1, 0);
    if (argc >= 4) {
        ifstream sm(argv[3]);
        string s;
        while (sm >> s) {
            if (s == "v") continue;
            if (s == "s") {
                string rest;
                getline(sm, rest);
                continue;
            }
            int l = atoi(s.c_str());
            if (l != 0 && abs(l) <= f.vars) seed_val[abs(l)] = l > 0 ? 1 : -1;
        }
    }

    auto initialize = [&](int mode) {
        unsat.clear();
        fill(pos_unsat.begin(), pos_unsat.end(), -1);
        for (int v = 1; v <= f.vars; ++v) {
            if (fixed[v] > 0) val[v] = 1;
            else if (fixed[v] < 0) val[v] = 0;
            else if (seed_val[v]) val[v] = seed_val[v] > 0;
            else if (mode == 0) val[v] = 0;
            else if (mode == 1) val[v] = 1;
            else val[v] = (uint8_t)(rng() & 1);
        }
        for (int c = 0; c < C; ++c) {
            uint16_t s = 0;
            for (uint32_t p = f.start[c]; p < f.start[c + 1]; ++p) s += lit_true(f.lits[p], val);
            sat[c] = s;
            if (s == 0) add_unsat(c);
        }
    };

    auto flip_var = [&](int v) {
        bool old = val[v];
        val[v] = !old;
        for (int sign = 0; sign < 2; ++sign) {
            int idx = (v - 1) * 2 + sign;
            bool literal_was_true = (sign == 1) ? old : !old;
            for (uint32_t op = f.occ_start[idx]; op < f.occ_start[idx + 1]; ++op) {
                int c = f.occ_lit[op];
                if (literal_was_true) {
                    if (--sat[c] == 0) add_unsat(c);
                } else {
                    if (sat[c]++ == 0) rem_unsat(c);
                }
            }
        }
    };

    auto weighted_score = [&](int v, int best_known) {
        bool old = val[v];
        int make = 0;
        int brk = 0;
        for (int sign = 0; sign < 2; ++sign) {
            int idx = (v - 1) * 2 + sign;
            bool literal_is_true = (sign == 1) ? old : !old;
            for (uint32_t op = f.occ_start[idx]; op < f.occ_start[idx + 1]; ++op) {
                int c = f.occ_lit[op];
                if (literal_is_true) {
                    if (sat[c] == 1) brk += (int)weight[c];
                } else {
                    if (sat[c] == 0) make += (int)weight[c];
                }
            }
        }
        (void)best_known;
        return make - brk;
    };

    int mode = 0;
    initialize(mode);
    int best = (int)unsat.size();
    long long flips = 0;
    auto deadline = t0 + chrono::milliseconds((int64_t)(seconds * 1000.0));
    uniform_int_distribution<int> percent(0, 99);
    while (chrono::steady_clock::now() < deadline) {
        if (unsat.empty()) {
            fprintf(stderr, "SAT flips=%lld\n", flips);
            cout << "s SATISFIABLE\nv ";
            int col = 0;
            for (int v = 1; v <= f.vars; ++v) {
                cout << (val[v] ? v : -v) << ' ';
                if (++col == 20) {
                    cout << "\nv ";
                    col = 0;
                }
            }
            cout << "0\n";
            return 10;
        }
        if ((int)unsat.size() < best) {
            best = (int)unsat.size();
            best_val = val;
            fprintf(stderr, "best unsat=%d flips=%lld\n", best, flips);
        }
        if (flips && flips % 2000000 == 0) {
            mode = (mode + 1) % 3;
            initialize(mode);
        }
        int c = unsat[rng() % unsat.size()];
        int chosen = 0;
        int best_score = INT32_MIN;
        if (percent(rng) < 6) {
            for (int tries = 0; tries < 8; ++tries) {
                int lit = f.lits[f.start[c] + (rng() % f.len[c])];
                int v = abs(lit);
                if (!fixed[v]) {
                    chosen = v;
                    break;
                }
            }
        }
        if (!chosen) {
            for (uint32_t p = f.start[c]; p < f.start[c + 1]; ++p) {
                int v = abs(f.lits[p]);
                if (fixed[v]) continue;
                int sc = weighted_score(v, best_score);
                if (sc > best_score || (sc == best_score && (rng() & 1))) {
                    best_score = sc;
                    chosen = v;
                }
            }
        }
        if (!chosen) {
            mode = (mode + 1) % 3;
            initialize(mode);
            continue;
        }
        if (best_score <= 0 && (flips & 31) == 0) {
            for (int uc : unsat) {
                if (weight[uc] < 1000000u) ++weight[uc];
            }
        }
        flip_var(chosen);
        ++flips;
    }
    if (!best_val.empty()) {
        ofstream bm("/workspace/sls_best.model");
        bm << "s UNKNOWN\nv ";
        int col = 0;
        for (int v = 1; v <= f.vars; ++v) {
            bm << (best_val[v] ? v : -v) << ' ';
            if (++col == 20) {
                bm << "\nv ";
                col = 0;
            }
        }
        bm << "0\n";
    }
    fprintf(stderr, "UNKNOWN best=%d current=%zu flips=%lld\n", best, unsat.size(), flips);
    cout << "s UNKNOWN\n";
    return 0;
}
