The La Plata Mountains as seen from above the author�s
            home.


Durango Bill�s

�C� Program to implement Sort Algorithms
(Source Code)



Return to the main Sort page

Web page generated via Sea Monkey's Composer HTML editor
within  a Linux Cinnamon Mint 18 operating system.
(Goodbye Microsoft)




//************* ********************* ************************* *****************
//
//                                Sort Algorithms
//                                      by
//                                  Bill Butler
//
//  This program can execute various sort algorithms to test how fast they run.
//
//  Sorting algorithms include:
//  Bubble sort
//  Insertion sort
//  Median-of-three quicksort
//  Multiple link list sort
//  Shell sort
//
//  For each of the above, the user can generate up to 10 million random
//  integers to sort. (Uses a pseudo random number generator so that the list
//  to sort can be exactly regenerated.)
//  The program also times how long each sort takes.
//
//************ ********************** ******************** *******************


#include <stdheaders.h>                      //  The usual stdio.h, etc.

void GenRandom(void);                        //  Generate a list of random nbrs.
void BubbleSort(void);                       //  Bubble Sort
void InsertionSort(void);                    //  Insertion Sort
void MLLsort(void);                          //  Multiple Link List Sort
void QuickSort(void);                        //  Median-of-three Quicksort
void ShellSort(void);                        //  Choose from 3 gap sequences

void PauseMsg(void);                         //  Pause the screen display


unsigned RandNbrs[10000004];                 //  The list of random numbers
                                             //  will be generated here. Note:
                                             //  RandNbrs[0] is used as a sentinel
int MLLlinks[26777220];                      //  Used by MLLsort. Will use up to
                                             //  10,000,000 + 2^24 + 1 of this
int QSleft[30];                              //  Stack arrays for the left/right
int QSright[30];                             //  ends of subgroups within
                                             //  QuickSort()
int Gaps13[24] = {0, 1, 3, 7, 21, 48, 112,   //  Three possible gap sequences
    336, 861, 1968, 4592, 13776, 33936,      //  may be used for experiments
    86961, 198768, 463792, 1391376,          //  with Shell Sort.
    3402672, 8382192, 21479367, 49095696,    //  See Sedgewick & ATT database
    114556624, 343669872};                   //  of integers for more info

int Gaps18[24] = {0, 1, 8, 23, 77, 281,      //  Gaps[i+2] = 4^(i+1) + 3*(2^i) + 1
    1073, 4193, 16577, 65921, 262913,
    1050113, 4197377, 16783361, 67121153,
    268460033, 1073790977};

int Gaps14[24] = {0, 1, 4, 13, 40, 121, 364, //  Gaps[i+1] = 3*Gaps[i] + 1
    1093, 3280, 9841, 29524, 88573, 265720,
    797161, 2391484, 7174453, 21523360,
    64570081, 193710244, 581130733, 1743392200};

int Gaps[24];                                //  Will copy one of the above into
                                             //  here.

int Nbr2Sort;                                //  Number of items to sort.

char Databuff[100];                          //  Buffer for user input (Assumes
                                             //  user does not abuse size)



int main(void) {

  int choice;

  puts("\nThis program demonstrates the performance of various sort algorithms.");
  puts("You may run time trials to compare results.\n");
  puts("Note: If you want to use identical inputs for the time trials,");
  puts("just use the same seed number for the random number generator.\n");
  PauseMsg();

  while(1) {                                //  Loop until user ends program
    puts("\n\n     *****     Enter number for menu choice     *****\n");
    puts("1)  Bubble sort");
    puts("2)  Insertion sort");
    puts("3)  Median-of-three Quicksort");
    puts("4)  Multiple link list sort");
    puts("5)  Shellsort (Choice of 3 different shell definitions)");
    puts("6)  End the program\n");

    gets(Databuff);
    choice = atoi(Databuff);

    if (choice == 6)
      break;

    GenRandom();                            //  Generate a list of random nbrs

    if (choice == 1) BubbleSort();
    else if (choice == 2) InsertionSort();
    else if (choice == 3) QuickSort();
    else if (choice == 4) MLLsort();
    else if (choice == 5) ShellSort();
  }
  return 0;
}



 
//************** ********************* *********************** ******************
//
//                                GenRandom
//
//  This routine generates a list of unsigned random integers in the range 0 to
//  4,294,967,295. An exact repetion of any list can be regenerated by using
//  the same seed number and the same quantity of numbers. Up to 10 million
//  numbers can be generated in the array RandNbrs[].
//
//  The random numbers will occupy positions RandNbrs[1] to RandNbrs[Nbr2Sort].
//  Nothing is placed in RandNbrs[0], but RandNbrs[0] will be used by some of
//  the sort routines to optimize execution speed.
//
//************** ******************** ************************** *****************

void GenRandom(void) {

  int i;
  unsigned seed;
  unsigned Multiplier;

  do {
    puts("\nEnter number of elements to sort (3 - 10,000,000)");
    gets(Databuff);
    Nbr2Sort = atoi(Databuff);
  } while ((Nbr2Sort < 3) || (Nbr2Sort > 10000000));

  puts("\nEnter a seed number for the random number generator");
  gets(Databuff);
  seed = atoi(Databuff);

  Multiplier = 3141592621;
  for (i = Nbr2Sort; i; i--) {                //  Fill array with random
    seed *= Multiplier;                       //  numbers.
    seed++;
    RandNbrs[i] = seed;
  }

  puts("\nDo you want to see the random numbers before they are sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    puts("");
    for (i = 1; i <= Nbr2Sort; i++) {
      printf("%4d) %'16u\n",i , RandNbrs[i]);
      if (!(i%20))                            //  Keeps list from running
        PauseMsg();                           //  off top of screen.
    }
    PauseMsg();
  }
}




//**************** ********************** ********************** ***************
//
//                                Bubble Sort
//
//  Bubble sort is not exactly the world's fastest sorting algorithm, but for
//  some reason, everyone seems to like it. Maybe it's related to:
//
//  "Tiny Bubbles"
//  "In the wine"
//  "Make you feel happy"
//  "Make you feel fine"
//
//  This version of Bubble Sort will sort the array RandNbrs[] in ascending
//  order. When the routine is finished, the lowest number in the RandNbrs[]
//  array will be found in position RandNbrs[1] while the largest number will
//  be in position RandNbrs[Nbr2Sort].
//
//  The algorithm starts by looking at positions 1 and 2 in the array. If the
//  number at position 1 is larger than the number at position 2, then the two
//  numbers are exchanged. The end result will leave the larger of the two
//  numbers at position 2 and the smaller at position 1.
//
//  After the above step, the algorithm looks at the numbers at positions 2 and
//  3. If the number at position 2 is larger, then it is exchanged so that the
//  larger number will now be in position 3.
//
//  The "j" loop continues this process until it reaches the bottom (highest
//  index location) of the array. At this point the largest number in the array
//  has been moved to the bottom of the array, and everything else has "bubbled"
//  up one level. Then, the "i" loop exerts control and the whole process
//  repeats. However, this time through, the 2nd largest number in the array
//  will be shifted down to the 2nd lowest position in the array. (Again this
//  stopping point is controled by the "i" loop.)
//
//  By the time "i" decreases to 1, the whole array is sorted.
//
//**************** ********************* *********************** *****************

void BubbleSort(void) {

  int i, j, k;
  unsigned temp;
  double Time1, Time2;

  puts("\nStarting Bubble Sort");

  Time1 = (double)clock();                        //  Save time to 0.001 sec.
  Time1 = Time1/(double)CLOCKS_PER_SEC;

  for (i = Nbr2Sort - 1; i; i--) {
    for (j = 1, k = 2; j <= i; j++, k++) {
      if (RandNbrs[j] > RandNbrs[k]) {
        temp = RandNbrs[j];
        RandNbrs[j] = RandNbrs[k];
        RandNbrs[k] = temp;
      }
    }
  }

  Time2 = (double)clock();
  Time2 = Time2/(double)CLOCKS_PER_SEC;

  printf("\nThe time to sort %'d items via Bubble Sort was %g seconds.\n",
    Nbr2Sort, Time2 - Time1);

  PauseMsg();

  puts("\nDo you want to see the random numbers after they were sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    puts("");
    for (i = 1; i <= Nbr2Sort; i++) {
      printf("%4d) %'16u\n", i, RandNbrs[i]);
      if (!(i%20))
        PauseMsg();
    }
    PauseMsg();
  }
}




//****************** ******************* *********************** ***************
//
//                                Insertion Sort
//
//  Insertion sort is simple to code and difficult to beat if you are sorting
//  a short list of elements. It is also very good at sorting a much larger
//  list that is nearly in sorted order. It is thus used as the basis of Shell
//  Sort and the final stage of "median-of-three Quicksort".
//
//  Computer processors usually have "on board" cache memories that provide an
//  added boost to Insertion Sort. Portions of the array to be sorted will be
//  stored in the (faster access) cache memory. If an array is nearly sorted
//  when Insertion Sort is called, then most of Insertion Sort's operations
//  will take place inside this high speed cache memory.
//
//  This version of Insertion Sort will sort the array RandNbrs[] in ascending
//  order. When the routine is finished the lowest number in the RandNbrs[]
//  array will be found in position RandNbrs[1] while the largest number will
//  be in position RandNbrs[Nbr2Sort].
//
//  The algorithm starts with some number in place at RandNbrs[1]. Then it
//  moves down to array position 2. The number at position 2 is copied to a
//  temporary holding position in variable "temp". Then all numbers that are in
//  lower index positions in the array and are also larger than what is in
//  "temp" are moved down one position. When a smaller number is encountered,
//  then the number in "temp" is inserted back into the array.
//
//  Next the algorithm works on the number in array position 3. Again all
//  numbers that are larger than what is in "temp" are moved down one position.
//  When a smaller (or equal) number is encounter, the number in "temp" is
//  inserted back into the empty space.
//
//  The algorithm continues in this fashion until it reaches the bottom of
//  the list to be sorted.
//
//************ **************************** ******************* ******************

void InsertionSort(void) {

  int i, j, k;
  unsigned temp;
  double Time1, Time2;

  puts("\nStarting Insertion Sort");

  Time1 = (double)clock();                      //  Save time to 0.001 sec.
  Time1 = Time1/(double)CLOCKS_PER_SEC;

  RandNbrs[0] = 0;            //  Sentinel for sort. Must be <= the lowest
                              //  value that will be sorted. Using a sentinel
                              //  speeds up the algorithm since an additional
                              //  run-off-the-"0"-end-of-the-array test will
                              //  not be needed.

  for (i = 2; i <= Nbr2Sort; i++) {
    k = i;
    j = i - 1;
    temp = RandNbrs[k];
    while (RandNbrs[j] > temp) {
      RandNbrs[k] = RandNbrs[j];
      j--;
      k--;
    }
    RandNbrs[k] = temp;
  }

  Time2 = (double)clock();
  Time2 = Time2/(double)CLOCKS_PER_SEC;

  printf("\nThe time to sort %'d items via Insertion Sort was %g seconds.\n",
    Nbr2Sort, Time2 - Time1);

  PauseMsg();

  puts("\nDo you want to see the random numbers after they were sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    for (i = 1; i <= Nbr2Sort; i++) {
      printf("%4d) %'16u\n", i, RandNbrs[i]);
      if (!(i%20))
        PauseMsg();
    }
    PauseMsg();
  }
}





//************** ******************** *********************** ***************
//
//                                MLLsort
//
//  If the numbers to be sorted are within a known range, and if on average
//  they are distributed approximately evenly, and if you have lots of extra
//  random access memory, then a multiple link list sort may be faster than
//  all other sorting algorithms. In this application, the numbers to be sorted
//  are in the array RandNbrs[]. The sorting algorithm never moves them.
//
//  Instead, for each element to be sorted, its proper sort location is
//  calculated as an index into the MLLlinks[] array. This initial calculated
//  link head address is equal to the sum of the Nbr2Sort plus a calculated
//  distance beyond this index number. The calculated distance can be varied to
//  see what produces the best result.
//
//  The correlation between the RandNbrs[] array and the MLLlinks[] array
//  looks like:
//
//            ------------------------
//  RandNbrs |    |    |    |    |    |
//            ------------------------
//              0    1    2    Nbr2Sort
//
//            --------------------------- -----------------------------------
//  MLLlinks |Link lists for each grp |       Link head for each group      |
//            ---------------------------- ----------------------------------
//             0     1   2     Nbr2Sort  Calculated pos. for each random nbr.
//
//  Once a calculated address is known, a link list is started using this
//  number's calculated addess. If any subsequent RandNbrs[] element ends up
//  using this same calculated address, it is added to the link list for this
//  address. The links in any link list are adjusted for these additions such
//  that the access order will be in sorted order.
//
//*************** ********************** *********************** *************

void MLLsort(void) {

  int HeadBase, i, count;
  unsigned NbrHeads, ShiftAmt, value;
  unsigned ptr1, ptr2;
  double Time1, Time2;

  puts("\nStarting Multiple Link List Sort");

  Time1 = (double)clock();                  //  Save time to 0.001 sec.
  Time1 = Time1/(double)CLOCKS_PER_SEC;

  RandNbrs[0] = 4294967295;                 //  Sentinel for sort.
                                            //  Must be >= the largest
                                            //  number to be sorted.
  HeadBase = Nbr2Sort + 1;
  NbrHeads = 4;                             //  Calculate how many list
  ShiftAmt = 30;                            //  heads and how many bit
  while (NbrHeads < Nbr2Sort) {             //  positions to shift for
    ShiftAmt--;                             //  indexing.
    NbrHeads <<= 1;
  }
                                            //  Optional debug info
//  printf("With %'d numbers to sort the calculated value for NbrHeads is %'d\n",
//        Nbr2Sort, NbrHeads);

  for (i = Nbr2Sort + NbrHeads; i >= HeadBase; i--)
    MLLlinks[i] = 0;                        //  Zero out the link heads. Note:
                                            //  If you are only going to run
                                            //  this routine once, you can take
                                            //  advantage of the built in
                                            //  initialization routines in C
                                            //  and ignore this step.

  for (i = Nbr2Sort; i; i--) {              //  For all input numbers.
    value = RandNbrs[i];                    //  Will calculate where it should
    ptr1 = value;                           //  go. Construct index.
    ptr1 >>= ShiftAmt;
    ptr1 += HeadBase;
                                            //  Search link list to see where
                                            //  to insert this element. Most of
                                            //  the time the new value will be
                                            //  the 1st in the list.
    for (ptr2 = MLLlinks[ptr1]; RandNbrs[ptr2] < value;
        ptr1 = ptr2, ptr2 = MLLlinks[ptr2]);

        //  Note: The average length of these link lists does not increase as
        //  "Nbr2Sort" increases. The processing time per sort element is
        //  a constant that is independent of "Nbr2Sort". Thus the algorithm
        //  runs in pure linear time and not something slower such as
        //  N*log(log(N)) time.

    MLLlinks[ptr1] = i;                     //  Insert location of new
    MLLlinks[i] = ptr2;                     //  value in link list.
  }

  Time2 = (double)clock();
  Time2 = Time2/(double)CLOCKS_PER_SEC;

  printf("\nThe time to sort %'d items via MLL Sort was %g seconds.\n",
    Nbr2Sort, Time2 - Time1);

  PauseMsg();

  puts("\nDo you want to see the random numbers after they were sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    count = 0;
    ptr2 = Nbr2Sort + NbrHeads;
    for (i = HeadBase; i <= ptr2; i++) {
      for (ptr1 = MLLlinks[i]; ptr1; ptr1 = MLLlinks[ptr1]) {
        count++;
        printf("%4d) %'16u\n", count,  RandNbrs[ptr1]);
        if (!(count%20))
          PauseMsg();
      }
    }
    PauseMsg();
  }
}




//*************** ******************** ************************ ******************
//
//                                    QuickSort
//
//  QuickSort has long had a reputation for being the fastest general purpose
//  sort algorithm. It is also perhaps the most difficult to code, and is
//  subject to sharply adverse execution time if the "pivot values" are picked
//  poorly - which can happen if the data to be sorted is already partially
//  sorted.
//
//  The algorithm works by picking one of the elements to be sorted as a "pivot
//  value". The list of items to be sorted is then partitioned so that all
//  elements that have a value less than the pivot end up in the front portion
//  of the array while all elements that are greater than the pivot value end
//  up in the other end. (Elements that are equal to the pivot could end up in
//  either section.) After the first pass, the array of items to be sorted
//  looks like:

//
//                                     Pivot
//             Low elements are here   Item     Higher elements are here
//            -----------------------------------------------------------
//  RandNbrs |    |    |    |    |    |    |    |    |    |    |    |    |
//            -----------------------------------------------------------
//              1    2    3    4                                  Nbr2Sort

//
//  After round 1, the QuickSort process is applied to both of the 2 subgroups.
//  Whichever subgroup was smaller is processed immediately while the location
//  of the left and right ends of the larger subgroup are placed on a stack
//  for later processing. This processing order will guarantee that the stack
//  will never exceed Log2(Nbr2Sort) items.
//
//  The repetitive processing of subgroups continues until the size of a
//  subgroup falls below a size defined by "MinGroup". Once a subgroup is
//  smaller than this, it is not sorted further by QuickSort. Small groups can
//  be processed faster by Insertion Sort. When Quicksort has reduced all
//  subgroups to < "MinGroup" size, control passes to "Insertion Sort" for a
//  final pass through the entire array.
//
//  In the "old days", the optimal size for "MinGroup" was about 18. The cache
//  memory on current processor chips reduces the time to access anything in
//  the cache - which includes the part of the array that is currently residing
//  in the cache. This greatly increases the efficiency of the final "Insertion
//  Sort" relative to the quicksort portion. Thus, significantly larger values
//  for "MinGroup" work better when a cache is being used. (You can experiment
//  with the value that is assigned to "MinGroup".)
//
//  Selection of the "pivot value" is crucial to the efficiency of Quicksort.
//  If the pivot value is selected so that it evenly partitions a subgroup,
//  then Quicksort is very efficient. On the other hand, if the value of the
//  "Pivot item" is near either the lowest or highest values that are going to
//  be partitioned within any subgroup, that particular round of Quicksort will
//  not do its job of quickly spliting the subgroups into ever smaller sizes.
//
//  The "median of three" portion of the routine is an effort to pick a good
//  "pivot value". If a "pivot value" can be picked so that it exactly splits a
//  subgroup into 2 equal portions, then Quicksort will be as efficient as
//  possible. An effort is made to do this by trying to find a value which is
//  close to the median of the subgroup. This is done by checking the values at
//  the second, last, and middle positions within a subgroup. The middle value
//  of these three is used as the "pivot value" while the two extremes are
//  placed at the two ends of the subgroup.
//
//  The code given here is based on a flier that Robert Sedgewick (author of
//  "Algorithms") handed out "a few years ago" during a 2-semester sequence of
//  "Analysis of Algorithms". (Professor Sedgewick is 2nd from the left in the
//  center photo at http://groups.yahoo.com/group/CSAtrium/)
//
//**************** ********************* ***********************  *****************

void QuickSort(void) {

  int    i, j, k, StackPtr;
  int    LeftEnd, RightEnd, LeftPtr, RightPtr, MidPtr, MinGroup;
  unsigned Pvalue, temp;
  double Time1, Time2;

  puts("\nStarting QuickSort");

  Time1 = (double)clock();                    //  Save time to 0.001 sec.
  Time1 = Time1/(double)CLOCKS_PER_SEC;

  RandNbrs[0] = 0;                            //  Sentinel for sort - used by
                                              //  by the Insertion Sort
                                              //  portion.

    //  Initialize left end, right end, stack pointer,
    //  and minimum size for subgroups.

  LeftEnd = 1;                                //  For the first round, the 2
  RightEnd = Nbr2Sort;                        //  ends will be the whole array
  MinGroup = 65;                              //  Years ago this would be ~18

  if (Nbr2Sort > MinGroup)                    //  Run quicksort until no
    StackPtr = 1;                             //  subgroup remains larger
  else StackPtr = 0;                          //  than "MinGroup" elements.


    //  Start quicksort. First, set the pivot value equal to the median of the
    //  array values at RandNbrs[LeftEnd+1], RandNbrs[(LeftEnd+RightEnd)/2],
    //  and RandNbrs[RightEnd]. The minimum of these 3 is placed at
    //  RandNbrs[LeftEnd+1] while the maximum is placed at RandNbrs[RightEnd].
    //  The value at RandNbrs[LeftEnd] is moved to
    //  RandNbrs[(LeftEnd+RightEnd)/2].

  while (StackPtr) {                          //  Loop until all subgroups
                                              //  are partitioned down to
                                              //  <= "MinGroup" size.
    LeftPtr = LeftEnd + 1;                    //  Ptr to left end.
    RightPtr = RightEnd;                      //  Ptr to right end.
    MidPtr = (LeftEnd + RightEnd)/2;          //  Point to middle

                                              //  Start sort of these 3
    if (RandNbrs[LeftPtr] > RandNbrs[RightPtr]) {
      temp = RandNbrs[LeftPtr];               //  elements
      RandNbrs[LeftPtr] = RandNbrs[RightPtr];
      RandNbrs[RightPtr] = temp;
    }

    if (RandNbrs[MidPtr] > RandNbrs[RightPtr]) {
      Pvalue = RandNbrs[RightPtr];
      RandNbrs[RightPtr] = RandNbrs[MidPtr];
    }
    else if (RandNbrs[MidPtr] < RandNbrs[LeftPtr]) {
      Pvalue = RandNbrs[LeftPtr];
      RandNbrs[LeftPtr] = RandNbrs[MidPtr];
    }
    else Pvalue = RandNbrs[MidPtr];
                                              //  The 3 values are sorted and
                                              //  and the median is in Pvalue
    RandNbrs[MidPtr] = RandNbrs[LeftEnd];     //  Fill in hole with LeftEnd

    //  Start the main loop. Move pointers inward until
    //  we find 2 elements that have to be exchanged.

    while (RandNbrs[++LeftPtr] < Pvalue);     //  Set up pointers
    while (RandNbrs[--RightPtr] > Pvalue);    //  for 1st exchange
    while (LeftPtr < RightPtr) {              //  Make these
      temp = RandNbrs[LeftPtr];               //  statements as
      RandNbrs[LeftPtr] = RandNbrs[RightPtr]; //  efficient as
      RandNbrs[RightPtr] = temp;              //  possible.
      while (RandNbrs[++LeftPtr] < Pvalue);   //  Continue this loop until
      while (RandNbrs[--RightPtr] > Pvalue);  //  the pointers cross.
    }

    RandNbrs[LeftEnd] = RandNbrs[RightPtr];   //  After pointers cross, fill
    RandNbrs[RightPtr] = Pvalue;              //  left end and middle hole.

    //  All values to the left of RandNbrs[RightPtr] are <= Pvalue while all to
    //  the right are >= Pvalue. Next, test the 2 subgroups on either side to
    //  see if they are still larger than the minimum efficient size. If both
    //  are still too large, then place the larger one on the stack and
    //  partition the smaller. If only one needs partitioning, then partition
    //  it, otherwise get the left and right ends of a subgroup stored on the
    //  stack in an earlier operation.

                                              //  Move RightPtr into
    RightPtr--;                               //  unsorted left subgroup

    if (RightPtr < MidPtr) {                  //  If left SubGroup is smaller
      if (RightPtr - LeftEnd > MinGroup) {    //  If both are large then put
        QSleft[StackPtr] = LeftPtr;           //  right side on the stack
        QSright[StackPtr] = RightEnd;         //  and sort the left side.
        RightEnd = RightPtr;
        ++StackPtr;                           //  Ready for next subgroup
      }
      else if (RightEnd - LeftPtr > MinGroup) //  Else if just have to
        LeftEnd = LeftPtr;                    //  sort the right side
      else {                                  //  Else neither gets sorted. Get a
        LeftEnd = QSleft[--StackPtr];         //  prior subgroup from the stack.
        RightEnd = QSright[StackPtr];         //  (Will be garbage if all
      }                                       //  subgroups are sorted)
    }                                         //  End of "if left is smaller"

    else {                                    //  Else left side is larger
      if (RightEnd - LeftPtr > MinGroup) {    //  If both sides are large
        QSleft[StackPtr] = LeftEnd;           //  then put left side on
        QSright[StackPtr] = RightPtr;         //  the stack
        LeftEnd = LeftPtr;                    //  and sort the right side
        ++StackPtr;                           //  Ready for next subgroup
      }
      else if (RightPtr - LeftEnd > MinGroup) //  else if left side is
        RightEnd = RightPtr;                  //  too large, then sort it.
      else {                                  //  Else neither gets sorted. Get a
        LeftEnd = QSleft[--StackPtr];         //  prior subgroup from the stack
        RightEnd = QSright[StackPtr];         //  (Will be garbage if all
      }                                       //  subgroups are sorted).
    }                                         //  End of "if left is larger"
  }                                           //  Repeat until all subgroups are
                                              //  small.

                                              //  Finish up with "Insertion Sort"
  for (i = 2; i <= Nbr2Sort; i++) {
    k = i;
    j = i - 1;
    temp = RandNbrs[k];
    while (RandNbrs[j] > temp) {
      RandNbrs[k] = RandNbrs[j];
      j--;
      k--;
    }
    RandNbrs[k] = temp;
  }

  Time2 = (double)clock();
  Time2 = Time2/(double)CLOCKS_PER_SEC;

  printf("\nThe time to sort %'d items via QuickSort was %g seconds.\n",
    Nbr2Sort, Time2 - Time1);


  PauseMsg();

  puts("\nDo you want to see the random numbers after they were sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    for (i = 1; i <= Nbr2Sort; i++) {
      printf("%4d %'16u\n", i, RandNbrs[i]);
      if (!(i%20))
        PauseMsg();
    }
    PauseMsg();
  }
}




//*************** ******************* ************************* ******************
//
//                                    ShellSort
//
//  If you are only going to learn how one sorting algorithm works, concentrate
//  on Shell Sort. On most arrays that you are ever going to work with it is
//  nearly as fast as Quicksort, and it is much easier to code
//  (and understand).
//
//  Shell Sort is based on Insertion Sort. In fact, if you are working on a
//  very small group of numbers, it is exactly the same as Insertion Sort. If
//  you are sorting a larger group, then it is just an expansion of Insertion
//  Sort. Shell Sort breaks down large arrays into a series of small sections
//  which it then treats as though they were "Insertion Sort".
//
//
//            -------------------------- -------------------------------------
//  RandNbrs | A | B | C | D | A | B | C  | D | A | B | C | D | A | B | C | D |
//            -------------------------- -------------------------------------
//             1   2   3   4   5   6   7    8   9   10  11  12  13  14  15  16
//
//  For example, let's assume you are going to sort 16 elements and are using
//  the 1, 4, 13, etc. sequence for "gap" sizes. The algorithm will first use a
//  "Gap" size of 4. It will run 4 separate simultaneous "Insertion Sorts". One
//  of these will be an "Insertion Sort" on the numbers in the "A" positions.
//  The other 3 "Insertion Sorts" will use the "B", "C", and "D" groups. When
//  the "Gap = 4" process is finished, the array will be roughly sorted with
//  most of the small value numbers concentrated near the low end of the array.
//  Similarly, most of large value numbers will be concentrated near the high
//  end of the array. The algorithm then continues with a "Gap" size of 1. This
//  is essentially an ordinary "Insertion Sort", and "Insertion Sort" is very
//  efficient on arrays that are nearly sorted.
//
//  If the number of elements to be sorted is still larger, then the elements
//  are initially sorted using a "gap" size of 13. This is followed by a "gap"
//  of 4 (as above) and finally a "gap" of 1. Still larger lists that are to be
//  sorted will use larger initial "Gap" sizes, and then gradually decrease the
//  "Gap" size as progress is made.
//
//  The efficiency of Shell Sort can be fine tuned by changing the sequence of
//  "gap" sizes. The 1, 4, 13, 40... series was originally sugested by Knuth.
//  Two other series are better candidates for the gap sizes. The user can
//  experiment with a choice of:
//  1)  1, 3, 7, 21, 48...
//  2)  1, 4, 13, 40...
//  3)  1, 8, 23, 77, 281, 1073...
//
//**************** *********************** *********************** ***************

void ShellSort(void) {

  int choice, i, j, k, GapPtr, Gap;
  unsigned temp;
  double Time1, Time2;

  puts("\nYou may experiment with the gap sizes that are used in Shell Sort");
  puts("\nPick one of the following sequences for the gap size.");
  puts("(Any other number cancels Shell Sort)\n");

  puts("1)  1, 3, 7, 21, 48, 112,...");
  puts("2)  1, 4, 13, 40, 121, 364,...");
  puts("3)  1, 8, 23, 77, 281, 1073,...");

  gets(Databuff);
  choice = atoi(Databuff);

  if (choice == 1) {                    //  Copy one of the three gap sequences
    for (i = 1; i <= 20; i++)
      Gaps[i] = Gaps13[i];
  }
  else if (choice == 2) {
    for (i = 1; i <= 20; i++)
      Gaps[i] = Gaps14[i];
  }
  else if (choice == 3) {
    for (i = 1; i <= 20; i++)
      Gaps[i] = Gaps18[i];
  }
  else return;

  puts("\nStarting Shell Sort");

  Time1 = (double)clock();                        //  Save time to 0.001 sec.
  Time1 = Time1/(double)CLOCKS_PER_SEC;

  temp = Nbr2Sort/3;                              //  Set up GapPtr
  for (GapPtr = 1; Gaps[GapPtr] < temp; GapPtr++);
  GapPtr--;
  Gap = Gaps[GapPtr];

  do {
    for (i = Gap + 1; i <= Nbr2Sort; i++) {
      temp = RandNbrs[i];
      k = i;
      for (j = i - Gap; j > 0; j -= Gap) {
        if (RandNbrs[j] <= temp)
          break;
        RandNbrs[k] = RandNbrs[j];
        k = j;
      }
      RandNbrs[k] = temp;
    }
  } while (Gap = Gaps[--GapPtr]);

  Time2 = (double)clock();
  Time2 = Time2/(double)CLOCKS_PER_SEC;

  printf("\nThe time to sort %'d items via Shell Sort was %g seconds.\n",
    Nbr2Sort, Time2 - Time1);

  PauseMsg();

  puts("\nDo you want to see the random numbers after they were sorted (Y or N)?");
  gets(Databuff);
  if (tolower(Databuff[0]) == 'y') {
    for (i = 1; i <= Nbr2Sort; i++) {
      printf("%4d) %'16u\n", i, RandNbrs[i]);
      if (!(i%20))
        PauseMsg();
    }
    PauseMsg();
  }
}




//*************** ******************** ****************** **************
//
//                    Misc routines
//
//*************** ******************** ****************** **************

void PauseMsg(void) {

  puts("\nPress RETURN to continue.");
  gets(Databuff);
}