//------------------------------------------------------------------------------
// GB_BinaryOp_compatible: check binary operator for type compatibility
//------------------------------------------------------------------------------

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

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

// check type compatibilty for C = op (A,B).  With typecasting: A is cast to
// op->xtype, B is cast to op->ytype, the operator is computed, and then the
// result of op->ztype is cast to C->type.

#include "GB.h"

GrB_Info GB_BinaryOp_compatible     // check for domain mismatch
(
    const GrB_BinaryOp op,          // binary operator to check
    const GrB_Type ctype,           // C must be compatible with op->ztype
    const GrB_Type atype,           // A must be compatible with op->xtype
    const GrB_Type btype,           // B must be compatible with op->ytype
    const GB_Type_code bcode,       // B may not have a type, just a code
    GB_Context Context
)
{

    //--------------------------------------------------------------------------
    // check inputs
    //--------------------------------------------------------------------------

    // ctype and btype may be NULL, but atype is never NULL
    ASSERT (op != NULL) ;
    ASSERT (atype != NULL) ;
    ASSERT (bcode <= GB_UDT_code) ;
    GB_Opcode opcode = op->opcode ;
    bool op_is_pair_or_positional = (opcode == GB_PAIR_binop_code) 
        || GB_OPCODE_IS_POSITIONAL (opcode) ;

    //--------------------------------------------------------------------------
    // first input A is cast into the type of op->xtype
    //--------------------------------------------------------------------------

    if (opcode == GB_SECOND_binop_code || op_is_pair_or_positional)
    { 
        // first input is unused, so A is always compatible
        ;
    }
    else if (!GB_Type_compatible (atype, op->xtype))
    { 
        GB_ERROR (GrB_DOMAIN_MISMATCH,
            "Incompatible type for z=%s(x,y):\n"
            "first input of type [%s]\n"
            "cannot be typecast to x input of type [%s]",
            op->name, atype->name, op->xtype->name) ;
    }

    //--------------------------------------------------------------------------
    // second input B is cast into the type of op->ytype
    //--------------------------------------------------------------------------

    if (opcode == GB_FIRST_binop_code || op_is_pair_or_positional)
    { 
        // second input is unused, so B is always compatible
        ;
    }
    else if (btype != NULL)
    {
        // B has a type
        if (!GB_Type_compatible (btype, op->ytype))
        { 
            GB_ERROR (GrB_DOMAIN_MISMATCH,
                "Incompatible type for z=%s(x,y):\n"
                "second input of type [%s]\n"
                "cannot be typecast to y input of type [%s]",
                op->name, btype->name, op->ytype->name) ;
        }
    }
    else
    {
        // B has a just a type code, not a type
        if (!GB_code_compatible (bcode, op->ytype->code))
        { 
            GB_ERROR (GrB_DOMAIN_MISMATCH,
                "Incompatible type for z=%s(x,y):\n"
                "second input of type [%s]\n"
                "cannot be typecast to y input of type [%s]",
                op->name, GB_code_string (bcode), op->ytype->name) ;
        }
    }

    //--------------------------------------------------------------------------
    // result of binary operator of op->ztype is cast to C
    //--------------------------------------------------------------------------

    if (!GB_Type_compatible (ctype, op->ztype))
    { 
        GB_ERROR (GrB_DOMAIN_MISMATCH,
            "Incompatible type for z=%s(x,y):\n"
            "operator output z of type [%s]\n"
            "cannot be typecast to result of type [%s]",
            op->name, op->ztype->name, ctype->name) ;
    }

    return (GrB_SUCCESS) ;
}

