#include "ands.h"
#include "eliminate.h"
#include "gates.h"
#include "inline.h"

bool
kissat_find_and_gate (kissat * solver, unsigned lit, unsigned negative)
{
  if (!GET_OPTION (ands))
    return false;
  size_t marked = kissat_mark_binaries (solver, lit);
  if (!marked)
    return false;
  if (marked < 2)
    {
      kissat_unmark_binaries (solver, lit);
      return false;
    }

  unsigned not_lit = NOT (lit);
  watches *not_watches = &WATCHES (not_lit);

  const word *arena = BEGIN_STACK (solver->arena);
  value *marks = solver->marks;
  const value *values = solver->values;

  clause *base = 0;
  for (all_binary_large_watches (watch, *not_watches))
    {
      if (watch.type.binary)
	continue;
      const reference ref = watch.large.ref;
      assert (ref < SIZE_STACK (solver->arena));
      clause *c = (clause *) (arena + ref);
      assert (!c->garbage);
      base = c;
      for (all_literals_in_clause (other, c))
	{
	  if (other == not_lit)
	    continue;
	  const value value = values[other];
	  if (value > 0)
	    {
	      kissat_eliminate_clause (solver, c, INVALID_LIT);
	      base = 0;
	      break;
	    }
	  if (value < 0)
	    continue;
	  const unsigned not_other = NOT (other);
	  signed char mark = marks[not_other];
	  if (mark)
	    continue;
	  base = 0;
	  break;
	}
      if (base)
	break;
    }
  if (!base)
    {
      kissat_unmark_binaries (solver, lit);
      return false;
    }
  LOGCLS (base, "found and gate %s base clause", LOGLIT (not_lit));
  for (all_literals_in_clause (other, base))
    {
      if (other == not_lit)
	continue;
      if (values[other])
	continue;
      const unsigned not_other = NOT (other);
      assert (marks[not_other]);
      marks[not_other] = 0;
    }
  watch tmp = kissat_binary_watch (0, false, false);
  watches *watches = &WATCHES (lit);
  for (all_binary_large_watches (watch, *watches))
    {
      if (!watch.type.binary)
	continue;
      const unsigned other = watch.binary.lit;
      assert (!solver->values[other]);
      if (marks[other])
	{
	  marks[other] = 0;
	  continue;
	}
      tmp.binary.lit = other;
      PUSH_STACK (solver->gates[negative], tmp);
    }
  tmp = kissat_large_watch (kissat_reference_clause (solver, base));
  PUSH_STACK (solver->gates[!negative], tmp);
  solver->gate_eliminated = GATE_ELIMINATED (ands);
  return true;
}
