/* Copyright 2004,2007,2021,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       : order.c                                 **/
/**                                                        **/
/**   AUTHOR     : Francois PELLEGRINI                     **/
/**                                                        **/
/**   FUNCTION   : This module handles generic orderings.  **/
/**                                                        **/
/**   DATES      : # Version 3.2  : from : 19 oct 1996     **/
/**                                 to   : 27 aug 1998     **/
/**                # Version 4.0  : from : 19 dec 2001     **/
/**                                 to   : 26 dec 2004     **/
/**                # Version 5.0  : from : 25 jul 2007     **/
/**                                 to   : 25 jul 2007     **/
/**                # Version 7.0  : from : 26 apr 2021     **/
/**                                 to   : 10 aug 2023     **/
/**                                                        **/
/************************************************************/

/*
**  The defines and includes.
*/

#define SCOTCH_ORDER

#include "module.h"
#include "common.h"
#include "graph.h"
#include "order.h"

/************************************/
/*                                  */
/* These routines handle orderings. */
/*                                  */
/************************************/

/* This routine initializes an ordering
** with respect to a given source graph.
** It returns:
** - 0   : on success.
** - !0  : on error.
*/

int
orderInit (
Order * restrict const      ordeptr,
const Gnum                  baseval,
const Gnum                  vnodnbr,
Gnum * restrict const       peritab)
{
  ordeptr->flagval         = ORDERNONE;
  ordeptr->baseval         = baseval;
  ordeptr->vnodnbr         = vnodnbr;
  ordeptr->treenbr         =                      /* Initialize a simple blocking */
  ordeptr->cblknbr         = 1;
  ordeptr->cblktre.typeval = ORDERCBLKLEAF;
  ordeptr->cblktre.vnodnbr = vnodnbr;
  ordeptr->cblktre.cblknbr = 0;
  ordeptr->cblktre.cblktab = NULL;
  ordeptr->peritab         = peritab;

  if (ordeptr->peritab == NULL) {                 /* Inverse permutation must be allocated */
    ordeptr->flagval |= ORDERFREEPERI;            /* Flag it so it will be freed           */
    if ((ordeptr->peritab = (Gnum *) memAlloc (vnodnbr * sizeof (Gnum))) == NULL) {
      errorPrint ("orderInit: out of memory");
      return (1);
    }
  }
#ifdef SCOTCH_PTHREAD
  pthread_mutex_init (&ordeptr->mutedat, NULL);   /* Initialize local mutex */
#endif /* SCOTCH_PTHREAD */

#ifdef SCOTCH_DEBUG_ORDER2
  memSet (ordeptr->peritab, ~0, vnodnbr * sizeof (Gnum));
#endif /* SCOTCH_DEBUG_ORDER2 */

  return (0);
}

/* This routine frees the contents
** of the given ordering.
** It returns:
** - 0   : on success.
** - !0  : on error.
*/

void
orderExit (
Order * restrict const      ordeptr)
{
  if (ordeptr->cblktre.cblktab != NULL)           /* Free column block tree */
    orderExit2 (ordeptr->cblktre.cblktab, ordeptr->cblktre.cblknbr);

  if ((ordeptr->peritab != NULL) && ((ordeptr->flagval & ORDERFREEPERI) != 0)) /* If peritab is group leader */
    memFree (ordeptr->peritab);                   /* Free group leader */

#ifdef SCOTCH_PTHREAD
  pthread_mutex_destroy (&ordeptr->mutedat);      /* Destroy local mutex */
#endif /* SCOTCH_PTHREAD */

#ifdef SCOTCH_DEBUG_ORDER2
  memSet (ordeptr, ~0, sizeof (Order));
#endif /* SCOTCH_DEBUG_ORDER2 */
}

static
void
orderExit2 (
OrderCblk * restrict const  cblktab,
const Gnum                  cblknbr)
{
  Gnum                cblknum;

  for (cblknum = 0; cblknum < cblknbr; cblknum ++) {
    if (cblktab[cblknum].cblktab != NULL)
      orderExit2 (cblktab[cblknum].cblktab, cblktab[cblknum].cblknbr);
  }
  memFree (cblktab);
}

/* This routine computes the inverse permutation
** of the given permutation, according to the
** direct and inverse base values.
** It returns:
** - VOID  : in all cases.
*/

void
orderPeri (
const Gnum * restrict const   permtab,            /* Permutation to invert             */
const Gnum                    permbas,            /* Permutation base value            */
const Gnum                    permnbr,            /* Number of permutation indices     */
Gnum * restrict const         peritab,            /* Array of inverse permutation      */
const Gnum                    peribas)            /* Base value of inverse permutation */
{
  Gnum                permnum;

  for (permnum = 0; permnum < permnbr; permnum ++)
    peritab[permtab[permnum] - permbas] = permnum + peribas;
}

/* This routine computes the column block
** range array of the given ordering.
** It returns:
** - VOID  : in all cases.
*/

void
orderRang (
const Order * restrict const  ordeptr,            /* Ordering                      */
Gnum * restrict const         rangtab)            /* Column block range array [+1] */
{
  Gnum *              rangptr;
  Gnum                ordenum;

  rangptr = rangtab;                              /* Set beginning of range array */
  ordenum = ordeptr->baseval;                     /* Set initial number           */
  orderRang2 (&rangptr, &ordenum, &ordeptr->cblktre);
  *rangptr = ordenum;                             /* Set end of range array */
}

static
void
orderRang2 (
Gnum ** restrict const            rangppt,
Gnum * restrict const             ordeppt,
const OrderCblk * restrict const  cblkptr)
{
  Gnum                cblknum;
#ifdef SCOTCH_DEBUG_ORDER2
  Gnum * restrict     rangtmp;

  if (cblkptr->vnodnbr < 1)
    errorPrint ("orderRang2: internal error (1)");
#endif /* SCOTCH_DEBUG_ORDER2 */

  if (cblkptr->cblktab == NULL) {                 /* If leaf of column block tree  */
    *(*rangppt) ++ = *ordeppt;                    /* Set beginning of column block */
    *ordeppt      += cblkptr->vnodnbr;            /* Advance by column block size  */
  }
  else {
#ifdef SCOTCH_DEBUG_ORDER2
    rangtmp = *rangppt;
#endif /* SCOTCH_DEBUG_ORDER2 */
    for (cblknum = 0; cblknum < cblkptr->cblknbr; cblknum ++)
      orderRang2 (rangppt, ordeppt, &cblkptr->cblktab[cblknum]);
#ifdef SCOTCH_DEBUG_ORDER2
    if ((*ordeppt - *rangtmp) != cblkptr->vnodnbr)
      errorPrint ("orderRang2: internal error (2)");
#endif /* SCOTCH_DEBUG_ORDER2 */
  }
}

/* This routine computes the separator tree
** array of the given ordering.
** It returns:
** - VOID  : in all cases.
*/

void
orderTree (
const Order * restrict const  ordeptr,            /* Ordering                          */
Gnum * restrict const         treetab)            /* Column block separator tree array */
{
  Gnum                cblanum;

  cblanum = ordeptr->cblknbr + ordeptr->baseval - 1; /* Set number of last column block */
  orderTree2 (treetab - ordeptr->baseval, &cblanum, &ordeptr->cblktre, -1);

#ifdef SCOTCH_DEBUG_ORDER2
  if (cblanum != ordeptr->baseval - 1)
    errorPrint ("orderTree: internal error");
#endif /* SCOTCH_DEBUG_ORDER2 */
}

static
void
orderTree2 (
Gnum * restrict const             treetax,        /* Based access to tree table                                          */
Gnum * restrict const             cblaptr,        /* Pointer to current number of last column block, in descending order */
const OrderCblk * restrict const  cblkptr,        /* Current column block tree node                                      */
Gnum                              cbfanum)        /* Current number of ancestor separator column block                   */
{
#ifdef SCOTCH_DEBUG_ORDER2
  if (cblkptr->vnodnbr < 1)
    errorPrint ("orderTree2: internal error (1)");
#endif /* SCOTCH_DEBUG_ORDER2 */

  if (cblkptr->cblktab == NULL) {                 /* If leaf of column block tree */
#ifdef SCOTCH_DEBUG_ORDER2
    if (cblkptr->typeval != ORDERCBLKLEAF)
      errorPrint ("orderTree2: invalid elimination tree (1)");
#endif /* SCOTCH_DEBUG_ORDER2 */
    treetax[(*cblaptr) --] = cbfanum;             /* Set its ancestor */
  }
  else {                                          /* Node has sub-nodes                 */
    Gnum                cblknum;                  /* Index of current sub-block         */
    Gnum                cblanum;                  /* Number assigned to rightmost block */

    cblknum = cblkptr->cblknbr - 1;               /* Assume all column blocks will be scanned */
    switch (cblkptr->typeval) {
      case ORDERCBLKNEDI :                        /* Node is a nested dissection node */
#ifdef SCOTCH_DEBUG_ORDER2
        if ((cblknum < 1) ||                      /* Nested dissection nodes have 2 or 3 sub-blocks */
            (cblknum > 2))
          errorPrint ("orderTree2: invalid elimination tree (2)");
#endif /* SCOTCH_DEBUG_ORDER2 */
        if (cblknum == 2) {                       /* If it has a non-empty separator (cblknbr == 3)   */
          cblanum = *cblaptr;                     /* Save number of last column block of separator    */
          orderTree2 (treetax, cblaptr, &cblkptr->cblktab[cblknum], cbfanum); /* Scan separator apart */
          cbfanum = cblanum;                      /* Separator becomes most recent ancestor of parts  */
          cblknum = 1;                            /* Only scan the two parts, not the separator       */
        }
        /* FALL THROUGH */
      case ORDERCBLKDICO :                        /* Node is a set of disconnected components (or a separatorless nested dissection node) */
        for ( ; cblknum >= 0; cblknum --) {
          orderTree2 (treetax, cblaptr, &cblkptr->cblktab[cblknum], cbfanum); /* Sub-blocks keep current father as father */
#ifdef SCOTCH_DEBUG_ORDER2
          if (*cblaptr < -1)
            errorPrint ("orderTree2: internal error (2)");
#endif /* SCOTCH_DEBUG_ORDER2 */
        }
        break;
      case ORDERCBLKSEQU :                        /* Node is a set of dependent sub-blocks */
        for ( ; cblknum >= 0; cblknum --) {
          cblanum = *cblaptr;                     /* Save number of current last column block          */
          orderTree2 (treetax, cblaptr, &cblkptr->cblktab[cblknum], cbfanum); /* Scan current block    */
          cbfanum = cblanum;                      /* Current last block becomes ancestor of next block */
#ifdef SCOTCH_DEBUG_ORDER2
          if (*cblaptr < -1)
            errorPrint ("orderTree2: internal error (3)");
#endif /* SCOTCH_DEBUG_ORDER2 */
        }
        break;
#ifdef SCOTCH_DEBUG_ORDER2
      default:
        errorPrint ("orderTree2: invalid elimination tree (3)");
        break;
#endif /* SCOTCH_DEBUG_ORDER2 */
    }
  }
}
