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

Module:

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

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

#include <assert.h>

#include <arith_tools.h>

#include "boolbv.h"

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

Function: boolbvt::convert_index

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_index(const exprt &expr, bvt &bv)
{
  if(expr.id()!="index")
    throw "expected index expression";

  if(expr.operands().size()!=2)
    throw "index takes two operands";
    
  const exprt &op0=expr.op0();
  const exprt &op1=expr.op1();

  if(op0.type().id()!="array")
    return true;

  // see if the array size is constant

  if(is_unbounded_array(op0.type()))
  {
    // make it uninterpreted

    unsigned width;
    if(get_width(expr.type(), width))
      return true;

    // free variables

    bv.resize(width);
    for(unsigned i=0; i<width; i++)
      bv[i]=prop.new_literal();

    record_function_application(expr);

    // record type if array is a symbol

    if(op0.id()=="symbol")
      get_map_entry(op0.get("identifier"), op0.type());

    // make sure we have the index in the cache
    bvt index_bv;
    convert_bv(op1, index_bv);

    return false;
  }

  // see if the index address is constant

  mp_integer index;
  if(!to_integer(op1, index))
    return convert_index(expr.op0(), index, bv);

  unsigned width;
  if(get_width(expr.type(), width))
    return true;

  mp_integer array_size;
  if(to_integer((exprt &)op0.type().find("size"), array_size))
  {
    std::cout << op0.type().find("size").pretty() << std::endl;
    throw "failed to convert array size";
  }

  // get literals for the whole array

  bvt array_bv;

  if(convert_bv(op0, array_bv))
    return true;

  if(array_size*width!=array_bv.size())
    throw "unexpected array size";

  if(prop.has_set_to())
  {
    // free variables

    bv.resize(width);
    for(unsigned i=0; i<width; i++)
      bv[i]=prop.new_literal();

    // add implications

    exprt equality("=", typet("bool"));
    equality.operands().resize(2);
    equality.op0()=op1; // index operand

    bvt equal_bv;
    equal_bv.resize(width);

    for(mp_integer i=0; i<array_size; i=i+1)
    {
      equality.op1()=from_integer(i, equality.op0().type());

      if(equality.op1().is_nil())
        throw "number conversion failed (1)";

      mp_integer offset=i*width;

      for(unsigned j=0; j<width; j++)
        equal_bv[j]=prop.lequal(bv[j],
                           array_bv[integer2long(offset+j)]);

      prop.l_set_to_true(
        prop.limplies(convert(equality), prop.land(equal_bv)));
    }
  }
  else
  {
    bv.resize(width);

    exprt equality("=", typet("bool"));
    equality.operands().resize(2);
    equality.op0()=op1; // index operand

    typet constant_type=op1.type(); // type of index operand
    
    assert(array_size>0);

    for(mp_integer i=0; i<array_size; i=i+1)
    {
      equality.op1()=from_integer(i, constant_type);
        
      literalt e=convert(equality);

      mp_integer offset=i*width;

      for(unsigned j=0; j<width; j++)
      {
        literalt l=array_bv[integer2long(offset+j)];

        if(i==0)
          bv[j]=l;
        else
          bv[j]=prop.lselect(e, l, bv[j]);
      }
    }    
  }

  return false;
}

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

Function: boolbvt::convert_index

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_index(
  const exprt &expr,
  const mp_integer &index,
  bvt &bv)
{
  if(expr.type().id()!="array")
    return true;

  unsigned width;
  if(get_width(expr.type().subtype(), width))
    return true;

  bv.resize(width);

  mp_integer offset=index*width;

  if(expr.id()=="symbol")
  {
    // optimization: only generate necessary literals

    const irep_idt &identifier=expr.get("identifier");
    
    const typet &type=expr.type();

    for(unsigned i=0; i<width; i++)
      bv[i]=get_literal(identifier, integer2long(offset+i), type);

    return false;
  }
  else
  {
    bvt tmp;

    if(convert_bv(expr, tmp)) // recursive call
      return true;

    if(mp_integer(tmp.size())<offset+width)
    {
      // out of bounds
      for(unsigned i=0; i<width; i++)
        bv[i]=prop.new_literal();
    }
    else
    {
      for(unsigned i=0; i<width; i++)
        bv[i]=tmp[integer2long(offset+i)];
    }

    return false;
  }

  return true;
}

