/* Copyright 2007,2008,2018,2023 IPB, Universite de Bordeaux, INRIA & CNRS
**
** This file is part of the Scotch software package for static mapping,
** graph partitioning and sparse matrix ordering.
**
** This software is governed by the CeCILL-C license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
**
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
**
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
**
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-C license and that you accept its terms.
*/
/************************************************************/
/**                                                        **/
/**   NAME       : dgraph_build_hcub.c                     **/
/**                                                        **/
/**   AUTHOR     : Francois PELLEGRINI                     **/
/**                Cedric CHEVALIER (v5.0)                 **/
/**                                                        **/
/**   FUNCTION   : These lines are the distributed source  **/
/**                graph building routines for hypercube   **/
/**                graphs.                                 **/
/**                                                        **/
/**   DATES      : # Version P0.1 : from : 19 may 1999     **/
/**                                 to   : 19 may 1999     **/
/**                # Version P0.2 : from : 02 feb 2000     **/
/**                                 to   : 02 feb 2000     **/
/**                # Version 5.0  : from : 20 jul 2005     **/
/**                                 to   : 10 sep 2007     **/
/**                # Version 5.1  : from : 12 nov 2008     **/
/**                                 to   : 12 nov 2008     **/
/**                # Version 6.0  : from : 15 may 2018     **/
/**                                 to   : 15 may 2018     **/
/**                # Version 7.0  : from : 17 jan 2023     **/
/**                                 to   : 17 jan 2023     **/
/**                                                        **/
/************************************************************/

/*
** The defines and includes.
*/

#include "module.h"
#include "common.h"
#include "dgraph.h"

/*******************************************/
/*                                         */
/* The distributed graph building routine. */
/*                                         */
/*******************************************/

/* This routine builds a distributed hypercube of
** given dimension.
** Since this routine calls dgraphBuild, the private
** data of the Dgraph structure will be initialized
** by the latter, if needed.
** It returns:
** - 0   : on success.
** - !0  : on error.
*/

int
dgraphBuildHcub (
Dgraph * restrict const     grafptr,              /* Graph               */
const Gnum                  hcubdim,              /* Hypercube dimension */
const Gnum                  baseval,              /* Base value          */
const Gnum                  flagval)              /* Flags               */
{
  Gnum                procngbnum;
  Gnum                vertglbnbr;                 /* Total number of vertices        */
  Gnum                vertglbnum;                 /* Global number of current vertex */
  Gnum                vertlocnbr;                 /* Number of local vertices        */
  Gnum                velolocnbr;                 /* Number of local vertex loads    */
  Gnum                vertlocnnd;
  Gnum                vertlocnum;
  Gnum * restrict     vertloctax;
  Gnum * restrict     veloloctax;
#ifdef SCOTCH_DEBUG_DGRAPH3
  Gnum * restrict     vlblloctax;
#endif /* SCOTCH_DEBUG_DGRAPH3 */
  Gnum                edgelocnbr;
  Gnum * restrict     edgeloctax;
  Gnum                edgelocnum;
  Gnum                edlolocnbr;
  Gnum * restrict     edloloctax;
  int                 cheklocval;
  Gnum                reduloctab[7];
  Gnum                reduglbtab[7];

  vertglbnbr = 1 << hcubdim;
  vertlocnbr = DATASIZE (vertglbnbr, grafptr->procglbnbr, grafptr->proclocnum);
  velolocnbr = ((flagval & 1) != 0) ? vertlocnbr : 0;
  edgelocnbr = vertlocnbr * hcubdim;              /* Set local number of arcs */
  edlolocnbr = ((flagval & 2) != 0) ? edgelocnbr : 0;

  for (procngbnum = 0, vertglbnum = 0;            /* Compute index of first local vertex */
       procngbnum < grafptr->proclocnum; procngbnum ++)
    vertglbnum += DATASIZE (vertglbnbr, grafptr->procglbnbr, procngbnum);

  cheklocval = 0;
  vertloctax =
  edgeloctax = NULL;
  if (memAllocGroup ((void **) (void *)
                     &vertloctax, (size_t) ((vertlocnbr + 1) * sizeof (Gnum)), /* Compact vertex array */
#ifdef SCOTCH_DEBUG_DGRAPH3
                     &vlblloctax, (size_t) (vertlocnbr       * sizeof (Gnum)),
#endif /* SCOTCH_DEBUG_DGRAPH3 */
                     &veloloctax, (size_t) (velolocnbr       * sizeof (Gnum)), NULL) == NULL) {
    errorPrint ("dgraphBuildHcub: out of memory (1)");
    cheklocval = 1;
  }
  else if (memAllocGroup ((void **) (void *)
                          &edgeloctax, (size_t) (edgelocnbr * sizeof (Gnum)),
                          &edloloctax, (size_t) (edlolocnbr * sizeof (Gnum)), NULL) == NULL) {
    errorPrint ("dgraphBuildHcub: out of memory (2)");
    cheklocval = 1;
  }
  reduloctab[0] =   hcubdim;
  reduloctab[1] = - hcubdim;
  reduloctab[2] =   baseval;
  reduloctab[3] = - baseval;
  reduloctab[4] =   flagval;
  reduloctab[5] = - flagval;
  reduloctab[6] =   cheklocval;

  if (MPI_Allreduce (reduloctab, reduglbtab, 7, GNUM_MPI, MPI_MAX, grafptr->proccomm) != MPI_SUCCESS) {
    errorPrint ("dgraphBuildHcub: communication error");
    return     (1);
  }
  if (reduglbtab[6] != 0) {
    if (vertloctax != NULL) {
      if (edgeloctax != NULL)
        memFree (edgeloctax);
      memFree (vertloctax);
    }
    return (1);
  }
  if ((reduglbtab[1] != - reduglbtab[0]) ||
      (reduglbtab[3] != - reduglbtab[2]) ||
      (reduglbtab[5] != - reduglbtab[4])) {
    errorPrint ("dgraphBuildHcub: inconsistent parameters");
    return     (1);
  }
  vertloctax -= baseval;
  veloloctax  = ((flagval & 1) != 0) ? (veloloctax - baseval) : NULL;
  edgeloctax -= baseval;
  edloloctax  = ((flagval & 2) != 0) ? (edloloctax - baseval) : NULL;
#ifdef SCOTCH_DEBUG_DGRAPH3
  vlblloctax -= baseval;
#endif /* SCOTCH_DEBUG_DGRAPH3 */

  for (vertlocnum = baseval, vertlocnnd = vertlocnbr + baseval, edgelocnum = baseval;
       vertlocnum < vertlocnnd; vertlocnum ++, vertglbnum ++) {
    Gnum                vertngbbit;               /* Bit that differs between neighbors */

    if (veloloctax != NULL)
      veloloctax[vertlocnum] = 1 + (vertglbnum & 3);  /* Pseudo random weight (1 to 5) */
#ifdef SCOTCH_DEBUG_DGRAPH3
    vlblloctax[vertlocnum] = ((vertglbnum * COARHASHPRIME) % vertglbnbr) + baseval; /* Hash vertices to spread labels */
#endif /* SCOTCH_DEBUG_DGRAPH3 */
    vertloctax[vertlocnum] = edgelocnum;

    for (vertngbbit = 1; vertngbbit < vertglbnbr; vertngbbit <<= 1, edgelocnum ++) {
#ifdef SCOTCH_DEBUG_DGRAPH3
      edgeloctax[edgelocnum] = (((vertglbnum ^ vertngbbit) * COARHASHPRIME) % vertglbnbr) + baseval;
#else /* SCOTCH_DEBUG_DGRAPH3 */
      edgeloctax[edgelocnum] = (vertglbnum ^ vertngbbit) + baseval;
#endif /* SCOTCH_DEBUG_DGRAPH3 */
      if (edloloctax != NULL)
        edloloctax[edgelocnum] = ((vertglbnum + edgeloctax[edgelocnum]) % 16) + 1; /* Pseudo random weight (1 to 16) */
    }
  }
  vertloctax[vertlocnum] = edgelocnum;            /* Mark end of local vertex array */

#ifdef SCOTCH_DEBUG_DGRAPH2
  if (edgelocnum != edgelocnbr + baseval) {
    errorPrint ("dgraphBuildHcub: internal error");
    memFree    (vertloctax + baseval);           /* Free memory group leader */
    return     (1);
  }
#endif /* SCOTCH_DEBUG_DGRAPH2 */

  if (dgraphBuild2 (grafptr, baseval,             /* Build the distributed graph */
#ifdef SCOTCH_DEBUG_DGRAPH3
                    vertlocnbr, vertlocnbr, vertloctax, vertloctax + 1, NULL, vertlocnbr, NULL, vlblloctax,
#else /* SCOTCH_DEBUG_DGRAPH3 */
                    vertlocnbr, vertlocnbr, vertloctax, vertloctax + 1, NULL, vertlocnbr, NULL, NULL,
#endif /* SCOTCH_DEBUG_DGRAPH3 */
                    edgelocnbr, edgelocnbr, edgeloctax, NULL, edloloctax, hcubdim) != 0) {
    memFree (edgeloctax + baseval);           /* Free memory group leaders */
    memFree (vertloctax + baseval);
    return  (1);
  }

  grafptr->flagval |= DGRAPHFREETABS | DGRAPHVERTGROUP | DGRAPHEDGEGROUP; /* Arrays created by the routine itself */

  return (0);
}
