home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / pascal / sortdemo.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1988-11-19  |  26.7 KB  |  834 lines

  1. (*                   SORTDEMO
  2.  * This    program    graphically demonstrates six common sorting algorithms.     It
  3.  * prints 25 or    43 horizontal bars, all    of different lengths and all in    random
  4.  * order, then sorts the bars from smallest to longest.
  5.  *
  6.  * The program also uses sound statements to generate different pitches,
  7.  * depending on the location of the bar being printed. Note that the sound
  8.  * statements delay the    speed of each sorting algorithm    so you can follow
  9.  * the progress    of the sort. Therefore,    the times shown    are for    comparison
  10.  * only. They are not an accurate measure of sort speed.
  11.  *
  12.  * If you use these sorting routines in    your own programs, you may notice
  13.  * a difference    in their relative speeds (for example, the exchange
  14.  * sort    may be faster than the shell sort) depending on    the number of
  15.  * elements to be sorted and how "scrambled" they are to begin with.
  16.  *)
  17.  
  18. PROGRAM    SortDemo;
  19.  
  20. CONST
  21.     INCL_SUB           = 1;       (* Include KBD, VIO, and MOU definitions *)
  22.     INCL_DOSDATETIME   = 1;       (* DOS date/time definitions    *)
  23.     INCL_DOSPROCESS    = 1;       (* Some DOS process definitions *)
  24.     INCL_NOCOM           = 1;       (* Don't include default sections of DOS *)
  25.  
  26. (*
  27.  * Define the data type used to hold the information for each colored bar:
  28.  *)
  29. TYPE
  30.     SortType = RECORD
  31.         Length: BYTE;    (* Bar length (the element compared
  32.                  * in the different sorts)           *)
  33.         ColorVal: BYTE;    (* Bar color                   *)
  34.            END;
  35.  
  36.     CELLINFO = RECORD
  37.         Char: BYTE;
  38.         Attr: BYTE;
  39.        END;
  40.  
  41. (* Declare global constants:
  42.  *)
  43. CONST
  44.     BLOCK = 223;
  45.     ESC    = CHR(27);
  46.     FIRSTMENU =    1;
  47.     LEFTCOLUMN = 48;
  48.     NLINES = 18;
  49.     NULL = 0;
  50.     SPACE = 32;
  51.     WIDTH = 80 - LEFTCOLUMN;
  52.     WHITE = 15;
  53.  
  54. (* Declare global variables, and allocate storage space    for them.  SortArray
  55.  * and SortBackup are both arrays of the data type SortType defined above:
  56.  *)
  57. VAR
  58.     sTime,wTime: _DATETIME;
  59.     KeyInfo: _KBDKEYINFO;
  60.     wMode: _VIOMODEINFO;
  61.     SortArray, SortBackup: ARRAY[1..43]    OF SortType;
  62.     Menu: ARRAY[1..NLINES] OF LSTRING(30);
  63.     Sound: BOOLEAN;
  64.     curSelect, MaxBars,    MaxColors: INTEGER;
  65.     oTime, nTime, Pause, RandSeed: INTEGER4;
  66.     ret: WORD;
  67.  
  68. (* Data    statements for the different options printed in    the sort menu:
  69.  *)
  70.  
  71. VALUE
  72.     Menu[1] := '     PASCAL Sorting Demo';
  73.     Menu[2] := ' ';
  74.     Menu[3] := 'Insertion';
  75.     Menu[4] := 'Bubble';
  76.     Menu[5] := 'Heap';
  77.     Menu[6] := 'Exchange';
  78.     Menu[7] := 'Shell';
  79.     Menu[8] := 'Quick';
  80.     Menu[9] := ' ';
  81.     Menu[10] := 'Toggle Sound: ';
  82.     Menu[11] := ' ';
  83.     Menu[12] := 'Pause Factor: ';
  84.     Menu[13] := '<   (Slower)';
  85.     Menu[14] := '>   (Faster)';
  86.     Menu[15] := ' ';
  87.     Menu[16] := 'Type first character of';
  88.     Menu[17] := 'choice ( I B H E S Q T < > )';
  89.     Menu[18] := 'or ESC key to end program: ';
  90.     wMode.cb :=    SIZEOF(wMode);
  91.  
  92. FUNCTION GETMQQ    (Wants:WORD):ADSMEM; EXTERN;
  93. FUNCTION RandInt (Lower,Upper:INTEGER):INTEGER;    FORWARD;
  94. PROCEDURE BoxInit; FORWARD;
  95. PROCEDURE BubbleSort; FORWARD;
  96. PROCEDURE DrawFrame (Top,Left,Width,Height:INTEGER); FORWARD;
  97. PROCEDURE ElapsedTime (CurrentRow:INTEGER); FORWARD;
  98. PROCEDURE ExchangeSort;    FORWARD;
  99. PROCEDURE HeapSort; FORWARD;
  100. PROCEDURE Initialize; FORWARD;
  101. PROCEDURE InsertionSort; FORWARD;
  102. PROCEDURE PercolateDown    (MaxLevel:INTEGER); FORWARD;
  103. PROCEDURE PercolateUp (MaxLevel:INTEGER); FORWARD;
  104. PROCEDURE PrintOneBar (Row:INTEGER); FORWARD;
  105. PROCEDURE QuickSort (Low,High:INTEGER);    FORWARD;
  106. PROCEDURE Reinitialize;    FORWARD;
  107. PROCEDURE ShellSort; FORWARD;
  108. FUNCTION Screen    (ACTION:BYTE):BOOLEAN; FORWARD;
  109. PROCEDURE SortMenu; FORWARD;
  110. PROCEDURE SwapBars (Row1,Row2:INTEGER);    FORWARD;
  111. PROCEDURE cls; FORWARD;
  112. PROCEDURE swaps    (VAR one, two:SortType); FORWARD;
  113.  
  114. (* =============================== BoxInit ====================================
  115.  *    Calls the    DrawFrame procedure to draw the    frame around the sort menu,
  116.  *    then prints the different    options    stored in the Menu array.
  117.  * ============================================================================
  118.  *)
  119. PROCEDURE BoxInit;
  120. VAR
  121.     Color: BYTE;
  122.     i: INTEGER;
  123.     Factor: LSTRING(3);
  124.  
  125. BEGIN
  126.     Color := WHITE;
  127.     DrawFrame(1, LEFTCOLUMN - 3, WIDTH + 3, 22);
  128.  
  129.     FOR i := 1 TO NLINES DO
  130.         ret := VioWrtCharStrAtt(ads Menu[i,1], Menu[i].len,
  131.                    FIRSTMENU + i, LEFTCOLUMN, Color, 0);
  132.  
  133.    (* Print the    current    value for Sound:
  134.     *)
  135.     IF (Sound) THEN
  136.         ret := VioWrtCharStrAtt(ads 'ON ',3, 11, LEFTCOLUMN + 14, Color, 0)
  137.     ELSE
  138.         ret := VioWrtCharStrAtt(ads 'OFF',3, 11, LEFTCOLUMN + 14, Color, 0);
  139.  
  140.     EVAL(ENCODE(Factor,Pause DIV 30:3));
  141.     ret := VioWrtCharStrAtt(ads Factor[1], 3, 13, LEFTCOLUMN + 14, Color, 0);
  142.  
  143.    (* Erase the    speed option if    the length of the Pause    is at a    limit
  144.     *)
  145.     IF (Pause =    900) THEN
  146.         ret := VioWrtCharStrAtt(ads '            ',12,14,LEFTCOLUMN,Color,0)
  147.     ELSE IF (Pause = 0)    THEN
  148.         ret := VioWrtCharStrAtt(ads '            ',12,15,LEFTCOLUMN,Color,0);
  149.  
  150. END;
  151.  
  152. (* ============================== BubbleSort ==================================
  153.  *    The BubbleSort algorithm cycles through SortArray, comparing adjacent
  154.  *    elements and swapping pairs that are out of order.  It continues to
  155.  *    do this until no pairs are swapped.
  156.  * ============================================================================
  157.  *)
  158. PROCEDURE BubbleSort;
  159. VAR
  160.     Row, Switch, Limit:    INTEGER;
  161.  
  162. BEGIN
  163.     Limit := MaxBars;
  164.     REPEAT
  165.     Switch := 0;
  166.     FOR Row    := 1 TO    Limit -    1 DO BEGIN
  167.  
  168.        (* Two adjacent elements are    out of order, so swap their values
  169.         * and redraw those two bars: *)
  170.         IF (SortArray[Row].Length >    SortArray[Row +    1].Length) THEN    BEGIN
  171.         swaps (SortArray[Row], SortArray[Row + 1]);
  172.         SwapBars (Row, Row + 1);
  173.         Switch := Row;
  174.         END;
  175.     END;
  176.  
  177.     (* Sort on next pass only to where the last    switch was made: *)
  178.     Limit := Switch;
  179.     UNTIL Switch = 0;
  180. END;
  181.  
  182. (* ============================== DrawFrame ===================================
  183.  *   Draws a rectangular frame using the high-order ASCII characters ╔ (201) ,
  184.  *   ╗ (187) , ╚ (200) , ╝ (188) , ║ (186) , and ═ (205). The parameters
  185.  *   TopSide, BottomSide, LeftSide, and    RightSide are the row and column
  186.  *   arguments for the upper-left and lower-right corners of the frame.
  187.  * ============================================================================
  188.  *)
  189. PROCEDURE DrawFrame {(Top, Left, Width,    Height)};
  190. CONST
  191.     ULEFT = 201;
  192.     URIGHT = 187;
  193.     LLEFT = 200;
  194.     LRIGHT = 188;
  195.     VERTICAL = 186;
  196.     HORIZONTAL = 205;
  197.     SPACE = ' ';
  198.  
  199. VAR
  200.     Attr: BYTE;
  201.     CellAttr, i, bottom, right:    INTEGER;
  202.     TempStr: STRING(80);
  203.  
  204. BEGIN
  205.       Attr := WHITE;
  206.       CellAttr := Attr * 256;
  207.       bottom :=    Top+Height-1;
  208.       right := Left+Width-1;
  209.  
  210.       ret := VioWrtNCell(ads (CellAttr OR ULEFT),1,Top,Left,0);
  211.       ret := VioWrtNCell(ads (CellAttr OR HORIZONTAL),Width-2,Top,Left+1,0);
  212.       ret := VioWrtNCell(ads (CellAttr OR URIGHT),1,Top,right,0);
  213.  
  214.       FILLSC(ads Tempstr,Width,CHR(SPACE));
  215.       Tempstr[1] := CHR(VERTICAL);
  216.       Tempstr[Width] :=    CHR(VERTICAL);
  217.       FOR i := 1 TO Height-2 DO
  218.           ret := VioWrtCharStrAtt(ads Tempstr,Width,i+Top,Left,Attr,0);
  219.  
  220.       ret := VioWrtNCell(ads (CellAttr OR LLEFT),1,bottom,Left,0);
  221.       ret := VioWrtNCell(ads (CellAttr OR HORIZONTAL),Width-2,bottom,Left+1,0);
  222.       ret := VioWrtNCell(ads (CellAttr OR LRIGHT),1,bottom,right,0);
  223. END;
  224.  
  225. (* ============================= ElapsedTime ==================================
  226.  *    Prints seconds elapsed since the given sorting routine started.
  227.  *    Note that    this time includes both    the time it takes to redraw the
  228.  *    bars plus    the pause while    the SOUND statement plays a note, and
  229.  *    thus is not an accurate indication of sorting speed.
  230.  * ============================================================================
  231.  *)
  232. PROCEDURE ElapsedTime {(CurrentRow)};
  233. VAR
  234.     Color: BYTE;
  235.     Timing: LSTRING(80);
  236.  
  237. BEGIN
  238.     Color := WHITE;
  239.  
  240.     ret := DosGetDateTime(ads wTime);
  241.  
  242.     nTime := (wTime.hours * 360000) +
  243.         (wTime.minutes * 6000) +
  244.         (wTime.seconds * 100) +
  245.          wTime.hundredths;
  246.  
  247.     EVAL(ENCODE(Timing,(nTime -    oTime) / 100:7:2));
  248.  
  249.     (* Print the number    of seconds elapsed *)
  250.     ret := VioWrtCharStrAtt(ads Timing[1], 7, curSelect + FIRSTMENU + 3,
  251.     LEFTCOLUMN + 15, Color,    0);
  252.  
  253.     IF (Sound) THEN
  254.         ret := DosBeep(60 * CurrentRow, 32);    (* Play a note. *)
  255.     ret := DosSleep(Pause);                     (* Pause. *)
  256.  
  257. END;
  258.  
  259. (* ============================= ExchangeSort =================================
  260.  *   The ExchangeSort compares each element in SortArray - starting with
  261.  *   the first element - with every following element.    If any of the
  262.  *   following elements    is smaller than    the current element, it    is exchanged
  263.  *   with the current element and the process is repeated for the next
  264.  *   element in    SortArray.
  265.  * ============================================================================
  266.  *)
  267. PROCEDURE ExchangeSort;
  268. VAR
  269.     Row, SmallestRow, j: INTEGER;
  270.  
  271. BEGIN
  272.     FOR    Row := 1 TO MaxBars - 1    DO BEGIN
  273.     SmallestRow := Row;
  274.     FOR j := Row + 1 TO MaxBars DO BEGIN
  275.         IF (SortArray[j].Length < SortArray[SmallestRow].Length) THEN BEGIN
  276.         SmallestRow := j;
  277.         ElapsedTime(j);
  278.         END;
  279.     END;
  280.        (* Found    a row shorter than the current row, so swap those
  281.     * two array elements: *)
  282.     IF (SmallestRow    > Row) THEN BEGIN
  283.         swaps (SortArray[Row], SortArray[SmallestRow]);
  284.         SwapBars (Row, SmallestRow);
  285.     END;
  286.     END;
  287.  
  288. END;
  289.  
  290. (* =============================== HeapSort ==================================
  291.  *  The    HeapSort procedure works by calling two    other procedures - PercolateUp
  292.  *  and    PercolateDown.    PercolateUp turns SortArray into a "heap," which has
  293.  *  the    properties outlined in the diagram below:
  294.  *
  295.  *                 SortArray(1)
  296.  *                 /        \
  297.  *              SortArray(2)         SortArray(3)
  298.  *             /        \         /        \
  299.  *       SortArray(4)      SortArray(5)     SortArray(6)  SortArray(7)
  300.  *        /       \       /       \       /      \     /    \
  301.  *      ...       ...     ...       ...     ...      ...  ...    ...
  302.  *
  303.  *
  304.  *  where each "parent node" is    greater    than each of its "child nodes";    for
  305.  *  example, SortArray(1) is greater than SortArray(2) or SortArray(3),
  306.  *  SortArray(3) is greater than SortArray(6) or SortArray(7), and so forth.
  307.  *
  308.  *  Therefore, once the    first FOR...NEXT loop in HeapSort is finished, the
  309.  *  largest element is in SortArray(1).
  310.  *
  311.  *  The    second FOR...NEXT loop in HeapSort swaps the element in    SortArray(1)
  312.  *  with the element in    MaxRow,    rebuilds the heap (with    PercolateDown) for
  313.  *  MaxRow - 1,    then swaps the element in SortArray(1) with the    element    in
  314.  *  MaxRow - 1,    rebuilds the heap for MaxRow - 2, and continues    in this    way
  315.  *  until the array is sorted.
  316.  * ===========================================================================
  317.  *)
  318. PROCEDURE HeapSort;
  319. VAR
  320.     i: INTEGER;
  321.  
  322. BEGIN
  323.     FOR    i := 2 TO MaxBars DO
  324.     PercolateUp (i);
  325.  
  326.     FOR    i := MaxBars DOWNTO 2 DO BEGIN
  327.     swaps (SortArray[1], SortArray[i]);
  328.     SwapBars (1, i);
  329.     PercolateDown (i - 1);
  330.     END;
  331. END;
  332.  
  333. (* ============================== Initialize =================================
  334.  *    Initializes the SortBackup array.     It also calls the BoxInit procedure.
  335.  * ===========================================================================
  336.  *)
  337. PROCEDURE Initialize;
  338. VAR
  339.     iTime: _DATETIME;
  340.     i, MaxIndex, Index,    BarLength: INTEGER;
  341.     TempArray: ARRAY [1..43] OF    INTEGER;
  342.  
  343. BEGIN
  344.     FOR    i := 1 TO MaxBars DO
  345.         TempArray[i] := i;
  346.  
  347.    (* If monochrome or color burst disabled, use one color *)
  348.     IF (((wMode.fbType AND VGMT_OTHER) <> 0) AND
  349.         ((wMode.fbType AND VGMT_DISABLEBURST) = 0))
  350.         MaxColors := 15;
  351.     ELSE
  352.         MaxColors := 1;
  353.  
  354.    (* Seed the random-number generator. *)
  355.     ret := DosGetDateTime(ads iTime);
  356.     RandSeed :=    (iTime.hours * 3600) +
  357.         (iTime.minutes * 60) +
  358.          iTime.seconds;
  359.     RandSeed :=    TRUNC4(RandSeed    / 86400.0 * 259199.0);
  360.  
  361.     MaxIndex :=    MaxBars;
  362.     FOR    i := 1 TO MaxBars DO BEGIN
  363.  
  364.        (* Find a random    element    in TempArray between 1 and MaxIndex,
  365.     * then assign the value    in that    element    to BarLength: *)
  366.     Index := RandInt(1,MaxIndex);
  367.     BarLength := TempArray[Index];
  368.  
  369.        (* Overwrite the    value in TempArray[Index] with the value in
  370.     * TempArray[MaxIndex] so the value in TempArray[Index] is
  371.     * chosen only once: *)
  372.     TempArray[Index] := TempArray[MaxIndex];
  373.  
  374.        (* Decrease the value of    MaxIndex so that TempArray[MaxIndex] can't
  375.     * be chosen on the next    pass through the loop: *)
  376.     MaxIndex := MaxIndex - 1;
  377.  
  378.     SortBackup[i].Length :=    BarLength;
  379.  
  380.         IF (MaxColors = 1) THEN
  381.             SortBackup[i].ColorVal := 7;
  382.         ELSE
  383.             SortBackup[i].ColorVal := (BarLength MOD MaxColors) + 1;
  384.     END;
  385.  
  386.     cls;
  387.     Reinitialize;      (* Assign values    in SortBackup to SortArray and draw *)
  388.              (* unsorted bars on the screen. *)
  389.     Sound := TRUE;
  390.     Pause := 30;       (* Initialize Pause.    *)
  391.     BoxInit;           (* Draw frame for the sort menu and print options. *)
  392.  
  393. END;
  394.  
  395. (* ============================= InsertionSort ===============================
  396.  *   The InsertionSort procedure compares the length of    each successive
  397.  *   element in    SortArray with the lengths of all the preceding    elements.
  398.  *   When the procedure    finds the appropriate place for    the new    element, it
  399.  *   inserts the element in its    new place, and moves all the other elements
  400.  *   down one place.
  401.  * ===========================================================================
  402.  *)
  403. PROCEDURE InsertionSort;
  404. VAR
  405.     j, Row, TempLength:    INTEGER;
  406.     TempVal: SortType;
  407.  
  408. BEGIN
  409.     FOR    Row := 2 TO MaxBars DO BEGIN
  410.     TempVal    := SortArray[Row];
  411.     TempLength := TempVal.Length;
  412.     FOR j := Row DOWNTO 2 DO BEGIN
  413.  
  414.        (* As long as the length of the j-1st element is greater than the
  415.         * length of    the original element in    SortArray(Row),    keep shifting
  416.         * the array    elements down: *)
  417.         IF (SortArray[j - 1].Length    > TempLength) THEN BEGIN
  418.         SortArray[j] :=    SortArray[j - 1];
  419.         PrintOneBar(j);            (* Print the new bar. *)
  420.         ElapsedTime(j);            (* Print the elapsed time. *)
  421.  
  422.          (*    Otherwise, exit: *)
  423.          END
  424.              ELSE
  425.         break;
  426.     END;
  427.  
  428.     (* Insert the original value of    SortArray(Row) in SortArray(j):    *)
  429.     SortArray[j] :=    TempVal;
  430.     PrintOneBar(j);
  431.     ElapsedTime(j);
  432.     END;
  433. END;
  434.  
  435. (* ============================    PercolateDown ================================
  436.  *   The PercolateDown procedure restores the elements of SortArray from 1 to
  437.  *   MaxLevel to a "heap" (see the diagram with    the HeapSort procedure).
  438.  * ===========================================================================
  439.  *)
  440. PROCEDURE PercolateDown    {(MaxLevel)};
  441. VAR
  442.     i, Child: INTEGER;
  443.  
  444. BEGIN
  445.     i := 1;
  446.    (* Move the value in    SortArray(1) down the heap until it has    reached
  447.     * its proper node (that is,    until it is less than its parent node
  448.     * or until it has reached MaxLevel,    the bottom of the current heap): *)
  449.     WHILE TRUE DO BEGIN
  450.     Child := 2 * i;           (* Get the subscript    for the    child node. *)
  451.  
  452.     (* Reached the bottom of the heap, so exit this    procedure: *)
  453.     IF (Child > MaxLevel) THEN
  454.         break;
  455.  
  456.     (* If there are    two child nodes, find out which    one is bigger: *)
  457.     IF (Child + 1 <= MaxLevel) THEN
  458.         IF (SortArray[Child    + 1].Length > SortArray[Child].Length) THEN
  459.         Child := Child+1;
  460.  
  461.        (* Move the value down if it is still not bigger    than either one    of
  462.     * its children:    *)
  463.     IF (SortArray[i].Length    < SortArray[Child].Length) THEN    BEGIN
  464.         swaps (SortArray[i], SortArray[Child]);
  465.         SwapBars (i, Child);
  466.         i := Child;
  467.  
  468.        (* Otherwise, SortArray has been    restored to a heap from    1 to
  469.     * MaxLevel, so exit: *)
  470.     END
  471.         ELSE
  472.         break;
  473.     END;
  474. END;
  475.  
  476. (* ============================== PercolateUp ================================
  477.  *   The PercolateUp procedure converts    the elements from 1 to MaxLevel    in
  478.  *   SortArray into a "heap" (see the diagram with the HeapSort    procedure).
  479.  * ===========================================================================
  480.  *)
  481. PROCEDURE PercolateUp {(MaxLevel)};
  482. VAR
  483.     i, Parent: INTEGER;
  484.  
  485. BEGIN
  486.     i := MaxLevel;
  487.    (* Move the value in    SortArray(MaxLevel) up the heap    until it has
  488.     * reached its proper node (that is,    until it is greater than either
  489.     * of its child nodes, or until it has reached 1, the top of    the heap): *)
  490.     WHILE (i <>    1) DO BEGIN
  491.     Parent := i DIV    2;         (*    Get the    subscript for the parent node. *)
  492.  
  493.        (* The value at the current node    is still bigger    than the value at
  494.     * its parent node, so swap these two array elements: *)
  495.     IF (SortArray[i].Length    > SortArray[Parent].Length) THEN BEGIN
  496.         swaps (SortArray[Parent], SortArray[i]);
  497.         SwapBars (Parent, i);
  498.         i := Parent;
  499.  
  500.        (* Otherwise, the element has reached its proper    place in the heap,
  501.     * so exit this procedure: *)
  502.     END
  503.     ELSE
  504.         break;
  505.     END;
  506. END;
  507.  
  508. (* ============================== PrintOneBar ================================
  509.  *  Prints SortArray(Row).BarString at the row indicated by the    Row
  510.  *  parameter, using the color in SortArray(Row).ColorVal.
  511.  * ===========================================================================
  512.  *)
  513. PROCEDURE PrintOneBar {(Row)};
  514. VAR
  515.     Cell: CELLINFO;
  516.     NumSpaces: INTEGER;
  517.  
  518. BEGIN
  519.     Cell.Attr := SortArray[Row].ColorVal;
  520.     Cell.Char := BLOCK;
  521.     ret := VioWrtNCell(ads Cell,SortArray[Row].Length,Row,1,0);
  522.     NumSpaces := MaxBars - SortArray[Row].Length;
  523.     IF NumSpaces > 0 THEN
  524.     Cell.Char := SPACE;
  525.         ret := VioWrtNCell(ads Cell,NumSpaces,Row,SortArray[Row].Length+1,0);
  526. END;
  527.  
  528. (* ============================== QuickSort ==================================
  529.  *   QuickSort works by    picking    a random "pivot" element in SortArray, then
  530.  *   moving every element that is bigger to one    side of    the pivot, and every
  531.  *   element that is smaller to    the other side.     QuickSort is then called
  532.  *   recursively with the two subdivisions created by the pivot.  Once the
  533.  *   number of elements    in a subdivision reaches two, the recursive calls end
  534.  *   and the array is sorted.
  535.  * ===========================================================================
  536.  *)
  537. PROCEDURE QuickSort {(Low, High)};
  538. VAR
  539.     i, j, RandIndex, Partition:    INTEGER;
  540.  
  541. BEGIN
  542.     IF (Low < High) THEN BEGIN
  543.  
  544.        (* Only two elements in this subdivision; swap them if they are out of
  545.     * order, then end recursive calls: *)
  546.     IF ((High - Low) = 1) THEN BEGIN
  547.         IF (SortArray[Low].Length >    SortArray[High].Length)    THEN BEGIN
  548.         swaps (SortArray[Low], SortArray[High]);
  549.         SwapBars (Low, High);
  550.         END;
  551.     END
  552.         ELSE BEGIN
  553.         Partition := SortArray[High].Length;
  554.         i := Low;
  555.         j := High;
  556.         WHILE i < j    DO BEGIN
  557.  
  558.         (* Move    in from    both sides towards the pivot element: *)
  559.         WHILE ((i < j) AND (SortArray[i].Length    <= Partition)) DO
  560.             i := i + 1;
  561.  
  562.         WHILE ((j > i) AND (SortArray[j].Length    >= Partition)) DO
  563.             j := j - 1;
  564.  
  565.            (* If we    haven't reached the pivot element, it means that two
  566.         * elements on either side are out of order, so swap them: *)
  567.         IF (i <    j) THEN    BEGIN
  568.             swaps (SortArray[i], SortArray[j]);
  569.             SwapBars (i, j);
  570.         END;
  571.         END;
  572.  
  573.        (* Move the pivot element back to its proper    place in the array: *)
  574.         swaps (SortArray[i], SortArray[High]);
  575.         SwapBars (i, High);
  576.  
  577.        (* Recursively call the QuickSort procedure (pass the smaller
  578.         * subdivision first    to use less stack space): *)
  579.         IF ((i - Low) < (High - i))    THEN BEGIN
  580.         QuickSort (Low,    i - 1);
  581.         QuickSort (i + 1, High);
  582.         END
  583.         ELSE BEGIN
  584.         QuickSort (i + 1, High);
  585.         QuickSort (Low,    i - 1);
  586.         END;
  587.     END;
  588.     END;
  589. END;
  590.  
  591. (* =============================== RandInt ===================================
  592.  *   Returns a random integer greater than or equal to the Lower parameter
  593.  *   and less than or equal to the Upper parameter.
  594.  * ===========================================================================
  595.  *)
  596. FUNCTION RandInt {(Lower, Upper)};
  597. BEGIN
  598.     RandSeed :=    (RandSeed*7141+54773) MOD 259200;
  599.     RandInt := ORD(Lower + ((Upper - Lower + 1)    * RandSeed) DIV    259200);
  600. END;
  601.  
  602. (* ============================== Reinitialize ===============================
  603.  *   Restores the array    SortArray to its original unsorted state, then
  604.  *   prints the    unsorted color bars.
  605.  * ===========================================================================
  606.  *)
  607. PROCEDURE Reinitialize;
  608. VAR
  609.     Row: INTEGER;
  610. BEGIN
  611.     FOR    Row := 1 TO MaxBars DO BEGIN
  612.     SortArray[Row] := SortBackup[Row];
  613.     PrintOneBar(Row);
  614.     END;
  615.  
  616.     ret := DosGetDateTime(ads sTime);
  617.     oTime := (sTime.hours * 360000) +
  618.         (sTime.minutes * 6000) +
  619.         (sTime.seconds * 100) +
  620.          sTime.hundredths;
  621. END;
  622.  
  623. FUNCTION Screen    {(ACTION)};
  624. VAR [STATIC]
  625.     Mode: _VIOMODEINFO;
  626.     CellStr: ADSMEM;
  627.     Row,Col,Length: WORD;
  628. BEGIN
  629.     if(ACTION=1) THEN BEGIN
  630.     Mode.cb    := sizeof(Mode);
  631.         ret := VioGetMode(ads Mode,0);
  632.     Length := 2*Mode.row*Mode.col;
  633.     CellStr    := GETMQQ(Length);
  634.     if(CellStr.r = NULL) THEN BEGIN    Screen := FALSE;return;END;
  635.         ret := VioReadCellStr(CellStr,Length,0,0,0);
  636.         ret := VioGetCurPos(Row,Col,0);
  637.     END
  638.     ELSE BEGIN
  639.         ret := VioSetMode(ads Mode,0);
  640.     if(CellStr.r = NULL) THEN BEGIN    Screen := FALSE;return;END;
  641.         ret := VioWrtCellStr(CellStr,Length,0,0,0);
  642.         ret := VioSetCurPos(Row,Col,0);
  643.     END;
  644.     Screen := TRUE;
  645. END;
  646.  
  647. (* =============================== ShellSort =================================
  648.  *  The    ShellSort procedure is similar to the BubbleSort procedure.  However,
  649.  *  ShellSort begins by    comparing elements that    are far    apart (separated by
  650.  *  the    value of the Offset variable, which is initially half the distance
  651.  *  between the    first and last element), then comparing    elements that are
  652.  *  closer together (when Offset is one, the last iteration of this procedure
  653.  *  is merely a    bubble sort).
  654.  * ===========================================================================
  655.  *)
  656. PROCEDURE ShellSort;
  657. VAR
  658.     Offset, Switch, Limit, Row:    INTEGER;
  659.  
  660. BEGIN
  661.     (* Set comparison offset to    half the number    of records in SortArray: *)
  662.     Offset := MaxBars DIV 2;
  663.  
  664.     WHILE (Offset>0) DO    BEGIN      (* Loop until    offset gets to zero. *)
  665.     Limit := MaxBars - Offset;
  666.     REPEAT
  667.         Switch := 0;    (* Assume no switches at this offset. *)
  668.  
  669.      (* Compare elements and switch    ones out of order: *)
  670.         FOR    Row := 1 TO Limit DO
  671.         IF (SortArray[Row].Length > SortArray[Row + Offset].Length) THEN BEGIN
  672.             swaps (SortArray[Row], SortArray[Row + Offset]);
  673.             SwapBars (Row, Row + Offset);
  674.             Switch := Row;
  675.         END;
  676.  
  677.         (* Sort on next pass only to where last switch was made: *)
  678.         Limit := Switch - Offset;
  679.     UNTIL Switch = 0;
  680.  
  681.        (* No switches at last offset, try one half as big: *)
  682.     Offset := Offset DIV 2;
  683.     END;
  684. END;
  685.  
  686. (* =============================== SortMenu ==================================
  687.  *   The SortMenu procedure first calls    the Reinitialize procedure to make
  688.  *   sure the SortArray    is in its unsorted form, then prompts the user to
  689.  *   make one of the following choices:
  690.  *
  691.  *         * One of the sorting algorithms
  692.  *         * Toggle sound    on or off
  693.  *         * Increase or decrease    speed
  694.  *         * End the program
  695.  * ===========================================================================
  696.  *)
  697. PROCEDURE SortMenu;
  698. BEGIN
  699.     WHILE TRUE DO BEGIN
  700.  
  701.         ret := VioSetCurPos(FIRSTMENU + NLINES, LEFTCOLUMN + Menu[NLINES].len, 0);
  702.  
  703.         ret := KbdCharIn(ads KeyInfo, 0, 0);
  704.         IF (CHR(KeyInfo.chChar) >= 'a') AND (CHR(KeyInfo.chChar) <= 'z') THEN
  705.         KeyInfo.chChar := KeyInfo.chChar - 32;
  706.  
  707.     (* Branch to the appropriate procedure depending on the    key typed: *)
  708.     CASE CHR(KeyInfo.chChar) OF
  709.  
  710.         'I': BEGIN
  711.             curSelect := 0;
  712.             Reinitialize;
  713.             InsertionSort;
  714.             ElapsedTime(0);    (* Print final time. *)
  715.          END;
  716.  
  717.         'B': BEGIN
  718.             curSelect := 1;
  719.             Reinitialize;
  720.             BubbleSort;
  721.             ElapsedTime(0);    (* Print final time. *)
  722.          END;
  723.  
  724.         'H': BEGIN
  725.             curSelect := 2;
  726.             Reinitialize;
  727.             HeapSort;
  728.             ElapsedTime(0);    (* Print final time. *)
  729.          END;
  730.  
  731.         'E': BEGIN
  732.             curSelect := 3;
  733.             Reinitialize;
  734.             ExchangeSort;
  735.             ElapsedTime(0);    (* Print final time. *)
  736.          END;
  737.  
  738.         'S': BEGIN
  739.             curSelect := 4;
  740.             Reinitialize;
  741.             ShellSort;
  742.             ElapsedTime(0);    (* Print final time. *)
  743.          END;
  744.  
  745.         'Q': BEGIN
  746.             curSelect := 5;
  747.             Reinitialize;
  748.             QuickSort (1, MaxBars);
  749.             ElapsedTime(0);    (* Print final time. *)
  750.          END;
  751.  
  752.         '>': BEGIN
  753.            (* Decrease pause length to speed up    sorting    time,
  754.             * then redraw the menu to clear any    timing results
  755.             * (since they won't compare with future results): *)
  756.                     IF (Pause <> 0) THEN
  757.                         Pause := Pause - 30;
  758.             BoxInit;
  759.          END;
  760.  
  761.         '<': BEGIN
  762.            (* Increase pause length to slow down sorting time,
  763.             * then redraw the menu to clear any    timing results
  764.             * (since they won't compare with future results): *)
  765.                     IF (Pause <> 900) THEN
  766.                         Pause := Pause + 30;
  767.             BoxInit;
  768.          END;
  769.  
  770.         'T': BEGIN
  771.             Sound := NOT Sound;
  772.             BoxInit;
  773.          END;
  774.  
  775.         ESC: return; (* User pressed ESC, so exit and return to main: *)
  776.  
  777.         OTHERWISE
  778.  
  779.     END;
  780.     END;
  781. END;
  782.  
  783. (* =============================== SwapBars ==================================
  784.  *   Calls PrintOneBar twice to    switch the two bars in Row1 and    Row2,
  785.  *   then calls    the ElapsedTime    procedure.
  786.  * ===========================================================================
  787.  *)
  788. PROCEDURE SwapBars {(Row1, Row2)};
  789. BEGIN
  790.     PrintOneBar    (Row1);
  791.     PrintOneBar    (Row2);
  792.     ElapsedTime    (Row1);
  793. END;
  794.  
  795. PROCEDURE cls;
  796. BEGIN
  797.     ret := VioScrollDn (0, 0, -1, -1, -1, ads 1824, 0);
  798. END;
  799.  
  800. PROCEDURE swaps    {(one, two)};
  801. VAR
  802.     temp: SortType;
  803.  
  804. BEGIN
  805.     temp := one;
  806.     one    := two;
  807.     two    := temp;
  808.  
  809. END;
  810.  
  811. (* Main    program    *)
  812. BEGIN
  813.     if(NOT Screen(1)) THEN cls;
  814.     ret := VioGetMode(ads wMode,0);
  815.     IF (wMode.row <> 43) THEN BEGIN     (* Use 43-line mode if available *)
  816.         wMode.row := 43;
  817.         wmode.hres := 640;              (* Try EGA *)
  818.         wmode.vres := 350;
  819.         IF (VioSetMode(ads wMode,0) <> 0) THEN BEGIN
  820.             wmode.hres := 720;          (* Try VGA *)
  821.             wmode.vres := 400;
  822.             IF (VioSetMode(ads wMode,0) <> 0) THEN BEGIN
  823.                 ret = VioGetMode(ads wMode,0)
  824.                 wMode.row := 25;        (* Use 25 lines *)
  825.                 ret = VioSetMode(ads wMode,0)
  826.             END
  827.         END;
  828.     END;
  829.     MaxBars := ORD(wMode.row);
  830.     Initialize;         (* Initialize data values. *)
  831.     SortMenu;         (* Print sort menu. *)
  832.     if(NOT Screen(0)) THEN cls;
  833. END.
  834.