/*
This file is part of the dpll(t) engine sammy
Copyright (C) 2005  The University of Iowa
George Hagen

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// SATBOX interface satbox_in.cpp

/* Contains: 
 * pred_id translation
 * All outgoing function calls
 * incoming function call: weight_add1_satbox
 *
 */

// note: also try map/hash_map to see which is faster in practice

//

#define MAXPREDS 130000
#define CLAUSE_TYPE 0
#define UNSAT 0
#define GROW 3
#define LEMMA_MEMORY 20

// define CVCL_THEORY at the command line compile if using CVCL

// Standard Template Library
#include <vector>
#include <compat_hash_map.h>

// IO
//#include <fstream>
#include <iostream>
#include <string>

// OCAML Interfacing
#include <caml/mlvalues.h>
//#include <caml/memory.h>
#include <caml/callback.h>

// SATBOX interface
extern "C" {		// since this is a C library
  #include <satbox.h>
}

// Theory Interface
#include <theory_w.h>

//#define DEBUG

//#define SBX_OUTPUT

using namespace std;

static int temparray[MAXPREDS+2];
static int returnarray[MAXPREDS+2];

static hash_map <int,unsigned int> sam2sat; //SAMMY ID -> SATBOX ID
static hash_map <unsigned int,int> sat2sam; //SATBOX ID -> SAMMY ID

static hash_map <unsigned int,int> sat2weight; //SATBOX ID -> weight
typedef hash_map <unsigned int,int>::iterator sat2weightIter; 

static hash_map <unsigned int,vector<unsigned int> > reason_list; //SATBOX ID -> reasons

//static string filename;

/************************************************************************/
// translate from integer id representation to satbox id representation
unsigned int translate_sam_id_to_sat (int sam_lit_id) {
  int sign = 0;
  if (sam_lit_id > 0)
    sign = 1;
  return (abs(sam_lit_id)*2+sign);
}

/************************************************************************/
// translate from satbox id representation to integer id representation
int translate_sat_id_to_sam (unsigned int sat_lit_id) {
  int sign = -1;
  if (sat_lit_id & 1)
    sign = 1;
  return (sign*(sat_lit_id/2));
}

/************************************************************************/
// CALLED BY SATBOX
// increment a literal's weight value
extern "C" void weight_add1_satbox (int lit_id) {

#ifdef DEBUG
//  cout << "SBI.weight_add1_satbox"<<endl;
//  cout.flush();
#endif

  if (sat2weight.find(lit_id) != sat2weight.end()) {
    sat2weight[lit_id] = sat2weight[lit_id] + 1;
  } else {
    sat2weight[lit_id] = 1;
  }
#ifdef DEBUG
//  cout << "SBI.weight_add1_satbox "<< lit_id <<":" << sat2weight[lit_id]  <<endl;
//  cout.flush();
#endif
}


/************************************************************************/
// CALLED BY SATBOX
// return a literal's reasons
extern "C" int dpllt_reason_satbox (int lit, int *reasons) {

#ifdef DEBUG
  cout << "SBI.dpllt_reason_satbox "<<lit<<endl;
  cout.flush();
#endif

  int length = 0;


#ifdef SBX_OUTPUT
  cout << "rea " << lit ;
  cout.flush();
#endif

#ifdef CVCL_THEORY
  length = cvcGetImplicants (lit, reasons);
#else
  int id = POS_ID(lit);
  if (reason_list.find(lit) == reason_list.end()) {
#ifdef DEBUG
  cout << "SBI.dpllt_reason_satbox "<<lit<<" has no reasons!"<<endl;
  cout.flush();
#endif
      reasons[0] = 0;
  }

  vector<unsigned int> vec = reason_list[id];
  length = vec.size();
  for (int index = 0; index < length; index++) {
    reasons[index] = vec[index];
  }
#endif
#ifdef SBX_OUTPUT
  cout << " " << length;
  for (int index = 0; index < length; index++) {
    cout << " " << reasons[index];
  }
  cout << endl;
  cout.flush();
#endif

  return length;
}

/************************************************************************/
// increases the weight of a literal
extern "C" void sbi_increase_activity (value vl, value vi) {

#ifdef DEBUG
//  cout << "SBI.increase_activity"<<endl;
//  cout.flush();
#endif

  unsigned int lit_id = (Int_val (vl));
  int i = (Int_val (vi));
  if (sat2weight.find(lit_id) != sat2weight.end()) {
    sat2weight[lit_id] = sat2weight[lit_id] + i;
  } else {
    sat2weight[lit_id] = i;
  }
}

/************************************************************************/
// sets up the reasons for a literal
// must be called as soon as possible
// this only stores the positive form of an ID
extern "C" void sbi_set_assertion_reasons (value v, value r_vals) {

#ifndef CVCL_THEORY
  unsigned int lit = POS_ID(Int_val (v));

#ifdef DEBUG
  cout << "SBI.set_assertion_reasons "<< lit <<":";
  cout.flush();
#endif

  int length = Wosize_val (r_vals);

  if (reason_list.find(lit) == reason_list.end()) {
    vector<unsigned int> vec;
    reason_list[lit] = vec;
  }

  reason_list[lit].clear();

  for (int index = 0; index < length; index++) {
    int i = (Int_val (Field(r_vals,index)));
#ifdef DEBUG
  cout << " "<< i ;
  cout.flush();
#endif
    reason_list[lit].push_back(i);
  }
#ifdef DEBUG
  cout << endl;
  cout.flush();
#endif
#endif

}

/************************************************************************/
// decay all literal wieghts
extern "C" void sbi_weight_decay () {

#ifdef DEBUG
  cout << "SBI.weight_decay start "<<endl;
  cout.flush();
#endif

  for (sat2weightIter i = sat2weight.begin(); 
       i != sat2weight.end(); 
       i++) {
    i->second /= 2;
  }
  
#ifdef DEBUG
  cout << "SBI.weight_decay end "<<endl;
  cout.flush();
#endif
}

/************************************************************************/
// return the weight of a literal
extern "C" value sbi_get_literal_weight (value v) {
  int id = Int_val (v);

#ifdef DEBUG
//  cout << "SBI.get_literal_weight " << id << endl;
//  cout.flush();
#endif
  int weight = 0;
  if (sat2weight.find(id) != sat2weight.end()) {
    weight = sat2weight[id];
  } else {
    sat2weight[id] = 0;
  }

#ifdef DEBUG
//  cout << "SBI.get_literal_weight " << id << ":" << weight <<endl;
//  cout.flush();
#endif

  return (Val_int (weight));
}

/************************************************************************/
// Initialize satbox (should be called once)
extern "C" void sbi_satbox_init (/*value fn*/) {

//  filename = String_val(fn);
  

#ifdef DEBUG
  cout << "SBI.satbox_init start "<<endl;
  cout.flush();
#endif

#ifdef SBX_OUTPUT
  cout << "ini " << MAXPREDS << endl;
  cout.flush();
#endif

  satbox_init(MAXPREDS);


//  satbox_set_flag(GROW, LEMMA_MEMORY);

//#ifdef SBX_OUTPUT
//  cout << "flg " << GROW << " " << LEMMA_MEMORY << endl;
//  cout.flush();
//#endif

// set max lemmas in tape.h

#ifdef DEBUG
  cout << "SBI.satbox_init end "<<endl;
  cout.flush();
#endif
}

/************************************************************************/
// add a non-removable clause to the satbox
// this assumes there are no duplicate literals
extern "C" void sbi_insert_clause (value v_vals) {

#ifdef DEBUG
  cout << "SBI.insert_clause start "<<endl;
  cout.flush();
#endif

  // construct the array

  int length = Wosize_val (v_vals);

#ifdef SBX_OUTPUT
  if (length > 1) {
    cout << "cla " << length;
  }
  else {
    cout << "uni ";
  }
  cout.flush();
#endif

  for (int index = 0; index < length; index++) {
    int id = (Int_val (Field(v_vals,index)));
#ifdef DEBUG
  cout << " " << id << " ";
  cout.flush();
#endif
#ifdef SBX_OUTPUT
  cout << " " << id;
  cout.flush();
#endif
    if (id/2 > MAXPREDS) {printf("id %d exceeded satbox_in.cpp MAXPREDS\n",id);exit(1);}
    returnarray[index] = id;
  }

#ifdef SBX_OUTPUT
  cout << endl;
  cout.flush();
#endif

#ifdef DEBUG
  cout << " end ";
  cout.flush();
#endif

  if (length == 1) {
    satbox_insert_unit_clause (returnarray[0]);
  } else {
    satbox_insert_clause (returnarray, length, CLAUSE_TYPE);
  }

#ifdef DEBUG
  cout << endl << "SBI.insert_clause end "<<endl;
  cout.flush();
#endif

}

    
/************************************************************************/
// adds a rectractable clause to the satbox
// returns 0 if the added clause causes the clause set to be unsat
extern "C" value sbi_insert_user_lemma(value v_vals)
{

#ifdef DEBUG
  cout << "SBI.insert_user_lemma start "<<endl;
  cout.flush();
#endif

  // construct the array

  int length = Wosize_val (v_vals);


#ifdef SBX_OUTPUT
  cout << "lem "<< length;
  cout.flush();
#endif

  for (int index = 0; index < length; index++) {
    int id = (Int_val (Field(v_vals,index)));
#ifdef DEBUG
  cout << " " << id << " ";
  cout.flush();
#endif
#ifdef SBX_OUTPUT
  cout << " " << id;
  cout.flush();
#endif
    temparray[index] = id;
  }
#ifdef SBX_OUTPUT
  cout << endl;
  cout.flush();
#endif

 int sat = satbox_insert_lemma (temparray, length, returnarray);

#ifdef DEBUG
  cout << "SBI.insert_user_lemma end "<<endl;
  cout.flush();
#endif

  return (Val_int(sat));
}


/************************************************************************/
// retracts the last n user lemmas added, but does not otherwise change
// the state
extern "C" value sbi_remove_last_user_lemma (value v_val) {
  int n = Int_val(v_val);
#ifdef DEBUG
  cout << "SBI.remove_last_user_lemma start "<<endl;
  cout.flush();
#endif
#ifdef SBX_OUTPUT
  cout << "rem " << n << endl;
  cout.flush();
#endif
  int r = satbox_remove_last_lemmas (n);

  return (Val_int(r));
}

/************************************************************************/
// performs an initial BCP step on the current set of clauses (prior to 
// assertions)
extern "C" value sbi_initial_bcp () {

#ifdef DEBUG
  cout << "SBI.initial_bcp start "<<endl;
  cout.flush();
#endif

  // set up array
  returnarray[0] = 0;
  returnarray[1] = MAXPREDS;

#ifdef SBX_OUTPUT
  cout << "sea" << endl;
  cout.flush();
#endif

  int sat = satbox_init_search( returnarray );

  return (Val_int(sat));
}

/************************************************************************/
extern "C" void sbi_restart_search () {

#ifdef DEBUG
  cout << "SBI.restart_search start "<<endl;
  cout.flush();
#endif


#ifdef SBX_OUTPUT
  cout << "rse" << endl;
  cout.flush();
#endif

  satbox_restart_search();
}



/************************************************************************/
// get the level from the satbox output array
extern "C" value sbi_get_level () {

#ifdef DEBUG
  cout << "SBI.get_level start "<<endl;
  cout.flush();
#endif

  int num = returnarray[0];

#ifdef DEBUG
  cout << "SBI.get_level: "<< num << endl;
  cout.flush();
#endif

  return (Val_int (num));
}

/************************************************************************/
// get the number of literals from the satbox output array
extern "C" value sbi_get_number_of_forced_lits () {

#ifdef DEBUG
  cout << "SBI.get_number_of_forced_lits start "<<endl;
  cout.flush();
#endif

  // NOTE: This appears to be an error in the SATBOX!
  int num = returnarray[1] - 1;

#ifdef DEBUG
  cout << "SBI.get_number_of_forced_literals: "<< num << endl;
  cout.flush();
#endif

  return (Val_int (num));
}

/************************************************************************/
// iterator to get a literal from the satbox output array, starting at 0
extern "C" value sbi_get_forced_literal_n (value v) {

  int n = (Int_val (v));
#ifdef DEBUG
  cout << "SBI.get_forced_literal_n #"<< n << endl;
  cout.flush();
#endif
  unsigned int id = returnarray[n+2];
#ifdef DEBUG
  cout << "SBI.get_forced_literal_n: "<< id << endl;
  cout.flush();
#endif
  
  return (Val_int (id));
}


/************************************************************************/
// sets output array
extern "C" value sbi_backtrack (value v) {

  unsigned int lit = (Int_val (v));
#ifdef DEBUG
  cout << "SBI.backtrack start "<< lit << endl;
  cout.flush();
#endif

  int return_val;

#ifdef SBX_OUTPUT
  cout << "bak " << lit << endl;
  cout.flush();
#endif

  return_val = satbox_backtrack(lit, returnarray);

#ifdef DEBUG
  cout << "SBI.backtrack end "<< return_val <<endl;
  cout.flush();
#endif

  return (Val_int (return_val));

}

/************************************************************************/
// sets output array
extern "C" value sbi_assert_literals (value v_vals ) {

#ifdef DEBUG
  cout << "SBI.assert_literal start ";
  cout.flush();
#endif



  int length = Wosize_val (v_vals);
#ifdef SBX_OUTPUT
  cout << "ast " << length;
  cout.flush();
#endif
  for (int index = 0; index < length; index++) {
    unsigned int tmp =  (unsigned int)(Int_val (Field(v_vals,index)));
#ifdef DEBUG
  cout << tmp <<" ";
  cout.flush();
#endif
#ifdef SBX_OUTPUT
  cout << " " << tmp;
  cout.flush();
#endif
    returnarray[index] = tmp;
  }

#ifdef SBX_OUTPUT
  cout << endl;
  cout.flush();
#endif

  int return_val = satbox_assert_true(length, returnarray);
#ifdef DEBUG
  cout << " DONE"<<endl;
  cout.flush();
#endif
  return (Val_int (return_val));
}

/************************************************************************/
// sets output array
extern "C" value sbi_decide_literal (value v) {

#ifdef DEBUG
  cout << "SBI.decide_literal start "<<endl;
  cout.flush();
#endif

  unsigned int lit = (Int_val (v));

#ifdef SBX_OUTPUT
  cout << "dec " << lit << endl;
  cout.flush();
#endif

  int return_val = satbox_decide_true(lit, returnarray);
#ifdef DEBUG
  cout << "SBI.decide_literal end "<<lit<<":"<<return_val<<endl;
  cout.flush();
#endif
  return (Val_int (return_val));
}

/************************************************************************/
extern "C" value sbi_lit_value (value v) {
  unsigned int lit = Int_val(v);
#ifdef SBX_OUTPUT
  cout << "lva " << lit << endl;
  cout.flush();
#endif
  return (Val_int (satbox_lit_value(lit)));
}

/************************************************************************/
// the var value is lit_value >> 1
extern "C" value sbi_var_value (value v) {
  unsigned int var = Int_val(v);
#ifdef SBX_OUTPUT
  cout << "vva " << var << endl;
  cout.flush();
#endif
  return (Val_int (satbox_var_value(var)));
}

/************************************************************************/
// return 0 if there is a partial model
extern "C" value sbi_satbox_check_model () {
#ifdef SBX_OUTPUT
  cout << "ckm" << endl;
  cout.flush();
#endif
  return (Val_int(satbox_check_model()));
}

/************************************************************************/
// jumps back to level X without forcing the opposite decision
extern "C" void sbi_back_to(value v) {
  int level = Int_val(v);

#ifdef SBX_OUTPUT
  cout << "bkt " << level << endl;
  cout.flush();
#endif
  satbox_back_to(level);
}

/************************************************************************/
// return the level of a literal
extern "C" value sbi_var_level(value v) {
  int var = Int_val(v);
#ifdef SBX_OUTPUT
  cout << "vlv " << var << endl;
  cout.flush();
#endif
  return (Val_int (satbox_var_level(var)));
}

/************************************************************************/
// returns 1 if the variable is watched
extern "C" value sbi_var_is_watched(value v) {
  int var = Int_val(v);
#ifdef SBX_OUTPUT
  cout << "wat " << var << endl;
  cout.flush();
#endif
  return (Val_int (satbox_var_is_watched_neat(var)));
}

/************************************************************************/
extern "C" void sbi_print_stats () {
  printf ("SATBOX (deleted):          %d\n",satbox_delete_num());
  printf ("SATBOX (lemmas):           %d\n",satbox_lemma_num());
  printf ("SATBOX (branches open):    %d\n",satbox_branch_open());
  printf ("SATBOX (branches jumped):  %d\n",satbox_branch_jump());
  printf ("SATBOX (branches failed):  %ld\n",satbox_branch_fail());
//  printf ("SATBOX (branches skipped): %d\n",satbox_branch_skip());
  printf ("SATBOX (memory count):     %d\n",satbox_memory_count());
  fflush(stdout);
}


