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

using Clock = std::chrono::steady_clock;

struct Rng {
    uint64_t s;
    explicit Rng(uint64_t seed) : s(seed) {}
    uint64_t next() {
        uint64_t z = (s += 0x9e3779b97f4a7c15ULL);
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
        z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
        return z ^ (z >> 31);
    }
    int randint(int n) { return (int)(next() % (uint64_t)n); }
    double unit() { return (next() >> 11) * (1.0 / 9007199254740992.0); }
};

struct Clause {
    int off = 0;
    int len = 0;
    bool positive = true;
};

struct Formula {
    int nvars = 0;
    int nclauses_header = 0;
    std::vector<Clause> clauses;
    std::vector<int> lits;
    std::vector<std::vector<int>> occ;
};

static bool parse_dimacs(const char* path, Formula& f) {
    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;
            in >> fmt >> f.nvars >> f.nclauses_header;
            if (fmt != "cnf" || f.nvars <= 0) return false;
            f.occ.assign(f.nvars + 1, {});
        } else if (tok == "%") {
            break;
        } else {
            int lit = std::stoi(tok);
            std::vector<int> clause;
            while (lit != 0) {
                if (lit == std::numeric_limits<int>::min()) return false;
                int v = std::abs(lit);
                if (v <= 0 || v > f.nvars) return false;
                clause.push_back(lit);
                if (!(in >> lit)) return false;
            }
            if (clause.empty()) return false;
            bool pos = clause[0] > 0;
            for (int x : clause) {
                if ((x > 0) != pos) return false;
            }
            Clause c;
            c.off = (int)f.lits.size();
            c.len = (int)clause.size();
            c.positive = pos;
            int ci = (int)f.clauses.size();
            f.clauses.push_back(c);
            for (int x : clause) {
                int v = std::abs(x);
                f.lits.push_back(v);
                f.occ[v].push_back(ci);
            }
        }
    }
    return f.nvars > 0 && (int)f.clauses.size() == f.nclauses_header;
}

static int satisfied_count(const Formula& f, const std::vector<unsigned char>& val, int ci) {
    const Clause& c = f.clauses[ci];
    int cnt = 0;
    for (int j = 0; j < c.len; ++j) {
        int v = f.lits[c.off + j];
        if ((bool)val[v] == c.positive) ++cnt;
    }
    return cnt;
}

static bool check_model(const Formula& f, const std::vector<unsigned char>& val) {
    for (int i = 0; i < (int)f.clauses.size(); ++i) {
        if (satisfied_count(f, val, i) == 0) return false;
    }
    return true;
}

static bool structural_green_tao_like(const Formula& f) {
    if (f.clauses.empty() || f.nvars <= 0) return false;
    int pos = 0, neg = 0;
    for (const Clause& c : f.clauses) {
        if (c.len < 2 || c.len > 10) return false;
        if (c.positive) ++pos; else ++neg;
        int last = 0;
        for (int j = 0; j < c.len; ++j) {
            int v = f.lits[c.off + j];
            if (v <= last) return false;
            last = v;
        }
    }
    return pos > 0 && neg > 0;
}

static int add_unsat(std::vector<int>& unsat, std::vector<int>& where, int ci) {
    if (where[ci] >= 0) return where[ci];
    where[ci] = (int)unsat.size();
    unsat.push_back(ci);
    return where[ci];
}

static void remove_unsat(std::vector<int>& unsat, std::vector<int>& where, int ci) {
    int p = where[ci];
    if (p < 0) return;
    int last = unsat.back();
    unsat[p] = last;
    where[last] = p;
    unsat.pop_back();
    where[ci] = -1;
}

static int flip_delta(const Formula& f, const std::vector<unsigned char>& val,
                      const std::vector<unsigned char>& cnt,
                      const std::vector<unsigned short>& weight, int var) {
    int delta = 0;
    bool old = val[var];
    for (int ci : f.occ[var]) {
        const Clause& c = f.clauses[ci];
        bool lit_true = (old == c.positive);
        if (lit_true) {
            if (cnt[ci] == 1) delta += weight[ci];
        } else {
            if (cnt[ci] == 0) delta -= weight[ci];
        }
    }
    return delta;
}

static int flip_delta_unweighted(const Formula& f, const std::vector<unsigned char>& val,
                                 const std::vector<unsigned char>& cnt, int var) {
    int delta = 0;
    bool old = val[var];
    for (int ci : f.occ[var]) {
        const Clause& c = f.clauses[ci];
        bool lit_true = (old == c.positive);
        if (lit_true) {
            if (cnt[ci] == 1) ++delta;
        } else {
            if (cnt[ci] == 0) --delta;
        }
    }
    return delta;
}

static void apply_flip(const Formula& f, std::vector<unsigned char>& val,
                       std::vector<unsigned char>& cnt, std::vector<int>& unsat,
                       std::vector<int>& where, int var) {
    bool old = val[var];
    val[var] = (unsigned char)!old;
    for (int ci : f.occ[var]) {
        const Clause& c = f.clauses[ci];
        bool lit_was_true = (old == c.positive);
        int before = cnt[ci];
        int after = before + (lit_was_true ? -1 : 1);
        cnt[ci] = (unsigned char)after;
        if (before == 0 && after > 0) remove_unsat(unsat, where, ci);
        else if (before > 0 && after == 0) add_unsat(unsat, where, ci);
    }
}

static bool bounded_repair_dfs(const Formula& f, std::vector<unsigned char>& val,
                               std::vector<unsigned char>& cnt,
                               std::vector<int>& unsat, std::vector<int>& where,
                               int depth, int& budget) {
    if (unsat.empty()) return true;
    if (depth <= 0 || budget-- <= 0) return false;

    int uc = unsat[0];
    for (int ci : unsat) {
        if (f.clauses[ci].len < f.clauses[uc].len) uc = ci;
    }
    const Clause& c = f.clauses[uc];
    std::vector<int> vars;
    vars.reserve(c.len);
    for (int j = 0; j < c.len; ++j) vars.push_back(f.lits[c.off + j]);
    std::sort(vars.begin(), vars.end(), [&](int a, int b) {
        int da = flip_delta_unweighted(f, val, cnt, a);
        int db = flip_delta_unweighted(f, val, cnt, b);
        if (da != db) return da < db;
        return f.occ[a].size() > f.occ[b].size();
    });

    int before = (int)unsat.size();
    for (int v : vars) {
        apply_flip(f, val, cnt, unsat, where, v);
        if ((int)unsat.size() <= before + 8) {
            if (bounded_repair_dfs(f, val, cnt, unsat, where, depth - 1, budget)) {
                return true;
            }
        }
        apply_flip(f, val, cnt, unsat, where, v);
        if (budget <= 0) break;
    }
    return false;
}

static bool repair_from_start(const Formula& f, std::vector<unsigned char>& val,
                              std::vector<unsigned char>& best_model,
                              Rng& rng, Clock::time_point start,
                              double seconds_limit, int restart_tag,
                              int& global_best,
                              std::vector<unsigned char>& best_seen) {
    auto elapsed = [&]() {
        return std::chrono::duration<double>(Clock::now() - start).count();
    };

    std::vector<unsigned char> cnt(f.clauses.size());
    std::vector<unsigned short> weight(f.clauses.size(), 1);
    std::vector<int> unsat, where(f.clauses.size(), -1);
    double small_noise = 0.22;
    if (const char* s = std::getenv("GT_SMALL_NOISE")) {
        double requested = std::strtod(s, nullptr);
        if (requested >= 0.0 && requested <= 1.0) small_noise = requested;
    }
    int local_pos_min = 99, local_neg_min = 99;
    for (const Clause& lc : f.clauses) {
        if (lc.positive) local_pos_min = std::min(local_pos_min, lc.len);
        else local_neg_min = std::min(local_neg_min, lc.len);
    }
    double probsat_mix = local_pos_min < local_neg_min ? 0.65 : 0.0;
    if (const char* s = std::getenv("GT_PROBSAT_MIX")) {
        double requested = std::strtod(s, nullptr);
        if (requested >= 0.0 && requested <= 1.0) probsat_mix = requested;
    }
    double global_kick = 0.0;
    if (const char* s = std::getenv("GT_GLOBAL_KICK")) {
        double requested = std::strtod(s, nullptr);
        if (requested >= 0.0 && requested <= 1.0) global_kick = requested;
    }

    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        cnt[ci] = (unsigned char)satisfied_count(f, val, ci);
        if (cnt[ci] == 0) add_unsat(unsat, where, ci);
    }

    int plateau = 0;
    int max_steps = std::max(300000, 3000 * f.nvars);
    for (int step = 0; step < max_steps && elapsed() < seconds_limit; ++step) {
        if (unsat.empty()) {
            if (check_model(f, val)) {
                best_model = val;
                std::printf("c found model restart=%d step=%d\n", restart_tag, step);
                return true;
            }
            return false;
        }
        if ((int)unsat.size() < global_best) {
            global_best = (int)unsat.size();
            best_seen = val;
            std::printf("c best_unsat=%d restart=%d step=%d\n", global_best, restart_tag, step);
            std::fflush(stdout);
            if (global_best <= 4) {
                int budget = 50000;
                int depth = 8 + (4 - global_best);
                if (bounded_repair_dfs(f, val, cnt, unsat, where, depth, budget) &&
                    check_model(f, val)) {
                    best_model = val;
                    std::printf("c found model bounded restart=%d step=%d\n", restart_tag, step);
                    return true;
                }
            }
        }

        int uc;
        if (rng.unit() < 0.88) uc = unsat[rng.randint((int)unsat.size())];
        else uc = unsat[0];
        const Clause& c = f.clauses[uc];
        int chosen = f.lits[c.off + rng.randint(c.len)];

        double noise = unsat.size() <= 10 ? small_noise : (plateau > 10000 ? 0.12 : 0.025);
        if (unsat.size() <= 3 && global_kick > 0.0 && rng.unit() < global_kick) {
            int best_delta = std::numeric_limits<int>::max();
            int best_v = 1 + rng.randint(f.nvars);
            for (int i = 0; i < 96; ++i) {
                int v = 1 + rng.randint(f.nvars);
                int d = flip_delta(f, val, cnt, weight, v);
                if (d < best_delta) {
                    best_delta = d;
                    best_v = v;
                }
            }
            chosen = best_v;
        } else if (rng.unit() >= noise) {
            if (probsat_mix > 0.0 && rng.unit() < probsat_mix) {
                double scores[16];
                double total = 0.0;
                int vars[16];
                int nvars = 0;
                for (int j = 0; j < c.len && nvars < 16; ++j) {
                    int v = f.lits[c.off + j];
                    int brk = 0;
                    bool old = val[v];
                    for (int ci : f.occ[v]) {
                        const Clause& bc = f.clauses[ci];
                        if (old == bc.positive && cnt[ci] == 1) brk += weight[ci];
                    }
                    vars[nvars] = v;
                    scores[nvars] = 1.0 / ((double)brk + 1.0);
                    scores[nvars] *= scores[nvars];
                    total += scores[nvars];
                    ++nvars;
                }
                double pick = rng.unit() * total;
                for (int i = 0; i < nvars; ++i) {
                    pick -= scores[i];
                    if (pick <= 0.0) {
                        chosen = vars[i];
                        break;
                    }
                }
            } else {
            int best_delta = std::numeric_limits<int>::max();
            int ties[16];
            int nties = 0;
            for (int j = 0; j < c.len; ++j) {
                int v = f.lits[c.off + j];
                int d = flip_delta(f, val, cnt, weight, v);
                if (d < best_delta) {
                    best_delta = d;
                    ties[0] = v;
                    nties = 1;
                } else if (d == best_delta && nties < 16) {
                    ties[nties++] = v;
                }
            }
            chosen = ties[rng.randint(nties)];
            if (best_delta >= 0) ++plateau; else plateau = 0;
            if (best_delta >= 0 && (plateau % 32 == 0 || unsat.size() < 8)) {
                for (int ci : unsat) {
                    if (weight[ci] < 60000) ++weight[ci];
                }
                if (plateau > 20000 && (plateau % 2048 == 0)) {
                    for (unsigned short& w : weight) {
                        w = (unsigned short)std::max(1, (int)w * 3 / 4);
                    }
                }
            }
            }
        }
        apply_flip(f, val, cnt, unsat, where, chosen);
    }
    return false;
}

static bool make_sparse_transversal_seed(const Formula& f, Rng& rng,
                                         std::vector<unsigned char>& val) {
    val.assign(f.nvars + 1, 1);
    std::vector<unsigned char> pos_false(f.clauses.size(), 0);
    std::vector<unsigned char> neg_hit(f.clauses.size(), 0);
    std::vector<unsigned short> ban(f.nvars + 1, 0);
    std::vector<int> uncovered, where(f.clauses.size(), -1);
    for (int ci = 0; ci < (int)f.clauses.size(); ++ci) {
        if (!f.clauses[ci].positive) {
            where[ci] = (int)uncovered.size();
            uncovered.push_back(ci);
        }
    }

    std::vector<int> cand_seen(f.nvars + 1, 0);
    std::vector<int> candidates;
    int stamp = 1;
    while (!uncovered.empty()) {
        candidates.clear();
        ++stamp;
        if (stamp == std::numeric_limits<int>::max()) {
            std::fill(cand_seen.begin(), cand_seen.end(), 0);
            stamp = 1;
        }
        for (int ci : uncovered) {
            const Clause& c = f.clauses[ci];
            for (int j = 0; j < c.len; ++j) {
                int v = f.lits[c.off + j];
                if (val[v] && ban[v] == 0 && cand_seen[v] != stamp) {
                    cand_seen[v] = stamp;
                    candidates.push_back(v);
                }
            }
        }
        if (candidates.empty()) return false;

        int chosen = candidates[0];
        int best_score = std::numeric_limits<int>::min();
        for (int v : candidates) {
            int cover = 0;
            int danger = 0;
            int blocked = 0;
            for (int ci : f.occ[v]) {
                const Clause& c = f.clauses[ci];
                if (c.positive) {
                    if (pos_false[ci] == c.len - 2) ++danger;
                    else if (pos_false[ci] >= c.len - 1) ++blocked;
                } else if (neg_hit[ci] == 0) {
                    ++cover;
                }
            }
            int score = cover * 10000 - danger * 9 - blocked * 20000 + (int)(rng.next() & 255);
            if (score > best_score) {
                best_score = score;
                chosen = v;
            }
        }

        val[chosen] = 0;
        for (int ci : f.occ[chosen]) {
            const Clause& c = f.clauses[ci];
            if (c.positive) {
                int after = ++pos_false[ci];
                if (after >= c.len) return false;
                if (after == c.len - 1) {
                    for (int j = 0; j < c.len; ++j) {
                        int u = f.lits[c.off + j];
                        if (val[u]) {
                            if (ban[u] < 65535) ++ban[u];
                            break;
                        }
                    }
                }
            } else {
                int before = neg_hit[ci]++;
                if (before == 0) remove_unsat(uncovered, where, ci);
            }
        }
    }
    return true;
}

struct CdclClause {
    std::vector<int> lits;
    int watch0 = 0;
    int watch1 = 0;
    bool learnt = false;
};

struct Cdcl {
    const Formula& f;
    std::vector<CdclClause> clauses;
    std::vector<std::vector<int>> watches;
    std::vector<int> assigns, levels, reasons, trail, trail_lim;
    std::vector<double> activity;
    std::vector<unsigned char> phase;
    double var_inc = 1.0;
    Clock::time_point start;
    double limit = 0.0;

    Cdcl(const Formula& formula, const std::vector<unsigned char>& phase_hint,
         double seconds_limit)
        : f(formula), watches(2 * formula.nvars + 2), assigns(formula.nvars + 1, 0),
          levels(formula.nvars + 1, 0), reasons(formula.nvars + 1, -1),
          activity(formula.nvars + 1, 0.0), phase(formula.nvars + 1, 1),
          start(Clock::now()), limit(seconds_limit) {
        if (phase_hint.size() == (size_t)formula.nvars + 1) phase = phase_hint;
        clauses.reserve(formula.clauses.size() + 10000);
        for (const Clause& c : formula.clauses) {
            CdclClause cc;
            cc.lits.reserve(c.len);
            for (int j = 0; j < c.len; ++j) {
                int v = formula.lits[c.off + j];
                cc.lits.push_back(c.positive ? v : -v);
                activity[v] += 1.0;
            }
            add_clause_object(std::move(cc));
        }
    }

    int lit_index(int lit) const {
        int v = std::abs(lit);
        return 2 * v + (lit < 0);
    }
    int current_level() const { return (int)trail_lim.size(); }
    bool lit_true(int lit) const {
        int a = assigns[std::abs(lit)];
        return a != 0 && ((lit > 0 && a > 0) || (lit < 0 && a < 0));
    }
    bool lit_false(int lit) const {
        int a = assigns[std::abs(lit)];
        return a != 0 && ((lit > 0 && a < 0) || (lit < 0 && a > 0));
    }
    bool timed_out() const {
        return std::chrono::duration<double>(Clock::now() - start).count() >= limit;
    }

    int add_clause_object(CdclClause&& cc) {
        int ci = (int)clauses.size();
        if (cc.lits.empty()) return ci;
        cc.watch0 = 0;
        cc.watch1 = cc.lits.size() > 1 ? 1 : 0;
        clauses.push_back(std::move(cc));
        watches[lit_index(clauses[ci].lits[clauses[ci].watch0])].push_back(ci);
        if (clauses[ci].watch1 != clauses[ci].watch0) {
            watches[lit_index(clauses[ci].lits[clauses[ci].watch1])].push_back(ci);
        }
        return ci;
    }

    bool enqueue(int lit, int reason) {
        int v = std::abs(lit);
        int val = lit > 0 ? 1 : -1;
        if (assigns[v] != 0) return assigns[v] == val;
        assigns[v] = val;
        levels[v] = current_level();
        reasons[v] = reason;
        phase[v] = (lit > 0);
        trail.push_back(lit);
        return true;
    }

    int propagate(size_t& qhead) {
        while (qhead < trail.size()) {
            int p = trail[qhead++];
            auto& ws = watches[lit_index(-p)];
            int out = 0;
            for (int idx = 0; idx < (int)ws.size(); ++idx) {
                int ci = ws[idx];
                CdclClause& c = clauses[ci];
                int false_watch = (c.lits[c.watch0] == -p) ? c.watch0 : c.watch1;
                int other_watch = (false_watch == c.watch0) ? c.watch1 : c.watch0;
                int other_lit = c.lits[other_watch];
                if (lit_true(other_lit)) {
                    ws[out++] = ci;
                    continue;
                }
                bool moved = false;
                for (int k = 0; k < (int)c.lits.size(); ++k) {
                    if (k == c.watch0 || k == c.watch1) continue;
                    if (!lit_false(c.lits[k])) {
                        if (false_watch == c.watch0) c.watch0 = k;
                        else c.watch1 = k;
                        watches[lit_index(c.lits[k])].push_back(ci);
                        moved = true;
                        break;
                    }
                }
                if (moved) continue;
                ws[out++] = ci;
                if (lit_false(other_lit)) {
                    for (++idx; idx < (int)ws.size(); ++idx) ws[out++] = ws[idx];
                    ws.resize(out);
                    return ci;
                }
                if (!enqueue(other_lit, ci)) {
                    for (++idx; idx < (int)ws.size(); ++idx) ws[out++] = ws[idx];
                    ws.resize(out);
                    return ci;
                }
            }
            ws.resize(out);
        }
        return -1;
    }

    void backtrack(int level) {
        if (current_level() <= level) return;
        int target = level == 0 ? 0 : trail_lim[level];
        for (int i = (int)trail.size() - 1; i >= target; --i) {
            int v = std::abs(trail[i]);
            assigns[v] = 0;
            levels[v] = 0;
            reasons[v] = -1;
        }
        trail.resize(target);
        trail_lim.resize(level);
    }

    int pick_branch_lit() {
        int best = 0;
        double best_act = -1.0;
        for (int v = 1; v <= f.nvars; ++v) {
            if (assigns[v] == 0 && activity[v] > best_act) {
                best_act = activity[v];
                best = v;
            }
        }
        if (best == 0) return 0;
        return phase[best] ? best : -best;
    }

    int analyze(int confl, std::vector<int>& learnt) {
        learnt.clear();
        learnt.push_back(0);
        std::vector<unsigned char> seen(f.nvars + 1, 0);
        int path_count = 0;
        int p = 0;
        int idx = (int)trail.size() - 1;
        int backtrack_level = 0;
        int ci = confl;
        do {
            CdclClause& c = clauses[ci];
            for (int lit : c.lits) {
                int v = std::abs(lit);
                if (v == std::abs(p) || seen[v] || levels[v] == 0) continue;
                seen[v] = 1;
                activity[v] += var_inc;
                if (levels[v] == current_level()) {
                    ++path_count;
                } else {
                    learnt.push_back(lit);
                    backtrack_level = std::max(backtrack_level, levels[v]);
                }
            }
            while (idx >= 0 && !seen[std::abs(trail[idx])]) --idx;
            p = trail[idx--];
            seen[std::abs(p)] = 0;
            ci = reasons[std::abs(p)];
            --path_count;
        } while (path_count > 0 && ci >= 0);
        learnt[0] = -p;
        if (var_inc < 1e100) var_inc *= 1.05;
        else {
            for (double& a : activity) a *= 1e-100;
            var_inc = 1.0;
        }
        return backtrack_level;
    }

    bool solve(std::vector<unsigned char>& model) {
        size_t qhead = 0;
        int confl = propagate(qhead);
        if (confl >= 0) return false;
        int conflicts = 0;
        int restart_limit = 200;
        std::vector<int> learnt;
        while (!timed_out()) {
            confl = propagate(qhead);
            if (confl >= 0) {
                ++conflicts;
                if (current_level() == 0) return false;
                int back = analyze(confl, learnt);
                backtrack(back);
                qhead = trail.size();
                CdclClause cc;
                cc.lits = learnt;
                cc.learnt = true;
                int ci = add_clause_object(std::move(cc));
                enqueue(clauses[ci].lits[0], ci);
                if (conflicts >= restart_limit) {
                    backtrack(0);
                    qhead = trail.size();
                    conflicts = 0;
                    restart_limit = std::min(restart_limit * 3 / 2 + 50, 5000);
                }
                continue;
            }
            int decision = pick_branch_lit();
            if (decision == 0) {
                model.assign(f.nvars + 1, 0);
                for (int v = 1; v <= f.nvars; ++v) model[v] = assigns[v] >= 0;
                return true;
            }
            trail_lim.push_back((int)trail.size());
            enqueue(decision, -1);
        }
        return false;
    }
};

static bool solve_cdcl_sat(const Formula& f, const std::vector<unsigned char>& phase_hint,
                           std::vector<unsigned char>& model, double seconds_limit) {
    if (seconds_limit < 5.0 || f.nvars > 6000 || f.clauses.size() > 300000) return false;
    Cdcl solver(f, phase_hint, seconds_limit);
    if (solver.solve(model) && check_model(f, model)) {
        std::printf("c cdcl found checked model\n");
        return true;
    }
    return false;
}

static bool solve_sls(const Formula& f, std::vector<unsigned char>& best_model,
                      double seconds_limit) {
    auto start = Clock::now();
    auto elapsed = [&]() {
        return std::chrono::duration<double>(Clock::now() - start).count();
    };

    int pos_min_len = 99, neg_min_len = 99, pos_count = 0, neg_count = 0;
    for (const Clause& c : f.clauses) {
        if (c.positive) { pos_min_len = std::min(pos_min_len, c.len); ++pos_count; }
        else { neg_min_len = std::min(neg_min_len, c.len); ++neg_count; }
    }

    std::vector<double> probs;
    if (pos_min_len < neg_min_len) {
        // In (3,k) Green-Tao encodings a model is a sparse false-color
        // transversal for the long negative APs.  Good solutions observed on
        // the training split use far more true literals than a balanced random
        // start, so bias restarts toward 10-25% false variables.
        probs = {0.84, 0.82, 0.86, 0.80, 0.88, 0.78, 0.90, 0.76, 0.74};
    } else if (neg_min_len < pos_min_len) {
        probs = {0.38, 0.34, 0.42, 0.30, 0.46, 0.26, 0.50};
    } else {
        probs = {0.50, 0.46, 0.54, 0.42, 0.58};
    }

    uint64_t seed_offset = 0;
    if (const char* s = std::getenv("GT_SEED_OFFSET")) {
        seed_offset = std::strtoull(s, nullptr, 10);
    }
    Rng rng(0x475245454e54414fULL ^ (uint64_t)f.nvars * 0x100000001b3ULL ^
            (uint64_t)f.clauses.size() ^ (seed_offset * 0x9e3779b97f4a7c15ULL));
    std::vector<unsigned char> val(f.nvars + 1);
    std::vector<unsigned char> best_seen;
    int global_best = std::numeric_limits<int>::max();
    double sls_deadline = seconds_limit;
    if (pos_min_len < neg_min_len && f.nvars >= 1000 && f.nvars <= 6000) {
        sls_deadline = std::min(seconds_limit, 240.0);
    }

    if (pos_min_len < neg_min_len) {
        for (int seed = 0; seed < 12 && elapsed() < sls_deadline; ++seed) {
            if (make_sparse_transversal_seed(f, rng, val)) {
                if (check_model(f, val)) {
                    best_model = val;
                    std::printf("c greedy seed is complete model seed=%d\n", seed);
                    return true;
                }
            }
            if (repair_from_start(f, val, best_model, rng, start, sls_deadline,
                                  -1 - seed, global_best, best_seen)) {
                return true;
            }
        }
    }

    for (int restart = 0; elapsed() < sls_deadline; ++restart) {
        double p = probs[restart % probs.size()];
        for (int v = 1; v <= f.nvars; ++v) val[v] = (rng.unit() < p);
        if (repair_from_start(f, val, best_model, rng, start, sls_deadline,
                              restart, global_best, best_seen)) {
            return true;
        }
    }
    double remaining = seconds_limit - elapsed();
    if (remaining > 5.0 && solve_cdcl_sat(f, best_seen, best_model, remaining - 1.0)) {
        return true;
    }
    if (const char* dump = std::getenv("GT_DUMP_BEST")) {
        if (!best_seen.empty()) {
            std::ofstream out(dump);
            if (out) {
                for (int v = 1; v <= f.nvars; ++v) {
                    out << (best_seen[v] ? v : -v) << (v == f.nvars ? '\n' : ' ');
                }
            }
        }
    }
    std::printf("c no model found best_unsat=%d\n", global_best);
    return false;
}

static void print_model(const Formula& f, const std::vector<unsigned char>& val) {
    std::printf("s SATISFIABLE\n");
    int col = 0;
    std::printf("v");
    for (int v = 1; v <= f.nvars; ++v) {
        if (col == 20) {
            std::printf(" 0\nv");
            col = 0;
        }
        std::printf(" %d", val[v] ? v : -v);
        ++col;
    }
    std::printf(" 0\n");
}

int main(int argc, char** argv) {
    if (argc != 3) {
        std::fprintf(stderr, "usage: solver <formula.cnf> <out.pbp>\n");
        return 1;
    }

    Formula f;
    if (!parse_dimacs(argv[1], f)) {
        std::printf("c parse failed\ns UNKNOWN\n");
        return 0;
    }
    std::printf("c vars=%d clauses=%zu\n", f.nvars, f.clauses.size());
    if (!structural_green_tao_like(f)) {
        std::printf("c unsupported non-monotone/non-GreenTao-like structure\ns UNKNOWN\n");
        return 0;
    }

    std::vector<unsigned char> model;
    double limit = 570.0;
    if (const char* s = std::getenv("GT_SECONDS_LIMIT")) {
        double requested = std::strtod(s, nullptr);
        if (requested > 1.0 && requested < limit) limit = requested;
    }
    if (solve_sls(f, model, limit) && check_model(f, model)) {
        print_model(f, model);
        return 10;
    }

    std::ofstream proof(argv[2]);
    if (proof) {
        proof << "pseudo-Boolean proof version 3.0\n";
        proof << "output NONE\n";
        proof << "end pseudo-Boolean proof\n";
    }
    std::printf("s UNKNOWN\n");
    return 0;
}
