/* Variable weights for selecting literals */

#define weight_set(ks) LD5[ks]->weight = LD5[ks]->weight_new; LD5[ks]->weight_new = 0
#define weight_add(ks,x) LD5[ks]->weight_new += x
#define weight_add1(ks)  LD5[ks]->weight_new++
#define weight_new_val(ks) LD5[ks]->weight_new
#define weight_of_lit(ks) LD5[ks]->weight
#define weight_of_var(k) VD5[k]->weight
#define var_order(i) VD5[i]->order

int var_order_satbox(i) int i; { return var_order(i); }

int var_weight_satbox(i) int i; { return weight_of_var(i); }
void set_var_weight_satbox(i,w) int i,w; { weight_of_var(i) = w; }
void add_var_weight_satbox(i,w) int i,w; { weight_of_var(i) += w; }

int lit_weight_satbox(i) int i; { return weight_of_lit(i); }
void set_lit_weight_satbox(i,w) int i,w; { weight_of_lit(i) = w; }
void add_lit_weight_satbox(i,w) int i,w; { weight_of_lit(i) += w; }

int lit_weight_new_satbox(i) int i; { return weight_new_val(i); }
void set_lit_weight_new_satbox(i,w) int i,w; { weight_new_val(i) = w; }
void add_lit_weight_new_satbox(i,w) int i,w; { weight_add(i, w); }
#ifndef SATBOX_LIBRARY_ONLY
void weight_add1_satbox(ks) int ks; { weight_add1(ks); }
#endif

/* weight_of_var(i) = max{weight_of_lit(x0), weight_of_lit(x1)} */
#define weight_update0()\
  if (((Branch_num) & 511)==1) {\
    for (i = 1; i <= Max_atom; i++) {\
      int x0 = make_lit(i,0);\
      int x1 = make_lit(i,1);\
      LD5[x0]->weight = (LD5[x0]->weight >> 1) + LD5[x0]->weight_new;\
      LD5[x1]->weight = (LD5[x1]->weight >> 1) + LD5[x1]->weight_new;\
      LD5[x0]->weight_new = LD5[x1]->weight_new = 0;\
      if (LD5[x0]->weight >= LD5[x1]->weight)\
          { weight_of_var(i) = LD5[x0]->weight; }\
      else \
          { weight_of_var(i) = LD5[x1]->weight; }\
    }\
    if (SELECT > 3) { mergesort_var_order();  VarOrderFirst = 0;} \
  }

/* weight_of_var(i) = weight_of_lit(x0)+weight_of_lit(x1) */
#define weight_update1()\
  if ((Branch_num & 511) == 1) {\
    for (i = 1; i <= Max_atom; i++) {\
      int x0 = make_lit(i,0);\
      int x1 = make_lit(i,1); \
      LD5[x0]->weight = (LD5[x0]->weight >> 1) + LD5[x0]->weight_new;\
      LD5[x1]->weight = (LD5[x1]->weight >> 1) + LD5[x1]->weight_new;\
      LD5[x0]->weight_new = LD5[x1]->weight_new = 0;\
      weight_of_var(i) = LD5[x0]->weight+LD5[x1]->weight;\
    }\
    if (SELECT > 3) { mergesort_var_order();  VarOrderFirst = 0;} \
  }

/* weight_of_var(i) = second-order(weight_of_lit(x0), weight_of_lit(x1)) */
#define weight_update7() if (!(Branch_fail & 15)) weight_update7_aux()

void weight_update7_aux ()
{
  int i;

  for (i = 1; i <= Max_atom; i++) {
    int x0 = make_lit(i,0);
    int x1 = make_lit(i,1);
    if (satbox_flag(GROW)) {
      LD5[x0]->weight = (LD5[x0]->weight >> 1) + LD5[x0]->weight_new;
      LD5[x1]->weight = (LD5[x1]->weight >> 1) + LD5[x1]->weight_new;
      LD5[x0]->weight_new = LD5[x1]->weight_new = 0;
    }
    if (satbox_var_value(i) == DC) {
      int w0 = second_weight(x0);
      int w1 = second_weight(x1);
      if (CHOICE2) 
	weight_of_var(i) = w0+w1;
      else if (w0 > w1) 
	weight_of_var(i) = w0;
      else weight_of_var(i) = w1;
      printf("W2[%d] = %d\n", i, weight_of_var(i));
    }
  }
  mergesort_var_order();  
  VarOrderFirst = 0;
}

void mergesort_var_order ()
{
  int i, s=1;
  
  /*
  FILE *f;
  f = fopen("array", "a");
  fprintf(f, "%d", VarOrderSize);
  for (i = 0; i < VarOrderSize; i++) {
    fprintf(f, " %d %d", VarOrder[i], weight_of_var(VarOrder[i]));
  }
  fprintf(f, "\n");
  fclose(f);
  */

  while (s < VarOrderSize) {
    s = s<<1;
    for (i = 0; i < VarOrderSize; i += s) {
      mergesorted(i, s);
    }
  }

  /*
  if (not_sorted(0, VarOrderSize)) { printf("not sorted\n"); }
  else printf("sorted\n");
  */

  VarOrderFirst = 0;
  for (i = 0; i < VarOrderSize; i++) {
    var_order(VarOrder[i]) = i;    
    /* printf("W%d = %d %d\n", VarOrder[i], LD5[VarOrder[i]<<1]->weight,
       LD5[(VarOrder[i]<<1)+1]->weight); */
  }
}

int B[MAX_SHORT];

void mergesorted (p, s)
     int p, s;
{
  int r, x, y;

  if (s == 2) {
    r = p+1;
    if (r >= VarOrderSize) return;
    x = VarOrder[p];
    y = VarOrder[r];
    if (weight_of_var(x) < weight_of_var(y)) {
      VarOrder[p] = y;
      VarOrder[r] = x;
    }
  } else {
    int q = p+(s>>1);
    int i;
    r = p+s;

    if (r >= VarOrderSize) {
      r = VarOrderSize; 
      if (r <= q) return;
    }

    x = VarOrder[--q]; y = VarOrder[--r];
    while (q < r && weight_of_var(x) >= weight_of_var(y)) { y = VarOrder[--r]; }
    if (q < r) {
      s = 0; i = q+1;
      while (i < r) B[s++] = VarOrder[i++];
      while (1) {
	if (weight_of_var(x) < weight_of_var(y)) {
	  VarOrder[r--] = x;
	  if (q == p) { B[s++] = y; break; }
	  x = VarOrder[--q];
	} else {
	  VarOrder[r--] = y;
	  if (s == 0) break;
	  y = B[--s];
	}
      }
      while (s) VarOrder[r--] = B[--s];
    }
  }
}

#ifdef NONDISCARD
mergesorted (p, s)
     int p, s;
{
  int r = p+s;
  int x, y;

  /*printf("%d, %d\n", p, r);*/

  if (s == 2) {
    if (r > VarOrderSize) return 0;
    x = VarOrder[p];
    y = VarOrder[r=p+1];
    if (weight_of_var(x) < weight_of_var(y)) {
      VarOrder[r] = x;
      VarOrder[p] = y;
    }
  } else {
    int m = p+(s>>1);
    int q = 0, i, j;

    if (m >= VarOrderSize) return 0;
    if (r > VarOrderSize) r = VarOrderSize; 

    x = VarOrder[p]; y = VarOrder[m];
    while (weight_of_var(x) >= weight_of_var(y) && p < m) { x = VarOrder[++p]; }

    i = p; j = m;
    while (i < m && j < r) {
      if (weight_of_var(x) < weight_of_var(y)) {
	A[q++] = y;
	y = VarOrder[++j];
      } else {
	A[q++] = x;
	x = VarOrder[++i];
      }
    }
    while (i < m) A[q++] = VarOrder[i++];
    for (i = 0; i < q; i++) VarOrder[p+i] = A[i];
  }
}

not_sorted (p, r)
     int p, r;
{
  int i;
  for (i = p+1; i < r; i++) if (weight_of_var(VarOrder[i-1]) < weight_of_var(VarOrder[i])) {
    printf("W%d = %d, W%d = %d\n", 
	   VarOrder[i-1], weight_of_var(VarOrder[i-1]),
	   VarOrder[i], weight_of_var(VarOrder[i]));
    return 1;
  }
  return 0;
}

bubblesort_var_order ()
{
  int i0=0, i, bubble=1, k=VarOrderSize-1, x, y;

  /* bubble sort */
  while (bubble < VarOrderSize) {
    bubble = VarOrderSize;
    x = VarOrder[i0];
    for (i = i0; i < k; i++) {
      y = VarOrder[i+1];
      if (weight_of_var(x) < weight_of_var(y)) {
	if (bubble > i) { bubble = i; }
	VarOrder[i] = y;
	VarOrder[i+1] = x;
      } else {
	x = y;
      }
    }
    k--;
    i0 = (bubble)? bubble-1 : bubble;
  }

  VarOrderFirst = 0;
  for (i = 0; i < VarOrderSize; i++) {
    var_order(VarOrder[i]) = i;    
    /* printf("W(x%d) = %d\n", VarOrder[i], weight_of_var(VarOrder[i]));*/
  }
}

quicksort (p, r)
     int p, r;
{
  int q = partition(p, r);
  /* printf("[%d %d]", p, r); */
  if (p < q) quicksort(p, q);
  if (q+1 < r) quicksort(q+1, r);
}

partition (p, r)
     int p, r;
{
  int i = p;
  int j = r;
  int x = weight_of_var(VarOrder[(p+r)>>1]);

  while (1) {
    while (weight_of_var(VarOrder[i]) > x) i++;
    while (weight_of_var(VarOrder[j]) < x) j--;
    if (i < j) {
      int y = VarOrder[i];
      VarOrder[i++] = VarOrder[j];
      VarOrder[j--] = y;
    } else
      return j;
  }
}
#endif

#define var5_best_key(i)\
  if (SELECT < 2) i=weight_strategy_satbox(); \
  else if (SELECT >= 8) i=basic_strategy_satbox(); \
  else i=satbox_strategy(SELECT) 

int basic_strategy_satbox ()
     /* the least unsigned variable */
{
  int i; 

  for (i = 1; i <= Max_atom; i++) if (satbox_var_value(i) == DC) {
    return make_lit(i, satbox_flag(CHOICE));
  }
  return 0;
}

int weight_strategy_satbox ()
{
  int i,k;

  for (i = VarOrderFirst; i < VarOrderSize; i++) {
    k = VarOrder[i];
    if (satbox_var_value(k) == DC) {
      VarOrderFirst = i+1;
      return (weight_of_lit(make_lit(k,0)) > weight_of_lit(make_lit(k,1)))? 
	make_lit(k,0) : make_lit(k,1);
    } 
  }
  for (i = 0; i < VarOrderSize; i++) {
    k = VarOrder[i];
    if (satbox_var_value(k) == DC) {
      VarOrderFirst = i+1;
      return (weight_of_lit(make_lit(k,0)) > weight_of_lit(make_lit(k,1)))? 
	make_lit(k,0) : make_lit(k,1);
    } 
  } 
  return 0;
}

void record_keeping ()
{
  int h, t, b, p;

  b = (100*Bicl_num)/Clause_num;
  t = (100*Trcl_num)/Clause_num;
  h = (1000*NH_num)/Clause_num;
  p = (1000*POS_num)/Clause_num;

  /* adjust splitting strategies */
  if (SELECT == 0) {
    if (satbox_flag(TCLAUSE) && b < 50) 
      SELECT = 2;
    else {
      SELECT = 4; satbox_set_flag(CHOICE, NEG(satbox_flag(CHOICE)));
    }

    /* if NH < 5% select 2. */    
    if (p && (h < 50 || p < 20)) {
      SELECT = 2; satbox_set_flag(CHOICE, NEG(satbox_flag(CHOICE)));
#ifndef FLEXIBLE
    } else if (t > 95 && Max_atom*4 <= Clause_num && Clause_num*2 <= Max_atom*9) { 
      SELECT = 6; 
#endif
    } else if (b > 75) { 
      SELECT = 10; 
    } else if (b > 5 && b < 10 && t+b > 95) {
      SELECT = 10; 
    }
  }

  trace(1, {
    printf("c Bin = %d, %d ", Bicl_num, b);
    printf("NH = %d, %2.1f ", NH_num, h/10.0);
    printf("Pos = %d, %2.1f ", POS_num, p/10.0);
    printf("Tri = %d, %d S = %d data\n", Trcl_num, t, SELECT);
  });
}

#define print_let_x_have(k) \
   trace(4, printf("[%d]  let x%d = %d.\n", satbox_branch_open(), lit_var(k), lit_sign(k)))

#define print_x_pure(k) \
   trace(4, printf("    x%d is set to %d by pure-literal deletion.\n", lit_var(k), lit_sign(k)))

void print_weight ()
{
  int i;
  for (i = 1; i <= Max_atom; i++) {
    printf("V%d: %d %d\n", i, weight_of_lit(make_lit(i,0)), weight_of_lit(make_lit(i,1)));
  }
}

int collect_best_keys (best_keys, max)
     int best_keys[], max;
{
  int i, k, x=0, num=0;

  if (max > 100) max = 100;

  for (i = VarOrderFirst; i < VarOrderSize; i++) {
    k = VarOrder[i];
    if (satbox_var_value(k) == DC) {
      if (!num) VarOrderFirst = i;
      best_keys[num++] = k;
      if (num >= max) break;
    }
  }

  if (!num) {
    for (i = 0; i < VarOrderSize; i++) {
      k = VarOrder[i];
      if (satbox_var_value(k) == DC) {
	if (!x) x = VarOrderFirst = i;
	best_keys[num++] = k;
	if (num >= max) break;
      }
    } 
  }
  return num;
}
