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

Module:

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

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

#include "boolbv.h"

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

Function: boolbvt::convert_add_sub

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_add_sub(const exprt &expr, bvt &bv)
{
  unsigned width;
  if(get_width(expr.type(), width))
    return true;

  if(expr.type().id()!="unsignedbv" &&
     expr.type().id()!="signedbv" &&
     expr.type().id()!="fixedbv" &&
     expr.type().id()!="range")
    return true;

  const exprt::operandst &operands=expr.operands();

  if(operands.size()==0)
    throw "operand "+expr.id_string()+" takes at least one operand";

  const exprt &op0=expr.op0();

  if(op0.type()!=expr.type())
  {
    std::cerr << expr.pretty() << std::endl;
    throw "add/sub with mixed types";
  }

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

  if(bv.size()!=width)
    throw "convert_add_sub: unexpected operand width";

  bool subtract=(expr.id()=="-" ||
                 expr.id()=="no-overflow-minus");
                 
  bool no_overflow=(expr.id()=="no-overflow-plus" ||
                    expr.id()=="no-overflow-minus");

  bool is_signed=(expr.type().id()=="signedbv" ||
                  expr.type().id()=="fixedbv");

  for(exprt::operandst::const_iterator it=operands.begin()+1;
      it!=operands.end(); it++)
  {
    bvt op;
    
    if(it->type()!=expr.type())
    {
      std::cerr << expr.pretty() << std::endl;
      throw "add/sub with mixed types";
    }

    if(convert_bv(*it, op)) return true;

    if(op.size()!=width)
      throw "convert_add_sub: unexpected operand width";
      
    if(no_overflow)
      bv_utils.adder_no_overflow(bv, op, subtract, is_signed);
    else
      bv_utils.adder(bv, op, subtract);
  }

  return false;
}

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

Function: boolbvt::convert_unary_minus

  Inputs:

 Outputs:

 Purpose:

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

bool boolbvt::convert_unary_minus(const exprt &expr, bvt &bv)
{
  unsigned width;
  if(get_width(expr.type(), width))
    return true;

  const exprt::operandst &operands=expr.operands();

  if(operands.size()!=1)
    throw "unary minus takes one operand";
    
  const exprt &op0=expr.op0();

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

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

  bool no_overflow=(expr.id()=="no-overflow-unary-minus");
  
  if(op_width!=0)
  {
    if(bvtype==IS_FIXED && op_bvtype==IS_FIXED)
    {
      if(no_overflow)
        bv_utils.negate_no_overflow(op_bv);
      else
        bv_utils.negate(op_bv);

      bv=op_bv;
      return false;
    }
    else if((op_bvtype==IS_SIGNED || op_bvtype==IS_UNSIGNED) &&
            (bvtype==IS_SIGNED || bvtype==IS_UNSIGNED))
    {
      if(no_overflow)
        prop.l_set_to(bv_utils.overflow_negate(op_bv), false);

      if(no_overflow)
        bv_utils.negate_no_overflow(op_bv);
      else
        bv_utils.negate(op_bv);

      bv=op_bv;
      return false;
    }
  }
  
  return true;
}
