#include "MatchBoxPC.h"
// ***********************************************************************
//
//        MatchboxP: A C++ library for approximate weighted matching
//               Mahantesh Halappanavar (hala@pnnl.gov)
//               Pacific Northwest National Laboratory
//
// ***********************************************************************
//
//       Copyright (2021) Battelle Memorial Institute
//                      All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ************************************************************************
//////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// DOMINATING EDGES MODEL ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
/* Function	: algoDistEdgeApproxDomEdgesLinearSearchMesgBndlSmallMate()
 *
 * Date     : New update: Feb 17, 2019, Richland, Washington.
 * Date		: Original development: May 17, 2009, E&CS Bldg.
 *
 * Purpose	: Compute Approximate Maximum Weight Matching in Linear Time
 *
 * Args		: inputMatrix - instance of Compressed-Col format of Matrix
 *                Mate - The Mate array
 *
 * Returns	: By Value: (void)
 *            By Reference: Mate
 *
 * Comments	: 1/2 Approx Algorithm. Picks the locally available heaviest edge.
 *                Assumption: The Mate Array is empty.
 */

/*
 NLVer = #of vertices, NLEdge = #of edges
 CSR/CSC/Compressed format: verLocPtr = Pointer, verLocInd = Index, edgeLocWeight = edge weights (positive real numbers)
 verDistance = A vector of size |P|+1 containing the cumulative number of vertices per process
 Mate = A vector of size |V_p| (local subgraph) to store the output (matching)
 MPI: myRank, numProcs, comm,
 Statistics: msgIndSent, msgActualSent, msgPercent : Size: |P| number of processes in the comm-world
 Statistics: ph0_time, ph1_time, ph2_time: Runtimes
 Statistics: ph1_card, ph2_card : Size: |P| number of processes in the comm-world (number of matched edges in Phase 1 and Phase 2)
 */

#ifdef SERIAL_MPI
#else
//MPI type map
template<typename T> MPI_Datatype TypeMap();
template<> inline MPI_Datatype TypeMap<int64_t>() { return MPI_LONG_LONG; }
template<> inline MPI_Datatype TypeMap<int>() { return MPI_INT; }
template<> inline MPI_Datatype TypeMap<double>() { return MPI_DOUBLE; }
template<> inline MPI_Datatype TypeMap<float>() { return MPI_FLOAT; }

// DOUBLE PRECISION VERSION
//WARNING: The vertex block on a given rank is contiguous
void dalgoDistEdgeApproxDomEdgesLinearSearchMesgBndlSmallMateC(
                                                              MilanLongInt NLVer, MilanLongInt NLEdge,
                                                              MilanLongInt* verLocPtr, MilanLongInt* verLocInd,
							      MilanReal* edgeLocWeight,
                                                              MilanLongInt* verDistance,
                                                              MilanLongInt* Mate,
                                                              MilanInt myRank, MilanInt numProcs, MPI_Comm comm,
                                                              MilanLongInt* msgIndSent, MilanLongInt* msgActualSent,
							      MilanReal* msgPercent,
                                                              MilanReal* ph0_time, MilanReal* ph1_time, MilanReal* ph2_time,
                                                              MilanLongInt* ph1_card, MilanLongInt* ph2_card ) {
#if !defined(SERIAL_MPI)
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Within algoEdgeApproxDominatingEdgesLinearSearchMessageBundling()"; fflush(stdout);
#endif

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") verDistance ["<< verDistance[0] << "," << verDistance[1] << "," << verDistance[2] <<"," << verDistance[3] <<"]"; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") verDistance ["<< verDistance[0] << "," << verDistance[1] << "," << verDistance[2] <<"," << verDistance[3] <<"]"; fflush(stdout);
#endif

    //inputSubGraph.getStartEndIndices(StartIndex, EndIndex);
    MilanLongInt StartIndex = verDistance[myRank]; //The starting vertex owned by the current rank
    //MilanLongInt EndIndex = verDistance[myRank+1]; //The ending vertex owned by the current rank
    MilanLongInt EndIndex = verDistance[myRank+1]-1; //The ending vertex owned by the current rank

    MPI_Status computeStatus;
    const int ComputeTag = 7;  //Predefined tag
    const int BundleTag = 9;   //Predefined tag
    int error_codeC;
    error_codeC = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN);
    char error_message[MPI_MAX_ERROR_STRING];
    int message_length;

    //MilanLongInt NLVer=0, NLEdge=0, StartIndex=0, EndIndex=0;
    MilanLongInt msgActual=0, msgInd=0;
    MilanReal heaviestEdgeWt=0.0f; //Assumes positive weight
    MilanReal startTime, finishTime;
    //MilanReal Precision = MPI_Wtick(); //Get the precision of the MPI Timer
    startTime = MPI_Wtime();
    //Get the iterators for the graph:
    //vector<MilanLongInt>::iterator verLocPtr  = inputSubGraph.getVerPtr_b();
    //vector<MilanLongInt>::iterator verLocInd  = inputSubGraph.getVerInd_b();
    //vector<MilanReal>::iterator edgeLocWeight = inputSubGraph.getEdgeWt_b();

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")NV: "<<NLVer<<"  Edges: "<<NLEdge; fflush(stdout);
    cout<<"\n("<<myRank<<")StartIndex: "<<StartIndex<<"  EndIndex: "<<EndIndex; fflush(stdout);
#endif
    //Other Variables:
    MilanLongInt u=-1, v=-1, w=-1, i=0;
    MilanLongInt k=-1, adj1=-1, adj2=-1;
    MilanLongInt k1=-1, adj11=-1, adj12=-1;
    MilanLongInt myCard = 0;
    MilanInt Sender=0; // This is the rank of the sending nodes, it has to be an integer! Fabio

    //Build the Ghost Vertex Set: Vg
    map<MilanLongInt, MilanLongInt> Ghost2LocalMap; //Map each ghost vertex to a local vertex
    // index that starts with zero to |Vg|  - 1
    map<MilanLongInt, MilanLongInt>::iterator storedAlready;
    vector<MilanLongInt> Counter;  //Store the edge count for each ghost vertex
    MilanLongInt numGhostVertices = 0, numGhostEdges = 0, insertMe=0; //Number of Ghost vertices
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")About to compute Ghost Vertices..."; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0)     cout<<"\n("<<myRank<<")About to compute Ghost Vertices..."; fflush(stdout);
#endif

    for ( i=0; i<NLEdge; i++ )  { //O(m) - Each edge stored twice
        insertMe = verLocInd[i];
        //cout<<"InsertMe on Process "<<myRank<<" is: "<<insertMe<<endl;
        if ( (insertMe < StartIndex) || (insertMe > EndIndex) ) { //Find a ghost
            storedAlready = Ghost2LocalMap.find( insertMe );
            if ( storedAlready != Ghost2LocalMap.end() ) { //Has already been added
                //cout<<"Process "<<myRank<<" found: "<<storedAlready->first<<" - "<<storedAlready->second<<endl;
                Counter[storedAlready->second]++; //Increment the counter
                numGhostEdges++;
            } else { //Insert an entry for the ghost:
                //cout<<"Process "<<myRank<<" * New insert:  Key="<<insertMe<< " : Value="<<numGhostVertices<<endl;
                Ghost2LocalMap[insertMe] = numGhostVertices; //Add a map entry
                Counter.push_back(1); //Initialize the counter
                numGhostEdges++;
                numGhostVertices++;  //Increment the number of ghost vertices
            } //End of else()
        } //End of if ( (insertMe < StartIndex) || (insertMe > EndIndex) )
    } //End of for(ghost vertices)
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")NGhosts:" << numGhostVertices << " GhostEdges: "<<numGhostEdges;
    if (!Ghost2LocalMap.empty()) {
        cout<<"\n("<<myRank<<")Final Map : on process ";
        cout<<"\n("<<myRank<<")Key \t Value \t Counter \n"; fflush(stdout);
        storedAlready = Ghost2LocalMap.begin();
        do {
            cout<<storedAlready->second<<" - "<<storedAlready->first<<" : "<<Counter[storedAlready->second]<<endl;
            fflush(stdout);
            storedAlready++;
        } while ( storedAlready != Ghost2LocalMap.end() );
    }
#endif
    //Build Adjacency Lists for Ghost Vertices:
    //cout<<"Building Ghost data structures ... \n\n";
    vector<MilanLongInt> verGhostPtr, verGhostInd, tempCounter;
    //Mate array for ghost vertices:
    vector<MilanLongInt> GMate;  //Proportional to the number of ghost vertices
    try {
        verGhostPtr.reserve(numGhostVertices+1); //Pointer Vector
        tempCounter.reserve(numGhostVertices); //Pointer Vector
        verGhostInd.reserve(numGhostEdges); //Index Vector
        GMate.reserve(numGhostVertices); //Ghost Mate Vector
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    //Initialize the Vectors:
    verGhostPtr.resize(numGhostVertices+1, 0); //Pointer Vector
    tempCounter.resize(numGhostVertices, 0); //Temporary Counter
    verGhostInd.resize(numGhostEdges, -1); //Index Vector
    GMate.resize(numGhostVertices, -1); //Temporary Counter
    verGhostPtr[0] = 0; //The first value
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Ghost Vertex Pointer: "; fflush(stdout);
#endif
    for ( i=0; i<numGhostVertices; i++ )  { //O(|Ghost Vertices|)
        verGhostPtr[i+1] = verGhostPtr[i] + Counter[i];
#ifdef PRINT_DEBUG_INFO_
        cout<<verGhostPtr[i]<<"\t"; fflush(stdout);
#endif
    }
#ifdef PRINT_DEBUG_INFO_
    if ( numGhostVertices > 0 )
        cout<<verGhostPtr[numGhostVertices]<<"\n";
    fflush(stdout);
#endif
    for ( v=0; v < NLVer; v++ ) {
        adj1 = verLocPtr[v];   //Vertex Pointer
        adj2 = verLocPtr[v+1];
        for( k = adj1; k < adj2; k++ ) {
            w = verLocInd[k]; //Get the adjacent vertex
            if ( (w < StartIndex) || (w > EndIndex) ) { //Find a ghost
                insertMe = verGhostPtr[Ghost2LocalMap[w]] + tempCounter[Ghost2LocalMap[w]]; //Where to insert
                verGhostInd[insertMe] = v+StartIndex; //Add the adjacency
                tempCounter[Ghost2LocalMap[w]]++; //Increment the counter
            } //End of if((w < StartIndex) || (w > EndIndex))
        } //End of for(k)
    } //End of for (v)
    tempCounter.clear(); //Do not need this any more
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Ghost Vertex Index: ";
    for ( v=0; v < numGhostEdges; v++ )
        cout<<verGhostInd[v]<<"\t";
    cout<<endl; fflush(stdout);
#endif

    //Data structures for sending and receiving messages:
    vector<MilanLongInt> Message; // [ u, v, message_type ]
    Message.resize(3,-1);
    const MilanLongInt REQUEST  = 1;
    const MilanLongInt SUCCESS  = 2;
    const MilanLongInt FAILURE  = 3;
    const MilanLongInt SIZEINFO = 4;
    MilanLongInt message_type = 0;
    //Data structures for Message Bundling:
    //Although up to two messages can be sent along any cross edge,
    //only one message will be sent in the initialization phase -
    //one of: REQUEST/FAILURE/SUCCESS
    vector<MilanLongInt> QLocalVtx, QGhostVtx, QMsgType;
    vector<MilanInt> QOwner; // Changed by Fabio to be an integer, addresses needs to be integers!
    vector<MilanLongInt> PCounter;
    MilanLongInt NumMessagesBundled=0;
    MilanInt ghostOwner=0; // Changed by Fabio to be an integer, addresses needs to be integers!
    try {
        QLocalVtx.reserve(numGhostEdges); //Local Vertex
        QGhostVtx.reserve(numGhostEdges); //Ghost Vertex
        QMsgType.reserve(numGhostEdges); //Message Type (Request/Failure)
        QOwner.reserve(numGhostEdges); //Owner of the ghost: COmpute once and use later
        PCounter.reserve( numProcs); //Store How many messages will be sent to each processor
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    PCounter.resize(numProcs, 0); //Only initialize the counter variable

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Allocating CandidateMate.. "; fflush(stdout);
#endif
    //Allocate Data Structures:
    vector<MilanLongInt> candidateMate;
    try {
        candidateMate.reserve(NLVer+numGhostVertices); //Dominating edge
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    //Initialize the Vectors:
    candidateMate.resize(NLVer+numGhostVertices, -1);
    //The Queue Data Structure for the Dominating Set:
    staticQueue U(NLVer+numGhostVertices); //Max size is the number of vertices
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    //MPI_Barrier(comm);
    finishTime = MPI_Wtime();
    *ph0_time = finishTime-startTime; //Time taken for Phase-0: Initialization
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Setup Time :"<< *ph0_time <<endl; fflush(stdout);
    fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Setup Time :"<< *ph0_time <<endl; fflush(stdout);
#endif
    startTime = MPI_Wtime();
    /////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////// INITIALIZATION /////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Compute the Initial Matching Set:
    MilanLongInt S = numGhostVertices; //Initialize S with number of Ghost Vertices
    for ( v=0; v < NLVer; v++ ) {
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Processing: "<<v+StartIndex<<endl; fflush(stdout);
#endif
        //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
        //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
        adj1 = verLocPtr[v];
        adj2 = verLocPtr[v+1];
        w = -1;
        heaviestEdgeWt = MilanRealMin; //Assign the smallest Value possible first LDBL_MIN
        for( k = adj1; k < adj2; k++ ) {
            if ( (verLocInd[k]<StartIndex) || (verLocInd[k]>EndIndex) ) { //Is it a ghost vertex?
                if(GMate[Ghost2LocalMap[verLocInd[k]]] >= 0 )// Already matched
                    continue;
            } else { //A local vertex
                if( Mate[verLocInd[k]-StartIndex] >= 0 ) // Already matched
                    continue;
            }

            if( (edgeLocWeight[k] > heaviestEdgeWt) ||
               ((edgeLocWeight[k] == heaviestEdgeWt)&&(w < verLocInd[k])) ) {
                heaviestEdgeWt = edgeLocWeight[k];
                w = verLocInd[k];
            }
        } //End of for loop
        candidateMate[v] = w;

        //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")"<<v+StartIndex<<" Points to: "<<w; fflush(stdout);
#endif
        //If found a dominating edge:
        if ( w >= 0 ) {
            if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost vertex
                //Build the Message Packet:
                //Message[0] = v+StartIndex; //LOCAL
                //Message[1] = w;            //GHOST
                //Message[2] = REQUEST;      //TYPE
                //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                cout<<"\n("<<myRank<<")Sending a request message (291):";
                cout<<"\n("<<myRank<<")Local is: "<<v+StartIndex<<" Ghost is "<<w<<" Owner is: "<< findOwnerOfGhost(w, verDistance, myRank, numProcs) <<endl;
                fflush(stdout);
#endif
                /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                 ComputeTag, comm);*/
                QLocalVtx.push_back(v+StartIndex);
                QGhostVtx.push_back(w);
                QMsgType.push_back(REQUEST);
                //ghostOwner = inputSubGraph.findOwner(w);
                ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                QOwner.push_back(ghostOwner);
                PCounter[ghostOwner]++;
                NumMessagesBundled++;
                msgInd++;
                if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v+StartIndex ) {
                    Mate[v] = w;
                    GMate[Ghost2LocalMap[w]]=v+StartIndex; //w is a Ghost
                    //Q.push_back(u);
                    U.push_back(v+StartIndex);
                    U.push_back(w);
                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")MATCH: ("<<v+StartIndex<<","<<w<<")"; fflush(stdout);
#endif
                    //Decrement the counter:
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v)
                    if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                        Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v)
                } //End of if CandidateMate[w] = v
            } //End of if a Ghost Vertex
            else { // w is a local vertex
                if ( candidateMate[w-StartIndex] == (v+StartIndex) ) {
                    Mate[v] = w;  //v is local
                    Mate[w-StartIndex] = v+StartIndex; //w is local
                    //Q.push_back(u);
                    U.push_back(v+StartIndex);
                    U.push_back(w);
                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")MATCH: ("<<v+StartIndex<<","<<w<<") "; fflush(stdout);
#endif
                } //End of if ( candidateMate[w-StartIndex] == (v+StartIndex) )
            } //End of Else
        } //End of if(w >=0)
        else {
            adj11 = verLocPtr[v];
            adj12 = verLocPtr[v+1];
            for( k1 = adj11; k1 < adj12; k1++ ) {
                w = verLocInd[k1];
                if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                    //Build the Message Packet:
                    //Message[0] = v+StartIndex; //LOCAL
                    //Message[1] = w;            //GHOST
                    //Message[2] = FAILURE;      //TYPE
                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Sending a failure message: ";
                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                    fflush(stdout);
#endif
                    /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                     ComputeTag, comm); */
                    QLocalVtx.push_back(v+StartIndex);
                    QGhostVtx.push_back(w);
                    QMsgType.push_back(FAILURE);
                    //ghostOwner = inputSubGraph.findOwner(w);
                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                    QOwner.push_back(ghostOwner);
                    PCounter[ghostOwner]++;
                    NumMessagesBundled++;
                    msgInd++;
                } //End of if(GHOST)
            } //End of for loop
        } // End of Else: w == -1
        //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
    } //End of for ( v=0; v < NLVer; v++ )

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    ///////////////////////////////////////////////////////////////////////////////////
    /////////////////////////// PROCESS MATCHED VERTICES //////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////
    while ( /*!Q.empty()*/ !U.empty() ) {
        //Q.pop_front();
        u = U.pop_front(); //Get an element from the queue
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")u: "<<u; fflush(stdout);
#endif
        if ( (u >= StartIndex) && (u <= EndIndex) ) { //Process Only the Local Vertices
            //Get the Adjacency list for u
            adj1 = verLocPtr[u-StartIndex];  //Pointer
            adj2 = verLocPtr[u-StartIndex+1];
            for( k = adj1; k < adj2; k++ ) {
                v = verLocInd[k];
                if ( (v >= StartIndex) && (v <= EndIndex) ) { //If Local Vertex:
                    if ( (v<StartIndex) || (v>EndIndex) ) { //Is it a ghost vertex?
                        if(GMate[Ghost2LocalMap[v]] >= 0 )// Already matched
                            continue;
                    } else { //A local vertex
                        if( Mate[v-StartIndex] >= 0 ) // Already matched
                            continue;
                    } //End of else

#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")v: "<<v<<" c(v)= "<<candidateMate[v-StartIndex]<<" Mate[v]: "<<Mate[v];
                    fflush(stdout);
#endif
                    if ( candidateMate[v-StartIndex] == u ) { //Only if pointing to the matched vertex
                        //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                        adj11 = verLocPtr[v-StartIndex];
                        adj12 = verLocPtr[v-StartIndex+1];
                        w = -1;
                        heaviestEdgeWt = MilanRealMin; //Assign the smallest Value possible first LDBL_MIN
                        for( k1 = adj11; k1 < adj12; k1++ ) {
                            if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                    continue;
                            } else { //A local vertex
                                if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                    continue;
                            }
                            if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                               ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                heaviestEdgeWt = edgeLocWeight[k1];
                                w = verLocInd[k1];
                            }
                        } //End of for loop
                        candidateMate[v-StartIndex] = w;
                        //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w; fflush(stdout);
#endif
                        //If found a dominating edge:
                        if ( w >= 0 ) {
                            if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                //Build the Message Packet:
                                //Message[0] = v; //LOCAL
                                //Message[1] = w; //GHOST
                                //Message[2] = REQUEST;  //TYPE
                                //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                cout<<"\n("<<myRank<<")Sending a request message:";
                                cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
#endif
                                /*MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                                 ComputeTag, comm);*/
                                QLocalVtx.push_back(v);
                                QGhostVtx.push_back(w);
                                QMsgType.push_back(REQUEST);
                                //ghostOwner = inputSubGraph.findOwner(w);
                                ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                QOwner.push_back(ghostOwner);
                                PCounter[ghostOwner]++;
                                NumMessagesBundled++;
                                msgInd++;
                                if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                    Mate[v-StartIndex] = w;  //v is a local vertex
                                    GMate[Ghost2LocalMap[w]] = v;  //w is a ghost vertex
                                    //Q.push_back(u);
                                    U.push_back(v);
                                    U.push_back(w);
                                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                    //Decrement the counter:
                                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                        Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                        if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                            fflush(stdout);
#endif
                                        }
                                    } //End of if Counter[w] > 0
                                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                } //End of if CandidateMate[w] = v
                            } //End of if a Ghost Vertex
                            else { //w is a local vertex
                                if ( candidateMate[w-StartIndex] == v )  {
                                    Mate[v-StartIndex] = w;  //v is a local vertex
                                    Mate[w-StartIndex] = v;  //w is a local vertex
                                    //Q.push_back(u);
                                    U.push_back(v);
                                    U.push_back(w);
                                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                } //End of if(CandidateMate(w) = v
                            } //End of Else
                        } //End of if(w >=0)
                        else {
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                w = verLocInd[k1];
                                if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                    //Build the Message Packet:
                                    //Message[0] = v;	     //LOCAL
                                    //Message[1] = w;            //GHOST
                                    //Message[2] = FAILURE;      //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a failure message: ";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                    fflush(stdout);
#endif
                                    /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                                     ComputeTag, comm); */
                                    QLocalVtx.push_back(v);
                                    QGhostVtx.push_back(w);
                                    QMsgType.push_back(FAILURE);
                                    //ghostOwner = inputSubGraph.findOwner(w);
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    QOwner.push_back(ghostOwner);
                                    PCounter[ghostOwner]++;
                                    NumMessagesBundled++;
                                    msgInd++;
                                } //End of if(GHOST)
                            } //End of for loop
                        } // End of Else: w == -1
                        //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                    } //End of If (candidateMate[v-StartIndex] == u)
                } //End of if ( (v >= StartIndex) && (v <= EndIndex) ) //If Local Vertex:
                else { //Neighbor is a ghost vertex
                    if ( candidateMate[NLVer+Ghost2LocalMap[v]] == u )
                        candidateMate[NLVer+Ghost2LocalMap[v]] = -1;
                    if ( v != Mate[u-StartIndex] ) { //u is local
                        //Build the Message Packet:
                        //Message[0] = u; //LOCAL
                        //Message[1] = v; //GHOST
                        //Message[2] = SUCCESS;  //TYPE
                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")Sending a success message: ";
                        cout<<"\n("<<myRank<<")Ghost is "<<v<<" Owner is: "<<findOwnerOfGhost(v, verDistance, myRank, numProcs)<<"\n"; fflush(stdout);
#endif
                        /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(v),
                         ComputeTag, comm); */
                        QLocalVtx.push_back(u);
                        QGhostVtx.push_back(v);
                        QMsgType.push_back(SUCCESS);
                        //ghostOwner = inputSubGraph.findOwner(v);
                        ghostOwner = findOwnerOfGhost(v, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                        QOwner.push_back(ghostOwner);
                        PCounter[ghostOwner]++;
                        NumMessagesBundled++;
                        msgInd++;
                    } //End of If( v != Mate[u] )
                } //End of Else //A Ghost Vertex
            } //End of For Loop adj(u)
        } //End of if ( (u >= StartIndex) && (u <= EndIndex) ) //Process Only If a Local Vertex
    } //End of while ( /*!Q.empty()*/ !U.empty() )
    ///////////////////////// END OF PROCESS MATCHED VERTICES /////////////////////////
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Send Bundles" <<endl; fflush(stdout);
#endif
    /////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////// SEND BUNDLED MESSAGES /////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Data structures for Bundled Messages:
    vector<MilanLongInt> PCumulative, PMessageBundle, PSizeInfoMessages;
    MilanLongInt myIndex=0;
    try {
        PMessageBundle.reserve(NumMessagesBundled*3); //Three integers per message
        PCumulative.reserve(numProcs+1); //Similar to Row Pointer vector in CSR data structure
        PSizeInfoMessages.reserve(numProcs*3); //Buffer to hold the Size info message packets
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    PMessageBundle.resize(NumMessagesBundled*3, -1);//Initialize
    PCumulative.resize(numProcs+1, 0); //Only initialize the counter variable
    PSizeInfoMessages.resize(numProcs*3, 0);

    for (MilanInt i=0; i<numProcs; i++) // Changed by Fabio to be an integer, addresses needs to be integers!
        PCumulative[i+1]=PCumulative[i]+PCounter[i];
    //Reuse PCounter to keep track of how many messages were inserted:
    for (MilanInt i=0; i<numProcs; i++) // Changed by Fabio to be an integer, addresses needs to be integers!
        PCounter[i]=0;
    //Build the Message Bundle packet:
    for (MilanInt i=0; i<NumMessagesBundled; i++) { // Changed by Fabio to be an integer, addresses needs to be integers!
        myIndex = ( PCumulative[QOwner[i]] + PCounter[QOwner[i]] )*3;
        PMessageBundle[myIndex+0] = QLocalVtx[i];
        PMessageBundle[myIndex+1] = QGhostVtx[i];
        PMessageBundle[myIndex+2] = QMsgType[i];
        PCounter[QOwner[i]]++;
    }
    //Send the Bundled Messages: Use ISend
    vector<MPI_Request> SRequest; //Requests that are used for each send message
    vector<MPI_Status> SStatus;   //Status of sent messages, used in MPI_Wait
    MilanLongInt MessageIndex=0; //Pointer for current message
    try {
        SRequest.reserve(numProcs*2); //At most two messages per processor
        SStatus.reserve(numProcs*2);//At most two messages per processor
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearchImmediateSend: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    MPI_Request myReq; //A sample request
    SRequest.resize(numProcs*2,myReq);
    MPI_Status myStat; //A sample status
    SStatus.resize(numProcs*2,myStat);
    //Send the Messages
    for (MilanInt i=0; i<numProcs; i++) { // Changed by Fabio to be an integer, addresses needs to be integers!
        if (i==myRank) //Do not send anything to yourself
            continue;
        //Send the Message with information about the size of next message:
        //Build the Message Packet:
        PSizeInfoMessages[i*3+0] = (PCumulative[i+1]-PCumulative[i])*3; // # of integers in the next message
        PSizeInfoMessages[i*3+1] = -1; //Dummy packet
        PSizeInfoMessages[i*3+2] = SIZEINFO;  //TYPE
        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Sending bundled message to process "<<i<<" size: "<<PSizeInfoMessages[i*3+0]<<endl;
        fflush(stdout);
#endif
        if ( PSizeInfoMessages[i*3+0] > 0 ) { //Send only if it is a nonempty packet
            MPI_Isend(&PSizeInfoMessages[i*3+0], 3, TypeMap<MilanLongInt>(), i, ComputeTag, comm, &SRequest[MessageIndex]);
            msgActual++;
            MessageIndex++;
            //Now Send the message with the data packet:
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Sending Bundle to : "<<i<<endl;
            for (k=(PCumulative[i]*3); k< (PCumulative[i]*3+PSizeInfoMessages[i*3+0]); k++)
                cout<<PMessageBundle[k]<<",";
            cout<<endl;
            fflush(stdout);
#endif
            MPI_Isend(&PMessageBundle[PCumulative[i]*3], PSizeInfoMessages[i*3+0], TypeMap<MilanLongInt>(), i, BundleTag, comm, &SRequest[MessageIndex]);
            MessageIndex++;
        } //End of if size > 0
    }
    //Free up temporary memory:
    PCumulative.clear();
    QLocalVtx.clear();
    QGhostVtx.clear();
    QMsgType.clear();
    QOwner.clear();
    PCounter.clear();
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<")Total number of potential message X 2 = "<<numGhostEdges*2;
    cout<<"\n("<<myRank<<")Number messages already sent in bundles = "<<NumMessagesBundled;
    if (numGhostEdges>0) {
      cout<<"\n("<<myRank<<")Percentage of total = "<<((double)NumMessagesBundled/(double)(numGhostEdges*2))*100.0<<"% \n";
    }
    fflush(stdout);
#endif

    //Allocate memory for MPI Send messages:
    /* WILL COME BACK HERE - NO NEED TO STORE ALL THIS MEMORY !! */
    MilanInt OneMessageSize=0;
    MPI_Pack_size(3, TypeMap<MilanLongInt>(), comm, &OneMessageSize); //Size of one message packet
    //How many messages to send?
    //Potentially three kinds of messages will be sent/received:
    //Request, Success, Failure.
    //But only two will be sent from a given processor.
    //Substract the number of messages that have already been sent as bundled messages:
    MilanLongInt numMessagesToSend = numGhostEdges*2 - NumMessagesBundled;
    MilanInt     BufferSize = (OneMessageSize+MPI_BSEND_OVERHEAD)*numMessagesToSend;

    MilanLongInt *Buffer=0;
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Size of One Message from PACK= "<<OneMessageSize;
    cout<<"\n("<<myRank<<")Size of Message overhead = "<<MPI_BSEND_OVERHEAD;
    cout<<"\n("<<myRank<<")Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<")Number of remaining message = "<<numMessagesToSend;
    cout<<"\n("<<myRank<<")BufferSize = "<<BufferSize;
    cout<<"\n("<<myRank<<")Attaching Buffer on.. ";
    fflush(stdout);
#endif
    if ( BufferSize > 0 ) {
        Buffer = (MilanLongInt *) malloc(BufferSize);  //Allocate memory
        if ( Buffer == 0 ) {
            cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
            cout<<"Not enough memory to allocate for send buffer on process "<<myRank<<"\n";
            exit(1);
        }
        MPI_Buffer_attach(Buffer, BufferSize); //Attach the Buffer
    }
    ///////////////////////// END OF SEND BUNDLED MESSAGES //////////////////////////////////

    finishTime = MPI_Wtime();
    *ph1_time = finishTime-startTime; //Time taken for Phase-1
    *ph1_card = myCard ; //Cardinality at the end of Phase-1
    startTime = MPI_Wtime();
    /////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////// MAIN LOOP //////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Main While Loop:
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Entering While(true) loop.."; fflush(stdout);
    //U.display(); fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    //Buffer to receive bundled messages
    //Maximum messages that can be received from any processor is
    //twice the edge cut: REQUEST; REQUEST+(FAILURE/SUCCESS)
    vector<MilanLongInt> ReceiveBuffer;
    MilanLongInt bundleSize=0, bundleCounter=0;
    try {
        ReceiveBuffer.reserve(numGhostEdges*2*3); //Three integers per cross edge
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    while ( true ) {
#ifdef DEBUG_HANG_
      if (myRank == 0) cout<<"\n("<<myRank<<") Main loop" <<endl; fflush(stdout);
#endif
        ///////////////////////////////////////////////////////////////////////////////////
        /////////////////////////// PROCESS MATCHED VERTICES //////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////
        while ( /*!Q.empty()*/ !U.empty() ) {
            //Q.pop_front();
            u = U.pop_front(); //Get an element from the queue
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")u: "<<u; fflush(stdout);
#endif
            if ( (u >= StartIndex) && (u <= EndIndex) ) { //Process Only If a Local Vertex
                //Get the Adjacency list for u
                adj1 = verLocPtr[u-StartIndex];  //Pointer
                adj2 = verLocPtr[u-StartIndex+1];
                for( k = adj1; k < adj2; k++ ) {
                    v = verLocInd[k];
                    if ( (v >= StartIndex) && (v <= EndIndex) ) { //v is a Local Vertex:
                        if ( Mate[v-StartIndex] >= 0 )   // v is already matched
                            continue;
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")v: "<<v<<" c(v)= "<<candidateMate[v-StartIndex]<<" Mate[v]: "<<Mate[v];
                        fflush(stdout);
#endif
                        if ( candidateMate[v-StartIndex] == u ) { //Only if pointing to the matched vertex
                            //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                            //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            w = -1;
                            heaviestEdgeWt = MilanRealMin; //Assign the smallest Value possible first LDBL_MIN
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                    if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                        continue;
                                }
                                else { //A local vertex
                                    if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                        continue;
                                }

                                if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                                   ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                    heaviestEdgeWt = edgeLocWeight[k1];
                                    w = verLocInd[k1];
                                }
                            } //End of for loop
                            candidateMate[v-StartIndex] = w;
                            //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w; fflush(stdout);
#endif
                            //If found a dominating edge:
                            if ( w >= 0 ) {
                                if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost
                                    //Build the Message Packet:
                                    Message[0] = v; //LOCAL
                                    Message[1] = w; //GHOST
                                    Message[2] = REQUEST;  //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a request message:";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                    fflush(stdout);
#endif
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                    msgInd++; msgActual++;
                                    if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        GMate[Ghost2LocalMap[w]] = v; //w is ghost
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                        //Decrement the counter:
                                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                        if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                            Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                            if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                                fflush(stdout);
#endif
                                            }
                                        } //End of if Counter[w] > 0
                                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    } //End of if CandidateMate[w] = v
                                } //End of if a Ghost Vertex
                                else { //w is a local vertex
                                    if ( candidateMate[w-StartIndex] == v )  {
                                        Mate[v-StartIndex] = w; //v is local
                                        Mate[w-StartIndex] = v; //w is local
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                    } //End of if(CandidateMate(w) = v
                                } //End of Else
                            } //End of if(w >=0)
                            else { //no dominating edge found: w == -1
                                adj11 = verLocPtr[v-StartIndex];
                                adj12 = verLocPtr[v-StartIndex+1];
                                for( k1 = adj11; k1 < adj12; k1++ ) {
                                    w = verLocInd[k1];
                                    if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                        //Build the Message Packet:
                                        Message[0] = v;			 //LOCAL
                                        Message[1] = w;            //GHOST
                                        Message[2] = FAILURE;      //TYPE
                                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")Sending a failure message: ";
                                        cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                        fflush(stdout);
#endif
                                        ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                        MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                        msgInd++; msgActual++;
                                    } //End of if(GHOST)
                                } //End of for loop
                            } // End of Else: w == -1
                            //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        } //End of If (candidateMate[v-StartIndex] == u)
                    } //End of if ( (v >= StartIndex) && (v <= EndIndex) ) //If Local Vertex:
                    else { //Neighbor v is a ghost vertex
                        if ( candidateMate[NLVer+Ghost2LocalMap[v]] == u )
                            candidateMate[NLVer+Ghost2LocalMap[v]] = -1;
                        if ( v != Mate[u-StartIndex] ) { //u is a local vertex
                            //Build the Message Packet:
                            Message[0] = u; //LOCAL
                            Message[1] = v; //GHOST
                            Message[2] = SUCCESS;  //TYPE
                            //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Sending a success message: ";
                            cout<<"\n("<<myRank<<")Ghost is "<<v<<" Owner is: "<<findOwnerOfGhost(v, verDistance, myRank, numProcs);
                            fflush(stdout);
#endif
                            ghostOwner = findOwnerOfGhost(v, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                            MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                            msgInd++; msgActual++;
#ifdef DEBUG_GHOST_
			    if ((u<StartIndex) || (u>EndIndex)) {
			      cout<<"\n("<<myRank<<") "<<__LINE__<<" From Send: should not happen: u= "<<u<<" v= "<<v<<
				" StartIndex "<<StartIndex<<" EndIndex "<<EndIndex<<endl;
			      fflush(stdout);
			    }
#endif

                        } //End of If( v != Mate[u] )
                    } //End of Else //A Ghost Vertex
                } //End of For Loop adj(u)
            } //End of if ( (u >= StartIndex) && (u <= EndIndex) ) //Process Only If a Local Vertex
        } //End of while ( /*!Q.empty()*/ !U.empty() )
        ///////////////////////// END OF PROCESS MATCHED VERTICES /////////////////////////

        //// BREAK IF NO MESSAGES EXPECTED /////////
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Deciding whether to break: S= "<<S<<endl;
#endif

        if ( S == 0 ) {
#ifdef DEBUG_HANG_
	  cout<<"\n("<<myRank<<") Breaking out" <<endl; fflush(stdout);
#endif
            break;
	}
        ///////////////////////////////////////////////////////////////////////////////////
        /////////////////////////// PROCESS MESSAGES //////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////
        /*
         RECEIVE message ( u, v, message_type );
         // u is a GHOST vertex ... v is a LOCAL vertex
         */
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
        fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")About to begin Message processing phase ... S="<<S<<endl;
        fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
        fflush(stdout);
#endif
        //BLOCKING RECEIVE:
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<" Waiting for blocking receive..."<<endl; fflush(stdout);
        fflush(stdout);
#endif
        error_codeC = MPI_Recv(&Message[0], 3, TypeMap<MilanLongInt>(), MPI_ANY_SOURCE, ComputeTag, comm, &computeStatus);
        if (error_codeC != MPI_SUCCESS ) {
            MPI_Error_string(error_codeC, error_message, &message_length);
            cout<<"\n*Error in call to MPI_Receive on Slave: "<<error_message<<"\n"; fflush(stdout);
        }
        Sender = computeStatus.MPI_SOURCE;
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Received message from Process "<<Sender<<" Type= "<<Message[2]<<endl;
        fflush(stdout);
#endif
        //If the Message Type is a size indicator, then receive the bigger message.
        if ( Message[2] == SIZEINFO ) {
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Received bundled message from Process "<<Sender<<" Size= "<<Message[0]<<endl;
            fflush(stdout);
#endif
            bundleSize = Message[0]; //#of integers in the message
            //Build the Message Buffer:
            if (!ReceiveBuffer.empty())
                ReceiveBuffer.clear(); //Empty it out first
            ReceiveBuffer.resize(bundleSize, -1); //Initialize
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Message Bundle Before: "<<endl;
            for (i=0; i<bundleSize; i++)
                cout<<ReceiveBuffer[i]<<",";
            cout<<endl;
            fflush(stdout);
#endif
            //Receive the message
            error_codeC = MPI_Recv(&ReceiveBuffer[0], bundleSize, TypeMap<MilanLongInt>(), Sender, BundleTag, comm, &computeStatus);
            if (error_codeC != MPI_SUCCESS ) {
                MPI_Error_string(error_codeC, error_message, &message_length);
                cout<<"\n*Error in call to MPI_Receive on processor "<<myRank<<" Error: "<<error_message<<"\n"; fflush(stdout);
            }
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Message Bundle After: "<<endl;
            for (i=0; i<bundleSize; i++)
                cout<<ReceiveBuffer[i]<<",";
            cout<<endl;
            fflush(stdout);
#endif
        }
        else { //Just a single message:
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Received regular message from Process "<<Sender<<" u= "<<Message[0]<<" v= "<<Message[1]<<endl;
            fflush(stdout);
#endif
            //Add the current message to Queue:
            bundleSize = 3; //#of integers in the message
            //Build the Message Buffer:
            if (!ReceiveBuffer.empty())
                ReceiveBuffer.clear(); //Empty it out first
            ReceiveBuffer.resize(bundleSize, -1); //Initialize

            ReceiveBuffer[0]=Message[0]; //u
            ReceiveBuffer[1]=Message[1]; //v
            ReceiveBuffer[2]=Message[2]; //message_type
        }
        bundleCounter = 0;
        while ( bundleCounter < bundleSize ) {
            u = ReceiveBuffer[bundleCounter]; //GHOST
            bundleCounter++;
            v = ReceiveBuffer[bundleCounter]; //LOCAL
            bundleCounter++;
            message_type = ReceiveBuffer[bundleCounter]; //TYPE
            bundleCounter++;
#ifdef DEBUG_GHOST_
	    if ((v<StartIndex) || (v>EndIndex)) {
	      cout<<"\n("<<myRank<<") From ReceiveBuffer: This should not happen: u= "<<u<<" v= "<<v<<" Type= "<<message_type<<
		" StartIndex "<<StartIndex<<" EndIndex "<<EndIndex<<endl;
	      fflush(stdout);
	    }
#endif
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Processing message: u= "<<u<<" v= "<<v<<" Type= "<<message_type<<endl;
            fflush(stdout);
#endif
            // CASE I: REQUEST
            if ( message_type == REQUEST ) {
#ifdef PRINT_DEBUG_INFO_
                cout<<"\n("<<myRank<<")Message type is REQUEST"<<endl; fflush(stdout);
#endif
#ifdef DEBUG_GHOST_
		if ((v<0)||(v<StartIndex) || ((v-StartIndex)>NLVer)) {
		  cout<<"\n("<<myRank<<") case 1 Bad address "<<v<<" "<<StartIndex<<" "<<v-StartIndex<<" "<<NLVer<<endl; fflush(stdout);
		}

#endif
                if ( Mate[v-StartIndex] == -1 ) { //Process only if not already matched  (v is local)
                    candidateMate[NLVer+Ghost2LocalMap[u]] = v;  //Set CandidateMate for the ghost
                    if ( candidateMate[v-StartIndex] == u ) {
                        GMate[Ghost2LocalMap[u]] = v; //u is ghost
                        Mate[v-StartIndex] = u; //v is local
                        //Q.push_back(u);
                        U.push_back(v);
                        U.push_back(u);
                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<u<<") "<<endl; fflush(stdout);
#endif
                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                        if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                            Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                            if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages"<<endl;
                                fflush(stdout);
#endif
                            }
                        } //End of if Counter[w] > 0
                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    } //End of if ( candidateMate[v-StartIndex] == u )e
                } //End of if ( Mate[v] == -1 )
            } //End of REQUEST
            else {   //CASE II: SUCCESS
                if ( message_type == SUCCESS ) {
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Message type is SUCCESS"<<endl; fflush(stdout);
#endif
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    GMate[Ghost2LocalMap[u]] = EndIndex+1; //Set a Dummy Mate to make sure that we do not (u is a ghost)
                    //process it again
                    if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                        Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
#ifdef DEBUG_GHOST_
		    if ((v<0)||(v<StartIndex) || ((v-StartIndex)>NLVer)) {
		      cout<<"\n("<<myRank<<") case 2  Bad address "<<v<<" "<<StartIndex<<" "<<v-StartIndex<<" "<<NLVer<<endl; fflush(stdout);
		    }
#endif
                    if ( Mate[v-StartIndex] == -1 ) { //Process only if not already matched ( v is local)
                        if ( candidateMate[v-StartIndex] == u ) {
                            //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                            //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            w = -1;
                            heaviestEdgeWt = MilanRealMin; //Assign the smallest Value possible first LDBL_MIN
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                    if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                        continue;
                                }
                                else { //A local vertex
                                    if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                        continue;
                                }

                                if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                                   ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                    heaviestEdgeWt = edgeLocWeight[k1];
                                    w = verLocInd[k1];
                                }
                            } //End of for loop
                            candidateMate[v-StartIndex] = w;
                            //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w<<endl; fflush(stdout);
#endif
                            //If found a dominating edge:
                            if ( w >= 0 ) {
                                if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost
                                    //Build the Message Packet:
                                    Message[0] = v; //LOCAL
                                    Message[1] = w; //GHOST
                                    Message[2] = REQUEST;  //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a request message: ";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs)<<endl;
                                    fflush(stdout);
#endif
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                    msgInd++; msgActual++;
                                    if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        GMate[Ghost2LocalMap[w]] = v; //w is ghost
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "<<endl; fflush(stdout);
#endif
                                        //Decrement the counter:
                                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                        if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                            Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                            if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                                fflush(stdout);
#endif
                                            }
                                        } //End of if Counter[w] > 0
                                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    } //End of if CandidateMate[w] = v
                                } //End of if a Ghost Vertex
                                else { //w is a local vertex
                                    if ( candidateMate[w-StartIndex] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        Mate[w-StartIndex] = v; //w is local
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "<<endl; fflush(stdout);
#endif
                                    } //End of if(CandidateMate(w) = v
                                } //End of Else
                            } //End of if(w >=0)
                            else { //No dominant edge found
                                adj11 = verLocPtr[v-StartIndex];
                                adj12 = verLocPtr[v-StartIndex+1];
                                for( k1 = adj11; k1 < adj12; k1++ ) {
                                    w = verLocInd[k1];
                                    if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                        //Build the Message Packet:
                                        Message[0] = v;			 //LOCAL
                                        Message[1] = w;            //GHOST
                                        Message[2] = FAILURE;      //TYPE
                                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")Sending a failure message: ";
                                        cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs)<<endl;
                                        fflush(stdout);
#endif
                                        //MPI_Bsend(&Message[0], 3, MilanMpiLongInt, findOwnerOfGhost(w, verDistance, myRank, numProcs),
                                        ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                        MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                        msgInd++; msgActual++;
                                    } //End of if(GHOST)
                                } //End of for loop
                            } // End of Else: w == -1
                            //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        } //End of if ( candidateMate[v-StartIndex] == u )
                    } //End of if ( Mate[v] == -1 )
                } //End of if ( message_type == SUCCESS )
                else { //CASE III: FAILURE
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Message type is FAILURE"<<endl; fflush(stdout);
#endif
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    GMate[Ghost2LocalMap[u]] = EndIndex+1; //Set a Dummy Mate to make sure that we do not (u is a ghost)
                    //process it again
                    if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                        Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                } //End of else: CASE III
            } //End of else: CASE I
        } //End of if (!MsgQ.empty())
        ///////////////////////// END OF PROCESS MESSAGES /////////////////////////////////
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Finished Message processing phase: S= "<<S; fflush(stdout);
        cout<<"\n("<<myRank<<")** SENT     : ACTUAL= "<<msgActual; fflush(stdout);
        cout<<"\n("<<myRank<<")** SENT     : INDIVIDUAL= "<<msgInd<<endl; fflush(stdout);
#endif
    } //End of while (true)

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Waitall= "<<endl; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    cout<<"\n("<<myRank<<") Waitall " <<endl; fflush(stdout);
#endif
    //MPI_Barrier(comm);
    //Cleanup Phase
    MPI_Waitall(MessageIndex, &SRequest[0], &SStatus[0]);
    //MPI_Buffer_attach(&Buffer, BufferSize); //Attach the Buffer
    if ( BufferSize > 0 ) {
      MPI_Buffer_detach(&Buffer, &BufferSize); //Detach the Buffer
      free(Buffer); //Free the memory that was allocated
    }
    finishTime = MPI_Wtime();
    *ph2_time = finishTime-startTime; //Time taken for Phase-2
    *ph2_card = myCard ; //Cardinality at the end of Phase-2

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")End of function to compute matching: "<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")myCardinality: "<<myCard<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")Matching took "<<finishTime-startTime<<"seconds"<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")** Getting out of the matching function **"<<endl; fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<") Total number of potential message X 2 = "<<numGhostEdges*2;
    cout<<"\n("<<myRank<<") Number messages bundled = "<<NumMessagesBundled;
    cout<<"\n("<<myRank<<") Total Individual Messages sent = "<< msgInd;
    if (msgInd>0) {
      cout<<"\n("<<myRank<<") Percentage of messages bundled = "<<((double)NumMessagesBundled/(double)(msgInd))*100.0<<"% \n";
    }
    fflush(stdout);
#endif

    *msgActualSent = msgActual;
    *msgIndSent = msgInd;
    if (msgInd > 0) {
      *msgPercent = ((double)NumMessagesBundled/(double)(msgInd))*100.0;
    } else {
      *msgPercent = 0;
    }
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Done" <<endl; fflush(stdout);
#endif
    //MPI_Barrier(comm);
}
//End of algoDistEdgeApproxDomEdgesLinearSearchMesgBndlSmallMate

// SINGLE PRECISION VERSION

void salgoDistEdgeApproxDomEdgesLinearSearchMesgBndlSmallMateC(
                                                              MilanLongInt NLVer, MilanLongInt NLEdge,
                                                              MilanLongInt* verLocPtr, MilanLongInt* verLocInd,
							      MilanFloat* edgeLocWeight,
                                                              MilanLongInt* verDistance,
                                                              MilanLongInt* Mate,
                                                              MilanInt myRank, MilanInt numProcs, MPI_Comm comm,
                                                              MilanLongInt* msgIndSent, MilanLongInt* msgActualSent,
							      MilanReal* msgPercent,
                                                              MilanReal* ph0_time, MilanReal* ph1_time, MilanReal* ph2_time,
                                                              MilanLongInt* ph1_card, MilanLongInt* ph2_card ) {
#if !defined(SERIAL_MPI)
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Within algoEdgeApproxDominatingEdgesLinearSearchMessageBundling()"; fflush(stdout);
#endif

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") verDistance ["<< verDistance[0] << "," << verDistance[1] << "," << verDistance[2] <<"," << verDistance[3] <<"]"; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") verDistance ["<< verDistance[0] << "," << verDistance[1] << "," << verDistance[2] <<"," << verDistance[3] <<"]"; fflush(stdout);
#endif

    //inputSubGraph.getStartEndIndices(StartIndex, EndIndex);
    MilanLongInt StartIndex = verDistance[myRank]; //The starting vertex owned by the current rank
    //MilanLongInt EndIndex = verDistance[myRank+1]; //The ending vertex owned by the current rank
    MilanLongInt EndIndex = verDistance[myRank+1]-1; //The ending vertex owned by the current rank

    MPI_Status computeStatus;
    const int ComputeTag = 7;  //Predefined tag
    const int BundleTag = 9;   //Predefined tag
    int error_codeC;
    error_codeC = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN);
    char error_message[MPI_MAX_ERROR_STRING];
    int message_length;

    //MilanLongInt NLVer=0, NLEdge=0, StartIndex=0, EndIndex=0;
    MilanLongInt msgActual=0, msgInd=0;
    MilanFloat heaviestEdgeWt=0.0f; //Assumes positive weight
    MilanReal startTime, finishTime;
    //MilanReal Precision = MPI_Wtick(); //Get the precision of the MPI Timer
    startTime = MPI_Wtime();
    //Get the iterators for the graph:
    //vector<MilanLongInt>::iterator verLocPtr  = inputSubGraph.getVerPtr_b();
    //vector<MilanLongInt>::iterator verLocInd  = inputSubGraph.getVerInd_b();
    //vector<MilanReal>::iterator edgeLocWeight = inputSubGraph.getEdgeWt_b();

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")NV: "<<NLVer<<"  Edges: "<<NLEdge; fflush(stdout);
    cout<<"\n("<<myRank<<")StartIndex: "<<StartIndex<<"  EndIndex: "<<EndIndex; fflush(stdout);
#endif
    //Other Variables:
    MilanLongInt u=-1, v=-1, w=-1, i=0;
    MilanLongInt k=-1, adj1=-1, adj2=-1;
    MilanLongInt k1=-1, adj11=-1, adj12=-1;
    MilanLongInt myCard = 0;
    MilanInt Sender=0; // This is the rank of the sending nodes, it has to be an integer! Fabio

    //Build the Ghost Vertex Set: Vg
    map<MilanLongInt, MilanLongInt> Ghost2LocalMap; //Map each ghost vertex to a local vertex
    // index that starts with zero to |Vg|  - 1
    map<MilanLongInt, MilanLongInt>::iterator storedAlready;
    vector<MilanLongInt> Counter;  //Store the edge count for each ghost vertex
    MilanLongInt numGhostVertices = 0, numGhostEdges = 0, insertMe=0; //Number of Ghost vertices
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")About to compute Ghost Vertices..."; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0)     cout<<"\n("<<myRank<<")About to compute Ghost Vertices..."; fflush(stdout);
#endif

    for ( i=0; i<NLEdge; i++ )  { //O(m) - Each edge stored twice
        insertMe = verLocInd[i];
        //cout<<"InsertMe on Process "<<myRank<<" is: "<<insertMe<<endl;
        if ( (insertMe < StartIndex) || (insertMe > EndIndex) ) { //Find a ghost
            storedAlready = Ghost2LocalMap.find( insertMe );
            if ( storedAlready != Ghost2LocalMap.end() ) { //Has already been added
                //cout<<"Process "<<myRank<<" found: "<<storedAlready->first<<" - "<<storedAlready->second<<endl;
                Counter[storedAlready->second]++; //Increment the counter
                numGhostEdges++;
            } else { //Insert an entry for the ghost:
                //cout<<"Process "<<myRank<<" * New insert:  Key="<<insertMe<< " : Value="<<numGhostVertices<<endl;
                Ghost2LocalMap[insertMe] = numGhostVertices; //Add a map entry
                Counter.push_back(1); //Initialize the counter
                numGhostEdges++;
                numGhostVertices++;  //Increment the number of ghost vertices
            } //End of else()
        } //End of if ( (insertMe < StartIndex) || (insertMe > EndIndex) )
    } //End of for(ghost vertices)
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")NGhosts:" << numGhostVertices << " GhostEdges: "<<numGhostEdges;
    if (!Ghost2LocalMap.empty()) {
        cout<<"\n("<<myRank<<")Final Map : on process ";
        cout<<"\n("<<myRank<<")Key \t Value \t Counter \n"; fflush(stdout);
        storedAlready = Ghost2LocalMap.begin();
        do {
            cout<<storedAlready->second<<" - "<<storedAlready->first<<" : "<<Counter[storedAlready->second]<<endl;
            fflush(stdout);
            storedAlready++;
        } while ( storedAlready != Ghost2LocalMap.end() );
    }
#endif
    //Build Adjacency Lists for Ghost Vertices:
    //cout<<"Building Ghost data structures ... \n\n";
    vector<MilanLongInt> verGhostPtr, verGhostInd, tempCounter;
    //Mate array for ghost vertices:
    vector<MilanLongInt> GMate;  //Proportional to the number of ghost vertices
    try {
        verGhostPtr.reserve(numGhostVertices+1); //Pointer Vector
        tempCounter.reserve(numGhostVertices); //Pointer Vector
        verGhostInd.reserve(numGhostEdges); //Index Vector
        GMate.reserve(numGhostVertices); //Ghost Mate Vector
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    //Initialize the Vectors:
    verGhostPtr.resize(numGhostVertices+1, 0); //Pointer Vector
    tempCounter.resize(numGhostVertices, 0); //Temporary Counter
    verGhostInd.resize(numGhostEdges, -1); //Index Vector
    GMate.resize(numGhostVertices, -1); //Temporary Counter
    verGhostPtr[0] = 0; //The first value
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Ghost Vertex Pointer: "; fflush(stdout);
#endif
    for ( i=0; i<numGhostVertices; i++ )  { //O(|Ghost Vertices|)
        verGhostPtr[i+1] = verGhostPtr[i] + Counter[i];
#ifdef PRINT_DEBUG_INFO_
        cout<<verGhostPtr[i]<<"\t"; fflush(stdout);
#endif
    }
#ifdef PRINT_DEBUG_INFO_
    if ( numGhostVertices > 0 )
        cout<<verGhostPtr[numGhostVertices]<<"\n";
    fflush(stdout);
#endif
    for ( v=0; v < NLVer; v++ ) {
        adj1 = verLocPtr[v];   //Vertex Pointer
        adj2 = verLocPtr[v+1];
        for( k = adj1; k < adj2; k++ ) {
            w = verLocInd[k]; //Get the adjacent vertex
            if ( (w < StartIndex) || (w > EndIndex) ) { //Find a ghost
                insertMe = verGhostPtr[Ghost2LocalMap[w]] + tempCounter[Ghost2LocalMap[w]]; //Where to insert
                verGhostInd[insertMe] = v+StartIndex; //Add the adjacency
                tempCounter[Ghost2LocalMap[w]]++; //Increment the counter
            } //End of if((w < StartIndex) || (w > EndIndex))
        } //End of for(k)
    } //End of for (v)
    tempCounter.clear(); //Do not need this any more
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Ghost Vertex Index: ";
    for ( v=0; v < numGhostEdges; v++ )
        cout<<verGhostInd[v]<<"\t";
    cout<<endl; fflush(stdout);
#endif

    //Data structures for sending and receiving messages:
    vector<MilanLongInt> Message; // [ u, v, message_type ]
    Message.resize(3,-1);
    const MilanLongInt REQUEST  = 1;
    const MilanLongInt SUCCESS  = 2;
    const MilanLongInt FAILURE  = 3;
    const MilanLongInt SIZEINFO = 4;
    MilanLongInt message_type = 0;
    //Data structures for Message Bundling:
    //Although up to two messages can be sent along any cross edge,
    //only one message will be sent in the initialization phase -
    //one of: REQUEST/FAILURE/SUCCESS
    vector<MilanLongInt> QLocalVtx, QGhostVtx, QMsgType;
    vector<MilanInt> QOwner; // Changed by Fabio to be an integer, addresses needs to be integers!
    vector<MilanLongInt> PCounter;
    MilanLongInt NumMessagesBundled=0;
    MilanInt ghostOwner=0; // Changed by Fabio to be an integer, addresses needs to be integers!
    try {
        QLocalVtx.reserve(numGhostEdges); //Local Vertex
        QGhostVtx.reserve(numGhostEdges); //Ghost Vertex
        QMsgType.reserve(numGhostEdges); //Message Type (Request/Failure)
        QOwner.reserve(numGhostEdges); //Owner of the ghost: COmpute once and use later
        PCounter.reserve( numProcs); //Store How many messages will be sent to each processor
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    PCounter.resize(numProcs, 0); //Only initialize the counter variable

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Allocating CandidateMate.. "; fflush(stdout);
#endif
    //Allocate Data Structures:
    vector<MilanLongInt> candidateMate;
    try {
        candidateMate.reserve(NLVer+numGhostVertices); //Dominating edge
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    //Initialize the Vectors:
    candidateMate.resize(NLVer+numGhostVertices, -1);
    //The Queue Data Structure for the Dominating Set:
    staticQueue U(NLVer+numGhostVertices); //Max size is the number of vertices
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    //MPI_Barrier(comm);
    finishTime = MPI_Wtime();
    *ph0_time = finishTime-startTime; //Time taken for Phase-0: Initialization
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Setup Time :"<< *ph0_time <<endl; fflush(stdout);
    fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Setup Time :"<< *ph0_time <<endl; fflush(stdout);
#endif
    startTime = MPI_Wtime();
    /////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////// INITIALIZATION /////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Compute the Initial Matching Set:
    MilanLongInt S = numGhostVertices; //Initialize S with number of Ghost Vertices
    for ( v=0; v < NLVer; v++ ) {
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Processing: "<<v+StartIndex<<endl; fflush(stdout);
#endif
        //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
        //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
        adj1 = verLocPtr[v];
        adj2 = verLocPtr[v+1];
        w = -1;
        heaviestEdgeWt = MilanFloatMin; //Assign the smallest Value possible first LDBL_MIN
        for( k = adj1; k < adj2; k++ ) {
            if ( (verLocInd[k]<StartIndex) || (verLocInd[k]>EndIndex) ) { //Is it a ghost vertex?
                if(GMate[Ghost2LocalMap[verLocInd[k]]] >= 0 )// Already matched
                    continue;
            } else { //A local vertex
                if( Mate[verLocInd[k]-StartIndex] >= 0 ) // Already matched
                    continue;
            }

            if( (edgeLocWeight[k] > heaviestEdgeWt) ||
               ((edgeLocWeight[k] == heaviestEdgeWt)&&(w < verLocInd[k])) ) {
                heaviestEdgeWt = edgeLocWeight[k];
                w = verLocInd[k];
            }
        } //End of for loop
        candidateMate[v] = w;

        //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")"<<v+StartIndex<<" Points to: "<<w; fflush(stdout);
#endif
        //If found a dominating edge:
        if ( w >= 0 ) {
            if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost vertex
                //Build the Message Packet:
                //Message[0] = v+StartIndex; //LOCAL
                //Message[1] = w;            //GHOST
                //Message[2] = REQUEST;      //TYPE
                //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                cout<<"\n("<<myRank<<")Sending a request message (291):";
                cout<<"\n("<<myRank<<")Local is: "<<v+StartIndex<<" Ghost is "<<w<<" Owner is: "<< findOwnerOfGhost(w, verDistance, myRank, numProcs) <<endl;
                fflush(stdout);
#endif
                /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                 ComputeTag, comm);*/
                QLocalVtx.push_back(v+StartIndex);
                QGhostVtx.push_back(w);
                QMsgType.push_back(REQUEST);
                //ghostOwner = inputSubGraph.findOwner(w);
                ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                QOwner.push_back(ghostOwner);
                PCounter[ghostOwner]++;
                NumMessagesBundled++;
                msgInd++;
                if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v+StartIndex ) {
                    Mate[v] = w;
                    GMate[Ghost2LocalMap[w]]=v+StartIndex; //w is a Ghost
                    //Q.push_back(u);
                    U.push_back(v+StartIndex);
                    U.push_back(w);
                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")MATCH: ("<<v+StartIndex<<","<<w<<")"; fflush(stdout);
#endif
                    //Decrement the counter:
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v)
                    if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                        Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v)
                } //End of if CandidateMate[w] = v
            } //End of if a Ghost Vertex
            else { // w is a local vertex
                if ( candidateMate[w-StartIndex] == (v+StartIndex) ) {
                    Mate[v] = w;  //v is local
                    Mate[w-StartIndex] = v+StartIndex; //w is local
                    //Q.push_back(u);
                    U.push_back(v+StartIndex);
                    U.push_back(w);
                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")MATCH: ("<<v+StartIndex<<","<<w<<") "; fflush(stdout);
#endif
                } //End of if ( candidateMate[w-StartIndex] == (v+StartIndex) )
            } //End of Else
        } //End of if(w >=0)
        else {
            adj11 = verLocPtr[v];
            adj12 = verLocPtr[v+1];
            for( k1 = adj11; k1 < adj12; k1++ ) {
                w = verLocInd[k1];
                if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                    //Build the Message Packet:
                    //Message[0] = v+StartIndex; //LOCAL
                    //Message[1] = w;            //GHOST
                    //Message[2] = FAILURE;      //TYPE
                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Sending a failure message: ";
                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                    fflush(stdout);
#endif
                    /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                     ComputeTag, comm); */
                    QLocalVtx.push_back(v+StartIndex);
                    QGhostVtx.push_back(w);
                    QMsgType.push_back(FAILURE);
                    //ghostOwner = inputSubGraph.findOwner(w);
                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                    QOwner.push_back(ghostOwner);
                    PCounter[ghostOwner]++;
                    NumMessagesBundled++;
                    msgInd++;
                } //End of if(GHOST)
            } //End of for loop
        } // End of Else: w == -1
        //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
    } //End of for ( v=0; v < NLVer; v++ )

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    ///////////////////////////////////////////////////////////////////////////////////
    /////////////////////////// PROCESS MATCHED VERTICES //////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////
    while ( /*!Q.empty()*/ !U.empty() ) {
        //Q.pop_front();
        u = U.pop_front(); //Get an element from the queue
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")u: "<<u; fflush(stdout);
#endif
        if ( (u >= StartIndex) && (u <= EndIndex) ) { //Process Only the Local Vertices
            //Get the Adjacency list for u
            adj1 = verLocPtr[u-StartIndex];  //Pointer
            adj2 = verLocPtr[u-StartIndex+1];
            for( k = adj1; k < adj2; k++ ) {
                v = verLocInd[k];
                if ( (v >= StartIndex) && (v <= EndIndex) ) { //If Local Vertex:
                    if ( (v<StartIndex) || (v>EndIndex) ) { //Is it a ghost vertex?
                        if(GMate[Ghost2LocalMap[v]] >= 0 )// Already matched
                            continue;
                    } else { //A local vertex
                        if( Mate[v-StartIndex] >= 0 ) // Already matched
                            continue;
                    } //End of else

#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")v: "<<v<<" c(v)= "<<candidateMate[v-StartIndex]<<" Mate[v]: "<<Mate[v];
                    fflush(stdout);
#endif
                    if ( candidateMate[v-StartIndex] == u ) { //Only if pointing to the matched vertex
                        //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                        adj11 = verLocPtr[v-StartIndex];
                        adj12 = verLocPtr[v-StartIndex+1];
                        w = -1;
                        heaviestEdgeWt = MilanFloatMin; //Assign the smallest Value possible first LDBL_MIN
                        for( k1 = adj11; k1 < adj12; k1++ ) {
                            if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                    continue;
                            } else { //A local vertex
                                if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                    continue;
                            }
                            if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                               ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                heaviestEdgeWt = edgeLocWeight[k1];
                                w = verLocInd[k1];
                            }
                        } //End of for loop
                        candidateMate[v-StartIndex] = w;
                        //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w; fflush(stdout);
#endif
                        //If found a dominating edge:
                        if ( w >= 0 ) {
                            if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                //Build the Message Packet:
                                //Message[0] = v; //LOCAL
                                //Message[1] = w; //GHOST
                                //Message[2] = REQUEST;  //TYPE
                                //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                cout<<"\n("<<myRank<<")Sending a request message:";
                                cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
#endif
                                /*MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                                 ComputeTag, comm);*/
                                QLocalVtx.push_back(v);
                                QGhostVtx.push_back(w);
                                QMsgType.push_back(REQUEST);
                                //ghostOwner = inputSubGraph.findOwner(w);
                                ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                QOwner.push_back(ghostOwner);
                                PCounter[ghostOwner]++;
                                NumMessagesBundled++;
                                msgInd++;
                                if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                    Mate[v-StartIndex] = w;  //v is a local vertex
                                    GMate[Ghost2LocalMap[w]] = v;  //w is a ghost vertex
                                    //Q.push_back(u);
                                    U.push_back(v);
                                    U.push_back(w);
                                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                    //Decrement the counter:
                                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                        Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                        if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                            fflush(stdout);
#endif
                                        }
                                    } //End of if Counter[w] > 0
                                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                } //End of if CandidateMate[w] = v
                            } //End of if a Ghost Vertex
                            else { //w is a local vertex
                                if ( candidateMate[w-StartIndex] == v )  {
                                    Mate[v-StartIndex] = w;  //v is a local vertex
                                    Mate[w-StartIndex] = v;  //w is a local vertex
                                    //Q.push_back(u);
                                    U.push_back(v);
                                    U.push_back(w);
                                    myCard++;
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                } //End of if(CandidateMate(w) = v
                            } //End of Else
                        } //End of if(w >=0)
                        else {
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                w = verLocInd[k1];
                                if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                    //Build the Message Packet:
                                    //Message[0] = v;	     //LOCAL
                                    //Message[1] = w;            //GHOST
                                    //Message[2] = FAILURE;      //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a failure message: ";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                    fflush(stdout);
#endif
                                    /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(w),
                                     ComputeTag, comm); */
                                    QLocalVtx.push_back(v);
                                    QGhostVtx.push_back(w);
                                    QMsgType.push_back(FAILURE);
                                    //ghostOwner = inputSubGraph.findOwner(w);
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    QOwner.push_back(ghostOwner);
                                    PCounter[ghostOwner]++;
                                    NumMessagesBundled++;
                                    msgInd++;
                                } //End of if(GHOST)
                            } //End of for loop
                        } // End of Else: w == -1
                        //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                    } //End of If (candidateMate[v-StartIndex] == u)
                } //End of if ( (v >= StartIndex) && (v <= EndIndex) ) //If Local Vertex:
                else { //Neighbor is a ghost vertex
                    if ( candidateMate[NLVer+Ghost2LocalMap[v]] == u )
                        candidateMate[NLVer+Ghost2LocalMap[v]] = -1;
                    if ( v != Mate[u-StartIndex] ) { //u is local
                        //Build the Message Packet:
                        //Message[0] = u; //LOCAL
                        //Message[1] = v; //GHOST
                        //Message[2] = SUCCESS;  //TYPE
                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")Sending a success message: ";
                        cout<<"\n("<<myRank<<")Ghost is "<<v<<" Owner is: "<<findOwnerOfGhost(v, verDistance, myRank, numProcs)<<"\n"; fflush(stdout);
#endif
                        /* MPI_Bsend(&Message[0], 3, MPI_INT, inputSubGraph.findOwner(v),
                         ComputeTag, comm); */
                        QLocalVtx.push_back(u);
                        QGhostVtx.push_back(v);
                        QMsgType.push_back(SUCCESS);
                        //ghostOwner = inputSubGraph.findOwner(v);
                        ghostOwner = findOwnerOfGhost(v, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                        QOwner.push_back(ghostOwner);
                        PCounter[ghostOwner]++;
                        NumMessagesBundled++;
                        msgInd++;
                    } //End of If( v != Mate[u] )
                } //End of Else //A Ghost Vertex
            } //End of For Loop adj(u)
        } //End of if ( (u >= StartIndex) && (u <= EndIndex) ) //Process Only If a Local Vertex
    } //End of while ( /*!Q.empty()*/ !U.empty() )
    ///////////////////////// END OF PROCESS MATCHED VERTICES /////////////////////////
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Send Bundles" <<endl; fflush(stdout);
#endif
    /////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////// SEND BUNDLED MESSAGES /////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Data structures for Bundled Messages:
    vector<MilanLongInt> PCumulative, PMessageBundle, PSizeInfoMessages;
    MilanLongInt myIndex=0;
    try {
        PMessageBundle.reserve(NumMessagesBundled*3); //Three integers per message
        PCumulative.reserve(numProcs+1); //Similar to Row Pointer vector in CSR data structure
        PSizeInfoMessages.reserve(numProcs*3); //Buffer to hold the Size info message packets
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    PMessageBundle.resize(NumMessagesBundled*3, -1);//Initialize
    PCumulative.resize(numProcs+1, 0); //Only initialize the counter variable
    PSizeInfoMessages.resize(numProcs*3, 0);

    for (MilanInt i=0; i<numProcs; i++) // Changed by Fabio to be an integer, addresses needs to be integers!
        PCumulative[i+1]=PCumulative[i]+PCounter[i];
    //Reuse PCounter to keep track of how many messages were inserted:
    for (MilanInt i=0; i<numProcs; i++) // Changed by Fabio to be an integer, addresses needs to be integers!
        PCounter[i]=0;
    //Build the Message Bundle packet:
    for (MilanInt i=0; i<NumMessagesBundled; i++) { // Changed by Fabio to be an integer, addresses needs to be integers!
        myIndex = ( PCumulative[QOwner[i]] + PCounter[QOwner[i]] )*3;
        PMessageBundle[myIndex+0] = QLocalVtx[i];
        PMessageBundle[myIndex+1] = QGhostVtx[i];
        PMessageBundle[myIndex+2] = QMsgType[i];
        PCounter[QOwner[i]]++;
    }
    //Send the Bundled Messages: Use ISend
    vector<MPI_Request> SRequest; //Requests that are used for each send message
    vector<MPI_Status> SStatus;   //Status of sent messages, used in MPI_Wait
    MilanLongInt MessageIndex=0; //Pointer for current message
    try {
        SRequest.reserve(numProcs*2); //At most two messages per processor
        SStatus.reserve(numProcs*2);//At most two messages per processor
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearchImmediateSend: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    MPI_Request myReq; //A sample request
    SRequest.resize(numProcs*2,myReq);
    MPI_Status myStat; //A sample status
    SStatus.resize(numProcs*2,myStat);
    //Send the Messages
    for (MilanInt i=0; i<numProcs; i++) { // Changed by Fabio to be an integer, addresses needs to be integers!
        if (i==myRank) //Do not send anything to yourself
            continue;
        //Send the Message with information about the size of next message:
        //Build the Message Packet:
        PSizeInfoMessages[i*3+0] = (PCumulative[i+1]-PCumulative[i])*3; // # of integers in the next message
        PSizeInfoMessages[i*3+1] = -1; //Dummy packet
        PSizeInfoMessages[i*3+2] = SIZEINFO;  //TYPE
        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Sending bundled message to process "<<i<<" size: "<<PSizeInfoMessages[i*3+0]<<endl;
        fflush(stdout);
#endif
        if ( PSizeInfoMessages[i*3+0] > 0 ) { //Send only if it is a nonempty packet
            MPI_Isend(&PSizeInfoMessages[i*3+0], 3, TypeMap<MilanLongInt>(), i, ComputeTag, comm, &SRequest[MessageIndex]);
            msgActual++;
            MessageIndex++;
            //Now Send the message with the data packet:
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Sending Bundle to : "<<i<<endl;
            for (k=(PCumulative[i]*3); k< (PCumulative[i]*3+PSizeInfoMessages[i*3+0]); k++)
                cout<<PMessageBundle[k]<<",";
            cout<<endl;
            fflush(stdout);
#endif
            MPI_Isend(&PMessageBundle[PCumulative[i]*3], PSizeInfoMessages[i*3+0], TypeMap<MilanLongInt>(), i, BundleTag, comm, &SRequest[MessageIndex]);
            MessageIndex++;
        } //End of if size > 0
    }
    //Free up temporary memory:
    PCumulative.clear();
    QLocalVtx.clear();
    QGhostVtx.clear();
    QMsgType.clear();
    QOwner.clear();
    PCounter.clear();
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<")Total number of potential message X 2 = "<<numGhostEdges*2;
    cout<<"\n("<<myRank<<")Number messages already sent in bundles = "<<NumMessagesBundled;
    if (numGhostEdges>0) {
      cout<<"\n("<<myRank<<")Percentage of total = "<<((double)NumMessagesBundled/(double)(numGhostEdges*2))*100.0<<"% \n";
    }
    fflush(stdout);
#endif

    //Allocate memory for MPI Send messages:
    /* WILL COME BACK HERE - NO NEED TO STORE ALL THIS MEMORY !! */
    MilanInt OneMessageSize=0;
    MPI_Pack_size(3, TypeMap<MilanLongInt>(), comm, &OneMessageSize); //Size of one message packet
    //How many messages to send?
    //Potentially three kinds of messages will be sent/received:
    //Request, Success, Failure.
    //But only two will be sent from a given processor.
    //Substract the number of messages that have already been sent as bundled messages:
    MilanLongInt numMessagesToSend = numGhostEdges*2 - NumMessagesBundled;
    MilanInt     BufferSize = (OneMessageSize+MPI_BSEND_OVERHEAD)*numMessagesToSend;

    MilanLongInt *Buffer=0;
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Size of One Message from PACK= "<<OneMessageSize;
    cout<<"\n("<<myRank<<")Size of Message overhead = "<<MPI_BSEND_OVERHEAD;
    cout<<"\n("<<myRank<<")Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<")Number of remaining message = "<<numMessagesToSend;
    cout<<"\n("<<myRank<<")BufferSize = "<<BufferSize;
    cout<<"\n("<<myRank<<")Attaching Buffer on.. ";
    fflush(stdout);
#endif
    if ( BufferSize > 0 ) {
        Buffer = (MilanLongInt *) malloc(BufferSize);  //Allocate memory
        if ( Buffer == 0 ) {
            cout<<"Error in function algoDistEdgeApproxDominatingEdgesLinearSearch: \n";
            cout<<"Not enough memory to allocate for send buffer on process "<<myRank<<"\n";
            exit(1);
        }
        MPI_Buffer_attach(Buffer, BufferSize); //Attach the Buffer
    }
    ///////////////////////// END OF SEND BUNDLED MESSAGES //////////////////////////////////

    finishTime = MPI_Wtime();
    *ph1_time = finishTime-startTime; //Time taken for Phase-1
    *ph1_card = myCard ; //Cardinality at the end of Phase-1
    startTime = MPI_Wtime();
    /////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////// MAIN LOOP //////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    //Main While Loop:
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")Entering While(true) loop.."; fflush(stdout);
    //U.display(); fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
    fflush(stdout);
#endif
    //Buffer to receive bundled messages
    //Maximum messages that can be received from any processor is
    //twice the edge cut: REQUEST; REQUEST+(FAILURE/SUCCESS)
    vector<MilanLongInt> ReceiveBuffer;
    MilanLongInt bundleSize=0, bundleCounter=0;
    try {
        ReceiveBuffer.reserve(numGhostEdges*2*3); //Three integers per cross edge
    } catch ( length_error ) {
        cout<<"Error in function algoDistEdgeApproxDominatingEdgesMessageBundling: \n";
        cout<<"Not enough memory to allocate the internal variables \n";
        exit(1);
    }
    while ( true ) {
#ifdef DEBUG_HANG_
      if (myRank == 0) cout<<"\n("<<myRank<<") Main loop" <<endl; fflush(stdout);
#endif
        ///////////////////////////////////////////////////////////////////////////////////
        /////////////////////////// PROCESS MATCHED VERTICES //////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////
        while ( /*!Q.empty()*/ !U.empty() ) {
            //Q.pop_front();
            u = U.pop_front(); //Get an element from the queue
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")u: "<<u; fflush(stdout);
#endif
            if ( (u >= StartIndex) && (u <= EndIndex) ) { //Process Only If a Local Vertex
                //Get the Adjacency list for u
                adj1 = verLocPtr[u-StartIndex];  //Pointer
                adj2 = verLocPtr[u-StartIndex+1];
                for( k = adj1; k < adj2; k++ ) {
                    v = verLocInd[k];
                    if ( (v >= StartIndex) && (v <= EndIndex) ) { //v is a Local Vertex:
                        if ( Mate[v-StartIndex] >= 0 )   // v is already matched
                            continue;
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")v: "<<v<<" c(v)= "<<candidateMate[v-StartIndex]<<" Mate[v]: "<<Mate[v];
                        fflush(stdout);
#endif
                        if ( candidateMate[v-StartIndex] == u ) { //Only if pointing to the matched vertex
                            //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                            //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            w = -1;
                            heaviestEdgeWt = MilanFloatMin; //Assign the smallest Value possible first LDBL_MIN
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                    if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                        continue;
                                }
                                else { //A local vertex
                                    if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                        continue;
                                }

                                if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                                   ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                    heaviestEdgeWt = edgeLocWeight[k1];
                                    w = verLocInd[k1];
                                }
                            } //End of for loop
                            candidateMate[v-StartIndex] = w;
                            //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w; fflush(stdout);
#endif
                            //If found a dominating edge:
                            if ( w >= 0 ) {
                                if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost
                                    //Build the Message Packet:
                                    Message[0] = v; //LOCAL
                                    Message[1] = w; //GHOST
                                    Message[2] = REQUEST;  //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a request message:";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                    fflush(stdout);
#endif
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                    msgInd++; msgActual++;
                                    if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        GMate[Ghost2LocalMap[w]] = v; //w is ghost
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                        //Decrement the counter:
                                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                        if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                            Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                            if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                                fflush(stdout);
#endif
                                            }
                                        } //End of if Counter[w] > 0
                                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    } //End of if CandidateMate[w] = v
                                } //End of if a Ghost Vertex
                                else { //w is a local vertex
                                    if ( candidateMate[w-StartIndex] == v )  {
                                        Mate[v-StartIndex] = w; //v is local
                                        Mate[w-StartIndex] = v; //w is local
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "; fflush(stdout);
#endif
                                    } //End of if(CandidateMate(w) = v
                                } //End of Else
                            } //End of if(w >=0)
                            else { //no dominating edge found: w == -1
                                adj11 = verLocPtr[v-StartIndex];
                                adj12 = verLocPtr[v-StartIndex+1];
                                for( k1 = adj11; k1 < adj12; k1++ ) {
                                    w = verLocInd[k1];
                                    if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                        //Build the Message Packet:
                                        Message[0] = v;			 //LOCAL
                                        Message[1] = w;            //GHOST
                                        Message[2] = FAILURE;      //TYPE
                                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")Sending a failure message: ";
                                        cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs);
                                        fflush(stdout);
#endif
                                        ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                        MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                        msgInd++; msgActual++;
                                    } //End of if(GHOST)
                                } //End of for loop
                            } // End of Else: w == -1
                            //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        } //End of If (candidateMate[v-StartIndex] == u)
                    } //End of if ( (v >= StartIndex) && (v <= EndIndex) ) //If Local Vertex:
                    else { //Neighbor v is a ghost vertex
                        if ( candidateMate[NLVer+Ghost2LocalMap[v]] == u )
                            candidateMate[NLVer+Ghost2LocalMap[v]] = -1;
                        if ( v != Mate[u-StartIndex] ) { //u is a local vertex
                            //Build the Message Packet:
                            Message[0] = u; //LOCAL
                            Message[1] = v; //GHOST
                            Message[2] = SUCCESS;  //TYPE
                            //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Sending a success message: ";
                            cout<<"\n("<<myRank<<")Ghost is "<<v<<" Owner is: "<<findOwnerOfGhost(v, verDistance, myRank, numProcs);
                            fflush(stdout);
#endif
                            ghostOwner = findOwnerOfGhost(v, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                            MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                            msgInd++; msgActual++;
#ifdef DEBUG_GHOST_
			    if ((u<StartIndex) || (u>EndIndex)) {
			      cout<<"\n("<<myRank<<") "<<__LINE__<<" From Send: should not happen: u= "<<u<<" v= "<<v<<
				" StartIndex "<<StartIndex<<" EndIndex "<<EndIndex<<endl;
			      fflush(stdout);
			    }
#endif

                        } //End of If( v != Mate[u] )
                    } //End of Else //A Ghost Vertex
                } //End of For Loop adj(u)
            } //End of if ( (u >= StartIndex) && (u <= EndIndex) ) //Process Only If a Local Vertex
        } //End of while ( /*!Q.empty()*/ !U.empty() )
        ///////////////////////// END OF PROCESS MATCHED VERTICES /////////////////////////

        //// BREAK IF NO MESSAGES EXPECTED /////////
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Deciding whether to break: S= "<<S<<endl;
#endif

        if ( S == 0 ) {
#ifdef DEBUG_HANG_
	  cout<<"\n("<<myRank<<") Breaking out" <<endl; fflush(stdout);
#endif
            break;
	}
        ///////////////////////////////////////////////////////////////////////////////////
        /////////////////////////// PROCESS MESSAGES //////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////
        /*
         RECEIVE message ( u, v, message_type );
         // u is a GHOST vertex ... v is a LOCAL vertex
         */
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
        fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")About to begin Message processing phase ... S="<<S<<endl;
        fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<"=========================************==============================="<<endl; fflush(stdout);
        fflush(stdout);
#endif
        //BLOCKING RECEIVE:
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<" Waiting for blocking receive..."<<endl; fflush(stdout);
        fflush(stdout);
#endif
        error_codeC = MPI_Recv(&Message[0], 3, TypeMap<MilanLongInt>(), MPI_ANY_SOURCE, ComputeTag, comm, &computeStatus);
        if (error_codeC != MPI_SUCCESS ) {
            MPI_Error_string(error_codeC, error_message, &message_length);
            cout<<"\n*Error in call to MPI_Receive on Slave: "<<error_message<<"\n"; fflush(stdout);
        }
        Sender = computeStatus.MPI_SOURCE;
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Received message from Process "<<Sender<<" Type= "<<Message[2]<<endl;
        fflush(stdout);
#endif
        //If the Message Type is a size indicator, then receive the bigger message.
        if ( Message[2] == SIZEINFO ) {
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Received bundled message from Process "<<Sender<<" Size= "<<Message[0]<<endl;
            fflush(stdout);
#endif
            bundleSize = Message[0]; //#of integers in the message
            //Build the Message Buffer:
            if (!ReceiveBuffer.empty())
                ReceiveBuffer.clear(); //Empty it out first
            ReceiveBuffer.resize(bundleSize, -1); //Initialize
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Message Bundle Before: "<<endl;
            for (i=0; i<bundleSize; i++)
                cout<<ReceiveBuffer[i]<<",";
            cout<<endl;
            fflush(stdout);
#endif
            //Receive the message
            error_codeC = MPI_Recv(&ReceiveBuffer[0], bundleSize, TypeMap<MilanLongInt>(), Sender, BundleTag, comm, &computeStatus);
            if (error_codeC != MPI_SUCCESS ) {
                MPI_Error_string(error_codeC, error_message, &message_length);
                cout<<"\n*Error in call to MPI_Receive on processor "<<myRank<<" Error: "<<error_message<<"\n"; fflush(stdout);
            }
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Message Bundle After: "<<endl;
            for (i=0; i<bundleSize; i++)
                cout<<ReceiveBuffer[i]<<",";
            cout<<endl;
            fflush(stdout);
#endif
        }
        else { //Just a single message:
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Received regular message from Process "<<Sender<<" u= "<<Message[0]<<" v= "<<Message[1]<<endl;
            fflush(stdout);
#endif
            //Add the current message to Queue:
            bundleSize = 3; //#of integers in the message
            //Build the Message Buffer:
            if (!ReceiveBuffer.empty())
                ReceiveBuffer.clear(); //Empty it out first
            ReceiveBuffer.resize(bundleSize, -1); //Initialize

            ReceiveBuffer[0]=Message[0]; //u
            ReceiveBuffer[1]=Message[1]; //v
            ReceiveBuffer[2]=Message[2]; //message_type
        }
        bundleCounter = 0;
        while ( bundleCounter < bundleSize ) {
            u = ReceiveBuffer[bundleCounter]; //GHOST
            bundleCounter++;
            v = ReceiveBuffer[bundleCounter]; //LOCAL
            bundleCounter++;
            message_type = ReceiveBuffer[bundleCounter]; //TYPE
            bundleCounter++;
#ifdef DEBUG_GHOST_
	    if ((v<StartIndex) || (v>EndIndex)) {
	      cout<<"\n("<<myRank<<") From ReceiveBuffer: This should not happen: u= "<<u<<" v= "<<v<<" Type= "<<message_type<<
		" StartIndex "<<StartIndex<<" EndIndex "<<EndIndex<<endl;
	      fflush(stdout);
	    }
#endif
#ifdef PRINT_DEBUG_INFO_
            cout<<"\n("<<myRank<<")Processing message: u= "<<u<<" v= "<<v<<" Type= "<<message_type<<endl;
            fflush(stdout);
#endif
            // CASE I: REQUEST
            if ( message_type == REQUEST ) {
#ifdef PRINT_DEBUG_INFO_
                cout<<"\n("<<myRank<<")Message type is REQUEST"<<endl; fflush(stdout);
#endif
#ifdef DEBUG_GHOST_
		if ((v<0)||(v<StartIndex) || ((v-StartIndex)>NLVer)) {
		  cout<<"\n("<<myRank<<") case 1 Bad address "<<v<<" "<<StartIndex<<" "<<v-StartIndex<<" "<<NLVer<<endl; fflush(stdout);
		}

#endif
                if ( Mate[v-StartIndex] == -1 ) { //Process only if not already matched  (v is local)
                    candidateMate[NLVer+Ghost2LocalMap[u]] = v;  //Set CandidateMate for the ghost
                    if ( candidateMate[v-StartIndex] == u ) {
                        GMate[Ghost2LocalMap[u]] = v; //u is ghost
                        Mate[v-StartIndex] = u; //v is local
                        //Q.push_back(u);
                        U.push_back(v);
                        U.push_back(u);
                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<u<<") "<<endl; fflush(stdout);
#endif
                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                        if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                            Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                            if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages"<<endl;
                                fflush(stdout);
#endif
                            }
                        } //End of if Counter[w] > 0
                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    } //End of if ( candidateMate[v-StartIndex] == u )e
                } //End of if ( Mate[v] == -1 )
            } //End of REQUEST
            else {   //CASE II: SUCCESS
                if ( message_type == SUCCESS ) {
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Message type is SUCCESS"<<endl; fflush(stdout);
#endif
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    GMate[Ghost2LocalMap[u]] = EndIndex+1; //Set a Dummy Mate to make sure that we do not (u is a ghost)
                    //process it again
                    if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                        Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
#ifdef DEBUG_GHOST_
		    if ((v<0)||(v<StartIndex) || ((v-StartIndex)>NLVer)) {
		      cout<<"\n("<<myRank<<") case 2  Bad address "<<v<<" "<<StartIndex<<" "<<v-StartIndex<<" "<<NLVer<<endl; fflush(stdout);
		    }
#endif
                    if ( Mate[v-StartIndex] == -1 ) { //Process only if not already matched ( v is local)
                        if ( candidateMate[v-StartIndex] == u ) {
                            //Start: PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                            //Start: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
                            adj11 = verLocPtr[v-StartIndex];
                            adj12 = verLocPtr[v-StartIndex+1];
                            w = -1;
                            heaviestEdgeWt = MilanFloatMin; //Assign the smallest Value possible first LDBL_MIN
                            for( k1 = adj11; k1 < adj12; k1++ ) {
                                if ( (verLocInd[k1]<StartIndex) || (verLocInd[k1]>EndIndex) ) { //Is it a ghost vertex?
                                    if(GMate[Ghost2LocalMap[verLocInd[k1]]] >= 0 )// Already matched
                                        continue;
                                }
                                else { //A local vertex
                                    if( Mate[verLocInd[k1]-StartIndex] >= 0 ) // Already matched
                                        continue;
                                }

                                if( (edgeLocWeight[k1] > heaviestEdgeWt) ||
                                   ((edgeLocWeight[k1] == heaviestEdgeWt)&&(w < verLocInd[k1])) ) {
                                    heaviestEdgeWt = edgeLocWeight[k1];
                                    w = verLocInd[k1];
                                }
                            } //End of for loop
                            candidateMate[v-StartIndex] = w;
                            //End: PARALLEL_COMPUTE_CANDIDATE_MATE_B(v)
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")"<<v<<" Points to: "<<w<<endl; fflush(stdout);
#endif
                            //If found a dominating edge:
                            if ( w >= 0 ) {
                                if ( (w < StartIndex) || (w > EndIndex) ) { //w is a ghost
                                    //Build the Message Packet:
                                    Message[0] = v; //LOCAL
                                    Message[1] = w; //GHOST
                                    Message[2] = REQUEST;  //TYPE
                                    //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                    cout<<"\n("<<myRank<<")Sending a request message: ";
                                    cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs)<<endl;
                                    fflush(stdout);
#endif
                                    ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                    MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                    msgInd++; msgActual++;
                                    if ( candidateMate[NLVer+Ghost2LocalMap[w]] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        GMate[Ghost2LocalMap[w]] = v; //w is ghost
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "<<endl; fflush(stdout);
#endif
                                        //Decrement the counter:
                                        //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                        if ( Counter[Ghost2LocalMap[w]] > 0 ) {
                                            Counter[Ghost2LocalMap[w]] = Counter[Ghost2LocalMap[w]] - 1; //Decrement
                                            if ( Counter[Ghost2LocalMap[w]] == 0 ) {
                                                S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                                                cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<w<<" has received all its messages";
                                                fflush(stdout);
#endif
                                            }
                                        } //End of if Counter[w] > 0
                                        //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,w)
                                    } //End of if CandidateMate[w] = v
                                } //End of if a Ghost Vertex
                                else { //w is a local vertex
                                    if ( candidateMate[w-StartIndex] == v ) {
                                        Mate[v-StartIndex] = w; //v is local
                                        Mate[w-StartIndex] = v; //w is local
                                        //Q.push_back(u);
                                        U.push_back(v);
                                        U.push_back(w);
                                        myCard++;
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")MATCH: ("<<v<<","<<w<<") "<<endl; fflush(stdout);
#endif
                                    } //End of if(CandidateMate(w) = v
                                } //End of Else
                            } //End of if(w >=0)
                            else { //No dominant edge found
                                adj11 = verLocPtr[v-StartIndex];
                                adj12 = verLocPtr[v-StartIndex+1];
                                for( k1 = adj11; k1 < adj12; k1++ ) {
                                    w = verLocInd[k1];
                                    if ( (w < StartIndex) || (w > EndIndex) ) { //A ghost
                                        //Build the Message Packet:
                                        Message[0] = v;			 //LOCAL
                                        Message[1] = w;            //GHOST
                                        Message[2] = FAILURE;      //TYPE
                                        //Send a Request (Asynchronous)
#ifdef PRINT_DEBUG_INFO_
                                        cout<<"\n("<<myRank<<")Sending a failure message: ";
                                        cout<<"\n("<<myRank<<")Ghost is "<<w<<" Owner is: "<<findOwnerOfGhost(w, verDistance, myRank, numProcs)<<endl;
                                        fflush(stdout);
#endif
                                        //MPI_Bsend(&Message[0], 3, MilanMpiLongInt, findOwnerOfGhost(w, verDistance, myRank, numProcs),
                                        ghostOwner = findOwnerOfGhost(w, verDistance, myRank, numProcs); assert(ghostOwner != -1); assert(ghostOwner != myRank);
                                        MPI_Bsend(&Message[0], 3, TypeMap<MilanLongInt>(), ghostOwner, ComputeTag, comm);
                                        msgInd++; msgActual++;
                                    } //End of if(GHOST)
                                } //End of for loop
                            } // End of Else: w == -1
                            //End:   PARALLEL_PROCESS_EXPOSED_VERTEX_B(v)
                        } //End of if ( candidateMate[v-StartIndex] == u )
                    } //End of if ( Mate[v] == -1 )
                } //End of if ( message_type == SUCCESS )
                else { //CASE III: FAILURE
#ifdef PRINT_DEBUG_INFO_
                    cout<<"\n("<<myRank<<")Message type is FAILURE"<<endl; fflush(stdout);
#endif
                    //Start: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                    GMate[Ghost2LocalMap[u]] = EndIndex+1; //Set a Dummy Mate to make sure that we do not (u is a ghost)
                    //process it again
                    if ( Counter[Ghost2LocalMap[u]] > 0 ) {
                        Counter[Ghost2LocalMap[u]] = Counter[Ghost2LocalMap[u]] - 1; //Decrement
                        if ( Counter[Ghost2LocalMap[u]] == 0 ) {
                            S--; //Decrement S
#ifdef PRINT_DEBUG_INFO_
                            cout<<"\n("<<myRank<<")Decrementing S: Ghost vertex "<<u<<" has received all its messages";
                            fflush(stdout);
#endif
                        }
                    } //End of if Counter[w] > 0
                    //End: PARALLEL_PROCESS_CROSS_EDGE_B(v,u)
                } //End of else: CASE III
            } //End of else: CASE I
        } //End of if (!MsgQ.empty())
        ///////////////////////// END OF PROCESS MESSAGES /////////////////////////////////
#ifdef PRINT_DEBUG_INFO_
        cout<<"\n("<<myRank<<")Finished Message processing phase: S= "<<S; fflush(stdout);
        cout<<"\n("<<myRank<<")** SENT     : ACTUAL= "<<msgActual; fflush(stdout);
        cout<<"\n("<<myRank<<")** SENT     : INDIVIDUAL= "<<msgInd<<endl; fflush(stdout);
#endif
    } //End of while (true)

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Waitall= "<<endl; fflush(stdout);
#endif
#ifdef DEBUG_HANG_
    cout<<"\n("<<myRank<<") Waitall " <<endl; fflush(stdout);
#endif
    //MPI_Barrier(comm);
    //Cleanup Phase
    MPI_Waitall(MessageIndex, &SRequest[0], &SStatus[0]);
    //MPI_Buffer_attach(&Buffer, BufferSize); //Attach the Buffer
    if ( BufferSize > 0 ) {
      MPI_Buffer_detach(&Buffer, &BufferSize); //Detach the Buffer
      free(Buffer); //Free the memory that was allocated
    }
    finishTime = MPI_Wtime();
    *ph2_time = finishTime-startTime; //Time taken for Phase-2
    *ph2_card = myCard ; //Cardinality at the end of Phase-2

#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<")End of function to compute matching: "<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")myCardinality: "<<myCard<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")Matching took "<<finishTime-startTime<<"seconds"<<endl; fflush(stdout);
    cout<<"\n("<<myRank<<")** Getting out of the matching function **"<<endl; fflush(stdout);
#endif
#ifdef PRINT_DEBUG_INFO_
    cout<<"\n("<<myRank<<") Number of Ghost edges = "<<numGhostEdges;
    cout<<"\n("<<myRank<<") Total number of potential message X 2 = "<<numGhostEdges*2;
    cout<<"\n("<<myRank<<") Number messages bundled = "<<NumMessagesBundled;
    cout<<"\n("<<myRank<<") Total Individual Messages sent = "<< msgInd;
    if (msgInd>0) {
      cout<<"\n("<<myRank<<") Percentage of messages bundled = "<<((double)NumMessagesBundled/(double)(msgInd))*100.0<<"% \n";
    }
    fflush(stdout);
#endif

    *msgActualSent = msgActual;
    *msgIndSent = msgInd;
    if (msgInd > 0) {
      *msgPercent = ((double)NumMessagesBundled/(double)(msgInd))*100.0;
    } else {
      *msgPercent = 0;
    }
#ifdef DEBUG_HANG_
    if (myRank == 0) cout<<"\n("<<myRank<<") Done" <<endl; fflush(stdout);
#endif
    //MPI_Barrier(comm);
#endif
} //End of algoDistEdgeApproxDomEdgesLinearSearchMesgBndlSmallMate
#endif

///Find the owner of a ghost node:
inline MilanInt findOwnerOfGhost(MilanLongInt vtxIndex, MilanLongInt *mVerDistance,
                                     MilanInt myRank, MilanInt numProcs) {
  //MilanLongInt Size = mVerDistance.size();
  MilanLongInt mStartInd = mVerDistance[myRank];
  MilanInt Start = 0;
  MilanInt End = numProcs;
  MilanInt Current = 0;

#if 0
  if ( vtxIndex < mStartInd )
    End = myRank;
  else
    Start = myRank;
#endif

  while ( Start <= End ) {
    Current = (End + Start)/2;
    //CASE-1:
    if ( mVerDistance[Current] == vtxIndex ) {
      while ( mVerDistance[Current+1] == vtxIndex ) {
	Current++;
	if ( Current == numProcs )
	  return (-1);
      }
      return (Current);
    }
    else { //CASE 2:
      if ( mVerDistance[Current] > vtxIndex )
	End = Current - 1;
      else //CASE 3:
	Start = Current + 1;
    }
  } //End of While()
  if ( Current == 0 )
    return (Current);
  else {
    if ( mVerDistance[Current] > vtxIndex )
      return (Current-1);
    else
      return (Current);
  } //End of else
  return (-1); //It should not reach here!
} //End of findOwnerOfGhost()
#endif
