
/*! \file
Copyright (c) 2003, The Regents of the University of California, through
Lawrence Berkeley National Laboratory (subject to receipt of any required
approvals from U.S. Dept. of Energy)

All rights reserved.

The source code is distributed under BSD license, see the file License.txt
at the top-level directory.
*/


/*! @file
 * \brief Factorization routines for the subtree using 2D process grid.
 *
 * <pre>
 * -- Distributed SuperLU routine (version 9.0) --
 * Lawrence Berkeley National Lab, Georgia Institute of Technology,
 * Oak Ridge National Lab
 * May 12, 2021
 *
 * Last update:
 * December 12, 2021  v7.2.0
 * October 1, 2022    v8.1.1  code cleanup (remove #if 0 ...)
 */

#include "superlu_sdefs.h"

int_t sLluBufInit(sLUValSubBuf_t* LUvsb, sLUstruct_t *LUstruct)
{
    sLocalLU_t *Llu = LUstruct->Llu;
    LUvsb->Lsub_buf = intMalloc_dist(Llu->bufmax[0]); //INT_T_ALLOC(Llu->bufmax[0]);
    LUvsb->Lval_buf = floatMalloc_dist(Llu->bufmax[1]); //DOUBLE_ALLOC(Llu->bufmax[1]);
    LUvsb->Usub_buf = intMalloc_dist(Llu->bufmax[2]); //INT_T_ALLOC(Llu->bufmax[2]);
    LUvsb->Uval_buf = floatMalloc_dist(Llu->bufmax[3]); //DOUBLE_ALLOC(Llu->bufmax[3]);
    return 0;
}

/* Allocate an array of buffers for the diagonal blocks on a level of the tree.
   The sizes are uniform: ldt is the maximum among all the nodes.    */
sdiagFactBufs_t** sinitDiagFactBufsArr(int mxLeafNode, int ldt, gridinfo_t* grid)
{
    sdiagFactBufs_t** dFBufs;

    /* Sherry fix:
     * mxLeafNode can be 0 for the replicated layers of the processes ?? */
    if ( mxLeafNode ) dFBufs = (sdiagFactBufs_t** )
                          SUPERLU_MALLOC(mxLeafNode * sizeof(sdiagFactBufs_t*));

    for (int i = 0; i < mxLeafNode; ++i)
    {
        /* code */
        dFBufs[i] = (sdiagFactBufs_t* ) SUPERLU_MALLOC(sizeof(sdiagFactBufs_t));
        assert(dFBufs[i]);
        sinitDiagFactBufs(ldt, dFBufs[i]);

    }/*Minor for loop -2 for (int i = 0; i < mxLeafNode; ++i)*/

    return dFBufs;
}
sdiagFactBufs_t** sinitDiagFactBufsArrMod(int mxLeafNode, int* ldts, gridinfo_t* grid)
{
    sdiagFactBufs_t** dFBufs;

    /* Sherry fix:
     * mxLeafNode can be 0 for the replicated layers of the processes ?? */
    if ( mxLeafNode ) dFBufs = (sdiagFactBufs_t** )
                          SUPERLU_MALLOC(mxLeafNode * sizeof(sdiagFactBufs_t*));

    for (int i = 0; i < mxLeafNode; ++i)
    {
        /* code */
        dFBufs[i] = (sdiagFactBufs_t* ) SUPERLU_MALLOC(sizeof(sdiagFactBufs_t));
        assert(dFBufs[i]);
        sinitDiagFactBufs(ldts[i], dFBufs[i]);

    }/*Minor for loop -2 for (int i = 0; i < mxLeafNode; ++i)*/

    return dFBufs;
}

// sherry added
int sfreeDiagFactBufsArr(int mxLeafNode, sdiagFactBufs_t** dFBufs)
{
    for (int i = 0; i < mxLeafNode; ++i) {
	SUPERLU_FREE(dFBufs[i]->BlockUFactor);
	SUPERLU_FREE(dFBufs[i]->BlockLFactor);
	SUPERLU_FREE(dFBufs[i]);
    }

    /* Sherry fix:
     * mxLeafNode can be 0 for the replicated layers of the processes ?? */
    if ( mxLeafNode ) SUPERLU_FREE(dFBufs);

    return 0;
}

sLUValSubBuf_t** sLluBufInitArr(int_t numLA, sLUstruct_t *LUstruct)
{
    sLUValSubBuf_t** LUvsbs = (sLUValSubBuf_t**) SUPERLU_MALLOC(numLA * sizeof(sLUValSubBuf_t*));
    for (int_t i = 0; i < numLA; ++i)
    {
        /* code */
        LUvsbs[i] = (sLUValSubBuf_t*) SUPERLU_MALLOC(sizeof(sLUValSubBuf_t));
        sLluBufInit(LUvsbs[i], LUstruct);
    } /*minor for loop-3 for (int_t i = 0; i < numLA; ++i)*/

    return LUvsbs;
}

// sherry added
int sLluBufFreeArr(int_t numLA, sLUValSubBuf_t **LUvsbs)
{
    for (int_t i = 0; i < numLA; ++i) {
	SUPERLU_FREE(LUvsbs[i]->Lsub_buf);
	SUPERLU_FREE(LUvsbs[i]->Lval_buf);
	SUPERLU_FREE(LUvsbs[i]->Usub_buf);
	SUPERLU_FREE(LUvsbs[i]->Uval_buf);
	SUPERLU_FREE(LUvsbs[i]);
    }
    SUPERLU_FREE(LUvsbs);
    return 0;
}


int_t sinitScuBufs(superlu_dist_options_t *options,
                  int_t ldt, int_t num_threads, int_t nsupers,
                  sscuBufs_t* scuBufs,
                  sLUstruct_t* LUstruct,
                  gridinfo_t * grid)
{
#if (DEBUGlevel >= 1)
	CHECK_MALLOC(grid->iam, "Enter sinitScuBufs()");
#endif
    scuBufs->bigV = sgetBigV(ldt, num_threads);
    scuBufs->bigU = sgetBigU(options, nsupers, grid, LUstruct);
#if (DEBUGlevel >= 1)
	CHECK_MALLOC(grid->iam, "Exit sinitScuBufs()");
#endif
    return 0;
}

// sherry added
int sfreeScuBufs(sscuBufs_t* scuBufs)
{
    SUPERLU_FREE(scuBufs->bigV);
    SUPERLU_FREE(scuBufs->bigU);
    return 0;
}

int sinitDiagFactBufs(int ldt, sdiagFactBufs_t* dFBuf)
{
    dFBuf->BlockUFactor = floatMalloc_dist(ldt * ldt); //DOUBLE_ALLOC( ldt * ldt);
    dFBuf->BlockLFactor = floatMalloc_dist(ldt * ldt); //DOUBLE_ALLOC( ldt * ldt);
    return 0;
}

int_t sdenseTreeFactor(
    int_t nnodes,          // number of nodes in the tree
    int_t *perm_c_supno,    // list of nodes in the order of factorization
    commRequests_t *comReqs,    // lists of communication requests
    sscuBufs_t *scuBufs,   // contains buffers for schur complement update
    packLUInfo_t*packLUInfo,
    msgs_t*msgs,
    sLUValSubBuf_t* LUvsb,
    sdiagFactBufs_t *dFBuf,
    factStat_t *factStat,
    factNodelists_t  *fNlists,
    superlu_dist_options_t *options,
    int_t * gIperm_c_supno,
    int_t ldt,
    sLUstruct_t *LUstruct, gridinfo3d_t * grid3d, SuperLUStat_t *stat,
    double thresh,  SCT_t *SCT, int tag_ub,
    int *info
)
{
    gridinfo_t* grid = &(grid3d->grid2d);
    sLocalLU_t *Llu = LUstruct->Llu;

    /*main loop over all the super nodes*/
    for (int_t k0 = 0; k0 < nnodes   ; ++k0)
    {
        int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno

        /* diagonal factorization */
	sDiagFactIBCast(k, k, dFBuf->BlockUFactor, dFBuf->BlockLFactor,
			factStat->IrecvPlcd_D,
			comReqs->U_diag_blk_recv_req,
			comReqs->L_diag_blk_recv_req,
			comReqs->U_diag_blk_send_req,
			comReqs->L_diag_blk_send_req,
			grid, options, thresh, LUstruct, stat, info, SCT, tag_ub);

        /*L update */
	sLPanelUpdate(k, factStat->IrecvPlcd_D, factStat->factored_L,
		      comReqs->U_diag_blk_recv_req, dFBuf->BlockUFactor, grid, LUstruct, SCT);
        /*L Ibcast*/
	sIBcastRecvLPanel(k, k, msgs->msgcnt, comReqs->send_req, comReqs->recv_req,
			  LUvsb->Lsub_buf, LUvsb->Lval_buf, factStat->factored,
			  grid, LUstruct, SCT, tag_ub);
        /*U update*/
	sUPanelUpdate(k, factStat->factored_U, comReqs->L_diag_blk_recv_req,
		      dFBuf->BlockLFactor, scuBufs->bigV, ldt,
		      packLUInfo->Ublock_info, grid, LUstruct, stat, SCT);
        /*U bcast*/
	sIBcastRecvUPanel(k, k, msgs->msgcnt, comReqs->send_requ, comReqs->recv_requ,
			  LUvsb->Usub_buf, LUvsb->Uval_buf,
			  grid, LUstruct, SCT, tag_ub);
	sWaitL(k, msgs->msgcnt, msgs->msgcntU, comReqs->send_req, comReqs->recv_req,
	       grid, LUstruct, SCT);
	sWaitU(k, msgs->msgcnt, comReqs->send_requ, comReqs->recv_requ, grid, LUstruct, SCT);

        double tsch = SuperLU_timer_();

	int_t LU_nonempty= sSchurComplementSetup(k, msgs->msgcnt,
				 packLUInfo->Ublock_info, packLUInfo->Remain_info,
				 packLUInfo->uPanelInfo, packLUInfo->lPanelInfo,
				 gIperm_c_supno, fNlists->iperm_u, fNlists->perm_u,
				 scuBufs->bigU, LUvsb->Lsub_buf, LUvsb->Lval_buf,
				 LUvsb->Usub_buf, LUvsb->Uval_buf,
				 grid, LUstruct);
        if (LU_nonempty)
        {
            Ublock_info_t* Ublock_info = packLUInfo->Ublock_info;
            Remain_info_t*  Remain_info = packLUInfo->Remain_info;
            uPanelInfo_t* uPanelInfo = packLUInfo->uPanelInfo;
            lPanelInfo_t* lPanelInfo = packLUInfo->lPanelInfo;
            int* indirect  = fNlists->indirect;
            int* indirect2  = fNlists->indirect2;
            /*Schurcomplement Update*/
            int_t nub = uPanelInfo->nub;
            int_t nlb = lPanelInfo->nlb;
            float* bigV = scuBufs->bigV;
            float* bigU = scuBufs->bigU;

#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
            for (int_t ij = 0; ij < nub * nlb; ++ij)
            {
                /* code */
                int_t** Lrowind_bc_ptr = Llu->Lrowind_bc_ptr;
                float** Lnzval_bc_ptr = Llu->Lnzval_bc_ptr;
                int_t** Ufstnz_br_ptr = LUstruct->Llu->Ufstnz_br_ptr;
                float** Unzval_br_ptr = LUstruct->Llu->Unzval_br_ptr;
                int_t* xsup = LUstruct->Glu_persist->xsup;
                int_t ub = ij / nlb;
                int_t lb
                    = ij % nlb;
                float *L_mat = lPanelInfo->lusup;
                int_t ldl = lPanelInfo->nsupr;
                int_t luptr0 = lPanelInfo->luptr0;
                float *U_mat = bigU;
                int_t ldu = uPanelInfo->ldu;
                int_t knsupc = SuperSize(k);
                int_t klst = FstBlockC (k + 1);
                int_t *lsub = lPanelInfo->lsub;
                int_t *usub = uPanelInfo->usub;
#ifdef _OPENMP
                int thread_id = omp_get_thread_num();
#else
                int thread_id = 0;
#endif
                sblock_gemm_scatter( lb, ub,
                                    Ublock_info,
                                    Remain_info,
                                    &L_mat[luptr0], ldl,
                                    U_mat, ldu,
                                    bigV,
                                    knsupc, klst,
                                    lsub, usub, ldt,
                                    thread_id, indirect, indirect2,
                                    Lrowind_bc_ptr, Lnzval_bc_ptr,
                                    Ufstnz_br_ptr, Unzval_br_ptr,
                                    xsup, grid, stat
#ifdef SCATTER_PROFILE
                                    , Host_TheadScatterMOP, Host_TheadScatterTimer
#endif
                                  );
            } /*for (int_t ij = 0; ij < nub * nlb;*/
        } /*if (LU_nonempty)*/
        SCT->NetSchurUpTimer += SuperLU_timer_() - tsch;

	Wait_LUDiagSend(k, comReqs->U_diag_blk_send_req, comReqs->L_diag_blk_send_req,
			grid, SCT);

    } /*for main loop (int_t k0 = 0; k0 < gNodeCount[tree]; ++k0)*/

    return 0;
} /* sdenseTreeFactor */

/*
 * 2D factorization at individual subtree. -- CPU only
 */
int_t ssparseTreeFactor_ASYNC(
    sForest_t* sforest,
    commRequests_t **comReqss,    // lists of communication requests // size maxEtree level
    sscuBufs_t *scuBufs,       // contains buffers for schur complement update
    packLUInfo_t*packLUInfo,
    msgs_t**msgss,                  // size=num Look ahead
    sLUValSubBuf_t** LUvsbs,          // size=num Look ahead
    sdiagFactBufs_t **dFBufs,         // size maxEtree level
    factStat_t *factStat,
    factNodelists_t  *fNlists,
    gEtreeInfo_t*   gEtreeInfo,        // global etree info
    superlu_dist_options_t *options,
    int_t * gIperm_c_supno,
    int_t ldt,
    HyP_t* HyP,
    sLUstruct_t *LUstruct, gridinfo3d_t * grid3d, SuperLUStat_t *stat,
    double thresh,  SCT_t *SCT, int tag_ub,
    int *info
)
{
    int_t nnodes =   sforest->nNodes ;      // number of nodes in the tree
    if (nnodes < 1)
    {
        return 1;
    }

    /* Test the input parameters. */
    *info = 0;

#if ( DEBUGlevel>=1 )
    CHECK_MALLOC (grid3d->iam, "Enter ssparseTreeFactor_ASYNC()");
#endif

    int_t *perm_c_supno = sforest->nodeList ;  // list of nodes in the order of factorization
    treeTopoInfo_t* treeTopoInfo = &sforest->topoInfo;
    int_t* myIperm = treeTopoInfo->myIperm;

    gridinfo_t* grid = &(grid3d->grid2d);
    /*main loop over all the levels*/

    int_t maxTopoLevel = treeTopoInfo->numLvl;
    int_t* eTreeTopLims = treeTopoInfo->eTreeTopLims;
    int * IrecvPlcd_D = factStat->IrecvPlcd_D;
    int * factored_D = factStat->factored_D;
    int * factored_L = factStat->factored_L;
    int * factored_U = factStat->factored_U;
    int * IbcastPanel_L = factStat->IbcastPanel_L;
    int * IbcastPanel_U = factStat->IbcastPanel_U;
    int_t* xsup = LUstruct->Glu_persist->xsup;

    int_t numLAMax = getNumLookAhead(options);
    int_t numLA = numLAMax;

#if ( PRNTlevel>=2 )
    // Sherry print
    printf("sforest: nNodes %d, numlvl %d\n", (int) nnodes, (int) maxTopoLevel);
    //PrintInt10("perm_c_supno", nnodes, perm_c_supno);
    PrintInt10("eTreeTopLims", maxTopoLevel + 1, eTreeTopLims);
    for (int topoLvl = 0; topoLvl < maxTopoLevel; ++topoLvl)
    {
        int k_st = eTreeTopLims[topoLvl];
        int k_end = eTreeTopLims[topoLvl + 1];
	printf("level %d\n", topoLvl);
	PrintInt10("perm_c_supno", k_end - k_st, &perm_c_supno[k_st]);
    }
    ////////
#endif

    for (int_t k0 = 0; k0 < eTreeTopLims[1]; ++k0)
    {
        int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno
        int_t offset = k0;
        /* k-th diagonal factorization */
        /*Now factor and broadcast diagonal block*/

	sDiagFactIBCast(k, k, dFBufs[offset]->BlockUFactor, dFBufs[offset]->BlockLFactor,
			factStat->IrecvPlcd_D,
			comReqss[offset]->U_diag_blk_recv_req,
			comReqss[offset]->L_diag_blk_recv_req,
			comReqss[offset]->U_diag_blk_send_req,
			comReqss[offset]->L_diag_blk_send_req,
			grid, options, thresh, LUstruct, stat, info, SCT, tag_ub);

        factored_D[k] = 1;
    }

    for (int_t topoLvl = 0; topoLvl < maxTopoLevel; ++topoLvl)
    {
        /* code */
        int_t k_st = eTreeTopLims[topoLvl];
        int_t k_end = eTreeTopLims[topoLvl + 1];
        for (int_t k0 = k_st; k0 < k_end; ++k0)
        {
            int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno
            int_t offset = k0 - k_st;
            /* diagonal factorization */
            if (!factored_D[k] )
            {
                /*If LU panels from GPU are not reduced then reduce
                them before diagonal factorization*/

		sDiagFactIBCast(k, k, dFBufs[offset]->BlockUFactor,
				dFBufs[offset]->BlockLFactor, factStat->IrecvPlcd_D,
				comReqss[offset]->U_diag_blk_recv_req,
				comReqss[offset]->L_diag_blk_recv_req,
				comReqss[offset]->U_diag_blk_send_req,
				comReqss[offset]->L_diag_blk_send_req,
				grid, options, thresh, LUstruct, stat, info, SCT, tag_ub);
            }
        }
        double t_apt = SuperLU_timer_();

        for (int_t k0 = k_st; k0 < k_end; ++k0)
        {
            int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno
            int_t offset = k0 - k_st;

            /*L update */
            if (factored_L[k] == 0)
            {
		sLPanelUpdate(k, factStat->IrecvPlcd_D, factStat->factored_L,
			      comReqss[offset]->U_diag_blk_recv_req,
			      dFBufs[offset]->BlockUFactor, grid, LUstruct, SCT);
                factored_L[k] = 1;
            }
            /*U update*/
            if (factored_U[k] == 0)
            {
		sUPanelUpdate(k, factStat->factored_U, comReqss[offset]->L_diag_blk_recv_req,
			      dFBufs[offset]->BlockLFactor, scuBufs->bigV, ldt,
			      packLUInfo->Ublock_info, grid, LUstruct, stat, SCT);
                factored_U[k] = 1;
            }
        }

        for (int_t k0 = k_st; k0 < SUPERLU_MIN(k_end, k_st + numLA); ++k0)
        {
            int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno
            int_t offset = k0 % numLA;
            /* diagonal factorization */

            /*L Ibcast*/
            if (IbcastPanel_L[k] == 0)
	    {
		sIBcastRecvLPanel(k, k, msgss[offset]->msgcnt, comReqss[offset]->send_req,
				  comReqss[offset]->recv_req, LUvsbs[offset]->Lsub_buf,
				  LUvsbs[offset]->Lval_buf, factStat->factored,
				  grid, LUstruct, SCT, tag_ub);
                IbcastPanel_L[k] = 1; /*for consistancy; unused later*/
            }

            /*U Ibcast*/
            if (IbcastPanel_U[k] == 0)
            {
		sIBcastRecvUPanel(k, k, msgss[offset]->msgcnt, comReqss[offset]->send_requ,
				  comReqss[offset]->recv_requ, LUvsbs[offset]->Usub_buf,
				  LUvsbs[offset]->Uval_buf, grid, LUstruct, SCT, tag_ub);
                IbcastPanel_U[k] = 1;
            }
        }

        // if (topoLvl) SCT->tAsyncPipeTail += SuperLU_timer_() - t_apt;
        SCT->tAsyncPipeTail += SuperLU_timer_() - t_apt;

        for (int_t k0 = k_st; k0 < k_end; ++k0)
        {
            int_t k = perm_c_supno[k0];   // direct computation no perm_c_supno
            int_t offset = k0 % numLA;

	    sWaitL(k, msgss[offset]->msgcnt, msgss[offset]->msgcntU,
		   comReqss[offset]->send_req, comReqss[offset]->recv_req,
		   grid, LUstruct, SCT);
	    sWaitU(k, msgss[offset]->msgcnt, comReqss[offset]->send_requ,
		   comReqss[offset]->recv_requ, grid, LUstruct, SCT);

            double tsch = SuperLU_timer_();

            int_t LU_nonempty = sSchurComplementSetupGPU(k,
							 msgss[offset], packLUInfo,
							 myIperm, gIperm_c_supno,
							 perm_c_supno, gEtreeInfo,
							 fNlists, scuBufs,
							 LUvsbs[offset],
							 grid, LUstruct, HyP);
            // initializing D2H data transfer
            int_t jj_cpu = 0;

            scuStatUpdate( SuperSize(k), HyP,  SCT, stat);
            uPanelInfo_t* uPanelInfo = packLUInfo->uPanelInfo;
            lPanelInfo_t* lPanelInfo = packLUInfo->lPanelInfo;
            int_t *lsub = lPanelInfo->lsub;
            int_t *usub = uPanelInfo->usub;
            int* indirect  = fNlists->indirect;
            int* indirect2  = fNlists->indirect2;

            /*Schurcomplement Update*/

            int_t knsupc = SuperSize(k);
            int_t klst = FstBlockC (k + 1);

            float* bigV = scuBufs->bigV;

#ifdef _OPENMP
#pragma omp parallel
#endif
            {
#ifdef _OPENMP
#pragma omp for schedule(dynamic,2) nowait
#endif
		/* Each thread is assigned one loop index ij, responsible for
		   block update L(lb,k) * U(k,j) -> tempv[]. */
                for (int_t ij = 0; ij < HyP->lookAheadBlk * HyP->num_u_blks; ++ij)
                {
		    /* Get the entire area of L (look-ahead) X U (all-blocks). */
		    /* for each j-block in U, go through all L-blocks in the
		       look-ahead window. */
                    int_t j   = ij / HyP->lookAheadBlk;

                    int_t lb  = ij % HyP->lookAheadBlk;
                    sblock_gemm_scatterTopLeft( lb,  j, bigV, knsupc, klst, lsub,
					       usub, ldt,  indirect, indirect2, HyP,
					       LUstruct, grid, SCT, stat );
                }

#ifdef _OPENMP
#pragma omp for schedule(dynamic,2) nowait
#endif
                for (int_t ij = 0; ij < HyP->lookAheadBlk * HyP->num_u_blks_Phi; ++ij)
                {
                    int_t j   = ij / HyP->lookAheadBlk ;
                    int_t lb  = ij % HyP->lookAheadBlk;
                    sblock_gemm_scatterTopRight( lb,  j, bigV, knsupc, klst, lsub,
                                                usub, ldt,  indirect, indirect2, HyP,
						LUstruct, grid, SCT, stat);
                }

#ifdef _OPENMP
#pragma omp for schedule(dynamic,2) nowait
#endif
                for (int_t ij = 0; ij < HyP->RemainBlk * HyP->num_u_blks; ++ij) //
                {
                    int_t j   = ij / HyP->RemainBlk;
                    int_t lb  = ij % HyP->RemainBlk;
                    sblock_gemm_scatterBottomLeft( lb,  j, bigV, knsupc, klst, lsub,
                                                  usub, ldt,  indirect, indirect2,
						  HyP, LUstruct, grid, SCT, stat);
                } /*for (int_t ij =*/
            }

            if (topoLvl < maxTopoLevel - 1)
            {
                int_t k_parent = gEtreeInfo->setree[k];
                if(k_parent < nnodes) {
		    gEtreeInfo->numChildLeft[k_parent]--;
                    if (gEtreeInfo->numChildLeft[k_parent] == 0) {
                        int_t k0_parent =  myIperm[k_parent];
                        if (k0_parent > 0)
			{
                            /* code */
                            assert(k0_parent < nnodes);
                            int_t offset = k0_parent - k_end;

			    sDiagFactIBCast(k_parent, k_parent, dFBufs[offset]->BlockUFactor,
					dFBufs[offset]->BlockLFactor, factStat->IrecvPlcd_D,
					comReqss[offset]->U_diag_blk_recv_req,
					comReqss[offset]->L_diag_blk_recv_req,
					comReqss[offset]->U_diag_blk_send_req,
					comReqss[offset]->L_diag_blk_send_req,
					grid, options, thresh, LUstruct, stat, info, SCT, tag_ub);

                            factored_D[k_parent] = 1;
                        }
		    }
                }
            }

#ifdef _OPENMP
#pragma omp parallel
#endif
            {
#ifdef _OPENMP
#pragma omp for schedule(dynamic,2) nowait
#endif
                for (int_t ij = 0; ij < HyP->RemainBlk * (HyP->num_u_blks_Phi - jj_cpu) ; ++ij)
                {
                    int_t j   = ij / HyP->RemainBlk + jj_cpu;
                    int_t lb  = ij % HyP->RemainBlk;
                    sblock_gemm_scatterBottomRight( lb,  j, bigV, knsupc, klst, lsub,
                                                   usub, ldt,  indirect, indirect2,
						   HyP, LUstruct, grid, SCT, stat);
                } /*for (int_t ij =*/

            }

            SCT->NetSchurUpTimer += SuperLU_timer_() - tsch;
            // finish waiting for diag block send
            int_t abs_offset = k0 - k_st;

	    Wait_LUDiagSend(k, comReqss[abs_offset]->U_diag_blk_send_req,
			    comReqss[abs_offset]->L_diag_blk_send_req,
			    grid, SCT);

            /*Schedule next I bcasts*/
            for (int_t next_k0 = k0 + 1; next_k0 < SUPERLU_MIN( k0 + 1 + numLA, nnodes); ++next_k0)
            {
                /* code */
                int_t next_k = perm_c_supno[next_k0];
                int_t offset = next_k0 % numLA;

                /*L Ibcast*/
                if (IbcastPanel_L[next_k] == 0 && factored_L[next_k])
                {
		    sIBcastRecvLPanel(next_k, next_k, msgss[offset]->msgcnt,
				      comReqss[offset]->send_req, comReqss[offset]->recv_req,
				      LUvsbs[offset]->Lsub_buf, LUvsbs[offset]->Lval_buf,
				      factStat->factored, grid, LUstruct, SCT, tag_ub);
                    IbcastPanel_L[next_k] = 1; /*will be used later*/
                }
                /*U Ibcast*/
                if (IbcastPanel_U[next_k] == 0 && factored_U[next_k])
                {
		    sIBcastRecvUPanel(next_k, next_k, msgss[offset]->msgcnt,
				      comReqss[offset]->send_requ, comReqss[offset]->recv_requ,
				      LUvsbs[offset]->Usub_buf, LUvsbs[offset]->Uval_buf,
				      grid, LUstruct, SCT, tag_ub);

                    IbcastPanel_U[next_k] = 1;
                }
            }

            if (topoLvl < maxTopoLevel - 1)
            {

                /*look ahead LU factorization*/
                int_t kx_st = eTreeTopLims[topoLvl + 1];
                int_t kx_end = eTreeTopLims[topoLvl + 2];
                for (int_t k0x = kx_st; k0x < kx_end; k0x++)
                {
                    /* code */
                    int_t kx = perm_c_supno[k0x];
                    int_t offset = k0x - kx_st;
                    if (IrecvPlcd_D[kx] && !factored_L[kx])
                    {
                        /*check if received*/
                        int_t recvUDiag = checkRecvUDiag(kx, comReqss[offset],
                                                         grid, SCT);
                        if (recvUDiag)
                        {

			    sLPanelTrSolve( kx, factStat->factored_L,
					    dFBufs[offset]->BlockUFactor, grid, LUstruct);

                            factored_L[kx] = 1;

                            /*check if an L_Ibcast is possible*/

                            if (IbcastPanel_L[kx] == 0 &&
                                    k0x - k0 < numLA + 1  && // is within lookahead window
                                    factored_L[kx])
                            {
                                int_t offset1 = k0x % numLA;

				sIBcastRecvLPanel(kx, kx, msgss[offset1]->msgcnt,
						  comReqss[offset1]->send_req,
						  comReqss[offset1]->recv_req,
						  LUvsbs[offset1]->Lsub_buf,
						  LUvsbs[offset1]->Lval_buf,
						  factStat->factored,
						  grid, LUstruct, SCT, tag_ub);

                                IbcastPanel_L[kx] = 1; /*will be used later*/
                            }

                        }
                    }

                    if (IrecvPlcd_D[kx] && !factored_U[kx])
                    {
                        /*check if received*/
                        int_t recvLDiag = checkRecvLDiag( kx, comReqss[offset],
                                                          grid, SCT);
                        if (recvLDiag)
                        {

			    sUPanelTrSolve( kx, dFBufs[offset]->BlockLFactor,
                                            scuBufs->bigV,
					    ldt, packLUInfo->Ublock_info,
					    grid, LUstruct, stat, SCT);

                            factored_U[kx] = 1;
                            /*check if an L_Ibcast is possible*/

                            if (IbcastPanel_U[kx] == 0 &&
                                    k0x - k0 < numLA + 1  && // is within lookahead window
                                    factored_U[kx])
                            {
                                int_t offset = k0x % numLA;

				sIBcastRecvUPanel(kx, kx, msgss[offset]->msgcnt,
						  comReqss[offset]->send_requ,
						  comReqss[offset]->recv_requ,
						  LUvsbs[offset]->Usub_buf,
						  LUvsbs[offset]->Uval_buf,
						  grid, LUstruct, SCT, tag_ub);

                                IbcastPanel_U[kx] = 1; /*will be used later*/
                            }
                        }
                    }
                }

            }
        }/*for main loop (int_t k0 = 0; k0 < gNodeCount[tree]; ++k0)*/

    }

#if ( DEBUGlevel>=1 )
    CHECK_MALLOC (grid3d->iam, "Exit ssparseTreeFactor_ASYNC()");
#endif

    return 0;
} /* ssparseTreeFactor_ASYNC */
