//------------------------------------------------------------------------------
// GB_complex.h: definitions for complex types
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2023, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

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

// These macros allow GraphBLAS to be compiled with a C++ compiler. See:
// https://www.drdobbs.com/complex-arithmetic-in-the-intersection-o/184401628#

#ifndef GB_COMPLEX_H
#define GB_COMPLEX_H

//------------------------------------------------------------------------------
// complex constructors
//------------------------------------------------------------------------------

#if GB_HAS_CMPLX_MACROS

    //--------------------------------------------------------------------------
    // typical case
    //--------------------------------------------------------------------------

    // The GxB_CMPLX* macros defined in GraphBLAS.h do no flops so they are
    // safe to use if the inputs are Inf or NaN.  The CUDA kernels use these
    // methods.

    #define GJ_CMPLX32(xreal,ximag) GxB_CMPLXF (xreal, ximag)
    #define GJ_CMPLX64(xreal,ximag) GxB_CMPLX  (xreal, ximag)

    #define GB_CMPLX32(xreal,ximag) GxB_CMPLXF (xreal, ximag)
    #define GB_CMPLX64(xreal,ximag) GxB_CMPLX  (xreal, ximag)

#else

    //--------------------------------------------------------------------------
    // Mac only, or other compilers that do not #define CMPLX and CMPLXF
    //--------------------------------------------------------------------------

    // gcc on the Mac does not define the CMPLX and CMPLXF macros.  The macros
    // defined in GraphBLAS.h do arithmetic, so they are not safe with Inf or
    // NaN.

    //--------------------------------------------------------------------------
    // GJ_* macros and methods for kernels (JIT or Factory)
    //--------------------------------------------------------------------------

    // These methods are 'static inline' because they are meant to be used
    // directly inside JIT, or Factory Kernels.

    #define GJ_CMPLX32(xreal,ximag) GJ_complexf (xreal, ximag)
    #define GJ_CMPLX64(xreal,ximag) GJ_complex  (xreal, ximag)

    static inline GxB_FC32_t GJ_complexf (float xreal, float ximag)
    {
        float z [2] ;
        z [0] = xreal ;
        z [1] = ximag ;
        return (* ((GxB_FC32_t *) z)) ;
    }

    static inline GxB_FC64_t GJ_complex (double xreal, double ximag)
    {
        double z [2] ;
        z [0] = xreal ;
        z [1] = ximag ;
        return (* ((GxB_FC64_t *) z)) ;
    }

    //--------------------------------------------------------------------------
    // GB_* macros and methods for the generic case
    //--------------------------------------------------------------------------

    // These methods are not 'static inline' because they are used inside other
    // 'inline' methods.

    #define GB_CMPLX32(xreal,ximag) GB_complexf (xreal, ximag)
    #define GB_CMPLX64(xreal,ximag) GB_complex  (xreal, ximag)

    inline GxB_FC32_t GB_complexf (float xreal, float ximag)
    {
        float z [2] ;
        z [0] = xreal ;
        z [1] = ximag ;
        return (* ((GxB_FC32_t *) z)) ;
    }

    inline GxB_FC64_t GB_complex (double xreal, double ximag)
    {
        double z [2] ;
        z [0] = xreal ;
        z [1] = ximag ;
        return (* ((GxB_FC64_t *) z)) ;
    }

#endif

//------------------------------------------------------------------------------
// macros for complex built-in functions
//------------------------------------------------------------------------------

#if defined ( __cplusplus ) || defined ( __NVCC__ )

    //--------------------------------------------------------------------------
    // ANSI C++ or NVCC
    //--------------------------------------------------------------------------

    #define GB_crealf(x)   std::real(x)
    #define GB_creal(x)    std::real(x)
    #define GB_cimagf(x)   std::imag(x)
    #define GB_cimag(x)    std::imag(x)
    #define GB_cpowf(x,y)  std::pow(x,y)
    #define GB_cpow(x,y)   std::pow(x,y)
    #define GB_cexpf(x)    std::exp(x)
    #define GB_cexp(x)     std::exp(x)
    #define GB_clogf(x)    std::log(x)
    #define GB_clog(x)     std::log(x)
    #define GB_cabsf(x)    std::abs(x)
    #define GB_cabs(x)     std::abs(x)
    #define GB_csqrtf(x)   std::sqrt(x)
    #define GB_csqrt(x)    std::sqrt(x)
    #define GB_conjf(x)    std::conj(x)
    #define GB_conj(x)     std::conj(x)
    #define GB_cargf(x)    std::arg(x)
    #define GB_carg(x)     std::arg(x)
    #define GB_csinf(x)    std::sin(x)
    #define GB_csin(x)     std::sin(x)
    #define GB_ccosf(x)    std::cos(x)
    #define GB_ccos(x)     std::cos(x)
    #define GB_ctanf(x)    std::tan(x)
    #define GB_ctan(x)     std::tan(x)
    #define GB_casinf(x)   std::asin(x)
    #define GB_casin(x)    std::asin(x)
    #define GB_cacosf(x)   std::acos(x)
    #define GB_cacos(x)    std::acos(x)
    #define GB_catanf(x)   std::atan(x)
    #define GB_catan(x)    std::atan(x)
    #define GB_csinhf(x)   std::sinh(x)
    #define GB_csinh(x)    std::sinh(x)
    #define GB_ccoshf(x)   std::cosh(x)
    #define GB_ccosh(x)    std::cosh(x)
    #define GB_ctanhf(x)   std::tanh(x)
    #define GB_ctanh(x)    std::tanh(x)
    #define GB_casinhf(x)  std::asinh(x)
    #define GB_casinh(x)   std::asinh(x)
    #define GB_cacoshf(x)  std::acosh(x)
    #define GB_cacosh(x)   std::acosh(x)
    #define GB_catanhf(x)  std::atanh(x)
    #define GB_catanh(x)   std::atanh(x)

#else

    //--------------------------------------------------------------------------
    // ANSI C11
    //--------------------------------------------------------------------------

    #define GB_crealf(x)   crealf(x)
    #define GB_creal(x)    creal(x)
    #define GB_cimagf(x)   cimagf(x)
    #define GB_cimag(x)    cimag(x)
    #define GB_cpowf(x,y)  cpowf(x,y)
    #define GB_cpow(x,y)   cpow(x,y)
    #define GB_cexpf(x)    cexpf(x)
    #define GB_cexp(x)     cexp(x)
    #define GB_clogf(x)    clogf(x)
    #define GB_clog(x)     clog(x)
    #define GB_cabsf(x)    cabsf(x)
    #define GB_cabs(x)     cabs(x)
    #define GB_csqrtf(x)   csqrtf(x)
    #define GB_csqrt(x)    csqrt(x)
    #define GB_conjf(x)    conjf(x)
    #define GB_conj(x)     conj(x)
    #define GB_cargf(x)    cargf(x)
    #define GB_carg(x)     carg(x)
    #define GB_csinf(x)    csinf(x)
    #define GB_csin(x)     csin(x)
    #define GB_ccosf(x)    ccosf(x)
    #define GB_ccos(x)     ccos(x)
    #define GB_ctanf(x)    ctanf(x)
    #define GB_ctan(x)     ctan(x)
    #define GB_casinf(x)   casinf(x)
    #define GB_casin(x)    casin(x)
    #define GB_cacosf(x)   cacosf(x)
    #define GB_cacos(x)    cacos(x)
    #define GB_catanf(x)   catanf(x)
    #define GB_catan(x)    catan(x)
    #define GB_csinhf(x)   csinhf(x)
    #define GB_csinh(x)    csinh(x)
    #define GB_ccoshf(x)   ccoshf(x)
    #define GB_ccosh(x)    ccosh(x)
    #define GB_ctanhf(x)   ctanhf(x)
    #define GB_ctanh(x)    ctanh(x)
    #define GB_casinhf(x)  casinhf(x)
    #define GB_casinh(x)   casinh(x)
    #define GB_cacoshf(x)  cacoshf(x)
    #define GB_cacosh(x)   cacosh(x)
    #define GB_catanhf(x)  catanhf(x)
    #define GB_catanh(x)   catanh(x)

#endif

//------------------------------------------------------------------------------
// macros for basic complex operations: mult, add, minus, ainv
//------------------------------------------------------------------------------

#if GB_COMPILER_MSC

    //--------------------------------------------------------------------------
    // Microsoft Visual Studio compiler with its own complex type
    //--------------------------------------------------------------------------

    // complex-complex multiply: z = x*y where both x and y are complex
    #define GB_FC32_mul(x,y) (_FCmulcc (x, y))
    #define GB_FC64_mul(x,y) ( _Cmulcc (x, y))

    // complex-complex addition: z = x+y where both x and y are complex
    #define GB_FC32_add(x,y) GxB_CMPLXF (GB_crealf (x) + GB_crealf (y), GB_cimagf (x) + GB_cimagf (y))
    #define GB_FC64_add(x,y) GxB_CMPLX  (GB_creal  (x) + GB_creal  (y), GB_cimag  (x) + GB_cimag  (y))

    // complex-complex subtraction: z = x-y where both x and y are complex
    #define GB_FC32_minus(x,y) GxB_CMPLXF (GB_crealf (x) - GB_crealf (y), GB_cimagf (x) - GB_cimagf (y))
    #define GB_FC64_minus(x,y) GxB_CMPLX  (GB_creal  (x) - GB_creal  (y), GB_cimag  (x) - GB_cimag  (y))

    // complex negation: z = -x
    #define GB_FC32_ainv(x) GxB_CMPLXF (-GB_crealf (x), -GB_cimagf (x))
    #define GB_FC64_ainv(x) GxB_CMPLX  (-GB_creal  (x), -GB_cimag  (x))

#else

    //--------------------------------------------------------------------------
    // native complex type support
    //--------------------------------------------------------------------------

    // complex-complex multiply: z = x*y where both x and y are complex
    #define GB_FC32_mul(x,y) ((x) * (y))
    #define GB_FC64_mul(x,y) ((x) * (y))

    // complex-complex addition: z = x+y where both x and y are complex
    #define GB_FC32_add(x,y) ((x) + (y))
    #define GB_FC64_add(x,y) ((x) + (y))

    // complex-complex subtraction: z = x-y where both x and y are complex
    #define GB_FC32_minus(x,y) ((x) - (y))
    #define GB_FC64_minus(x,y) ((x) - (y))

    // complex negation
    #define GB_FC32_ainv(x) (-(x))
    #define GB_FC64_ainv(x) (-(x))

#endif

//------------------------------------------------------------------------------
// complex comparators
//------------------------------------------------------------------------------

#define GB_FC32_eq(x,y) ((GB_crealf(x) == GB_crealf(y)) && (GB_cimagf(x) == GB_cimagf(y)))
#define GB_FC64_eq(x,y) ((GB_creal (x) == GB_creal (y)) && (GB_cimag (x) == GB_cimag (y)))

#define GB_FC32_ne(x,y) ((GB_crealf(x) != GB_crealf(y)) || (GB_cimagf(x) != GB_cimagf(y)))
#define GB_FC64_ne(x,y) ((GB_creal (x) != GB_creal (y)) || (GB_cimag (x) != GB_cimag (y)))

// safe to use GxB_CMPLX* here because the eq and ne operators return boolean
#define GB_FC32_iseq(x,y) GxB_CMPLXF ((float)  GB_FC32_eq (x,y), 0)
#define GB_FC64_iseq(x,y) GxB_CMPLX  ((double) GB_FC64_eq (x,y), 0)

#define GB_FC32_isne(x,y) GxB_CMPLXF ((float)  GB_FC32_ne (x,y), 0)
#define GB_FC64_isne(x,y) GxB_CMPLX  ((double) GB_FC64_ne (x,y), 0)

#define GB_FC32_eq0(x) ((GB_crealf (x) == 0) && (GB_cimagf (x) == 0))
#define GB_FC64_eq0(x) ((GB_creal  (x) == 0) && (GB_cimag  (x) == 0))

#define GB_FC32_ne0(x) ((GB_crealf (x) != 0) || (GB_cimagf (x) != 0))
#define GB_FC64_ne0(x) ((GB_creal  (x) != 0) || (GB_cimag  (x) != 0))

#endif

