/*****************************************************************************
*      FILE: heap.c: This file contains routines for sorting individuals for
*                     selection
*
*      Authors: David M. Levine, Philip L. Hallstrom, David M. Noelle
*****************************************************************************/

#include "pgapack.h"

/*U****************************************************************************
   PGASortPop - Creates an (internal) array of indices according to one of
   three criteria.  If PGA_POPREPL_BEST is used (the default) the array is
   sorted according from most to least fit.  If PGA_POPREPL_RANDOM_REP is
   used the indices in the array are selected randomly with replacement.
   If PGA_POPREPL_RANDOM_NOREP is used the indices in the array are selected
   randomly without replacement.  The function PGASetPopReplacement is used
   to specify which strategy is used.  The indices of the sorted population
   members may then be accessed from the internal array via PGAGetSortPop.
   This routine is typically used during population replacement.

   Category: Sorting

   Inputs:
       ctx      - context variable
       popindex - symbolic constant of the population from which to create
                  the srted array.

   Output:
      An array of indices created according to one of three criteria.

   Example:
      The following code copies the five best strings from the old
      population into the new one (the rest of the new population will
      be created by recombination

      PGAContext *ctx;
      int i,j;
      :
      PGASetPopReplacement(ctx,PGA_POPREPL_BEST)
      PGASortPop(ctx, PGA_OLDPOP);
      for ( i=0; i < 5; i++) {
          j = PGAGetSortPop(ctx, i);
      PGACopyIndividual (ctx, j, PGA_OLDPOP, i, PGA_NEWPOP);
      
****************************************************************************U*/
void PGASortPop ( PGAContext *ctx, int pop )
{
    int i,j;
    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGASortPop", "Entered", 
                  PGA_VOID, NULL );
    switch (ctx->ga.PopReplace) {
    case PGA_POPREPL_BEST:
            switch ( pop ) {
            case PGA_OLDPOP:
                for (i = 0 ; i < ctx->ga.PopSize ; i++ ) {
                    ctx->ga.sorted[i] = i;
                    ctx->scratch.dblscratch[i] = ctx->ga.oldpop[i].fitness;
                };
                break;
            case PGA_NEWPOP:
                for (i = 0 ; i < ctx->ga.PopSize ; i++ ) {
                    ctx->ga.sorted[i] = i;
                    ctx->scratch.dblscratch[i] = ctx->ga.newpop[i].fitness;
                };
                break;
            default:
                PGAError( ctx,
                         "PGASort: Invalid value of pop:",
                         PGA_FATAL,
                         PGA_INT,
                         (void *) &pop );
                break;
            };
            PGADblHeapSort ( ctx, ctx->scratch.dblscratch, ctx->ga.sorted,
                            ctx->ga.PopSize );
            break;
    case PGA_POPREPL_RANDOM_REP:
        if ((pop != PGA_OLDPOP) && (pop != PGA_NEWPOP))
            PGAError( ctx,
                     "PGASort: Invalid value of pop:",
                      PGA_FATAL,
                      PGA_INT,
                      (void *) &pop );
        for (i = 0; i < ctx->ga.PopSize; i++) {
            ctx->scratch.intscratch[i] = i;
        };
        for (i = 0; i < ctx->ga.PopSize; i++) {
            j = PGARandomInterval ( ctx, 0, ctx->ga.PopSize-1 );
            ctx->ga.sorted[i] = ctx->scratch.intscratch[j];
        };
        break;
    case PGA_POPREPL_RANDOM_NOREP:
        if ((pop != PGA_OLDPOP) && (pop != PGA_NEWPOP))
            PGAError( ctx,
                     "PGASort: Invalid value of pop:",
                      PGA_FATAL,
                      PGA_INT,
                      (void *) &pop );
        for (i = 0; i < ctx->ga.PopSize; i++) {
            ctx->scratch.intscratch[i] = i;
        };
        for (i = 0; i < ctx->ga.PopSize; i++) {
            j = PGARandomInterval ( ctx, 0, ctx->ga.PopSize-i-1 );
            ctx->ga.sorted[i] = ctx->scratch.intscratch[j];
            ctx->scratch.intscratch[j] =
                ctx->scratch.intscratch[ctx->ga.PopSize-i-1];
        };
        break;
    }
}

/*U****************************************************************************
   PGAGetSortPop - returns an index from the array created by PGASortPop

   Category: Sorting

   Inputs:
       ctx      - context variable
       n        - specified which index element is to be returned.

   Output:
       An index from the array created by PGASortPop

   Example:
      The following code copies the five best strings from the old
      population into the new one (the rest of the new population will
      be created by recombination

      PGAContext *ctx;
      int i,j;
      :
      PGASetPopReplacement(ctx,PGA_POPREPL_BEST)
      PGASortPop(ctx, PGA_OLDPOP);
      for ( i=0; i < 5; i++) {
          j = PGAGetSortPop(ctx, i);
      PGACopyIndividual (ctx, j, PGA_OLDPOP, i, PGA_NEWPOP);

****************************************************************************U*/
int PGAGetSortPop ( PGAContext *ctx, int n )
{
     PGADebugPrint( ctx, PGADEBUG_ENTEREDHIGH, "PGAGetSortPop", "Entered", 
                   PGA_VOID, NULL );
     if (n >= 0 && n < ctx->ga.PopSize )
        return ( ctx->ga.sorted[n] );
     else
     {
         PGAError( ctx, "PGAGetSorted: Invalid value of n:",
                  PGA_FATAL, PGA_INT, (void *) &n );
         return(PGAERROR);
     }
}

/*I****************************************************************************
   PGADblHeapSort - Uses a heapsort algorithm to sort from largest to smallest
   element.  An integer array, intialized with the original indices of the
   elements of array a is sorted also so that the original locations are known

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (double) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:
       The sorted arrays a and idx

   Example:
      The following code sorts the population by fitness

      PGAContext *ctx;
      int i,j,n,idx[LARGE]
      double a[LARGE];
      :
      n = PGAGetPopsize(ctx);
      for(i=0;i<n;i++) {
        a[i]   = PGAGetFitness(ctx,p,PGA_OLDPOP);
        idx[i] = i;
      }
      PGADblHeapSort ( ctx, a, idx, n);

****************************************************************************I*/
void PGADblHeapSort ( PGAContext *ctx, double *a, int *idx, int n )
{
  int i;                   /* index of for loops                      */
  double temp_a;
  int temp_idx;
  PGADblHeapify(ctx, a,idx,n);  /* first transform the elements into a heap*/
  for ( i=n-1; i>=1; i--)  /* interchange the new maximum with the   */
  {                        /* element at the end of the tree         */
    temp_a   = a[i];
    temp_idx = idx[i];
    a[i]     = a[0];
    idx[i]   = idx[0];
    a[0]     = temp_a;
    idx[0]     = temp_idx;
    PGADblAdjustHeap ( ctx, a,idx,0,i);
  }
  return;
}

/*I****************************************************************************
   PGADblHeapify - Auxiliary routine called by PGADblHeapSort

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (double) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:

   Example:

****************************************************************************I*/
void PGADblHeapify( PGAContext *ctx, double *a, int *idx, int n )
{
  int i;            /* index of for loops                      */
  for (i=(n-2)/2; i>=0; i--)
  {
    PGADblAdjustHeap(ctx, a,idx,i,n);
  }
  return;
}

/*I****************************************************************************
   PGADblAdjustHeap - Auxiliary routine called by PGADblHeapSort

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (double) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:

   Example:

****************************************************************************I*/
void PGADblAdjustHeap ( PGAContext *ctx, double *a, int *idx, int i, int n )
/* double a[];            the array being sorted               */
/* int idx[];                                                  */
/* int i;              combine the node at a[i] with the two   */
/* int n;              heaps at a[2i+1] and a[2i+2] to form a  */
/*                     single heap.    0<=i<=n-1               */
{
  int j;
  double item;
  int item_idx; /* item holds the value currently in the root */
  item = a[i];
  item_idx = idx[i];
  j = 2*i+1;          /* let j be the left child                 */
  while ( j < n )
  {
    if ( j<n-1 && a[j] > a[j+1] )    /* compare left & right child         */
       j = j + 1;                    /* j points to the larger child       */
    if ( item <= a[j] )              /* a position for item has been found */
       break;
    else
    {
      a  [(j-1)/2] = a  [j];  /* move the larger child up a level */
      idx[(j-1)/2] = idx[j];
      j = j*2+1;
    }
  }
  a[(j-1)/2]   = item;
  idx[(j-1)/2] = item_idx;
  return;
}

/*I****************************************************************************
   PGAIntHeapSort - Uses a heapsort algorithm to sort from largest to smallest
   element.  An integer array, intialized with the original indices of the
   elements of array a is sorted also so that the original locations are known

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (int) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:
       The sorted arrays a and idx

   Example:
      The following code sorts the population by fitness

      PGAContext *ctx;
      int i,j,n,idx[LARGE],a[LARGE];
      :
      n = PGAGetPopsize(ctx);
      for(i=0;i<n;i++) {
        a[i]   = (int) PGAGetEvaluate(ctx,p,PGA_OLDPOP);
        idx[i] = i;
      }
      PGAIntHeapSort ( ctx, a, idx, n);

****************************************************************************I*/
void PGAIntHeapSort ( PGAContext *ctx, int *a, int *idx, int n )
{
  int i;                   /* index of for loops                      */
  int temp_a;
  int temp_idx;
  PGAIntHeapify(ctx, a,idx,n);    /* first transform the elements into a heap*/
  for ( i=n-1; i>=1; i--)  /* interchange the new maximum with the   */
  {                        /* element at the end of the tree         */
    temp_a   = a[i];
    temp_idx = idx[i];
    a[i]     = a[0];
    idx[i]   = idx[0];
    a[0]     = temp_a;
    idx[0]     = temp_idx;
    PGAIntAdjustHeap ( ctx, a,idx,0,i);
  }
  return;
}

/*I****************************************************************************
   PGAIntHeapify - Auxiliary routine called by PGAIntHeapSort

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (double) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:

   Example:

****************************************************************************I*/
void PGAIntHeapify( PGAContext *ctx, int *a, int *idx, int n )
{
  int i;            /* index of for loops                      */
  for (i=(n-2)/2; i>=0; i--)
  {
    PGAIntAdjustHeap(ctx, a,idx,i,n);
  }
  return;
}

/*I****************************************************************************
   PGAIntAdjustHeap - Auxiliary routine called by PGAIntHeapSort

   Category: Sorting

   Inputs:
       ctx      - context variable
       a        - array of (double) values to be sorted
       idx      - array of integer indices corresponding to the array
                  a being sorted
       n        - size of the arrays a and idx

   Output:

   Example:

****************************************************************************I*/
void PGAIntAdjustHeap ( PGAContext *ctx, int *a, int *idx, int i, int n )
/* int a[];            the array being sorted                  */
/* int idx[];                                                  */
/* int i;              combine the node at a[i] with the two   */
/* int n;              heaps at a[2i+1] and a[2i+2] to form a  */
/*                     single heap.    0<=i<=n-1               */
{
  int j;
  int item,item_idx; /* item holds the value currently in the root */
   item = a[i];
  item_idx = idx[i];
  j = 2*i+1;          /* let j be the left child                 */
   while ( j < n )
  {
    if ( j<n-1 && a[j] < a[j+1] )    /* compare left & right child         */
       j = j + 1;                    /* j points to the larger child       */
    if ( item >= a[j] )              /* a position for item has been found */
       break;
    else
    {
      a  [(j-1)/2] = a  [j];  /* move the larger child up a level */
      idx[(j-1)/2] = idx[j];
      j = j*2+1;
    }
  }
  a[(j-1)/2]   = item;
  idx[(j-1)/2] = item_idx;
  return;
}
