// -*- C++ -*-
//===--------------------------- complex ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCUDACXX_COMPLEX
#define _LIBCUDACXX_COMPLEX

/*
    complex synopsis

namespace std
{

template<class T>
class complex
{
public:
    typedef T value_type;

    complex(const T& re = T(), const T& im = T()); // constexpr in C++14
    complex(const complex&);  // constexpr in C++14
    template<class X> complex(const complex<X>&);  // constexpr in C++14

    T real() const; // constexpr in C++14
    T imag() const; // constexpr in C++14

    void real(T); // constexpr in C++20
    void imag(T); // constexpr in C++20

    complex<T>& operator= (const T&); // constexpr in C++20
    complex<T>& operator+=(const T&); // constexpr in C++20
    complex<T>& operator-=(const T&); // constexpr in C++20
    complex<T>& operator*=(const T&); // constexpr in C++20
    complex<T>& operator/=(const T&); // constexpr in C++20

    complex& operator=(const complex&); // constexpr in C++20
    template<class X> complex<T>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<T>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<float>
{
public:
    typedef float value_type;

    constexpr complex(float re = 0.0f, float im = 0.0f);
    explicit constexpr complex(const complex<double>&);
    explicit constexpr complex(const complex<long double>&);

    constexpr float real() const;
    void real(float); // constexpr in C++20
    constexpr float imag() const;
    void imag(float); // constexpr in C++20

    complex<float>& operator= (float); // constexpr in C++20
    complex<float>& operator+=(float); // constexpr in C++20
    complex<float>& operator-=(float); // constexpr in C++20
    complex<float>& operator*=(float); // constexpr in C++20
    complex<float>& operator/=(float); // constexpr in C++20

    complex<float>& operator=(const complex<float>&); // constexpr in C++20
    template<class X> complex<float>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<float>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<double>
{
public:
    typedef double value_type;

    constexpr complex(double re = 0.0, double im = 0.0);
    constexpr complex(const complex<float>&);
    explicit constexpr complex(const complex<long double>&);

    constexpr double real() const;
    void real(double); // constexpr in C++20
    constexpr double imag() const;
    void imag(double); // constexpr in C++20

    complex<double>& operator= (double); // constexpr in C++20
    complex<double>& operator+=(double); // constexpr in C++20
    complex<double>& operator-=(double); // constexpr in C++20
    complex<double>& operator*=(double); // constexpr in C++20
    complex<double>& operator/=(double); // constexpr in C++20
    complex<double>& operator=(const complex<double>&); // constexpr in C++20

    template<class X> complex<double>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<double>& operator/=(const complex<X>&); // constexpr in C++20
};

template<>
class complex<long double>
{
public:
    typedef long double value_type;

    constexpr complex(long double re = 0.0L, long double im = 0.0L);
    constexpr complex(const complex<float>&);
    constexpr complex(const complex<double>&);

    constexpr long double real() const;
    void real(long double); // constexpr in C++20
    constexpr long double imag() const;
    void imag(long double); // constexpr in C++20

    complex<long double>& operator=(const complex<long double>&); // constexpr in C++20
    complex<long double>& operator= (long double); // constexpr in C++20
    complex<long double>& operator+=(long double); // constexpr in C++20
    complex<long double>& operator-=(long double); // constexpr in C++20
    complex<long double>& operator*=(long double); // constexpr in C++20
    complex<long double>& operator/=(long double); // constexpr in C++20

    template<class X> complex<long double>& operator= (const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator+=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator-=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator*=(const complex<X>&); // constexpr in C++20
    template<class X> complex<long double>& operator/=(const complex<X>&); // constexpr in C++20
};

// 26.3.6 operators:
template<class T> complex<T> operator+(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator+(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator+(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator-(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator*(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator*(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator*(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator/(const complex<T>&, const complex<T>&); // constexpr in C++20
template<class T> complex<T> operator/(const complex<T>&, const T&);          // constexpr in C++20
template<class T> complex<T> operator/(const T&, const complex<T>&);          // constexpr in C++20
template<class T> complex<T> operator+(const complex<T>&);                    // constexpr in C++20
template<class T> complex<T> operator-(const complex<T>&);                    // constexpr in C++20
template<class T> bool operator==(const complex<T>&, const complex<T>&); // constexpr in C++14
template<class T> bool operator==(const complex<T>&, const T&); // constexpr in C++14
template<class T> bool operator==(const T&, const complex<T>&); // constexpr in C++14
template<class T> bool operator!=(const complex<T>&, const complex<T>&); // constexpr in C++14
template<class T> bool operator!=(const complex<T>&, const T&); // constexpr in C++14
template<class T> bool operator!=(const T&, const complex<T>&); // constexpr in C++14

template<class T, class charT, class traits>
  basic_istream<charT, traits>&
  operator>>(basic_istream<charT, traits>&, complex<T>&);
template<class T, class charT, class traits>
  basic_ostream<charT, traits>&
  operator<<(basic_ostream<charT, traits>&, const complex<T>&);

// 26.3.7 values:

template<class T>              T real(const complex<T>&); // constexpr in C++14
                     long double real(long double);       // constexpr in C++14
                          double real(double);            // constexpr in C++14
template<Integral T>      double real(T);                 // constexpr in C++14
                          float  real(float);             // constexpr in C++14

template<class T>              T imag(const complex<T>&); // constexpr in C++14
                     long double imag(long double);       // constexpr in C++14
                          double imag(double);            // constexpr in C++14
template<Integral T>      double imag(T);                 // constexpr in C++14
                          float  imag(float);             // constexpr in C++14

template<class T> T abs(const complex<T>&);

template<class T>              T arg(const complex<T>&);
                     long double arg(long double);
                          double arg(double);
template<Integral T>      double arg(T);
                          float  arg(float);

template<class T>              T norm(const complex<T>&); // constexpr in C++20
                     long double norm(long double);       // constexpr in C++20
                          double norm(double);            // constexpr in C++20
template<Integral T>      double norm(T);                 // constexpr in C++20
                          float  norm(float);             // constexpr in C++20

template<class T>      complex<T>           conj(const complex<T>&); // constexpr in C++20
                       complex<long double> conj(long double);       // constexpr in C++20
                       complex<double>      conj(double);            // constexpr in C++20
template<Integral T>   complex<double>      conj(T);                 // constexpr in C++20
                       complex<float>       conj(float);             // constexpr in C++20

template<class T>    complex<T>           proj(const complex<T>&);
                     complex<long double> proj(long double);
                     complex<double>      proj(double);
template<Integral T> complex<double>      proj(T);
                     complex<float>       proj(float);

template<class T> complex<T> polar(const T&, const T& = T());

// 26.3.8 transcendentals:
template<class T> complex<T> acos(const complex<T>&);
template<class T> complex<T> asin(const complex<T>&);
template<class T> complex<T> atan(const complex<T>&);
template<class T> complex<T> acosh(const complex<T>&);
template<class T> complex<T> asinh(const complex<T>&);
template<class T> complex<T> atanh(const complex<T>&);
template<class T> complex<T> cos (const complex<T>&);
template<class T> complex<T> cosh (const complex<T>&);
template<class T> complex<T> exp (const complex<T>&);
template<class T> complex<T> log (const complex<T>&);
template<class T> complex<T> log10(const complex<T>&);

template<class T> complex<T> pow(const complex<T>&, const T&);
template<class T> complex<T> pow(const complex<T>&, const complex<T>&);
template<class T> complex<T> pow(const T&, const complex<T>&);

template<class T> complex<T> sin (const complex<T>&);
template<class T> complex<T> sinh (const complex<T>&);
template<class T> complex<T> sqrt (const complex<T>&);
template<class T> complex<T> tan (const complex<T>&);
template<class T> complex<T> tanh (const complex<T>&);

}  // std

*/

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/std/__complex/vector_support.h>
#include <cuda/std/__type_traits/enable_if.h>
#include <cuda/std/__type_traits/is_constructible.h>
#include <cuda/std/__type_traits/is_floating_point.h>
#include <cuda/std/__type_traits/is_integral.h>
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/__type_traits/promote.h>
#include <cuda/std/climits>
#include <cuda/std/cmath>
#include <cuda/std/cstdint>
#include <cuda/std/detail/libcxx/include/__assert> // all public C++ headers provide the assertion handler
#include <cuda/std/type_traits>
#include <cuda/std/version>

// Compatability helpers for thrust to convert between `std::complex` and `cuda::std::complex`
#if !defined(_CCCL_COMPILER_NVRTC)
#  include <complex>
#  include <sstream> // for std::basic_ostringstream

#  define _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__c) reinterpret_cast<const _Up(&)[2]>(__c)[0]
#  define _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__c) reinterpret_cast<const _Up(&)[2]>(__c)[1]
#endif // !_CCCL_COMPILER_NVRTC

_CCCL_PUSH_MACROS

#ifdef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_OPERATIONS
#  ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#    define LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#  endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
#  ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#    define LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#  endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_OPERATIONS

_LIBCUDACXX_BEGIN_NAMESPACE_STD

template <class _Tp>
class _CCCL_TYPE_VISIBILITY_DEFAULT _LIBCUDACXX_COMPLEX_ALIGNAS complex
{
  _Tp __re_;
  _Tp __im_;

  template <class _Up>
  friend class complex;

public:
  using value_type = _Tp;

  _LIBCUDACXX_HIDE_FROM_ABI constexpr complex(const value_type& __re = value_type(),
                                              const value_type& __im = value_type())
      : __re_(__re)
      , __im_(__im)
  {}

  template <class _Up, __enable_if_t<__cccl_internal::__is_non_narrowing_convertible<_Tp, _Up>::value, int> = 0>
  _LIBCUDACXX_HIDE_FROM_ABI constexpr complex(const complex<_Up>& __c)
      : __re_(static_cast<_Tp>(__c.real()))
      , __im_(static_cast<_Tp>(__c.imag()))
  {}

  template <class _Up,
            __enable_if_t<!__cccl_internal::__is_non_narrowing_convertible<_Tp, _Up>::value, int> = 0,
            __enable_if_t<_CCCL_TRAIT(is_constructible, _Tp, _Up), int>                           = 0>
  _LIBCUDACXX_HIDE_FROM_ABI explicit constexpr complex(const complex<_Up>& __c)
      : __re_(static_cast<_Tp>(__c.real()))
      , __im_(static_cast<_Tp>(__c.imag()))
  {}

  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator=(const value_type& __re)
  {
    __re_ = __re;
    __im_ = value_type();
    return *this;
  }

  template <class _Up>
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator=(const complex<_Up>& __c)
  {
    __re_ = __c.real();
    __im_ = __c.imag();
    return *this;
  }

#if !defined(_CCCL_COMPILER_NVRTC)
  template <class _Up>
  _LIBCUDACXX_HIDE_FROM_ABI complex(const ::std::complex<_Up>& __other)
      : __re_(_LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__other))
      , __im_(_LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__other))
  {}

  template <class _Up>
  _LIBCUDACXX_HIDE_FROM_ABI complex& operator=(const ::std::complex<_Up>& __other)
  {
    __re_ = _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__other);
    __im_ = _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__other);
    return *this;
  }

  _CCCL_HOST _CCCL_CONSTEXPR_CXX14 operator ::std::complex<_Tp>() const
  {
    return {__re_, __im_};
  }
#endif // !_CCCL_COMPILER_NVRTC

  _LIBCUDACXX_HIDE_FROM_ABI constexpr value_type real() const
  {
    return __re_;
  }
  _LIBCUDACXX_HIDE_FROM_ABI constexpr value_type imag() const
  {
    return __im_;
  }

  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 void real(value_type __re)
  {
    __re_ = __re;
  }
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 void imag(value_type __im)
  {
    __im_ = __im;
  }

  // Those additional volatile overloads are meant to help with reductions in thrust
  _LIBCUDACXX_HIDE_FROM_ABI value_type real() const volatile
  {
    return __re_;
  }
  _LIBCUDACXX_HIDE_FROM_ABI value_type imag() const volatile
  {
    return __im_;
  }

  _LIBCUDACXX_HIDE_FROM_ABI void real(value_type __re) volatile
  {
    __re_ = __re;
  }
  _LIBCUDACXX_HIDE_FROM_ABI void imag(value_type __im) volatile
  {
    __im_ = __im;
  }

  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator+=(const value_type& __re)
  {
    __re_ += __re;
    return *this;
  }
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator-=(const value_type& __re)
  {
    __re_ -= __re;
    return *this;
  }
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator*=(const value_type& __re)
  {
    __re_ *= __re;
    __im_ *= __re;
    return *this;
  }
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator/=(const value_type& __re)
  {
    __re_ /= __re;
    __im_ /= __re;
    return *this;
  }

  template <class _Up>
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator+=(const complex<_Up>& __c)
  {
    __re_ += __c.real();
    __im_ += __c.imag();
    return *this;
  }
  template <class _Up>
  _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex& operator-=(const complex<_Up>& __c)
  {
    __re_ -= __c.real();
    __im_ -= __c.imag();
    return *this;
  }
};

template <class>
struct __is_complex : false_type
{};

template <class _Tp>
struct __is_complex<complex<_Tp>> : true_type
{};

template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>&
operator*=(complex<_Tp>& __lhs, const complex<_Up>& __rhs)
{
  __lhs = __lhs * complex<_Tp>(__rhs.real(), __rhs.imag());
  return __lhs;
}
template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>&
operator/=(complex<_Tp>& __lhs, const complex<_Up>& __rhs)
{
  __lhs = __lhs / complex<_Tp>(__rhs.real(), __rhs.imag());
  return __lhs;
}

// 26.3.6 operators:
template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator+(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t += __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator+(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t += __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator+(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__y);
  __t += __x;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator-(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t -= __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator-(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t -= __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator-(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(-__y);
  __t += __x;
  return __t;
}
template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>
operator*(const complex<_Tp>& __z, const complex<_Tp>& __w)
{
  _Tp __a = __z.real();
  _Tp __b = __z.imag();
  _Tp __c = __w.real();
  _Tp __d = __w.imag();

#if _CCCL_STD_VER > 2011 && defined(_LIBCUDACXX_IS_CONSTANT_EVALUATED)
  // Avoid floating point operations that are invalid during constant evaluation
  if (__libcpp_is_constant_evaluated())
  {
    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
    bool __z_inf  = _CUDA_VSTD::__constexpr_isinf(__a) || _CUDA_VSTD::__constexpr_isinf(__b);
    bool __w_inf  = _CUDA_VSTD::__constexpr_isinf(__c) || _CUDA_VSTD::__constexpr_isinf(__d);
    bool __z_nan  = !__z_inf
                && ((_CUDA_VSTD::__constexpr_isnan(__a) && _CUDA_VSTD::__constexpr_isnan(__b))
                    || (_CUDA_VSTD::__constexpr_isnan(__a) && __b == _Tp(0))
                    || (__a == _Tp(0) && _CUDA_VSTD::__constexpr_isnan(__b)));
    bool __w_nan = !__w_inf
                && ((_CUDA_VSTD::__constexpr_isnan(__c) && _CUDA_VSTD::__constexpr_isnan(__d))
                    || (_CUDA_VSTD::__constexpr_isnan(__c) && __d == _Tp(0))
                    || (__c == _Tp(0) && _CUDA_VSTD::__constexpr_isnan(__d)));
    if (__z_nan || __w_nan)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    if (__z_inf || __w_inf)
    {
      if (__z_zero || __w_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
    bool __z_nonzero_nan =
      !__z_inf && !__z_nan && (_CUDA_VSTD::__constexpr_isnan(__a) || _CUDA_VSTD::__constexpr_isnan(__b));
    bool __w_nonzero_nan =
      !__w_inf && !__w_nan && (_CUDA_VSTD::__constexpr_isnan(__c) || _CUDA_VSTD::__constexpr_isnan(__d));
    if (__z_nonzero_nan || __w_nonzero_nan)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
  }
#endif // _CCCL_STD_VER > 2011 && defined(_LIBCUDACXX_IS_CONSTANT_EVALUATED)

  __abcd_results<_Tp> __partials = __complex_calculate_partials(__a, __b, __c, __d);

  _Tp __x = __partials.__ac - __partials.__bd;
  _Tp __y = __partials.__ad + __partials.__bc;
#ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
  if (_CUDA_VSTD::__constexpr_isnan(__x) && _CUDA_VSTD::__constexpr_isnan(__y))
  {
    bool __recalc = false;
    if (_CUDA_VSTD::__constexpr_isinf(__a) || _CUDA_VSTD::__constexpr_isinf(__b))
    {
      __a = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
      __b = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
      if (_CUDA_VSTD::__constexpr_isnan(__c))
      {
        __c = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __c);
      }
      if (_CUDA_VSTD::__constexpr_isnan(__d))
      {
        __d = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __d);
      }
      __recalc = true;
    }
    if (_CUDA_VSTD::__constexpr_isinf(__c) || _CUDA_VSTD::__constexpr_isinf(__d))
    {
      __c = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
      __d = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
      if (_CUDA_VSTD::__constexpr_isnan(__a))
      {
        __a = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __a);
      }
      if (_CUDA_VSTD::__constexpr_isnan(__b))
      {
        __b = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __b);
      }
      __recalc = true;
    }
    if (!__recalc
        && (_CUDA_VSTD::__constexpr_isinf(__partials.__ac) || _CUDA_VSTD::__constexpr_isinf(__partials.__bd)
            || _CUDA_VSTD::__constexpr_isinf(__partials.__ad) || _CUDA_VSTD::__constexpr_isinf(__partials.__bc)))
    {
      if (_CUDA_VSTD::__constexpr_isnan(__a))
      {
        __a = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __a);
      }
      if (_CUDA_VSTD::__constexpr_isnan(__b))
      {
        __b = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __b);
      }
      if (_CUDA_VSTD::__constexpr_isnan(__c))
      {
        __c = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __c);
      }
      if (_CUDA_VSTD::__constexpr_isnan(__d))
      {
        __d = _CUDA_VSTD::__constexpr_copysign(_Tp(0), __d);
      }
      __recalc = true;
    }
    if (__recalc)
    {
      __partials = __complex_calculate_partials(__a, __b, __c, __d);

      __x = _Tp(INFINITY) * (__partials.__ac - __partials.__bd);
      __y = _Tp(INFINITY) * (__partials.__ad + __partials.__bc);
    }
  }
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_MULTIPLICATION
  return complex<_Tp>(__x, __y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator*(const complex<_Tp>& __x, const _Tp& __y)
{
  complex<_Tp> __t(__x);
  __t *= __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator*(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__y);
  __t *= __x;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14_COMPLEX complex<_Tp>
operator/(const complex<_Tp>& __z, const complex<_Tp>& __w)
{
  int __ilogbw = 0;
  _Tp __a      = __z.real();
  _Tp __b      = __z.imag();
  _Tp __c      = __w.real();
  _Tp __d      = __w.imag();
  _Tp __logbw  = _CUDA_VSTD::__constexpr_logb(
    _CUDA_VSTD::__constexpr_fmax(_CUDA_VSTD::__constexpr_fabs(__c), _CUDA_VSTD::__constexpr_fabs(__d)));
  if (_CUDA_VSTD::__constexpr_isfinite(__logbw))
  {
    __ilogbw = static_cast<int>(__logbw);
    __c      = _CUDA_VSTD::__constexpr_scalbn(__c, -__ilogbw);
    __d      = _CUDA_VSTD::__constexpr_scalbn(__d, -__ilogbw);
  }

#if _CCCL_STD_VER > 2011 && defined(_LIBCUDACXX_IS_CONSTANT_EVALUATED)
  // Avoid floating point operations that are invalid during constant evaluation
  if (__libcpp_is_constant_evaluated())
  {
    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
    bool __z_inf  = _CUDA_VSTD::__constexpr_isinf(__a) || _CUDA_VSTD::__constexpr_isinf(__b);
    bool __w_inf  = _CUDA_VSTD::__constexpr_isinf(__c) || _CUDA_VSTD::__constexpr_isinf(__d);
    bool __z_nan  = !__z_inf
                && ((_CUDA_VSTD::__constexpr_isnan(__a) && _CUDA_VSTD::__constexpr_isnan(__b))
                    || (_CUDA_VSTD::__constexpr_isnan(__a) && __b == _Tp(0))
                    || (__a == _Tp(0) && _CUDA_VSTD::__constexpr_isnan(__b)));
    bool __w_nan = !__w_inf
                && ((_CUDA_VSTD::__constexpr_isnan(__c) && _CUDA_VSTD::__constexpr_isnan(__d))
                    || (_CUDA_VSTD::__constexpr_isnan(__c) && __d == _Tp(0))
                    || (__c == _Tp(0) && _CUDA_VSTD::__constexpr_isnan(__d)));
    if ((__z_nan || __w_nan) || (__z_inf && __w_inf))
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    bool __z_nonzero_nan =
      !__z_inf && !__z_nan && (_CUDA_VSTD::__constexpr_isnan(__a) || _CUDA_VSTD::__constexpr_isnan(__b));
    bool __w_nonzero_nan =
      !__w_inf && !__w_nan && (_CUDA_VSTD::__constexpr_isnan(__c) || _CUDA_VSTD::__constexpr_isnan(__d));
    if (__z_nonzero_nan || __w_nonzero_nan)
    {
      if (__w_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
    }
    if (__w_inf)
    {
      return complex<_Tp>(_Tp(0), _Tp(0));
    }
    if (__z_inf)
    {
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
    if (__w_zero)
    {
      if (__z_zero)
      {
        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
      }
      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
    }
  }
#endif // _CCCL_STD_VER > 2011 && defined(_LIBCUDACXX_IS_CONSTANT_EVALUATED

  __abcd_results<_Tp> __partials = __complex_calculate_partials(__a, __b, __c, __d);
  __ab_results<_Tp> __denom_vec  = __complex_piecewise_mul(__c, __d, __c, __d);

  _Tp __denom = __denom_vec.__a + __denom_vec.__b;
  _Tp __x     = _CUDA_VSTD::__constexpr_scalbn((__partials.__ac + __partials.__bd) / __denom, -__ilogbw);
  _Tp __y     = _CUDA_VSTD::__constexpr_scalbn((__partials.__bc - __partials.__ad) / __denom, -__ilogbw);
#ifndef LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
  if (_CUDA_VSTD::__constexpr_isnan(__x) && _CUDA_VSTD::__constexpr_isnan(__y))
  {
    if ((__denom == _Tp(0)) && (!_CUDA_VSTD::__constexpr_isnan(__a) || !_CUDA_VSTD::__constexpr_isnan(__b)))
    {
      __x = _CUDA_VSTD::__constexpr_copysign(_Tp(INFINITY), __c) * __a;
      __y = _CUDA_VSTD::__constexpr_copysign(_Tp(INFINITY), __c) * __b;
    }
    else if ((_CUDA_VSTD::__constexpr_isinf(__a) || _CUDA_VSTD::__constexpr_isinf(__b))
             && _CUDA_VSTD::__constexpr_isfinite(__c) && _CUDA_VSTD::__constexpr_isfinite(__d))
    {
      __a = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
      __b = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
      __x = _Tp(INFINITY) * (__a * __c + __b * __d);
      __y = _Tp(INFINITY) * (__b * __c - __a * __d);
    }
    else if (_CUDA_VSTD::__constexpr_isinf(__logbw) && __logbw > _Tp(0) && _CUDA_VSTD::__constexpr_isfinite(__a)
             && _CUDA_VSTD::__constexpr_isfinite(__b))
    {
      __c = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
      __d = _CUDA_VSTD::__constexpr_copysign(_CUDA_VSTD::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
      __x = _Tp(0) * (__a * __c + __b * __d);
      __y = _Tp(0) * (__b * __c - __a * __d);
    }
  }
#endif // LIBCUDACXX_ENABLE_SIMPLIFIED_COMPLEX_DIVISION
  return complex<_Tp>(__x, __y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator/(const complex<_Tp>& __x, const _Tp& __y)
{
  return complex<_Tp>(__x.real() / __y, __x.imag() / __y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator/(const _Tp& __x, const complex<_Tp>& __y)
{
  complex<_Tp> __t(__x);
  __t /= __y;
  return __t;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator+(const complex<_Tp>& __x)
{
  return __x;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> operator-(const complex<_Tp>& __x)
{
  return complex<_Tp>(-__x.real(), -__x.imag());
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator==(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return __x.real() == __y.real() && __x.imag() == __y.imag();
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator==(const complex<_Tp>& __x, const _Tp& __y)
{
  return __x.real() == __y && __x.imag() == _Tp(0);
}

#if _CCCL_STD_VER <= 2017
template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator==(const _Tp& __x, const complex<_Tp>& __y)
{
  return __x == __y.real() && _Tp(0) == __y.imag();
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator!=(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator!=(const complex<_Tp>& __x, const _Tp& __y)
{
  return !(__x == __y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator!=(const _Tp& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}
#endif // _CCCL_STD_VER <= 2017

#if !defined(_CCCL_COMPILER_NVRTC)
template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator==(const complex<_Tp>& __x, const ::std::complex<_Up>& __y)
{
  return __x.real() == _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__y)
      && __x.imag() == _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__y);
}

#  if _CCCL_STD_VER <= 2017
template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator==(const ::std::complex<_Up>& __x, const complex<_Tp>& __y)
{
  return __y.real() == _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL(__x)
      && __y.imag() == _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG(__x);
}

template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator!=(const complex<_Tp>& __x, const ::std::complex<_Up>& __y)
{
  return !(__x == __y);
}

template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 bool operator!=(const ::std::complex<_Up>& __x, const complex<_Tp>& __y)
{
  return !(__x == __y);
}
#  endif // _CCCL_STD_VER <= 2017
#endif // !_CCCL_COMPILER_NVRTC

// real

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 _Tp real(const complex<_Tp>& __c)
{
  return __c.real();
}

// 26.3.7 values:

template <class _Tp, bool = _CCCL_TRAIT(is_integral, _Tp), bool = _CCCL_TRAIT(is_floating_point, _Tp)>
struct __libcpp_complex_overload_traits
{};

// Integral Types
template <class _Tp>
struct __libcpp_complex_overload_traits<_Tp, true, false>
{
  using _ValueType   = double;
  using _ComplexType = complex<double>;
};

// Floating point types
template <class _Tp>
struct __libcpp_complex_overload_traits<_Tp, false, true>
{
  using _ValueType   = _Tp;
  using _ComplexType = complex<_Tp>;
};

template <class _Tp>
using __libcpp_complex_value_type = typename __libcpp_complex_overload_traits<_Tp>::_ValueType;

template <class _Tp>
using __libcpp_complex_complex_type = typename __libcpp_complex_overload_traits<_Tp>::_ComplexType;

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 __libcpp_complex_value_type<_Tp> real(_Tp __re)
{
  return __re;
}

// imag

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 _Tp imag(const complex<_Tp>& __c)
{
  return __c.imag();
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 __libcpp_complex_value_type<_Tp> imag(_Tp)
{
  return 0;
}

// abs

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _Tp abs(const complex<_Tp>& __c)
{
  return _CUDA_VSTD::hypot(__c.real(), __c.imag());
}

// arg

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _Tp arg(const complex<_Tp>& __c)
{
  return _CUDA_VSTD::atan2(__c.imag(), __c.real());
}

#ifdef _LIBCUDACXX_HAS_COMPLEX_LONG_DOUBLE
template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI __enable_if_t<is_same<_Tp, long double>::value, long double> arg(_Tp __re)
{
  return _CUDA_VSTD::atan2l(0.L, __re);
}
#endif // _LIBCUDACXX_HAS_COMPLEX_LONG_DOUBLE

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI __enable_if_t<is_integral<_Tp>::value || is_same<_Tp, double>::value, double> arg(_Tp __re)
{
  // integrals need to be promoted to double
  return _CUDA_VSTD::atan2(0., static_cast<double>(__re));
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI __enable_if_t<is_same<_Tp, float>::value, float> arg(_Tp __re)
{
  return _CUDA_VSTD::atan2f(0.F, __re);
}

// norm

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 _Tp norm(const complex<_Tp>& __c)
{
  if (_CUDA_VSTD::__constexpr_isinf(__c.real()))
  {
    return _CUDA_VSTD::abs(__c.real());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__c.imag()))
  {
    return _CUDA_VSTD::abs(__c.imag());
  }
  return __c.real() * __c.real() + __c.imag() * __c.imag();
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 __libcpp_complex_value_type<_Tp> norm(_Tp __re)
{
  return static_cast<__libcpp_complex_value_type<_Tp>>(__re) * __re;
}

// conj

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 complex<_Tp> conj(const complex<_Tp>& __c)
{
  return complex<_Tp>(__c.real(), -__c.imag());
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX14 __libcpp_complex_complex_type<_Tp> conj(_Tp __re)
{
  return __libcpp_complex_complex_type<_Tp>(__re);
}

// proj

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> proj(const complex<_Tp>& __c)
{
  complex<_Tp> __r = __c;
  if (_CUDA_VSTD::__constexpr_isinf(__c.real()) || _CUDA_VSTD::__constexpr_isinf(__c.imag()))
  {
    __r = complex<_Tp>(INFINITY, __constexpr_copysign(_Tp(0), __c.imag()));
  }
  return __r;
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI __enable_if_t<__is_complex_float<_Tp>::value, __libcpp_complex_complex_type<_Tp>>
proj(_Tp __re)
{
  if (_CUDA_VSTD::__constexpr_isinf(__re))
  {
    __re = _CUDA_VSTD::abs(__re);
  }
  return complex<_Tp>(__re);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI __enable_if_t<is_integral<_Tp>::value, __libcpp_complex_complex_type<_Tp>> proj(_Tp __re)
{
  return __libcpp_complex_complex_type<_Tp>(__re);
}

// polar

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> polar(const _Tp& __rho, const _Tp& __theta = _Tp())
{
  if (_CUDA_VSTD::__constexpr_isnan(__rho) || _CUDA_VSTD::signbit(__rho))
  {
    return complex<_Tp>(_Tp(NAN), _Tp(NAN));
  }
  if (_CUDA_VSTD::__constexpr_isnan(__theta))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__rho))
    {
      return complex<_Tp>(__rho, __theta);
    }
    return complex<_Tp>(__theta, __theta);
  }
  if (_CUDA_VSTD::__constexpr_isinf(__theta))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__rho))
    {
      return complex<_Tp>(__rho, _Tp(NAN));
    }
    return complex<_Tp>(_Tp(NAN), _Tp(NAN));
  }
  _Tp __x = __rho * _CUDA_VSTD::cos(__theta);
  if (_CUDA_VSTD::__constexpr_isnan(__x))
  {
    __x = 0;
  }
  _Tp __y = __rho * _CUDA_VSTD::sin(__theta);
  if (_CUDA_VSTD::__constexpr_isnan(__y))
  {
    __y = 0;
  }
  return complex<_Tp>(__x, __y);
}

// log

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> log(const complex<_Tp>& __x)
{
  return complex<_Tp>(_CUDA_VSTD::log(_CUDA_VSTD::abs(__x)), _CUDA_VSTD::arg(__x));
}

// log10

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> log10(const complex<_Tp>& __x)
{
  return _CUDA_VSTD::log(__x) / _CUDA_VSTD::log(_Tp(10));
}

// sqrt

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> sqrt(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
  {
    return complex<_Tp>(_Tp(INFINITY), __x.imag());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (__x.real() > _Tp(0))
    {
      return complex<_Tp>(
        __x.real(), _CUDA_VSTD::__constexpr_isnan(__x.imag()) ? __x.imag() : __constexpr_copysign(_Tp(0), __x.imag()));
    }
    return complex<_Tp>(_CUDA_VSTD::__constexpr_isnan(__x.imag()) ? __x.imag() : _Tp(0),
                        __constexpr_copysign(__x.real(), __x.imag()));
  }
  return _CUDA_VSTD::polar(_CUDA_VSTD::sqrt(_CUDA_VSTD::abs(__x)), _CUDA_VSTD::arg(__x) / _Tp(2));
}

// exp

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x)
{
  _Tp __i = __x.imag();
  if (__i == _Tp(0))
  {
    return complex<_Tp>(_CUDA_VSTD::exp(__x.real()), __constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (__x.real() < _Tp(0))
    {
      if (!_CUDA_VSTD::__constexpr_isfinite(__i))
      {
        __i = _Tp(1);
      }
    }
    else if (__i == _Tp(0) || !_CUDA_VSTD::__constexpr_isfinite(__i))
    {
      if (_CUDA_VSTD::__constexpr_isinf(__i))
      {
        __i = _Tp(NAN);
      }
      return complex<_Tp>(__x.real(), __i);
    }
  }
  _Tp __e = _CUDA_VSTD::exp(__x.real());
  return complex<_Tp>(__e * _CUDA_VSTD::cos(__i), __e * _CUDA_VSTD::sin(__i));
}

// pow

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> pow(const complex<_Tp>& __x, const complex<_Tp>& __y)
{
  return _CUDA_VSTD::exp(__y * _CUDA_VSTD::log(__x));
}

_CCCL_DIAG_PUSH
_CCCL_DIAG_SUPPRESS_MSVC(4244)

template <class _Tp, class _Up>
_LIBCUDACXX_HIDE_FROM_ABI complex<__common_type_t<_Tp, _Up>> pow(const complex<_Tp>& __x, const complex<_Up>& __y)
{
  using __result_type = complex<__common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x), __result_type(__y));
}

template <class _Tp, class _Up, __enable_if_t<!__is_complex<_Up>::value, int> = 0>
_LIBCUDACXX_HIDE_FROM_ABI complex<__common_type_t<_Tp, _Up>> pow(const complex<_Tp>& __x, const _Up& __y)
{
  using __result_type = complex<__common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x), __result_type(__y));
}

template <class _Tp, class _Up, __enable_if_t<!__is_complex<_Tp>::value, int> = 0>
_LIBCUDACXX_HIDE_FROM_ABI complex<__common_type_t<_Tp, _Up>> pow(const _Tp& __x, const complex<_Up>& __y)
{
  using __result_type = complex<__common_type_t<_Tp, _Up>>;
  return _CUDA_VSTD::pow(__result_type(__x, 0), __result_type(__y));
}

_CCCL_DIAG_POP

// __sqr, computes pow(x, 2)

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> __sqr(const complex<_Tp>& __x)
{
  return complex<_Tp>((__x.real() - __x.imag()) * (__x.real() + __x.imag()), _Tp(2) * __x.real() * __x.imag());
}

// asinh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> asinh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isnan(__x.imag()))
    {
      return __x;
    }
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      return complex<_Tp>(__x.real(), __constexpr_copysign(__pi * _Tp(0.25), __x.imag()));
    }
    return complex<_Tp>(__x.real(), __constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      return complex<_Tp>(__x.imag(), __x.real());
    }
    if (__x.imag() == _Tp(0))
    {
      return __x;
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
  {
    return complex<_Tp>(__constexpr_copysign(__x.imag(), __x.real()), __constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) + _Tp(1)));
  return complex<_Tp>(__constexpr_copysign(__z.real(), __x.real()), __constexpr_copysign(__z.imag(), __x.imag()));
}

// acosh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> acosh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isnan(__x.imag()))
    {
      return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), __x.imag());
    }
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      if (__x.real() > _Tp(0))
      {
        return complex<_Tp>(__x.real(), __constexpr_copysign(__pi * _Tp(0.25), __x.imag()));
      }
      else
      {
        return complex<_Tp>(-__x.real(), __constexpr_copysign(__pi * _Tp(0.75), __x.imag()));
      }
    }
    if (__x.real() < _Tp(0))
    {
      return complex<_Tp>(-__x.real(), __constexpr_copysign(__pi, __x.imag()));
    }
    return complex<_Tp>(__x.real(), __constexpr_copysign(_Tp(0), __x.imag()));
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      return complex<_Tp>(_CUDA_VSTD::abs(__x.imag()), __x.real());
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.imag()), __constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) - _Tp(1)));
  return complex<_Tp>(__constexpr_copysign(__z.real(), _Tp(0)), __constexpr_copysign(__z.imag(), __x.imag()));
}

// atanh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> atanh(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
  {
    return complex<_Tp>(__constexpr_copysign(_Tp(0), __x.real()), __constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.imag()))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__x.real()) || __x.real() == _Tp(0))
    {
      return complex<_Tp>(__constexpr_copysign(_Tp(0), __x.real()), __x.imag());
    }
    return complex<_Tp>(__x.imag(), __x.imag());
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.real()))
  {
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    return complex<_Tp>(__constexpr_copysign(_Tp(0), __x.real()), __constexpr_copysign(__pi / _Tp(2), __x.imag()));
  }
  if (_CUDA_VSTD::abs(__x.real()) == _Tp(1) && __x.imag() == _Tp(0))
  {
    return complex<_Tp>(__constexpr_copysign(_Tp(INFINITY), __x.real()), __constexpr_copysign(_Tp(0), __x.imag()));
  }
  complex<_Tp> __z = _CUDA_VSTD::log((_Tp(1) + __x) / (_Tp(1) - __x)) / _Tp(2);
  return complex<_Tp>(__constexpr_copysign(__z.real(), __x.real()), __constexpr_copysign(__z.imag(), __x.imag()));
}

// sinh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> sinh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()) && !_CUDA_VSTD::__constexpr_isfinite(__x.imag()))
  {
    return complex<_Tp>(__x.real(), _Tp(NAN));
  }
  if (__x.real() == _Tp(0) && !_CUDA_VSTD::__constexpr_isfinite(__x.imag()))
  {
    return complex<_Tp>(__x.real(), _Tp(NAN));
  }
  if (__x.imag() == _Tp(0) && !_CUDA_VSTD::__constexpr_isfinite(__x.real()))
  {
    return __x;
  }
  return complex<_Tp>(_CUDA_VSTD::sinh(__x.real()) * _CUDA_VSTD::cos(__x.imag()),
                      _CUDA_VSTD::cosh(__x.real()) * _CUDA_VSTD::sin(__x.imag()));
}

// cosh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> cosh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()) && !_CUDA_VSTD::__constexpr_isfinite(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), _Tp(NAN));
  }
  if (__x.real() == _Tp(0) && !_CUDA_VSTD::__constexpr_isfinite(__x.imag()))
  {
    return complex<_Tp>(_Tp(NAN), __x.real());
  }
  if (__x.real() == _Tp(0) && __x.imag() == _Tp(0))
  {
    return complex<_Tp>(_Tp(1), __x.imag());
  }
  if (__x.imag() == _Tp(0) && !_CUDA_VSTD::__constexpr_isfinite(__x.real()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__x.real()), __x.imag());
  }
  return complex<_Tp>(_CUDA_VSTD::cosh(__x.real()) * _CUDA_VSTD::cos(__x.imag()),
                      _CUDA_VSTD::sinh(__x.real()) * _CUDA_VSTD::sin(__x.imag()));
}

// tanh

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> tanh(const complex<_Tp>& __x)
{
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (!_CUDA_VSTD::__constexpr_isfinite(__x.imag()))
    {
      return complex<_Tp>(__constexpr_copysign(_Tp(1), __x.real()), _Tp(0));
    }
    return complex<_Tp>(__constexpr_copysign(_Tp(1), __x.real()),
                        __constexpr_copysign(_Tp(0), _CUDA_VSTD::sin(_Tp(2) * __x.imag())));
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.real()) && __x.imag() == _Tp(0))
  {
    return __x;
  }
  _Tp __2r(_Tp(2) * __x.real());
  _Tp __2i(_Tp(2) * __x.imag());
  _Tp __d(_CUDA_VSTD::cosh(__2r) + _CUDA_VSTD::cos(__2i));
  _Tp __2rsh(_CUDA_VSTD::sinh(__2r));
  if (_CUDA_VSTD::__constexpr_isinf(__2rsh) && _CUDA_VSTD::__constexpr_isinf(__d))
  {
    return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __2i > _Tp(0) ? _Tp(0) : _Tp(-0.));
  }
  return complex<_Tp>(__2rsh / __d, _CUDA_VSTD::sin(__2i) / __d);
}

// asin

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> asin(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::asinh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// acos

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> acos(const complex<_Tp>& __x)
{
  const _Tp __pi(static_cast<_Tp>(atan2(+0., -0.)));
  if (_CUDA_VSTD::__constexpr_isinf(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isnan(__x.imag()))
    {
      return complex<_Tp>(__x.imag(), __x.real());
    }
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      if (__x.real() < _Tp(0))
      {
        return complex<_Tp>(_Tp(0.75) * __pi, -__x.imag());
      }
      return complex<_Tp>(_Tp(0.25) * __pi, -__x.imag());
    }
    if (__x.real() < _Tp(0))
    {
      return complex<_Tp>(__pi, _CUDA_VSTD::signbit(__x.imag()) ? -__x.real() : __x.real());
    }
    return complex<_Tp>(_Tp(0), _CUDA_VSTD::signbit(__x.imag()) ? __x.real() : -__x.real());
  }
  if (_CUDA_VSTD::__constexpr_isnan(__x.real()))
  {
    if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
    {
      return complex<_Tp>(__x.real(), -__x.imag());
    }
    return complex<_Tp>(__x.real(), __x.real());
  }
  if (_CUDA_VSTD::__constexpr_isinf(__x.imag()))
  {
    return complex<_Tp>(__pi / _Tp(2), -__x.imag());
  }
  if (__x.real() == _Tp(0) && (__x.imag() == _Tp(0) || _CUDA_VSTD::isnan(__x.imag())))
  {
    return complex<_Tp>(__pi / _Tp(2), -__x.imag());
  }
  complex<_Tp> __z = _CUDA_VSTD::log(__x + _CUDA_VSTD::sqrt(_CUDA_VSTD::__sqr(__x) - _Tp(1)));
  if (_CUDA_VSTD::signbit(__x.imag()))
  {
    return complex<_Tp>(_CUDA_VSTD::abs(__z.imag()), _CUDA_VSTD::abs(__z.real()));
  }
  return complex<_Tp>(_CUDA_VSTD::abs(__z.imag()), -_CUDA_VSTD::abs(__z.real()));
}

// atan

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> atan(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::atanh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// sin

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> sin(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::sinh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

// cos

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> cos(const complex<_Tp>& __x)
{
  return _CUDA_VSTD::cosh(complex<_Tp>(-__x.imag(), __x.real()));
}

// tan

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI complex<_Tp> tan(const complex<_Tp>& __x)
{
  complex<_Tp> __z = _CUDA_VSTD::tanh(complex<_Tp>(-__x.imag(), __x.real()));
  return complex<_Tp>(__z.imag(), -__z.real());
}

#if !defined(_CCCL_COMPILER_NVRTC)
template <class _Tp, class _CharT, class _Traits>
::std::basic_istream<_CharT, _Traits>& operator>>(::std::basic_istream<_CharT, _Traits>& __is, complex<_Tp>& __x)
{
  ::std::complex<_Tp> __temp;
  __is >> __temp;
  __x = __temp;
  return __is;
}

template <class _Tp, class _CharT, class _Traits>
::std::basic_ostream<_CharT, _Traits>& operator<<(::std::basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x)
{
  return __os << static_cast<::std::complex<_Tp>>(__x);
}
#endif // !_CCCL_COMPILER_NVRTC

#if _CCCL_STD_VER > 2011 && defined(_LIBCUDACXX_HAS_STL_LITERALS)
// Literal suffix for complex number literals [complex.literals]

_CCCL_DIAG_PUSH
_CCCL_DIAG_SUPPRESS_GCC("-Wliteral-suffix")
_CCCL_DIAG_SUPPRESS_CLANG("-Wuser-defined-literals")
_CCCL_DIAG_SUPPRESS_MSVC(4455)

inline namespace literals
{
inline namespace complex_literals
{
#  ifdef _LIBCUDACXX_HAS_COMPLEX_LONG_DOUBLE
// NOTE: if you get a warning from GCC <7 here that "literal operator suffixes not preceded by ‘_’ are reserved for
// future standardization" then we are sorry. The warning was implemented before GCC 7, but can only be disabled since
// GCC 7. See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69523
_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<long double> operator""il(long double __im)
{
  return {0.0l, __im};
}
_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<long double> operator""il(unsigned long long __im)
{
  return {0.0l, static_cast<long double>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<double> operator""i(long double __im)
{
  return {0.0, static_cast<double>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<double> operator""i(unsigned long long __im)
{
  return {0.0, static_cast<double>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<float> operator""if(long double __im)
{
  return {0.0f, static_cast<float>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<float> operator""if(unsigned long long __im)
{
  return {0.0f, static_cast<float>(__im)};
}
#  else
_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<double> operator""i(double __im)
{
  return {0.0, static_cast<double>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<double> operator""i(unsigned long long __im)
{
  return {0.0, static_cast<double>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<float> operator""if(double __im)
{
  return {0.0f, static_cast<float>(__im)};
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr complex<float> operator""if(unsigned long long __im)
{
  return {0.0f, static_cast<float>(__im)};
}
#  endif
} // namespace complex_literals
} // namespace literals

_CCCL_DIAG_POP

#endif

_LIBCUDACXX_END_NAMESPACE_STD

#ifdef _LIBCUDACXX_HAS_NVFP16
#  include <cuda/std/__complex/nvfp16.h>
#endif // _LIBCUDACXX_HAS_NVFP16

#ifdef _LIBCUDACXX_HAS_NVBF16
#  include <cuda/std/__complex/nvbf16.h>
#endif // _LIBCUDACXX_HAS_NVBF16

#undef _LIBCUDACXX_ACCESS_STD_COMPLEX_REAL
#undef _LIBCUDACXX_ACCESS_STD_COMPLEX_IMAG

_CCCL_POP_MACROS

#endif // _LIBCUDACXX_COMPLEX
