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

Module:

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

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

#include <assert.h>

#include <std_types.h>

#include "boolbv.h"

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

Function: boolbvt::convert_typecast

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_typecast(const exprt &expr, bvt &bv)
{
  if(expr.operands().size()!=1)
    throw "typecast takes one operand";
    
  const exprt &op=expr.op0();

  bvt op_bv;
  if(convert_bv(op, op_bv)) return true;

  bvtypet bvtype=get_bvtype(expr.type());
  bvtypet op_bvtype=get_bvtype(op.type());
  unsigned op_width=op_bv.size();

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

  if(op_width==0) return true;
  
  switch(bvtype)
  {
  case IS_RANGE:
    if(op_bvtype==IS_UNSIGNED || op_bvtype==IS_SIGNED)
    {
      mp_integer from=string2integer(expr.type().get_string("from"));

      if(from==0)
      {
        // do zero extension
        for(unsigned i=0; i<bv.size(); i++)
          bv[i]=(i<op_bv.size()?op_bv[i]:prop.constant(false));
        return false;
      }
    }
    break;
    
  case IS_FIXED:
    if(op_bvtype==IS_FIXED)
    {
      // fixed to fixed
      
      unsigned fraction_bits=to_fixedbv_type(expr.type()).get_fraction_bits(),
               int_bits=width-fraction_bits;
      unsigned op_fraction_bits=to_fixedbv_type(op.type()).get_fraction_bits(),
               op_int_bits=op_width-op_fraction_bits;
      
      bv.resize(width);
      
      // i == position after dot
      // i == 0: first position after dot

      for(unsigned i=0; i<fraction_bits; i++)
      {
        // position in bv
        unsigned p=fraction_bits-i-1;
      
        if(i<op_fraction_bits)
          bv[p]=op_bv[op_fraction_bits-i-1];
        else 
          bv[p]=prop.constant(false); // zero padding
      }

      for(unsigned i=0; i<int_bits; i++)
      {
        // position in bv
        unsigned p=fraction_bits+i;
        assert(p<width);
      
        if(i<op_int_bits)
          bv[p]=op_bv[i+op_fraction_bits];
        else 
          bv[p]=op_bv[op_width-1]; // sign extension
      }

      return false;
    }
    else if(op_bvtype==IS_UNSIGNED || op_bvtype==IS_SIGNED || op_bvtype==IS_C_ENUM)
    {
      // integer to fixed

      unsigned fraction_bits=
        to_fixedbv_type(expr.type()).get_fraction_bits();

      for(unsigned i=0; i<fraction_bits; i++)
        bv.push_back(prop.constant(false)); // zero padding

      for(unsigned i=0; i<width-fraction_bits; i++)
      {
        literalt l;
      
        if(i<op_width)
          l=op_bv[i];
        else
        {
          if(op_bvtype==IS_SIGNED || op_bvtype==IS_C_ENUM)
            l=op_bv[op_width-1]; // sign extension
          else
            l=prop.constant(false); // zero extension
        }
        
        bv.push_back(l);
      }

      return false;
    }
    else if(op.type().id()=="bool")
    {
      // bool to fixed
      unsigned fraction_bits=
        to_fixedbv_type(expr.type()).get_fraction_bits();

      assert(op_width==1);

      for(unsigned i=0; i<width; i++)
      {
        if(i==fraction_bits)
          bv.push_back(op_bv[0]);
        else
          bv.push_back(prop.constant(false));
      }

      return false;
    }
    break;
  
  case IS_UNSIGNED:
  case IS_SIGNED:
  case IS_C_ENUM:
    if(op_bvtype==IS_FIXED)
    {
      // fixed to integer

      unsigned op_fraction_bits=
        to_fixedbv_type(op.type()).get_fraction_bits();

      for(unsigned i=0; i<width; i++)
      {
        if(i<op_width-op_fraction_bits)
          bv.push_back(op_bv[i+op_fraction_bits]);
        else if(bvtype==IS_SIGNED && op_bvtype==IS_SIGNED)
          bv.push_back(op_bv[op_width-1]); // sign extension
        else
          bv.push_back(prop.constant(false));
      }

      return false;
    }
    else if(op_bvtype==IS_UNSIGNED || op_bvtype==IS_SIGNED || op_bvtype==IS_C_ENUM)
    {
      // integer to integer
      
      bool sign_extension=
        (bvtype==IS_SIGNED || bvtype==IS_C_ENUM) &&
        (op_bvtype==IS_SIGNED || op_bvtype==IS_C_ENUM);

      for(unsigned i=0; i<width; i++)
      {
        if(i<op_width)
          bv.push_back(op_bv[i]);
        else if(sign_extension)
          bv.push_back(op_bv[op_width-1]); // sign extension
        else
          bv.push_back(prop.constant(false));
      }

      return false;
    }
    else if(op.type().id()=="bool")
    {
      // bool to integer

      assert(op_width==1);

      for(unsigned i=0; i<width; i++)
      {
        if(i==0)
          bv.push_back(op_bv[0]);
        else
          bv.push_back(prop.constant(false));
      }

      return false;
    }
    break;
    
  case IS_VERILOGBV:
    if(op_bvtype==IS_UNSIGNED)
    {
      for(unsigned i=0, j=0; i<width; i+=2, j++)
      {
        if(j<op_width)
          bv.push_back(op_bv[j]);
        else
          bv.push_back(prop.constant(false));

        bv.push_back(prop.constant(false));
      }

      return false;
    }
    break;
    
  default:
    if(expr.type().id()=="array")
    {
      assert(op_width==width);
      bv=op_bv;
      return false;
    }
    else if(expr.type().id()=="struct")
    {
      // we do prefixes
      if(op.type().id()=="struct")
      {
        if(to_struct_type(expr.type()).is_prefix_of(
           to_struct_type(op.type())))
        {
          assert(op_width>=width);
          bv=op_bv;
          bv.resize(width);
          return false;
        }
      }
    }
  }

  return true;
}

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

Function: boolbvt::convert_typecast

  Inputs:

 Outputs:

 Purpose: conversion from bitvector types to boolean

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

literalt boolbvt::convert_typecast(const exprt &expr)
{
  if(expr.operands().size()==1)
  {
    bvt bv;
    if(!convert_bv(expr.op0(), bv))
      if(bv.size()!=0)
        return prop.lor(bv);
  }
  
  return SUB::convert_rest(expr);
}
