/*********************************************************************
; Copyright 2004, Hantao Zhang (HZ).  All rights reserved. 
; By using this software the USER indicates that he or she has read, 
; understood and will comply with the following:
;
; --- HZ 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 HZ.  Title
; to copyright to this software and its associated documentation shall at
; all times remain with HZ.  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 HZ.  Software and/or its associated
; documentation identified as "confidential," if any, will be protected
; from unauthorized use/disclosure with the same degree of care USER
; regularly employs to safeguard its own such information.
;
; --- This software and any associated documentation is provided "as is," and
; HZ 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.  HZ, the University of Iowa,
; its Regents, officers, and employees 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 HZ has been advised
; of the possibility of those damages.
*********************************************************************/
#include "common.h"
#include "array.h"

int stop2_num=0;
int stop2info=0;
#include "sato.h"
#include "tape.h"
#include "vstack.h"
#include "select.h"
#include "satbox.h"

int satbox_lit_value (int i) { return lit_value(i); }
int satbox_var_value (int i) { return var_value(i); }
void satbox_set_var_value (int i, int s) { var_value(i) = s; } // use with caution
int satbox_var_level (int i) { return var_level(i); }

int satbox_delete_num() { return SX.Delete_num; }
int satbox_lemma_num() { return SX.Lemma_num; }
int satbox_branch_open() { return SX.Branch_open; }
int satbox_branch_jump() { return SX.Branch_jump; }
long satbox_branch_fail() { return SX.Branch_fail; }
int satbox_memory_count() { return SX.Memory_count; }

int satbox_conflict_cl_level() { return Conflict_cl_level; }
void satbox_set_conflict_cl_level(int lvl) { Conflict_cl_level = lvl; Backjumping_down = 0; }

void satbox_back_to (int level) {
  vstack_backup(level);
  Backjumping_down = 1;
}

int satbox_flag (i) int i; { return Flags[i]; }
void satbox_set_flag(i,v) int i, v; { Flags[i] = v; }

int flags_initialized=0;
void satbox_init_flags ()
{
  if (flags_initialized) return;
  flags_initialized=1;
  Flags[BINARY] = 0;
  Flags[CHOICE] = 0;
  Flags[DLENGTH] = 30;  /* Lemmas longer will be deleted */
  Flags[GROW] = 9;
  Flags[JUMP] = 1;
  Flags[NONHORN] = 0;
  Flags[TCLAUSE] = 0;
  Flags[RESTART] = 0;

#ifdef TEST
  Flags[TRACE] = -1;
#else
  Flags[TRACE] = 1;
#endif
}

void satbox_init_input (max_atom)
     int max_atom;
{
  int i;

  SX.Max_atom = max_atom;
  SX.Delete_num = SX.Lemma_num = SX.Branch_jump = 0;
  SX.Branch_fail = SX.Memory_count = 0;

  uqueue_create();

  tape_init();
  V5 = (struct var5 **) malloc (sizeof(struct var5 *)*(MAX_ATOM));
  memory_count((sizeof(struct var5))*(MAX_ATOM));

  L5 = (struct lit5 **) malloc (sizeof(struct lit5 *)*(MAX_ATOM<<1));
  memory_count((sizeof(int *)*4+sizeof(struct lit5))*(MAX_ATOM<<1));

  for (i=1; i < MAX_ATOM; i++)  {
    struct var5 *v;

    V5[i] = v = (struct var5 *) malloc(sizeof(struct var5));

    v->stamp = 0;
    v->timer = 0;
    v->level = 0;
    v->gmark = 0;
    v->value = DC;
  }
  
  for (i=2; i < (MAX_ATOM<<1); i++)  {
    int *arr;
    int **ptrs;
    struct lit5 *xl;

    L5[i] = xl = (struct lit5 *) malloc(sizeof(struct lit5));

    array_init(ptrs, int*);
    xl->hts = ptrs; 

    array_init(arr, int);
    xl->bicls = arr; 

#ifdef TLCLAUSE
    xl->ncls = NULL;
#endif

    xl->deleted = 0;
  }

  Empty_binary[2] = 0;
  Timer = 0;
  vstack_init(); 
  uqueue_init();
  Stamp = 1;
  Default_level = 1;
}

void satbox_insert_unit_clause (x)
     int x;
{
  int i = lit_var(x);
  var_level(i) = Default_level-1;
  var_gmark(i) = 0;
  assign_value0(x);
  vstack_push0(x);
  trace(6, printf(" input unit %s%d\n", (lit_sign(x))? "" : "-", i));
}

/* Return the status if variable is watched:
   0: unassigned and variable is not watched
   1: unassigned and watched by its negative literal
   2: unassigned and watched by its positive literal
   3: unassigned and watched by both positive and negative literals
   10: assigned false
   11: assigned true

   The difference between satbox_var_is_watched() and satbox_var_is_watched_neat()
   is that the latter doesn't count a literal as watched if the other watched literal
   in the same clause is true.
*/

int satbox_var_is_watched_neat ( int v )
{
  int **q, *p;
  int j, i=var_value(v);

  if (i == FF) return 10;
  else if (i == TT) return 11;
  
  i = 0;
  v = (v<<1);
  p = L5[v]->bicls;
  q = L5[v]->hts;

  for (j = array_count(p)-1; j >= array_begin(); j--) 
    if (var_value(lit_var(p[j])) == DC) { i = 1; break; }

  if (!i) for (j = array_count(q)-1; j >= array_begin(); j--) {
    int *cl = q[j];
    if (var_value(lit_var(get_head(cl))) != DC) continue;
    if (var_value(lit_var(get_tail(cl))) != DC) continue;
    i = 1; 
    break; 
  }

  v |= 1;
  p = L5[v]->bicls;
  q = L5[v]->hts;

  for (j = array_count(p)-1; j >= array_begin(); j--) 
    if (var_value(lit_var(p[j])) == DC) { i += 2; break; }

  if (i<2) for (j = array_count(q)-1; j >= array_begin(); j--) {
    int *cl = q[j];
    if (var_value(lit_var(get_head(cl))) != DC) continue;
    if (var_value(lit_var(get_tail(cl))) != DC) continue;
    i += 2; 
    break; 
  }

  return i;
}

int satbox_var_is_watched ( int v )
{
  int **q, *p;
  int i=var_value(v);

  if (i == FF) return 10;
  else if (i == TT) return 11;
  
  i = 0;
  v = (v<<1);
  p = L5[v]->bicls;
  q = L5[v]->hts;

  if (array_count(p)>array_begin()) i = 1;
  else if (array_count(q)>array_begin()) i = 1;

  v |= 1;
  p = L5[v]->bicls;
  q = L5[v]->hts;
  if (array_count(p)>array_begin()) i += 2;
  else if (array_count(q)>array_begin()) i += 2;

  return i;
}

#ifdef TLCLAUSE
struct nlink *get_nlink ()
{
  struct nlink *p = (struct nlink *) malloc(sizeof(struct nlink));
  memory_count(sizeof(struct nlink), "nlink");

  if (p == NULL) {
    fprintf(stderr, "get_nlink return NULL.\n");
    exit(1);
  }
  p->next = NULL;
  return(p);
} 
#endif

int insert_binary_clause (v1, v2)
     int v1, v2;
{
  int vs1[MAX_STACK];
  int vs2[MAX_STACK];
  int *bcs, i, nv2, num=2;

  vs1[0] = vs2[1] = v1;
  vs2[0] = vs1[1] = v2;

  while (num) {
    v1 = vs1[--num];
    v2 = vs2[num];
    nv2 = NEG(v2);

    check((v1 == v2), printf("OOOPS! "));

    bcs = L5[v1]->bicls;

    trace(9, printf(" binary ( %s%d %s%d )\n",
		    (lit_sign(v1))? "" : "-", lit_var(v1), 
		    (lit_sign(v2))? "" : "-", lit_var(v2)));

    /* check duplicates or unit clause */
    for (i = array_count(bcs)-1; i >= array_begin(); i--) {
      if (bcs[i] == v2) goto label;
      if (bcs[i] == nv2) {
	if (vstack_nonempty()) {
	  trace(3, printf(" Unit ( %s%d )\n", (lit_sign(v1))? "" : "-", lit_var(v1)));
	  New_unit = v1;
	  New_unit_clause = NULL;
	  Conflict_cl_level = Backjumping_down = Default_level; 
	  return 0;
	} else 
	  return insert_unit_cl_satbox(lit_var(v1), lit_sign(v1)); 
      }
    }

    array_insert(bcs,int,v2);
    // sblit_add_watched_satbox(v1);
    L5[v1]->bicls = bcs;

    if (Flags[BINARY]) {
      bcs = L5[NEG(v1)]->bicls;
      for (i = array_count(bcs)-1; i >= array_begin(); i--) {
	v1 = bcs[i];
	if (v1 == v2) {
	  if (vstack_nonempty()) {
	    trace(3, printf(" Unit ( %s%d )\n", lit_sign(v1)? "" : "-", lit_var(v1)));
	    New_unit = v2; 
	    New_unit_clause = NULL;
	    Conflict_cl_level = Backjumping_down = Default_level; 
	    return 0;
	  } else 
	    return insert_unit_cl_satbox(lit_var(v1), lit_sign(v1)); 
	} else if (v1 != nv2) {
	  vs1[num] = v1;
	  vs2[num++] = v2;
	  vs1[num] = v2;
	  vs2[num++] = v1;
	}
      }
    label: ;
    }
  }
  return 0;
}

void satbox_restart_search ()
{
  trace(2, printf("c Restarting...\n"));
  vstack_backup(0);
  uqueue_init();
  reset_all_clauses_stamps();
}

int satbox_init_search (output)
     int output[];
{ 
  int i=2;
  int s=0;

  adjust_tapes();

#ifdef AGE
  ages_init();
#endif

#ifdef FLEXIBLE
  for (i = 1; i <= SX.Max_atom; i++)
    if (var_value_flexible(i)) { printf("v%d is flexible\n", i); }
#endif

  if (handle_unit_clauses() == UNSAT) return UNSAT;

  i = 2;
  s = 0;
  while (s < vstack_size()) { output[i++] = vs_literal(vstack_of(s++)); }
  output[0] = Level;
  output[1] = i-1;
  remember_vs_size();
  return SAT;
}

void satbox_decide_true_simple ( int lit )
{
  int i = lit_var(lit);
  Timer++; 
  SX.Branch_open++;
  print_let_x_have(lit);
  assign_value2(lit);
  vstack_push1(lit);
  var_level(i) = Level;
  Conflict_cl_level = Level;
  check(var_gmark(i), printf("var_gmark(%d) != NULL\n", i));
}

int satbox_assert_aux (lit0, output, flag)
     int lit0, output[], flag;
{
  int s, i, j, lit=lit0;

  if (flag) {
    s = vstack_size();
    if (flag == 1) {
      i = lit_var(lit);
      check((var_value(i) != DC), 
	    { printf("satbox_assert_aux: X%d has value %d\n", i, var_value(i)); stop0();});
      Timer++; 
      SX.Branch_open++;
      print_let_x_have(lit);
      assign_value2(lit);
      vstack_push1(lit);
      var_level(i) = Level;
      if (Timer > MAX_INT) { Timer = 1; output[0] = RESTART_NOW; return 0; }
      Conflict_cl_level = Level;
      check(var_gmark(i), printf("var_gmark(%d) != NULL\n", i));
    } else if (flag > 0) {
      for (j = flag-3; j>=0; j--) {
	lit = output[j];
	i = lit_var(lit);
	if (var_value(i) != DC) continue; // duplicate literals
	assign_value2(lit);
	print_x_has_to(lit);
	vstack_push2(lit);
	var_level(i) = Level;
	var_gmark(i) = OUTSIDE;
      } 
    }
  } else /* flag == 0 */
  if (lit>0) {
    var5_fail_end(ptr_for_binary(lit));
    var5_prev_key();
    s = vstack_size();
  } else {
    Conflict_cl_level = (lit)? -lit : Level;
    while (1) {
      SX.Branch_fail++;
      uqueue_init();
      var5_prev_key();
      if (Conflict_cl_level >= Level) break;
    }
    Timer++;
    s = vstack_size();
  }

  i = UNSAT;
  while (New_unit || Second_try || uqueue_nonempty()) {

    if (handle_unit_clauses() == UNSAT) {
      i = UNSAT;
      var5_prev_key();
      s = vstack_size();
    } else {	
      i = SAT;
      break;
    }
  } /* while */

  if (i == UNSAT) return UNSAT;
  i = 2;
  while (s < vstack_size()) { output[i++] = vs_literal(vstack_of(s++)); }
  output[0] = Level;
  if ((i == 2) && (output[2] == lit0)) { 
    // only one known literal
    output[1] = 0; 
    return UNCHANGE; 
  }
  output[1] = i-1;
  return SAT;
}

int var5_prev_key ()
{
  trace(4, printf("a %d open, %ld failed, %d jumped (lvl=%d).\n", 
		  SX.Branch_open, SX.Branch_fail, SX.Branch_jump, 
		  Conflict_cl_level));

  if (!SX.Branch_open) return UNSAT;

  if (Flags[JUMP] && (Conflict_cl_level) < Level) {
    trace(4, printf("    Jump%d from level %d to %d\n", 
		    Backjumping_down, Level, Conflict_cl_level-1));
    SX.Branch_jump += Level-Conflict_cl_level;
    Second_try = vstack_backup(Conflict_cl_level);
    if (Backjumping_down) Second_try = 0;
  } else {
    Backjumping_down = 0;
    Second_try = vstack_backup(Level);
  }

#ifdef FLEXIBLE
  if (Conflict_cl) {
    i = lit_var(get_head(Conflict_cl));
    if (var_value_flexible(i)) { var_reset_value_flexible(i); }
    i = lit_var(get_tail(Conflict_cl));
    if (var_value_flexible(i)) { var_reset_value_flexible(i); }
    Conflict_cl = NULL;
  }
#endif

  trace(4, printf("b %d open, %ld failed, %d jumped\n", 
		  Level, SX.Branch_fail, SX.Branch_jump)); 
  return 0;
}

void var5_record_conflict (gcl)
     TAPE gcl;
{
  // If is_from_binary(gcl) then gcl is a false literal reported from outside of satbox.
  int num;
  int cl_arr[MAX_LENGTH];

  trace(3, if (gcl>0) { printf(" Empty cl: "); print_clause(gcl);});

  if ((num = var5_collect_parents(gcl, cl_arr)) > 0) {
    var5_insert_lemma(num, cl_arr, gcl);
  }
}

void var5_insert_lemma (num, cl_arr, cl)
     int num, cl_arr[];
     TAPE cl;
{
  int i, j;
  TAPE gcl = NULL;

  SX.Lemma_num++;

  /* Assume the clause is sorted by level: First literal is at maximal level */
  trace(3, { printf("NC [%d]: (", Level);
             for (i = 0; i < num; i++) {
	       j = cl_arr[i];
	       trace(3, printf(" %s%d (%d)", (lit_sign(j))? "" : "-", lit_var(j), var_level(lit_var(j))));
	     }
             printf(" )\n"); });

  if (model_read_satbox()) {
    int good=0;
    for (i = 0; i < num; i++) {
      j = cl_arr[i];
      if (model_value_satbox(lit_var(j)) != 1-lit_sign(j)) good=1;
    }

    if (!good) { 
      printf("false new\n"); stop0(); 
      if (satbox_flag(TRACE)>=8) exit(1);
      satbox_set_flag(TRACE,8);
      var5_record_conflict(cl);
      exit(1);
    }
  }

  if (num == 1) {
    New_unit_clause = NULL;
  } else if (num == 2) { 
    New_unit_clause = ptr_for_binary(lit_var(cl_arr[1]));
    weight_add1_satbox(cl_arr[0]); 
    weight_add1_satbox(cl_arr[1]); 
    insert_binary_clause(cl_arr[0], cl_arr[1]); 
  } else {

    if ((gcl = insert_lemma_to_tape(cl_arr, num))) {
      trace(5, { printf("NEW(%d): ", Level); print_clause(gcl);});
      Conflict_gcl = gcl; 
#ifdef AGE
      if (age) ages_insert_clause(age, gcl);
#endif
    } 
    New_unit_clause = gcl;
  }
}

int var5_collect_parents (gcl0, cl_arr)
     // If is_from_binary(gcl0) then cl is a false literal reported from outside of satbox.
     TAPE gcl0;
     int cl_arr[];
{
  int key, i, j, k, var_num=0, total=0;
  // int second_try=0;
  TAPE gcl=gcl0;
  int lit_num = 0;
  int lits[MAX_LENGTH];

  First_false_var = 0;
  if (++Stamp >= MAX_INT) Stamp = 1;

  if (is_from_binary(gcl)) {
    key = key_from_binary(gcl);
    k = dpllt_reason_satbox(key, lits);
    if (!k) return 0;
    key = lit_var(key);
    collect_one_key(key);
    for (j = 0; j < k; j++) {
      key = lit_var(lits[j]);
      collect_one_key(key);
    }
    //if (lit_num+total==0) return 0;

    if (var_num+total==0) {
      Backjumping_down = 1;
      Conflict_cl_level = Default_level;
      cl_arr[0] = New_unit = key_from_binary(gcl0);
      return 1;
    }

  } else 

#ifdef TLCLAUSE
  if (is_tl_cl(gcl)) {
    int *p = get_body(gcl);
    for (j = clause_offset(); j < size_of_clause(p); j++) {
      if (lit_value(p[j]) == TT) {
	k = p[j]; key = lit_var(k);
	collect_one_key(key);
      } 
    }
  } else 
#endif

    {
      k = get_head(gcl); key = lit_var(k); // weight_add1_satbox(k); 
      collect_one_key(key);
      k = get_tail(gcl); key = lit_var(k); // weight_add1_satbox(k); 
      collect_one_key(key);
      
      if (is_3cnf(gcl)) {
	k = third_lit(gcl); key = lit_var(k); // weight_add1_satbox(k); 
	collect_one_key(key);
      } else if (is_long_cl(gcl)) {
	int *p = get_body(gcl);
	for (j = lit0(); j < size_of_clause(p); j++) {
	  k = lp_get_lit(p[j]); key = lit_var(k); // weight_add1_satbox(k); 
	  collect_one_key(key);
	}
      }
#ifdef AGE
      if (age_of_cl(gcl) > age) age = age_of_cl(gcl);
#endif
    }

  k = vstack_size(); 
  while (k && var_num) {
    key = vs_var(vstack_of(--k));
    if (var_level(key) < Level) break;
    if (var_stamp(key) == Stamp) {
      trace(4, printf("unqueue %d\n", key));
      if (--var_num == 0 && First_false_var) { 
	cl_arr[lit_num++] = make_lit(key,NEG(var_value(key)));
	break;
      }

      if ((gcl=(int*)var_gmark(key))) {
	if ((var_gmark(key) == OUTSIDE)) {
 	  i = dpllt_reason_satbox(make_lit(key, var_value(key)), lits);
	  if (i) {
	    First_false_var = key;
	    for (j = 0; j < i; j++) {
	      key = lit_var(lits[j]);
	      collect_one_key(key);
	    }
	  } else {
	    cl_arr[lit_num++] = make_lit(key,NEG(var_value(key)));
	  }
	} else if (is_from_binary(gcl)) {
	  i = key_from_binary(gcl);
	  trace(4, printf("binary with %d\n", i));
	  collect_one_key(i);
	} else {
	  trace(4, print_clause(gcl));

#ifdef TLCLAUSE
	  if (is_tl_cl(gcl)) {
	    /* collect only true literals for LSEQ clauses */
	    int *p = get_body(gcl);
	    for (j = clause_offset(); j < size_of_clause(p); j++) if (lit_value(p[j]) == TT) { 
	      i = lit_var(p[j]);
	      collect_one_key(i);
	    }
	  }
	  else
#endif
          {
#ifdef AGE
	    if (age_of_cl(gcl) > age) age = age_of_cl(gcl);
#endif
	    i = lit_var(get_head(gcl)); collect_one_key(i);
	    i = lit_var(get_tail(gcl)); collect_one_key(i);
	    if (is_3cnf(gcl)) {
	      i = lit_var(third_lit(gcl)); collect_one_key(i);
	    } else if (is_long_cl(gcl)) {
	      int *p = get_body(gcl);
	      for (j = lit0(); j < size_of_clause(p); j++) {
		i = lp_get_var(p[j]);
		collect_one_key(i);
	      }
	    }
	  }
	}
	First_false_var = key;

	// weight_add1_satbox(vs_literal(vstack_of(k)));
	if (!var_num) { break; }
      } else { 
	cl_arr[lit_num++] = make_lit(key,NEG(var_value(key)));
      }
    }
  }
 
  if (var_num) {
    trace(1, printf("no new lemma %d %ld\n", var_num, SX.Branch_fail));
    stop0();
    return 0; // ignore this new clause

    if (Flags[TRACE] == 9) { stop1(); exit(0); } 
    Flags[TRACE]=9; 
    printf("failed %d %ld\n", var_num, SX.Branch_fail); 
    vstack_print(); 
    var5_record_conflict(gcl0);
  }

  if (total + lit_num > MAX_LENGTH) {
    trace(4, printf("new clause too big\n")); 
    return 0; 
  }

  /* find the max level of clause */
  Conflict_cl_level = Level;
  if (lit_num > 1) {
    Backjumping_down = 0;
  } else {
    Backjumping_down = 1;
    New_unit = cl_arr[0];
  }

  /* fix the sign of literals in the new clause */
  while ((k > 0) && total) {
    key = vs_var(vstack_of(--k));
    if (var_stamp(key) == Stamp) {
      cl_arr[lit_num++] = make_lit(key, NEG(var_value(key)));
      total--;
    } 
  }

  if (Backjumping_down) {
    if (lit_num == 1) Conflict_cl_level = Default_level;
    else {
      Conflict_cl_level = var_level(lit_var(cl_arr[1]))+1;
      if (Default_level > Conflict_cl_level) 
	Conflict_cl_level = Default_level;
      trace(4,  printf("Level new %d, old %d\n", Conflict_cl_level, Level));
    } 
  }

  if (total) {
    trace(1, printf("Learning Failed %d %ld\n", total, SX.Branch_fail)); 
    return 0;

    check(total, 
	{ if (Flags[TRACE] == 9) { stop1(); exit(0); } Flags[TRACE]=9; 
	vstack_print(); 
	var5_record_conflict(gcl0); exit(0);}); 
  }

#ifdef AGE
  cl_arr[lit_num] = age;
#endif

  return lit_num; 
}

#ifdef FLEXIBLE

int grow_propagate (ks0)
     int ks0;
{
  /* more than 95% of total time is consumed by this function for 
     many hard examples */
  int **pts = L5[ks0]->hts;
  int *p = L5[ks0]->bicls;
  int i, j, ks, num, endcl;
  int head, tail_lit, head_pos, *cl;
  int last_flex, flex_idx, tail_flex;

  /* propagate binary clauses first */
  for (j = array_count(p)-1; j >= array_begin(); j--) {
    ks = NEG(p[j]);
    i = lit_var(ks);
    trace(7, printf("%d => %s%s%d\n", 
	   lit_var(ks0), var_value_flexible(i)? "?" : "", 
	   (lit_sign(ks))? "" : "-", i,
	   (var_value(i) == TT)? "+" : ((var_value(i) == FF)? "-" : ""))); 

    if (var_value_flexible(i)) {
      var_reset_value_flexible0(i);
      var_value(i) = DC;
    }
    if (var_value(i) == DC) {
      var_gmark(i) = ptr_for_binary(lit_var(ks0));
      var_level(i) = Level;
      trace(6, printf("%d: unit cl %s%d at %d from (%s%d %s%d)\n", Timer,
		      (lit_sign(ks))? "" : "-", i, Level,
		      (lit_sign(ks))? "" : "-", i,  
		      (lit_sign(ks0))? "-" : "", lit_var(ks0)));
      print_x_has_to(ks);
      assign_value2(ks);
      vstack_push0(ks);
    } else if (lit_value(ks)) { 
      Empty_binary[0] = ks0;
      Empty_binary[1] = p[j];
      return var5_fail_end(Empty_binary);
    }
  }

  /* propagate long clauses */
  j = array_begin();
  num = array_count(pts);
  trace(7, printf("(%d, %d) has %d clauses\n", (ks0>>1), ks0&1, num-j));

  while (j < num) {
    cl = pts[j];
    if (!undeleted_clause(cl)) { /* if the clause is deleted */
      pts[j] = pts[--num];
      continue;
    } else if (is_head(cl,ks0)) { /* p is a real head */
      /* head moves from left to right and tail moves from right to left */
      head = 0;
      tail_lit = get_tail(cl);
    } else { /* p is actually tail */
      head = 1;
      tail_lit = get_head(cl);
    }

    tail_flex = last_flex = 0;
    /* check tail first */
    if (var_value_flexible(lit_var(tail_lit))) {
      tail_flex = tail_lit;
    } else if (i=lit_value(tail_lit)) {
      if (i == TT) {
	j++;
	continue; /* tail_lit is true */
      }
    } else {
      /* tail_lit is false */
      tail_lit = 0;  
    }

    if (is_3cnf(cl)) { /* clause length == 3 */
      /* check the third literal */
      ks = third_lit(cl);

      if (tail_flex && var_value_flexible(lit_var(ks))) {
	/* one of them must be not flexible */
	var_reset_value_flexible(lit_var(tail_flex));
	tail_lit = tail_flex;
	tail_flex = 0;
      }

      if (lit_value(ks) || var_value_flexible(lit_var(ks))) {
	/* ks is either true or unassigned or flexible */
	/* switch third_lit(cl) with cl[head] */
	switch_head_3cnf(cl,head,ks);
	pts[j] = pts[--num];
	insert_head_tail(cl,ks);
	continue;

	/* Now third_lit(cl) is false */
      } else if (tail_lit || tail_flex) {
	if (tail_flex) {
	  i = lit_var(tail_flex);
	  if (lit_value(tail_flex)) {
	    tail_flex = NEG(tail_flex);
	    var_reset_value_flexible0(i);
	    var_gmark(i) = (int) cl;
	    var_level(i) = Level;
	    vstack_push_only(tail_flex);
	    trace(6, {
 	     printf("%d: Unit cl %s%d at %d from ", (lit_sign(tail_flex))? "" : "-", Timer, i, Level);
 	     print_clause(cl);});
	    print_x_confirm_to(tail_flex);
	    j++;
	    continue;
	  }
	  var_reset_value_flexible(i);
	  tail_lit = tail_flex;
	}
	/* a unit clause is found */
	ks = NEG(tail_lit);
	i = lit_var(ks);
	report_unit_lit(ks,i,cl);
	j++;
	continue;
      } else {
	array_set_count(pts,int*,num);
	return var5_fail_end(cl);
      }

    } else if (is_long_cl(cl)) {
      int key;

      /* work on long clause */
      p = get_body(cl);
      head_pos = i = get_head_pos(p);
      endcl = size_of_clause(p);

      while (1) {
	/* printf("i=%d, h=%d, e=%d\n", i, head, endcl); */

	/* p[i] is a literal */
	ks = p[i];
	key = lit_var(ks);

	if (tail_flex) {
	  if (var_value_flexible(key)) {
	    last_flex = ks; flex_idx = i;
	  } else if (lit_value(ks)) {
	    /* p[i] is either true or unassigned */
	    remove_head_cl();
	    break;
	  }
	} else if (lit_value(ks) || var_value_flexible(lit_var(ks))) {
	  /* p[i] is either true or unassigned or flexible */
	  remove_head_cl();
	  break;
	}
	if (++i == endcl) i = lit0(); /* beginning */
	if (i != head_pos) continue;

	/* Now the whole clause has been scanned. */
	if (tail_flex) {
	  if (last_flex) {
	    /* one of them must be not flexible */
	    var_reset_value_flexible(lit_var(tail_flex));
	    i = flex_idx; ks = last_flex;
	    remove_head_cl();
	    break;
	  } else {
	    i = lit_var(tail_flex);
	    if (lit_value(tail_flex)) {
	      tail_flex = NEG(tail_flex);
	      var_reset_value_flexible0(i);
	      var_gmark(i) = (int) cl;
	      var_level(i) = Level;
	      vstack_push_only(tail_flex);
	      trace(6, {
		printf("%d: Unit cl %s%d at %d from ", Timer, (lit_sign(tail_flex))? "" : "-", i, Level);
		print_clause(cl);});
	      print_x_confirm_to(tail_flex);
	      j++;
	      break;
	    }
	    var_reset_value_flexible(i);
	    tail_lit = tail_flex;
	  }
	}

	if (tail_lit) {
	  /* a unit clause is found */
	  ks = NEG(tail_lit);
	  key = lit_var(ks);
	  report_unit_lit(ks,key,cl);
	  j++;
	  break; /* look for next clause  */
	} else {
	  array_set_count(pts,int*,num);
	  return var5_fail_end(cl);
	} 
      }
    } else {
      printf("c strange: %d %d\n", get_head(cl), get_tail(cl));
      j++;
    }
  }
  array_set_count(pts,int*,num);

#ifdef TLCLAUSE
  if (Flags[TCLAUSE]) {
    /* these for the new types of clauses with numbers */
    struct nlink *nl = L5[ks0]->ncls;
    /*printf("Q%d ", ks0);*/
    while (nl) {
      cl = nl->cl;
      if ((dec_cvalue(cl) <= 0) &&
	  double_check_nclause(cl) == UNSAT) {
	return var5_fail_end(cl);
      }
      nl = nl->next;
    }
  }
#endif

  return SAT;
}

#else

int grow_propagate (ks0)
     int ks0;
{
  /* more than 95% of total time is consumed by this function for hard examples */
  int **pts;
  int *p;
  int i, j, ks, num, endcl, key, nks0;
  int head, tail_lit, last_pos, *cl;

  nks0 = NEG(ks0);
  pts = L5[nks0]->hts;
  p = L5[nks0]->bicls;

  /* propagate binary clauses first */
  for (j = array_count(p)-1; j >= array_begin(); j--) {
    ks = p[j]; 
    i = lit_var(ks);
    if (var_value(i) == DC) { /* a unit clause is found */
      var_gmark(i) = (int) ptr_for_binary(lit_var(ks0));
      var_level(i) = Level;
      trace(6, printf("unit cl %s%d at %d from (%s%d %s%d)\n", 
		      (lit_sign(ks))? "" : "-", i, Level,
		      (lit_sign(ks))? "" : "-", i,  
		      (lit_sign(ks0))? "-" : "", lit_var(ks0)));
      print_x_has_to(ks);
      uqueue_add(ks); 
      vstack_push0(ks);
      assign_value2(ks);
    } else if (!lit_value(ks)) {
      Empty_binary[0] = nks0;
      Empty_binary[1] = ks;
      return var5_fail_end(Empty_binary);
    }
  }

  /* propagate long clauses */
  j = array_begin();
  num = array_count(pts);
  while (j < num) {
    cl = pts[j];

    if (!undeleted_clause(cl)) { /* if the clause is deleted */
      pts[j] = pts[--num];
      continue;
    } else if (is_head(cl,nks0)) { 
      head = 0;
      tail_lit = get_tail(cl);
    } else { 
      head = 1;
      tail_lit = get_head(cl);
    }

    /* check tail first */
    if ((i=lit_value(tail_lit))) {
      if (i == TT) {
	j++;
	continue; /* cl[tail] is true */
      }
    } else {
      tail_lit = 0;  /* cl[tail] is false */
    }

    if (is_3cnf(cl)) { /* clause length == 3 */
      /* check the third literal */
      ks = third_lit(cl);
      if (lit_value(ks)) {
	/* ks is either true or unassigned */
	/* switch third_lit(cl) with cl[head] */
	switch_head_3cnf(cl,head,ks);
	pts[j] = pts[--num];
	insert_head_tail(cl,ks);
 	// if (lit_value(ks) == DC) sblit_add_watched_satbox(ks);
	continue;

	/* Now third_lit(cl) is false */
      } else if (tail_lit) {
	/* a unit clause is found */
	ks = tail_lit;
	i = lit_var(ks);
	report_unit_lit(ks,i,cl);
	j++;
	continue;
      } else {
	array_set_count(pts,int*,num);
	return var5_fail_end(cl);
      }
    } else if (is_2cnf(cl)) { 
      if (tail_lit) {
	/* a unit clause is found */
	ks = tail_lit;
	i = lit_var(ks);
	report_unit_lit(ks,i,cl);
	j++;
	continue;
      } else {
	array_set_count(pts,int*,num);
	return var5_fail_end(cl);
      }
    } else {
      /* work on long clause */
      p = get_body(cl);
     
      last_pos = get_head_pos(p);
      endcl = size_of_clause(p);

      if (last_pos < endcl) {
	/* calibration */
	ks = get_body_stamp(p);

	while (last_pos && last_pos < endcl) {
	  if (lit_timer(lp_get_lit(p[last_pos])) > ks) {
	    last_pos = lp_get_position(p[last_pos]);
	  } else { break; }
	}
	if (!last_pos) last_pos = endcl;
      }

      for (i = last_pos-1; i >= lit0(); i--) {
	/* printf("i=%d, h=%d, e=%d\n", i, head, endcl); */

	/* ks is a literal */
	ks = lp_get_lit(p[i]);
	if (lit_value(ks)) {
	  /* ks is either true or unassigned */
	  pts[j] = pts[--num];

	  trace(6, {
	    printf("change head-tail from %d to %d at pos %d in ", 
		   lit_var(cl[head]), lit_var(ks), i);
	    print_clause(cl);});

	  switch_head_body(cl,head,p,i,ks,last_pos);
	  set_head_pos_tim(p,i);
	  insert_head_tail(cl,ks);

	  trace(6, print_clause(cl)); 
	    /* print_var5_clause(lit_var(ks), 3); */
	  i = 0;
	  break;
	}
      }

      if (i) {
	/* Now the whole clause has been scanned. */
	if (tail_lit) {
	  /* a unit clause is found */
	  /* set_head_pos_tim(p,lit0());*/
	  ks = tail_lit;
          key = lit_var(ks);
	  report_unit_lit(ks,key,cl);
	  j++;
	  continue; /* look for next clause  */
	} else {
	  array_set_count(pts,int*,num);
	  return var5_fail_end(cl);
	} 
      }

#ifdef HANTAO
      i = last_pos;
      if (last_pos > lit0()) {
	if (last_pos < endcl) {
	  last_lit = lp_get_lit(p[last_pos]);
	  last_pos = lp_get_position(p[last_pos]);
	  last_tim = lit_timer(last_lit);
	} else {
	  last_lit = 0;
	}

	while (--i >= lit0()) {

	  /* ks is a literal */
	  ks = lp_get_lit(p[i]);
	  if (lit_value(ks)) {
	    /* ks is either true or unassigned */
	    pts[j] = pts[--num];

	    trace(6, {
	      printf("change head-tail from %d to %d at pos %d in ", 
		     lit_var(cl[head]), lit_var(ks), i);
	      print_clause(cl);});

	    switch_head_body(cl,head,p,i,ks,last_pos);
	    set_head_pos_tim(p,i);
	    if (last_lit) { lp_set_lp(p[i+1], last_lit, last_pos); }
	    insert_head_tail(cl,ks);

	    trace(6, print_clause(cl)); 
	    /* print_var5_clause(lit_var(ks), 3); */

	    i = 0;
	    break;
	  }

	  if (!last_lit) {
	    last_lit = ks;
	    last_tim = lit_timer(ks);
	  } else if (last_tim < lit_timer(ks)) {
	    last_tim = lit_timer(ks);
	    lp_set_lp(p[i+1], last_lit, last_pos);
	    last_lit = ks;
	    last_pos = i+1;
	  } else {
	    lp_set_lp(p[i+1], ks, last_pos);
	  }
	}
      } else { last_lit = 0; }

      if (i) {
	if (last_lit) { 
	  lp_set_lp(p[i+1], last_lit, last_pos);
	  set_head_pos(p, i+1);
	}

	/* Now the whole clause has been scanned. */
	if (tail_lit) {
	  /* a unit clause is found */
	  set_head_pos_tim(p, lit0());
	  ks = NEG(tail_lit);
          key = lit_var(ks);
	  report_unit_lit(ks,key,cl);
	  j++;
	  continue; /* look for next clause  */
	} else {
	  array_set_count(pts,int*,num);
	  return var5_fail_end(cl);
	} 
      }
#endif
    }
  }
  array_set_count(pts,int*,num);

#ifdef TLCLAUSE
  if (Flags[TCLAUSE]) {
    /* these for the new types of clauses with numbers */
    struct nlink *nl = L5[ks0]->ncls;

    while (nl) {
      cl = nl->cl;
      if ((dec_cvalue(cl) <= 0) &&
	  double_check_nclause(cl) == UNSAT) {
	return var5_fail_end(cl);
      }
      nl = nl->next;
    }
  }
#endif

  return SAT;
}
#endif

#ifdef TLCLAUSE

int double_check_nclause (cl)
     TAPE cl;
{
  int tt = 0;
  int value = value_of(cl);
  int i, j, v, nc, s, ks;

  for (j = lit0(); j < size_of_clause(cl); j++) 
    if ((i=lit_value(cl[j])) >= DC) nc++;
    else if (i) tt++;

  tt = value-tt;
  /* printf("nc=%d, tt=%d ", nc, tt); print_clause(cl);*/
  if (tt > 0) { set_ncl_val(cl,tt); return SAT; }

  /* LSEQ only */
  if (tt < 0) { set_ncl_val(cl,0); return UNSAT; }

  /* tt = 0, all the remaining literals should be false */
  for (j = lit0(); nc && j < size_of_clause(cl); j++) {
    ks=cl[j];
    i = lit_var(ks);
    if (var_value(i) == DC) {
      var_gmark(i) = (int) cl;
      var_level(i) = Level;
      trace(6, {
	printf("set x%d = %d at %d by LSEQ: ", i, lit_sign(ks), Level);
	print_clause(cl);
      });
      print_x_has_to(ks);
      vstack_push0(ks);
      assign_value0(ks);
      nc--;
    }
  }
  return SAT;
}
#endif

int handle_unit_clauses ()
     /* handle each unit clause one by one ... */
{
  int i, s;

  /* see if there is a unit clause from lemma */
  // check_new_unit();
  if (New_unit) { 
      s = New_unit; 
      New_unit = 0;
      i = lit_var(s);
      if (var_value(i) == DC) { 
      var_level((i)) = Level; 
      var_gmark((i)) = (int) New_unit_clause; 
      trace(6, { printf("%d: unit x%d at level %d by ", Timer, i, Level); 
                 if (New_unit_clause) print_clause(New_unit_clause); else printf("\n");}); 
      print_x_has_to(s); 
      assign_value2(s); 
      vstack_push2(s);
    } else if (var_value(i) != lit_sign(s)) { 
      return var5_fail_end(New_unit_clause);
    }
  }

  /* now handle clauses */
  while (uqueue_nonempty()) {
    uqueue_pop(s);
    i = lit_var(s);
    if (!lit_value(s)) {
      trace(3, printf("    x%d = %d is a conflict\n", i, lit_sign(s)));
      if (var5_fail_end(NULL) == UNSAT) return UNSAT;
    } else if (grow_propagate(s) == UNSAT) return UNSAT;
  }

  if (Second_try) {
    s = NEG(Second_try);
    i = lit_var(s);
    Second_try = 0;
#ifdef FLEXIBLE
    if (var_value_flexible(i)) {
      var_reset_value_flexible(i);
#else
    if (lit_value(s) == DC) {
#endif
      var_level(lit_var(s)) = Level;
      print_now_let_x_have(s);
      assign_value2(s);
      vstack_push2(s);
      return handle_unit_clauses();
    }
    else vstack_set_second(i);
  }

  return SAT;
}

int check_true_atoms (num)
     int num;
{
  int i, j=0;

  for (i = 1; i <= SX.Max_atom; i++) if (var_value(i) == TT) j++;
  if (num>0 && j != num) {
    printf("Warning: true variables = %d != %d\n", j, num);
    stop1();
    return -1;
  }
  return j;
}

int var5_fail_end (cl)
     TAPE cl;
{
  // If is_from_binary(cl) then cl is a false literal reported from outside of satbox.
  Conflict_cl = (is_from_binary(cl))? NULL : cl;
  Conflict_cl_level = Level;
  New_unit = Second_try = 0;
  uqueue_init();

  trace(3, if (!(is_from_binary(cl))) { printf(" empty cl %d %d: ", Level, Timer); 
                                        print_clause(cl);});
  

  if (Flags[GROW] && cl && SX.Branch_open) {
    int num;
    int cl_arr[MAX_LENGTH];

    if ((num = var5_collect_parents(cl, cl_arr)) > 0) {
      var5_insert_lemma(num, cl_arr, cl);
    }
  }
  
  SX.Branch_fail++;
  Timer++;
  return UNSAT;
}

void var5_assign (i, sign)
     int i, sign;
{
  var_level(i) = Level;
  i = make_lit(i,sign);
  print_x_has_to(i);
  vstack_push0(i);
  assign_value2(i);
}


void print_var5_clause (i, flag)
     int i, flag;
{
  int j, m;
  int **hts = L5[make_lit(i,0)]->hts;

  if ((flag&1) &&(m=array_count(hts))>array_begin()) {
    printf("/******* Positive clauses for variable %d.*******/\n",i);
    for (j = array_begin(); j < m; j++) print_clause(hts[j]);
  }

  hts = L5[make_lit(i,1)]->hts;
  if ((flag>1) &&(m=array_count(hts))>array_begin()) {
    printf("/******* Negative clauses for variable %d.*******/\n",i);
    for (j = array_begin(); j < m; j++) print_clause(hts[j]);
  }
}

#ifdef TLCLAUSE
void print_var5_nclause (i, flag)
     int i, flag;
{
  int j, l;
  struct nlink *gl;

  if (L5[make_lit(i,1)]->ncls && (flag & 1)) {
    printf("/******* Positive clauses for variable %d.*******/\n",i);
    gl = L5[make_lit(i,1)]->ncls;
    while (gl) {
      print_clause(gl->cl);
      gl = gl->next;
    }
  }
  
  if (L5[make_lit(i,0)]->ncls && flag > 1) {
    printf("/******* Negative clauses for variable %d.*******/\n",i);
    gl = L5[make_lit(i,0)]->ncls;
    while (gl) {
      print_clause(gl->cl);
      gl = gl->next;
    }
  }
}
#endif

int variable_value (v) int v; { return var_value(v); }

int binsat_scc1 ()
{
  int i, k; 
  if (++Stamp >= MAX_INT) Stamp = 1;  
  Timer = 0;
  trace(5, printf("Pre-2-SAT\n"));
  for (k = 1; k <= SX.Max_atom; k++) {
    i = make_lit(k,Flags[CHOICE]);
    if (var_value(k) == DC && unassigned_lit10(i)) {
      lit_stamp(i) = Stamp;
      trace(5, printf("  Pre-2-SAT:  %s%d be 1\n", 
		      (lit_sign(i))? "-" : "", lit_var(i)));
      if (binary_scc(i^1) == UNSAT) {
	trace(5, printf("End Pre-2-SAT\n"));
	return UNSAT;
      }
    }
  }
  trace(5, printf("End Pre-2-SAT\n"));
  return SAT;
}

int binary_scc (ks0)
     int ks0;
{
  int *p;
  int j, ks;

  Timer++;
  dfs_time(ks0) = dfs_lowlink(ks0) = Timer;

  p = L5[ks0]->bicls;

  for (j = array_count(p)-1; j >= array_begin(); j--) {
    if (lit_value(ks0) != DC) return SAT;
    if (lit_value(ks=p[j]) == DC) { 
      if (unassigned_lit10(ks)) {
	/* a unit clause is found */
	trace(6, printf("  Pre-2-SAT:  %s%d from (%s%d %s%d)\n", 
			(lit_sign(ks))? "-" : "", lit_var(ks), 
			(lit_sign(ks))? "-" : "", lit_var(ks), 
			(lit_sign(ks0))? "-" : "", lit_var(ks0)));
	lit_stamp(ks) = Stamp;
	if (binary_scc(ks^1) == UNSAT) return UNSAT;
	if (dfs_lowlink(ks0) > dfs_lowlink(ks)) dfs_lowlink(ks0) = dfs_lowlink(ks);
      } else if (assigned_false10(ks)) {
	/* a conflict and ks has to be true */
	uqueue_init();
	var_level(ks>>1) = Level;
	trace(2, printf("  Pre-2-SAT:  %s%d be 1 by (%s%d %s%d)\n", 
			(lit_sign(ks))? "-" : "", lit_var(ks), 
			(lit_sign(ks))? "-" : "", lit_var(ks), 
			(lit_sign(ks0))? "-" : "", lit_var(ks0)));
	ks = ks^1;
	vstack_push0(ks);
	assign_value2(ks);
	if (handle_unit_clauses() == UNSAT) return UNSAT;
      } else if (dfs_done(ks) != Stamp) {
	if (dfs_lowlink(ks0) > dfs_time(ks)) dfs_lowlink(ks0) = dfs_time(ks);
      }
    }
  }

  if (lit_value(ks0) == DC) {
    if (dfs_lowlink(ks0) != dfs_time(ks0)) { 
      trace(5, printf("push %d\n", ks0)); 
    } else {
      trace(5, printf("root %d\n", ks0));
      dfs_done(ks0) = Stamp;
    }
  }

  return SAT;
}

int stop1 () { return 1; }
int stop0 () { return 1; }
