#include "allocate.h"
#include "decide.h"
#include "dense.h"
#include "inline.h"
#include "print.h"
#include "report.h"
#include "rephase.h"
#include "terminate.h"
#include "walk.h"

typedef struct tagged tagged;
typedef struct counter counter;
typedef struct walker walker;

#define LD_MAX_WALK_REF 31
#define MAX_WALK_REF ((1u << LD_MAX_WALK_REF) - 1)
int walk_time = 0;
int selected_strategy = 1;
struct tagged {
    unsigned ref : LD_MAX_WALK_REF;
    bool binary : 1;
};

static inline tagged make_tagged(bool binary, unsigned ref)
{
    assert(ref <= MAX_WALK_REF);
    tagged res = {
        .binary = binary,
        .ref = ref
    };
    return res;
}

struct counter {
    unsigned count;
    unsigned pos;
};

// *INDENT-OFF*
typedef STACK(double) doubles;
// *INDENT-ON*

#define INVALID_BEST UINT_MAX

struct walker {
    kissat *solver;
    generator random;
    counter *counters;
    litpairs *binaries;
    value *saved;
    unsigned clauses;
    unsigned offset;
    unsigned minimum;
    unsigned current;
    unsigned initial;
    tagged *refs;
    double *table;
    double epsilon;
    unsigned exponents;
    doubles scores;
    unsigneds unsat;
    // unsigneds steps;
    unsigneds trail;
    unsigned best;
    uint64_t limit;
#ifndef QUIET
    uint64_t start;
    uint64_t flipped;
    struct {
        uint64_t flipped;
        unsigned minimum;
    } report;
#endif
};

static const unsigned *dereference_literals(kissat *solver, walker *walker, unsigned counter_ref, unsigned *size_ptr)
{
    assert(counter_ref < walker->clauses);
    tagged tagged = walker->refs[counter_ref];
    const unsigned *lits;
    if (tagged.binary) {
        const unsigned binary_ref = tagged.ref;
        lits = PEEK_STACK(*walker->binaries, binary_ref).lits;
        *size_ptr = 2;
    } else {
        const reference clause_ref = tagged.ref;
        clause *c = kissat_dereference_clause(solver, clause_ref);
        *size_ptr = c->size;
        lits = c->lits;
    }
    return lits;
}

static void push_unsat(kissat *solver, walker *walker, counter *counters, unsigned counter_ref)
{
    assert(counter_ref < walker->clauses);
    counter *counter = counters + counter_ref;
    assert(SIZE_STACK(walker->unsat) <= UINT_MAX);
    counter->pos = SIZE_STACK(walker->unsat);
    PUSH_STACK(walker->unsat, counter_ref);
#ifdef LOGGING
    unsigned size;
    const unsigned *lits = dereference_literals(solver, walker, counter_ref, &size);
    LOGLITS(size, lits, "pushed unsatisfied[%u]", counter->pos);
#endif
}

static bool pop_unsat(kissat *solver, walker *walker, counter *counters, unsigned counter_ref, unsigned pos)
{
    assert(walker->current);
    assert(counter_ref < walker->clauses);
    assert(counters[counter_ref].pos == pos);
    assert(walker->current == SIZE_STACK(walker->unsat));
    const unsigned other_counter_ref = POP_STACK(walker->unsat);
    walker->current--;
    bool res = false;
    if (counter_ref != other_counter_ref) {
        assert(other_counter_ref < walker->clauses);
        counter *other_counter = counters + other_counter_ref;
        assert(other_counter->pos == walker->current);
        assert(pos < other_counter->pos);
        other_counter->pos = pos;
        POKE_STACK(walker->unsat, pos, other_counter_ref);
        res = true;
    }
#ifdef LOGGING
    unsigned size;
    const unsigned *lits = dereference_literals(solver, walker, counter_ref, &size);
    LOGLITS(size, lits, "popped unsatisfied[%u]", pos);
#else
    (void)solver;
#endif
    return res;
}

static void init_score_table(walker *walker)
{
    kissat *solver = walker->solver;

    const double cb = 2.0;
    const double base = 1 / cb;

    double next;
    unsigned exponents = 0;
    for (next = 1; next; next *= base)
        exponents++;

    walker->table = kissat_malloc(solver, exponents * sizeof(double));

    unsigned i = 0;
    double epsilon;
    for (epsilon = next = 1; next; next = epsilon * base)
        walker->table[i++] = epsilon = next;

    assert(i == exponents);
    walker->exponents = exponents;
    walker->epsilon = epsilon;

    kissat_phase(solver, "walk", GET(walks), "CB %.2f with inverse %.2f as base", cb, base);
    kissat_phase(solver, "walk", GET(walks), "table size %u and epsilon %g", exponents, epsilon);
}

static unsigned currently_unsatified(walker *walker)
{
    return SIZE_STACK(walker->unsat);
}

static void import_decision_phases(walker *walker)
{
    kissat *solver = walker->solver;
    const phase *phases = solver->phases;
    const value initial_phase = INITIAL_PHASE;
    const flags *flags = solver->flags;
    const bool stable = solver->stable;
    value *values = solver->values;
    for (all_variables(idx)) {
        if (!flags[idx].active)
            continue;
        const phase *p = phases + idx;
        value value = 0;
        if (stable)
            value = p->target;
        if (!value)
            value = p->saved;
        if (!value)
            value = initial_phase;
        assert(value);
        const unsigned lit = LIT(idx);
        const unsigned not_lit = NOT(lit);
        values[lit] = value;
        values[not_lit] = -value;
        LOG("copied variable %u decision phase %d", idx, (int)value);
    }
    kissat_phase(solver, "walk", GET(walks), "copied decision phases");
}

static unsigned connect_binary_counters(walker *walker)
{
    kissat *solver = walker->solver;
    value *values = solver->values;
    tagged *refs = walker->refs;
    watches *all_watches = solver->watches;
    counter *counters = walker->counters;

    assert(SIZE_STACK(*walker->binaries) <= UINT_MAX);
    const unsigned size = SIZE_STACK(*walker->binaries);
    litpair *binaries = BEGIN_STACK(*walker->binaries);
    unsigned unsat = 0, counter_ref = 0;

    for (unsigned binary_ref = 0; binary_ref < size; binary_ref++) {
        const litpair *litpair = binaries + binary_ref;
        const unsigned first = litpair->lits[0];
        const unsigned second = litpair->lits[1];
        assert(first < LITS), assert(second < LITS);
        const value first_value = values[first];
        const value second_value = values[second];
        if (!first_value || !second_value)
            continue;
        assert(counter_ref < walker->clauses);
        refs[counter_ref] = make_tagged(true, binary_ref);
        watches *first_watches = all_watches + first;
        watches *second_watches = all_watches + second;
        kissat_push_large_watch(solver, first_watches, counter_ref);
        kissat_push_large_watch(solver, second_watches, counter_ref);
        const unsigned count = (first_value > 0) + (second_value > 0);
        counter *counter = counters + counter_ref;
        counter->count = count;
        if (!count) {
            push_unsat(solver, walker, counters, counter_ref);
            unsat++;
        }
        counter_ref++;
    }
    kissat_phase(solver, "walk", GET(walks), "initially %u unsatisfied binary clauses %.0f%% out of %u", unsat,
        kissat_percent(unsat, counter_ref), counter_ref);
#ifdef QUIET
    (void)unsat;
#endif
    return counter_ref;
}

static void connect_large_counters(walker *walker, unsigned counter_ref)
{
    kissat *solver = walker->solver;
    assert(!solver->level);
    const value *saved = walker->saved;
    const value *values = solver->values;
    const word *arena = BEGIN_STACK(solver->arena);
    counter *counters = walker->counters;
    tagged *refs = walker->refs;

    unsigned unsat = 0;
    unsigned large = 0;

    clause *last_irredundant = kissat_last_irredundant_clause(solver);

    for (all_clauses(c)) {
        if (last_irredundant && c > last_irredundant)
            break;
        if (c->garbage)
            continue;
        if (c->redundant)
            continue;
        for (all_literals_in_clause(lit, c)) {
            const value value = saved[lit];
            if (value <= 0)
                continue;
            LOGCLS(c, "%s satisfied", LOGLIT(lit));
            kissat_mark_clause_as_garbage(solver, c);
            break;
        }
        if (c->garbage)
            continue;
        large++;
        assert(kissat_clause_in_arena(solver, c));
        reference clause_ref = (word *)c - arena;
        assert(clause_ref <= MAX_WALK_REF);
        assert(counter_ref < walker->clauses);
        refs[counter_ref] = make_tagged(false, clause_ref);
        unsigned count = 0;
        for (all_literals_in_clause(lit, c)) {
            const value value = values[lit];
            if (!value)
                continue;
            watches *watches = &WATCHES(lit);
            kissat_push_large_watch(solver, watches, counter_ref);
            if (value < 0)
                continue;
            count++;
        }
        counter *counter = walker->counters + counter_ref;
        counter->count = count;
        if (!count) {
            push_unsat(solver, walker, counters, counter_ref);
            unsat++;
        }
        counter_ref++;
    }
    kissat_phase(solver, "walk", GET(walks), "initially %u unsatisfied large clauses %.0f%% out of %u", unsat,
        kissat_percent(unsat, large), large);
#ifdef QUIET
    (void)large;
    (void)unsat;
#endif
}

#ifndef QUIET

static void report_initial_minimum(kissat *solver, walker *walker)
{
    walker->report.minimum = walker->minimum;
    kissat_very_verbose(solver, "initial minimum of %u unsatisfied clauses", walker->minimum);
}

static void report_minimum(const char *type, kissat *solver, walker *walker)
{
    assert(walker->minimum <= walker->report.minimum);
    kissat_very_verbose(solver, "%s minimum of %u unsatisfied clauses after %" PRIu64 " flipped literals", type,
        walker->minimum, walker->flipped);
    walker->report.minimum = walker->minimum;
}
#else
#define report_initial_minimum(...) \
    do {                            \
    } while (0)
#define report_minimum(...) \
    do {                    \
    } while (0)
#endif

static void init_walker(kissat *solver, walker *walker, litpairs *binaries)
{
    assert(IRREDUNDANT_CLAUSES <= MAX_WALK_REF);
    const unsigned clauses = IRREDUNDANT_CLAUSES;

    memset(walker, 0, sizeof *walker);

    walker->solver = solver;
    walker->clauses = clauses;
    walker->binaries = binaries;
    walker->random = solver->random;

    walker->saved = solver->values;
    solver->values = kissat_calloc(solver, LITS, 1);
    // INIT_STACK(walker->steps);

    import_decision_phases(walker);

    walker->counters = kissat_malloc(solver, clauses * sizeof(counter));
    walker->refs = kissat_malloc(solver, clauses * sizeof(tagged));

    const unsigned counter_ref = connect_binary_counters(walker);
    connect_large_counters(walker, counter_ref);

    walker->current = walker->initial = currently_unsatified(walker);

    kissat_phase(solver, "walk", GET(walks),
        "initially %u unsatisfied irredundant clauses %.0f%% "
        "out of %" PRIu64,
        walker->initial, kissat_percent(walker->initial, IRREDUNDANT_CLAUSES), IRREDUNDANT_CLAUSES);

    walker->minimum = walker->current;
    init_score_table(walker);

    report_initial_minimum(solver, walker);
}

static void init_walker_limit(kissat *solver, walker *walker)
{
    SET_EFFICIENCY_BOUND(limit, walk, walk_steps, search_ticks, 2 * CLAUSES);
    walker->limit = limit;

#ifndef QUIET
    walker->start = solver->statistics.walk_steps;
    walker->flipped = 0;
    walker->report.minimum = UINT_MAX;
    walker->report.flipped = 0;
    ;
#endif
}

static void release_walker(walker *walker)
{
    kissat *solver = walker->solver;
    kissat_dealloc(solver, walker->table, walker->exponents, sizeof(double));
    unsigned clauses = walker->clauses;
    kissat_dealloc(solver, walker->refs, clauses, sizeof(tagged));
    kissat_dealloc(solver, walker->counters, clauses, sizeof(counter));
    RELEASE_STACK(walker->unsat);
    RELEASE_STACK(walker->scores);
    RELEASE_STACK(walker->trail);
    kissat_free(solver, solver->values, LITS);
    RELEASE_STACK(walker->unsat);
    solver->values = walker->saved;
}

static unsigned break_value(kissat *solver, walker *walker, value *values, unsigned lit)
{
    assert(values[lit] < 0);
    const unsigned not_lit = NOT(lit);
    watches *watches = &WATCHES(not_lit);
    unsigned steps = 0;
    unsigned res = 0;
    for (all_binary_large_watches(watch, *watches)) {
        steps++;
        assert(!watch.type.binary);
        reference counter_ref = watch.large.ref;
        assert(counter_ref < walker->clauses);
        counter *counter = walker->counters + counter_ref;
        if (counter->count == 1)
            res++;
    }
    ADD(walk_steps, steps);
#ifdef NDEBUG
    (void)values;
#endif
    return res;
}

static double scale_score(walker *walker, unsigned breaks)
{
    if (breaks < walker->exponents)
        return walker->table[breaks];
    else
        return walker->epsilon;
}

static unsigned pick_literal(kissat *solver, walker *walker)
{
    assert(walker->current == SIZE_STACK(walker->unsat));
    const unsigned pos = kissat_next_random32(&walker->random) % walker->current;
    const unsigned counter_ref = PEEK_STACK(walker->unsat, pos);
    unsigned size;
    const unsigned *lits = dereference_literals(solver, walker, counter_ref, &size);

    assert(EMPTY_STACK(walker->scores));

    value *values = solver->values;

    double sum = 0;
    unsigned picked_lit = INVALID_LIT;

    const unsigned *end_of_lits = lits + size;
    for (const unsigned *p = lits; p != end_of_lits; p++) {
        const unsigned lit = *p;
        if (!values[lit])
            continue;
        picked_lit = lit;
        const unsigned breaks = break_value(solver, walker, values, lit);
        const double score = scale_score(walker, breaks);
        assert(score > 0);
        LOG("literal %s breaks %u score %g", LOGLIT(lit), breaks, score);
        PUSH_STACK(walker->scores, score);
        sum += score;
    }
    assert(picked_lit != INVALID_LIT);
    assert(0 < sum);

    const double random = kissat_pick_double(&walker->random);
    assert(0 <= random), assert(random < 1);

    const double threshold = sum * random;
    LOG("score sum %g and random threshold %g", sum, threshold);

    // assert (threshold < sum); // NOT TRUE!!!!

    double *scores = BEGIN_STACK(walker->scores);
#ifdef LOGGING
    double picked_score = 0;
#endif

    sum = 0;
    // CLEAR_STACK(walker->steps);

    for (const unsigned *p = lits; p != end_of_lits; p++) {
        const unsigned lit = *p;
        if (!values[lit])
            continue;
        const double score = *scores++;
        sum += score;
        if (threshold < sum) {
            picked_lit = lit;
#ifdef LOGGING
            picked_score = score;
#endif
            break;
        } else {
            // if(SIZE_STACK(walker->steps) + 1 < GET_OPTION(walkbits)) {
            //    PUSH_STACK(walker->steps, lit);
            // }
        }
    }
    assert(picked_lit != INVALID_LIT);
    LOG("picked literal %s with score %g", LOGLIT(picked_lit), picked_score);

    CLEAR_STACK(walker->scores);

    // PUSH_STACK(walker->steps, picked_lit);


    return picked_lit;
}

static void break_clauses(kissat *solver, walker *walker, const value *values, unsigned flipped)
{
#ifdef LOGGING
    unsigned broken = 0;
#endif
    const unsigned not_flipped = NOT(flipped);
    assert(values[not_flipped] < 0);
    LOG("breaking one-satisfied clauses containing negated flipped literal %s", LOGLIT(not_flipped));
    watches *watches = &WATCHES(not_flipped);
    counter *counters = walker->counters;
    unsigned steps = 0;
    for (all_binary_large_watches(watch, *watches)) {
        steps++;
        assert(!watch.type.binary);
        const unsigned counter_ref = watch.large.ref;
        assert(counter_ref < walker->clauses);
        counter *counter = counters + counter_ref;
        assert(counter->count);
        if (--counter->count)
            continue;
        push_unsat(solver, walker, counters, counter_ref);
#ifdef LOGGING
        broken++;
#endif
    }
    LOG("broken %u one-satisfied clauses containing "
        "negated flipped literal %s",
        broken, LOGLIT(not_flipped));
    ADD(walk_steps, steps);
#ifdef NDEBUG
    (void)values;
#endif
}

static void make_clauses(kissat *solver, walker *walker, const value *values, unsigned flipped)
{
    assert(values[flipped] > 0);
    LOG("making unsatisfied clauses containing flipped literal %s", LOGLIT(flipped));
    watches *watches = &WATCHES(flipped);
    counter *counters = walker->counters;
    unsigned steps = 0;
#ifdef LOGGING
    unsigned made = 0;
#endif
    for (all_binary_large_watches(watch, *watches)) {
        steps++;
        assert(!watch.type.binary);
        const unsigned counter_ref = watch.large.ref;
        assert(counter_ref < walker->clauses);
        counter *counter = counters + counter_ref;
        assert(counter->count < UINT_MAX);
        if (counter->count++)
            continue;
        if (pop_unsat(solver, walker, counters, counter_ref, counter->pos))
            steps++;
#ifdef LOGGING
        made++;
#endif
    }
    LOG("made %u unsatisfied clauses containing flipped literal %s", made, LOGLIT(flipped));
    ADD(walk_steps, steps);
#ifdef NDEBUG
    (void)values;
#endif
}

static void save_all_values(kissat *solver, walker *walker)
{
    assert(EMPTY_STACK(walker->trail));
    assert(walker->best == INVALID_BEST);
    LOG("copying all values as saved phases since trail is invalid");
    const value *values = solver->values;
    phase *phases = solver->phases;
    for (all_variables(idx)) {
        const unsigned lit = LIT(idx);
        const value value = values[lit];
        if (value)
            phases[idx].saved = value;
    }
    LOG("reset best trail position to 0");
    walker->best = 0;
}
unsigned calc_satisfied(kissat *solver, walker *walker, value *values)
{
    assert(SIZE_STACK(*walker->binaries) <= UINT_MAX);
    const unsigned size = SIZE_STACK(*walker->binaries);
    litpair *binaries = BEGIN_STACK(*walker->binaries);
    unsigned sat = 0;

    for (unsigned binary_ref = 0; binary_ref < size; binary_ref++) {
        const litpair *litpair = binaries + binary_ref;
        unsigned first = litpair->lits[0];
        unsigned second = litpair->lits[1];
        assert(first < LITS), assert(second < LITS);
        value first_value = values[IDX(first)];
        if (NEGATED(first))
            first_value *= -1;
        value second_value = values[IDX(second)];
        if (NEGATED(second))
            second_value *= -1;
        int count = (first_value > 0) + (second_value > 0);
        if (count > 0) {
            sat++;
        }
    }

    clause *last_irredundant = kissat_last_irredundant_clause(solver);

    for (all_clauses(c)) {
        if (last_irredundant && c > last_irredundant)
            break;
        if (c->garbage)
            continue;
        if (c->redundant)
            continue;
        assert(kissat_clause_in_arena(solver, c));
        unsigned count = 0;
        for (all_literals_in_clause(lit, c)) {
            value value = values[IDX(lit)];
            if (NEGATED(lit)) {
                value *= -1;
            }
            if (value < 0)
                continue;
            count++;
        }
        if (count > 0) {
            sat++;
        }
    }
    return sat;
}

/*unsigned kth_largest(unsigneds *a, unsigned rank)
{
    if (rank > SIZE_STACK(*a)) {
        return 0;
    }
    unsigned le = 0, ri = 0;
    for (all_stack(unsigned, x, *a)) {
        if (x > ri)
            ri = x;
    }
    ri++;
    while (le != ri) {
        unsigned mid = (le + ri) / 2;
        unsigned cnt = 0;
        for (all_stack(unsigned, x, *a)) {
            cnt += x >= mid;
        }
        if (cnt >= rank) {
            le = mid + 1;
        } else {
            ri = mid;
        }
    }
    return le;
}
static value *produce(kissat *solver, value *a, value *b)
{
    value *res = kissat_malloc(solver, VARS);
    for (all_variables(i)) {
        res[i] = kissat_next_random32(&solver->random) & 1 ? a[i] : b[i];
    }
    return res;
}
static unsigned genetic_algorithm(kissat *solver, walker *walker)
{
    unsigneds sat_cnt;
    INIT_STACK(sat_cnt);
    for (unsigned p = 0; p < solver->max_seed_count; p++) {
        PUSH_STACK(sat_cnt, calc_satisfied(solver, walker, PEEK_STACK(solver->seeds, p)));
    }
    unsigned ori = 0;
    for (unsigned p = 0; p < solver->max_seed_count; p++) {
        if (PEEK_STACK(sat_cnt, p) > ori) {
            ori = PEEK_STACK(sat_cnt, p);
        }
    }
    for (int i = 0; i < 100; i++) {
        for (unsigned p = 0; p < solver->max_seed_count; p++) {
            unsigned q = kissat_next_random32(&solver->random) % (solver->max_seed_count - 1);
            if (q == p)
                q++;
            PUSH_STACK(solver->seeds, produce(solver, PEEK_STACK(solver->seeds, p), PEEK_STACK(solver->seeds, q)));
            PUSH_STACK(sat_cnt, calc_satisfied(solver, walker, TOP_STACK(solver->seeds)));
        }
        unsigned bound = kth_largest(&sat_cnt, solver->max_seed_count);
        int st = 0;
        for (unsigned p = 0; p < SIZE_STACK(solver->seeds) && SIZE_STACK(solver->seeds) > solver->max_seed_count; p++) {
            if (PEEK_STACK(sat_cnt, p) >= bound) {
                value *tmp = PEEK_STACK(solver->seeds, p);
                POKE_STACK(solver->seeds, p, PEEK_STACK(solver->seeds, st));
                POKE_STACK(solver->seeds, st, tmp);

                unsigned tc = PEEK_STACK(sat_cnt, p);
                POKE_STACK(sat_cnt, p, PEEK_STACK(sat_cnt, st));
                POKE_STACK(sat_cnt, st, tc);

                st++;
            }
        }
        while (SIZE_STACK(solver->seeds) > solver->max_seed_count) {
            POP_STACK(sat_cnt);
            kissat_free(solver, TOP_STACK(solver->seeds), VARS);
            POP_STACK(solver->seeds);
        }
    }
    unsigned max_sat = 0, max_p = 0;
    for (unsigned p = 0; p < solver->max_seed_count; p++) {
        if (PEEK_STACK(sat_cnt, p) > max_sat) {
            max_sat = PEEK_STACK(sat_cnt, p);
            max_p = p;
        }
    }
    RELEASE_STACK(sat_cnt);
    return max_p;
}*/

static void save_walker_trail(kissat *solver, walker *walker, bool keep)
{
#if defined(LOGGING) || !defined(NDEBUG)
    assert(walker->best != INVALID_BEST);
    assert(SIZE_STACK(walker->trail) <= UINT_MAX);
    const unsigned size_trail = SIZE_STACK(walker->trail);
    assert(walker->best <= size_trail);
    const unsigned kept = size_trail - walker->best;
    LOG("saving %u values of flipped literals on trail of size %u", walker->best, size_trail);
#endif
    unsigned *begin = BEGIN_STACK(walker->trail);
    const unsigned *best = begin + walker->best;
    const value *values = solver->values;
    phase *phases = solver->phases;
    for (const unsigned *p = begin; p != best; p++) {
        const unsigned lit = *p;
        value value = values[lit];
        assert(value);
        if (NEGATED(lit))
            value = -value;
        const unsigned idx = IDX(lit);
        phases[idx].saved = value;
    }
    /*if (false && solver->max_seed_count > 0 && solver->statistics.walks >= 5 * solver->max_seed_count) {
        int pos;
        if (solver->max_seed_count > SIZE_STACK(solver->seeds)) {
            PUSH_STACK(solver->seeds, NULL);
            pos = SIZE_STACK(solver->seeds) - 1;
        } else {
            pos = kissat_next_random32(&walker->random) % SIZE_STACK(solver->seeds);
        }
        if (PEEK_STACK(solver->seeds, pos) != NULL) {
            kissat_free(solver, PEEK_STACK(solver->seeds, pos), VARS);
        }

        PEEK_STACK(solver->seeds, pos) = kissat_malloc(solver, VARS);
        value *nw = PEEK_STACK(solver->seeds, pos);
        for (unsigned i = 0; i < VARS; i++) {
            nw[i] = phases[i].saved;
        }
        if (solver->max_seed_count == SIZE_STACK(solver->seeds) &&
            solver->statistics.walks % (10 * solver->max_seed_count) == 0) { // 2 times genetic algo
            unsigned max_p = genetic_algorithm(solver, walker);
            for (all_variables(i)) {
                phases[i].saved = PEEK_STACK(solver->seeds, max_p)[i];
            }
        }
    }*/

    if (!keep) {
        LOG("no need to shift and keep remaining %u literals", kept);
        return;
    }
    LOG("flushed %u literals %.0f%% from trail", walker->best, kissat_percent(walker->best, size_trail));
    const unsigned *end = END_STACK(walker->trail);
    unsigned *q = begin;
    for (const unsigned *p = best; p != end; p++)
        *q++ = *p;
    assert((size_t)(end - q) == walker->best);
    assert((size_t)(q - begin) == kept);
    SET_END_OF_STACK(walker->trail, q);
    LOG("keeping %u literals %.0f%% on trail", kept, kissat_percent(kept, size_trail));
    LOG("reset best trail position to 0");
    walker->best = 0;
}

static void push_flipped(kissat *solver, walker *walker, unsigned flipped)
{
    if (walker->best == INVALID_BEST) {
        LOG("not pushing flipped %s to already invalid trail", LOGLIT(flipped));
        assert(EMPTY_STACK(walker->trail));
    } else {
        assert(SIZE_STACK(walker->trail) <= UINT_MAX);
        const unsigned size_trail = SIZE_STACK(walker->trail);
        assert(walker->best <= size_trail);
        const unsigned limit = VARS / 4 + 1;
        assert(limit < INVALID_BEST);
        if (size_trail < limit) {
            PUSH_STACK(walker->trail, flipped);
            LOG("pushed flipped %s to trail which now has size %u", LOGLIT(flipped), size_trail + 1);
        } else if (walker->best) {
            LOG("trail reached limit %u but has best position %u", limit, walker->best);
            save_walker_trail(solver, walker, true);
            PUSH_STACK(walker->trail, flipped);
            assert(SIZE_STACK(walker->trail) <= UINT_MAX);
            LOG("pushed flipped %s to trail which now has size %u", LOGLIT(flipped), SIZE_STACK(walker->trail));
        } else {
            LOG("trail reached limit %u without best position", limit);
            CLEAR_STACK(walker->trail);
            LOG("not pushing %s to invalidated trail", LOGLIT(flipped));
            walker->best = INVALID_BEST;
            LOG("best trail position becomes invalid");
        }
    }
}

static void flip_literal(kissat *solver, walker *walker, unsigned flip)
{
    LOG("flipping literal %s", LOGLIT(flip));
    value *values = solver->values;
    const value value = values[flip];
    assert(value < 0);
    values[flip] = -value;
    values[NOT(flip)] = value;
    make_clauses(solver, walker, values, flip);
    break_clauses(solver, walker, values, flip);
    walker->current = currently_unsatified(walker);
}

static void update_best(kissat *solver, walker *walker)
{
    assert(walker->current < walker->minimum);
    walker->minimum = walker->current;
#ifndef QUIET
    int verbosity = kissat_verbosity(solver);
    bool report = (verbosity > 2);
    if (verbosity == 2) {
        if (walker->flipped / 2 >= walker->report.flipped)
            report = true;
        else if (walker->minimum < 5 || walker->report.minimum == UINT_MAX ||
            walker->minimum <= walker->report.minimum / 2)
            report = true;
        if (report) {
            walker->report.minimum = walker->minimum;
            walker->report.flipped = walker->flipped;
        }
    }
    if (report)
        report_minimum("new", solver, walker);
#endif
    if (walker->best == INVALID_BEST)
        save_all_values(solver, walker);
    else {
        assert(SIZE_STACK(walker->trail) < INVALID_BEST);
        walker->best = SIZE_STACK(walker->trail);
        LOG("new best trail position %u", walker->best);
    }
}

static void local_search_step(kissat *solver, walker *walker)
{
    assert(walker->current);
    INC(flipped);
#ifndef QUIET
    assert(walker->flipped < UINT64_MAX);
    walker->flipped++;
#endif
    LOG("starting local search flip %" PRIu64 " with %u unsatisfied clauses", GET(flipped), walker->current);
    unsigned lit = pick_literal(solver, walker);
    flip_literal(solver, walker, lit);
    push_flipped(solver, walker, lit);
    if (walker->current < walker->minimum)
        update_best(solver, walker);
    LOG("ending local search step %" PRIu64 " with %u unsatisfied clauses", GET(flipped), walker->current);
}

static bool local_search_round(walker *walker, unsigned round)
{
    kissat *solver = walker->solver;
    kissat_very_verbose(solver, "round %u starts with %u unsatisfied clauses", round, walker->current);
    init_walker_limit(solver, walker);
    const unsigned before = walker->minimum;

#ifndef QUIET
#endif
    statistics *statistics = &solver->statistics;
    while (walker->minimum && walker->limit > statistics->walk_steps) {
        if (TERMINATED(23))
            break;
        local_search_step(solver, walker);
    }
#ifndef QUIET
    report_minimum("last", solver, walker);
    assert(statistics->walk_steps >= walker->start);
    const uint64_t steps = statistics->walk_steps - walker->start;
    // *INDENT-OFF*
    kissat_very_verbose(solver, "round %u ends with %u unsatisfied clauses", round, walker->current);
    kissat_very_verbose(solver, "flipping %" PRIu64 " literals took %" PRIu64 " steps (%.2f per flipped)",
        walker->flipped, steps, kissat_average(steps, walker->flipped));
    // *INDENT-ON*
    kissat_phase(solver, "walk", GET(walks), "round %u ends with %s minimum %u after %" PRIu64 " flips", round,
        walker->minimum < before ? "new" : "unchanged", walker->minimum, walker->flipped);
#else
    (void)round;
#endif
    const unsigned after = walker->minimum;
    return after < before;
}

static char save_final_minimum(walker *walker)
{
    kissat *solver = walker->solver;

    assert(walker->minimum <= walker->initial);
    if (walker->minimum == walker->initial) {
        kissat_phase(solver, "walk", GET(walks), "no improvement thus keeping saved phases");
        return 0;
    }

    if (!walker->best || walker->best == INVALID_BEST)
        LOG("minimum already saved");
    else
        save_walker_trail(solver, walker, false);

    INC(walk_improved);
    kissat_phase(solver, "walk", GET(walks), "improved assignment to %u unsatisfied clauses", walker->minimum);
    return 'W';
}

static char walking_phase(kissat *solver)
{
    INC(walks);
    litpairs irredundant;
    litwatches redundant;
    INIT_STACK(irredundant);
    INIT_STACK(redundant);
    kissat_enter_dense_mode(solver, &irredundant, &redundant);
    walker walker;
    init_walker(solver, &walker, &irredundant);
    unsigned max_rounds = GET_OPTION(walkrounds);
    if(solver->walk_strategy == 2) {
        max_rounds *= 5;
    }else if(solver->walk_strategy == 1) {
        max_rounds = 0;
    }
    // max_rounds *= 5;
    for (unsigned round = 1; walker.minimum && round <= max_rounds; round++) {
        if (TERMINATED(24))
            break;
        //bool improved = 
        local_search_round(&walker, round);
        // if(improved) max_rounds++;
    }
    char res = save_final_minimum(&walker);
    release_walker(&walker);
    kissat_resume_sparse_mode(solver, false, &irredundant, &redundant);
    RELEASE_STACK(irredundant);
    RELEASE_STACK(redundant);
    return res;
}

char kissat_walk(kissat *solver)
{
    assert(!solver->level);
    assert(!solver->inconsistent);
    assert(kissat_propagated(solver));

    reference last_irredundant = solver->last_irredundant;
    if (last_irredundant == INVALID_REF)
        last_irredundant = SIZE_STACK(solver->arena);

    if (last_irredundant > MAX_WALK_REF) {
        kissat_phase(solver, "walk", GET(walks), "last irredundant clause reference %u too large", last_irredundant);
        return kissat_rephase_best(solver);
    }

    if (IRREDUNDANT_CLAUSES > MAX_WALK_REF) {
        kissat_phase(solver, "walk", GET(walks), "way too many irredundant clauses %" PRIu64, IRREDUNDANT_CLAUSES);
        return kissat_rephase_best(solver);
    }

    STOP_SEARCH_AND_START_SIMPLIFIER(walking);
    char res = walking_phase(solver);
    STOP_SIMPLIFIER_AND_RESUME_SEARCH(walking);
    return res;
}

int kissat_walk_initially(kissat *solver)
{
    if (solver->PSIDS) {
        return 0;
    }
    if (solver->inconsistent)
        return 20;
    if (TERMINATED(25))
        return 0;
    if (!GET_OPTION(walkinitially))
        return 0;
#ifndef QUIET
    char type =
#endif
        kissat_walk(solver);
    REPORT(0, type ? type : 'W');
    return 0;
}
