/* File: iter_merge.c
   Non-recursive Merge Sort Routines
*/

#include <assert.h>
#include <stdio.h>
#include "sorting.h"

/* Global variable temp must be allocated the same
   amount of space as the array to be sorted
*/

static data *temp ;

void init_mergesort(index n) {

   temp = (data *) malloc(n * sizeof(data)) ;
   if (temp == NULL) {
      printf("Could allocate temporary space for merge\n") ;
      exit(1) ;
   }
}


void merge(data A[], index low1, index high1, index low2, index high2) {
   index t, i1, i2 ;

   /* Sanity check */
   assert(low2 == high1 + 1) ;
  
   /* while there are elements in both halves, copy the lowest one */
   i1 = low1 ;
   i2 = low2 ;
   t = 0 ;
   while (i1 <= high1 && i2 <= high2) {
      if (A[i1] < A[i2]) {
         temp[t] = A[i1] ;
         i1++ ;
         t++ ;
      } else {
         temp[t] = A[i2] ;
         i2++ ;
         t++ ;
      }
   }

   /* copy any remaining elements */
   while (i1 <= high1) {
      temp[t] = A[i1] ;
      t++ ;
      i1++ ;
   }
   while (i2 <= high2) {
      temp[t] = A[i2] ;
      t++ ;
      i2++ ;
   }

   /* copy the now-sorted elements back into the original array */
   for (t = low1; t <= high2; t++) {
      A[t] = temp[t - low1] ;
   }
}


/* Non recursive Merge Sort
   Idea: merge adjacent entries of the array to form sorted groups
   of 2 entries.  Then merge the adjacent groups to form sorted groups
   of 4 entries, then 8 entries, ... until the entire array is sorted.

*/

void mergesort(data A[], index low, index high) {
   index length, low1, high1, low2, high2, n ;

   n = high - low + 1 ;
   length = 1 ; /* group length */


   while(length < n) {
      low1  = low ;
      high1 = low1 + length - 1 ;
      low2  = low1 + length ;
      high2 = low2 + length - 1 ;

      while (high2 <= high) {
         merge(A, low1, high1, low2, high2) ;

         low1  = low1 + 2*length ;
         high1 = low1 + length - 1 ;
         low2  = low1 + length ;
         high2 = low2 + length - 1 ;
      }

      /* Merge last group with remaining entries if they exist */
      if (high1 < high) {
         merge(A,low1,high1,low2,high) ;
      }

      length = 2*length ;     
   }
}

