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

Module:

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

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

#include <std_types.h>

#include "boolbv.h"

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

Function: boolbvt::convert_mult

  Inputs:

 Outputs:

 Purpose:

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

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

  bv.resize(width);

  const exprt::operandst &operands=expr.operands();
  if(operands.size()==0) return true;

  const exprt &op0=expr.op0();

  bool no_overflow=expr.id()=="no-overflow-mult";
  
  if(expr.type().id()=="fixedbv")
  {
    if(op0.type()!=expr.type())
      throw "multiplication with mixed types";
    
    if(convert_bv(op0, bv)) return true;

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

    unsigned fraction_bits=
      to_fixedbv_type(expr.type()).get_fraction_bits();
             
    // do a sign extension by fraction_bits bits
    bv_utils.extend(bv, bv.size()+fraction_bits, true);
      
    for(exprt::operandst::const_iterator it=operands.begin()+1;
        it!=operands.end(); it++)
    {
      bvt op;

      if(it->type()!=expr.type())
        throw "multiplication with mixed types";

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

      if(op.size()!=width)
        throw "convert_mult: unexpected operand width";

      bv_utils.extend(op, bv.size(), true);

      bv_utils.multiplier(bv, op, true);
    }
    
    // cut it down again
    bv.erase(bv.begin(), bv.begin()+fraction_bits);

    return false;
  }
  else if(expr.type().id()=="unsignedbv" ||
          expr.type().id()=="signedbv")
  {
    if(op0.type()!=expr.type())
      throw "multiplication with mixed types";
      
    bool is_signed=expr.type().id()=="signedbv";
    
    if(convert_bv(op0, bv)) return true;

    if(bv.size()!=width)
      throw "convert_mult: unexpected operand width";
      
    for(exprt::operandst::const_iterator it=operands.begin()+1;
        it!=operands.end(); it++)
    {
      bvt op;

      if(it->type()!=expr.type())
        throw "multiplication with mixed types";

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

      if(op.size()!=width)
        throw "convert_mult: unexpected operand width";

      if(no_overflow)
        bv_utils.multiplier_no_overflow(bv, op, is_signed);        
      else
        bv_utils.multiplier(bv, op, is_signed);
    }    

    return false;
  }
  
  return true;
}
