//------------------------------------------------------------------------------
// GB_macrofy_input: construct a macro to load values from an input matrix
//------------------------------------------------------------------------------

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

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

// The macro, typically called GB_GETA or GB_GETB, also does typecasting.

#include "GB.h"
#include "jitifyer/GB_stringify.h"

void GB_macrofy_input
(
    FILE *fp,
    // input:
    const char *aname,      // name of the scalar aij = ...
    const char *Amacro,     // name of the macro is GB_GET*(Amacro)
    const char *Aname,      // name of the input matrix (typically A or B)
    bool do_matrix_macros,  // if true, do the matrix macros
    GrB_Type a2type,        // type of aij after casting to x or y of f(x,y)
    GrB_Type atype,         // type of the input matrix
    int asparsity,          // sparsity format of the input matrix
    int acode,              // type code of the input (0 if pattern,
                            // 15 if A is NULL)
    int A_iso_code,         // 1 if A is iso
    int azombies            // 1 if A has zombies, 0 if A has no zombies;
                            // -1 if the macro should not be created.
)
{

    //--------------------------------------------------------------------------
    // construct the matrix status macros: pattern, iso, typename
    //--------------------------------------------------------------------------

    if (acode == 15)
    { 
        // quick return: no input matrix (for binary op with bind 1st or 2nd)
        return ;
    }

    int A_is_pattern = ((acode == 0) ? 1 : 0) || (atype == NULL) ;

    if (do_matrix_macros)
    {
        GB_macrofy_sparsity (fp, Aname, asparsity) ;
        GB_macrofy_nvals (fp, Aname, asparsity, A_iso_code) ;
        if (azombies >= 0)
        { 
            // if negative, do not create the macro at all.  Typically this
            // means A has no zombies.  For GrB_wait, using GB_select to
            // delete zombies, it means A always has zombies.
            fprintf (fp, "#define GB_A_HAS_ZOMBIES %d\n", azombies) ;
        }
    }

    fprintf (fp, "#define GB_%s_ISO %d\n", Aname, A_iso_code) ;
    if (A_is_pattern)
    { 
        // values of A are not accessed
        fprintf (fp, "#define GB_%s_IS_PATTERN 1\n", Aname) ;
        GB_macrofy_type (fp, Aname, "_", "void") ;
    }
    else
    { 
        GB_macrofy_type (fp, Aname, "_", atype->name) ;
    }

    //--------------------------------------------------------------------------
    // construct the macros to declare scalars and get values from the matrix
    //--------------------------------------------------------------------------

    if (A_is_pattern || a2type == NULL)
    {

        //----------------------------------------------------------------------
        // no need to access the values of A
        //----------------------------------------------------------------------

        // aij is not needed as input to the operator
        GB_macrofy_type (fp, Aname, "2", "void") ;

        fprintf (fp, "#define GB_DECLARE%s(%s)\n", Amacro, aname) ;
        fprintf (fp, "#define GB_GET%s(%s,%sx,p,iso)\n", Amacro, aname, Aname) ;

    }
    else
    {

        //----------------------------------------------------------------------
        // construct the scalar/workspace declaration macros
        //----------------------------------------------------------------------

        // Declare a scalar or work array.  For example, suppose A has type
        // double, and the x input to an unflipped operator has type float.  To
        // declare a simple scalar or work array:

        //      GB_DECLAREA (aij) ;
        //      GB_DECLAREA (w [32]) ;

        // becomes:

        //      float aij ;
        //      float w [32] ;

        GB_macrofy_type (fp, Aname, "2", a2type->name) ;

        fprintf (fp, "#define GB_DECLARE%s(%s) %s %s\n",
            Amacro, aname, a2type->name, aname) ;

        //----------------------------------------------------------------------
        // construct the GB_GETA macro
        //----------------------------------------------------------------------

        // #define GB_GETA(a,Ax,p,iso) a = (a2type) Ax [iso ? 0 : p]
        // to load a value from the A matrix, and typecast it to the scalar a
        // of type a2type.  Note that the iso status is baked into the macro,
        // since the kernel will be jitified for that particular iso status.
        // If two cases are identical except for the iso status of an input
        // matrix, two different kernels will be constructed and compiled.

        // For example, to load the scalar aij (with a2type float, in the
        // example above), from the matrix A of type double:

        //      GB_GETA (aij,Ax,p,iso) ;

        // becomes:

        //      aij = ((float) (Ax [p])) ;

        // or, if A is iso:

        //      aij = ((float) (Ax [0])) ;

        #define SLEN 256
        char macro_name [SLEN+1], xargs [SLEN+1], xexpr [SLEN+1] ;
        snprintf (macro_name, SLEN, "GB_GET%s", Amacro) ;
        snprintf (xargs, SLEN, "%sx,p,iso", Aname) ;
        snprintf (xexpr, SLEN, A_iso_code ? "%sx [0]" : "%sx [p]", Aname) ;
        GB_macrofy_cast_input (fp, macro_name, aname, xargs, xexpr, a2type,
            atype) ;
    }
}
