#include "reduce.h"
#include "allocate.h"
#include "collect.h"
#include "inline.h"
#include "print.h"
#include "rank.h"
#include "report.h"
#include "tiers.h"
#include "trail.h"

#include <inttypes.h>
#include <math.h>

bool kissat_reducing (kissat *solver) {
  if (!GET_OPTION (reduce))
    return false;
  if (!solver->statistics.clauses_redundant)
    return false;
  if (CONFLICTS < solver->limits.reduce.conflicts)
    return false;
  return true;
}

bool kissat_decaying (kissat *solver) {
  
 // if ((CONFLICTS & ((1<<11)-1)))  //20250718cyl
  if ((CONFLICTS & ((1<<12)-1)))  //Oct tiaocan
    return false;
  return true;
}

void kissat_decay (kissat *solver) {
  for (all_clauses (c)) {
    if (!c->redundant)
      continue;
    if (c->garbage)
      continue;
    if (c->lru_time_stamp)
      --c->lru_time_stamp;
  }
}

// void kissat_decay (kissat *solver) {
//   uint64_t lru_sum[26] = {0};
//   uint64_t lru_count[26] = {0};
//   for (all_clauses (c)) {
//     if (!c->redundant) continue;
//     if (c->garbage) continue;
//     if (c->glue >= 1 && c->glue <= 25) {
//       lru_sum[c->glue] += c->lru_time_stamp;
//       lru_count[c->glue]++;
//     }
//     if (c->lru_time_stamp)
//       --c->lru_time_stamp;
//   }
//   for (int i = 1; i <= 25; ++i) {
//     if (lru_count[i])
//       solver->average_score[i] = (unsigned)round((double)lru_sum[i] / lru_count[i]);
//     else
//       solver->average_score[i] = 0;
//   }
// }

bool kissat_observing (kissat *solver) {
  if (CONFLICTS==150000)
    return true;
  return false;
}

// void kissat_observe(kissat *solver) {
//   // 统计 glue=2~25 的子句的 lru_time_stamp 总和与数量
//   uint64_t lru_sum[26] = {0};   // glue 0~25
//   uint64_t lru_count[26] = {0};
//   bool overflow = false;
//   uint64_t glue2_25_total = 0; // glue在2-25之间的子句总数
//   uint64_t all_redundant_total = 0; // 所有学习子句总数

//   // 新增：统计长度为2~50的子句的lru_time_stamp总和与数量
//   uint64_t len_sum[51] = {0};   // len 0~50
//   uint64_t len_count[51] = {0};

//   // 新增：统计长度为4~8的子句，内部不同lbd的平均lru_time_stamp
//   uint64_t lbd_sum[9][9] = {{0}};   // lbd_sum[size][lbd]，size=4~8，lbd=1~size
//   uint64_t lbd_count[9][9] = {{0}};

//   // 新增：统计lbd为2~8的子句，内部不同长度的平均lru_time_stamp
//   uint64_t lbd_len_sum[9][51] = {{0}};   // lbd=2~8, len=2~50
//   uint64_t lbd_len_count[9][51] = {{0}};

//   for (all_clauses(c)) {
//     if (!c->redundant) continue;
//     if (c->garbage) continue;
//     all_redundant_total++;
//     unsigned glue = c->glue;
//     unsigned len = c->size;
//     if (glue >= 1 && glue <= 25) {
//       glue2_25_total++;
//       // 检查lru_sum溢出
//       if (UINT64_MAX - lru_sum[glue] < c->lru_time_stamp) {
//         overflow = true;
//         break;
//       }
//       lru_sum[glue] += c->lru_time_stamp;
//       // 检查lru_count溢出
//       if (lru_count[glue] == UINT64_MAX) {
//         overflow = true;
//         break;
//       }
//       lru_count[glue]++;
//     }
//     // 统计长度
//     if (len >= 2 && len <= 50) {
//       if (UINT64_MAX - len_sum[len] < c->lru_time_stamp) {
//         overflow = true;
//         break;
//       }
//       len_sum[len] += c->lru_time_stamp;
//       if (len_count[len] == UINT64_MAX) {
//         overflow = true;
//         break;
//       }
//       len_count[len]++;
//     }
//     // 统计长度为4~8的子句内部不同lbd的平均lru_time_stamp
//     if (len >= 4 && len <= 8 && glue >= 1 && glue <= len) {
//       if (UINT64_MAX - lbd_sum[len][glue] < c->lru_time_stamp) {
//         overflow = true;
//         break;
//       }
//       lbd_sum[len][glue] += c->lru_time_stamp;
//       if (lbd_count[len][glue] == UINT64_MAX) {
//         overflow = true;
//         break;
//       }
//       lbd_count[len][glue]++;
//     }
//     // 统计lbd为2~8的子句内部不同长度的平均lru_time_stamp
//     if (glue >= 2 && glue <= 8 && len >= glue && len <= 50) {
//       if (UINT64_MAX - lbd_len_sum[glue][len] < c->lru_time_stamp) {
//         overflow = true;
//         break;
//       }
//       lbd_len_sum[glue][len] += c->lru_time_stamp;
//       if (lbd_len_count[glue][len] == UINT64_MAX) {
//         overflow = true;
//         break;
//       }
//       lbd_len_count[glue][len]++;
//     }
//   }

//   if (overflow) return;

//   FILE *fp = fopen("./lru_glue_stat.txt", "w");
//   if (!fp) return;

//   for (unsigned glue = 1; glue <= 25; ++glue) {
//     double avg = lru_count[glue] ? (double)lru_sum[glue] / lru_count[glue] : 0.0;
//     fprintf(fp, "glue=%u avg_lru_time_stamp=%.2f count=%" PRIu64 "\n", glue, avg, lru_count[glue]);
//   }
//   fprintf(fp, "glue_1_25_total=%" PRIu64 "\n", glue2_25_total);
//   fprintf(fp, "all_redundant_total=%" PRIu64 "\n", all_redundant_total);

//   // 输出长度为2~50的子句的lru_time_stamp平均值
//   for (unsigned len = 2; len <= 50; ++len) {
//     double avg = len_count[len] ? (double)len_sum[len] / len_count[len] : 0.0;
//     fprintf(fp, "len=%u avg_lru_time_stamp=%.2f count=%" PRIu64 "\n", len, avg, len_count[len]);
//   }

//   // 输出长度为4~8的子句内部不同lbd的平均lru_time_stamp
//   for (unsigned size = 4; size <= 8; ++size) {
//     for (unsigned lbd = 1; lbd <= size; ++lbd) {
//       double avg = lbd_count[size][lbd] ? (double)lbd_sum[size][lbd] / lbd_count[size][lbd] : 0.0;
//       fprintf(fp, "size=%u lbd=%u avg_lru_time_stamp=%.2f count=%" PRIu64 "\n", size, lbd, avg, lbd_count[size][lbd]);
//     }
//   }

//   // 输出lbd为2~8的子句内部不同长度的平均lru_time_stamp
//   for (unsigned lbd = 2; lbd <= 8; ++lbd) {
//     for (unsigned len = lbd; len <= 50; ++len) {
//       double avg = lbd_len_count[lbd][len] ? (double)lbd_len_sum[lbd][len] / lbd_len_count[lbd][len] : 0.0;
//       fprintf(fp, "lbd=%u len=%u avg_lru_time_stamp=%.2f count=%" PRIu64 "\n", lbd, len, avg, lbd_len_count[lbd][len]);
//     }
//   }

//   fclose(fp);
// }

// void kissat_observe(kissat *solver) {
//     FILE *fp = fopen("./score_matrix.txt", "w");
//     if (!fp) return;

//     // 输出表头
//     fprintf(fp, "     ");
//     for (int j = 1; j <= 30; ++j) {
//         fprintf(fp, "%5d ", j);
//     }
//     fprintf(fp, "\n");

//     for (int i = 1; i <= 30; ++i) {
//         fprintf(fp, "%3d: ", i);
//         for (int j = 1; j <= 30; ++j) {
//             uint64_t avg = 0;
//             if (solver->clause_count[i][j] != 0) {
//                 avg = (uint64_t)((double)solver->total_score[i][j] / (double)solver->clause_count[i][j]);
//             }
//             fprintf(fp, "%5" PRIu64 " ", avg);
//         }
//         fprintf(fp, "\n");
//     }

//     fclose(fp);
// }

typedef struct reducible reducible;

struct reducible {
  uint64_t rank;
  unsigned ref;
};

#define RANK_REDUCIBLE(RED) (RED).rank

// clang-format off
typedef STACK (reducible) reducibles;
// clang-format on

static bool collect_reducibles (kissat *solver, reducibles *reds,
                                reference start_ref) {
  assert (start_ref != INVALID_REF);
  assert (start_ref <= SIZE_STACK (solver->arena));
  ward *const arena = BEGIN_STACK (solver->arena);
  clause *start = (clause *) (arena + start_ref);
  const clause *const end = (clause *) END_STACK (solver->arena);
  assert (start < end);
  while (start != end && !start->redundant)
    start = kissat_next_clause (start);
  if (start == end) {
    solver->first_reducible = INVALID_REF;
    LOG ("no reducible clause candidate left");
    return false;
  }
  const reference redundant = (ward *) start - arena;
#ifdef LOGGING
  if (redundant < solver->first_reducible)
    LOG ("updating start of redundant clauses from %zu to %zu",
         (size_t) solver->first_reducible, (size_t) redundant);
  else
    LOG ("no update to start of redundant clauses %zu",
         (size_t) solver->first_reducible);
#endif
  solver->first_reducible = redundant;
  const unsigned tier1 = TIER1;
  const unsigned tier2 = MAX (tier1, TIER2);
  assert (tier1 <= tier2);
  for (clause *c = start; c != end; c = kissat_next_clause (c)) {
    if (!c->redundant)
      continue;
    if (c->garbage)
      continue;
    const unsigned used = c->used;
    if (used)
      c->used = used - 1;
    if (c->reason)
      continue;

    //  const unsigned glue = c->glue;
    // if (glue <= tier1 && used)
    //   continue;
    // if (glue <= tier2 && used >= MAX_USED - 1)
    //   continue;
    if(c->lru_time_stamp){
        // --c->lru_time_stamp;   0717cyl
        continue;
    }
#if 1
   
    assert (kissat_clause_in_arena (solver, c));
    reducible red;
#if 1
    // const uint64_t negative_size = ~c->size;
    // const uint64_t negative_glue = ~c->glue;
    // red.rank = negative_size | (negative_glue << 32);
   /* const uint64_t negative_size = ~c->size;
    const uint64_t negative_glue = ~c->glue;
    uint64_t all_used_num = c->all_used_num;
    if(all_used_num>UINT32_MAX)all_used_num=UINT32_MAX;
    red.rank = negative_size | (negative_glue << 16) | (all_used_num << 32);*/
    const uint64_t negative_size = ~c->size;
    red.rank = negative_size;
#else
    if(c->size<1000&&c->glue<1000){
      if(solver->clause_count[c->glue][c->size]){
        red.rank = (uint64_t)((double)solver->total_score[c->glue][c->size]/(double)solver->clause_count[c->glue][c->size]);
        // red.rank = ~red.rank;
      }
      else
        red.rank = 0;
    }
    else 
      red.rank = 0;
#endif
    red.ref = (ward *) c - arena;
    PUSH_STACK (*reds, red);
#else
    // 2025-06-26 (taomengxia): LRU
    const uint64_t lru_time_stamp = c->lru_time_stamp;
    assert (kissat_clause_in_arena (solver, c));
    reducible red;
    // red.rank = lru_time_stamp;
    red.rank = ~lru_time_stamp; //cylxiashi
    red.ref = (ward *) c - arena;
    PUSH_STACK (*reds, red);
#endif
  }
  if (EMPTY_STACK (*reds)) {
    kissat_phase (solver, "reduce", GET (reductions),
                  "did not find any reducible redundant clause");
    return false;
  }
  return true;
}

#define USEFULNESS RANK_REDUCIBLE

static void sort_reducibles (kissat *solver, reducibles *reds) {
  RADIX_STACK (reducible, uint64_t, *reds, USEFULNESS);
}

static void mark_less_useful_clauses_as_garbage (kissat *solver,
                                                 reducibles *reds) {
  statistics *statistics = &solver->statistics;
  const double high = GET_OPTION (reducehigh) * 0.1;
  const double low = GET_OPTION (reducelow) * 0.1;
  double percent;
  if (low < high) {
    const double delta = high - low;
    percent = high - delta / log10 (statistics->reductions + 9);
  } else
    percent = low;
  const double fraction = percent / 100.0;
  const size_t size = SIZE_STACK (*reds);
  size_t target = size * fraction;
#ifndef QUIET
  const size_t clauses =
      statistics->clauses_irredundant + statistics->clauses_redundant;
  kissat_phase (solver, "reduce", GET (reductions),
                "reducing %zu (%.0f%%) out of %zu (%.0f%%) "
                "reducible clauses",
                target, kissat_percent (target, size), size,
                kissat_percent (size, clauses));
#endif
  unsigned reduced = 0, reduced1 = 0, reduced2 = 0, reduced3 = 0;
  ward *arena = BEGIN_STACK (solver->arena);
  const reducible *const begin = BEGIN_STACK (*reds);
  const reducible *const end = END_STACK (*reds);
  const unsigned tier1 = TIER1;
  const unsigned tier2 = TIER2;
  for (const reducible *p = begin; p != end && target--; p++) {
    clause *c = (clause *) (arena + p->ref);
    assert (kissat_clause_in_arena (solver, c));
    assert (!c->garbage);
    assert (!c->reason);
    assert (c->redundant);
    LOGCLS (c, "reducing");
    kissat_mark_clause_as_garbage (solver, c);
    reduced++;
    if (c->glue <= tier1)
      reduced1++;
    else if (c->glue <= tier2)
      reduced2++;
    else
      reduced3++;
  }
  ADD (clauses_reduced_tier1, reduced1);
  ADD (clauses_reduced_tier2, reduced2);
  ADD (clauses_reduced_tier3, reduced3);
  ADD (clauses_reduced, reduced);
}

int kissat_reduce (kissat *solver) {
  START (reduce);
  INC (reductions);
  kissat_phase (solver, "reduce", GET (reductions),
                "reduce limit %" PRIu64 " hit after %" PRIu64 " conflicts",
                solver->limits.reduce.conflicts, CONFLICTS);
  kissat_compute_and_set_tier_limits (solver);
  bool compact = kissat_compacting (solver);
  reference start = compact ? 0 : solver->first_reducible;
  if (start != INVALID_REF) {
#ifndef QUIET
    size_t arena_size = SIZE_STACK (solver->arena);
    size_t words_to_sweep = arena_size - start;
    size_t bytes_to_sweep = sizeof (word) * words_to_sweep;
    kissat_phase (solver, "reduce", GET (reductions),
                  "reducing clauses after offset %" REFERENCE_FORMAT
                  " in arena",
                  start);
    kissat_phase (solver, "reduce", GET (reductions),
                  "reducing %zu words %s %.0f%%", words_to_sweep,
                  FORMAT_BYTES (bytes_to_sweep),
                  kissat_percent (words_to_sweep, arena_size));
#endif
    if (kissat_flush_and_mark_reason_clauses (solver, start)) {
      reducibles reds;
      INIT_STACK (reds);
      if (collect_reducibles (solver, &reds, start)) {
        sort_reducibles (solver, &reds);
        mark_less_useful_clauses_as_garbage (solver, &reds);
        RELEASE_STACK (reds);
        kissat_sparse_collect (solver, compact, start);
      } else if (compact)
        kissat_sparse_collect (solver, compact, start);
      else
        kissat_unmark_reason_clauses (solver, start);
    } else
      assert (solver->inconsistent);
  } else
    kissat_phase (solver, "reduce", GET (reductions), "nothing to reduce");
  kissat_classify (solver);
  UPDATE_CONFLICT_LIMIT (reduce, reductions, SQRT, false);
  solver->last.conflicts.reduce = CONFLICTS;
  REPORT (0, '-');
  STOP (reduce);
  return solver->inconsistent ? 20 : 0;
}
