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

Module:

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

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

#include <assert.h>

#include "bv_utils.h"

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

Function: bv_utilst::carry

  Inputs:

 Outputs:

 Purpose:

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

literalt bv_utilst::carry(literalt a, literalt b, literalt c)
{
  #if 0

  bvt clause;

  literalt x=prop.new_literal();

  /*
  carry_correct: LEMMA
    (    a OR     b OR          NOT x) AND
    (    a OR NOT b OR     c OR NOT x) AND
    (    a OR NOT b OR NOT c OR     x) AND
    (NOT a OR     b OR     c OR NOT x) AND
    (NOT a OR     b OR NOT c OR     x) AND
    (NOT a OR NOT b OR              x)
    IFF
    (x=((a AND b) OR (a AND c) OR (b AND c)));
  */

  clause.resize(3);
  clause[0]=a;
  clause[1]=b;
  clause[2]=neg(x);
  lcnf(clause);

  clause.resize(4);
  clause[0]=a;
  clause[1]=neg(b);
  clause[2]=c;
  clause[3]=neg(x);
  lcnf(clause);

  clause.resize(4);
  clause[0]=a;
  clause[1]=neg(b);
  clause[2]=neg(c);
  clause[3]=x;
  lcnf(clause);

  clause.resize(4);
  clause[0]=neg(a);
  clause[1]=b;
  clause[2]=c;
  clause[3]=neg(x);
  lcnf(clause);

  clause.resize(4);
  clause[0]=neg(a);
  clause[1]=b;
  clause[2]=neg(c);
  clause[3]=x;
  lcnf(clause);

  clause.resize(3);
  clause[0]=neg(a);
  clause[1]=neg(b);
  clause[2]=x;
  lcnf(clause);

  return x;

  #else

  bvt tmp;

  tmp.push_back(prop.land(a, b));
  tmp.push_back(prop.land(a, c));
  tmp.push_back(prop.land(b, c));

  return prop.lor(tmp);
  #endif
}

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

Function: bv_utilst::adder

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::adder(
  bvt &sum,
  const bvt &op,
  bool subtract,
  literalt carry_in,
  literalt &carry_out,
  bool carry_only)
{
  carry_out=carry_in;

  for(unsigned i=0; i<sum.size(); i++)
  {
    literalt op0_bit=sum[i];

    literalt op1_bit=
      subtract?prop.lnot(op[i]):op[i];

    if(!carry_only)
      sum[i]=prop.lxor(
             prop.lxor(op0_bit, op1_bit), carry_out);

    carry_out=carry(op0_bit, op1_bit, carry_out);
  }
}

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

Function: bv_utilst::adder

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::adder(bvt &sum, const bvt &op, bool subtract)
{
  literalt carry_in=prop.constant(subtract);
  literalt carry_out;

  adder(sum, op, subtract, carry_in, carry_out, false);
}

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

Function: bv_utilst::adder_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::adder_no_overflow(
  bvt &sum,
  const bvt &op,
  bool subtract,
  bool is_signed)
{
  if(is_signed)
  {
    // 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=sum[sum.size()-1];
    literalt sign_the_same=prop.lequal(sum[sum.size()-1], op[op.size()-1]);
    if(subtract) sign_the_same=prop.lnot(sign_the_same);

    literalt carry;
    adder(sum, op, subtract, prop.constant(subtract), carry, false);

    // result of addition in sum
    prop.l_set_to_false(
      prop.land(sign_the_same, prop.lxor(sum[sum.size()-1], old_sign)));
  }
  else
  {
    literalt carry;
    adder(sum, op, subtract, prop.constant(subtract), carry, false);
    prop.l_set_to(carry, subtract);
  }
}

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

Function: bv_utilst::adder_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::adder_no_overflow(bvt &sum, const bvt &op)
{
  literalt carry_out=prop.constant(false);

  for(unsigned i=0; i<sum.size(); i++)
  {
    literalt op0_bit=sum[i];

    sum[i]=prop.lxor(
           prop.lxor(op0_bit, op[i]), carry_out);

    carry_out=carry(op0_bit, op[i], carry_out);
  }

  prop.l_set_to_false(carry_out); // enforce no overflow
}

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

Function: bv_utilst::shifter

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::shifter(const shiftt shift, bvt &op, const bvt &dist)
{
  unsigned d=1, width=op.size();

  for(unsigned stage=0; stage<dist.size(); stage++)
  {
    if(d>=width) break;

    if(dist[stage]!=prop.constant(false))
    {
      bvt tmp(op);

      for(unsigned i=0; i<width; i++)
      {
        literalt other;

        if(shift==LEFT)
          other=(d<=i?tmp[i-d]:prop.constant(false));
        else if(shift==ARIGHT)
          other=(i+d<width?tmp[i+d]:tmp[width-1]); // sign bit
        else if(shift==LRIGHT)
          other=(i+d<width?tmp[i+d]:prop.constant(false));

        op[i]=prop.lselect(dist[stage], other, tmp[i]);
      }
    }

    d=d<<1;
  }
}

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

Function: bv_utilst::negate

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::negate(bvt &bv)
{
  invert(bv);
  literalt carry_out;
  incrementer(bv, prop.constant(true), carry_out);
}

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

Function: bv_utilst::negate_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::negate_no_overflow(bvt &bv)
{
  prop.l_set_to(overflow_negate(bv), false);
  negate(bv);
}

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

Function: bv_utilst::overflow_negate

  Inputs:

 Outputs:

 Purpose:

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

literalt bv_utilst::overflow_negate(const bvt &bv)
{
  // a overflow on unary- can only happen with the smallest
  // representable number 100....0

  bvt zeros;

  for(unsigned i=0; i<bv.size()-1; i++)
    zeros.push_back(bv[i]);

  return prop.land(bv[bv.size()-1], prop.lnot(prop.lor(zeros)));
}

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

Function: bv_utilst::incrementer

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::incrementer(
  bvt &bv,
  literalt carry_in,
  literalt &carry_out)
{
  carry_out=carry_in;

  for(unsigned i=0; i<bv.size(); i++)
  {
    literalt new_carry=prop.land(carry_out, bv[i]);
    bv[i]=prop.lxor(bv[i], carry_out);
    carry_out=new_carry;
  }
}

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

Function: bv_utilst::invert

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::invert(bvt &bv)
{
  for(unsigned i=0; i<bv.size(); i++)
    bv[i]=prop.lnot(bv[i]);
}

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

Function: bv_utilst::unsigned_multiplier

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::unsigned_multiplier(bvt &product, const bvt &op)
{
  bvt op1=product, op2=op;

  if(is_constant(op2))
    op1.swap(op2);

  for(unsigned i=0; i<product.size(); i++)
    product[i]=prop.constant(false);

  for(unsigned sum=0; sum<op1.size(); sum++)
    if(op1[sum]!=prop.constant(false))
    {
      bvt tmpop;

      tmpop.reserve(op1.size());

      for(unsigned idx=0; idx<sum; idx++)
        tmpop.push_back(prop.constant(false));

      for(unsigned idx=sum; idx<op1.size(); idx++)
        tmpop.push_back(prop.land(op2[idx-sum], op1[sum]));

      adder(product, tmpop, false);
    }
}

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

Function: bv_utilst::unsigned_multiplier_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::unsigned_multiplier_no_overflow(
  const bvt &op0,
  const bvt &op1,
  bvt &product)
{
  bvt _op0=op0, _op1=op1;

  if(is_constant(_op1))
    _op0.swap(_op1);

  assert(_op0.size()==_op1.size());

  product.resize(_op0.size());

  for(unsigned i=0; i<product.size(); i++)
    product[i]=prop.constant(false);

  for(unsigned sum=0; sum<op0.size(); sum++)
    if(op0[sum]!=prop.constant(false))
    {
      bvt tmpop;

      tmpop.reserve(product.size());

      for(unsigned idx=0; idx<sum; idx++)
        tmpop.push_back(prop.constant(false));

      for(unsigned idx=sum; idx<product.size(); idx++)
        tmpop.push_back(prop.land(op1[idx-sum], op0[sum]));

      adder_no_overflow(product, tmpop);

      for(unsigned idx=op1.size()-sum; idx<op1.size(); idx++)
        prop.l_set_to_false(prop.land(op1[idx], op0[sum]));
    }
}

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

Function: bv_utilst::unsigned_multiplier_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::unsigned_multiplier_no_overflow(
  bvt &product,
  const bvt &op)
{
  const bvt tmp(product);
  unsigned_multiplier_no_overflow(tmp, op, product);
}

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

Function: bv_utilst::signed_multiplier

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::signed_multiplier(bvt &product, const bvt &op)
{
  if(product.size()==0 || op.size()==0) return;

  bvt &a=product;
  bvt b(op);

  literalt sign_a=a[a.size()-1];
  literalt sign_b=b[b.size()-1];

  cond_negate(a, sign_a);
  cond_negate(b, sign_b);

  unsigned_multiplier(a, b);

  literalt result_sign=prop.lxor(sign_a, sign_b);
  
  cond_negate(a, result_sign);
}

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

Function: bv_utilst::cond_negate

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::cond_negate(bvt &bv, const literalt cond)
{
  bvt neg_bv(bv);

  negate(neg_bv);

  for(unsigned i=0; i<bv.size(); i++)
    bv[i]=(prop.lselect(cond, neg_bv[i], bv[i]));
}

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

Function: bv_utilst::cond_negate_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::cond_negate_no_overflow(bvt &bv, literalt cond)
{
  prop.l_set_to(
    prop.limplies(cond, prop.lnot(overflow_negate(bv))),
    true);

  cond_negate(bv, cond);
}

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

Function: bv_utilst::signed_multiplier_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::signed_multiplier_no_overflow(
  bvt &product,
  const bvt &op)
{
  if(product.size()==0 || op.size()==0) return;
  
  bvt &a=product;
  bvt b(op);

  literalt sign_a=a[a.size()-1];
  literalt sign_b=b[b.size()-1];

  cond_negate_no_overflow(a, sign_a);
  cond_negate_no_overflow(b, sign_b);

  unsigned_multiplier_no_overflow(a, b);
  
  prop.l_set_to(a[a.size()-1], false);

  literalt result_sign=prop.lxor(sign_a, sign_b);
  
  cond_negate_no_overflow(a, result_sign);
}

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

Function: bv_utilst::multiplier

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::multiplier(
  bvt &product,
  const bvt &op, 
  bool is_signed)
{
  if(is_signed)
    signed_multiplier(product, op);
  else
    unsigned_multiplier(product, op);
}

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

Function: bv_utilst::multiplier_no_overflow

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::multiplier_no_overflow(
  bvt &product,
  const bvt &op, 
  bool is_signed)
{
  if(is_signed)
    signed_multiplier_no_overflow(product, op);
  else
    unsigned_multiplier_no_overflow(product, op);
}

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

Function: bv_utilst::signed_divider

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::signed_divider(const bvt &op0, const bvt &op1,
                               bvt &res, bvt &rem)
{
  if(op0.size()==0 || op1.size()==0) return;

  bvt _op0(op0), _op1(op1);

  literalt sign_0=_op0[_op0.size()-1];
  literalt sign_1=_op1[_op1.size()-1];

  bvt neg_0(_op0), neg_1(_op1);

  negate(neg_0);
  negate(neg_1);

  for(unsigned i=0; i<_op0.size(); i++)
    _op0[i]=(prop.lselect(sign_0, neg_0[i], _op0[i]));

  for(unsigned i=0; i<_op1.size(); i++)
    _op1[i]=(prop.lselect(sign_1, neg_1[i], _op1[i]));

  unsigned_divider(_op0, _op1, res, rem);

  bvt neg_result(res);
  negate(neg_result);

  literalt result_sign=prop.lxor(sign_0, sign_1);

  for(unsigned i=0; i<res.size(); i++)
    res[i]=prop.lselect(result_sign, neg_result[i], res[i]);
}

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

Function: bv_utilst::divider

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::divider(const bvt &op0, const bvt &op1,
                        bvt &res, bvt &rem, bool is_signed)
{
  assert(prop.has_set_to());

  if(is_signed)
    signed_divider(op0, op1, res, rem);
  else
    unsigned_divider(op0, op1, res, rem);
}

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

Function: bv_utilst::unsigned_divider

  Inputs:

 Outputs:

 Purpose:

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

void bv_utilst::unsigned_divider(const bvt &op0, const bvt &op1,
                                 bvt &res, bvt &rem)
{
  unsigned width=op0.size();

  // division by zero test

  literalt is_not_zero=prop.lor(op1);

  // free variables for result of division

  res.resize(width);
  rem.resize(width);
  for(unsigned i=0; i<width; i++)
  {
    res[i]=prop.new_literal();
    rem[i]=prop.new_literal();
  }

  // add implications

  bvt product;
  unsigned_multiplier_no_overflow(res, op1, product);

  // res*op1 + rem = op0

  bvt sum=product;

  adder_no_overflow(sum, rem);
  
  literalt is_equal=equal(sum, op0);

  prop.l_set_to_true(prop.limplies(is_not_zero, is_equal));

  // op1!=0 => rem < op1

  prop.l_set_to_true(
    prop.limplies(is_not_zero, lt_or_le(false, rem, op1, false)));

  // op1!=0 => res <= op0

  prop.l_set_to_true(
    prop.limplies(is_not_zero, lt_or_le(true, res, op0, false)));
}

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

Function: bv_utilst::equal

  Inputs:

 Outputs:

 Purpose:

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

literalt bv_utilst::equal(const bvt &op0, const bvt &op1)
{
  assert(op0.size()==op1.size());

  bvt equal_bv;
  equal_bv.resize(op0.size());

  for(unsigned i=0; i<op0.size(); i++)
    equal_bv[i]=prop.lequal(op0[i], op1[i]);

  return prop.land(equal_bv);
}

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

Function: bv_utilst::bv_if

  Inputs:

 Outputs:

 Purpose:

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

bvt bv_utilst::bv_if(const literalt op0, const bvt &op1, const bvt &op2)
{
  assert(op1.size()==op2.size());

  bvt bv;
 
  bv.resize(op1.size());

  for(unsigned i=0; i<bv.size(); i++)
    bv[i]=prop.lselect(op0, op1[i], op2[i]);

  return bv;
}

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

Function: bv_utilst::lt_or_le

  Inputs:

 Outputs:

 Purpose:

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

literalt bv_utilst::lt_or_le(bool or_equal,
                             const bvt &bv0,
                             const bvt &bv1,
                             bool is_signed)
{
  literalt top0=bv0[bv0.size()-1],
           top1=bv1[bv1.size()-1];

  literalt carry;
  bvt t_bv0(bv0);

  #if 0
  adder(t_bv0, bv1, true, prop.constant(true), carry, true);

  literalt literal;

  if(is_signed)
    literal=prop.lxor(prop.lequal(top0, top1), carry);
  else
    literal=prop.lnot(carry);

  if(or_equal)
    literal=lor(literal,
            prop.land(equal(bv0, bv1)));

  #else
  adder(t_bv0, bv1, true, prop.constant(true), carry, !or_equal);

  literalt literal;

  if(is_signed)
    literal=prop.lxor(prop.lequal(top0, top1), carry);
  else
    literal=prop.lnot(carry);

  if(or_equal)
    literal=prop.lor(literal, is_zero(t_bv0));
  #endif

  return literal;
}

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

Function: bv_utilst::is_constant

  Inputs:

 Outputs:

 Purpose:

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

bool bv_utilst::is_constant(const bvt &bv)
{
  for(unsigned i=0; i<bv.size(); i++)
    if(bv[i]!=prop.constant(false) && bv[i]!=prop.constant(true))
      return false;

  return true;
}
