int
SCOTCH_graphMapCompute (
SCOTCH_Graph * const        grafptr,              /*+ Graph to order     +*/
SCOTCH_Mapping * const      mappptr,              /*+ Mapping to compute +*/
SCOTCH_Strat * const        stratptr)             /*+ Mapping strategy   +*/
{
  Kgraph                mapgrafdat;               /* Effective mapping graph     */
  const Strat *         mapstratptr;              /* Pointer to mapping strategy */
  LibMapping * restrict lmapptr;
  int                   o;

#ifdef SCOTCH_DEBUG_GRAPH2
  if (graphCheck ((Graph *) grafptr) != 0) {
    errorPrint ("SCOTCH_graphMapCompute: invalid input graph");
    return     (1);
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */

  lmapptr = (LibMapping *) mappptr;
  if (*((Strat **) stratptr) == NULL) {           /* Set default mapping strategy if necessary */
    ArchDom             archdomnorg;

    archDomFrst (&lmapptr->m.archdat, &archdomnorg);
    if (archVar (&lmapptr->m.archdat))
      SCOTCH_stratGraphClusterBuild (stratptr, 0, 1, 0.0, 0.05);
    else
      SCOTCH_stratGraphMapBuild (stratptr, 0, archDomSize (&lmapptr->m.archdat, &archdomnorg), 0.05);
  }

  mapstratptr = *((Strat **) stratptr);
  if (mapstratptr->tabl != &kgraphmapststratab) {
    errorPrint ("SCOTCH_graphMapCompute: not a graph mapping strategy");
    return     (1);
  }

  if (kgraphInit (&mapgrafdat, (Graph *) grafptr, &lmapptr->m) != 0)
    return (1);
  o = kgraphMapSt (&mapgrafdat, mapstratptr);     /* Perform mapping */

  lmapptr->m.domnmax = mapgrafdat.m.domnmax;      /* Do not free the mapping, as it has been cloned */
  lmapptr->m.domnnbr = mapgrafdat.m.domnnbr;
  lmapptr->m.domntab = mapgrafdat.m.domntab;      /* Update pointer to domntab in case it has changed */

  mapgrafdat.m.parttax = NULL;                    /* Prevent mapping arrays from being freed by graph */
  mapgrafdat.m.domntab = NULL;
  kgraphExit (&mapgrafdat);

  if (lmapptr->parttax != NULL) {                 /* Propagate mapping data to user partition array */
    Gnum                vertnum;
    Gnum                vertnnd;

    for (vertnum = lmapptr->m.baseval, vertnnd = vertnum + lmapptr->m.vertnbr;
         vertnum < vertnnd; vertnum ++)
      lmapptr->parttax[vertnum] = archDomNum (&lmapptr->m.archdat, &lmapptr->m.domntab[lmapptr->m.parttax[vertnum]]);
  }

  return (o);
}
int
SCOTCH_graphMapLoad (
const SCOTCH_Graph * const    grafptr,            /*+ Graph to order  +*/
const SCOTCH_Mapping * const  mappptr,            /*+ Mapping to save +*/
FILE * const                  stream)             /*+ Output stream   +*/
{
  LibMapping * restrict lmapptr;
  int                   o;

  lmapptr = (LibMapping *) mappptr;
  if ((o = mapLoad (&lmapptr->m, ((Graph *) grafptr)->vlbltax, stream)) != 0)
    return (o);

  if (lmapptr->parttax != NULL) {                 /* Propagate mapping data to user partition array */
    Gnum                vertnum;
    Gnum                vertnnd;

    for (vertnum = lmapptr->m.baseval, vertnnd = vertnum + lmapptr->m.vertnbr;
         vertnum < vertnnd; vertnum ++)
      lmapptr->parttax[vertnum] = archDomNum (&lmapptr->m.archdat, &lmapptr->m.domntab[lmapptr->m.parttax[vertnum]]);
  }

  return (0);
}
int
SCOTCH_graphMapView (
const SCOTCH_Graph * const    libgrafptr,
const SCOTCH_Mapping * const  libmappptr,
FILE * const                  stream)
{
  const Graph * restrict    grafptr;
  const Mapping * restrict  mappptr;
  Anum * restrict           parttax;              /* Part array                                   */
  MappingSort * restrict    domntab;              /* Pointer to domain sort array                 */
  ArchDom                   domnfrst;             /* Largest domain in architecture               */
  Anum                      tgtnbr;               /* Number of processors in target topology      */
  Anum                      mapnbr;               /* Number of processors effectively used        */
  Anum                      mapnum;
  double                    mapavg;               /* Average mapping weight                       */
  Gnum                      mapmin;
  Gnum                      mapmax;
  Gnum                      mapsum;               /* (Partial) sum of vertex loads                */
  double                    mapdlt;
  double                    mapmmy;               /* Maximum / average ratio                      */
  Anum * restrict           nghbtab;              /* Table storing neighbors of current subdomain */
  Anum                      nghbnbr;
  Anum                      nghbmin;
  Anum                      nghbmax;
  Anum                      nghbsum;
  Gnum                      vertnum;
  Gnum                      veloval;
  Gnum                      edloval;
  Gnum                      commdist[256];        /* Array of load distribution */
  Gnum                      commload;             /* Total edge load (edge sum) */
  Gnum                      commdilat;            /* Total edge dilation        */
  Gnum                      commexpan;            /* Total edge expansion       */
  Anum                      distmax;
  Anum                      distval;
  Gnum                      diammin;
  Gnum                      diammax;
  Gnum                      diamsum;

  const Gnum * restrict const verttax = ((Graph *) libgrafptr)->verttax;
  const Gnum * restrict const vendtax = ((Graph *) libgrafptr)->vendtax;
  const Gnum * restrict const velotax = ((Graph *) libgrafptr)->velotax;
  const Gnum * restrict const edgetax = ((Graph *) libgrafptr)->edgetax;
  const Gnum * restrict const edlotax = ((Graph *) libgrafptr)->edlotax;

  grafptr = (Graph *) libgrafptr;
  mappptr = &((LibMapping *) libmappptr)->m;

  if ((grafptr->vertnbr == 0) ||                  /* Return if nothing to do */
      (grafptr->edgenbr == 0))
    return (0);

  if (memAllocGroup ((void **) (void *)
                     &domntab, (size_t) ((grafptr->vertnbr + 1) * sizeof (MappingSort)),
                     &parttax, (size_t) (grafptr->vertnbr       * sizeof (Anum)),
                     &nghbtab, (size_t) ((grafptr->vertnbr + 2) * sizeof (Anum)), NULL) == NULL) {
    errorPrint ("SCOTCH_graphMapView: out of memory");
    return     (1);
  }

  memSet (parttax, ~0, grafptr->vertnbr * sizeof (Anum));

  for (vertnum = 0; vertnum < grafptr->vertnbr; vertnum ++) {
    parttax[vertnum]      =
    domntab[vertnum].labl = archDomNum (&mappptr->archdat, mapDomain (mappptr, vertnum + grafptr->baseval));
    domntab[vertnum].peri = vertnum + grafptr->baseval; /* Build inverse permutation */
  }
  domntab[grafptr->vertnbr].labl = ARCHDOMNOTTERM; /* TRICK: avoid testing (i+1)   */
  domntab[grafptr->vertnbr].peri = ~0;            /* Prevent Valgrind from yelling */
  parttax -= grafptr->baseval;                    /* From now on, base part array  */

  intSort2asc2 (domntab, grafptr->vertnbr);       /* Sort domain label array by increasing target labels */

  archDomFrst (&mappptr->archdat, &domnfrst);     /* Get architecture domain    */
  tgtnbr = archDomSize (&mappptr->archdat, &domnfrst); /* Get architecture size */

  mapsum  = 0;
  mapnbr  = 0;
  veloval = 1;                                    /* Assume unweighted vertices */
  for (vertnum = 0; domntab[vertnum].labl != ARCHDOMNOTTERM; vertnum ++) {
    parttax[domntab[vertnum].peri] = mapnbr;      /* Build map of partition parts starting from 0  */
    if (domntab[vertnum].labl != domntab[vertnum + 1].labl) /* TRICK: if new (or end) domain label */
      mapnbr ++;
    if (velotax != NULL)
      veloval = velotax[domntab[vertnum].peri];
    mapsum += veloval;
  }
  mapavg = (mapnbr == 0) ? 0.0L : (double) mapsum / (double) mapnbr;

  mapsum = 0;
  mapmin = GNUMMAX;
  mapmax = 0;
  mapdlt = 0.0L;
  for (vertnum = 0; domntab[vertnum].labl != ARCHDOMNOTTERM; vertnum ++) {
    if (velotax != NULL)
      veloval = velotax[domntab[vertnum].peri];
    mapsum += veloval;

    if (domntab[vertnum].labl != domntab[vertnum + 1].labl) { /* TRICK: if new (or end) domain label */
      if (mapsum < mapmin)
        mapmin = mapsum;
      if (mapsum > mapmax)
        mapmax = mapsum;
      mapdlt += fabs ((double) mapsum - mapavg);
      mapsum = 0;                                 /* Reset domain load sum */
    }
  }
  mapdlt = (mapnbr != 0) ? mapdlt / ((double) mapnbr * mapavg) : 0.0L;
  mapmmy = (mapnbr != 0) ? (double) mapmax / (double) mapavg : 0.0L;

  if (mapnbr > tgtnbr) {                          /* If more subdomains than architecture size */
#ifdef SCOTCH_DEBUG_MAP2
    if (! archVar (&mappptr->archdat)) {          /* If not a variable-sized architecture */
      errorPrint ("SCOTCH_graphMapView: invalid mapping");
      memFree    (domntab);                       /* Free group leader */
      return     (1);
    }
#endif /* SCOTCH_DEBUG_MAP2 */
    tgtnbr = mapnbr;                              /* Assume it is a variable-sized architecture */
  }

  fprintf (stream, "M\tProcessors " GNUMSTRING "/" GNUMSTRING " (%g)\n",
           (Gnum) mapnbr,
           (Gnum) tgtnbr,
           (double) mapnbr / (double) tgtnbr);
  fprintf (stream, "M\tTarget min=" GNUMSTRING "\tmax=" GNUMSTRING "\tavg=%g\tdlt=%g\tmaxavg=%g\n",
           (Gnum) mapmin,
           (Gnum) mapmax,
           mapavg,
           mapdlt,
           mapmmy);

  nghbnbr = 0;
  nghbmin = ANUMMAX;
  nghbmax = 0;
  nghbsum = 0;
  nghbnbr = 0;
  nghbtab[0] = -2;
  for (vertnum = 0; domntab[vertnum].labl != ARCHDOMNOTTERM; vertnum ++) {
    Gnum                edgenum;
    Gnum                edgennd;
    Anum                partnum;

    partnum = parttax[domntab[vertnum].peri];
    for (edgenum = verttax[domntab[vertnum].peri],
         edgennd = vendtax[domntab[vertnum].peri];
         edgenum < edgennd; edgenum ++) {
      Anum                partend;

      partend = parttax[edgetax[edgenum]];
      if ((partend != partnum) &&                 /* If edge is not internal                                      */
          (partend != nghbtab[nghbnbr])) {        /* And neighbor is not sole neighbor or has not just been found */
        Anum                partmin;
        Anum                partmax;

        partmin = 0;
        partmax = nghbnbr;
        while ((partmax - partmin) > 1) {
          Anum                partmed;

          partmed = (partmax + partmin) >> 1;
          if (nghbtab[partmed] > partend)
            partmax = partmed;
          else
            partmin = partmed;
        }
        if (nghbtab[partmin] == partend)          /* If neighboring part found, skip to next neighbor */
          continue;

#ifdef SCOTCH_DEBUG_MAP2
        if (nghbnbr >= (grafptr->vertnbr + 1)) {
          errorPrint ("SCOTCH_graphMapView: internal error");
          return (1);
        }
#endif /* SCOTCH_DEBUG_MAP2 */

        nghbnbr ++;
        for (partmax = nghbnbr; partmax > (partmin + 1); partmax --)
          nghbtab[partmax] = nghbtab[partmax - 1];
        nghbtab[partmin + 1] = partend;           /* Add new neighbor part in the right place */
      }
    }
    if (domntab[vertnum].labl != domntab[vertnum + 1].labl) { /* TRICK: if new (or end) domain label */
      if (nghbnbr < nghbmin)
        nghbmin = nghbnbr;
      if (nghbnbr > nghbmax)
        nghbmax = nghbnbr;
      nghbsum += nghbnbr;

      nghbnbr = 0;
    }
  }
static
int
graphMapCompute2 (
SCOTCH_Graph * const        grafptr,              /*+ Graph to order                  +*/
SCOTCH_Mapping * const      mappptr,              /*+ Mapping to compute              +*/
SCOTCH_Mapping * const      mapoptr,              /*+ Old mapping                     +*/
const double                emraval,              /*+ Edge migration ratio            +*/ 
const SCOTCH_Num *          vmlotab,              /*+ Vertex migration cost array     +*/
Gnum                        vfixval,              /*+ Equal to 0 if no fixed vertices +*/
SCOTCH_Strat * const        straptr)              /*+ Mapping strategy                +*/
{
  Kgraph                mapgrafdat;               /* Effective mapping graph              */
  const Strat *         mapstraptr;               /* Pointer to mapping strategy          */
  LibMapping * restrict lmapptr;
  LibMapping * restrict lmaoptr;
  Anum *                pfixtax;
  Gnum                  baseval;
  Anum *                parttax;                  /* Partition array                      */
  Anum *                parotax;                  /* Old partition array                  */
  Gnum                  crloval;                  /* Coefficient load for regular edges   */
  Gnum                  cmloval;                  /* Coefficient load for migration edges */
  const Gnum *          vmlotax;                  /* Vertex migration cost array          */
  Gnum                  vertnum;
  Gnum                  vertnnd;
  Gnum                  vertnbr;
  int                   o;

  lmapptr = (LibMapping *) mappptr;
#ifdef SCOTCH_DEBUG_GRAPH2
  if ((Graph *) grafptr != lmapptr->grafptr) {
    errorPrint ("graphMapCompute2: output mapping does not correspond to input graph");
    return     (1);
  }
  if (graphCheck ((Graph *) grafptr) != 0) {      /* Vertex loads can be 0 if we have fixed vertices */
    errorPrint ("graphMapCompute2: invalid input graph");
    return     (1);
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */

  if (*((Strat **) straptr) == NULL) {            /* Set default mapping strategy if necessary */
    ArchDom             archdomnorg;

    archDomFrst (lmapptr->archptr, &archdomnorg);
    SCOTCH_stratGraphMapBuild (straptr, SCOTCH_STRATDEFAULT, archDomSize (lmapptr->archptr, &archdomnorg), 0.01);
  }

  mapstraptr = *((Strat **) straptr);
  if (mapstraptr->tabl != &kgraphmapststratab) {
    errorPrint ("graphMapCompute2: not a graph mapping strategy");
    return     (1);
  }

  baseval = lmapptr->grafptr->baseval;
  vertnbr = lmapptr->grafptr->vertnbr;
  if (mapoptr != NULL) {                          /* We are doing a repartitioning */
    LibMapping *        lmaoptr;
    Gnum                numeval;
    Gnum                denoval;

    lmaoptr = (LibMapping *) mapoptr;
#ifdef SCOTCH_DEBUG_GRAPH2
    if (lmapptr->grafptr != lmaoptr->grafptr) {
      errorPrint ("graphMapCompute2: output and old mapping must correspond to the same graph");
      return     (1);
    }
    if (lmapptr->archptr != lmaoptr->archptr) {
      errorPrint ("graphMapCompute2: output and old mapping must correspond to the same architecture");
      return     (1);
    }
#endif /* SCOTCH_DEBUG_GRAPH2 */

    parotax = lmaoptr->parttab - baseval;
    vmlotax = (vmlotab != NULL) ? vmlotab - baseval : NULL;
    numeval = (INT) ((emraval * 100.0) + 0.5);
    denoval = intGcd (numeval, 100);
    cmloval = numeval / denoval;
    crloval = 100     / denoval;
  }
  else {
    parotax = NULL;
    vmlotax = NULL;
    cmloval =
    crloval = 1;
  }

  parttax = NULL;
  if (vfixval != 0) {                             /* We have fixed vertices */
#ifdef SCOTCH_DEBUG_GRAPH2
    if (lmapptr->parttab == NULL) {               /* We must have fixed vertices information */
      errorPrint ("graphMapCompute2: missing output mapping part array");
      return     (1);
    }
#endif /* SCOTCH_DEBUG_GRAPH2 */
    pfixtax = lmapptr->parttab - baseval;
    if ((parttax = (Anum *) memAlloc (vertnbr * sizeof (Anum))) == NULL) {
      errorPrint ("graphMapCompute2: out of memory (1)");
      return     (1);
    }
    memSet (parttax, 0, vertnbr * sizeof (Anum)); /* All vertices mapped to first domain */
    parttax -= baseval;
  } else {
    pfixtax = NULL;
    if (lmapptr->parttab == NULL) {               /* If user mapping not initialized */
      errorPrint ("graphMapCompute2: invalid user mapping");
      return     (1);
    }
    parttax = lmapptr->parttab - baseval;
  }

  intRandInit ();                                 /* Check that random number generator is initialized */

  if (kgraphInit (&mapgrafdat, (Graph *) grafptr, lmapptr->archptr, NULL, parttax, parotax, crloval, cmloval, vmlotax, pfixtax) != 0)
    return (1);

  o = 0;
  if (mapgrafdat.vfixnbr != mapgrafdat.s.vertnbr) { /* Perform mapping if not all fixed vertices */
    o = kgraphMapSt (&mapgrafdat, mapstraptr);

    lmapptr->parttab -= baseval;
    for (vertnum = baseval, vertnnd = vertnum + lmapptr->grafptr->vertnbr;
        vertnum < vertnnd; vertnum ++)
      lmapptr->parttab[vertnum] = archDomNum (lmapptr->archptr, &mapgrafdat.m.domntab[mapgrafdat.m.parttax[vertnum]]);
    lmapptr->parttab += baseval;
  }

  if (vfixval != 0)                               /* We have fixed vertices */
    memFree (parttax + baseval);

  kgraphExit (&mapgrafdat);

  return (o);
}