// sc_float - class for floating point arithmetic in SystemC
// Do not use denormal numbers, not efficent for implementation, not implemented in division

#ifndef _SC_FLOAT_H_
#define _SC_FLOAT_H_

#include <systemc.h>

// define real number from interval <0.0, 1.0>  0 - low accuracy, 1 - high accuracy
#define _SC_FLOAT_MULT_ACCURACY_ 1.0    // recommended value 1.0
#define _SC_FLOAT_ADD_SUB_ACCURACY_ 0.0 // recommended value 0.0
#define _SC_FLOAT_DIV_ACCURACY_ 1.0     // recommended value 0.5 - relative error for single precision is about 1E-6

#define COMB_ARITH                      // operators are combinatorial logic - TODO how to use sequential logic arith?
//#define EN_DENORM_NUM                 // enable denormal numbers -- TODO - still some bugs in code

// enable/disable messages
#define c_DEBUG false                   // enable/disable debuging messages
#define c_WARNING false                 // enable/disable warning messages

//#include "sc_float_assign_macro.inc"

// namespace sc_dt - data types
//namespace sc_dt
//{
// classes defined in this module
//template <int E_width, int M_width> class sc_float;

template <int E_width = 8, int M_width = 23>       // float 32-bit IEEE 754
class sc_float
{ 
 public :
// constructor
  sc_float() {}

// private : // public is only for debugging
// internal data structure -in this order should be  numbers mapped right way 
//                          (as described below), if not, try to change order
   sc_uint<M_width> man;       // should be mapped to [M_width-1 : 0]	
   sc_uint<E_width> exp;       // should be mapped to [E_width+M_width-1 : M_width]
   bool sign;            // should be mapped ro [E_width+M_width]

 public :

// **************************************************************************//
// operators overloading : ==, !=, <, <=, >, >=, = , <<, sc_trace, *, /, +, -
// **************************************************************************//

//
// ************************** stream operator "<<" **************************//
// for writing sc_float into waves

 inline friend std::ostream& operator << ( std::ostream& os, const sc_float &v)
  {
   if (c_DEBUG) std::cout << "debug: operator << called " << endl; //debug
   // fixme - this is only copy of sc_float2double function
   double temp;
   temp = (double)v.man / exp2(M_width);
   temp += 1.0;
   temp *= exp2((double)v.exp - exp2((double)E_width - 1.0) + 1.0);
   temp *= (v.sign == true ? -1.0 : 1.0);
   //os << "(" << v.sign << " , " << v.exp << " , " << v.man << ")"; // debug
   os << temp;
  return os;
 }

//
// ************************* compare operator "==" **************************//
//  NOT ALL TESTED // TODO

 inline bool operator == (sc_float &rhs) // for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F1 " << endl; //DEBUG
   return(rhs.sign==sign && rhs.exp == exp && rhs.man == man);
  }

 inline bool operator == (const sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F2 " << endl; //DEBUG
   return(rhs.sign==sign && rhs.exp == exp && rhs.man == man);
  }

 inline bool operator == (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F3 " << endl; //DEBUG
   sc_float temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(temp.sign==sign && temp.exp == exp && temp.man == man);
  }

 inline bool operator == (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F4 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(temp.sign==sign && temp.exp == exp && temp.man == man);
  }

 inline bool operator == (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F5 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(temp.sign==sign && temp.exp == exp && temp.man == man);
  }

 inline bool operator == (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR == called : F6 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(temp.sign==sign && temp.exp == exp && temp.man == man);
  }

//
// ************************* compare operator "!=" **************************//
//  NOT ALL TESTED // TODO


 inline bool operator != (sc_float &rhs) // for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F1 " << endl; //DEBUG
   return(!(*this==rhs));
  }

 inline bool operator != (const sc_float &rhs) const
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F2 " << endl; //DEBUG
   return(!(*this==rhs));
  }

 inline bool operator != (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(!(*this==temp));
  }

 inline bool operator != (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F4 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(!(*this==temp));
  }

 inline bool operator != (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F5 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(!(*this==temp));
  }

 inline bool operator != (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR != called : F6 " << endl; //DEBUG

   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(!(*this==temp));
  }

//
// ************************** compare operator "<" **************************//
//  NOT ALL TESTED // TODO

 inline bool operator < (sc_float<E_width,M_width> &rhs) //for SYNTH
  {
   bool res;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F1 " << endl; //DEBUG
   if (sign == rhs.sign)
    {
     if (exp == rhs.exp)
      {
       res = sign? (man > rhs.man) :(man < rhs.man);       
      }
     else res = sign? (exp > rhs.exp) : (exp < rhs.exp);
    }
   else res = sign;
   return res;
  }

 inline bool operator < (const sc_float<E_width,M_width> &rhs) //for SYNTH
  {
   bool res;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F2 " << endl; //DEBUG
   if (sign == rhs.sign)
    {
     if (exp == rhs.exp)
      {
       res = sign? (man > rhs.man) :(man < rhs.man);       
      }
     else res = sign? (exp > rhs.exp) : (exp < rhs.exp);
    }
   else res = sign;
   return res;
  }

 inline bool operator < (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this < temp);
  }

 inline bool operator < (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this < temp);
  }

 inline bool operator < (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this < temp);
  }

 inline bool operator < (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR < called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this < temp);
  }

//
// ************************** compare operator "<=" **************************//
//  NOT ALL TESTED // TODO
 inline bool operator <= (sc_float &rhs)  //for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F1 " << endl; //DEBUG
   return ((*this==rhs) || (*this < rhs));
  }

 inline bool operator <= (const sc_float &rhs)  //for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F2 " << endl; //DEBUG
   return ((*this==rhs) || (*this < rhs));
  }

 inline bool operator <= (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this <= temp);
  }

 inline bool operator <= (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this <= temp);
  }

 inline bool operator <= (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this <= temp);
  }

 inline bool operator <= (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR <= called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this <= temp);
  }

//
// ************************** compare operator ">" **************************//
//  NOT ALL TESTED // TODO

 inline bool operator > (sc_float &rhs)  //for SYNTH
  {
   bool res;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F1 " << endl; //DEBUG
   if (sign == rhs.sign)
    {
     if (exp == rhs.exp)
      {
       res = sign? (man < rhs.man) :(man > rhs.man);       
      }
     else res = sign? (exp < rhs.exp) : (exp > rhs.exp);
    }
   else res = rhs.sign;
   return res;
  }
 inline bool operator > (const sc_float &rhs)  //for SYNTH
  {
   bool res;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F2 " << endl; //DEBUG
   if (sign == rhs.sign)
    {
     if (exp == rhs.exp)
      {
       res = sign? (man < rhs.man) :(man > rhs.man);       
      }
     else res = sign? (exp < rhs.exp) : (exp > rhs.exp);
    }
   else res = rhs.sign;
   return res;
  }

 inline bool operator > (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this > temp);
  }

 inline bool operator > (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this > temp);
  }

 inline bool operator > (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this > temp);
  }

 inline bool operator > (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR > called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this > temp);
  }

//
// ************************** compare operator ">=" **************************//
//  NOT ALL TESTED // TODO
 inline bool operator >= (sc_float &rhs)  //for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F1 " << endl; //DEBUG
   return ((*this==rhs) || (*this > rhs));
  }

 inline bool operator >= (const sc_float &rhs)  //for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F2 " << endl; //DEBUG
   return ((*this==rhs) || (*this > rhs));
  }

 inline bool operator >= (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this >= temp);
  }

 inline bool operator >= (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this >= temp);
  }

 inline bool operator >= (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this >= temp);
  }

 inline bool operator >= (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR >= called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return(*this >= temp);
  }

//
// ************************** assign operator "=" ***************************//
//

 inline sc_float& operator = ( sc_float &rhs)    // SYNTH  (?)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F1 " << endl; //DEBUG
   sign = rhs.sign;
   exp = rhs.exp;
   man = rhs.man;
   return *this;
  }

 inline sc_float& operator = ( const sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F2 " << endl; //DEBUG
   sign = rhs.sign;
   exp = rhs.exp;
   man = rhs.man;
   return *this;
  }

 inline sc_float& operator = ( const float &rhs)
 {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F3 " << endl; //DEBUG
   double2sc_float((double)rhs, sign, exp, man);
  return *this;
  }

 inline sc_float& operator = ( float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F4 " << endl; //DEBUG
   double2sc_float((double)rhs, sign, exp, man);
   return *this;
  }

// inline sc_float& operator = ( const double &rhs)
//  {
//   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F5 " << endl; //DEBUG
//   double2sc_float(rhs, sign, exp, man);
//   return *this;
//  }

 inline sc_float& operator = ( double &rhs)
 {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F6 " << endl; //DEBUG
   double2sc_float(rhs, sign, exp, man);
   return *this;
  }


#define INT2FLOAT_ASSIGN(BITWIDTH)                                            \
 inline sc_float& operator = ( sc_int<BITWIDTH> &rhs)                         \
  {                                                                           \
   sc_float<E_width,M_width> temp;                                                        \
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F7 " << endl;        \
   temp = int2sc_float<BITWIDTH>(rhs);                                        \
                                                                              \
   sign = temp.sign;                                                          \
   exp  = temp.exp;                                                           \
   man  = temp.man;                                                           \
   return *this;                                                              \
  }                                                                           \
                                                                              \
 inline sc_float& operator = (const sc_int<BITWIDTH> &rhs)                    \
  {                                                                           \
   sc_float<E_width,M_width> temp;                                                        \
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F8 " << endl;        \
   temp = int2sc_float<BITWIDTH>(rhs);                                        \
                                                                              \
   sign = temp.sign;                                                          \
   exp  = temp.exp;                                                           \
   man  = temp.man;                                                           \
   return *this;                                                              \
  }                                                                           \
 inline sc_float& operator = ( sc_uint<BITWIDTH> &rhs)                        \
  {                                                                           \
   sc_float<E_width,M_width> temp;                                                        \
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F9 " << endl;        \
   temp = uint2sc_float<BITWIDTH>(rhs);                                       \
                                                                              \
   sign = temp.sign;                                                          \
   exp  = temp.exp;                                                           \
   man  = temp.man;                                                           \
   return *this;                                                              \
  }                                                                           \
                                                                              \
 inline sc_float& operator = (const sc_uint<BITWIDTH> &rhs)                   \
  {                                                                           \
   sc_float<E_width,M_width> temp;                                                        \
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR = called : F10 " << endl;       \
   temp = uint2sc_float<BITWIDTH>(rhs);                                       \
                                                                              \
   sign = temp.sign;                                                          \
   exp  = temp.exp;                                                           \
   man  = temp.man;                                                           \
   return *this;                                                              \
  }                                                                           \

// assign operator must be specified for all sc_int/sc_uint bitwidth
INT2FLOAT_ASSIGN(1)  INT2FLOAT_ASSIGN(2)  INT2FLOAT_ASSIGN(3)  INT2FLOAT_ASSIGN(4)
INT2FLOAT_ASSIGN(5)  INT2FLOAT_ASSIGN(6)  INT2FLOAT_ASSIGN(7)  INT2FLOAT_ASSIGN(8)
INT2FLOAT_ASSIGN(9)  INT2FLOAT_ASSIGN(10) INT2FLOAT_ASSIGN(11) INT2FLOAT_ASSIGN(12)
INT2FLOAT_ASSIGN(13) INT2FLOAT_ASSIGN(14) INT2FLOAT_ASSIGN(15) INT2FLOAT_ASSIGN(16)
INT2FLOAT_ASSIGN(17) INT2FLOAT_ASSIGN(18) INT2FLOAT_ASSIGN(19) INT2FLOAT_ASSIGN(20)
INT2FLOAT_ASSIGN(21) INT2FLOAT_ASSIGN(22) INT2FLOAT_ASSIGN(23) INT2FLOAT_ASSIGN(24)
INT2FLOAT_ASSIGN(25) INT2FLOAT_ASSIGN(26) INT2FLOAT_ASSIGN(27) INT2FLOAT_ASSIGN(28)
INT2FLOAT_ASSIGN(29) INT2FLOAT_ASSIGN(30) INT2FLOAT_ASSIGN(31) INT2FLOAT_ASSIGN(32)
INT2FLOAT_ASSIGN(33) INT2FLOAT_ASSIGN(34) INT2FLOAT_ASSIGN(35) INT2FLOAT_ASSIGN(36)
INT2FLOAT_ASSIGN(37) INT2FLOAT_ASSIGN(38) INT2FLOAT_ASSIGN(39) INT2FLOAT_ASSIGN(40)
INT2FLOAT_ASSIGN(41) INT2FLOAT_ASSIGN(42) INT2FLOAT_ASSIGN(43) INT2FLOAT_ASSIGN(44)
INT2FLOAT_ASSIGN(45) INT2FLOAT_ASSIGN(46) INT2FLOAT_ASSIGN(47) INT2FLOAT_ASSIGN(48)
INT2FLOAT_ASSIGN(49) INT2FLOAT_ASSIGN(50) INT2FLOAT_ASSIGN(51) INT2FLOAT_ASSIGN(52)
INT2FLOAT_ASSIGN(53) INT2FLOAT_ASSIGN(54) INT2FLOAT_ASSIGN(55) INT2FLOAT_ASSIGN(56)
INT2FLOAT_ASSIGN(57) INT2FLOAT_ASSIGN(58) INT2FLOAT_ASSIGN(59) INT2FLOAT_ASSIGN(60)
INT2FLOAT_ASSIGN(61) INT2FLOAT_ASSIGN(62) INT2FLOAT_ASSIGN(63) INT2FLOAT_ASSIGN(64)
/*
#ifdef SIM
#define ASSIGN(SIG,CONS)         \
 SIG = CONS;                     \

#else 
#define ASSIGN(SIG,CONS)         \
 SIG.sign = CONS.sign;           \
 SIG.exp = CONS.exp;             \
 SIG.man = CONS.man;             \

#endif
*/

//
// ************** floating point multiplication - operator "*" **************//
// function for multiplication of two sc_float are in file sc_float_arith.inc

 inline sc_float operator * (sc_float &rhs) // for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F1 " << endl; //DEBUG
   return multiply_exceptions(*this,rhs);
  }

 inline sc_float operator * (const sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F2 " << endl; //DEBUG
   return multiply_exceptions(*this,rhs);
  }

 inline sc_float operator * (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);
   return multiply_exceptions(*this,temp);
  }

 inline sc_float operator * (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);
   return multiply_exceptions(*this,temp);
  }

 inline sc_float operator * (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return multiply_exceptions(*this,temp);
  }

 inline sc_float operator * (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR * called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return multiply_exceptions(*this,temp);
  }

//
// **************** floating point division - operator "/" ***************//
// function for dividing of two sc_float are in file sc_float_arith.inc

 inline sc_float operator / (sc_float &rhs) // for SYNTH
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F1 " << endl; //DEBUG
   return divide_exceptions(*this,rhs);  // with exceptions
  }

 inline sc_float operator / (const sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F2 " << endl; //DEBUG
   return divide_exceptions(*this,rhs);
  }

 inline sc_float operator / (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);
   return divide_exceptions(*this,temp);
  }

 inline sc_float operator / (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);
   return divide_exceptions(*this,temp);
  }

 inline sc_float operator / (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return divide_exceptions(*this,temp);
  }

 inline sc_float operator / (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR / called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);
   return divide_exceptions(*this,temp);
  }

//
// ***************** floating point addition - operator "+" *****************//
// function for addition of two sc_float are in file sc_float_arith.inc

 inline sc_float operator + (sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F1 " << endl; //DEBUG   
   return add_sub_exceptions(*this, rhs);
  }

 inline sc_float operator + (const sc_float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F2 " << endl; //DEBUG
   return add_sub_exceptions(*this, rhs);
  }

 inline sc_float operator + (float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F3 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);   
   return add_sub_exceptions(*this, temp);
  }

 inline sc_float operator + (const float &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F4 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float((double)rhs, temp.sign, temp.exp, temp.man);   
   return add_sub_exceptions(*this, temp);
  }

 inline sc_float operator + (double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F5 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);   
   return add_sub_exceptions(*this, temp);
  }

 inline sc_float operator + (const double &rhs)
  {
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR + called : F6 " << endl; //DEBUG
   sc_float<E_width,M_width> temp;
   double2sc_float(rhs, temp.sign, temp.exp, temp.man);   
   return add_sub_exceptions(*this, temp);
  }

//
// *************** floating point substraction - operator "-" ***************//
// function for substraction of two sc_float are in file sc_float_arith.inc

 inline sc_float operator - (sc_float &rhs) // for SYNTH
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F1 " << endl; //DEBUG
   rhs_t = rhs;
   rhs_t.sign = !rhs.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }

 inline sc_float operator - (const sc_float &rhs)
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F2 " << endl; //DEBUG
   rhs_t = rhs;
   rhs_t.sign = !rhs.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }

 inline sc_float operator - (float &rhs)
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F3 " << endl; //DEBUG
   double2sc_float((double)rhs, rhs_t.sign, rhs_t.exp, rhs_t.man);   
   rhs_t.sign = !rhs_t.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }

 inline sc_float operator - (const float &rhs)
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F4 " << endl; //DEBUG
   double2sc_float((double)rhs, rhs_t.sign, rhs_t.exp, rhs_t.man);   
   rhs_t.sign = !rhs_t.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }

 inline sc_float operator - (double &rhs)
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F5 " << endl; //DEBUG
   double2sc_float(rhs, rhs_t.sign, rhs_t.exp, rhs_t.man);   
   rhs_t.sign = !rhs_t.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }

 inline sc_float operator - (const double &rhs)
  {
   sc_float<E_width,M_width> temp, rhs_t;
   if (c_DEBUG) std::cout << "DEBUG: OPERATOR - called : F6 " << endl; //DEBUG
   double2sc_float(rhs, rhs_t.sign, rhs_t.exp, rhs_t.man);   
   rhs_t.sign = !rhs_t.sign;
   temp = add_sub_exceptions(*this, rhs_t);   
   return temp;
  }


//
// *************************** function sc_trace ****************************//
// TODO - not tested

 inline friend void sc_trace (sc_trace_file *tf, const sc_float &v, const std::string &NAME)
  {
   if (c_DEBUG) std::cout << "DEBUG: FUNCTION sc_trace called" << endl; //DEBUG
   sc_trace(tf,v.sign, NAME + ".sign");
   sc_trace(tf,v.exp, NAME + ".exp");
   sc_trace(tf,v.man, NAME + ".man");
  }


// ************************************************************************* //
// function for operation with floating point
// ************************************************************************* //

  void write_sign(bool s)
   {
    sign = s; 
   }
  bool get_sign()
   {
    return sign; 
   }

  void write_exp(sc_uint<E_width> e)
   {
    exp = e; 
   }
  void write_exp(sc_uint<E_width> e, int MSB, int LSB)
   {
    exp.range(MSB,LSB) = e; 
   }
  void write_exp(sc_uint<E_width> e, int POS)
   {
    exp.range(POS,POS) = e; 
   }
  sc_uint<E_width> get_exp()
   {
    return exp; 
   }
  int get_exp(int MSB, int LSB)
   {
    return exp.range(MSB,LSB); 
   }
  int get_exp(int POS)
   {
    return exp.range(POS,POS); 
   }
  int get_exp_size()
   {
    return E_width; 
   }

  void write_man(sc_uint<M_width> m)
   {
    man = m; 
   }
  void write_man(sc_uint<M_width> m, int POS)
   {
    man.range(POS,POS) = m; 
   }
  void write_man(sc_uint<M_width> m, int MSB, int LSB)
   {
    man.range(MSB,LSB) = m; 
   }
  sc_uint<M_width> get_man()
   {
    return man; 
   }
  int get_man(int MSB, int LSB)
   {
    return man.range(MSB,LSB); 
   }
  int get_man_size()
   {
    return M_width; 
   }


 private :
// include file with synthetisable aritmetic operations
#include "sc_float_arith.inc"

//
// ********************** convert double to sc_float *********************** //
//

  sc_float double2sc_float (double fp)
   {
    sc_float<E_width,M_width>  temp;
    if (c_DEBUG)
     std::cout << "DEBUG : function double2sc_float 1 called with val" << fp << endl;
    double2sc_float(fp, temp.sign, temp.exp, temp.man);
    return temp; // FIXME
   }
//#define c_DEBUG true
  void double2sc_float (double fp, bool& _sign, sc_uint<E_width>& _exp, sc_uint<M_width>& _man)
   {
    int step = 0;
    unsigned long long  exp2_tmp;
    double temp;
    unsigned int exponent;
    sc_biguint<M_width> mantisa;
//Vis.Studio error C2124 c++ ... literal 0.0 cause problems!
double zero = 0.0; 
///////////////////

    if (c_DEBUG)
     std::cout << "DEBUG : function double2sc_float 2 called with val " << fp << endl;

   // TODO - if denormal
   // if NaN - according to definition, NaN != NaN
    if (fp != fp)
     {
      if (c_WARNING)
       std::cout << "WARNING : function double2sc_float : During conversion of number "
                 << "into sc_float, NaN appeared. It is not recommended to use NaN " 
                 << "as input number for sc_float class, the only one possible result "
                 << "of all operations is NaN. NaN converted into sc_float" << endl;
      exponent = (1 << E_width) - 1;
      mantisa = 1;
     }
   // if inf
//Vis.Studio error C2124 c++ ... literal 0.0 cause problems!
    else if (fp == (1.0/zero) || fp == -(1.0/zero))
////////////////////////////////////////////////////

//CTOS 0.0 No problem
 //    else if (fp == (1.0/0.0) || fp == -(1.0/0.0))
//////////////////////////////////////////////////
     {
      if (c_WARNING)
       std::cout << "WARNING : function double2sc_float : During conversion of number "
                 << "into sc_float, infinity appeared. Infinity converted into sc_float" << endl;
      exponent = (1 << E_width) - 1;
      mantisa = 0;
     }
   // if zero
    else if (fp == 0.0 || fp == -0.0)
     {
      if (c_DEBUG)
       std::cout << "DEBUG : function double2sc_float 2 : zero appeared " << fp << endl;  
      exponent = 0;
      mantisa = 0;
     }
    // if normal number
    else
     {
      temp = fp < 0 ? fp * -1.0 : fp;   // abs value

      // count offset
      exp2_tmp = 1;
      for (int i = 1; i < E_width; ++i){ exp2_tmp *= 2; } 

      exponent = exp2_tmp - 1;
//      exponent = (int)(exp2((double)E_width-1.0)) - 1;  
      mantisa = 0;

      // count full exponent
      exp2_tmp = 1;
      for (int i = 0; i < E_width; ++i){ exp2_tmp *= 2; } 

   // get normalized value
      while (temp>=2.0)
       {
        temp /= 2.0;
        exponent++;
//        if (exponent == (int)(exp2(E_width))-1)
        if (exponent == exp2_tmp-1)
         {
          if (c_WARNING)
           std::cout << "WARNING : During conversion of number " << fp << " into sc_float, "
                     << "exponent overflows and result is infinity! If the overflow of exponent " 
                     << "should not apear, use sc_float with bigger exponent. " << endl;
          break;
         }
       }
      while (temp<=1.0)
       {
        temp *= 2.0;
        exponent--;
        if (exponent == 0)
         {
          if (c_WARNING)
           std::cout << "WARNING : During conversion of number " << fp << " into sc_float, "
                     << "exponent underflows and result is zero! This could be caused by using "
                     << "the denormal numbers or too small exponent of sc_float object. "
                     << "If the result should not be zero, use sc_float with bigger exponent. " << endl;
          break;
         }
       }

      temp -= 1.0;

   // compute mantisa
      while (step < M_width)
       {
        temp*=2;
        if (temp>=1.0)  
         {
          // count bit in mantisa
          exp2_tmp = 1;
          for (int i = 1; i < M_width-step; ++i){ exp2_tmp *= 2; } 
          mantisa += exp2_tmp;
//          mantisa += (long long)(exp2(M_width-step-1));
          temp -= 1.0;
         }
        step++;
       }

     }

 // assign values
      _sign = fp <= 0 ? true : false;
      _exp = exponent;
      _man = mantisa;
   } 

//
// ********************** convert sc_float to double *********************** //
//

 public :
  double sc_float2double()
   {
   if (c_DEBUG)
    std::cout << "DEBUG : function sc_float2double 1 called with val " <<endl;
    return sc_float2double(*this);
   }

  double sc_float2double (sc_float<E_width,M_width> fp) const
  {
   if (c_DEBUG)
   std::cout << "DEBUG : function sc_float2double 2 called with val " <<endl;
   double temp;
//Vis.Studio error C2124 c++ ... literal 0.0 cause problems!
   double zero = 0.0;	

   if (is_NaN(fp)) temp = (0.0/zero);
   else if (is_inf(fp)) temp = (1.0/zero);
   else if (is_zero(fp)) temp = zero;
///////////////////////////////////////////

///CTOS 
//   if (is_NaN(fp)) temp = (0.0/0.0);
//   else if (is_inf(fp)) temp = (1.0/0.0);
//  else if (is_zero(fp)) temp = 0.0;
////////////////////////////////////////
   else
    {
     temp = (double)fp.man / exp2(M_width);
     temp += 1.0;
     temp *= exp2((double)fp.exp - exp2((double)E_width - 1.0) + 1.0);
    }
   temp *= (fp.sign == true ? -1.0 : 1.0);
   return temp; 
  }
#define c_DEBUG false

//
// **************** change sign - avoid to use operator - ****************** //
//
/*
 public :
  sc_float change_sign()
   {
    if (c_DEBUG)
     std::cout << "DEBUG : function change_sign called " <<endl;
    sign = sign ? false : true;
   }
*/
};

//}  // namespace sc_dt

#endif
