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

Module: Symbolic Execution

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

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

#include <assert.h>

#include <i2string.h>

#include "goto_symex_state.h"

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

Function: goto_symex_statet::level1t::level1t

  Inputs:

 Outputs:

 Purpose:

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

goto_symex_statet::level1t::level1t()
{
}

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

Function: goto_symex_statet::level2t::level2t

  Inputs:

 Outputs:

 Purpose:

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

goto_symex_statet::level2t::level2t()
{
}

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

Function: goto_symex_statet::level1t::~level1t

  Inputs:

 Outputs:

 Purpose:

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

goto_symex_statet::level1t::~level1t()
{
}

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

Function: goto_symex_statet::level2t::~level2t

  Inputs:

 Outputs:

 Purpose:

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

goto_symex_statet::level2t::~level2t()
{
}

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

Function: goto_symex_statet::name_frame

  Inputs:

 Outputs:

 Purpose:

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

std::string goto_symex_statet::level1t::name(
  const irep_idt &identifier,
  unsigned frame) const
{
  return id2string(identifier)+"@"+i2string(frame);
}

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

Function: goto_symex_statet::name_count

  Inputs:

 Outputs:

 Purpose:

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

std::string goto_symex_statet::level2t::name(
  const irep_idt &identifier,
  unsigned count) const
{
  return id2string(identifier)+"#"+i2string(count);
}

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

Function: goto_symex_statet::current_number

  Inputs:

 Outputs:

 Purpose:

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

unsigned goto_symex_statet::level2t::current_number(
  const irep_idt &identifier) const
{
  current_namest::const_iterator it=current_names.find(identifier);
  if(it==current_names.end()) return 0;
  return it->second.count;
}

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

Function: goto_symex_statet::level1t::current_name

  Inputs:

 Outputs:

 Purpose:

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

std::string goto_symex_statet::level1t::current_name(
  const irep_idt &identifier) const
{
  current_namest::const_iterator it=
    current_names.find(identifier);

  if(it==current_names.end())
    return id2string(identifier);

  return name(identifier, it->second);
}

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

Function: goto_symex_statet::level2t::current_name

  Inputs:

 Outputs:

 Purpose:

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

std::string goto_symex_statet::level2t::current_name(
  const irep_idt &identifier) const
{
  current_namest::const_iterator it=
    current_names.find(identifier);

  if(it==current_names.end())
    return name(identifier, 0);

  return name(identifier, it->second.count);
}

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

Function: goto_symex_statet::assignment

  Inputs:

 Outputs:

 Purpose: write to a variable

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

void goto_symex_statet::assignment(
  exprt &lhs,
  const exprt &rhs,
  const namespacet &ns,
  bool init,
  bool record_value)
{
  assert(lhs.id()=="symbol");

  // the type might need renaming
  rename(lhs.type());

  const irep_idt &identifier=
    lhs.get("identifier");
    
  // identifier should be l0 or l1, make sure it's l1
  
  const std::string l1_identifier=
    level1.current_name(identifier);

  // do the l2 renaming  
  level2t::valuet &entry=level2.current_names[l1_identifier];

  if(init)
  {
    if(entry.count!=0)
      throw "variable "+l1_identifier+" initialized after writing";
  }
  else
    entry.count++;
    
  level2.rename(l1_identifier, entry.count);
  
  lhs.set("identifier", level2.name(l1_identifier, entry.count));
  
  if(record_value)
  {
    // for constant propagation
    
    if(rhs.is_constant())
      entry.constant=rhs;
    else
      entry.constant.make_nil();
  }
  else
    entry.constant.make_nil();
      
  if(use_value_set)
  {
    // update value sets
    value_sett::expr_sett rhs_value_set;
    exprt l1_rhs(rhs);
    level2.get_original_name(l1_rhs);

    exprt l1_lhs("symbol", lhs.type());
    l1_lhs.set("identifier", l1_identifier);

    value_set.assign(l1_lhs, l1_rhs, ns);  
  }
}

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

Function: goto_symex_statet::rename

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::rename(exprt &expr)
{
  // rename all the symbols with their last known value
  
  rename(expr.type());

  if(expr.id()=="symbol")
  {
    level1.rename(expr);
    level2.rename(expr);
  }
  else if(expr.id()=="address_of" ||
          expr.id()=="implicit_address_of" ||
          expr.id()=="reference_to")
  {
    assert(expr.operands().size()==1);
    rename_address(expr.op0());
  }
  else
  {
    // do this recursively
    Forall_operands(it, expr)
      rename(*it);
  }
}

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

Function: goto_symex_statet::rename_address

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::rename_address(exprt &expr)
{
  // rename all the symbols with their last known value

  rename(expr.type());

  if(expr.id()=="symbol")
  {
    // only do L1
    level1.rename(expr);
  }
  else if(expr.id()=="index")
  {
    assert(expr.operands().size()==2);
    rename_address(expr.op0());
    rename(expr.op1());
  }
  else
  {
    // do this recursively
    Forall_operands(it, expr)
      rename_address(*it);
  }
}

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

Function: goto_symex_statet::level1t::rename

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::level1t::rename(exprt &expr)
{
  // rename all the symbols with their last known value
  
  rename(expr.type());

  if(expr.id()=="symbol")
  {
    const irep_idt &identifier=expr.get("identifier");

    // first see if it's already an l1 name
    
    if(original_identifiers.find(identifier)!=
       original_identifiers.end())
      return;
    
    const current_namest::const_iterator it=
      current_names.find(identifier);
    
    if(it!=current_names.end())
      expr.set("identifier", name(identifier, it->second));
  }
  else if(expr.id()=="address_of" ||
          expr.id()=="implicit_address_of" ||
          expr.id()=="reference_to")
  {
    assert(expr.operands().size()==1);
    rename(expr.op0());
  }
  else
  {
    // do this recursively
    Forall_operands(it, expr)
      rename(*it);
  }
}
/*******************************************************************\

Function: goto_symex_statet::level2t::rename

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::level2t::rename(exprt &expr)
{
  // rename all the symbols with their last known value
  
  rename(expr.type());

  if(expr.id()=="symbol")
  {
    const irep_idt &identifier=expr.get("identifier");

    // first see if it's already an l2 name

    if(original_identifiers.find(identifier)!=
       original_identifiers.end())
      return;
    
    const current_namest::const_iterator it=
      current_names.find(identifier);
    
    if(it!=current_names.end())
    {
      if(it->second.constant.is_not_nil())
        expr=it->second.constant;
      else
        expr.set("identifier", name(identifier, it->second.count));
    }
    else
    {
      std::string new_identifier=name(identifier, 0);
      original_identifiers[new_identifier]=identifier;
      expr.set("identifier", new_identifier);
    }
  }
  else if(expr.id()=="address_of" ||
          expr.id()=="implicit_address_of" ||
          expr.id()=="reference_to")
  {
    // do nothing
  }
  else
  {
    // do this recursively
    Forall_operands(it, expr)
      rename(*it);
  }
}

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

Function: goto_symex_statet::rename

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::rename(typet &type)
{
  // rename all the symbols with their last known value

  if(type.id()=="array")
  {
    rename(type.subtype());
    rename((exprt &)type.add("size"));
  }
  else if(type.id()=="struct" ||
          type.id()=="union" ||
          type.id()=="class")
  {
    // TODO
  }
  else if(type.id()=="pointer")
  {
    rename(type.subtype());
  }
}

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

Function: goto_symex_statet::rename

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::renaming_levelt::rename(typet &type)
{
  // rename all the symbols with their last known value

  if(type.id()=="array")
  {
    rename(type.subtype());
    rename((exprt &)type.add("size"));
  }
  else if(type.id()=="struct" ||
          type.id()=="union" ||
          type.id()=="class")
  {
    // TODO
  }
  else if(type.id()=="pointer")
  {
    rename(type.subtype());
  }
}

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

Function: goto_symex_statet::get_original_name

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::get_original_name(exprt &expr) const
{
  Forall_operands(it, expr)
    get_original_name(*it);

  if(expr.id()=="symbol")
  {
    level2.get_original_name(expr);
    level1.get_original_name(expr);
  }
}

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

Function: goto_symex_statet::level1t::get_original_name

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::level1t::get_original_name(exprt &expr) const
{
  Forall_operands(it, expr)
    get_original_name(*it);

  if(expr.id()=="symbol")
  {
    original_identifierst::const_iterator it=
      original_identifiers.find(expr.get("identifier"));
    if(it==original_identifiers.end()) return;
    expr.set("identifier", it->second);
  }
}

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

Function: goto_symex_statet::levelt2t::get_original_name

  Inputs:

 Outputs:

 Purpose:

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

void goto_symex_statet::level2t::get_original_name(exprt &expr) const
{
  Forall_operands(it, expr)
    get_original_name(*it);

  if(expr.id()=="symbol")
  {
    original_identifierst::const_iterator it=
      original_identifiers.find(expr.get("identifier"));
    if(it==original_identifiers.end()) return;
    expr.set("identifier", it->second);
  }
}

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

Function: goto_symex_statet::level1t::get_original_name

  Inputs:

 Outputs:

 Purpose:

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

const irep_idt &goto_symex_statet::level1t::get_original_name(
  const irep_idt &identifier) const
{
  original_identifierst::const_iterator it=
    original_identifiers.find(identifier);
  if(it==original_identifiers.end()) return identifier;
  return it->second;
}

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

Function: goto_symex_statet::levelt2t::get_original_name

  Inputs:

 Outputs:

 Purpose:

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

const irep_idt &goto_symex_statet::level2t::get_original_name(
  const irep_idt &identifier) const
{
  original_identifierst::const_iterator it=
    original_identifiers.find(identifier);
  if(it==original_identifiers.end()) return identifier;
  return it->second;
}

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

Function: goto_symex_statet::get_original_identifier

  Inputs:

 Outputs:

 Purpose:

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

const irep_idt &goto_symex_statet::get_original_name(
  const irep_idt &identifier) const
{
  return level1.get_original_name(
         level2.get_original_name(identifier));
}

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

Function: goto_symex_statet::current_name

  Inputs:

 Outputs:

 Purpose:

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

std::string goto_symex_statet::current_name(
  const irep_idt &identifier) const
{
  return level2.current_name(
         level1.current_name(identifier));
}
