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

Module:

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

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

#include <assert.h>

#include "boolbv.h"

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

Function: boolbvt::convert_rest

  Inputs:

 Outputs:

 Purpose:

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

literalt boolbvt::convert_overflow(const exprt &expr)
{
  const exprt::operandst &operands=expr.operands();

  if(expr.id()=="overflow-+"  || expr.id()=="overflow--")
  {
    if(operands.size()!=2)
      throw "operand "+expr.id_string()+" takes two operands";

    bvt bv0, bv1;

    if(convert_bv(operands[0], bv0) ||
       convert_bv(operands[1], bv1))
      return SUB::convert_rest(expr);

    if(bv0.size()!=bv1.size())
      return SUB::convert_rest(expr);

    bool subtract=expr.id()=="overflow--";
    
    // an overflow occurs if the signs of the two operands are the same
    // and the sign of the sum is the opposite

    literalt old_sign=bv0[bv0.size()-1];
    literalt sign_the_same=prop.lequal(bv0[bv0.size()-1], bv1[bv1.size()-1]);
    if(subtract) sign_the_same=prop.lnot(sign_the_same);

    literalt carry;
    bv_utils.adder(bv0, bv1, subtract, prop.constant(subtract), carry, false);

    // result of addition in bv0
    return prop.land(sign_the_same, prop.lxor(bv0[bv0.size()-1], old_sign));
  }
  else if(expr.id()=="overflow-*")
  {
    if(operands.size()!=2)
      throw "operand "+expr.id_string()+" takes two operands";

    bvt bv0, bv1;

    if(convert_bv(operands[0], bv0) ||
       convert_bv(operands[1], bv1))
      return SUB::convert_rest(expr);

    if(bv0.size()!=bv1.size())
      return SUB::convert_rest(expr);

    if(operands[0].type().id()!="unsignedbv" &&
       operands[0].type().id()!="signedbv")
      return SUB::convert_rest(expr);

    bool is_signed=operands[0].type().id()=="signedbv";

    if(operands[0].type()!=operands[1].type())
      return SUB::convert_rest(expr);

    assert(bv0.size()==bv1.size());
    unsigned old_size=bv0.size();
    bv0.resize(old_size*2);
    bv1.resize(old_size*2);

    // sign extension
    for(unsigned i=old_size; i<bv0.size(); i++)
    {
      bv0[i]=is_signed?bv0[old_size-1]:prop.constant(0);
      bv1[i]=is_signed?bv1[old_size-1]:prop.constant(0);
    }

    // result is in bv0
    bv_utils.multiplier(bv0, bv1, is_signed);

    bvt bv_overflow;
    bv_overflow.reserve(old_size);
    // get top result bits
    for(unsigned i=old_size; i<bv0.size(); i++)
      bv_overflow.push_back(bv0[i]);

    return prop.lor(bv_overflow);
  }
  else if(expr.id()=="overflow-unary-")
  {
    if(operands.size()!=1)
      throw "operand "+expr.id_string()+" takes one operand";

    bvt bv;

    if(convert_bv(operands[0], bv))
      return SUB::convert_rest(expr);
      
    return bv_utils.overflow_negate(bv);
  }
  else if(std::string(expr.id_string(), 0, 18)=="overflow-typecast-")
  {
    unsigned bits=atoi(expr.id().c_str()+18);

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

    if(operands.size()!=1)
      throw "operand "+expr.id_string()+" takes one operand";

    bvt bv;

    if(convert_bv(operands[0], bv))
      return SUB::convert_rest(expr);

    if(bits>=bv.size())
      throw "overflow-typecast got wrong number of bits";

    bvt tmp_bv;
    for(unsigned i=bits; i<bv.size(); i++)
      tmp_bv.push_back(bv[i]);

    return prop.lor(tmp_bv);
  }

  return SUB::convert_rest(expr);
}
