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);
}