//------------------------------------------------------------------------------
// GraphBLAS/Demo/Source/usercomplex.c:  complex numbers as a user-defined type
//------------------------------------------------------------------------------

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

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

#include "GraphBLAS.h"
#undef GB_PUBLIC
#define GB_LIBRARY
#include "graphblas_demos.h"

#if defined __INTEL_COMPILER
#pragma warning (disable: 58 167 144 161 177 181 186 188 589 593 869 981 1418 1419 1572 1599 2259 2282 2557 2547 3280 )
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#if !defined ( __cplusplus )
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
#endif
#endif

GrB_BinaryOp Complex_first = NULL, Complex_second = NULL, Complex_min = NULL,
             Complex_max   = NULL, Complex_plus   = NULL, Complex_minus = NULL,
             Complex_times = NULL, Complex_div    = NULL, Complex_rminus = NULL,
             Complex_rdiv  = NULL, Complex_pair   = NULL ;

GrB_BinaryOp Complex_iseq = NULL, Complex_isne = NULL,
             Complex_isgt = NULL, Complex_islt = NULL,
             Complex_isge = NULL, Complex_isle = NULL ;

GrB_BinaryOp Complex_or = NULL, Complex_and = NULL, Complex_xor = NULL ;

GrB_BinaryOp Complex_eq = NULL, Complex_ne = NULL,
             Complex_gt = NULL, Complex_lt = NULL,
             Complex_ge = NULL, Complex_le = NULL ;

GrB_BinaryOp Complex_complex = NULL ;

GrB_UnaryOp  Complex_identity = NULL, Complex_ainv = NULL, Complex_minv = NULL,
             Complex_not = NULL,      Complex_conj = NULL,
             Complex_one = NULL,      Complex_abs  = NULL ;

GrB_UnaryOp Complex_real = NULL, Complex_imag = NULL,
            Complex_cabs = NULL, Complex_angle = NULL ;

GrB_UnaryOp Complex_complex_real = NULL, Complex_complex_imag = NULL ;

GrB_Type Complex = NULL ;
GrB_Monoid   Complex_plus_monoid = NULL, Complex_times_monoid = NULL ;
GrB_Semiring Complex_plus_times = NULL ;

#define ONE  GxB_CMPLX(1,0)
#define ZERO GxB_CMPLX(0,0)
#define C    GxB_FC64_t

#define X *x
#define Y *y
#define Z *z

#define T ONE
#define F ZERO
#define BOOL(X) (creal (X) != 0 || cimag (X) != 0)

//------------------------------------------------------------------------------
// binary functions, z=f(x,y), where CxC -> C
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC void complex_first  (C Z, const C X, const C Y) { Z = X ; }
GB_PUBLIC void complex_second (C Z, const C X, const C Y) { Z = Y ; }
GB_PUBLIC void complex_pair   (C Z, const C X, const C Y) { Z = ONE ; }
GB_PUBLIC void complex_plus   (C Z, const C X, const C Y) { Z = X + Y ; }
GB_PUBLIC void complex_minus  (C Z, const C X, const C Y) { Z = X - Y ; }
GB_PUBLIC void complex_rminus (C Z, const C X, const C Y) { Z = Y - X ; }
GB_PUBLIC void complex_times  (C Z, const C X, const C Y) { Z = X * Y ; }
GB_PUBLIC void complex_div    (C Z, const C X, const C Y) { Z = X / Y ; }
GB_PUBLIC void complex_rdiv   (C Z, const C X, const C Y) { Z = Y / X ; }
#endif

GB_PUBLIC
void complex_min (C Z, const C X, const C Y)
{
    // min (x,y): complex number with smallest magnitude.  If tied, select the
    // one with the smallest phase angle.
    // No special cases for NaNs.
    double absx = cabs (X) ;
    double absy = cabs (Y) ;
    if (absx < absy)
    {
        Z = X ;
    }
    else if (absx > absy)
    {
        Z = Y ;
    }
    else
    {
        if (carg (X) < carg (Y))
        {
            Z = X ;
        }
        else
        {
            Z = Y ;
        }
    }
}

GB_PUBLIC
void complex_max (C Z, const C X, const C Y)
{
    // max (x,y): complex number with largest magnitude.  If tied, select the
    // one with the largest phase angle.
    // No special cases for NaNs.
    double absx = cabs (X) ;
    double absy = cabs (Y) ;
    if (absx > absy)
    {
        Z = X ;
    }
    else if (absx < absy)
    {
        Z = Y ;
    }
    else
    {
        if (carg (X) > carg (Y))
        {
            Z = X ;
        }
        else
        {
            Z = Y ;
        }
    }
}

//------------------------------------------------------------------------------
// 6 binary functions, z=f(x,y), where CxC -> C ; (1,0) = true, (0,0) = false
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC
void complex_iseq (C Z, const C X, const C Y)
{
    Z = (creal (X) == creal (Y) && cimag (X) == cimag (Y)) ? T : F ;
}

GB_PUBLIC
void complex_isne (C Z, const C X, const C Y)
{
    Z = (creal (X) != creal (Y) || cimag (X) != cimag (Y)) ? T : F ;
}
#endif

GB_PUBLIC
void complex_isgt (C Z, const C X, const C Y)
{
    Z = (creal (X) > creal (Y)) ? T : F ;
}

GB_PUBLIC
void complex_islt (C Z, const C X, const C Y)
{
    Z = (creal (X) < creal (Y)) ? T : F ;
}
GB_PUBLIC
void complex_isge (C Z, const C X, const C Y)
{
    Z = (creal (X) >= creal (Y)) ? T : F ;
}
GB_PUBLIC
void complex_isle (C Z, const C X, const C Y)
{
    Z = (creal (X) <= creal (Y)) ? T : F ;
}

//------------------------------------------------------------------------------
// binary boolean functions, z=f(x,y), where CxC -> C
//------------------------------------------------------------------------------

GB_PUBLIC
void complex_or (C Z, const C X, const C Y)
{
    Z = (BOOL (X) || BOOL (Y)) ? T : F ;
}

GB_PUBLIC
void complex_and (C Z, const C X, const C Y)
{
    Z = (BOOL (X) && BOOL (Y)) ? T : F ;
}

GB_PUBLIC
void complex_xor (C Z, const C X, const C Y)
{
    Z = (BOOL (X) != BOOL (Y)) ? T : F ;
}

//------------------------------------------------------------------------------
// 6 binary functions, z=f(x,y), where CxC -> bool
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC
void complex_eq (bool Z, const C X, const C Y)
{
    Z = (creal (X) == creal (Y) && cimag (X) == cimag (Y)) ;
}

GB_PUBLIC
void complex_ne (bool Z, const C X, const C Y)
{
    Z = (creal (X) != creal (Y) || cimag (X) != cimag (Y)) ;
}
#endif

GB_PUBLIC
void complex_gt (bool Z, const C X, const C Y)
{
    Z = (creal (X) > creal (Y)) ;
}

GB_PUBLIC
void complex_lt (bool Z, const C X, const C Y)
{
    Z = (creal (X) < creal (Y)) ;
}

GB_PUBLIC
void complex_ge (bool Z, const C X, const C Y)
{
    Z = (creal (X) >= creal (Y)) ;
}

GB_PUBLIC
void complex_le (bool Z, const C X, const C Y)
{
    Z = (creal (X) <= creal (Y)) ;
}

//------------------------------------------------------------------------------
// binary functions, z=f(x,y), where double x double -> complex
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC
void complex_complex (C Z, const double X, const double Y)
{
    Z = GxB_CMPLX (X,Y) ;
}
#endif

//------------------------------------------------------------------------------
// unary functions, z=f(x) where C -> C
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC
void complex_one      (C Z, const C X) { Z =      ONE ; }
GB_PUBLIC
void complex_identity (C Z, const C X) { Z =       X  ; }
GB_PUBLIC
void complex_ainv     (C Z, const C X) { Z =      -X  ; }
GB_PUBLIC
void complex_minv     (C Z, const C X) { Z =  1. / X  ; }
GB_PUBLIC
void complex_conj     (C Z, const C X) { Z = conj (X) ; }
#endif

GB_PUBLIC
void complex_abs      (C Z, const C X) { Z = GxB_CMPLX (cabs (X), 0) ; }
GB_PUBLIC
void complex_not      (C Z, const C X) { Z = BOOL (X) ? F : T ; }

//------------------------------------------------------------------------------
// unary functions, z=f(x) where C -> double
//------------------------------------------------------------------------------

#if GxB_STDC_VERSION >= 201112L
GB_PUBLIC
void complex_real  (double Z, const C X) { Z = creal (X) ; }
GB_PUBLIC
void complex_imag  (double Z, const C X) { Z = cimag (X) ; }
GB_PUBLIC
void complex_cabs  (double Z, const C X) { Z = cabs  (X) ; }
GB_PUBLIC
void complex_angle (double Z, const C X) { Z = carg  (X) ; }
#endif

//------------------------------------------------------------------------------
// unary functions, z=f(x) where double -> C
//------------------------------------------------------------------------------

GB_PUBLIC
void complex_complex_real (C Z, const double X) { Z = GxB_CMPLX (X, 0) ; }
GB_PUBLIC
void complex_complex_imag (C Z, const double X) { Z = GxB_CMPLX (0, X) ; }

//------------------------------------------------------------------------------
// macro to check if a method fails
//------------------------------------------------------------------------------

#undef OK
#define OK(method)              \
    info = method ;             \
    if (info != GrB_SUCCESS)    \
    {                           \
        Complex_finalize ( ) ;  \
        return (info) ;         \
    }

//------------------------------------------------------------------------------
// Complex_init: create the complex type, operators, monoids, and semiring
//------------------------------------------------------------------------------

#undef C
#undef D
#define C Complex
#define D GrB_FP64

#define U (GxB_unary_function)
#define B (GxB_binary_function)

GB_PUBLIC
GrB_Info Complex_init (bool builtin_complex)
{

    GrB_Info info ;

#if GxB_STDC_VERSION < 201112L
    // the Complex type requires the ANSI C11 "double complex" type
    builtin_complex = true ;
#endif

    //--------------------------------------------------------------------------
    // create the Complex type, or set to GxB_FC64
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in type
        Complex = GxB_FC64 ;
    }
    else
    {
        // create the user-defined type
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_Type_new (&Complex, sizeof (GxB_FC64_t))) ;
        #endif
    }

    //--------------------------------------------------------------------------
    // create the Complex binary operators, CxC->C
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_first  = GxB_FIRST_FC64 ;
        Complex_second = GxB_SECOND_FC64 ;
        Complex_pair   = GxB_PAIR_FC64 ;
        Complex_plus   = GxB_PLUS_FC64 ;
        Complex_minus  = GxB_MINUS_FC64 ;
        Complex_rminus = GxB_RMINUS_FC64 ;
        Complex_times  = GxB_TIMES_FC64 ;
        Complex_div    = GxB_DIV_FC64 ;
        Complex_rdiv   = GxB_RDIV_FC64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_BinaryOp_new (&Complex_first  , B complex_first  , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_second , B complex_second , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_pair   , B complex_pair   , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_plus   , B complex_plus   , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_minus  , B complex_minus  , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_rminus , B complex_rminus , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_times  , B complex_times  , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_div    , B complex_div    , C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_rdiv   , B complex_rdiv   , C, C, C)) ;
        #endif
    }

    // these are not built-in
    OK (GrB_BinaryOp_new (&Complex_min    , B complex_min    , C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_max    , B complex_max    , C, C, C)) ;

    //--------------------------------------------------------------------------
    // create the Complex binary comparators, CxC -> C
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_iseq = GxB_ISEQ_FC64 ;
        Complex_isne = GxB_ISNE_FC64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_BinaryOp_new (&Complex_iseq , B complex_iseq ,  C, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_isne , B complex_isne ,  C, C, C)) ;
        #endif
    }

    // these are not built-in
    OK (GrB_BinaryOp_new (&Complex_isgt , B complex_isgt ,  C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_islt , B complex_islt ,  C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_isge , B complex_isge ,  C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_isle , B complex_isle ,  C, C, C)) ;

    //--------------------------------------------------------------------------
    // create the Complex boolean operators, CxC -> C
    //--------------------------------------------------------------------------

    // these are not built-in
    OK (GrB_BinaryOp_new (&Complex_or  , B complex_or  ,  C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_and , B complex_and ,  C, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_xor , B complex_xor ,  C, C, C)) ;

    //--------------------------------------------------------------------------
    // create the Complex binary operators, CxC -> bool
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_eq = GxB_EQ_FC64 ;
        Complex_ne = GxB_NE_FC64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_BinaryOp_new (&Complex_eq , B complex_eq ,  GrB_BOOL, C, C)) ;
        OK (GrB_BinaryOp_new (&Complex_ne , B complex_ne ,  GrB_BOOL, C, C)) ;
        #endif
    }

    // these are not built-in
    OK (GrB_BinaryOp_new (&Complex_gt , B complex_gt ,  GrB_BOOL, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_lt , B complex_lt ,  GrB_BOOL, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_ge , B complex_ge ,  GrB_BOOL, C, C)) ;
    OK (GrB_BinaryOp_new (&Complex_le , B complex_le ,  GrB_BOOL, C, C)) ;

    //--------------------------------------------------------------------------
    // create the Complex binary operator, double x double -> C
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_complex = GxB_CMPLX_FP64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_BinaryOp_new (&Complex_complex, B complex_complex, C, D, D)) ;
        #endif
    }

    //--------------------------------------------------------------------------
    // create the Complex unary operators, C->C
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_one      = GxB_ONE_FC64 ;
        Complex_identity = GxB_IDENTITY_FC64 ;
        Complex_ainv     = GxB_AINV_FC64 ;
        Complex_minv     = GxB_MINV_FC64 ;
        Complex_conj     = GxB_CONJ_FC64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_UnaryOp_new (&Complex_one     , U complex_one     , C, C)) ;
        OK (GrB_UnaryOp_new (&Complex_identity, U complex_identity, C, C)) ;
        OK (GrB_UnaryOp_new (&Complex_ainv    , U complex_ainv    , C, C)) ;
        OK (GrB_UnaryOp_new (&Complex_minv    , U complex_minv    , C, C)) ;
        OK (GrB_UnaryOp_new (&Complex_conj    , U complex_conj    , C, C)) ;
        #endif
    }

    // these are not built-in
    OK (GrB_UnaryOp_new (&Complex_abs     , U complex_abs     , C, C)) ;
    OK (GrB_UnaryOp_new (&Complex_not     , U complex_not     , C, C)) ;

    //--------------------------------------------------------------------------
    // create the unary functions, C -> double
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_real  = GxB_CREAL_FC64 ;
        Complex_imag  = GxB_CIMAG_FC64 ;
        Complex_cabs  = GxB_ABS_FC64 ;
        Complex_angle = GxB_CARG_FC64 ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        OK (GrB_UnaryOp_new (&Complex_real  , U complex_real  , D, C)) ;
        OK (GrB_UnaryOp_new (&Complex_imag  , U complex_imag  , D, C)) ;
        OK (GrB_UnaryOp_new (&Complex_cabs  , U complex_cabs  , D, C)) ;
        OK (GrB_UnaryOp_new (&Complex_angle , U complex_angle , D, C)) ;
        #endif
    }

    //--------------------------------------------------------------------------
    // create the unary functions, double -> C
    //--------------------------------------------------------------------------

    // these are not built-in
    OK (GrB_UnaryOp_new (&Complex_complex_real, U complex_complex_real, C, D)) ;
    OK (GrB_UnaryOp_new (&Complex_complex_imag, U complex_complex_imag, C, D)) ;

    //--------------------------------------------------------------------------
    // create the Complex monoids
    //--------------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_plus_monoid  = GxB_PLUS_FC64_MONOID ;
        Complex_times_monoid = GxB_TIMES_FC64_MONOID ;
    }
    else
    {
        // create user-defined versions
        #if GxB_STDC_VERSION >= 201112L
        double complex C_1 = ONE ;
        double complex C_0 = ZERO ;
        OK (GrB_Monoid_new_UDT (&Complex_plus_monoid,  Complex_plus,  &C_0)) ;
        OK (GrB_Monoid_new_UDT (&Complex_times_monoid, Complex_times, &C_1)) ;
        #endif
    }

    //----------------------------------------------------------------------
    // create the Complex plus-times semiring
    //----------------------------------------------------------------------

    if (builtin_complex)
    {
        // use the built-in versions
        Complex_plus_times = GxB_PLUS_TIMES_FC64 ;
    }
    else
    {
        // more could be created, but this suffices for testing GraphBLAS
        OK (GrB_Semiring_new (&Complex_plus_times, Complex_plus_monoid,
            Complex_times)) ;
    }

    return (GrB_SUCCESS) ;
}


//------------------------------------------------------------------------------
// Complex_finalize: free all complex types, operators, monoids, and semiring
//------------------------------------------------------------------------------

// These may be built-in types and operators.  They are safe to free; the
// GrB_*_free functions silently do nothing if asked to free bulit-in objects.

GB_PUBLIC
GrB_Info Complex_finalize ( )
{

    //--------------------------------------------------------------------------
    // free the Complex plus-times semiring
    //--------------------------------------------------------------------------

    GrB_Semiring_free (&Complex_plus_times) ;

    //--------------------------------------------------------------------------
    // free the Complex monoids
    //--------------------------------------------------------------------------

    GrB_Monoid_free (&Complex_plus_monoid ) ;
    GrB_Monoid_free (&Complex_times_monoid) ;

    //--------------------------------------------------------------------------
    // free the Complex binary operators, CxC->C
    //--------------------------------------------------------------------------

    GrB_BinaryOp_free (&Complex_first ) ;
    GrB_BinaryOp_free (&Complex_second) ;
    GrB_BinaryOp_free (&Complex_pair  ) ;
    GrB_BinaryOp_free (&Complex_min   ) ;
    GrB_BinaryOp_free (&Complex_max   ) ;
    GrB_BinaryOp_free (&Complex_plus  ) ;
    GrB_BinaryOp_free (&Complex_minus ) ;
    GrB_BinaryOp_free (&Complex_rminus) ;
    GrB_BinaryOp_free (&Complex_times ) ;
    GrB_BinaryOp_free (&Complex_div   ) ;
    GrB_BinaryOp_free (&Complex_rdiv  ) ;

    GrB_BinaryOp_free (&Complex_iseq) ;
    GrB_BinaryOp_free (&Complex_isne) ;
    GrB_BinaryOp_free (&Complex_isgt) ;
    GrB_BinaryOp_free (&Complex_islt) ;
    GrB_BinaryOp_free (&Complex_isge) ;
    GrB_BinaryOp_free (&Complex_isle) ;

    GrB_BinaryOp_free (&Complex_or) ;
    GrB_BinaryOp_free (&Complex_and) ;
    GrB_BinaryOp_free (&Complex_xor) ;

    //--------------------------------------------------------------------------
    // free the Complex binary operators, CxC -> bool
    //--------------------------------------------------------------------------

    GrB_BinaryOp_free (&Complex_eq) ;
    GrB_BinaryOp_free (&Complex_ne) ;
    GrB_BinaryOp_free (&Complex_gt) ;
    GrB_BinaryOp_free (&Complex_lt) ;
    GrB_BinaryOp_free (&Complex_ge) ;
    GrB_BinaryOp_free (&Complex_le) ;

    //--------------------------------------------------------------------------
    // free the Complex binary operator, double x double -> complex
    //--------------------------------------------------------------------------

    GrB_BinaryOp_free (&Complex_complex) ;

    //--------------------------------------------------------------------------
    // free the Complex unary operators, C->C
    //--------------------------------------------------------------------------

    GrB_UnaryOp_free (&Complex_one     ) ;
    GrB_UnaryOp_free (&Complex_identity) ;
    GrB_UnaryOp_free (&Complex_ainv    ) ;
    GrB_UnaryOp_free (&Complex_abs     ) ;
    GrB_UnaryOp_free (&Complex_minv    ) ;
    GrB_UnaryOp_free (&Complex_not     ) ;
    GrB_UnaryOp_free (&Complex_conj    ) ;

    //--------------------------------------------------------------------------
    // free the unary functions, C -> double
    //--------------------------------------------------------------------------

    GrB_UnaryOp_free (&Complex_real ) ;
    GrB_UnaryOp_free (&Complex_imag ) ;
    GrB_UnaryOp_free (&Complex_cabs ) ;
    GrB_UnaryOp_free (&Complex_angle) ;

    //--------------------------------------------------------------------------
    // free the unary functions, double -> C
    //--------------------------------------------------------------------------

    GrB_UnaryOp_free (&Complex_complex_real) ;
    GrB_UnaryOp_free (&Complex_complex_imag) ;

    //--------------------------------------------------------------------------
    // free the Complex type
    //--------------------------------------------------------------------------

    GrB_Type_free (&Complex) ;

    return (GrB_SUCCESS) ;
}

