//------------------------------------------------------------------------------
// GB_macrofy_reduce: construct all macros for a reduction to scalar
//------------------------------------------------------------------------------

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

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

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

void GB_macrofy_reduce      // construct all macros for GrB_reduce to scalar
(
    FILE *fp,               // target file to write, already open
    // input:
    uint64_t rcode,         // encoded problem
    GrB_Monoid monoid,      // monoid to macrofy
    GrB_Type atype          // type of the A matrix to reduce
)
{ 

    //--------------------------------------------------------------------------
    // extract the reduction rcode
    //--------------------------------------------------------------------------

    // monoid
//  int cheese      = GB_RSHIFT (rcode, 27, 1) ;
    int red_ecode   = GB_RSHIFT (rcode, 22, 5) ;
    int id_ecode    = GB_RSHIFT (rcode, 17, 5) ;
    int term_ecode  = GB_RSHIFT (rcode, 12, 5) ;
//  bool is_term    = (term_ecode < 30) ;

    // type of the monoid
    int zcode       = GB_RSHIFT (rcode, 8, 4) ;

    // type of A
    int acode       = GB_RSHIFT (rcode, 4, 4) ;

    // zombies
    int azombies    = GB_RSHIFT (rcode, 2, 1) ;

    // format of A
    int asparsity   = GB_RSHIFT (rcode, 0, 2) ;

    //--------------------------------------------------------------------------
    // copyright, license, and describe monoid
    //--------------------------------------------------------------------------

    fprintf (fp, "// reduce: (%s, %s)\n",
        monoid->op->name, monoid->op->ztype->name) ;

    //--------------------------------------------------------------------------
    // construct the typedefs
    //--------------------------------------------------------------------------

    GB_macrofy_typedefs (fp, NULL, atype, NULL, NULL, NULL, monoid->op->ztype) ;

    //--------------------------------------------------------------------------
    // construct the monoid macros
    //--------------------------------------------------------------------------

    fprintf (fp, "\n// monoid:\n") ;
    GB_macrofy_type (fp, "Z", "_", monoid->op->ztype->name) ;
    GB_macrofy_monoid (fp, red_ecode, id_ecode, term_ecode, false, monoid,
        false, NULL, NULL) ;

    fprintf (fp, "#define GB_GETA_AND_UPDATE(z,Ax,p)") ;
    if (atype == monoid->op->ztype)
    { 
        // z += Ax [p], with no typecasting.  A is never iso.
        fprintf (fp, " GB_UPDATE (z, Ax [p])\n") ;
    }
    else
    { 
        // aij = (ztype) Ax [p] ; z += aij ; with typecasting.  A is never iso.
        fprintf (fp, " \\\n"
                     "{                             \\\n"
                     "    /* z += (ztype) Ax [p] */ \\\n"
                     "    GB_DECLAREA (aij) ;       \\\n"
                     "    GB_GETA (aij, Ax, p, ) ;  \\\n"
                     "    GB_UPDATE (z, aij) ;      \\\n"
                     "}\n"
                     ) ;
    }

    //--------------------------------------------------------------------------
    // construct the macros for A
    //--------------------------------------------------------------------------

    // iso reduction is handled by GB_reduce_to_scalar_iso, which takes
    // O(log(nvals(A))) for any monoid and uses the function pointer of the
    // monoid operator.  No JIT kernel is ever required to reduce an iso matrix
    // to a scalar, even for user-defined types and monoids.

    GB_macrofy_input (fp, "a", "A", "A", true, monoid->op->ztype,
        atype, asparsity, acode, false, azombies) ;

    //--------------------------------------------------------------------------
    // reduction method
    //--------------------------------------------------------------------------

    fprintf (fp, "\n// panel size for reduction:\n") ;
    int zsize = (int) monoid->op->ztype->size ;
    int panel = 1 ;

    GB_Opcode opcode = monoid->op->opcode ;

    if (opcode == GB_ANY_binop_code)
    { 
        // ANY monoid: do not use panel reduction method
        panel = 1 ;
    }
    else if (zcode == GB_BOOL_code)
    { 
        // all boolean monoids, including user-defined
        panel = 8 ;
    }
    else
    { 

        switch (monoid->op->opcode)
        {

            // min and max
            case GB_MIN_binop_code : 
            case GB_MAX_binop_code : 
                panel = 16 ;
                break ;

            // plus, times, and all bitwise monoids
            case GB_PLUS_binop_code  : 
            case GB_TIMES_binop_code : 
            case GB_BOR_binop_code   : 
            case GB_BAND_binop_code  : 
            case GB_BXOR_binop_code  : 
            case GB_BXNOR_binop_code : 
                switch (zcode)
                {
                    // integer:
                    case GB_INT8_code    : 
                    case GB_UINT8_code   : 
                    case GB_INT16_code   : 
                    case GB_UINT16_code  : 
                    case GB_INT32_code   : 
                    case GB_UINT32_code  : panel = 64 ; break ;
                    case GB_INT64_code   : 
                    case GB_UINT64_code  : panel = 32 ; break ;

                    // floating point:
                    case GB_FP32_code    : panel = 64 ; break ;
                    case GB_FP64_code    : panel = 32 ; break ;
                    case GB_FC32_code    : panel = 32 ; break ;
                    case GB_FC64_code    : panel = 16 ; break ;
                    default:;
                }
                break ;

            default : 

                // all other monoids, including user-defined monoids
                if (zsize <= 16)
                { 
                    panel = 16 ;
                }
                else if (zsize <= 32)
                { 
                    panel = 8 ;
                }
                else
                { 
                    // type is large; do not use panel reduction method
                    panel = 1 ;
                }
        }
    }

    fprintf (fp, "#define GB_PANEL %d\n", panel) ;

    //--------------------------------------------------------------------------
    // include the final default definitions
    //--------------------------------------------------------------------------

    fprintf (fp, "\n#include \"include/GB_monoid_shared_definitions.h\"\n") ;
}

