#include <algorithm>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <random>
#include <string>
#include <vector>

using namespace std;

struct Occ {
    int clause;
    int sign;
};

int main(int argc, char** argv) {
    if (argc < 2) {
        cerr << "usage: localsearch formula.cnf [seconds]\n";
        return 2;
    }
    double seconds = argc >= 3 ? stod(argv[2]) : 60.0;
    ifstream in(argv[1]);
    if (!in) return 2;
    int n = 0, m = 0;
    string tok;
    vector<vector<int>> clauses;
    while (in >> tok) {
        if (tok == "c") {
            string rest;
            getline(in, rest);
        } else if (tok == "p") {
            string cnf;
            in >> cnf >> n >> m;
        } else {
            int lit = stoi(tok);
            vector<int> c;
            while (lit != 0) {
                c.push_back(lit);
                in >> lit;
            }
            clauses.push_back(c);
        }
    }
    m = clauses.size();
    vector<vector<Occ>> occ(n + 1);
    vector<int> fixed(n + 1, -1);
    for (int i = 0; i < m; ++i) {
        if (clauses[i].size() == 1) fixed[abs(clauses[i][0])] = clauses[i][0] > 0;
        for (int lit : clauses[i]) occ[abs(lit)].push_back({i, lit > 0});
    }

    vector<uint8_t> val(n + 1);
    vector<int> satcnt(m), pos(m, -1), unsat, weight(m, 1);
    auto lit_sat = [&](int lit) { return val[abs(lit)] == (lit > 0); };
    auto remove_unsat = [&](int ci) {
        int p = pos[ci];
        if (p < 0) return;
        int last = unsat.back();
        unsat[p] = last;
        pos[last] = p;
        unsat.pop_back();
        pos[ci] = -1;
    };
    auto add_unsat = [&](int ci) {
        if (pos[ci] >= 0) return;
        pos[ci] = (int)unsat.size();
        unsat.push_back(ci);
    };

    mt19937_64 rng(1);
    auto init = [&](uint64_t seed) {
        rng.seed(seed);
        unsat.clear();
        fill(pos.begin(), pos.end(), -1);
        for (int v = 1; v <= n; ++v) val[v] = (rng() >> 63) & 1;
        for (int v = 1; v <= n; ++v) if (fixed[v] != -1) val[v] = fixed[v];
        for (int i = 0; i < m; ++i) {
            int c = 0;
            for (int lit : clauses[i]) c += lit_sat(lit);
            satcnt[i] = c;
            if (c == 0) add_unsat(i);
        }
    };
    auto flip = [&](int v) {
        val[v] ^= 1;
        for (const Occ& o : occ[v]) {
            int before = satcnt[o.clause];
            bool now_sat = val[v] == o.sign;
            int after = before + (now_sat ? 1 : -1);
            satcnt[o.clause] = after;
            if (before == 0 && after > 0) remove_unsat(o.clause);
            else if (before > 0 && after == 0) add_unsat(o.clause);
        }
    };
    auto score_var = [&](int v) {
        int s = 0;
        for (const Occ& o : occ[v]) {
            bool currently_sat = val[v] == o.sign;
            int c = o.clause;
            if (currently_sat) {
                if (satcnt[c] == 1) s -= weight[c];
            } else {
                if (satcnt[c] == 0) s += weight[c];
            }
        }
        return s;
    };

    auto start = chrono::steady_clock::now();
    int best = m;
    uint64_t flips = 0;
    for (uint64_t restart = 0;; ++restart) {
        init(1009 + restart * 7919);
        for (int step = 0; step < 50000000; ++step) {
            if (unsat.empty()) {
                cout << "SAT flips " << flips << "\n";
                for (int v = 1; v <= n; ++v) cout << (val[v] ? v : -v) << (v == n ? " 0\n" : " ");
                return 0;
            }
            if ((flips & 65535ULL) == 0) {
                double elapsed = chrono::duration<double>(chrono::steady_clock::now() - start).count();
                if (elapsed > seconds) {
                    cerr << "best " << best << " current " << unsat.size() << " flips " << flips << "\n";
                    return 1;
                }
                if ((int)unsat.size() < best) {
                    best = unsat.size();
                    cerr << "best " << best << " flips " << flips << " restart " << restart << "\n";
                }
            }
            int ci = unsat[rng() % unsat.size()];
            int chosen = 0;
            if ((rng() % 100) < 3) {
                vector<int> cand;
                for (int lit : clauses[ci]) if (fixed[abs(lit)] == -1) cand.push_back(abs(lit));
                if (cand.empty()) break;
                chosen = cand[rng() % cand.size()];
            } else {
                int best_score = -1000000000;
                vector<int> cand;
                for (int lit : clauses[ci]) {
                    int v = abs(lit);
                    if (fixed[v] != -1) continue;
                    int sc = score_var(v);
                    if (sc > best_score) {
                        best_score = sc;
                        cand.clear();
                        cand.push_back(v);
                    } else if (sc == best_score) {
                        cand.push_back(v);
                    }
                }
                if (cand.empty()) break;
                chosen = cand[rng() % cand.size()];
            }
            flip(chosen);
            ++flips;
            if ((flips % 20000) == 0) {
                for (int c : unsat) weight[c] += 1;
            }
        }
    }
}
