// xcomplex internal header -*-c++-*-
// Copyright 2009-2010 IAR Systems AB.
#ifndef _DLIB5_XCOMPLEX_
#define _DLIB5_XCOMPLEX_

#ifndef _SYSTEM_BUILD
#pragma system_include
#endif

// TEMPLATE FUNCTION operator+
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator+(const _CMPLX(_Ty)& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // add complex to complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp += _Right);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator+(const _CMPLX(_Ty)& _Left,
                      const _Ty& _Right)
{       // add real to complex
  _CMPLX(_Ty) _Tmp(_Left);
  _Tmp.real(_Tmp.real() + _Right);
  return (_Tmp);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator+(const _Ty& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // add complex to real
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp += _Right);
}

// TEMPLATE FUNCTION operator-
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator-(const _CMPLX(_Ty)& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // subtract complex from complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp -= _Right);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator-(const _CMPLX(_Ty)& _Left,
                      const _Ty& _Right)
{       // subtract real from complex
  _CMPLX(_Ty) _Tmp(_Left);
  _Tmp.real(_Tmp.real() - _Right);
  return (_Tmp);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator-(const _Ty& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // subtract complex from real
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp -= _Right);
}

// TEMPLATE FUNCTION operator*
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator*(const _CMPLX(_Ty)& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // multiply complex by complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp *= _Right);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator*(const _CMPLX(_Ty)& _Left,
                      const _Ty& _Right)
{       // multiply complex by real
  _CMPLX(_Ty) _Tmp(_Left);
  _Tmp.real(_Tmp.real() * _Right);
  _Tmp.imag(_Tmp.imag() * _Right);
  return (_Tmp);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator*(const _Ty& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // multiply real by complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp *= _Right);
}

// TEMPLATE FUNCTION operator/
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator/(const _CMPLX(_Ty)& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // divide complex by complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp /= _Right);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator/(const _CMPLX(_Ty)& _Left,
                      const _Ty& _Right)
{       // divide complex by real
  _CMPLX(_Ty) _Tmp(_Left);
  _Tmp.real(_Tmp.real() / _Right);
  _Tmp.imag(_Tmp.imag() / _Right);
  return (_Tmp);
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) operator/(const _Ty& _Left,
                      const _CMPLX(_Ty)& _Right)
{       // divide real by complex
  _CMPLX(_Ty) _Tmp(_Left);
  return (_Tmp /= _Right);
}

// TEMPLATE FUNCTION UNARY operator+
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator+(const _CMPLX(_Ty)& _Left)
{       // return +complex
  return (_CMPLX(_Ty)(_Left));
}

// TEMPLATE FUNCTION UNARY operator-
_TMPLT(_Ty) inline
_CMPLX(_Ty) operator-(const _CMPLX(_Ty)& _Left)
{       // return -complex
  return (_CMPLX(_Ty)(-_Left.real(), -_Left.imag()));
}

// TEMPLATE FUNCTION operator==
_TMPLT(_Ty) inline
bool operator==(const _CMPLX(_Ty)& _Left,
                const _CMPLX(_Ty)& _Right)
{       // test complex equal to complex
  return (_Left.real() == _Right.real()
          && _Left.imag() == _Right.imag());
}

_TMPLT(_Ty) inline
bool operator==(const _CMPLX(_Ty)& _Left,
                const _Ty& _Right)
{       // test real equal to complex
  return (_Left.real() == _Right
          && _Left.imag() ==  0);
}

_TMPLT(_Ty) inline
bool operator==(const _Ty& _Left,
                const _CMPLX(_Ty)& _Right)
{       // test complex equal to real
  return (_Left == _Right.real()
          && 0 == _Right.imag());
}

// TEMPLATE FUNCTION operator!=
_TMPLT(_Ty) inline
bool operator!=(const _CMPLX(_Ty)& _Left,
                const _CMPLX(_Ty)& _Right)
{       // test complex not equal to complex
  return (!(_Left == _Right));
}

_TMPLT(_Ty) inline
bool operator!=(const _CMPLX(_Ty)& _Left,
                const _Ty& _Right)
{       // test real not equal to complex
  return (!(_Left == _Right));
}

_TMPLT(_Ty) inline
bool operator!=(const _Ty& _Left,
                const _CMPLX(_Ty)& _Right)
{       // test complex not equal to real
  return (!(_Left == _Right));
}

// TEMPLATE FUNCTION imag
_TMPLT(_Ty) inline
_Ty imag(const _CMPLX(_Ty)& _Left)
{       // return imaginary component
  return (_Left.imag());
}

// TEMPLATE FUNCTION real
_TMPLT(_Ty) inline
_Ty real(const _CMPLX(_Ty)& _Left)
{       // return real component
  return (_Left.real());
}

// TEMPLATE FUNCTION _Fabs
_TMPLT(_Ty) inline
_Ty _Fabs(const _CMPLX(_Ty)& _Left, int *_Pexp)
{       // return magnitude and scale factor
  *_Pexp = 0;
  _Ty _Av = real(_Left);
  _Ty _Bv = imag(_Left);

  if (_CTR(_Ty)::_Isinf(_Av) || _CTR(_Ty)::_Isinf(_Bv))
    return (_CTR(_Ty)::_Infv(_Bv)); // at least one component is INF
  else if (_CTR(_Ty)::_Isnan(_Av))
    return (_Av);   //      real component is NaN
  else if (_CTR(_Ty)::_Isnan(_Bv))
    return (_Bv);   // imaginary component is NaN
  else
  {       // neither component is NaN or INF
    if (_Av < 0)
      _Av = -_Av;
    if (_Bv < 0)
      _Bv = -_Bv;
    if (_Av < _Bv)
    {       // ensure that |_Bv| <= |_Av|
      _Ty _Tmp = _Av;
      _Av = _Bv;
      _Bv = _Tmp;
    }

    if (_Av == 0)
      return (_Av);   // |0| == 0
    if (1 <= _Av)
      *_Pexp = 2, _Av = _Av * (_Ty)0.25, _Bv = _Bv * (_Ty)0.25;
    else
      *_Pexp = -2, _Av = _Av * 4, _Bv = _Bv * 4;

    _Ty _Tmp = _Av - _Bv;
    if (_Tmp == _Av)
      return (_Av);   // _Bv unimportant
    else if (_Bv < _Tmp)
    {       // use simple approximation
      const _Ty _Qv = _Av / _Bv;
      return (_Av + _Bv / (_Qv + _CTR(_Ty)::sqrt(_Qv * _Qv + 1)));
    }
    else
    {       // use 1 1/2 precision to preserve bits
      static const _Ty _Root2 =
        (_Ty)1.4142135623730950488016887242096981L;
      static const _Ty _Oneplusroot2high =
        (_Ty)(10125945.0 / 4194304.0);  // exact if prec >= 24 bits
      static const _Ty _Oneplusroot2low =
        (_Ty)1.4341252375973918872420969807856967e-7L;

      const _Ty _Qv = _Tmp / _Bv;
      const _Ty _Rv = (_Qv + 2) * _Qv;
      const _Ty _Sv = _Rv / (_Root2 + _CTR(_Ty)::sqrt(_Rv + 2))
        + _Oneplusroot2low + _Qv + _Oneplusroot2high;
      return (_Av + _Bv / _Sv);
    }
  }
}

#if _HAS_DINKUM_COMPLEX 
// The 'casts' in these implementations are there to cast from the 
// C++ class complex type to the C++ and C struct complex type. 
// The class complex type is derived from the struct complex type. 
//

// TEMPLATE FUNCTION abs
_TMPLT(_Ty) inline
_Ty abs(const _CMPLX(_Ty)& _Left)
{       // return abs
  _CMPLX(_Ty)::_MyBaseT const &_L = _Left;
  return (_CSTD abs(_L));
}

// TEMPLATE FUNCTION acos
_TMPLT(_Ty) inline
_CMPLX(_Ty) acos(const _CMPLX(_Ty)& _Left)
{       // return acos
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD acos(_L));
}

// TEMPLATE FUNCTION acosh
_TMPLT(_Ty) inline
_CMPLX(_Ty) acosh(const _CMPLX(_Ty)& _Left)
{       // return acosh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD acosh(_L));
}

// TEMPLATE FUNCTION asin
_TMPLT(_Ty) inline
_CMPLX(_Ty) asin(const _CMPLX(_Ty)& _Left)
{       // return asin
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD asin(_L));
}

// TEMPLATE FUNCTION asinh
_TMPLT(_Ty) inline
_CMPLX(_Ty) asinh(const _CMPLX(_Ty)& _Left)
{       // return asinh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD asinh(_L));
}

// TEMPLATE FUNCTION atan
_TMPLT(_Ty) inline
_CMPLX(_Ty) atan(const _CMPLX(_Ty)& _Left)
{       // return atan
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD atan(_L));
}

// TEMPLATE FUNCTION atanh
_TMPLT(_Ty) inline
_CMPLX(_Ty) atanh(const _CMPLX(_Ty)& _Left)
{       // return atanh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD atanh(_L));
}

// TEMPLATE FUNCTION cosh
_TMPLT(_Ty) inline
_CMPLX(_Ty) cosh(const _CMPLX(_Ty)& _Left)
{       // return cosh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD cosh(_L));
}

// TEMPLATE FUNCTION exp
_TMPLT(_Ty) inline
_CMPLX(_Ty) exp(const _CMPLX(_Ty)& _Left)
{       // return exp
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD exp(_L));
}

// TEMPLATE FUNCTION log
_TMPLT(_Ty) inline
_CMPLX(_Ty) log(const _CMPLX(_Ty)& _Left)
{       // return log
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD log(_L));
}

// TEMPLATE FUNCTION pow
_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left, const _Ty& _Right)
{       // return pow
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  _CMPLX(_Ty) _R(_Right);
  _CMPLX(_Ty)::_MyBaseT const &_R2 = _R;
  return (_CSTD pow(_L, _R2));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left, int _Right)
{       // return pow
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  _CMPLX(_Ty) _R(_Right);
  _CMPLX(_Ty)::_MyBaseT const &_R2 = _R;
  return (_CSTD pow(_L, _R2));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _Ty& _Left, const _CMPLX(_Ty)& _Right)
{       // return pow
  _CMPLX(_Ty) _L(_Left);
  _CMPLX(_Ty)::_MyBaseT const &_L2 = _L;
  _CMPLX(_Ty)::_MyBaseT const & _R = _Right;
  return (_CSTD pow(_L2, _R));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left,
                const _CMPLX(_Ty)& _Right)
{       // return pow
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  _CMPLX(_Ty)::_MyBaseT const & _R = _Right;
  return (_CSTD pow(_L, _R));
}

// TEMPLATE FUNCTION sinh
_TMPLT(_Ty) inline
_CMPLX(_Ty) sinh(const _CMPLX(_Ty)& _Left)
{       // return sinh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD sinh(_L));
}

// TEMPLATE FUNCTION sqrt
_TMPLT(_Ty) inline
_CMPLX(_Ty) sqrt(const _CMPLX(_Ty)& _Left)
{       // return sqrt
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD sqrt(_L));
}

// TEMPLATE FUNCTION tanh
_TMPLT(_Ty) inline
_CMPLX(_Ty) tanh(const _CMPLX(_Ty)& _Left)
{       // return tanh
  _CMPLX(_Ty)::_MyBaseT const & _L = _Left;
  return (_CSTD tanh(_L));
}

#else /* _HAS_DINKUM_COMPLEX */
// TEMPLATE FUNCTION abs
_TMPLT(_Ty) inline
_Ty abs(const _CMPLX(_Ty)& _Left)
{       // return |complex| as real
  int _Leftexp;
  _Ty _Rho = _Fabs(_Left, &_Leftexp);     // get magnitude and scale factor

  if (_Leftexp == 0)
    return (_Rho);  // no scale factor
  else
    return (_CTR(_Ty)::ldexp(_Rho, _Leftexp));      // scale result
}

// TEMPLATE FUNCTION cosh
_TMPLT(_Ty) inline
_CMPLX(_Ty) cosh(const _CMPLX(_Ty)& _Left)
{       // return cosh(complex)
  return (_CMPLX(_Ty)(
            _CTR(_Ty)::_Cosh(real(_Left), _CTR(_Ty)::cos(imag(_Left))),
            _CTR(_Ty)::_Sinh(real(_Left), _CTR(_Ty)::sin(imag(_Left)))));
}

// TEMPLATE FUNCTION exp
_TMPLT(_Ty) inline
_CMPLX(_Ty) exp(const _CMPLX(_Ty)& _Left)
{       // return exp(complex)
  _Ty _Real(real(_Left)), _Imag(real(_Left));
  _CTR(_Ty)::_Exp(&_Real, _CTR(_Ty)::cos(imag(_Left)), 0);
  _CTR(_Ty)::_Exp(&_Imag, _CTR(_Ty)::sin(imag(_Left)), 0);
  return (_CMPLX(_Ty)(_Real, _Imag));
}

// TEMPLATE FUNCTION log
_TMPLT(_Ty) inline
_CMPLX(_Ty) log(const _CMPLX(_Ty)& _Left)
{       // return log(complex)
  _Ty _Theta = _CTR(_Ty)::atan2(imag(_Left), real(_Left));        // get phase

  if (_CTR(_Ty)::_Isnan(_Theta))
    return (_CMPLX(_Ty)(_Theta, _Theta));   // real or imag is NaN
  else
  {       // use 1 1/2 precision to preserve bits
    static const _Ty _Cm = (_Ty)(22713.0L / 32768.0L);
    static const _Ty _Cl =
      (_Ty)1.4286068203094172321214581765680755e-6L;
    int _Leftexp;
    _Ty _Rho = _Fabs(_Left, &_Leftexp);     // get magnitude and scale factor

    _Ty _Leftn = (_Ty)_Leftexp;
    _CMPLX(_Ty) _Tmp(
      _Rho == 0 ? -_CTR(_Ty)::_Infv(_Rho)     // log(0) == -INF
      : _CTR(_Ty)::_Isinf(_Rho) ? _Rho        // log(INF) == INF
      : _CTR(_Ty)::log(_Rho) + _Leftn * _Cl + _Leftn * _Cm,
      _Theta);
    return (_Tmp);
  }
}

// TEMPLATE FUNCTION pow
_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left, const _Ty& _Right)
{       // return complex ^ real
  if (imag(_Left) == 0 && 0 < real(_Left))
    return (_CMPLX(_Ty)(_CTR(_Ty)::pow(real(_Left), _Right)));
  else
    return (exp(_Right * log(_Left)));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left, int _Right)
{       // return complex ^ integer
  _CMPLX(_Ty) _Tmp = _Left;
  unsigned int _Count = _Right;

  if (_Right < 0)
    _Count = 0 - _Count;    // safe negation as unsigned

  for (_CMPLX(_Ty) _Zv = _CMPLX(_Ty)(1); ; _Tmp *= _Tmp)
  {       // fold in _Left ^ (2 ^ _Count) as needed
    if ((_Count & 1) != 0)
      _Zv *= _Tmp;
    if ((_Count >>= 1) == 0)
      return (_Right < 0 ? _CMPLX(_Ty)(1) / _Zv : _Zv);
  }
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _Ty& _Left, const _CMPLX(_Ty)& _Right)
{       // return real ^ complex
  if (imag(_Right) == 0)
    return (_CMPLX(_Ty)(_CTR(_Ty)::pow(_Left, real(_Right))));
  else if (0 < _Left)
    return (exp(_Right * _CTR(_Ty)::log(_Left)));
  else
    return (exp(_Right * log(_CMPLX(_Ty)(_Left))));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) pow(const _CMPLX(_Ty)& _Left,
                const _CMPLX(_Ty)& _Right)
{       // return complex ^ complex
  if (imag(_Right) == 0)
    return (pow(_Left, real(_Right)));
  else if (imag(_Left) == 0)
    return (_CMPLX(_Ty)(pow(real(_Left), _Right)));
  else
    return (exp(_Right * log(_Left)));
}

// TEMPLATE FUNCTION sinh
_TMPLT(_Ty) inline
_CMPLX(_Ty) sinh(const _CMPLX(_Ty)& _Left)
{       // return sinh(complex)
  return (_CMPLX(_Ty)(
            _CTR(_Ty)::_Sinh(real(_Left), _CTR(_Ty)::cos(imag(_Left))),
            _CTR(_Ty)::_Cosh(real(_Left), _CTR(_Ty)::sin(imag(_Left)))));
}

// TEMPLATE FUNCTION sqrt
_TMPLT(_Ty) inline
_CMPLX(_Ty) sqrt(const _CMPLX(_Ty)& _Left)
{       // return sqrt(complex)
  int _Leftexp;
  _Ty _Rho = _Fabs(_Left, &_Leftexp);     // get magnitude and scale factor

  if (_Leftexp == 0)
    return (_CMPLX(_Ty)(_Rho, _Rho));       // argument is zero, INF, or NaN
  else
  {       // compute in safest quadrant
    _Ty _Realmag = _CTR(_Ty)::ldexp(real(_Left) < 0
                                    ? - real(_Left) : real(_Left), -_Leftexp);
    _Rho = _CTR(_Ty)::ldexp(_CTR(_Ty)::sqrt(
                              2 * (_Realmag + _Rho)), _Leftexp / 2 - 1);

    if (0 <= real(_Left))
      return (_CMPLX(_Ty)(_Rho, imag(_Left) / (2 * _Rho)));
    else if (imag(_Left) < 0)
      return (_CMPLX(_Ty)(-imag(_Left) / (2 * _Rho), -_Rho));
    else
      return (_CMPLX(_Ty)(imag(_Left) / (2 * _Rho), _Rho));
  }
}

// TEMPLATE FUNCTION tanh
_TMPLT(_Ty) inline
_CMPLX(_Ty) tanh(const _CMPLX(_Ty)& _Left)
{       // return tanh(complex)
  _Ty _Tv = _CTR(_Ty)::tan(imag(_Left));
  _Ty _Sv = _CTR(_Ty)::_Sinh(real(_Left), (_Ty)(1));
  _Ty _Bv = _Sv *((_Ty)(1) + _Tv * _Tv);
  _Ty _Dv = (_Ty)(1) + _Bv * _Sv;

  if (_CTR(_Ty)::_Isinf(_Dv))
    return (_CMPLX(_Ty)(_Sv < (_Ty)0 ? (_Ty)(-1) : (_Ty)(1),
                        _Tv * (_Ty)(0)));
  else
    return (_CMPLX(_Ty)((_CTR(_Ty)::sqrt((_Ty)(1) + _Sv * _Sv))
                        * _Bv / _Dv, _Tv / _Dv));
}
#endif /* _HAS_DINKUM_COMPLEX */

// TEMPLATE FUNCTION arg
_TMPLT(_Ty) inline
_Ty arg(const _CMPLX(_Ty)& _Left)
{       // return phase angle of complex as real
  return (_CTR(_Ty)::atan2(imag(_Left), real(_Left)));
}

// TEMPLATE FUNCTION conj
_TMPLT(_Ty) inline
_CMPLX(_Ty) conj(const _CMPLX(_Ty)& _Left)
{       // return complex conjugate
  return (_CMPLX(_Ty)(real(_Left), -imag(_Left)));
}

// TEMPLATE FUNCTION cos
_TMPLT(_Ty) inline
_CMPLX(_Ty) cos(const _CMPLX(_Ty)& _Left)
{       // return cos(complex)
  return (_CMPLX(_Ty)(
            _CTR(_Ty)::_Cosh(imag(_Left), _CTR(_Ty)::cos(real(_Left))),
            -_CTR(_Ty)::_Sinh(imag(_Left),
                              _CTR(_Ty)::sin(real(_Left)))));
}

// TEMPLATE FUNCTION log10
_TMPLT(_Ty) inline
_CMPLX(_Ty) log10(const _CMPLX(_Ty)& _Left)
{       // return log10(complex)
  return (log(_Left) * (_Ty)0.43429448190325182765112891891660508L);
}

// TEMPLATE FUNCTION norm
_TMPLT(_Ty) inline
_Ty norm(const _CMPLX(_Ty)& _Left)
{       // return squared magnitude
  return (real(_Left) * real(_Left) + imag(_Left) * imag(_Left));
}

// TEMPLATE FUNCTION polar
_TMPLT(_Ty) inline
_CMPLX(_Ty) polar(const _Ty& _Rho, const _Ty& _Theta)
{       // return _Rho * exp(i * _Theta) as complex
  return (_CMPLX(_Ty)(_Rho * _CTR(_Ty)::cos(_Theta),
                      _Rho * _CTR(_Ty)::sin(_Theta)));
}

_TMPLT(_Ty) inline
_CMPLX(_Ty) polar(const _Ty& _Rho)
{       // return _Rho * exp(i * 0) as complex
  return (_CMPLX(_Ty)(_Rho, (_Ty)0));
}

// TEMPLATE FUNCTION sin
_TMPLT(_Ty) inline
_CMPLX(_Ty) sin(const _CMPLX(_Ty)& _Left)
{       // return sin(complex)
  return (_CMPLX(_Ty)(
            _CTR(_Ty)::_Cosh(imag(_Left), _CTR(_Ty)::sin(real(_Left))),
            _CTR(_Ty)::_Sinh(imag(_Left), _CTR(_Ty)::cos(real(_Left)))));
}

// TEMPLATE FUNCTION tan
_TMPLT(_Ty) inline
_CMPLX(_Ty) tan(const _CMPLX(_Ty)& _Left)
{       // return tan(complex)
  _CMPLX(_Ty) _Zv(tanh(_CMPLX(_Ty)(-imag(_Left), real(_Left))));
  return (_CMPLX(_Ty)(imag(_Zv), -real(_Zv)));
}


#undef _DLIB5_XCOMPLEX_       /* SIC: may be included multiple times */
#endif /* _DLIB5_XCOMPLEX_ */

/*
 * Copyright (c) 1992-2009 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 V5.04:0576 */
