/*******************************************************************\

Module: Symbolic Execution

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#include <assert.h>

#include <base_type.h>
#include <i2string.h>
#include <langapi/language_util.h>

#include "goto_symex_state.h"
#include "symex_target_equation.h"

/*******************************************************************\

Function: symex_target_equationt::assignment

  Inputs:

 Outputs:

 Purpose: write to a variable

\*******************************************************************/

void symex_target_equationt::assignment(
  const guardt &guard,
  const exprt &lhs,
  const exprt &original_lhs,
  exprt &rhs,
  const sourcet &source,
  assignment_typet assignment_type)
{
  assert(lhs.is_not_nil());
  
  states.push_back(statet());
  statet &state=states.back();
  
  guard.expr(state.guard);
  state.lhs=lhs;
  state.original_lhs=original_lhs;
  state.rhs.swap(rhs);
  state.assignment_type=assignment_type;

  state.cond=exprt("=", typet("bool"));
  state.cond.copy_to_operands(state.lhs, state.rhs);
  state.type=goto_trace_stept::ASSIGNMENT;
  state.set_source(source);
}

/*******************************************************************\

Function: symex_target_equationt::location

  Inputs:

 Outputs:

 Purpose: just record a location

\*******************************************************************/

void symex_target_equationt::location(
  const guardt &guard,
  const sourcet &source)
{
  states.push_back(statet());
  statet &state=states.back();
  
  guard.expr(state.guard);
  state.lhs.make_nil();
  state.type=goto_trace_stept::LOCATION;
  state.set_source(source);
}

/*******************************************************************\

Function: symex_target_equationt::trace_event

  Inputs:

 Outputs:

 Purpose: just record output

\*******************************************************************/

void symex_target_equationt::output(
  const guardt &guard,
  const sourcet &source,
  const std::list<exprt> &args)
{
  states.push_back(statet());
  statet &state=states.back();
  
  guard.expr(state.guard);
  state.lhs.make_nil();
  state.type=goto_trace_stept::OUTPUT;
  state.set_source(source);
  state.output_args=args;
}

/*******************************************************************\

Function: symex_target_equationt::assumption

  Inputs: cond is destroyed

 Outputs:

 Purpose: record an assumption

\*******************************************************************/

void symex_target_equationt::assumption(
  const guardt &guard,
  exprt &cond,
  const sourcet &source)
{
  states.push_back(statet());
  statet &state=states.back();
  
  guard.expr(state.guard);
  state.lhs.make_nil();
  state.cond.swap(cond);
  state.type=goto_trace_stept::ASSUME;
  state.set_source(source);
}

/*******************************************************************\

Function: symex_target_equationt::assertion

  Inputs: cond is destroyed

 Outputs:

 Purpose: record an assertion

\*******************************************************************/

void symex_target_equationt::assertion(
  const guardt &guard,
  exprt &cond,
  const std::string &msg,
  const sourcet &source)
{
  states.push_back(statet());
  statet &state=states.back();
  
  guard.expr(state.guard);
  state.lhs.make_nil();
  state.cond.swap(cond);
  state.type=goto_trace_stept::ASSERT;
  state.set_source(source);
  state.comment=msg;
}

/*******************************************************************\

Function: symex_target_equationt::convert

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::convert(
  prop_convt &prop_conv)
{
  convert_guards(prop_conv);
  convert_assignments(prop_conv);
  convert_assumptions(prop_conv);
  convert_assertions(prop_conv);
}

/*******************************************************************\

Function: symex_target_equationt::convert_assignments

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::convert_assignments(
  decision_proceduret &decision_procedure) const
{
  for(statest::const_iterator it=states.begin();
      it!=states.end(); it++)
  {
    if(it->is_assignment() && !it->ignore)
    {
      exprt tmp(it->cond);
      ::base_type(tmp, ns);
      decision_procedure.set_to_true(tmp);
    }
  }
}

/*******************************************************************\

Function: symex_target_equationt::convert_guards

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::convert_guards(
  prop_convt &prop_conv)
{
  for(statest::iterator it=states.begin();
      it!=states.end(); it++)
  {
    exprt tmp(it->guard);
    ::base_type(tmp, ns);
    it->guard_literal=prop_conv.convert(tmp);
  }
}

/*******************************************************************\

Function: symex_target_equationt::convert_assumptions

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::convert_assumptions(
  prop_convt &prop_conv)
{
  for(statest::iterator it=states.begin();
      it!=states.end(); it++)
  {
    if(it->is_assume())
    {
      exprt tmp(it->cond);
      ::base_type(tmp, ns);
      it->cond_literal=prop_conv.convert(tmp);
    }
  }
}

/*******************************************************************\

Function: symex_target_equationt::convert_assertions

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::convert_assertions(
  prop_convt &prop_conv)
{
  bvt bv;

  bv.reserve(states.size());
  
  literalt assumption_literal=prop_conv.prop.constant(true);

  for(statest::iterator it=states.begin();
      it!=states.end(); it++)
    if(it->is_assert())
    {
      exprt tmp(it->cond);
      ::base_type(tmp, ns);

      // do the expression
      literalt tmp_literal=prop_conv.convert(tmp);

      it->cond_literal=prop_conv.prop.limplies(assumption_literal, tmp_literal);

      bv.push_back(prop_conv.prop.lnot(it->cond_literal));
    }
    else if(it->is_assume())
      assumption_literal=
        prop_conv.prop.land(assumption_literal, it->cond_literal);

  if(!bv.empty())
    prop_conv.prop.lcnf(bv);
}

/*******************************************************************\

Function: symex_target_equationt::output

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::output(std::ostream &out) const
{
  for(statest::const_iterator
      it=states.begin();
      it!=states.end();
      it++)
  {
    it->output(ns, out);    
    out << "--------------" << std::endl;
  }
}

/*******************************************************************\

Function: symex_target_equationt::statet::output

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void symex_target_equationt::statet::output(
  const namespacet &ns,
  std::ostream &out) const
{
  out << "Thread " << thread_nr;

  if(pc->location.is_not_nil())
    out << " " << pc->location << std::endl;
  else
    out << std::endl;

  switch(type)
  {
  case goto_trace_stept::ASSERT: out << "ASSERT" << std::endl; break;
  case goto_trace_stept::ASSUME: out << "ASSUME" << std::endl; break;
  case goto_trace_stept::LOCATION: out << "LOCATION" << std::endl; break;
  case goto_trace_stept::OUTPUT: out << "OUTPUT" << std::endl; break;

  case goto_trace_stept::ASSIGNMENT:
    out << "ASSIGNMENT (";
    switch(assignment_type)
    {
    case INIT: out << "INIT"; break;
    case HIDDEN: out << "HIDDEN"; break;
    case STATE: out << "STATE"; break;
    default:;
    }

    std::cout << ")" << std::endl;
    break;

  default: assert(false);
  }

  if(is_assert() || is_assume() || is_assignment())
    out << from_expr(ns, "", cond) << std::endl;
  
  if(is_assert())
    out << comment << std::endl;

  out << "Guard: " << from_expr(ns, "", guard) << std::endl;
}

/*******************************************************************\

Function: operator <<

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

std::ostream &operator<<(
  std::ostream &out,
  const symex_target_equationt &equation)
{
  equation.output(out);
  return out;
}
