#ifndef _limits_h_INCLUDED
#define _limits_h_INCLUDED

#include <stdbool.h>
#include <stdint.h>

typedef struct bounds bounds;
typedef struct changes changes;
typedef struct delays delays;
typedef struct delay delay;
typedef struct enabled enabled;
typedef struct limited limited;
typedef struct limits limits;
typedef struct waiting waiting;

struct bounds
{
  struct
  {
    uint64_t max_bound_completed;
    unsigned additional_clauses;
    unsigned clause_size;
    unsigned occurrences;
  } eliminate;

  struct
  {
    unsigned clause_size;
    unsigned occurrences;
  } subsume;

  struct
  {
    unsigned clause_size;
  } xork;
};

struct changes
{
  struct
  {
    uint64_t added;
    uint64_t removed;
    unsigned units;
  } variables;
  struct
  {
    unsigned additional_clauses;
  } eliminate;
};

struct limits
{
  uint64_t conflicts;
  uint64_t decisions;
  uint64_t reports;

  union
  {
    uint64_t ticks;
    uint64_t conflicts;
  } mode;

  struct
  {
    struct
    {
      uint64_t added;
      uint64_t removed;
    } variables;
    uint64_t conflicts;
  } eliminate;

  struct
  {
    uint64_t conflicts;
  } probe, reduce, rephase, restart;
};

struct limited
{
  bool conflicts;
  bool decisions;
};

struct enabled
{
  bool autarky;
  bool eliminate;
  bool focus;
  bool mode;
  bool probe;
};

struct delay
{
  unsigned count;
  unsigned current;
};

struct delays
{
  delay autarky;
  delay eliminate;
  delay failed;
  delay probe;
  delay substitute;
  delay ternary;
};

struct waiting
{
  struct
  {
    uint64_t reduce;
  } eliminate, probe;
};

struct kissat;

changes kissat_changes (struct kissat *);

bool kissat_changed (changes before, changes after);

void kissat_init_limits (struct kissat *);

uint64_t kissat_scale_delta (struct kissat *, const char *, uint64_t);

uint64_t kissat_scale_limit (struct kissat *,
			     const char *, uint64_t count, int base);

#define SCALE_LIMIT(COUNT,NAME) \
  kissat_scale_limit (solver, #NAME, \
                      solver->statistics.COUNT, GET_OPTION (NAME))

uint64_t kissat_logn (uint64_t);
uint64_t kissat_ndivlogn (uint64_t);
uint64_t kissat_linear (uint64_t);
uint64_t kissat_nlogn (uint64_t);
uint64_t kissat_nlognlogn (uint64_t);
uint64_t kissat_quadratic (uint64_t);

#define NDIVLOGN(COUNT) kissat_ndivlogn (COUNT)
#define LINEAR(COUNT) kissat_linear (COUNT)
#define NLOGN(COUNT) kissat_nlogn (COUNT)
#define NLOGNLOGN(COUNT) kissat_nlognlogn (COUNT)
#define QUADRATIC(COUNT) kissat_quadratic (COUNT)

#define INIT_CONFLICT_LIMIT(NAME,SCALE) \
do { \
  const uint64_t DELTA = GET_OPTION (NAME ## init); \
  const uint64_t SCALED = !(SCALE) ? DELTA : \
    kissat_scale_delta (solver, #NAME, DELTA); \
  limits->NAME.conflicts = CONFLICTS + SCALED; \
  kissat_very_verbose (solver, \
    "initial " #NAME " limit of %s conflicts", \
    FORMAT_COUNT (limits->NAME.conflicts)); \
} while (0)

#define UPDATE_CONFLICT_LIMIT(NAME,COUNT,SCALE_COUNT_FUNCTION,SCALE_DELTA) \
do { \
  if (solver->inconsistent) \
    break; \
  struct statistics *statistics = &solver->statistics; \
  struct limits *limits = &solver->limits; \
  uint64_t DELTA = GET_OPTION (NAME ## int); \
  DELTA *= SCALE_COUNT_FUNCTION (statistics->COUNT) + 1; \
  const uint64_t SCALED = !(SCALE_DELTA) ? DELTA : \
    kissat_scale_delta (solver, #NAME, DELTA); \
  limits->NAME.conflicts = CONFLICTS + SCALED; \
  kissat_phase (solver, #NAME, \
		GET (COUNT), \
		"new limit of %s after %s conflicts", \
		FORMAT_COUNT (limits->NAME.conflicts), \
		FORMAT_COUNT (SCALED)); \
} while (0)

#include <inttypes.h>

#define SET_EFFICIENCY_BOUND(BOUND,NAME,START,REFERENCE,ADDITIONAL) \
  uint64_t BOUND = solver->statistics.START; \
  do { \
    const uint64_t REFERENCE = solver->statistics.REFERENCE; \
    const uint64_t MINIMUM = GET_OPTION (NAME ## mineff); \
    const uint64_t MAXIMUM = MINIMUM * GET_OPTION (NAME ## maxeff); \
    const double EFFICIENCY = (double) GET_OPTION (NAME ## releff) / 1e3; \
    const uint64_t ADJUSTMENT = (ADDITIONAL); \
    const uint64_t PRODUCT = REFERENCE * EFFICIENCY; \
    uint64_t DELTA = PRODUCT + ADJUSTMENT; \
    if (DELTA < MINIMUM) \
      DELTA = MINIMUM; \
    if (DELTA > MAXIMUM) \
      DELTA = MAXIMUM; \
    BOUND += DELTA; \
    kissat_very_verbose (solver, \
      #NAME " efficiency limit %s delta %s = %s + %s", \
      FORMAT_COUNT (BOUND), FORMAT_COUNT (DELTA), \
      FORMAT_COUNT (PRODUCT), FORMAT_COUNT (ADJUSTMENT)); \
  } while (0)

#define RETURN_IF_DELAYED(NAME) \
do { \
  assert (!solver->inconsistent); \
  if (!GET_OPTION (NAME ## delay)) \
    break; \
  delay * DELAY = &solver->delays.NAME; \
  assert (DELAY->count <= DELAY->current); \
  if (!DELAY->count) \
    break; \
  kissat_very_verbose (solver, \
                       #NAME " delayed %u more time%s", \
		       DELAY->count, DELAY->count > 1 ? "s" : ""); \
  DELAY->count--; \
  return; \
} while (0)

#define UPDATE_DELAY(SUCCESS,NAME) \
do { \
  if (solver->inconsistent) \
    break; \
  if (!GET_OPTION (NAME ## delay)) \
    break; \
  delay * DELAY = &solver->delays.NAME; \
  unsigned MAX_DELAY = GET_OPTION (delay); \
  assert (DELAY->count <= DELAY->current); \
  if (SUCCESS) \
    { \
      if (DELAY->current) \
	{ \
	  kissat_very_verbose (solver, #NAME " delay reset"); \
	  DELAY->current = DELAY->count = 0; \
	} \
      else \
	assert (!DELAY->count); \
    } \
  else \
    { \
      if (DELAY->current < MAX_DELAY) \
	{ \
	  DELAY->current++; \
	  kissat_very_verbose (solver, \
			       #NAME " delay increased to %u", \
			       DELAY->current); \
	} \
      else \
	kissat_very_verbose (solver, \
			     "keeping " #NAME " delay at maximum %u", \
			     DELAY->current); \
      DELAY->count = DELAY->current; \
    } \
  assert (DELAY->count <= DELAY->current); \
} while (0)

#endif
