#include <stdio.h>
#include <ctype.h>
#include <std.h>

/* the next 6 #defines implement a very fast in-line stack abstraction       */

#define MAX_STACK_SIZE 32
#define DISPOSE_STACK(S) (free(S))
#define PUSH(LOW,HIGH) do {top->lo = LOW;top++->hi = HIGH;} while (0)
#define POP(LOW,HIGH)  do {LOW = (--top)->lo;HIGH = top->hi;} while (0)
#define STACK_NOT_EMPTY (stack < top)                
#define SWAP(A,B) do {char *_temp = (A);(A) = (B);(B) = _temp;} while (0)

/* Discontinue quicksort algorithm when partition gets below this size.
   This particular magic number was chosen to work best on a Sun 4/260. */
#define MAX_THRESH 30

/* Data structure for stack of unfulfilled obligations. */
typedef struct 
{
  char **lo;
  char **hi;
} stack_node;

/* Once main array is partially sorted by quicksort the remainder is 
   completely sorted using insertion sort, since this is efficient 
   for partitions below MAX_THRESH size. BASE points to the beginning 
   of the array to sort, and END_PTR points at the very last element in
   the array (*not* one beyond it!). */

inline static void
insert_sort (char **base_ptr, char **end_ptr)
{
  char **run_ptr;
  char **tmp_ptr = base_ptr;
#ifdef __GNUC__
  char **thresh  = end_ptr <? base_ptr + MAX_THRESH;
#else
  char **thresh  = base_ptr + MAX_THRESH;
  if (thresh > end_ptr)
    thresh = end_ptr;
#endif

  /* Find the smallest element in the first threshold and put it at the 
     end of the array.  This is guaranteed to be the smallest element in 
     the array, and it speeds up the inner loop of insertion sort. */

  for (run_ptr = tmp_ptr + 1; run_ptr <= thresh; run_ptr++)
    if (strcmp (*run_ptr, *tmp_ptr) < 0)
      tmp_ptr = run_ptr;

  SWAP (*tmp_ptr, *base_ptr);

  /* Typical insertion sort, but we run from the `right-hand-side'
     downto the `left-hand-side.' */

  for (run_ptr = base_ptr + 1; run_ptr < end_ptr; run_ptr++)
    {
      char *temp = *(tmp_ptr = run_ptr + 1);

      /* Select the correct location for the new element, 
         by sliding everyone down by 1 to make room! */

      while (strcmp (temp, *--tmp_ptr) < 0)
        tmp_ptr[1] = *tmp_ptr;

      tmp_ptr[1] = temp; 
    }
}

/* Return the median value selected from among the 
   LOW, MIDDLE, and HIGH.  Rearrange LOW and HIGH so
   the three values are sorted amongst themselves. 
   This speeds up later work... */

inline static char *
find_pivot (char **low, char **high)
{
  char **middle = low + ((high - low ) >> 1);

  if (strcmp (*middle, *low) < 0)
    SWAP (*middle, *low);
  if (strcmp (*high, *middle) < 0)
    SWAP (*middle, *high);
  else 
    return *middle;

  if (strcmp (*middle, *low) < 0)
    SWAP (*middle, *low);

  return *middle;
}

/* Order elements using quicksort.  This implementation incorporates
   4 optimizations discussed in Sedgewick:
   
   1. Non-recursive, using an explicit stack of log (n) pointers that 
      indicate the next array partition to sort.

   2. Choses the pivot element to be the median-of-three, reducing
      the probability of selecting a bad pivot value.

   3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
      insertion sort to sort within the partitions.  This is a
      big win, since insertion sort is faster for small, mostly
      sorted array segements.
   
   4. The larger of the 2 sub-partitions are always pushed onto the
      stack first, with the algorithm then concentrating on the
      smaller partition.  This *guarantees* no more than log (n)
      stack size is needed! */
      
void
sort (char **base_ptr, int total_elems)
{
  if (total_elems > MAX_THRESH)
    {
      char       **lo = base_ptr;
      char       **hi = lo + (total_elems - 1);
      char       **left_ptr;
      char       **right_ptr;
      stack_node   stack[MAX_STACK_SIZE];
      stack_node  *top = stack + 1;

      while (STACK_NOT_EMPTY)
        {
          /* Select the median-of-three here. This allows us to
            skip forward for the LEFT_PTR and RIGHT_PTR. */
          char *pivot = find_pivot (lo, hi);
          left_ptr  = lo + 1;
          right_ptr = hi - 1; 

          /* Here's the famous ``collapse the walls'' section of
            quicksort.  Gotta like those tight inner loops! */
          do 
            {
              while (strcmp (*left_ptr, pivot) < 0)
                left_ptr++;

              while (strcmp (pivot, *right_ptr) < 0)
                right_ptr--;

              if (left_ptr < right_ptr) 
                {
                  SWAP (*left_ptr, *right_ptr);
                  left_ptr++;
                  right_ptr--;
                }
              else if (left_ptr == right_ptr) 
                {
                  left_ptr++;
                  right_ptr--;
                  break;
                }
            } 
          while (left_ptr <= right_ptr);

          if ((right_ptr - lo) <= MAX_THRESH) 
            {
              if ((hi - left_ptr) <= MAX_THRESH) /* ignore both small tables */
                POP (lo, hi);
              else              /* ignore small left table */
                lo = left_ptr;
            }
          else if ((hi - left_ptr) <= MAX_THRESH) /* ignore small right table */
            hi = right_ptr;
          else if ((right_ptr - lo) > (hi - left_ptr)) 
            {                   /* push larger left table */
              PUSH (lo, right_ptr);
              lo = left_ptr;
            }
          else 
            {                   /* push larger right table */
              PUSH (left_ptr, hi);
              hi = right_ptr;
            }
        }
    }

  insert_sort (base_ptr, base_ptr + (total_elems - 1));
}
