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

Module:

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

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

#include <assert.h>

#include <arith_tools.h>

#include "boolbv.h"

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

Function: boolbvt::convert_byte_extract

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_byte_extract(const exprt &expr, bvt &bv)
{
  if(expr.operands().size()!=2)
    throw "byte_extract takes two operands";
    
  const exprt &op0=expr.op0();
  const exprt &op1=expr.op1();

  bool little_endian;
  
  if(expr.id()=="byte_extract_little_endian")
    little_endian=true;
  else if(expr.id()=="byte_extract_big_endian")
    little_endian=false;
  else
    assert(false);

  // see if the byte number is constant

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

  bvt op0_bv;
  
  if(convert_bv(op0, op0_bv))
    return true;

  unsigned bytes=op0_bv.size()/8;
  
  if(bytes==0)
    throw "byte_extract expects at least one byte as operand";

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

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

    // add implications

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

    typet constant_type=op1.type(); // type of index operand

    bvt equal_bv;
    equal_bv.resize(8);

    for(unsigned i=0; i<bytes; i++)
    {
      equality.op1()=from_integer(i, constant_type);

      unsigned offset=i*8;

      for(unsigned j=0; j<8; j++)
        equal_bv[j]=prop.lequal(bv[j], op0_bv[offset+j]);

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

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

    typet constant_type(op1.type()); // type of index operand
    
    for(unsigned i=0; i<bytes; i++)
    {
      equality.op1()=from_integer(i, constant_type);
        
      literalt e=convert(equality);

      unsigned offset=i*8;

      for(unsigned j=0; j<8; j++)
      {
        literalt l=op0_bv[offset+j];

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

  return false;
}

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

Function: boolbvt::convert_byte_extract

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_byte_extract(
  const exprt &expr,
  const mp_integer &index,
  bvt &bv,
  bool little_endian)
{
  bv.resize(8);
  
  bvt tmp;

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

  mp_integer offset;
  
  if(little_endian)
    offset=index*8;
  else
    offset=(mp_integer(tmp.size()/8)-index-1)*8;

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

  return false;
}

