/* Functions for literal-selection strategies */

int min_weight_var (cl)
     TAPE cl;
{
  int i, j, best_key=0, best_w=0;

  if (is_normal(cl)) {
    if (var_value(lit_var(get_head(cl))) == DC) { 
      best_key = lit_var(get_head(cl)); 
      best_w = var_weight_satbox(best_key); 
    }
    if (var_value(lit_var(get_tail(cl))) == DC) {
      i = var_weight_satbox(lit_var(get_tail(cl)));
      if (i > best_w) { 
	best_key = lit_var(get_tail(cl));  best_w = i; 
      }
    }
    if (is_3cnf(cl)) {
      j = third_lit(cl);
      if (var_value(lit_var(j)) == DC) {
	if (((i = var_weight_satbox(lit_var(j)))) > best_w) { 
	  best_key = lit_var(j);  best_w = i;
	}
      }
    } else if (is_long_cl(cl)) {
      int w, *p = get_body(cl);
      for (i = lit0(); i < size_of_clause(p); i++) {
	j = lit_var(lp_get_lit(p[i]));
	if (var_value(j) == DC && (w = var_weight_satbox(j)) > best_w) {
	  best_key = j; best_w = w;
	}
      }
    }
  } else {
    int w, *p = get_body(cl);
    for (i = clause_offset(); i < size_of_clause(p); i++) {
      j = lit_var(p[i]);
      if (var_value(j) == DC && (w = var_weight_satbox(j)) > best_w) {
	best_key = j; best_w = w;
      }
    }
  }
  if (best_key) return make_lit(best_key,FF);
  return 0;
}

int first_key (cl)
     TAPE cl;
{
  if (is_normal(cl)) {
    if (var_value(lit_var(get_head(cl))) == DC) { return NEG(get_head(cl)); }
  } else {
    int i, j, *p = get_body(cl);
    for (i = clause_offset(); i < size_of_clause(p); i++) {
      j = lp_get_lit(p[i]);
      if (var_value(lit_var(j)) == DC) return j;
    }
  }
  return 0;
}

int *shortest_clause () 
{
  int *cl, *best_cl=NULL;
  int x, i, j;
  int min = MAX_INT;

  for (x = 0; x < Num_nonhorns; x++) {
    cl = Nonhorns[x];
    if (is_normal(cl) && 
	((lit_value(get_head(cl)) == TT) || (lit_value(get_tail(cl)) == TT)))
      continue;

    if (is_normal(cl)) {
      j = 2;
      if (is_3cnf(cl)) {
	if (lit_value(third_lit(cl)) == FF) return cl;
	if (lit_value(third_lit(cl)) == TT || ++j >= min) { continue; }
      } else if (is_long_cl(cl)) {
	int *p = get_body(cl);
	for (i = lit0(); i < size_of_clause(p); i++) {
	  int s = var_value(lit_var(lp_get_lit(p[i])));
	  if (s >= DC) {
	    if (++j >= min) {  continue; }
	  } else if (s) {
	    continue;
	  }
	}
      }
    } else {
      if ((j=(cvalue(cl)+1)) >=min) continue;
    }

    if (j) {
      best_cl = cl;
      if ((min=j) <= 2) break; 
    }
  }
  /* if (best_cl) { printf("best cl "); print_clause(best_cl);  }*/
  return best_cl;
}

int collect_best_keys_9 (best_keys, max)
     int best_keys[], max;
{
  int i, j, x=0, num=0;
  int *cl, *cl2;

  if (max > 100) max = 100;

  for (i = make_lit(SX.Max_atom,1); i > 1; i--) { 
    set_lit_weight_satbox(i, 0);
    set_lit_weight_new_satbox(i, 0);
  }

  /* scan the input clauses to compute lit_weight_satbox */
  for (i = 0; i <= Input_tape; i++) {
    cl = Tape_space[i];
    cl2 = cl+Tape_limit[i];
    while (cl < cl2) {
      /* work only for 3-SAT */
      if (is_3cnf(cl) &&
	  var_value(lit_var(get_head(cl))) == DC &&
	  var_value(lit_var(get_tail(cl))) == DC) {
	if (var_value(lit_var(third_lit(cl))) == DC) {
	  add_lit_weight_satbox(get_head(cl), 1);
	  add_lit_weight_satbox(get_tail(cl), 1);
	  add_lit_weight_satbox(third_lit(cl), 1);
	} else if (lit_value(third_lit(cl)) == FF) {
	  add_lit_weight_satbox(get_head(cl), 4);
	  add_lit_weight_satbox(get_tail(cl), 4);
	}
      }
      cl += clause_head_size();
    }
  }

  /* scan the input clauses to compute lit_weight */
  for (i = 0; i <= Input_tape; i++) {
    cl = Tape_space[i];
    cl2 = cl+Tape_limit[i];
    while (cl < cl2) {
      /* work only for 3-SAT */
      if (is_3cnf(cl) &&
	  var_value(lit_var(get_head(cl))) == DC &&
	  var_value(lit_var(get_tail(cl))) == DC &&
	  var_value(lit_var(third_lit(cl))) == DC) {
	add_lit_weight_new_satbox(get_head(cl), 
				  lit_weight_satbox(get_tail(cl)^1)*lit_weight_satbox(third_lit(cl)^1));
	add_lit_weight_new_satbox(get_tail(cl),
				  lit_weight_satbox(third_lit(cl)^1)*lit_weight_satbox(get_head(cl)^1));
	add_lit_weight_new_satbox(third_lit(cl),
				  lit_weight_satbox(get_head(cl)^1)*lit_weight_satbox(get_tail(cl)^1));
      }
      cl += clause_head_size();
    }
  }

  /* use lit_weight to select best keys */
  for (i = 1; i <= SX.Max_atom; i++) if (var_value(i) == DC) 
    set_var_weight_satbox(i, lit_weight_new_satbox(make_lit(i,0))*lit_weight_new_satbox(make_lit(i,1)));

  for (i = 1; i <= SX.Max_atom; i++) if (var_value(i) == DC) {
    if (num < max) best_keys[num++] = i;
    else {
      j = x;
      while (1) {
	if (var_weight_satbox(i) > var_weight_satbox(best_keys[j])) {
	  best_keys[x=j] = i; break;
	} else {
	  if (++j == max) j = 0;
	  if (j == x) break;
	}
      }
    }
  }
  return num;
}

int satbox_strategy ( int i )
{
  switch (i) {
  case 2: return sato5_strategy2(); 
  case 3: return sato5_strategy3(); 
  case 4: return sato5_strategy4(); 
  case 5: return sato5_strategy5(); 
  case 6: return sato5_strategy6(); 
  case 7: return sato5_strategy7(); 
  }
  return 0;
}

int sato5_strategy2 ()
     /* the heaviest unsigned variable in a shortest non-horn clause */
{
  static TAPE best_cl;
  static int prev_level;
  int i;

  if (!vstack_nonempty()) { prev_level = Level; }
  if ((prev_level < Level) && best_cl && (i = first_key(best_cl))) return i;
  prev_level = Level;
  if ((best_cl = shortest_clause())) {  return first_key(best_cl);  }
  return basic_strategy_satbox();
}

int sato5_strategy3 ()
     /* strategy2 plus weight */
{
  static TAPE best_cl;
  static int prev_level;
  int i;

  if (!vstack_nonempty()) { prev_level = Level; }
  if ((prev_level < Level) && best_cl && (i = min_weight_var(best_cl))) return i;
  prev_level = Level;
  if ((best_cl = shortest_clause())) return min_weight_var(best_cl); 
  return weight_strategy_satbox();
}

int sato5_strategy4 ()
     /* Use weight, look at Conflict_gcl first */
{
  if (Conflict_gcl && is_normal(Conflict_gcl) && undeleted_clause(Conflict_gcl)) {
    if (var_value(lit_var(get_head(Conflict_gcl))) == DC) return get_head(Conflict_gcl);
    if (var_value(lit_var(get_tail(Conflict_gcl))) == DC) return get_tail(Conflict_gcl);
  } 
  return weight_strategy_satbox();
}

int sato5_strategy5 ()
     /* Use weight, look at Head_tape first */
{
  int *cl, *cl2;

  cl = Tape_space[Head_tape];
  cl2 = cl+Tape_limit[Head_tape];

  while (cl < cl2) {
    if (undeleted_clause(cl) &&
	var_value(lit_var(get_head(cl))) == DC &&
	var_value(lit_var(get_tail(cl))) == DC) {
      return get_head(cl);
    }
    cl += clause_head_size();
  }

  return weight_strategy_satbox();
}

unsigned long branch_fail;

int sato5_strategy6 ()
     /* using lookahead */
{
  int k, num, best_keys[100];
  int best_weight=0, best_lit=0;
  branch_fail = SX.Branch_fail;

  trace(3, printf("Begin lookahead\n"));
  /*
  if (SELECT == 8) {
    num = collect_best_keys(best_keys, SX.Max_atom>>2);
    for (i = make_lit(SX.Max_atom,1); i > 1; i--) lit_weight(i) = lit_weight_satbox(i);
  } else 
  */
  num = collect_best_keys_9(best_keys, SX.Max_atom>>2);

  Conflict_cl_level = Level+1;
  while (num) {
    if (var_value(k=best_keys[--num]) == DC) {
      if (lookahead(k, num, best_keys, &best_lit, &best_weight, 1) == UNSAT) break;
    }
  }

  trace(3, printf("End lookahead %d\n", best_lit));
  SX.Branch_fail = branch_fail;
  if (!best_lit) return basic_strategy_satbox();
  if (best_lit < 1 || var_value(lit_var(best_lit)) == DC) return best_lit;
  return sato5_strategy6();
}

int sato5_strategy7 ()
     /* a random heavy literal using weight */
{
  int i, num, best_keys[100];
  
  num = collect_best_keys(best_keys, SX.Max_atom>>3);
  if (num) {
    i = best_keys[rand() % num];
    return make_lit(i,(rand() & 1));
  } 
  return basic_strategy_satbox();
}

int lookahead (k, num, best_keys, best_lit, best_weight, depth)
     int k, num, best_keys[], *best_lit, *best_weight, depth;
{
  int i, j;
  int w0, w1;
  int stack_size = vstack_size();
  int ulit=0, units8[100];
  
  ++SX.Branch_open;
  ++Timer;
  var_level(k) = Level;
  k = make_lit(k, FF);
  print_let_x_have(k);
  vstack_push1(k);
  assign_value2(k);
  if (handle_unit_clauses() == SAT) {
    w0 = (vstack_size()-stack_size)<<1;
    /* lookahead 2 steps
    if (0 && depth) {
      int dummy, best_weight2;
      for (i = 0; i < num; i++) if (var_value(j=best_keys[i]) == DC) {
	best_weight2=0;
	if (lookahead(j, num, best_keys, &dummy, &best_weight2, 0) == UNSAT)
	  goto unsat;
	w0 += best_weight2;
      }
    }
    */
    if (++Stamp >= MAX_INT) Stamp = 1;
    for (j = stack_size; j < vstack_size(); j++) {
      i = vs_literal(vstack_of(j));
      lit_umark(i) = -Stamp;
      w0 += lit_weight_new_satbox(i);
    }
    vstack_backup(Level);
    uqueue_init();
    k = k^1;
    ++SX.Branch_open;
    ++Timer;
    print_now_let_x_have(k);
    vstack_push1(k);
    assign_value2(k);
    if (handle_unit_clauses() == SAT) {
      w1 = (vstack_size()-stack_size)<<1;
      /*
      if (depth) {
	int dummy, best_weight2=0;
	for (i = 0; i < num; i++) if (var_value(j=best_keys[i]) == DC) {
	  best_weight2=0;
	  if (lookahead(j, num, best_keys, &dummy, &best_weight2, 0) == UNSAT)
	    goto unsat;
	  w1 += best_weight2;
	}
      }
      */
      for (j = stack_size; j < vstack_size(); j++) {
	i = vs_literal(vstack_of(j));
	w1 += lit_weight_new_satbox(i);
	if (lit_umark(i) == -Stamp) {
	  if (ulit < 100) units8[ulit++] = i;
	}
      }
      vstack_backup(Level);
      uqueue_init();

      while (ulit) {
	j = units8[--ulit];
	var_level((j>>1)) = Level;
	print_x_has_to(j);
	vstack_push0(j);
	assign_value2(j);
      }
      if (uqueue_nonempty()) {
	if (handle_unit_clauses() == UNSAT) {
	  *best_lit = -1;
	  branch_fail++;
	  return UNSAT;
	}
      }

      j = w1*w0;
      if (j > *best_weight) {
	*best_weight = j;
	*best_lit = (w1 > w0)? k : k^1;
	trace(8, printf("  weight_of(%d) = %d, %d, %d\n", (k>>1), j, w0, w1));
      }
    } else { /* UNSAT */
      goto unsat;
    }
  } else { /* UNSAT */
    goto unsat;
  }

  return SAT;

 unsat:
  if (Conflict_cl_level < Level) {
    *best_lit = -1;
    branch_fail++;
    ++Timer;
    return UNSAT;
  }

  vstack_backup(Level);
  uqueue_init();
  k = k^1;
  var_level((k>>1)) = Level;
  print_x_has_to(k);
  vstack_push0(k);
  assign_value2(k);
  if (handle_unit_clauses() == UNSAT) {
    *best_lit = -1;
    branch_fail++;
    return UNSAT;
  }
  return SAT;
}

int unit_num=0;
#define unassigned_lit10(i) (lit_stamp(i) != Stamp && lit_stamp(i^1) != Stamp) 
#define assigned_false10(i) (lit_stamp(i^1) == Stamp) 

int basic_strategy_satbox0 ()
     /* using decision procedure for 2SAT */
{
  int i, k; 
  int best_weight=0, best_lit=0;

  if (++Stamp >= MAX_INT) Stamp = 1;  
  trace(3, printf("Begin 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)) {
      unit_num = 1;
      lit_stamp(i) = Stamp;
      trace(6, printf("  2-SAT:  %s%d be 1\n", 
		      (lit_sign(i))? "-" : "", lit_var(i)));
      if (binary_propagate(i^1) == UNSAT) {
	trace(3, printf("End 2-SAT\n"));
	return -1;
      }
      if (lit_value(i) == DC && unit_num > best_weight) {
	best_weight = unit_num;
	best_lit = i^1;
      }
    }
  }
  trace(3, printf("End 2-SAT\n"));
  if (best_lit < 1 || var_value(lit_var(best_lit)) == DC) return best_lit;
  return basic_strategy_satbox0();
}

int binary_propagate (ks0)
     int ks0;
{
  int **pts;
  int *p, *cl;
  int j, ks, num;

  pts = L5[ks0]->hts;
  p = L5[ks0]->bicls;

  /* propagate binary clauses first */
  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("  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_propagate(ks^1) == UNSAT) return UNSAT;
      } else if (assigned_false10(ks)) {
	/* a conflict and ks has to be true */
	if (++Stamp >= MAX_INT) Stamp = 1;  
	uqueue_init();
	var_level(ks>>1) = Level;
	trace(6, printf("  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;
      }
    }
  }

  /* propagate 3-SAT clauses */
  j = array_begin();
  num = array_count(pts);
  while (j < num) {
    if (lit_value(ks0) != DC) return SAT;
    cl = pts[j];
    if (is_head(cl, ks0)) { 
      ks = get_tail(cl);
    } else { 
      ks = get_head(cl);
    }

    if (is_3cnf(cl) && lit_value(third_lit(cl)) == FF && lit_value(ks) == DC) { 
      if (unassigned_lit10(ks)) {
	/* a unit clause is found */
	trace(6, { printf("  2-SAT:  %s%d from ", 
			  (lit_sign(ks))? "-" : "", lit_var(ks));
	             print_clause(cl);});
	lit_stamp(ks) = Stamp;
	if (binary_propagate(ks^1) == UNSAT) return UNSAT;
      } else if (assigned_false10(ks)) {
	/* a conflict and ks has to be true */
	/* this is buggy: a depth-first search procedure is needed */
	if (++Stamp >= MAX_INT) Stamp = 1;  
	uqueue_init();
	var_level(ks>>1) = Level;
	trace(6, { printf("  2-SAT:  %s%d be 1 by ", 
			  (lit_sign(ks))? "-" : "", lit_var(ks));
	             print_clause(cl);});
	ks = ks^1;
	vstack_push0(ks);
	assign_value2(ks);
	if (handle_unit_clauses() == UNSAT) return UNSAT;
      }
    }
    j++;
  }
  return SAT;
}

int basic_strategy_satbox1 ()
     /* the first unassigned clause and make it true */
{

  int *cl, *cl2;

  cl = Tape_space[0]; 
  cl2 = cl+Tape_limit[0]; 
  while (cl < cl2) {
    if (is_normal(cl) &&
	var_value(lit_var(get_head(cl))) == DC &&
	var_value(lit_var(get_tail(cl))) == DC) {
	return NEG(get_tail(cl));
    } 
    cl += size_of_clause(cl);
  }

  return basic_strategy_satbox();
}

int second_weight (ks0)
     int ks0;
{
  int **pts = L5[ks0]->hts;
  int *bcs = L5[ks0]->bicls;
  int i, j, num, ks, total = 0;
  int *cl;

  /* consider binary clauses first */
  for (j = array_count(bcs)-1; j >= array_begin(); j--) {
    if (var_value(lit_var(ks = bcs[j])) == DC) 
      total += lit_weight_satbox(NEG(ks));
  }

  num = array_count(pts);
  for (j = array_begin(); j < num; j++) {
    cl = pts[j];
    if (is_head(cl, ks0)) { /* p is a real head */
      ks = get_tail(cl);
    } else { /* p is actually tail */
      ks = get_head(cl);
    }

    if (is_3cnf(cl) && var_value(lit_var(ks)) == DC) {
      i = third_lit(cl);
      if (var_value(lit_var(i)) == DC)
	total += (lit_weight_satbox(NEG(ks))+lit_weight_satbox(NEG(i)))>>1;
      else if (lit_value(i))
	total += (lit_weight_satbox(NEG(ks)));
    }

  }
  
  return total;
}
