#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Scanner {
    static constexpr size_t BUFSZ = 1 << 20;
    FILE* f = nullptr;
    vector<char> buf;
    size_t pos = 0, len = 0;
    explicit Scanner(const char* path) : f(fopen(path, "rb")), buf(BUFSZ) {}
    ~Scanner() { if (f) fclose(f); }
    int get() {
        if (pos >= len) {
            len = f ? fread(buf.data(), 1, BUFSZ, f) : 0;
            pos = 0;
            if (!len) return EOF;
        }
        return (unsigned char)buf[pos++];
    }
    void skip_line() {
        int c;
        while ((c = get()) != EOF && c != '\n') {}
    }
    int next_non_ws() {
        while (true) {
            int c = get();
            if (c == EOF) return EOF;
            if (isspace(c)) continue;
            if (c == 'c') { skip_line(); continue; }
            return c;
        }
    }
    bool read_word_raw(string& out) {
        out.clear();
        int c;
        do {
            c = get();
            if (c == EOF) return false;
        } while (isspace(c));
        while (c != EOF && !isspace(c)) {
            out.push_back((char)c);
            c = get();
        }
        return true;
    }
    bool read_int(int& out) {
        int c = next_non_ws();
        if (c == EOF) return false;
        int sign = 1;
        if (c == '-') { sign = -1; c = get(); }
        int x = 0;
        while (c != EOF && isdigit(c)) {
            x = x * 10 + (c - '0');
            c = get();
        }
        out = sign * x;
        return true;
    }
    bool read_header(int& n, int& m) {
        int c;
        while ((c = next_non_ws()) != EOF) {
            if (c != 'p') { skip_line(); continue; }
            string fmt;
            if (!read_word_raw(fmt)) return false;
            return fmt == "cnf" && read_int(n) && read_int(m);
        }
        return false;
    }
};

static int digits(int x) {
    int d = 1;
    while (x >= 10) { x /= 10; d++; }
    return d;
}

static long long model_bytes(const vector<signed char>& sel) {
    long long b = 15;
    int col = 0;
    for (int v = 1; v < (int)sel.size(); ++v) {
        if (!sel[(size_t)v]) continue;
        if (col == 0) b++;
        b += 1 + digits(v) + (sel[(size_t)v] < 0 ? 1 : 0);
        if (++col == 12) { b++; col = 0; }
    }
    b += (col == 0 ? 4 : 3);
    return b;
}

struct Stats {
    long long covered = 0;
    long long uncovered = 0;
    long long conflicts = 0;
};

static Stats count_uncovered(const char* path, vector<unsigned char>& covered,
                             const vector<signed char>& selected,
                             vector<int>* pos, vector<int>* neg) {
    if (pos) fill(pos->begin(), pos->end(), 0);
    if (neg) fill(neg->begin(), neg->end(), 0);
    Stats st;
    Scanner sc(path);
    int n = 0, m = 0;
    sc.read_header(n, m);
    vector<int> clause;
    clause.reserve(16);
    int lit;
    int ci = 0;
    while (sc.read_int(lit)) {
        if (lit == 0) {
            bool sat = covered[(size_t)ci] != 0;
            if (!sat) {
                for (int l : clause) {
                    signed char s = selected[(size_t)abs(l)];
                    if (s && ((l > 0 && s > 0) || (l < 0 && s < 0))) {
                        sat = true;
                        break;
                    }
                }
            }
            if (sat) {
                covered[(size_t)ci] = 1;
                st.covered++;
            } else {
                st.uncovered++;
                if (pos && neg) {
                    for (int l : clause) {
                        int v = abs(l);
                        if (selected[(size_t)v]) continue;
                        if (l > 0) (*pos)[(size_t)v]++;
                        else (*neg)[(size_t)v]++;
                    }
                }
            }
            clause.clear();
            ci++;
        } else {
            clause.push_back(lit);
        }
    }
    return st;
}

static bool patch_uncovered(const char* path, vector<unsigned char>& covered,
                            vector<signed char>& selected,
                            const vector<int>& pos, const vector<int>& neg) {
    Scanner sc(path);
    int n = 0, m = 0;
    sc.read_header(n, m);
    vector<int> clause;
    clause.reserve(16);
    int lit;
    int ci = 0;
    bool ok = true;
    while (sc.read_int(lit)) {
        if (lit == 0) {
            if (!covered[(size_t)ci]) {
                int best = 0;
                int best_score = -1;
                for (int l : clause) {
                    int v = abs(l);
                    signed char s = selected[(size_t)v];
                    if (s) {
                        if ((l > 0 && s > 0) || (l < 0 && s < 0)) {
                            best = 0;
                            break;
                        }
                        continue;
                    }
                    int score = l > 0 ? pos[(size_t)v] : neg[(size_t)v];
                    if (score > best_score) {
                        best_score = score;
                        best = l;
                    }
                }
                if (best != 0) selected[(size_t)abs(best)] = best > 0 ? 1 : -1;
                else ok = false;
            }
            clause.clear();
            ci++;
        } else {
            clause.push_back(lit);
        }
    }
    fill(covered.begin(), covered.end(), 0);
    return ok;
}

int main(int argc, char** argv) {
    if (argc < 2 || argc > 3) {
        cerr << "usage: implicant_greedy formula.cnf [model.out]\n";
        return 2;
    }
    int n = 0, m = 0;
    {
        Scanner sc(argv[1]);
        if (!sc.f || !sc.read_header(n, m)) return 2;
    }
    vector<signed char> selected((size_t)n + 1, 0);
    vector<unsigned char> covered((size_t)m, 0);
    vector<int> pos((size_t)n + 1, 0), neg((size_t)n + 1, 0);

    long long selected_count = 0;
    for (int threshold : {4096, 2048, 1024, 512, 256, 128, 96, 64, 48, 32, 24, 16, 12, 10, 8, 6, 5, 4, 3}) {
        Stats st = count_uncovered(argv[1], covered, selected, &pos, &neg);
        long long added = 0;
        for (int v = 1; v <= n; ++v) {
            if (selected[(size_t)v]) continue;
            int pp = pos[(size_t)v], nn = neg[(size_t)v];
            if (max(pp, nn) >= threshold) {
                selected[(size_t)v] = pp >= nn ? 1 : -1;
                selected_count++;
                added++;
            }
        }
        fill(covered.begin(), covered.end(), 0);
        cout << "threshold " << threshold << " uncovered " << st.uncovered
             << " added " << added << " selected " << selected_count
             << " bytes " << model_bytes(selected) << "\n";
        if (st.uncovered == 0) break;
    }

    for (int round = 0; round < 8; ++round) {
        Stats st = count_uncovered(argv[1], covered, selected, &pos, &neg);
        cout << "patch_round " << round << " uncovered " << st.uncovered
             << " selected " << selected_count
             << " bytes " << model_bytes(selected) << "\n";
        if (st.uncovered == 0) break;
        if (!patch_uncovered(argv[1], covered, selected, pos, neg)) {
            cout << "patch failed\n";
            return 0;
        }
        long long now = 0;
        for (int v = 1; v <= n; ++v) if (selected[(size_t)v]) now++;
        selected_count = now;
    }
    Stats final = count_uncovered(argv[1], covered, selected, nullptr, nullptr);
    cout << "final uncovered " << final.uncovered
         << " selected " << selected_count
         << " bytes " << model_bytes(selected) << "\n";
    if (argc == 3 && final.uncovered == 0) {
        FILE* out = fopen(argv[2], "wb");
        fprintf(out, "s SATISFIABLE\n");
        int col = 0;
        for (int v = 1; v <= n; ++v) {
            if (!selected[(size_t)v]) continue;
            if (col == 0) fprintf(out, "v");
            fprintf(out, " %d", selected[(size_t)v] > 0 ? v : -v);
            if (++col == 12) {
                fprintf(out, "\n");
                col = 0;
            }
        }
        if (col == 0) fprintf(out, "v");
        fprintf(out, " 0\n");
        fclose(out);
    }
    return 0;
}
