// *********************************************************************
// Copyright 2000-2004, Princeton University.  All rights reserved.
// By using this software the USER indicates that he or she has read,
// understood and will comply with the following:
//
// --- Princeton University hereby grants USER nonexclusive permission
// to use, copy and/or modify this software for internal, noncommercial,
// research purposes only. Any distribution, including commercial sale
// or license, of this software, copies of the software, its associated
// documentation and/or modifications of either is strictly prohibited
// without the prior consent of Princeton University.  Title to copyright
// to this software and its associated documentation shall at all times
// remain with Princeton University.  Appropriate copyright notice shall
// be placed on all software copies, and a complete copy of this notice
// shall be included in all copies of the associated documentation.
// No right is  granted to use in advertising, publicity or otherwise
// any trademark,  service mark, or the name of Princeton University.
//
//
// --- This software and any associated documentation is provided "as is"
//
// PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS
// OR IMPLIED, INCLUDING THOSE OF MERCHANTABILITY OR FITNESS FOR A
// PARTICULAR PURPOSE, OR THAT  USE OF THE SOFTWARE, MODIFICATIONS, OR
// ASSOCIATED DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS,
// TRADEMARKS OR OTHER INTELLECTUAL PROPERTY RIGHTS OF A THIRD PARTY.
//
// Princeton University shall not be liable under any circumstances for
// any direct, indirect, special, incidental, or consequential damages
// with respect to any claim by USER or any third party on account of
// or arising from the use, or inability to use, this software or its
// associated documentation, even if Princeton University has been advised
// of the possibility of those damages.
// *********************************************************************

#ifndef __SAT_SOLVER__
#define __SAT_SOLVER__

#include "zchaff_version.h"
#include "zchaff_dbase.h"

#ifndef _SAT_STATUS_
#define _SAT_STATUS_

enum MSAT_StatusT {
  UNDETERMINED,
  UNSATISFIABLE,
  SATISFIABLE,
  TIME_OUT,
  MEM_OUT,
  ABORTED
};
#endif

enum MSAT_DeductionT {
  CONFLICT,
  NO_CONFLICT
};

class MSolver;

typedef void(*HookFunPtrT)(void *) ;
typedef void(*OutsideConstraintHookPtrT)(MSolver * solver);
typedef bool(*SatHookPtrT)(MSolver * solver);

// **Struct********************************************************************
//
//  Synopsis    [Sat solver parameters ]
//
//  Description []
//
//  SeeAlso     []
//
// ****************************************************************************

struct MSolverParameters {
  float         time_limit;
  int           verbosity;

  struct {
    unsigned    size;
    int         enable;
    int         upper_bound;
    int         lower_bound;
    int         upper_delta;
    int         lower_delta;
    int         bound_update_frequency;
    unsigned    window_width;
  } shrinking;

  struct {
    int         base_randomness;
    int         bubble_init_step;
    int         decay_period;
  } decision;

  struct {
    bool        enable;
    unsigned    interval;
    unsigned    head_activity;
    unsigned    tail_activity;
    unsigned    head_num_lits;
    unsigned    tail_num_lits;
    int         tail_vs_head;
  } cls_deletion;

  struct {
    bool        enable;
    int         interval;
    int         first_restart;
    int         backtrack_incr;
  } restart;
};

// **Struct********************************************************************
//
//  Synopsis    [Sat solver statistics ]
//
// Description []
//
//  SeeAlso     []
//
// ****************************************************************************

struct MSolverStats {
  bool          been_reset;  // when delete clause in incremental solving,
                             // must reset.
  MSAT_StatusT   outcome;
  bool          is_mem_out;  // this flag will be set if memory out
  double        start_cpu_time;
  double        finish_cpu_time;
  int           total_cost;
  int           min_cost;
  int           current_randomness;
  int           next_restart;
  int           restart_incr;
  int           next_cls_deletion;
  int           next_var_score_decay;
  int           num_free_variables;
  int           num_free_branch_vars;
  long64        total_bubble_move;
  int           num_decisions;
  int           num_decisions_stack_conf;
  int           num_decisions_vsids;
  int           num_decisions_shrinking;
  int           num_shrinkings;
  int           shrinking_benefit;
  int           shrinking_cls_length;
  int           num_backtracks;
  int           max_dlevel;
  int           random_seed;
  long64        num_implications;
  int           num_restarts;
  int           num_del_orig_cls;
};

// **Class*********************************************************************
//
//  Synopsis    [Sat Solver]
//
//  Description [This class contains the process and datastructrues to solve
//               the Sat problem.]
//
//  SeeAlso     []
//
// ****************************************************************************

inline bool cmp_var_stat(const pair<MVariable *, int> & v1,
                         const pair<MVariable *, int> & v2) {
  return v1.second >= v2.second;
}

struct mcmp_var_assgn_pos {
  bool operator() (MVariable * v1, MVariable * v2) {
    if (v1->dlevel() > v2->dlevel())
      return true;
    else if (v1->dlevel() < v2->dlevel())
      return false;
    else if (v1->assgn_stack_pos() > v2->assgn_stack_pos())
      return true;
    return false;
  }
};

struct MImplication {
  int lit;
  int antecedent;
};

struct MImplicationQueue:queue<MImplication> {
  void dump(ostream & os) {
    queue<MImplication> temp(*this);
    os << "Implication Queue Previous: " ;
    while (!temp.empty()) {
      MImplication a = temp.front();
      os << "(" << ((a.lit & 0x1) ? "-" : "+") << (a.lit >> 1)
         << ":" << a.antecedent << ")  ";
      temp.pop();
    }
  }
};

class MSolver:public MDatabase {
  protected:
    int                 _id;                  // the id of the solver, in case
                                              // we need to distinguish
    bool                _force_terminate;
    MSolverParameters   _params;              // parameters for the solver
    MSolverStats        _stats;               // statistics and states

    int                 _dlevel;              // current decision elvel
    vector<vector<int>*>_assignment_stack;
    vector<int>         _mincost_assign;
    queue<int>          _recent_shrinkings;
    bool                _mark_increase_score;  // used in mark_vars during
                                              // multiple conflict analysis
    long64              _implication_id;
    MImplicationQueue    _implication_queue;

    // hook function run after certain number of decisions
    vector<pair<int, pair<HookFunPtrT, int> > > _hooks;
    OutsideConstraintHookPtrT                   _outside_constraint_hook;
    SatHookPtrT         _sat_hook;  // hook function run after a satisfiable
                                    // solution found, return true to continue
                              // solving and false to terminate as satisfiable

    // these are for decision making
    int                 _max_score_pos;   // index the unassigned var with
                                          // max score
    vector<pair<MVariable*, int> > _ordered_vars;  // pair's first pointing to
                                              // the var, second is the score.

    // these are for conflict analysis
    int               _num_marked;     // used when constructing learned clause
    int               _num_in_new_cl;  // used when constructing learned clause
    vector<MClauseIdx> _conflicts;      // the conflicting clauses
    vector<int>       _conflict_lits;  // used when constructing learned clause
    vector<int>       _resolvents;
    multimap<int, int> _shrinking_cls;
    vector<int>       MIS;
    set<int>       MIS_fl;
    vector<int>       fu;
    int               mis0;

  protected:
    void re_init_stats(void);
    void init_stats(void);
    void init_parameters(void);
    void init_solve(void);
    void real_solve(void);
    void restart(void);
    int preprocess(void);
    int deduce(void);
    int current_lower_bound(void);
    void find_MIS(void);
    int  MIS_LB(bool debug = false);
    void block_current_assignment(int diff, bool mis = true);
    void run_periodic_functions(void);

    // for decision making
    bool decide_next_branch(void);
    void decay_variable_score(void) ;
    void adjust_variable_order(int * lits, int n_lits);
    void update_var_score(void);
    // for conflict analysis
    int analyze_conflicts(void);
    MClauseIdx finish_add_conf_clause(int gflag);
    int conflict_analysis_firstUIP(void);
    void mark_vars(MClauseIdx cl, int var_idx);
    void back_track(int level);

    // for bcp
    void set_var_value(int var, int value, MClauseIdx ante, int dl);
    void set_var_value_BCP(int v, int value);
    void unset_var_value(int var);

    // misc functions
    bool time_out(void);
    void delete_unrelevant_clauses(void);
    MClauseIdx add_clause_with_gid(int * lits, int n_lits, int gid = 0);

  public:
    // constructors and destructors
    MSolver(void);
    ~MSolver(void);

    vector<int>         true_lits;
    int total_cost(void);
    int min_cost(void) {return _stats.min_cost;}
    bool find_another_sat(void);

    // member access function
    void set_time_limit(float t);
    void set_mem_limit(int s);
    void enable_cls_deletion(bool allow);
    void set_randomness(int n) ;
    void set_random_seed(int seed);

    void set_variable_number(int n);
    void set_variable_cost(int var_idx, int cost);
    int add_variable(void) ;
    void mark_var_branchable(int vid);
    void mark_var_unbranchable(int vid);

    inline int & dlevel(void) {
      return _dlevel;
    }

    inline int outcome(void) {
      return _stats.outcome;
    }

    inline int num_decisions(void) {
      return _stats.num_decisions;
    }

    inline int num_decisions_stack_conf(void) {
      return _stats.num_decisions_stack_conf;
    }

    inline int num_decisions_vsids(void) {
      return _stats.num_decisions_vsids;
    }

    inline int num_decisions_shrinking(void) {
      return _stats.num_decisions_shrinking;
    }

    inline int num_shrinkings(void) {
      return _stats.num_shrinkings;
    }

    inline int & num_free_variables(void) {
      return _stats.num_free_variables;
    }

    inline int max_dlevel(void) {
      return _stats.max_dlevel;
    }

    inline int random_seed(void) {
      return _stats.random_seed;
    }

    inline long64 num_implications(void) {
      return _stats.num_implications;
    }

    inline long64 total_bubble_move(void) {
      return _stats.total_bubble_move;
    }

    inline char * version(void) {
      return __ZCHAFF_VERSION__;
    }

    float elapsed_cpu_time(void);
    float cpu_run_time(void) ;
    int estimate_mem_usage(void) {
      return MDatabase::estimate_mem_usage();
    }

    int mem_usage(void);

    void queue_implication(int lit, MClauseIdx ante_clause) {
      //assert(lit != 114 || ante_clause != 8926);
      MImplication i;
      i.lit = lit;
      i.antecedent = ante_clause;
      _implication_queue.push(i);
    }

    // top level function
    inline int id(void) {
      return _id;
    }

    inline void set_id(int i) {
      _id = i;
    }

    inline void force_terminate(void) {
      _force_terminate = true;
    }

    inline void unset_force_terminate(void) {
      _force_terminate = false;
    }

    // for incremental SAT
    int add_clause_incr(int * lits, int n_lits, int gid = 0);

    void make_decision(int lit) {
        ++dlevel();
        queue_implication(lit, NULL_CLAUSE);
    }

    void add_hook(HookFunPtrT fun, int interval);

    inline void add_outside_constraint_hook(OutsideConstraintHookPtrT fun) {
      _outside_constraint_hook = fun;
    }

    inline void add_sat_hook(SatHookPtrT fun) {
      _sat_hook = fun;
    }

    void verify_integrity(void);
    void delete_clause_group(int gid);
    void reset(void);
    int solve(void);
    MClauseIdx add_orig_clause(int * lits, int n_lits, int gid = 0);
    MClauseIdx add_conflict_clause(int * lits, int n_lits, int gflag = 0);
    void clean_up_dbase(void);
    void dump_assignment_stack(ostream & os = cout);
    void dump_implication_queue(ostream & os = cout);

    void print_cls(ostream & os = cout);
    void dump(ostream & os = cout ) {
        MDatabase::dump(os);
        dump_assignment_stack(os);
    }
    void add_outside_clauses(void);
};
#endif
