/*
 * Copyright 2011-2019 Xilinx, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __AP_FIXED_BASE_H__
#define __AP_FIXED_BASE_H__

#ifndef __AP_FIXED_H__
#error "Only ap_fixed.h and ap_int.h can be included directly in user code."
#endif

// for ap_int_base and its reference types.
#include <ap_int.h>
#ifndef __SYNTHESIS__
#if _AP_ENABLE_HALF_ == 1
// for half type
#include <hls_half.h>
#endif
// for std io
#include <iostream>
#endif

#ifndef __cplusplus
#error "C++ is required to include this header file"
#else // __cplusplus

// for warning on unsupported rounding mode in conversion to float/double.
#if !defined(__SYNTHESIS__) && __cplusplus >= 201103L && \
    (defined(__gnu_linux__) || defined(_WIN32))
#define AP_FIXED_ENABLE_CPP_FENV 1
#include <cfenv>
#endif

// ----------------------------------------------------------------------

/* Major TODO
  long double support: constructor, assign and other operators.
  binary operators with ap_fixed_base and const char*.
  return ap_fixed/ap_ufixed when result signedness is known.
*/

// Helper function in conversion to floating point types.

#ifdef __SYNTHESIS__
#define _AP_ctype_op_get_bit(var, index) _AP_ROOT_op_get_bit(var, index)
#define _AP_ctype_op_set_bit(var, index, x) _AP_ROOT_op_set_bit(var, index, x)
#define _AP_ctype_op_get_range(var, low, high) \
  _AP_ROOT_op_get_range(var, low, high)
#define _AP_ctype_op_set_range(var, low, high, x) \
  _AP_ROOT_op_set_range(var, low, high, x)
#else // ifdef __SYNTHESIS__
template <typename _Tp1, typename _Tp2>
inline bool _AP_ctype_op_get_bit(_Tp1& var, const _Tp2& index) {
  return !!(var & (1ull << (index)));
}
template <typename _Tp1, typename _Tp2, typename _Tp3>
inline _Tp1 _AP_ctype_op_set_bit(_Tp1& var, const _Tp2& index, const _Tp3& x) {
  var |= (((x) ? 1ull : 0ull) << (index));
  return var;
}
template <typename _Tp1, typename _Tp2, typename _Tp3>
inline _Tp1 _AP_ctype_op_get_range(_Tp1& var, const _Tp2& low,
                                   const _Tp3& high) {
  _Tp1 r = var;
  ap_ulong mask = -1ll;
  mask >>= (sizeof(_Tp1) * 8 - ((high) - (low) + 1));
  r >>= (low);
  r &= mask;
  return r;
}
template <typename _Tp1, typename _Tp2, typename _Tp3, typename _Tp4>
inline _Tp1 _AP_ctype_op_set_range(_Tp1& var, const _Tp2& low, const _Tp3& high,
                                   const _Tp4& x) {
  ap_ulong mask = -1ll;
  mask >>= (_AP_SIZE_ap_slong - ((high) - (low) + 1));
  var &= ~(mask << (low));
  var |= ((mask & x) << (low));
  return var;
}
#endif // ifdef __SYNTHESIS__


// trait for letting base class to return derived class.
// Notice that derived class template is incomplete, and we cannot use
// the member of the derived class.
template <int _AP_W2, int _AP_I2, bool _AP_S2>
struct _ap_fixed_factory;
template <int _AP_W2, int _AP_I2>
struct _ap_fixed_factory<_AP_W2, _AP_I2, true> {
  typedef ap_fixed<_AP_W2, _AP_I2> type;
};
template <int _AP_W2, int _AP_I2>
struct _ap_fixed_factory<_AP_W2, _AP_I2, false> {
  typedef ap_ufixed<_AP_W2, _AP_I2> type;
};

/// ap_fixed_base: AutoPilot fixed point.
/** partial specialization of signed.
  @tparam _AP_W width.
  @tparam _AP_I integral part width.
  @tparam _AP_S signed.
  @tparam _AP_Q quantization mode. Default is AP_TRN.
  @tparam _AP_O saturation mode. Default is AP_WRAP.
  @tparam _AP_N saturation wrap value. Default is 0.
 */
// default for _AP_Q, _AP_O and _AP_N set in ap_decl.h
template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
struct ap_fixed_base : _AP_ROOT_TYPE<_AP_W, _AP_S> {
 public:
  typedef _AP_ROOT_TYPE<_AP_W, _AP_S> Base;
  static const int width = _AP_W;
  static const int iwidth = _AP_I;
  static const ap_q_mode qmode = _AP_Q;
  static const ap_o_mode omode = _AP_O;

  /// Return type trait.
  template <int _AP_W2, int _AP_I2, bool _AP_S2>
  struct RType {
    enum {
      _AP_F = _AP_W - _AP_I,
      F2 = _AP_W2 - _AP_I2,
      mult_w = _AP_W + _AP_W2,
      mult_i = _AP_I + _AP_I2,
      mult_s = _AP_S || _AP_S2,
      plus_w = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) +
               1 + AP_MAX(_AP_F, F2),
      plus_i =
          AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1,
      plus_s = _AP_S || _AP_S2,
      minus_w =
          AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1 +
          AP_MAX(_AP_F, F2),
      minus_i =
          AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1,
      minus_s = true,
#ifndef __SC_COMPATIBLE__
      div_w = _AP_S2 + _AP_W + AP_MAX(F2, 0),
#else
      div_w = _AP_S2 + _AP_W + AP_MAX(F2, 0) + AP_MAX(_AP_I2, 0),
#endif
      div_i = _AP_S2 + _AP_I + F2,
      div_s = _AP_S || _AP_S2,
      logic_w =
          AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) +
          AP_MAX(_AP_F, F2),
      logic_i = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)),
      logic_s = _AP_S || _AP_S2
    };

    typedef ap_fixed_base<_AP_W, _AP_I, _AP_S> lhs;
    typedef ap_fixed_base<_AP_W2, _AP_I2, _AP_S2> rhs;

    typedef ap_fixed_base<mult_w, mult_i, mult_s> mult_base;
    typedef ap_fixed_base<plus_w, plus_i, plus_s> plus_base;
    typedef ap_fixed_base<minus_w, minus_i, minus_s> minus_base;
    typedef ap_fixed_base<logic_w, logic_i, logic_s> logic_base;
    typedef ap_fixed_base<div_w, div_i, div_s> div_base;
    typedef ap_fixed_base<_AP_W, _AP_I, _AP_S> arg1_base;

    typedef typename _ap_fixed_factory<mult_w, mult_i, mult_s>::type mult;
    typedef typename _ap_fixed_factory<plus_w, plus_i, plus_s>::type plus;
    typedef typename _ap_fixed_factory<minus_w, minus_i, minus_s>::type minus;
    typedef typename _ap_fixed_factory<logic_w, logic_i, logic_s>::type logic;
    typedef typename _ap_fixed_factory<div_w, div_i, div_s>::type div;
    typedef typename _ap_fixed_factory<_AP_W, _AP_I, _AP_S>::type arg1;
  };

 private:
#ifndef __SYNTHESIS__
  // This cannot handle hex float format string.
  void fromString(const std::string& val, unsigned char radix) {
    _AP_ERROR(!(radix == 2 || radix == 8 || radix == 10 || radix == 16),
              "ap_fixed_base::fromString(%s, %d)", val.c_str(), radix);

    Base::V = 0;
    int startPos = 0;
    int endPos = val.length();
    int decPos = val.find(".");
    if (decPos == -1) decPos = endPos;

    // handle sign
    bool isNegative = false;
    if (val[0] == '-') {
      isNegative = true;
      ++startPos;
    } else if (val[0] == '+')
      ++startPos;

    // If there are no integer bits, e.g.:
    // .0000XXXX, then keep at least one bit.
    // If the width is greater than the number of integer bits, e.g.:
    // XXXX.XXXX, then we keep the integer bits
    // if the number of integer bits is greater than the width, e.g.:
    // XXX000 then we keep the integer bits.
    // Always keep one bit.
    ap_fixed_base<AP_MAX(_AP_I, 4) + 4, AP_MAX(_AP_I, 4) + 4, false>
        integer_bits = 0;

    // Figure out if we can shift instead of multiply
    unsigned shift = (radix == 16 ? 4 : radix == 8 ? 3 : radix == 2 ? 1 : 0);

    //std::cout << "\n\n" << val << "\n";
    //std::cout << startPos << " " << decPos << " " << endPos << "\n";

    bool sticky_int = false;

    // Traverse the integer digits from the MSD, multiplying by radix as we go.
    for (int i = startPos; i < decPos; i++) {
      // Get a digit
      char cdigit = val[i];
      if (cdigit == '\0') continue;
      unsigned digit = ap_private_ops::decode_digit(cdigit, radix);

      sticky_int |= integer_bits[AP_MAX(_AP_I, 4) + 4 - 1] |
                    integer_bits[AP_MAX(_AP_I, 4) + 4 - 2] |
                    integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] |
                    integer_bits[AP_MAX(_AP_I, 4) + 4 - 4];
      // Shift or multiply the value by the radix
      if (shift)
        integer_bits <<= shift;
      else
        integer_bits *= radix;

      // Add in the digit we just interpreted
      integer_bits += digit;
      //std::cout << "idigit = " << digit << " " << integer_bits.to_string()
      //    << "  " << sticky_int <<  "\n";
    }
    integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] =
        integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] | sticky_int;

    ap_fixed_base<AP_MAX(_AP_W - _AP_I, 0) + 4 + 4, 4, false> fractional_bits = 0;
    bool sticky = false;

    // Traverse the fractional digits from the LSD, dividing by radix as we go.
    for (int i = endPos - 1; i >= decPos + 1; i--) {
      // Get a digit
      char cdigit = val[i];
      if (cdigit == '\0') continue;
      unsigned digit = ap_private_ops::decode_digit(cdigit, radix);
      // Add in the digit we just interpreted
      fractional_bits += digit;

      sticky |= fractional_bits[0] | fractional_bits[1] | fractional_bits[2] |
                fractional_bits[3];
      // Shift or divide the value by the radix
      if (shift)
        fractional_bits >>= shift;
      else
        fractional_bits /= radix;

      //std::cout << "fdigit = " << digit << " " << fractional_bits.to_string()
      //    << " " << sticky << "\n";
    }

    //std::cout << "Int =" << integer_bits.to_string() << " " <<
    //    fractional_bits.to_string() << "\n";

    fractional_bits[0] = fractional_bits[0] | sticky;

    if (isNegative)
      *this = -(integer_bits + fractional_bits);
    else
      *this = integer_bits + fractional_bits;

    //std::cout << "end = " << this->to_string(16) << "\n";
  }

  /// report invalid constrction of ap_fixed_base
  INLINE void report() {
    if (!_AP_S && _AP_O == AP_WRAP_SM) {
      fprintf(stderr, "ap_ufxied<...> cannot support AP_WRAP_SM.\n");
      exit(1);
    }
    if (_AP_W > MAX_MODE(AP_INT_MAX_W) * 1024) {
      fprintf(stderr,
              "[E] ap_%sfixed<%d, ...>: Bitwidth exceeds the "
              "default max value %d. Please use macro "
              "AP_INT_MAX_W to set a larger max value.\n",
              _AP_S ? "" : "u", _AP_W, MAX_MODE(AP_INT_MAX_W) * 1024);
      exit(1);
    }
  }
#else
  INLINE void report() {}
#endif // ifdef __SYNTHESIS__

  /// @name helper functions.
  //  @{
  INLINE void overflow_adjust(bool underflow, bool overflow, bool lD,
                              bool sign) {
    if (!underflow && !overflow) return;
    if (_AP_O == AP_WRAP) {
      if (_AP_N == 0) return;
      if (_AP_S) {
        // signed AP_WRAP
        // n_bits == 1
        Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, sign);
        if (_AP_N > 1) {
          // n_bits > 1
          ap_int_base<_AP_W, false> mask(-1);
          if (sign) mask.V = 0;
          Base::V =
              _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 2, mask.V);
        }
      } else {
        // unsigned AP_WRAP
        ap_int_base<_AP_W, false> mask(-1);
        Base::V =
            _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 1, mask.V);
      }
    } else if (_AP_O == AP_SAT_ZERO) {
      Base::V = 0;
    } else if (_AP_O == AP_WRAP_SM && _AP_S) {
      bool Ro = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1);
      if (_AP_N == 0) {
        if (lD != Ro) {
          Base::V = ~Base::V;
          Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, lD);
        }
      } else {
        if (_AP_N == 1 && sign != Ro) {
          Base::V = ~Base::V;
        } else if (_AP_N > 1) {
          bool lNo = _AP_ROOT_op_get_bit(Base::V, _AP_W - _AP_N);
          if (lNo == sign) Base::V = ~Base::V;
          ap_int_base<_AP_W, false> mask(-1);
          if (sign) mask.V = 0;
          Base::V =
              _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 2, mask.V);
        }
        Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, sign);
      }
    } else {
      if (_AP_S) {
        if (overflow) {
          Base::V = 1;
          Base::V <<= _AP_W - 1;
          Base::V = ~Base::V;
        } else if (underflow) {
          Base::V = 1;
          Base::V <<= _AP_W - 1;
          if (_AP_O == AP_SAT_SYM) Base::V |= 1;
        }
      } else {
        if (overflow)
          Base::V = ~(ap_int_base<_AP_W, false>(0).V);
        else if (underflow)
          Base::V = 0;
      }
    }
  }

  INLINE bool quantization_adjust(bool qb, bool r, bool s) {
    bool carry = (bool)_AP_ROOT_op_get_bit(Base::V, _AP_W - 1);
    if (_AP_Q == AP_TRN) return false;
    if (_AP_Q == AP_RND_ZERO)
      qb &= s || r;
    else if (_AP_Q == AP_RND_MIN_INF)
      qb &= r;
    else if (_AP_Q == AP_RND_INF)
      qb &= !s || r;
    else if (_AP_Q == AP_RND_CONV)
      qb &= _AP_ROOT_op_get_bit(Base::V, 0) || r;
    else if (_AP_Q == AP_TRN_ZERO)
      qb = s && (qb || r);
    Base::V += qb;
    return carry && (!(bool)_AP_ROOT_op_get_bit(Base::V, _AP_W - 1));
  }
  //  @}

 public:
  /// @name constructors.
  //  @{
  /// default ctor.
  INLINE ap_fixed_base() {}

  /// copy ctor.
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
    operator=(op);
    report();
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base(
      const volatile ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
    operator=(op);
    report();
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base(const ap_int_base<_AP_W2, _AP_S2>& op) {
    ap_fixed_base<_AP_W2, _AP_W2, _AP_S2> tmp;
    tmp.V = op.V;
    operator=(tmp);
    report();
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base(const volatile ap_int_base<_AP_W2, _AP_S2>& op) {
    ap_fixed_base<_AP_W2, _AP_W2, _AP_S2> tmp;
    tmp.V = op.V;
    operator=(tmp);
    report();
  }

#ifndef __SYNTHESIS__
#ifndef NON_C99STRING
  INLINE ap_fixed_base(const char* s, signed char rd = 0) {
    unsigned char radix = rd;
    std::string str = ap_private_ops::parseString(s, radix); // will guess rd, default 10
    _AP_ERROR(radix == 0, "ap_fixed_base(const char* \"%s\", %d), str=%s, radix = %d",
              s, rd, str.c_str(), radix); // TODO remove this check
    fromString(str, radix);
  }
#else
  INLINE ap_fixed_base(const char* s, signed char rd = 10) {
    ap_int_base<_AP_W, _AP_S> t(s, rd);
    Base::V = t.V;
  }
#endif // ifndef NON_C99STRING
#else // ifndef __SYNTHESIS__
  // XXX _ssdm_string2bits only takes const string and const radix.
  // It seems XFORM will do compile time processing of the string.
  INLINE ap_fixed_base(const char* s) {
    typeof(Base::V) t;
    _ssdm_string2bits((void*)(&t), (const char*)(s), 10, _AP_I, _AP_S, _AP_Q,
                      _AP_O, _AP_N, _AP_C99);
    Base::V = t;
  }
  INLINE ap_fixed_base(const char* s, signed char rd) {
    typeof(Base::V) t;
    _ssdm_string2bits((void*)(&t), (const char*)(s), rd, _AP_I, _AP_S, _AP_Q,
                      _AP_O, _AP_N, _AP_C99);
    Base::V = t;
  }
#endif // ifndef __SYNTHESIS__ else

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base(const ap_bit_ref<_AP_W2, _AP_S2>& op) {
    *this = ((bool)op);
    report();
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base(const ap_range_ref<_AP_W2, _AP_S2>& op) {
    *this = (ap_int_base<_AP_W2, false>(op));
    report();
  }

  template <int _AP_W2, typename _AP_T2, int _AP_W3, typename _AP_T3>
  INLINE ap_fixed_base(
      const ap_concat_ref<_AP_W2, _AP_T2, _AP_W3, _AP_T3>& op) {
    *this = (ap_int_base<_AP_W2 + _AP_W3, false>(op));
    report();
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base(
      const af_bit_ref<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
    *this = (bool(op));
    report();
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base(
      const af_range_ref<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
    *this = (ap_int_base<_AP_W2, false>(op));
    report();
  }

  // ctors from c types.
  // make a temp ap_fixed_base first, and use ap_fixed_base.operator=
#define CTOR_FROM_INT(C_TYPE, _AP_W2, _AP_S2)        \
  INLINE ap_fixed_base(const C_TYPE x) {             \
    ap_fixed_base<(_AP_W2), (_AP_W2), (_AP_S2)> tmp; \
    tmp.V = x;                                       \
    *this = tmp;                                     \
  }

  CTOR_FROM_INT(bool, 1, false)
  CTOR_FROM_INT(char, 8, CHAR_IS_SIGNED)
  CTOR_FROM_INT(signed char, 8, true)
  CTOR_FROM_INT(unsigned char, 8, false)
  CTOR_FROM_INT(short, _AP_SIZE_short, true)
  CTOR_FROM_INT(unsigned short, _AP_SIZE_short, false)
  CTOR_FROM_INT(int, _AP_SIZE_int, true)
  CTOR_FROM_INT(unsigned int, _AP_SIZE_int, false)
  CTOR_FROM_INT(long, _AP_SIZE_long, true)
  CTOR_FROM_INT(unsigned long, _AP_SIZE_long, false)
  CTOR_FROM_INT(ap_slong, _AP_SIZE_ap_slong, true)
  CTOR_FROM_INT(ap_ulong, _AP_SIZE_ap_slong, false)
#undef CTOR_FROM_INT
/*
 * TODO:
 *Theere used to be several funtions which were AP_WEAK.
 *Now they're all INLINE expect ap_fixed_base(double d)
 *Maybe we can use '#pragma HLS inline' instead of INLINE.
 */
  AP_WEAK ap_fixed_base(double d) {
    ap_int_base<64, false> ireg;
    ireg.V = doubleToRawBits(d);
    bool isneg = _AP_ROOT_op_get_bit(ireg.V, 63);

    ap_int_base<DOUBLE_EXP + 1, true> exp;
    ap_int_base<DOUBLE_EXP, false> exp_tmp;
    exp_tmp.V =
        _AP_ROOT_op_get_range(ireg.V, DOUBLE_MAN, DOUBLE_MAN + DOUBLE_EXP - 1);
    exp = exp_tmp - DOUBLE_BIAS;
    ap_int_base<DOUBLE_MAN + 2, true> man;
    man.V = _AP_ROOT_op_get_range(ireg.V, 0, DOUBLE_MAN - 1);
    // do not support NaN
    _AP_WARNING(exp == APFX_IEEE_DOUBLE_E_MAX + 1 && man.V != 0,
                "assign NaN to fixed point value");
    man.V = _AP_ROOT_op_set_bit(man.V, DOUBLE_MAN, 1);
    if (isneg) man = -man;
    if ((ireg.V & 0x7fffffffffffffffLL) == 0) {
      Base::V = 0;
    } else {
      int _AP_W2 = DOUBLE_MAN + 2, _AP_I2 = exp.V + 2, _AP_F = _AP_W - _AP_I,
          F2 = _AP_W2 - _AP_I2;
      bool _AP_S2 = true,
           QUAN_INC = F2 > _AP_F &&
                      !(_AP_Q == AP_TRN || (_AP_Q == AP_TRN_ZERO && !_AP_S2));
      bool carry = false;
      // handle quantization
      unsigned sh_amt = (F2 > _AP_F) ? F2 - _AP_F : _AP_F - F2;
      if (F2 == _AP_F)
        Base::V = man.V;
      else if (F2 > _AP_F) {
        if (sh_amt < DOUBLE_MAN + 2)
          Base::V = man.V >> sh_amt;
        else {
          Base::V = isneg ? -1 : 0;
        }
        if ((_AP_Q != AP_TRN) && !((_AP_Q == AP_TRN_ZERO) && !_AP_S2)) {
          bool qb = (F2 - _AP_F > _AP_W2) ? isneg : (bool)_AP_ROOT_op_get_bit(
                                                        man.V, F2 - _AP_F - 1);
          bool r =
              (F2 > _AP_F + 1)
                  ? _AP_ROOT_op_get_range(man.V, 0, (F2 - _AP_F - 2 < _AP_W2)
                                                        ? (F2 - _AP_F - 2)
                                                        : (_AP_W2 - 1)) != 0
                  : false;
          carry = quantization_adjust(qb, r, isneg);
        }
      } else { // no quantization
        Base::V = man.V;
        if (sh_amt < _AP_W)
          Base::V = Base::V << sh_amt;
        else
          Base::V = 0;
      }
      // handle overflow/underflow
      if ((_AP_O != AP_WRAP || _AP_N != 0) &&
          ((!_AP_S && _AP_S2) ||
           _AP_I - _AP_S <
               _AP_I2 - _AP_S2 +
                   (QUAN_INC ||
                    (_AP_S2 && (_AP_O == AP_SAT_SYM))))) { // saturation
        bool deleted_zeros = _AP_S2 ? true : !carry, deleted_ones = true;
        bool neg_src = isneg;
        bool lD = false;
        int pos1 = F2 - _AP_F + _AP_W;
        int pos2 = F2 - _AP_F + _AP_W + 1;
        bool newsignbit = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1);
        if (pos1 < _AP_W2 && pos1 >= 0)
          // lD = _AP_ROOT_op_get_bit(man.V, pos1);
          lD = (man.V >> pos1) & 1;
        if (pos1 < _AP_W2) {
          bool Range1_all_ones = true;
          bool Range1_all_zeros = true;
          bool Range2_all_ones = true;
          ap_int_base<DOUBLE_MAN + 2, false> Range2;
          ap_int_base<DOUBLE_MAN + 2, false> all_ones(-1);

          if (pos2 >= 0 && pos2 < _AP_W2) {
            // Range2.V = _AP_ROOT_op_get_range(man.V,
            //                        pos2, _AP_W2 - 1);
            Range2.V = man.V;
            Range2.V >>= pos2;
            Range2_all_ones = Range2 == (all_ones >> pos2);
          } else if (pos2 < 0)
            Range2_all_ones = false;
          if (pos1 >= 0 && pos2 < _AP_W2) {
            Range1_all_ones = Range2_all_ones && lD;
            Range1_all_zeros = !Range2.V && !lD;
          } else if (pos2 == _AP_W2) {
            Range1_all_ones = lD;
            Range1_all_zeros = !lD;
          } else if (pos1 < 0) {
            Range1_all_zeros = !man.V;
            Range1_all_ones = false;
          }

          deleted_zeros =
              deleted_zeros && (carry ? Range1_all_ones : Range1_all_zeros);
          deleted_ones =
              carry ? Range2_all_ones && (pos1 < 0 || !lD) : Range1_all_ones;
          neg_src = isneg && !(carry && Range1_all_ones);
        } else
          neg_src = isneg && newsignbit;
        bool neg_trg = _AP_S && newsignbit;
        bool overflow = (neg_trg || !deleted_zeros) && !isneg;
        bool underflow = (!neg_trg || !deleted_ones) && neg_src;
        if ((_AP_O == AP_SAT_SYM) && _AP_S2 && _AP_S)
          underflow |=
              neg_src &&
              (_AP_W > 1 ? _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 2) == 0
                         : true);
        overflow_adjust(underflow, overflow, lD, neg_src);
      }
    }
    report();
  }

  // TODO more optimized implementation.
  INLINE ap_fixed_base(float d) { *this = ap_fixed_base(double(d)); }

#if _AP_ENABLE_HALF_ == 1
  // TODO more optimized implementation.
  INLINE ap_fixed_base(half d) { *this = ap_fixed_base(double(d)); }
#endif
  //  @}

  /// @name assign operator
  /// assign, using another ap_fixed_base of same template parameters.
  /*
  INLINE ap_fixed_base& operator=(
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {
    Base::V = op.V;
    return *this;
  }
  */

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base& operator=(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {

    const int _AP_F = _AP_W - _AP_I;
    const int F2 = _AP_W2 - _AP_I2;
    const int QUAN_INC =
          F2 > _AP_F && !(_AP_Q == AP_TRN || (_AP_Q == AP_TRN_ZERO && !_AP_S2));

    if (!op) Base::V = 0;
    bool carry = false;
    bool signbit = _AP_ROOT_op_get_bit(op.V, _AP_W2 - 1);
    bool isneg = signbit && _AP_S2;
    if (F2 == _AP_F)
      Base::V = op.V;
    else if (F2 > _AP_F) {
      unsigned int sh_amt = F2 - _AP_F;
      //  moves bits right, handle quantization.
      if (sh_amt < _AP_W2) {
        Base::V = op.V >> sh_amt;
      } else {
        Base::V = isneg ? -1 : 0;
      }
      if (_AP_Q != AP_TRN && !(_AP_Q == AP_TRN_ZERO && !_AP_S2)) {
        bool qbit = _AP_ROOT_op_get_bit(op.V, F2 - _AP_F - 1);
        // bit after LSB.
        bool qb = (F2 - _AP_F > _AP_W2) ? _AP_S2 && signbit : qbit;
        enum { hi = ((F2 - _AP_F - 2) < _AP_W2) ? (F2 - _AP_F - 2) : (_AP_W2 - 1) };
        // bits after qb.
        bool r = (F2 > _AP_F + 1) ? (_AP_ROOT_op_get_range(op.V, 0, hi) != 0) : false;
        carry = quantization_adjust(qb, r, isneg);
      }
    } else {
      unsigned  sh_amt = _AP_F - F2;
      // moves bits left, no quantization
      if (sh_amt < _AP_W) {
        if (_AP_W > _AP_W2) {
          // extend and then shift, avoid losing bits.
          Base::V = op.V;
          Base::V <<= sh_amt;
        } else {
          // shift and truncate.
          Base::V = op.V << sh_amt;
        }
      } else {
        Base::V = 0;
      }
    }
    // handle overflow/underflow
    if ((_AP_O != AP_WRAP || _AP_N != 0) &&
        ((!_AP_S && _AP_S2) ||
         _AP_I - _AP_S <
             _AP_I2 - _AP_S2 +
                 (QUAN_INC || (_AP_S2 && _AP_O == AP_SAT_SYM)))) { // saturation
      bool deleted_zeros = _AP_S2 ? true : !carry;
      bool deleted_ones = true;
      bool neg_src = isneg;
      bool newsignbit = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1);
      enum { pos1 = F2 - _AP_F + _AP_W, pos2 = F2 - _AP_F + _AP_W + 1 };
      bool lD = (pos1 < _AP_W2 && pos1 >= 0) ? _AP_ROOT_op_get_bit(op.V, pos1)
                                             : false;
      if (pos1 < _AP_W2) {
        bool Range1_all_ones = true;
        bool Range1_all_zeros = true;
        bool Range2_all_ones = true;
        ap_int_base<_AP_W2, false> all_ones(-1);

        if (pos2 < _AP_W2 && pos2 >= 0) {
          ap_int_base<_AP_W2, false> Range2;
          Range2.V = _AP_ROOT_op_get_range(op.V, pos2, _AP_W2 - 1);
          Range2_all_ones = Range2 == (all_ones >> pos2);
        } else if (pos2 < 0) {
          Range2_all_ones = false;
        }

        if (pos1 >= 0 && pos2 < _AP_W2) {
          ap_int_base<_AP_W2, false> Range1;
          Range1.V = _AP_ROOT_op_get_range(op.V, pos1, _AP_W2 - 1);
          Range1_all_ones = Range1 == (all_ones >> pos1);
          Range1_all_zeros = !Range1.V;
        } else if (pos2 == _AP_W2) {
          Range1_all_ones = lD;
          Range1_all_zeros = !lD;
        } else if (pos1 < 0) {
          Range1_all_zeros = !op.V;
          Range1_all_ones = false;
        }

        deleted_zeros =
            deleted_zeros && (carry ? Range1_all_ones : Range1_all_zeros);
        deleted_ones =
            carry ? Range2_all_ones && (pos1 < 0 || !lD) : Range1_all_ones;
        neg_src = isneg && !(carry && Range1_all_ones);
      } else
        neg_src = isneg && newsignbit;
      bool neg_trg = _AP_S && newsignbit;
      bool overflow = (neg_trg || !deleted_zeros) && !isneg;
      bool underflow = (!neg_trg || !deleted_ones) && neg_src;
      if ((_AP_O == AP_SAT_SYM) && _AP_S2 && _AP_S)
        underflow |=
            neg_src &&
            (_AP_W > 1 ? _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 2) == 0
                       : true);

      overflow_adjust(underflow, overflow, lD, neg_src);
    }
    return *this;
  } // operator= 

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base& operator=(
      const volatile ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
    operator=(const_cast<const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>&>(op));
    return *this;
  }

  /// Set this ap_fixed_base with ULL.
  INLINE ap_fixed_base& setBits(ap_ulong bv) {
    // TODO when ull is not be long enough...
    Base::V = bv;
    return *this;
  }

  /// Return a ap_fixed_base object whose this->V is assigned by bv.
  static INLINE ap_fixed_base bitsToFixed(ap_ulong bv) {
    // TODO fix when ull is not be long enough...
    ap_fixed_base t;
#ifdef __SYNTHESIS__
    t.V = bv;
#else
    t.V.set_bits(bv);
#endif
    return t;
  }

  // Explicit conversion functions to ap_int_base.
  /** Captures all integer bits, in truncate mode.
   *  @param[in] Cnative follow conversion from double to int.
   */
  INLINE ap_int_base<AP_MAX(_AP_I, 1), _AP_S> to_ap_int_base(
      bool Cnative = true) const {
    ap_int_base<AP_MAX(_AP_I, 1), _AP_S> ret;
    if (_AP_I == 0) {
      ret.V = 0;
    } else if (_AP_I > 0 && _AP_I <= _AP_W) {
      ret.V = _AP_ROOT_op_get_range(Base::V, _AP_W - _AP_I, _AP_W - 1);
    } else if (_AP_I > _AP_W) {
      ret.V = _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 1);
      ret.V <<= (_AP_I - _AP_W);
    }
    /* Consider the following case
     *   float f = -7.5f;
     *   ap_fixed<8,4> t = f;  // -8 0 0 0 . 0.5
     *   int i = t.to_int();
     * the result should be -7 instead of -8.
     * Therefore, after truncation, the value should be increated by 1.
     * For (-1, 0), carry to MSB will happen, but result 0 is still correct.
     */
    if (Cnative && _AP_I < _AP_W) {
      // Follow C native data type, conversion from double to int
      if (_AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1) && (_AP_I < _AP_W) &&
          (_AP_ROOT_op_get_range(
               Base::V, 0, _AP_I < 0 ? _AP_W - 1 : _AP_W - _AP_I - 1) != 0))
        ++ret;
    } else {
      // Follow OSCI library, conversion from sc_fixed to sc_int
    }
    return ret;
  };

 public:
  template <int _AP_W2, bool _AP_S2>
  INLINE operator ap_int_base<_AP_W2, _AP_S2>() const {
    return ap_int_base<_AP_W2, _AP_S2>(to_ap_int_base());
  }

  // Explicit conversion function to C built-in integral type.
  INLINE char to_char() const { return to_ap_int_base().to_char(); }

  INLINE int to_int() const { return to_ap_int_base().to_int(); }

  INLINE unsigned to_uint() const { return to_ap_int_base().to_uint(); }

  INLINE ap_slong to_int64() const { return to_ap_int_base().to_int64(); }

  INLINE ap_ulong to_uint64() const { return to_ap_int_base().to_uint64(); }

  /// covert function to double.
  /** only round-half-to-even mode supported, does not obey FE env. */
  INLINE double to_double() const {
#if defined(AP_FIXED_ENABLE_CPP_FENV)
    _AP_WARNING(std::fegetround() != FE_TONEAREST,
                "Only FE_TONEAREST is supported");
#endif
    enum { BITS = DOUBLE_MAN + DOUBLE_EXP + 1 };
    if (!Base::V) return 0.0f;
    bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign.
    ap_int_base<_AP_W, false> tmp;
    if (s)
      tmp.V = -Base::V; // may truncate one bit extra from neg in sim.
    else
      tmp.V = Base::V;
    int l = tmp.countLeadingZeros(); ///< number of leading zeros.
    int e = _AP_I - l - 1 + DOUBLE_BIAS; ///< exponent
    int lsb_index = _AP_W - l - 1 - DOUBLE_MAN;
    // more than 0.5?
    bool a = (lsb_index >=2) ?
        (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0;
    // round to even
    a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0;
    // ull is at least 64-bit
    ap_ulong m;
    // may actually left shift, ensure buffer is wide enough.
    if (_AP_W > BITS) {
      m = (lsb_index >= 1) ? (ap_ulong)(tmp.V >> (lsb_index - 1))
                           : (ap_ulong)(tmp.V << (1 - lsb_index));
    } else {
      m = (ap_ulong)tmp.V;
      m = (lsb_index >= 1) ? (m >> (lsb_index - 1))
                           : (m << (1 - lsb_index));
    }
    m += a;
    m >>= 1;
    //std::cout << '\n' << std::hex << m << '\n'; // TODO delete this
    // carry to MSB, increase exponent
    if (_AP_ctype_op_get_bit(m, DOUBLE_MAN + 1)) {
      e += 1;
    }
    // set sign and exponent
    m = _AP_ctype_op_set_bit(m, BITS - 1, s);
    //std::cout << m << '\n'; // TODO delete this
    m = _AP_ctype_op_set_range(m, DOUBLE_MAN, DOUBLE_MAN + DOUBLE_EXP - 1, e);
    //std::cout << std::hex << m << std::dec << std::endl; // TODO delete this
    // cast to fp
    return rawBitsToDouble(m);
  }

  /// convert function to float.
  /** only round-half-to-even mode supported, does not obey FE env. */
  INLINE float to_float() const {
#if defined(AP_FIXED_ENABLE_CPP_FENV)
    _AP_WARNING(std::fegetround() != FE_TONEAREST,
                "Only FE_TONEAREST is supported");
#endif
    enum { BITS = FLOAT_MAN + FLOAT_EXP + 1 };
    if (!Base::V) return 0.0f;
    bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign.
    ap_int_base<_AP_W, false> tmp;
    if (s)
      tmp.V = -Base::V; // may truncate one bit extra from neg in sim.
    else
      tmp.V = Base::V;
    int l = tmp.countLeadingZeros();  ///< number of leading zeros.
    int e = _AP_I - l - 1 + FLOAT_BIAS; ///< exponent
    int lsb_index = _AP_W - l - 1 - FLOAT_MAN;
    // more than 0.5?
    bool a = (lsb_index >=2) ?
        (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0;
    // round to even
    a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0;
    // ul is at least 32-bit
    unsigned long m;
    // may actually left shift, ensure buffer is wide enough.
    if (_AP_W > BITS) {
      m = (lsb_index >= 1) ? (unsigned long)(tmp.V >> (lsb_index - 1))
                           : (unsigned long)(tmp.V << (1 - lsb_index));
    } else {
      m = (unsigned long)tmp.V;
      m = (lsb_index >= 1) ? (m >> (lsb_index - 1))
                           : (m << (1 - lsb_index));
    }
    m += a;
    m >>= 1;
    // carry to MSB, increase exponent
    if (_AP_ctype_op_get_bit(m, FLOAT_MAN + 1)) {
      e += 1;
    }
    // set sign and exponent
    m = _AP_ctype_op_set_bit(m, BITS - 1, s);
    m = _AP_ctype_op_set_range(m, FLOAT_MAN, FLOAT_MAN + FLOAT_EXP - 1, e);
    // cast to fp
    return rawBitsToFloat(m);
  }

#if _AP_ENABLE_HALF_ == 1
  /// convert function to half.
  /** only round-half-to-even mode supported, does not obey FE env. */
  INLINE half to_half() const {
#if defined(AP_FIXED_ENABLE_CPP_FENV)
    _AP_WARNING(std::fegetround() != FE_TONEAREST,
                "Only FE_TONEAREST is supported");
#endif
    enum { BITS = HALF_MAN + HALF_EXP + 1 };
    if (!Base::V) return 0.0f;
    bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign.
    ap_int_base<_AP_W, false> tmp;
    if (s)
      tmp.V = -Base::V; // may truncate one bit extra from neg in sim.
    else
      tmp.V = Base::V;
    int l = tmp.countLeadingZeros();  ///< number of leading zeros.
    int e = _AP_I - l - 1 + HALF_BIAS; ///< exponent
    int lsb_index = _AP_W - l - 1 - HALF_MAN;
    // more than 0.5?
    bool a = (lsb_index >=2) ?
        (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0;
    // round to even
    a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0;
    // short is at least 16-bit
    unsigned short m;
    // may actually left shift, ensure buffer is wide enough.
    if (_AP_W > BITS) {
      m = (lsb_index >= 1) ? (unsigned short)(tmp.V >> (lsb_index - 1))
                           : (unsigned short)(tmp.V << (1 - lsb_index));
    } else {
      m = (unsigned short)tmp.V;
      m = (lsb_index >= 1) ? (m >> (lsb_index - 1))
                           : (m << (1 - lsb_index));
    }
    m += a;
    m >>= 1;
    // carry to MSB, increase exponent
    if (_AP_ctype_op_get_bit(m, HALF_MAN + 1)) {
      e += 1;
    }
    // set sign and exponent
    m = _AP_ctype_op_set_bit(m, BITS - 1, s);
    m = _AP_ctype_op_set_range(m, HALF_MAN, HALF_MAN + HALF_EXP - 1, e);
    // cast to fp
    return rawBitsToHalf(m);
  }
#endif

  // FIXME inherited from old code, this may loose precision!
  INLINE operator long double() const { return (long double)to_double(); }

  INLINE operator double() const { return to_double(); }

  INLINE operator float() const { return to_float(); }

#if _AP_ENABLE_HALF_ == 1
  INLINE operator half() const { return to_half(); }
#endif

  INLINE operator bool() const { return (bool)Base::V != 0; }

  INLINE operator char() const { return (char)to_int(); }

  INLINE operator signed char() const { return (signed char)to_int(); }

  INLINE operator unsigned char() const { return (unsigned char)to_uint(); }

  INLINE operator short() const { return (short)to_int(); }

  INLINE operator unsigned short() const { return (unsigned short)to_uint(); }

  INLINE operator int() const { return to_int(); }

  INLINE operator unsigned int() const { return to_uint(); }

// FIXME don't assume data width...
#ifdef __x86_64__
  INLINE operator long() const { return (long)to_int64(); }

  INLINE operator unsigned long() const { return (unsigned long)to_uint64(); }
#else
  INLINE operator long() const { return (long)to_int(); }

  INLINE operator unsigned long() const { return (unsigned long)to_uint(); }
#endif // ifdef __x86_64__ else

  INLINE operator ap_ulong() const { return to_uint64(); }

  INLINE operator ap_slong() const { return to_int64(); }

  INLINE int length() const { return _AP_W; };

  // bits_to_int64 deleted.
#ifndef __SYNTHESIS__
  // Used in autowrap, when _AP_W < 64.
  INLINE ap_ulong bits_to_uint64() const {
    return (Base::V).to_uint64();
  }
#endif

  // Count the number of zeros from the most significant bit
  // to the first one bit. Note this is only for ap_fixed_base whose
  // _AP_W <= 64, otherwise will incur assertion.
  INLINE int countLeadingZeros() {
#ifdef __SYNTHESIS__
    // TODO: used llvm.ctlz intrinsic ?
    if (_AP_W <= 32) {
      ap_int_base<32, false> t(-1ULL);
      t.range(_AP_W - 1, 0) = this->range(0, _AP_W - 1);
      return __builtin_ctz(t.V);
    } else if (_AP_W <= 64) {
      ap_int_base<64, false> t(-1ULL);
      t.range(_AP_W - 1, 0) = this->range(0, _AP_W - 1);
      return __builtin_ctzll(t.V);
    } else {
      enum {__N = (_AP_W + 63) / 64};
      int NZeros = 0;
      int i = 0;
      bool hitNonZero = false;
      for (i = 0; i < __N - 1; ++i) {
        ap_int_base<64, false> t;
        t.range(0, 63) = this->range(_AP_W - i * 64 - 64, _AP_W - i * 64 - 1);
        NZeros += hitNonZero ? 0 : __builtin_clzll(t.V);
        hitNonZero |= (t != 0);
      }
      if (!hitNonZero) {
        ap_int_base<64, false> t(-1ULL);
        t.range(63 - (_AP_W - 1) % 64, 63) = this->range(0, (_AP_W - 1) % 64);
        NZeros += __builtin_clzll(t.V);
      }
      return NZeros;
    }
#else
    return Base::V.countLeadingZeros();
#endif
  }

  // Arithmetic : Binary
  // -------------------------------------------------------------------------
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::mult operator*(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2)
      const {
    typename RType<_AP_W2, _AP_I2, _AP_S2>::mult_base r, t;
    r.V = Base::V;
    t.V = op2.V;
    r.V *= op2.V;
    return r;
  }

  // multiply function deleted.

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::div operator/(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2)
      const {
    typename RType<_AP_W2, _AP_I2, _AP_S2>::div_base r;
#ifndef __SYNTHESIS__
    enum {F2 = _AP_W2-_AP_I2,
              _W1=AP_MAX(_AP_W + AP_MAX(F2, 0) + ((_AP_S2 && !_AP_S) ? 1 : 0), _AP_W2 + ((_AP_S && !_AP_S2) ? 1 : 0))};
    ap_int_base<_W1,_AP_S||_AP_S2> dividend,divisior;
    ap_int_base<_W1,_AP_S> tmp1;
    ap_int_base<_W1,_AP_S2> tmp2;
    tmp1.V = Base::V;
    tmp1.V <<= AP_MAX(F2,0);
    tmp2.V = op2.V;
    dividend = tmp1;
    divisior = tmp2;
    r.V = ((_AP_S||_AP_S2) ? dividend.V.sdiv(divisior.V): dividend.V.udiv(divisior.V));
#else
    #ifndef __SC_COMPATIBLE__
        ap_fixed_base<_AP_W + AP_MAX(_AP_W2 - _AP_I2, 0),_AP_I, _AP_S> t(*this);
    #else
        ap_fixed_base<_AP_W + AP_MAX(_AP_W2 - _AP_I2, 0) + AP_MAX(_AP_I2, 0),_AP_I, _AP_S> t(*this);
    #endif
        r.V = t.V / op2.V;
#endif
/*
    enum {
      F2 = _AP_W2 - _AP_I2,
      shl = AP_MAX(F2, 0) + AP_MAX(_AP_I2, 0),
#ifndef __SC_COMPATIBLE__
      shr = AP_MAX(_AP_I2, 0),
#else
      shr = 0,
#endif
      W3 = _AP_S2 + _AP_W + shl,
      S3 = _AP_S || _AP_S2,
    };
    ap_int_base<W3, S3> dividend, t;
    dividend.V = Base::V;
    // multiply both by (1 << F2), and than do integer division.
    dividend.V <<= (int) shl;
#ifdef __SYNTHESIS__
    // .V's have right signedness, and will have right extending.
    t.V = dividend.V / op2.V;
#else
    // XXX op2 may be wider than dividend, and sdiv and udiv takes the same with
    // as left hand operand, so data might be truncated by mistake if not
    // handled here.
    t.V = S3 ? dividend.V.sdiv(op2.V) : dividend.V.udiv(op2.V);
#endif
    r.V = t.V >> (int) shr;
*/
    return r;
  }

#define OP_BIN_AF(Sym, Rty)                                                \
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,         \
            ap_o_mode _AP_O2, int _AP_N2>                                  \
  INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::Rty operator Sym(         \
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& \
          op2) const {                                                     \
    typename RType<_AP_W2, _AP_I2, _AP_S2>::Rty##_base ret, lhs(*this),    \
        rhs(op2);                                                          \
    ret.V = lhs.V Sym rhs.V;                                               \
    return ret;                                                            \
  }

  OP_BIN_AF(+, plus)
  OP_BIN_AF(-, minus)
  OP_BIN_AF(&, logic)
  OP_BIN_AF(|, logic)
  OP_BIN_AF(^, logic)

// Arithmetic : assign
// -------------------------------------------------------------------------
#define OP_ASSIGN_AF(Sym)                                                  \
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,         \
            ap_o_mode _AP_O2, int _AP_N2>                                  \
  INLINE ap_fixed_base& operator Sym##=(                                   \
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& \
          op2) {                                                           \
    *this = operator Sym(op2);                                             \
    return *this;                                                          \
  }

  OP_ASSIGN_AF(*)
  OP_ASSIGN_AF(/)
  OP_ASSIGN_AF(+)
  OP_ASSIGN_AF(-)
  OP_ASSIGN_AF(&)
  OP_ASSIGN_AF(|)
  OP_ASSIGN_AF(^)

  // Prefix and postfix increment and decrement.
  // -------------------------------------------------------------------------

  /// Prefix increment
  INLINE ap_fixed_base& operator++() {
    operator+=(ap_fixed_base<_AP_W - _AP_I + 1, 1, false>(1));
    return *this;
  }

  /// Prefix decrement.
  INLINE ap_fixed_base& operator--() {
    operator-=(ap_fixed_base<_AP_W - _AP_I + 1, 1, false>(1));
    return *this;
  }

  /// Postfix increment
  INLINE const ap_fixed_base operator++(int) {
    ap_fixed_base r(*this);
    operator++();
    return r;
  }

  /// Postfix decrement
  INLINE const ap_fixed_base operator--(int) {
    ap_fixed_base r(*this);
    operator--();
    return r;
  }

  // Unary arithmetic.
  // -------------------------------------------------------------------------
  INLINE ap_fixed_base operator+() { return *this; }

  INLINE ap_fixed_base<_AP_W + 1, _AP_I + 1, true> operator-() const {
    ap_fixed_base<_AP_W + 1, _AP_I + 1, true> r(*this);
    r.V = -r.V;
    return r;
  }

  INLINE ap_fixed_base<_AP_W, _AP_I, true, _AP_Q, _AP_O, _AP_N> getNeg() {
    ap_fixed_base<_AP_W, _AP_I, true, _AP_Q, _AP_O, _AP_N> r(*this);
    r.V = -r.V;
    return r;
  }

  // Not (!)
  // -------------------------------------------------------------------------
  INLINE bool operator!() const { return Base::V == 0; }

  // Bitwise complement
  // -------------------------------------------------------------------------
  // XXX different from Mentor's ac_fixed.
  INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S> operator~() const {
    ap_fixed_base<_AP_W, _AP_I, _AP_S> r;
    r.V = ~Base::V;
    return r;
  }

  // Shift
  // -------------------------------------------------------------------------
  // left shift is the same as moving point right, i.e. increate I.
  template <int _AP_SHIFT>
  INLINE ap_fixed_base<_AP_W, _AP_I + _AP_SHIFT, _AP_S> lshift() const {
    ap_fixed_base<_AP_W, _AP_I + _AP_SHIFT, _AP_S> r;
    r.V = Base::V;
    return r;
  }

  template <int _AP_SHIFT>
  INLINE ap_fixed_base<_AP_W, _AP_I - _AP_SHIFT, _AP_S> rshift() const {
    ap_fixed_base<_AP_W, _AP_I - _AP_SHIFT, _AP_S> r;
    r.V = Base::V;
    return r;
  }

  // Because the return type is the type of the the first operand, shift assign
  // operators do not carry out any quantization or overflow
  // While systemc, shift assigns for sc_fixed/sc_ufixed will result in
  // quantization or overflow (depending on the mode of the first operand)
  INLINE ap_fixed_base operator<<(unsigned int sh) const {
    ap_fixed_base r;
    r.V = Base::V << sh;
// TODO check shift overflow?
#ifdef __SC_COMPATIBLE__
    if (sh == 0) return r;
    if (_AP_O != AP_WRAP || _AP_N != 0) {
      bool neg_src = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1);
      bool allones, allzeros;
      ap_int_base<_AP_W, false> ones(-1);
      if (sh <= _AP_W) {
        ap_int_base<_AP_W, false> range1;
        range1.V = _AP_ROOT_op_get_range(
            const_cast<ap_fixed_base*>(this)->Base::V, _AP_W - sh, _AP_W - 1);
        allones = range1 == (ones >> (_AP_W - sh));
        allzeros = range1 == 0;
      } else {
        allones = false;
        allzeros = Base::V == 0;
      }
      bool overflow = !allzeros && !neg_src;
      bool underflow = !allones && neg_src;
      if ((_AP_O == AP_SAT_SYM) && _AP_S)
        underflow |=
            neg_src &&
            (_AP_W > 1 ? _AP_ROOT_op_get_range(r.V, 0, _AP_W - 2) == 0 : true);
      bool lD = false;
      if (sh < _AP_W) lD = _AP_ROOT_op_get_bit(Base::V, _AP_W - sh - 1);
      r.overflow_adjust(underflow, overflow, lD, neg_src);
    }
#endif
    return r;
  }

  INLINE ap_fixed_base operator>>(unsigned int sh) const {
    ap_fixed_base r;
    r.V = Base::V >> sh;
// TODO check shift overflow?
#ifdef __SC_COMPATIBLE__
    if (sh == 0) return r;
    if (_AP_Q != AP_TRN) {
      bool qb = false;
      if (sh <= _AP_W) qb = _AP_ROOT_op_get_bit(Base::V, sh - 1);
      bool rb = false;
      if (sh > 1 && sh <= _AP_W)
        rb = _AP_ROOT_op_get_range(const_cast<ap_fixed_base*>(this)->Base::V, 0,
                                   sh - 2) != 0;
      else if (sh > _AP_W)
        rb = Base::V != 0;
      r.quantization_adjust(qb, rb,
                            _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1));
    }
#endif
    return r;
  }

  // left and right shift for int
  INLINE ap_fixed_base operator<<(int sh) const {
    ap_fixed_base r;
    bool isNeg = sh < 0;
    unsigned int ush = isNeg ? -sh : sh;
    if (isNeg) {
      return operator>>(ush);
    } else {
      return operator<<(ush);
    }
  }

  INLINE ap_fixed_base operator>>(int sh) const {
    bool isNeg = sh < 0;
    unsigned int ush = isNeg ? -sh : sh;
    if (isNeg) {
      return operator<<(ush);
    } else {
      return operator>>(ush);
    }
  }

  // left and right shift for ap_int.
  template <int _AP_W2>
  INLINE ap_fixed_base operator<<(const ap_int_base<_AP_W2, true>& op2) const {
    // TODO the code seems not optimal. ap_fixed<8,8> << ap_int<2> needs only a
    // small mux, but integer need a big one!
    int sh = op2.to_int();
    return operator<<(sh);
  }

  template <int _AP_W2>
  INLINE ap_fixed_base operator>>(const ap_int_base<_AP_W2, true>& op2) const {
    int sh = op2.to_int();
    return operator>>(sh);
  }

  // left and right shift for ap_uint.
  template <int _AP_W2>
  INLINE ap_fixed_base operator<<(const ap_int_base<_AP_W2, false>& op2) const {
    unsigned int sh = op2.to_uint();
    return operator<<(sh);
  }

  template <int _AP_W2>
  INLINE ap_fixed_base operator>>(const ap_int_base<_AP_W2, false>& op2) const {
    unsigned int sh = op2.to_uint();
    return operator>>(sh);
  }

  // left and right shift for ap_fixed
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base operator<<(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>&
          op2) {
    return operator<<(op2.to_ap_int_base());
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base operator>>(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>&
          op2) {
    return operator>>(op2.to_ap_int_base());
  }

  // Shift assign.
  // -------------------------------------------------------------------------

  // left shift assign.
  INLINE ap_fixed_base& operator<<=(const int sh) {
    *this = operator<<(sh);
    return *this;
  }

  INLINE ap_fixed_base& operator<<=(const unsigned int sh) {
    *this = operator<<(sh);
    return *this;
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base& operator<<=(const ap_int_base<_AP_W2, _AP_S2>& sh) {
    *this = operator<<(sh.to_int());
    return *this;
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base& operator<<=(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>&
          sh) {
    *this = operator<<(sh.to_int());
    return *this;
  }

  // right shift assign.
  INLINE ap_fixed_base& operator>>=(const int sh) {
    *this = operator>>(sh);
    return *this;
  }

  INLINE ap_fixed_base& operator>>=(const unsigned int sh) {
    *this = operator>>(sh);
    return *this;
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE ap_fixed_base& operator>>=(const ap_int_base<_AP_W2, _AP_S2>& sh) {
    *this = operator>>(sh.to_int());
    return *this;
  }

  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
            ap_o_mode _AP_O2, int _AP_N2>
  INLINE ap_fixed_base& operator>>=(
      const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>&
          sh) {
    *this = operator>>(sh.to_int());
    return *this;
  }

// Comparisons.
// -------------------------------------------------------------------------
#define OP_CMP_AF(Sym)                                                         \
  template <int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,             \
            ap_o_mode _AP_O2, int _AP_N2>                                      \
  INLINE bool operator Sym(const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, \
                                               _AP_O2, _AP_N2>& op2) const {   \
    enum { _AP_F = _AP_W - _AP_I, F2 = _AP_W2 - _AP_I2 };                      \
    if (_AP_F == F2)                                                           \
      return Base::V Sym op2.V;                                                \
    else if (_AP_F > F2)                                                       \
      return Base::V Sym ap_fixed_base<AP_MAX(_AP_W2 + _AP_F - F2, 1), _AP_I2, \
                                       _AP_S2, _AP_Q2, _AP_O2, _AP_N2>(op2).V; \
    else                                                                       \
      return ap_fixed_base<AP_MAX(_AP_W + F2 - _AP_F + 1, 1), _AP_I + 1,       \
                           _AP_S, _AP_Q, _AP_O, _AP_N>(*this).V Sym op2.V;     \
    return false;                                                              \
  }

  OP_CMP_AF(>)
  OP_CMP_AF(<)
  OP_CMP_AF(>=)
  OP_CMP_AF(<=)
  OP_CMP_AF(==)
  OP_CMP_AF(!=)
// FIXME: Move compare with double out of struct ap_fixed_base defination
//        and combine it with compare operator(double, ap_fixed_base)
#define DOUBLE_CMP_AF(Sym) \
  INLINE bool operator Sym(double d) const { return to_double() Sym d; }

  DOUBLE_CMP_AF(>)
  DOUBLE_CMP_AF(<)
  DOUBLE_CMP_AF(>=)
  DOUBLE_CMP_AF(<=)
  DOUBLE_CMP_AF(==)
  DOUBLE_CMP_AF(!=)

  // Bit and Slice Select
  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator[](
      unsigned index) {
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index);
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator[](
      const ap_int_base<_AP_W2, _AP_S2>& index) {
    _AP_WARNING(index < 0, "Attempting to read bit with negative index");
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this,
                                                                index.to_int());
  }

  INLINE bool operator[](unsigned index) const {
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return _AP_ROOT_op_get_bit(const_cast<ap_fixed_base*>(this)->V, index);
  }

  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> bit(
      unsigned index) {
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index);
  }

  template <int _AP_W2, bool _AP_S2>
  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> bit(
      const ap_int_base<_AP_W2, _AP_S2>& index) {
    _AP_WARNING(index < 0, "Attempting to read bit with negative index");
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this,
                                                                index.to_int());
  }

  INLINE bool bit(unsigned index) const {
    _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB");
    return _AP_ROOT_op_get_bit(const_cast<ap_fixed_base*>(this)->V, index);
  }

  template <int _AP_W2>
  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> get_bit(
      const ap_int_base<_AP_W2, true>& index) {
    _AP_WARNING(index < _AP_I - _AP_W,
                "Attempting to read bit with negative index");
    _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(
        this, index.to_int() + _AP_W - _AP_I);
  }

  INLINE bool get_bit(int index) const {
    _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB");
    _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit beyond MSB");
    return _AP_ROOT_op_get_bit(const_cast<ap_fixed_base*>(this)->V,
                               index + _AP_W - _AP_I);
  }
#if 0
  INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> get_bit(
      int index) {
    _AP_WARNING(index < _AP_I - _AP_W,
              "Attempting to read bit with negative index");
    _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB");
    return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(
        this, index + _AP_W - _AP_I);
  }
#endif

  template <int _AP_W2>
  INLINE bool get_bit(const ap_int_base<_AP_W2, true>& index) const {
    _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB");
    _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit beyond MSB");
    return _AP_ROOT_op_get_bit(const_cast<ap_fixed_base*>(this)->V,
                               index.to_int() + _AP_W - _AP_I);
  }

  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range(int Hi,
                                                                      int Lo) {
    _AP_WARNING((Hi >= _AP_W) || (Lo >= _AP_W), "Out of bounds in range()");
    return af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, Hi, Lo);
  }

  // This is a must to strip constness to produce reference type.
  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range(
      int Hi, int Lo) const {
    _AP_WARNING((Hi >= _AP_W) || (Lo >= _AP_W), "Out of bounds in range()");
    return af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(
        const_cast<ap_fixed_base*>(this), Hi, Lo);
  }

  template <int _AP_W2, bool _AP_S2, int _AP_W3, bool _AP_S3>
  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range(
      const ap_int_base<_AP_W2, _AP_S2>& HiIdx,
      const ap_int_base<_AP_W3, _AP_S3>& LoIdx) {
    int Hi = HiIdx.to_int();
    int Lo = LoIdx.to_int();
    return this->range(Hi, Lo);
  }

  template <int _AP_W2, bool _AP_S2, int _AP_W3, bool _AP_S3>
  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range(
      const ap_int_base<_AP_W2, _AP_S2>& HiIdx,
      const ap_int_base<_AP_W3, _AP_S3>& LoIdx) const {
    int Hi = HiIdx.to_int();
    int Lo = LoIdx.to_int();
    return this->range(Hi, Lo);
  }

  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range() {
    return this->range(_AP_W - 1, 0);
  }

  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range() const {
    return this->range(_AP_W - 1, 0);
  }

  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()(
      int Hi, int Lo) {
    return this->range(Hi, Lo);
  }

  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()(
      int Hi, int Lo) const {
    return this->range(Hi, Lo);
  }

  template <int _AP_W2, bool _AP_S2, int _AP_W3, bool _AP_S3>
  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()(
      const ap_int_base<_AP_W2, _AP_S2>& HiIdx,
      const ap_int_base<_AP_W3, _AP_S3>& LoIdx) {
    int Hi = HiIdx.to_int();
    int Lo = LoIdx.to_int();
    return this->range(Hi, Lo);
  }

  template <int _AP_W2, bool _AP_S2, int _AP_W3, bool _AP_S3>
  INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()(
      const ap_int_base<_AP_W2, _AP_S2>& HiIdx,
      const ap_int_base<_AP_W3, _AP_S3>& LoIdx) const {
    int Hi = HiIdx.to_int();
    int Lo = LoIdx.to_int();
    return this->range(Hi, Lo);
  }

  INLINE bool is_zero() const { return Base::V == 0; }

  INLINE bool is_neg() const {
    if (_AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1)) return true;
    return false;
  }

  INLINE int wl() const { return _AP_W; }

  INLINE int iwl() const { return _AP_I; }

  INLINE ap_q_mode q_mode() const { return _AP_Q; }

  INLINE ap_o_mode o_mode() const { return _AP_O; }

  INLINE int n_bits() const { return _AP_N; }

  // print a string representation of this number in the given radix.
  // Radix support is 2, 8, 10, or 16.
  // The result will include a prefix indicating the radix, except for decimal,
  // where no prefix is needed.  The default is to output a signed representation
  // of signed numbers, or an unsigned representation  of unsigned numbers.  For
  // non-decimal formats, this can be changed by the 'sign' argument.
#ifndef __SYNTHESIS__
  std::string to_string(unsigned char radix = 2, bool sign = _AP_S) const {
    // XXX in autosim/autowrap.tcl "(${name}).to_string(2).c_str()" is used to
    // initialize sc_lv, which seems incapable of handling format "-0b".
    if (radix == 2) sign = false;

    std::string str;
    str.clear();
    char step = 0;
    bool isNeg = sign && (Base::V < 0);

    // Extend to take care of the -MAX case.
    ap_fixed_base<_AP_W + 1, _AP_I + 1> tmp(*this);
    if (isNeg) {
      tmp = -tmp;
      str += '-';
    }
    std::string prefix;
    switch (radix) {
      case 2:
        prefix = "0b";
        step = 1;
        break;
      case 8:
        prefix = "0o";
        step = 3;
        break;
      case 16:
        prefix = "0x";
        step = 4;
        break;
      default:
        break;
    }

    if (_AP_I > 0) {
      // Note we drop the quantization and rounding flags here.  The
      // integer part is always in range, and the fractional part we
      // want to drop.  Also, the number is always positive, because
      // of the absolute value above.
      ap_int_base<AP_MAX(_AP_I + 1, 1), false> int_part;
      //   [1] [ I ] d [ W - I ]
      //    |     |            |
      //    |    W-I           0
      //    W
      int_part.V = _AP_ROOT_op_get_range(
          tmp.V, _AP_W - _AP_I, _AP_W);
      str += int_part.to_string(radix, false);
    } else {
      str += prefix;
      str += '0';
    }

    ap_fixed_base<AP_MAX(_AP_W - _AP_I, 1), 0, false> frac_part = tmp;

    if (radix == 10) {
      if (frac_part != 0) {
        str += ".";
        while (frac_part != 0) {
          char digit = (frac_part * radix).to_char();
          str += static_cast<char>(digit + '0');
          frac_part *= radix;
        }
      }
    } else {
      if (frac_part != 0) {
        str += ".";
        for (signed i = _AP_W - _AP_I - 1; i >= 0; i -= step) {
          char digit = frac_part.range(i, AP_MAX(0, i - step + 1)).to_char();
          // If we have a partial bit pattern at the end, then we need
          // to put it in the high-order bits of 'digit'.
          int offset = AP_MIN(0, i - step + 1);
          digit <<= -offset;
          str += digit < 10 ? static_cast<char>(digit + '0')
                            : static_cast<char>(digit - 10 + 'a');
        }
        if (radix == 16)
          str += "p0"; // C99 Hex constants are required to have an exponent.
      }
    }
    return str;
  }
#else
  // XXX HLS will delete this in synthesis
  INLINE char* to_string(unsigned char radix = 2, bool sign = _AP_S) const {
    return 0;
  }
#endif
}; // struct ap_fixed_base.

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE void b_not(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {
  ret.V = ~op.V;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE void b_and(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  ret.V = op1.V & op2.V;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE void b_or(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  ret.V = op1.V | op2.V;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE void b_xor(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  ret.V = op1.V ^ op2.V;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N, int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
          ap_o_mode _AP_O2, int _AP_N2>
INLINE void neg(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) {
  ap_fixed_base<_AP_W2 + !_AP_S2, _AP_I2 + !_AP_S2, true, _AP_Q2, _AP_O2,
                _AP_N2>
      t;
  t.V = -op.V;
  ret = t;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N, int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
          ap_o_mode _AP_O2, int _AP_N2>
INLINE void lshift(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op,
    int i) {
  enum {
    F2 = _AP_W2 - _AP_I2,
    _AP_I3 = AP_MAX(_AP_I, _AP_I2),
    _AP_W3 = _AP_I3 + F2,
  };
  // wide buffer
  ap_fixed_base<_AP_W3, _AP_I3, _AP_S2, _AP_Q2, _AP_O2, _AP_N2> t;
  t.V = op.V;
  t.V <<= i; // FIXME overflow?
  // handle quantization and overflow
  ret = t;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N, int _AP_W2, int _AP_I2, bool _AP_S2, ap_q_mode _AP_Q2,
          ap_o_mode _AP_O2, int _AP_N2>
INLINE void rshift(
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret,
    const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op,
    int i) {
  enum {
    F = _AP_W - _AP_I,
    F2 = _AP_W2 - _AP_I2,
    F3 = AP_MAX(F, F2),
    _AP_W3 = _AP_I2 + F3,
    sh = F - F2,
  };
  // wide buffer
  ap_fixed_base<_AP_W3, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2> t;
  t.V = op.V;
  if (sh >= 0)
    t.V <<= (int) sh;
  t.V >>= i;
  // handle quantization and overflow
  ret = t;
}

//// FIXME
//// These partial specialization ctors allow code like
////   char c = 'a';
////   ap_fixed_base<8, 8, true> x(c);
//// but what bout ap_fixed_base<9, 9, true> y(c) ?
//

#ifndef __SYNTHESIS__
INLINE std::string scientificFormat(std::string& input) {
  if (input.length() == 0) return input;

  size_t decPosition = input.find('.');
  if (decPosition == std::string::npos) decPosition = input.length();

  size_t firstNonZeroPos = 0;
  for (; input[firstNonZeroPos] > '9' || input[firstNonZeroPos] < '1';
       firstNonZeroPos++)
    ;

  int exp;
  if (firstNonZeroPos > decPosition)
    exp = decPosition - firstNonZeroPos;
  else
    exp = decPosition - firstNonZeroPos - 1;
  std::string expString = "";
  if (exp == 0)
    ;
  else if (exp < 0) {
    expString += "e-";
    exp = -exp;
  } else
    expString += "e+";

  if (exp < 10 && exp > 0) {
    expString += '0';
    expString += (char)('0' + exp);
  } else if (exp != 0) {
    std::string tmp;

    std::ostringstream oss;
    oss << exp;

    tmp = oss.str();
    expString += tmp;
  }

  int lastNonZeroPos = (int)(input.length() - 1);
  for (; lastNonZeroPos >= 0; --lastNonZeroPos)
    if (input[lastNonZeroPos] <= '9' && input[lastNonZeroPos] > '0') break;

  std::string ans = "";
  ans += input[firstNonZeroPos];
  if (firstNonZeroPos != (size_t)lastNonZeroPos) {
    ans += '.';
    for (int i = firstNonZeroPos + 1; i <= lastNonZeroPos; i++)
      if (input[i] != '.') ans += input[i];
  }

  ans += expString;
  return ans;
}

INLINE std::string reduceToPrecision(std::string& input, int precision) {
  bool isZero = true;
  size_t inputLen = input.length();
  for (size_t i = 0; i < inputLen && isZero; i++)
    if (input[i] != '.' && input[i] != '0') isZero = false;
  if (isZero) return "0";

  // Find the first valid number, skip '-'
  int FirstNonZeroPos = 0;
  int LastNonZeroPos = (int)inputLen - 1;
  int truncBitPosition = 0;
  size_t decPosition = input.find('.');
  for (; input[FirstNonZeroPos] < '1' || input[FirstNonZeroPos] > '9';
       FirstNonZeroPos++)
    ;

  for (; input[LastNonZeroPos] < '1' || input[LastNonZeroPos] > '9';
       LastNonZeroPos--)
    ;

  if (decPosition == std::string::npos) decPosition = inputLen;
  // Count the valid number, to decide whether we need to truncate
  if ((int)decPosition > LastNonZeroPos) {
    if (LastNonZeroPos - FirstNonZeroPos + 1 <= precision) return input;
    truncBitPosition = FirstNonZeroPos + precision;
  } else if ((int)decPosition < FirstNonZeroPos) { // This is pure decimal
    if (LastNonZeroPos - FirstNonZeroPos + 1 <= precision) {
      if (FirstNonZeroPos - decPosition - 1 < 4) {
        return input;
      } else {
        if (input[0] == '-') {
          std::string tmp = input.substr(1, inputLen - 1);
          return std::string("-") + scientificFormat(tmp);
        } else
          return scientificFormat(input);
      }
    }
    truncBitPosition = FirstNonZeroPos + precision;
  } else {
    if (LastNonZeroPos - FirstNonZeroPos <= precision) return input;
    truncBitPosition = FirstNonZeroPos + precision + 1;
  }

  // duplicate the input string, we want to add "0" before the valid numbers
  // This is easy for quantization, since we may change 9999 to 10000
  std::string ans = "";
  std::string dupInput = "0";
  if (input[0] == '-') {
    ans += '-';
    dupInput += input.substr(1, inputLen - 1);
  } else {
    dupInput += input.substr(0, inputLen);
    ++truncBitPosition;
  }

  // Add 'carry' after truncation, if necessary
  bool carry = dupInput[truncBitPosition] > '4';
  for (int i = truncBitPosition - 1; i >= 0 && carry; i--) {
    if (dupInput[i] == '.') continue;
    if (dupInput[i] == '9')
      dupInput[i] = '0';
    else {
      ++dupInput[i];
      carry = false;
    }
  }

  // bits outside precision range should be set to 0
  if (dupInput[0] == '1')
    FirstNonZeroPos = 0;
  else {
    FirstNonZeroPos = 0;
    while (dupInput[FirstNonZeroPos] < '1' || dupInput[FirstNonZeroPos] > '9')
      ++FirstNonZeroPos;
  }

  unsigned it = FirstNonZeroPos;
  int NValidNumber = 0;
  while (it < dupInput.length()) {
    if (dupInput[it] == '.') {
      ++it;
      continue;
    }
    ++NValidNumber;
    if (NValidNumber > precision) dupInput[it] = '0';
    ++it;
  }

  // Here we wanted to adjust the truncate position and the value
  decPosition = dupInput.find('.');
  if (decPosition == std::string::npos) // When this is integer
    truncBitPosition = (int)dupInput.length();
  else
    for (truncBitPosition = (int)(dupInput.length() - 1); truncBitPosition >= 0;
         --truncBitPosition) {
      if (dupInput[truncBitPosition] == '.') break;
      if (dupInput[truncBitPosition] != '0') {
        truncBitPosition++;
        break;
      }
    }

  if (dupInput[0] == '1')
    dupInput = dupInput.substr(0, truncBitPosition);
  else
    dupInput = dupInput.substr(1, truncBitPosition - 1);

  decPosition = dupInput.find('.');
  if (decPosition != std::string::npos) {
    size_t it = 0;
    for (it = decPosition + 1; dupInput[it] == '0'; it++)
      ;
    if (it - decPosition - 1 < 4) {
      ans += dupInput;
      return ans;
    } else {
      ans += scientificFormat(dupInput);
      return ans;
    }
  } else if ((int)(dupInput.length()) <= precision) {
    ans += dupInput;
    return ans;
  }

  ans += scientificFormat(dupInput);
  return ans;
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE void print(
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) {
  if (_AP_I > 0) {
    ap_int_base<_AP_I, _AP_S> p1;
    p1.V = x.V >> (_AP_W - _AP_I);
    print(p1.V); // print overlaod for .V should exit
  } else {
    printf("0");
  }
  printf(".");
  if (_AP_I < _AP_W) {
    ap_int_base<_AP_W - _AP_I, false> p2;
    p2.V = _AP_ROOT_op_get_range(x.V, 0, _AP_W - _AP_I);
    print(p2.V, false); // print overlaod for .V should exit
  }
}
#endif // ifndef __SYNTHESIS__

// XXX the following two functions have to exist in synthesis,
// as some old HLS Video Library code uses the ostream overload,
// although HLS will later delete I/O function call.

/// Output streaming
//-----------------------------------------------------------------------------
// XXX apcc cannot handle global std::ios_base::Init() brought in by <iostream>
#ifndef AP_AUTOCC
#ifndef __SYNTHESIS__
template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE std::ostream& operator<<(
    std::ostream& out,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) {
  // TODO support std::ios_base::fmtflags
  unsigned width = out.width();
  unsigned precision = out.precision();
  char fill = out.fill();
  std::string str = x.to_string(10, _AP_S);
  str = reduceToPrecision(str, precision);
  if (width > str.length()) {
    for (unsigned i = 0; i < width - str.length(); ++i)
      out << fill;
  }
  out << str;
  return out;
}
#endif // ifndef __SYNTHESIS__

/// Input streaming
// -----------------------------------------------------------------------------
#ifndef __SYNTHESIS__
template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE std::istream& operator>>(
    std::istream& in,
    ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) {
  double d;
  in >> d;
  x = ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(d);
  return in;
}
#endif
#endif // ifndef AP_AUTOCC

/// Operators mixing Integers with ap_fixed_base
// -----------------------------------------------------------------------------
#define AF_BIN_OP_WITH_INT_SF(BIN_OP, C_TYPE, _AP_W2, _AP_S2, RTYPE)     \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,           \
            ap_o_mode _AP_O, int _AP_N>                                  \
  INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType<    \
      _AP_W2, _AP_W2, _AP_S2>::RTYPE                                     \
  operator BIN_OP(                                                       \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \
      C_TYPE i_op) {                                                     \
    return op.operator BIN_OP(ap_int_base<_AP_W2, _AP_S2>(i_op));        \
  }

#define AF_BIN_OP_WITH_INT(BIN_OP, C_TYPE, _AP_W2, _AP_S2, RTYPE)           \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N>                                     \
  INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType<       \
      _AP_W2, _AP_W2, _AP_S2>::RTYPE                                        \
  operator BIN_OP(                                                          \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,    \
      C_TYPE i_op) {                                                        \
    return op.operator BIN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }                                                                         \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N>                                     \
  INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType<       \
      _AP_W2, _AP_W2, _AP_S2>::RTYPE                                        \
  operator BIN_OP(                                                          \
      C_TYPE i_op,                                                          \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {  \
    return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator BIN_OP(op); \
  }

#define AF_REL_OP_WITH_INT(REL_OP, C_TYPE, _AP_W2, _AP_S2)                  \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N>                                     \
  INLINE bool operator REL_OP(                                              \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,    \
      C_TYPE i_op) {                                                        \
    return op.operator REL_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }                                                                         \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N>                                     \
  INLINE bool operator REL_OP(                                              \
      C_TYPE i_op,                                                          \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {  \
    return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator REL_OP(op); \
  }

#define AF_ASSIGN_OP_WITH_INT(ASSIGN_OP, C_TYPE, _AP_W2, _AP_S2)               \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,                 \
            ap_o_mode _AP_O, int _AP_N>                                        \
  INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>&              \
  operator ASSIGN_OP(                                                          \
      ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,             \
      C_TYPE i_op) {                                                           \
    return op.operator ASSIGN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }

#define AF_ASSIGN_OP_WITH_INT_SF(ASSIGN_OP, C_TYPE, _AP_W2, _AP_S2)  \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,       \
            ap_o_mode _AP_O, int _AP_N>                              \
  INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>&    \
  operator ASSIGN_OP(                                                \
      ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,   \
      C_TYPE i_op) {                                                 \
    return op.operator ASSIGN_OP(ap_int_base<_AP_W2, _AP_S2>(i_op)); \
  }

#define ALL_AF_OP_WITH_INT(C_TYPE, BITS, SIGN)               \
  AF_BIN_OP_WITH_INT(+, C_TYPE, (BITS), (SIGN), plus)     \
  AF_BIN_OP_WITH_INT(-, C_TYPE, (BITS), (SIGN), minus)    \
  AF_BIN_OP_WITH_INT(*, C_TYPE, (BITS), (SIGN), mult)     \
  AF_BIN_OP_WITH_INT(/, C_TYPE, (BITS), (SIGN), div)      \
  AF_BIN_OP_WITH_INT(&, C_TYPE, (BITS), (SIGN), logic)    \
  AF_BIN_OP_WITH_INT(|, C_TYPE, (BITS), (SIGN), logic)    \
  AF_BIN_OP_WITH_INT(^, C_TYPE, (BITS), (SIGN), logic)    \
  AF_BIN_OP_WITH_INT_SF(>>, C_TYPE, (BITS), (SIGN), lhs)  \
  AF_BIN_OP_WITH_INT_SF(<<, C_TYPE, (BITS), (SIGN), lhs)  \
                                                          \
  AF_ASSIGN_OP_WITH_INT(+=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(-=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(*=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(/=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(&=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(|=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT(^=, C_TYPE, (BITS), (SIGN))     \
  AF_ASSIGN_OP_WITH_INT_SF(>>=, C_TYPE, (BITS), (SIGN)) \
  AF_ASSIGN_OP_WITH_INT_SF(<<=, C_TYPE, (BITS), (SIGN)) \
                                                          \
  AF_REL_OP_WITH_INT(>, C_TYPE, (BITS), (SIGN))           \
  AF_REL_OP_WITH_INT(<, C_TYPE, (BITS), (SIGN))           \
  AF_REL_OP_WITH_INT(>=, C_TYPE, (BITS), (SIGN))          \
  AF_REL_OP_WITH_INT(<=, C_TYPE, (BITS), (SIGN))          \
  AF_REL_OP_WITH_INT(==, C_TYPE, (BITS), (SIGN))          \
  AF_REL_OP_WITH_INT(!=, C_TYPE, (BITS), (SIGN))

ALL_AF_OP_WITH_INT(bool, 1, false)
ALL_AF_OP_WITH_INT(char, 8, CHAR_IS_SIGNED)
ALL_AF_OP_WITH_INT(signed char, 8, true)
ALL_AF_OP_WITH_INT(unsigned char, 8, false)
ALL_AF_OP_WITH_INT(short, _AP_SIZE_short, true)
ALL_AF_OP_WITH_INT(unsigned short, _AP_SIZE_short, false)
ALL_AF_OP_WITH_INT(int, _AP_SIZE_int, true)
ALL_AF_OP_WITH_INT(unsigned int, _AP_SIZE_int, false)
ALL_AF_OP_WITH_INT(long, _AP_SIZE_long, true)
ALL_AF_OP_WITH_INT(unsigned long, _AP_SIZE_long, false)
ALL_AF_OP_WITH_INT(ap_slong, _AP_SIZE_ap_slong, true)
ALL_AF_OP_WITH_INT(ap_ulong, _AP_SIZE_ap_slong, false)

#undef ALL_AF_OP_WITH_INT
#undef AF_BIN_OP_WITH_INT
#undef AF_BIN_OP_WITH_INT_SF
#undef AF_ASSIGN_OP_WITH_INT
#undef AF_ASSIGN_OP_WITH_INT_SF
#undef AF_REL_OP_WITH_INT

/*
 * **********************************************************************
 * TODO
 * There is no operator defined with float/double/long double, so that
 * code like
 *   ap_fixed<8,4> a = 1.5f;
 *   a += 0.5f;
 * will fail in compilation.
 * Operator with warning about conversion might be wanted.
 * **********************************************************************
 */

#define AF_BIN_OP_WITH_AP_INT(BIN_OP, RTYPE)                                \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>            \
  INLINE typename ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>::template RType<    \
      _AP_W, _AP_I, _AP_S>::RTYPE                                           \
  operator BIN_OP(                                                          \
      const ap_int_base<_AP_W2, _AP_S2>& i_op,                              \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {  \
    return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator BIN_OP(op); \
  }                                                                         \
                                                                            \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>            \
  INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType<       \
      _AP_W2, _AP_W2, _AP_S2>::RTYPE                                        \
  operator BIN_OP(                                                          \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,    \
      const ap_int_base<_AP_W2, _AP_S2>& i_op) {                            \
    return op.operator BIN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }

#define AF_REL_OP_WITH_AP_INT(REL_OP)                                       \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>            \
  INLINE bool operator REL_OP(                                              \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,    \
      const ap_int_base<_AP_W2, _AP_S2>& i_op) {                            \
    return op.operator REL_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }                                                                         \
                                                                            \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,              \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>            \
  INLINE bool operator REL_OP(                                              \
      const ap_int_base<_AP_W2, _AP_S2>& i_op,                              \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {  \
    return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator REL_OP(op); \
  }

#define AF_ASSIGN_OP_WITH_AP_INT(ASSIGN_OP)                                    \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,                 \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>               \
  INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>&              \
  operator ASSIGN_OP(                                                          \
      ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op,             \
      const ap_int_base<_AP_W2, _AP_S2>& i_op) {                               \
    return op.operator ASSIGN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \
  }                                                                            \
                                                                               \
  template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q,                 \
            ap_o_mode _AP_O, int _AP_N, int _AP_W2, bool _AP_S2>               \
  INLINE ap_int_base<_AP_W2, _AP_S2>& operator ASSIGN_OP(                      \
      ap_int_base<_AP_W2, _AP_S2>& i_op,                                       \
      const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) {     \
    return i_op.operator ASSIGN_OP(op.to_ap_int_base());                       \
  }

AF_BIN_OP_WITH_AP_INT(+, plus)
AF_BIN_OP_WITH_AP_INT(-, minus)
AF_BIN_OP_WITH_AP_INT(*, mult)
AF_BIN_OP_WITH_AP_INT(/, div)
AF_BIN_OP_WITH_AP_INT(&, logic)
AF_BIN_OP_WITH_AP_INT(|, logic)
AF_BIN_OP_WITH_AP_INT(^, logic)

#undef AF_BIN_OP_WITH_AP_INT

AF_ASSIGN_OP_WITH_AP_INT(+=)
AF_ASSIGN_OP_WITH_AP_INT(-=)
AF_ASSIGN_OP_WITH_AP_INT(*=)
AF_ASSIGN_OP_WITH_AP_INT(/=)
AF_ASSIGN_OP_WITH_AP_INT(&=)
AF_ASSIGN_OP_WITH_AP_INT(|=)
AF_ASSIGN_OP_WITH_AP_INT(^=)

#undef AF_ASSIGN_OP_WITH_AP_INT

AF_REL_OP_WITH_AP_INT(==)
AF_REL_OP_WITH_AP_INT(!=)
AF_REL_OP_WITH_AP_INT(>)
AF_REL_OP_WITH_AP_INT(>=)
AF_REL_OP_WITH_AP_INT(<)
AF_REL_OP_WITH_AP_INT(<=)

#undef AF_REL_OP_WITH_AP_INT

// Relational Operators with double
template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator==(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator==(op1);
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator!=(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator!=(op1);
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator>(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator<(op1);
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator>=(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator<=(op1);
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator<(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator>(op1);
}

template <int _AP_W, int _AP_I, bool _AP_S, ap_q_mode _AP_Q, ap_o_mode _AP_O,
          int _AP_N>
INLINE bool operator<=(
    double op1,
    const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) {
  return op2.operator>=(op1);
}

#endif // ifndef __cplusplus else

#endif // ifndef __AP_FIXED_BASE_H__ else

// -*- cpp -*-
