//------------------------------------------------------------------------------
// GB_bitmap_expand_to_hyper:  expand a compact bitmap C to hypersparse
//------------------------------------------------------------------------------

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

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

#define GB_FREE_ALL                 \
{                                   \
    GB_phbix_free (C) ;             \
    GB_FREE (&Cp, Cp_size) ;        \
    GB_FREE (&Ch, Ch_size) ;        \
    GB_FREE (&Ci, Ci_size) ;        \
}

#include "GB_mxm.h"

GrB_Info GB_bitmap_expand_to_hyper
(
    // input/output:
    GrB_Matrix C,
    // input
    int64_t cvlen_final,
    int64_t cvdim_final,
    GrB_Matrix A,
    GrB_Matrix B,
    GB_Context Context
)
{

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

    ASSERT (C != NULL && (GB_IS_BITMAP (C) || GB_IS_FULL (C))) ;
    ASSERT (A != NULL && B != NULL) ;
    GBURBLE ("(expand bitmap/full to hyper) ") ;
    ASSERT_MATRIX_OK (C, "C to expand from bitmap/full to hyper", GB0) ;
    ASSERT_MATRIX_OK (A, "A for expand C from bitmap/full to hyper", GB0) ;
    ASSERT_MATRIX_OK (B, "B for expand C from bitmap/full to hyper", GB0) ;

    int64_t cvlen = C->vlen ;
    int64_t cvdim = C->vdim ;
    int64_t cnz = cvlen * cvdim ;
    bool A_is_hyper = GB_IS_HYPERSPARSE (A) ;
    bool B_is_hyper = GB_IS_HYPERSPARSE (B) ;

    // C is currently a subset of its final dimension, in bitmap or full form.
    // It is converted back into sparse/hypersparse form, with zombies if
    // bitmap, and expanded in size to be cvlen_final by cvdim_final (A->vdim
    // by B->vdim for C=A'*B, or A->vlen by B->vdim for C=A*B).

    //----------------------------------------------------------------------
    // allocate the sparse/hypersparse structure of the final C
    //----------------------------------------------------------------------

    int64_t *restrict Cp = NULL ; size_t Cp_size = 0 ;
    int64_t *restrict Ch = NULL ; size_t Ch_size = 0 ;
    int64_t *restrict Ci = NULL ; size_t Ci_size = 0 ;

    Cp = GB_MALLOC (cvdim+1, int64_t, &Cp_size) ;
    Ch = NULL ;
    if (B_is_hyper)
    { 
        Ch = GB_MALLOC (cvdim, int64_t, &Ch_size) ;
    }
    Ci = GB_MALLOC (cnz, int64_t, &Ci_size) ;
    if (Cp == NULL || (B_is_hyper && Ch == NULL) || Ci == NULL)
    { 
        // out of memory
        GB_FREE_ALL ;
        return (GrB_OUT_OF_MEMORY) ;
    }

    //----------------------------------------------------------------------
    // construct the hyperlist of C, if B is hypersparse
    //----------------------------------------------------------------------

    GB_GET_NTHREADS_MAX (nthreads_max, chunk, Context) ;
    int nthreads = GB_nthreads (cvdim, chunk, nthreads_max) ;
    if (B_is_hyper)
    { 
        // C becomes hypersparse
        ASSERT (cvdim == B->nvec) ;
        GB_memcpy (Ch, B->h, cvdim * sizeof (int64_t), nthreads) ;
    }

    //----------------------------------------------------------------------
    // construct the vector pointers of C
    //----------------------------------------------------------------------

    int64_t pC ;
    #pragma omp parallel for num_threads(nthreads) schedule(static)
    for (pC = 0 ; pC < cvdim+1 ; pC++)
    { 
        Cp [pC] = pC * cvlen ;
    }

    //----------------------------------------------------------------------
    // construct the pattern of C from its bitmap
    //----------------------------------------------------------------------

    // C(i,j) becomes a zombie if not present in the bitmap
    nthreads = GB_nthreads (cnz, chunk, nthreads_max) ;

    int8_t *restrict Cb = C->b ;
    bool C_is_bitmap = (Cb != NULL) ;
    if (C_is_bitmap)
    {
        // C is bitmap
        if (A_is_hyper)
        { 
            // only for C=A'*B
            GrB_Index *restrict Ah = (GrB_Index *) A->h ;
            ASSERT (cvlen == A->nvec) ;
            #pragma omp parallel for num_threads(nthreads) schedule(static)
            for (pC = 0 ; pC < cnz ; pC++)
            {
                int64_t i = Ah [pC % cvlen] ;
                Ci [pC] = (Cb [pC]) ? i : GB_FLIP (i) ;
            }
        }
        else
        { 
            // for C=A'*B or C=A*B
            ASSERT (cvlen == cvlen_final) ;
            #pragma omp parallel for num_threads(nthreads) schedule(static)
            for (pC = 0 ; pC < cnz ; pC++)
            {
                int64_t i = pC % cvlen ;
                Ci [pC] = (Cb [pC]) ? i : GB_FLIP (i) ;
            }
        }
    }
    else
    {
        // C is full
        if (A_is_hyper)
        { 
            // only for C=A'*B
            GrB_Index *restrict Ah = (GrB_Index *) A->h ;
            ASSERT (cvlen == A->nvec) ;
            #pragma omp parallel for num_threads(nthreads) schedule(static)
            for (pC = 0 ; pC < cnz ; pC++)
            {
                int64_t i = Ah [pC % cvlen] ;
                Ci [pC] = i ;
            }
        }
        else
        { 
            // for C=A'*B or C=A*B
            ASSERT (cvlen == cvlen_final) ;
            #pragma omp parallel for num_threads(nthreads) schedule(static)
            for (pC = 0 ; pC < cnz ; pC++)
            {
                int64_t i = pC % cvlen ;
                Ci [pC] = i ;
            }
        }
    }

    //----------------------------------------------------------------------
    // transplant the new content and finalize C
    //----------------------------------------------------------------------

    C->p = Cp ; Cp = NULL ; C->p_size = Cp_size ;
    C->h = Ch ; Ch = NULL ; C->h_size = Ch_size ;
    C->i = Ci ; Ci = NULL ; C->i_size = Ci_size ;
    C->nzombies = (C_is_bitmap) ? (cnz - C->nvals) : 0 ;
    C->vdim = cvdim_final ;
    C->vlen = cvlen_final ;
    C->nvals = -1 ;
    C->nvec = cvdim ;
    C->plen = cvdim ;
    C->nvec_nonempty = (cvlen == 0) ? 0 : cvdim ;

    // free the bitmap, if present
    GB_FREE ((&C->b), C->b_size) ;

    // C is now sparse or hypersparse
    ASSERT_MATRIX_OK (C, "C expanded from bitmap/full to hyper", GB0) ;
    ASSERT (GB_ZOMBIES_OK (C)) ;
    return (GrB_SUCCESS) ;
}

