OVERLAY PROCEDURE CONVEX_HULL_MENU;

    { This procedure display the driving menu for the convex hull
      algorithm. The CONVEX_HULL procedure is called from this menu. }

PROCEDURE CONVEX_HULL;

  { AN ALGORITHM THAT DETERMINES WHICH POINTS OF A PLANER SET ARE
    VERTICES OF THE CONVEX HULL OF THE SET.

    There is one refinement. The paper giving the algorithm used
    only small population sizes to be analysized. This program
    allows for any number of points in a population by taking the
    points 100 at a time. Vertices are kept between iterations but
    all interior points are dropped. If 25 points from the first pass
    are vertices, then 75 internal points are dropped and 75 points are
    added from the data file. }


  TYPE
    AR1 = ARRAY[1..2,1..100] OF REAL;
    AR3 = ARRAY[1..200]      OF INTEGER;

    INDEXPOINTER = ^INDEXRECORD;

    INDEXRECORD = RECORD
                    INDEXVALUE : INTEGER;
                    NEXT       : INDEXPOINTER;
                  END;

  VAR


    FINAL_PASS : BOOLEAN;    { TRUE WHEN LAST SET OF POINTS HAVE BEEN PROCESSED }
    RETURN_FLAG : BOOLEAN;   { CONTROLS WHEN TO RETURN FROM CONVEX }
    ULFLAG     : BOOLEAN;    { CONTROLS SORTING FOR UPPER OR LOWER CURVE }

    I         : INTEGER;     { A WORK COUNTER }
    IK        : INTEGER;
    J         : INTEGER;     { A WORK COUNTER }
    NCOUNT    : INTEGER;     { THE NUMBER OF POINTS IN THE SAMPLE }
    NUMBER_OF_VERTICIES : INTEGER;
                             { NUMBER OF POINTS ON THE HULL}
    NUM_VERT_M1 : INTEGER;   { TRUE NUMBER OF VERTICES FOR AREA ROUTINES }
    POINTSREAD : INTEGER;
    STARTLOAD : INTEGER;     { INDEX TO START LOADING THE ARRAY IN }

    XX        : AR1;
    INAR      : ARRAY100I;
    VERTEX    : ARRAY100I;   { WAS IH IN ORIGINAL }
    XAR       : ARRAY100R;
    YAR       : ARRAY100R;

    LEFTEND   : INDEXPOINTER;
    RIGHTEND  : INDEXPOINTER;
    PT1       : INDEXPOINTER;
    PT2       : INDEXPOINTER;
    NEWINDEX  : INDEXPOINTER;
    LASTONE   : INDEXPOINTER;
    HEAPTOP   : ^INTEGER;

    PROCEDURE POLY_AREA(VAR X,Y        : ARRAY100R;
                        VAR NUM_POINTS : INTEGER;
                        VAR AREA       : REAL);
      { CALCULATES THE AREA OF THE HOME RANGE BY INTEGRATION.
        FIRST, THE AREA UNDER THE UPPER CURVE OF THE HOME RANGE IS FOUND.
        SECOND, THE AREA UNDER THE LOWER CURVE OF THE HOME RANGE IS FOUND.
        THIRD, THE LOWER AREA IS SUBTRACTED FROM THE UPPER AREA. }

    VAR
      UPPER    : ARRAY50I;               { UPPER POINTS OF THE POLYGON }
      LOWER    : ARRAY50I;               { LOWER POINTS OF THE POLYGON }

      AREA1    : REAL;                   { SECTION AREA FROM CURVE_AREA }
      INTER    : REAL;                   { Y INTERCEPT OF THE LINE }
      SLOPE    : REAL;                   { SLOPE OF THE LINE }
      ZEROVAL  : REAL;                   { ZERO VALUE OF ALL PTS IN THE EQUATION }

      I        : INTEGER;                { A WORK COUNTER }
      LPTR     : INTEGER;                { INDEX INTO LOWER ARRAY }
      UPTR     : INTEGER;                { INDEX INTO UPPER ARRAY }
      XLINDEX  : INTEGER;                { INDEX OF POINT WITH XLOW }
      XHINDEX  : INTEGER;                { INDEX OF POINT WITH XHIGH }

      PROCEDURE BUBBLE(VAR XSORT,YSORT : ARRAY100R;
                       VAR PTRVAL : ARRAY50I;
                       VAR APTR   : INTEGER;
                       VAR ULFLAG : BOOLEAN);

        VAR
          NOSWAP : BOOLEAN;   { SET TO TRUE WHEN SORT IS FINISHED }
          HOLD   : INTEGER;   { WORK AREA TO HOLD PART OF SWAPPED VAR }
          I      : INTEGER;   { A WORK COUNTER }

        BEGIN  { BUBBLE PROCEDURE }

          NOSWAP := FALSE;
          WHILE NOT NOSWAP DO
            BEGIN
              NOSWAP := TRUE;
              FOR I := 2 TO APTR DO
                IF XSORT[PTRVAL[I-1]] > XSORT[PTRVAL[I]] THEN
                  BEGIN
                    HOLD := PTRVAL[I-1];
                    PTRVAL[I-1] := PTRVAL[I];
                    PTRVAL[I] := HOLD;
                    NOSWAP := FALSE;
                  END;  { IF XSORT (I) > XSORT ( I+1 ) }
                IF XSORT[PTRVAL[I-1]] = XSORT[PTRVAL[I]] THEN
                  BEGIN
                    IF ULFLAG THEN       { ULFLAG = TRUE FOR UPPER CURVE }
                      IF YSORT[PTRVAL[I-1]] < YSORT[PTRVAL[I]] THEN
                        BEGIN
                          HOLD := PTRVAL[I-1];
                          PTRVAL[I-1] := PTRVAL[I];
                          PTRVAL[I] := HOLD;
                          NOSWAP := FALSE;
                        END;  { SWAP DUE TO Y VALUES }
                    IF NOT ULFLAG THEN     { ULFLAG = FALSE FOR LOWER CURVE}
                      IF YSORT[PTRVAL[I-1]] > YSORT[PTRVAL[I]] THEN
                        BEGIN
                          HOLD := PTRVAL[I-1];
                          PTRVAL[I-1] := PTRVAL[I];
                          PTRVAL[I] := HOLD;
                          NOSWAP := FALSE;
                        END;  { SWAP DUE TO Y VALUES  - LOWER CURVE }
                  END; { XSORT (I-1) = XSORT(I) }

            END;  { WHILE NOT NOSWAP DO }
        END;  { BUBBLE PROCEDURE }


      BEGIN  { POLY_AREA PROCEDURE }
      XLINDEX := 0;
      XHINDEX := 0;

      { GET LOW AND HIGH X VALUES }

      FOR I := 1 TO NUM_POINTS DO
        BEGIN
          IF ( I = 1 ) OR ( X[I] < X[XLINDEX] ) THEN XLINDEX := I;
          IF ( I = 1 ) OR ( X[I] > X[XHINDEX] ) THEN XHINDEX := I;
        END;

      { GET SLOPE OF CROSSING LINE }

      SLOPE := (Y[XHINDEX] - Y[XLINDEX]) / (X[XHINDEX] - X[XLINDEX]);
      INTER := Y[XLINDEX] - SLOPE*X[XLINDEX];

      { DIVIDE POINTS INTO UPPER OR LOWER ARRAYS }

      UPTR := 0;
      LPTR := 0;

      FOR I := 1 TO NUM_POINTS DO
        BEGIN
          ZEROVAL := Y[I] - SLOPE*X[I] - INTER;
          IF ZEROVAL >= -0.0001 THEN
            BEGIN
              UPTR := UPTR + 1;
             UPPER[UPTR] := I;
            END;
          IF ZEROVAL <= 0.0001 THEN
            BEGIN
              LPTR := LPTR + 1;
              LOWER[LPTR] := I;
            END;
        END;  { FOR I = 1 TO NUM_POINTS }

      { SORT THE TWO ARRAYS IN TO ASCENDING X ORDER }

      ULFLAG := TRUE;
      BUBBLE(X,Y,UPPER,UPTR,ULFLAG);
      ULFLAG := FALSE;
      BUBBLE(X,Y,LOWER,LPTR,ULFLAG);


      { NOW, CALL POINT BY POINT AND GET THE AREAS
        FIRST THE UPPER CURVE }

      AREA := 0;

      FOR I := 1 TO UPTR-1 DO
        BEGIN
          CURVE_AREA(X[UPPER[I]],Y[UPPER[I]],X[UPPER[I+1]],Y[UPPER[I+1]],AREA1);
          AREA := AREA + AREA1;
        END;


      { NOW, SUBTRACT THE LOWER CURVE }

      FOR I := 1 TO LPTR-1 DO
        BEGIN
          CURVE_AREA(X[LOWER[I]],Y[LOWER[I]],X[LOWER[I+1]],Y[LOWER[I+1]],AREA1);
          AREA := AREA - AREA1;
        END;

      END;  { POLY_AREA PROCEDURE }

   PROCEDURE CONVEX(VAR X :AR1;
                    VAR NUMBER_OF_VERTICIES : INTEGER);


    { PROCEDURE TO ACTUALLY DETERMINE WHICH POINTS ARE VERTICES }

    VAR
      MAXE    : BOOLEAN;
      MINE    : BOOLEAN;

      EXTREME_INDEX : INTEGER;    { ARRAY INDEX OF EXTREME POINT IN SET }
      I       : INTEGER;          { A WORK COUNTER }
      J       : INTEGER;          { A WORK COUNTER }
      MIN_X_INDEX : INTEGER;      { EXTREME MIN X POINT IN SET }
      MAX_X_INDEX : INTEGER;      { EXTREME MAX X POINT IN SET }

      HALF : NAME;                { TELLS WHAT PART OF HULL BEING WORKED ON }

  PROCEDURE SINGLE_POINT;

    BEGIN
      WRITE ('SINGLE_POINT');
      NUMBER_OF_VERTICIES := 1;
      NEW(NEWINDEX);
      NEWINDEX^.INDEXVALUE := 1;
      NEWINDEX^.NEXT := NIL;
      RIGHTEND := NEWINDEX;
      LEFTEND  := NEWINDEX;
      RETURN_FLAG   := TRUE;
    END; { SINGLE_POINT PROCEDURE }

  PROCEDURE DOUBLE_POINT;

    BEGIN
      WRITE ('DOUBLE_POINT');
      NEW(NEWINDEX);
      NEWINDEX^.INDEXVALUE := MAX_X_INDEX;
      RIGHTEND := NEWINDEX;

      NEW(NEWINDEX);
      NEWINDEX^.INDEXVALUE := MIN_X_INDEX;
      LEFTEND := NEWINDEX;
      RIGHTEND^.NEXT := NEWINDEX;
      LEFTEND^.NEXT := NIL;

      IF ( X[1,MIN_X_INDEX] = X[1,MAX_X_INDEX] ) AND
         ( X[2,MIN_X_INDEX] = X[2,MAX_X_INDEX] )
           THEN NUMBER_OF_VERTICIES := 1
           ELSE NUMBER_OF_VERTICIES := 2;

      RETURN_FLAG := TRUE;
    END; { DOUBLE_POINT PROCEDURE }

  PROCEDURE VERTICAL_LINE;

    BEGIN
      WRITE ('VERTICAL');
      MAX_X_INDEX := 1;
      MIN_X_INDEX := 1;
      FOR I := 1 TO NCOUNT DO
        BEGIN
          IF X[2,I] > X[2,MAX_X_INDEX] THEN
            MAX_X_INDEX := I;

          IF X[2,I] < X[2,MIN_X_INDEX] THEN
            MIN_X_INDEX  := I;

        END; { FOR I := 1 TO M }

    END; { VERTICAL_LINE PROCEDURE }

  PROCEDURE SPLIT (VAR PT1     : INDEXPOINTER;
                   VAR PT2     : INDEXPOINTER;
                   VAR EXTREME_INDEX : INTEGER;
                   VAR HALF    : NAME);

    { This procedure partitions a subset of array X into two smaller
      subsets. The split is made along a line joining two possible verticies
      of the convex hull containing all points in the full set.

      The point at maximum distance, if it exists, from the splitting line
      is returned to CONVEX. There are two points, one above the line and
      one below the line. }

    VAR
      VERTICAL : BOOLEAN;         { TRUE OF DIVIDING LINE IS VERTICAL }

      DIRECTION : INTEGER;        { SHOWS LINE DIRECTION FOR VERTICAL LINES }
      I       : INTEGER;          { A WORK VARIABLE }
      INDEX1  : INTEGER;          { ARRAY INDEX OF THE FIRST POINT }
      INDEX2  : INTEGER;          { ARRAY INDEX OF THE SECOND POINT }

      EXTREME_VALUE : REAL;       { ZERO_VALUE OF THE EXTREME_POINT }
      INTERCEPT : REAL;           { Y INTERCEPT FOR THE LINE }
      SLOPE     : REAL;           { SLOPE OF THE LINE }
      TOP_X   : REAL;             { X VALUE OF TOP POINT IN A VERTICAL LINE }
      ZERO_VALUE : REAL;          { VALUE OF POINT RELATIVE TO DIVIDING LINE }



    BEGIN  { SPLIT PROCEDURE }

      WRITE (OUTPUT,'+');

      INDEX1 := PT1^.INDEXVALUE;
      INDEX2 := PT2^.INDEXVALUE;

      { CHECK TO SEE IF THE LINE IS VERTICAL }

      VERTICAL := FALSE;

      IF X[1,INDEX2] = X[1,INDEX1] THEN
        BEGIN
WRITELN (' IN SPLIT A VERTICAL LINE');
          TOP_X := X[1,INDEX1];
          IF X[2,INDEX2] > X[2,INDEX1] THEN
            DIRECTION := 1                           { UP }
          ELSE
            DIRECTION := -1;                         { DOWN }
          IF HALF = 'TOP'
            THEN DIRECTION := DIRECTION * 1           { BOTTOM UP }
            ELSE DIRECTION := DIRECTION * -1;         { BOTTOM DOWN }
          VERTICAL := TRUE;
        END
      ELSE
        BEGIN
          SLOPE := ( X[2,INDEX2] - X[2,INDEX1] ) / ( X[1,INDEX2] - X[1,INDEX1] );
          INTERCEPT := X[2,INDEX1] - SLOPE * X[1,INDEX1];
        END;

      EXTREME_INDEX := 0;
      EXTREME_VALUE := 0.0;

      FOR I := 1 TO NCOUNT DO
        BEGIN
          IF VERTICAL
            THEN ZERO_VALUE := DIRECTION * ( X[1,I] - TOP_X )
            ELSE ZERO_VALUE := X[2,I] - SLOPE * X[1,I] - INTERCEPT;

          IF ( ZERO_VALUE > 0.00001 ) AND ( HALF = 'TOP') THEN
            BEGIN

              { THE POINT IS ABOVE THE LINE }

              IF ( I <> INDEX1 ) AND ( I <> INDEX2 ) THEN
              IF ZERO_VALUE > EXTREME_VALUE THEN
                BEGIN
                  EXTREME_INDEX := I;
                  EXTREME_VALUE := ZERO_VALUE;
                END;
             END;  { IF Z > 0 }

          IF ( ZERO_VALUE < -0.00001 ) AND ( HALF = 'BOTTOM') THEN
            BEGIN

              { THE POINT IS BELOW THE LINE AND HALF IS BOTTOM }

              IF ( I <> INDEX1 ) AND ( I <> INDEX2 ) THEN
              IF ZERO_VALUE < EXTREME_VALUE THEN
                BEGIN
                  EXTREME_INDEX := I;
                  EXTREME_VALUE := ZERO_VALUE;
                END;
            END;  { IF Z < -0.00001 AND HALF = 'BOTTOM' }

        END;  { FOR I = 1 TO NCOUNT }

    END;  { SPLIT PROCEDURE }


    BEGIN  { CONVEX PROCEDURE }

      GOTOXY(1,22);
      WRITE(OUTPUT,'.');


      RETURN_FLAG := FALSE;

      IF NOT RETURN_FLAG THEN
        IF NCOUNT = 1 THEN SINGLE_POINT;

      IF NOT RETURN_FLAG THEN
        IF NCOUNT = 2 THEN DOUBLE_POINT;

      IF NOT RETURN_FLAG THEN
      BEGIN                     { IF NOT RETURN_FLAG 4 }
        MAXE    := FALSE;
        MINE    := FALSE;

      { FIND TWO VERTICES OF THE CONVEX HULL FOR THE INITIAL PARTITION }

        MAX_X_INDEX := 1;
        MIN_X_INDEX := 1;

        FOR I := 2 TO NCOUNT DO
          BEGIN

            {  MAXE IS TRUE WHEN THERE ARE MORE THAN ONE MAXIMUM X PTS.}

            IF X[1,I] = X[1,MAX_X_INDEX] THEN MAXE := TRUE;
            IF X[1,I] > X[1,MAX_X_INDEX] THEN
              BEGIN
                MAXE        := FALSE;
                MAX_X_INDEX := I;
              END;

            { MINE IS TRUE WHEN THERE ARE MORE THEN ONE MINIMUM X PTS.}

            IF X[1,I] = X[1,MIN_X_INDEX] THEN MINE := TRUE;
            IF X[1,I] < X[1,MIN_X_INDEX] THEN
              BEGIN
                MINE        := FALSE;
                MIN_X_INDEX := I;
              END;

          END;  { FOR I := 2 TO NCOUNT }

      { PROCESSING FOR TWO VERTICIES. ALL POINTS ARE ALONG ONE LINE. }

        IF MAX_X_INDEX = MIN_X_INDEX THEN
          BEGIN
            VERTICAL_LINE;
            IF MAX_X_INDEX = MIN_X_INDEX
              THEN SINGLE_POINT
              ELSE DOUBLE_POINT;
          END;
      END;                        { IF NOT RETURN_FLAG 4 }


    IF NOT RETURN_FLAG THEN
    BEGIN                         { IF NOT RETURN_FLAG 5 }

   { IF THERE ARE SEVERAL POINTS WITH THE SAME LARGEST FIRST COORDINATE }

      IF ( MAXE ) THEN
        FOR I := 1 TO NCOUNT DO

          IF ( X[1,I] = X[1,MAX_X_INDEX] )
            AND ( X[2,I] > X[2,MAX_X_INDEX] )

              THEN MAX_X_INDEX := I;

   { IF THERE ARE SEVERALPOINTS WITH THE SAME SMALLEST FIRST COORDINATE }

      IF ( MINE ) THEN
        FOR I := 1 TO NCOUNT DO

          IF ( X[1,I] = X[1,MIN_X_INDEX] )
            AND ( X[2,I] < X[2,MIN_X_INDEX] )

              THEN  MIN_X_INDEX := I;

    { THROUGH CHECKING SPECIAL CASES. NOW PROCESS }
    { SET UP THE RECORDS }

    MARK(HEAPTOP);          { MARK MEMORY TO RELEASE }
    LEFTEND  := NIL;
    RIGHTEND := NIL;

    NEW(NEWINDEX);          { FIRST RECORD IS THE EXTREME MAX X POINT }
    NEWINDEX^.INDEXVALUE := MAX_X_INDEX;
    RIGHTEND := NEWINDEX;

    NEW(NEWINDEX);          { SECOND RECORD IS THE EXTREME MIN X_POINT }
    NEWINDEX^.INDEXVALUE := MIN_X_INDEX;
    RIGHTEND^.NEXT := NEWINDEX;
    LEFTEND := NEWINDEX;

    NEW(NEWINDEX);          { THIRD RECORD IS FIRST RECORD - FULL CIRCLE }
    NEWINDEX^.INDEXVALUE := MAX_X_INDEX;
    LEFTEND^.NEXT := NEWINDEX;
    LASTONE       := NEWINDEX;
    LASTONE^.NEXT := NIL;

    PT1 := RIGHTEND;
    PT2 := LEFTEND;
    HALF := 'TOP';
    NUMBER_OF_VERTICIES := 3;

    { BEGIN BY MAKING THE INITIAL DIVISION }

    SPLIT(PT1,PT2,EXTREME_INDEX,HALF);

    WHILE HALF = 'TOP' DO
      BEGIN
        IF EXTREME_INDEX = 0 THEN
          BEGIN
            PT1 := PT1^.NEXT;      { NO VERTEX BETWEEN PT1 AND PT2, }
            PT2 := PT1^.NEXT;      { SHIFT DOWN TO NEXT SET OF POINTS }
          END
        ELSE
          BEGIN

            { INSERT A NEW VERTEX IN THE CHAIN OF HULL POINTS }

            NEW(NEWINDEX);
            NEWINDEX^.INDEXVALUE := EXTREME_INDEX;
            NEWINDEX^.NEXT := PT1^.NEXT;
            PT1^.NEXT := NEWINDEX;
            PT2 := PT1^.NEXT;
            NUMBER_OF_VERTICIES := NUMBER_OF_VERTICIES + 1;
          END;

        { CHECK IF THE TOP HALF IS DONE }

        IF PT1 = LEFTEND THEN HALF := 'BOTTOM';

        SPLIT(PT1,PT2,EXTREME_INDEX,HALF);

      END;  { WHILE HALF = 'TOP' }

    WHILE HALF = 'BOTTOM' DO
      BEGIN
        IF EXTREME_INDEX = 0 THEN
          BEGIN
            PT1 := PT1^.NEXT;      { NO VERTEX BETWEEN PT1 AND PT2, }
            PT2 := PT1^.NEXT;      { SHIFT DOWN TO NEXT SET OF POINTS }
          END
        ELSE
          BEGIN

            { INSERT A NEW VERTEX IN THE CHAIN OF HULL POINTS }

            NEW(NEWINDEX);
            NEWINDEX^.INDEXVALUE := EXTREME_INDEX;
            NEWINDEX^.NEXT := PT1^.NEXT;
            PT1^.NEXT := NEWINDEX;
            PT2 := PT1^.NEXT;
            NUMBER_OF_VERTICIES := NUMBER_OF_VERTICIES + 1;
          END;

        { CHECK IF THE BOTTOM HALF IS DONE }

        IF PT2 = NIL
          THEN HALF := 'DONE'
          ELSE SPLIT(PT1,PT2,EXTREME_INDEX,HALF);

      END;  { WHILE HALF = 'BOTTOM' }

    RETURN_FLAG := TRUE;
    END;                    { IF NOT RETURN_FLAG 5 }

    END; { PROCEDURE CONVEX }


  PROCEDURE DATA_ARRAY;

    { This procedure takes data from the input file and puts it into the
      array INAR. Initially 100 points are loaded. After that, each load
      will replace only interior points of the convex hull. If there are
      100 verticies on the hull, then the program terminates.

      The procedure PACK_ARRAY is called to move all verticies to the front
      of the array. }

    BEGIN { DATA_ARRAY }

      GOTOXY(1,23);
      WRITELN ('LOADING DATA_ARRAY');

      FOR I := STARTLOAD TO 100 DO
        BEGIN
          IF NOT EOF(FILEVAR2) THEN
            BEGIN
              READ (FILEVAR2,FILE2REC);
              WITH FILE2REC DO
                BEGIN
                  XX[1,I] := X;
                  XX[2,I] := Y;
                END;
              NCOUNT := I;
              POINTSREAD := POINTSREAD + 1;
            END
          ELSE
            BEGIN
              XX[1,I] := 0.00;
              XX[2,I] := 0.00;
              FINAL_PASS := TRUE;
            END;

        END; { FOR I = STARTLOAD TO 100 }

      GOTOXY(1,23);
      DELLINE;

    END; { PROCEDURE DATA_ARRAY }


  BEGIN  { CONVEX_HULL PROCEDURE }


    { OPEN AND SET THE FILE }

    ASSIGN (FILEVAR2,FILEOUT);
    RESET  (FILEVAR2);                 { POSITION AT BEGINNING OF FILE }
    SEEK   (FILEVAR2,3);               { POSITION FILE BEYOND HEADERS }

    FINAL_PASS := FALSE;
    STARTLOAD  := 1;                   { INITIAL LOAD WILL GET 100 POINTS }
    NUMBER_OF_VERTICIES := 0;
    POINTSREAD := 0;

    WHILE NOT FINAL_PASS DO
      BEGIN

      DATA_ARRAY;                      { FILL THE ARRAY XX }

      LINE_OUT(1,22,SPACE30);
      LINE_OUT(1,22,'-');

      CONVEX(XX,NUMBER_OF_VERTICIES);

      NEWINDEX := RIGHTEND;
      I := 0;

      WHILE NEWINDEX <> NIL DO
        BEGIN
          J      := NEWINDEX^.INDEXVALUE;
          I := I + 1;
          XAR[I] := XX[1,J];
          YAR[I] := XX[2,J];
          NEWINDEX := NEWINDEX^.NEXT;
        END;


      LINE_OUT(5,2,SPACE79);
      LINE_OUT(1,2,SPACE79);
      LINE_OUT(1,3,SPACE79);
      GOTOXY(5,2);
      WRITE (OUTPUT,'SAMPLE SIZE ',NCOUNT:5,' VERTICIES ',
             NUMBER_OF_VERTICIES:5,' SPLIT ',NCOUNT);
      WRITE (OUTPUT,' NUMBER OF POINTS READ: ',POINTSREAD:5);
      WRITELN;


      IF NOT FINAL_PASS THEN
        BEGIN
          FOR I := 1 TO NUMBER_OF_VERTICIES DO
            BEGIN
              XX[1,I] := XAR[I];
              XX[2,I] := YAR[I];
            END;
          STARTLOAD := NUMBER_OF_VERTICIES + 1;
          WRITELN (OUTPUT,'NUMBER OF VERTICIES CARRIED OVER: ',
            NUMBER_OF_VERTICIES);
        END;  { IF NOT FINAL_PASS }

     RELEASE(HEAPTOP);                 { RELEASE HEAP SPACE FOR NEXT PASS }

    END; { WHILE NOT FINAL_PASS }

  { DISPLAY THE VERTICIES OF THE POLYGON }

  LINE_OUT(1,3,SPACE79);
  GOTOXY(1,3);
  FOR I := 1 TO NUMBER_OF_VERTICIES DO
    WRITELN (OUTPUT,XAR[I]:10:5,YAR[I]:10:5);

  NUM_VERT_M1 := NUMBER_OF_VERTICIES - 1;

  IF PRECISION = 'I' THEN
    BEGIN

      { AREA BY INTEGRATION }

        POLY_AREA(XAR,YAR,NUM_VERT_M1,AREA);

        AREA := AREA * METER_VALUE * METER_VALUE;
        AREAK := AREA / 1000;
        AREAK := AREAK / 1000;
     END;  { AREA BY INTEGRATION }

{*******************************************************************}

     IF PRECISION = 'G' THEN
       BEGIN

         { AREA BY GRID CELL COUNT }

         AREA  := 0.0;
         AREAK := 0.0;

         XGRID_NUM := 0;     { XGRID_NUM HOLDS THE AREA IN GRID SQUARE NUMBERS }
         YGRID_NUM := 0;     { YGRID_NUM IS A WORK VARIABLE }

         WRITELN('Calculating the area by grid cell count.');

{ XHIGH AND XLOW WILL NEED REDEFINING }


{ THIS CODE NEED TO BE DONE. }


       END; { AREA BY GRID CELL COUNT }

{ *****************************************************************}

     IF AREA <= 10000 THEN
       WRITELN('                        AREA: ',AREA:10:5,' Square meters')
     ELSE
       WRITELN('                        AREA: ',AREAK:10:5,' Square kilometers');

      LINE_OUT(1,22,SPACE79);
      KEYBOARD_PAUSE;

      KEYVALUE := 'R';

      IF DRAWCHAR = 'Y' THEN
        BEGIN

          IF STUDYFILE <> '' THEN
            BEGIN
              ASSIGN (FILEVAR3,STUDYFILE);
              RESET  (FILEVAR3);
              STUDYOPEN := TRUE;
            END;

          { DRAW THE POINTS IN THE XAR AND YAR }
          { FIRST, GET THE X AND Y EXTREME VALUES }

          XHIGH := 0;
          XLOW  := 0;
          YHIGH := 0;
          YLOW  := 0;

          FOR I := 1 TO NUMBER_OF_VERTICIES DO
            BEGIN
              IF ( I = 1 ) OR ( XAR[I] < XLOW  ) THEN XLOW  := XAR[I];
              IF ( I = 1 ) OR ( XAR[I] > XHIGH ) THEN XHIGH := XAR[I];
              IF ( I = 1 ) OR ( YAR[I] < YLOW  ) THEN YLOW  := YAR[I];
              IF ( I = 1 ) OR ( YAR[I] > YHIGH ) THEN YHIGH := YAR[I];
            END;  { FOR I = 1 TO NUMBER OF VERTICIES }

          { SAVE THE MIN AND MAX VALUES FROM THE POLYGON }

          SXLOW := XLOW;
          SYLOW := YLOW;
          SZLOW := ZLOW;
          SXHIGH := XHIGH;
          SYHIGH := YHIGH;
          SZHIGH := ZHIGH;

          GRID_CALCULATIONS;

          HIRES;
          HIRESCOLOR(YELLOW);

          LINE_OUT(1,1,'Minimum Convex Polygon');

          FOR I := 1 TO NUMBER_OF_VERTICIES-1 DO
            HIRES_VECTOR(XAR[I],YAR[I],XAR[I+1],YAR[I+1],
                           XOFFSET,YOFFSET,GRIDFACTOR);

          { PUT IN THE FILE INFO }

          LINE_OUT(60,4,'Data file');
          LINE_OUT(64,5,FILEOUT);

          IF STUDYFILE <> '' THEN
            BEGIN
              LINE_OUT(60,7,'Map file:');
              LINE_OUT(64,8,STUDYFILE);
            END;

          IF REPLAYFILE <> '' THEN
            BEGIN
              LINE_OUT(60,10,'Replay File:');
              LINE_OUT(64,11,REPLAYFILE);
            END;

          LINE_OUT(62,13,'AREA:');
          GOTOXY(62,14);
          IF AREA <= 10000 THEN
            BEGIN
              WRITE (AREA:8:4);
              LINE_OUT(62,15,'Sq. meters.');
            END
          ELSE
            BEGIN
              WRITE (AREAK:8:4);
              LINE_OUT(62,15,'Sq. kilometers.');
            END;

          IF (STUDYFILE <> '') THEN
            BEGIN
              LINE_OUT(1,25,SPACE79);
              LINE_OUT(1,25,'Draw the study file area overlay? (Y|N) ');

              YES_NO(STUDYDRAWN);

              LINE_OUT(1,25,SPACE79);

              IF STUDYDRAWN = 'Y' THEN
                BEGIN                       { DRAW THE STUDY AREA }

                  HIRES;
                  HIRESCOLOR(YELLOW);

                  SEEK (FILEVAR3,1);        { MAX AND MINS FOR STUDY AREA  }
                  READ (FILEVAR3,FILE3REC);

                  XLOW := MIN_VAL(XLOW,FILE3REC.X1);
                  YLOW := MIN_VAL(YLOW,FILE3REC.Y1);
                  ZLOW := MIN_VAL(ZLOW,FILE3REC.Z1);
                  XHIGH := MAX_VAL(XHIGH,FILE3REC.X2);
                  YHIGH := MAX_VAL(YHIGH,FILE3REC.Y2);
                  ZHIGH := MAX_VAL(ZHIGH,FILE3REC.Z2);

                  GRID_CALCULATIONS;

                  LINE_OUT(1,1,'Minimum Convex Polygon');

                  SEEK (FILEVAR3,2);        { DATA BEGINS AT THE THIRD RECORD }

                  WHILE NOT EOF(FILEVAR3) DO
                    BEGIN
                      READ(FILEVAR3,FILE3REC);
                      WITH FILE3REC DO
                        HIRES_VECTOR(X1,Y1,X2,Y2,XOFFSET,YOFFSET,GRIDFACTOR);
                    END;

                  FOR I := 1 TO NUMBER_OF_VERTICIES-1 DO
                    HIRES_VECTOR(XAR[I],YAR[I],XAR[I+1],YAR[I+1],
                                 XOFFSET,YOFFSET,GRIDFACTOR);

                  { PUT IN THE FILE INFO }

                  LINE_OUT(60,4,'Data file');
                  LINE_OUT(64,5,FILEOUT);

                  IF STUDYFILE <> '' THEN
                    BEGIN
                      LINE_OUT(60,7,'Map file:');
                      LINE_OUT(64,8,STUDYFILE);
                    END;

                  IF REPLAYFILE <> '' THEN
                    BEGIN
                      LINE_OUT(60,10,'Replay File:');
                      LINE_OUT(64,11,REPLAYFILE);
                    END;

                  LINE_OUT(62,13,'AREA:');
                  GOTOXY(62,14);
                  IF AREA <= 10000 THEN
                    BEGIN
                      WRITE (AREA:8:4);
                      LINE_OUT(62,15,'Sq. meters.');
                    END
                  ELSE
                    BEGIN
                      WRITE (AREAK:8:4);
                      LINE_OUT(62,15,'Sq. kilometers.');
                    END;

                END;  { STUDYDRAWN = Y }

            END;  { IF STUDYFILE <> '' }

          IF REPLAYFILE <> '' THEN
            BEGIN
              LINE_OUT(1,25,SPACE79);
              LINE_OUT(1,25,'Do you want to add this graph to the replay file? (Y|N) ');

              YES_NO(KEYVALUE);

              IF KEYVALUE = 'Y' THEN
                BEGIN

                  IF NOT EXIST(REPLAYFILE) THEN INIT_REPLAY_FILE;

                  ASSIGN (FILEVAR4,REPLAYFILE);
                  RESET  (FILEVAR4);
                  REPLAYOPEN := TRUE;

                  { WRITE OUT THE PLOT. WILL INCLUDE THE CONVEX POLYGON AND MAYBE STUDY FILE }

                  IF STUDYDRAWN = 'Y' THEN
                    IF NOT STUDY_IN_REPLAY THEN
                      ADD_GRAPH('STUDYFILE');    { PUT STUDY AREA IN FILE }

                  { UPDATE THE WHOLE FILE VALUES }

                  SEEK (FILEVAR4,1);
                  READ (FILEVAR4,FILE4REC);

                  FILE4REC.X1 := MIN_VAL(FILE4REC.X1,SXLOW);
                  FILE4REC.Y1 := MIN_VAL(FILE4REC.Y1,SYLOW);
                  FILE4REC.Z1 := MIN_VAL(FILE4REC.Z1,SZLOW);
                  FILE4REC.X2 := MAX_VAL(FILE4REC.X2,SXHIGH);
                  FILE4REC.Y2 := MAX_VAL(FILE4REC.Y2,SYHIGH);
                  FILE4REC.Z2 := MAX_VAL(FILE4REC.Z2,SZHIGH);
                  FILE4REC.LINE_TYPE := FILE4REC.LINE_TYPE + NUMBER_OF_VERTICIES;

                  SEEK (FILEVAR4,1);
                  WRITE(FILEVAR4,FILE4REC);      { WRITE OUT RECORD 2 }
                  READ (FILEVAR4,FILE4REC);      { READ RECORD 3 }

                  FILE4REC.X2 := FILE4REC.X2 + 1;     { NUMBER OF GRAPHS IN FILE }

                  SEEK (FILEVAR4,2);
                  WRITE(FILEVAR4,FILE4REC);

                  { NOW, MODIFY THE GRAPH HEADERS AND WRITE OUT THE POINTS }

                  SEEK (FILEVAR4,FILESIZE(FILEVAR4)-1);
                  READ (FILEVAR4,FILE4REC);

                  FILE4REC.X1 := -3;             { -3 MEANS CONVEX GRAPH }
                  FILE4REC.Y1 := -10;
                  FILE4REC.X2 := -10;
                  FILE4REC.Y2 := -10;

                  SEEK (FILEVAR4,FILESIZE(FILEVAR4)-1);  { LAST RECORD IN FILE }
                  WRITE (FILEVAR4,FILE4REC);

                  { BUILD SECOND GRAPH HEADER }

                  FILE4REC.X1 := SXLOW;
                  FILE4REC.Y1 := SYLOW;
                  FILE4REC.Z1 := SZLOW;
                  FILE4REC.X2 := SXHIGH;
                  FILE4REC.Y2 := SYHIGH;
                  FILE4REC.Z2 := SZHIGH;
                  FILE4REC.LINE_TYPE := NUMBER_OF_VERTICIES;
                  WRITE(FILEVAR4,FILE4REC);

                  FILE4REC.Z1 := 0.0;
                  FILE4REC.Z2 := 0.0;
                  FILE4REC.LINE_TYPE := 1;

                  FOR I := 1 TO NUMBER_OF_VERTICIES-1 DO
                    BEGIN
                      FILE4REC.X1 := XAR[I];
                      FILE4REC.Y1 := YAR[I];
                      FILE4REC.X2 := XAR[I+1];
                      FILE4REC.Y2 := YAR[I+1];
                      WRITE(FILEVAR4,FILE4REC);
                    END;  { FOR I = 1 TO NUMBER OF VERTICIES }

                  { WRITE OUT THE TRAILING RECORD }

                  FILE4REC.X1 := 0.0;
                  FILE4REC.Y1 := 0.0;
                  FILE4REC.X2 := 0.0;
                  FILE4REC.Y2 := 0.0;
                  FILE4REC.LINE_TYPE := 0;
                  WRITE(FILEVAR4,FILE4REC);

                END;  { IF KEYVALUE = 'Y' }

            END;  { IF REPLAYFILE <> '' }

          KEYBOARD_PAUSE;

          IF STUDYOPEN  THEN CLOSE(FILEVAR3);
          IF REPLAYOPEN THEN CLOSE(FILEVAR4);

          KEYVALUE := 'R';

        END; { IF DRAWCHAR = Y }

    CLOSE (FILEVAR2);

    { CHECK AND PRINT RESULTS }

    IF PRINTCHAR IN ['B','P'] THEN
      BEGIN
        WRITELN (LST);
        WRITE (LST,'SAMPLE SIZE ',NCOUNT:5,' VERTICIES ',
               NUMBER_OF_VERTICIES:5,' SPLIT ',NCOUNT);
        WRITELN (LST,' NUMBER OF POINTS READ: ',POINTSREAD:5);
        FOR I := 1 TO NUMBER_OF_VERTICIES DO
          WRITELN (LST,XAR[I]:10:5,YAR[I]:10:5);
        WRITELN (LST);

       IF AREA <= 10000 THEN
         WRITELN(LST,'                        AREA: ',AREA:10:5,' Square meters')
       ELSE
         WRITELN(LST,'                        AREA: ',AREAK:10:5,' Square kilometers');

      END; { IF PRINTCHAR IN ['B','P'] }


   END;  { CONVEX_HULL PROCEDURE }

BEGIN  { CONVEX_HULL_MENU }

  { Display the options menu for the convex hull procedure }

  KEYVALUE := 'R';

  WHILE KEYVALUE = 'R' DO
  BEGIN

  CLRSCR;
  DISPLAY_COLOR(CYAN);

  LINE_OUT(28,2,'(6) Convex Polygon Algorithm');
  LINE_OUT(10,4,'1  Data file:');
  LINE_OUT(10,6,'2  Study area file:');
  LINE_OUT(10,8,'3  Plot replay file:');
  LINE_OUT(10,10,'4  Text output displayed on:');
  LINE_OUT(10,12,'5  Graphics displayed on screen (Y|N):');
  LINE_OUT(10,14,'6  Area accuracy method (I|G):');
  LINE_OUT(10,16,'7  Width of a grid cell in meters:');
  LINE_OUT(10,18,'8  Scale of your map as meters between adjacent coordinates: ');

  TEXTCOLOR(YELLOW);
  LINE_OUT(24,4,FILEOUT);
  LINE_OUT(30,6,STUDYFILE);
  LINE_OUT(31,8,REPLAYFILE);

  CASE UPCASE(PRINTCHAR) OF
    'D' : PRINTSTR := 'DISPLAY ONLY';
    'P' : PRINTSTR := 'PRINTER ONLY';
    'B' : PRINTSTR := 'BOTH DISPLAY AND PRINTER';
  END;

  LINE_OUT(39,10,PRINTSTR);

  IF DRAWCHAR = 'N'
    THEN DRAWSTR := 'NO'
    ELSE DRAWSTR := 'YES';

  LINE_OUT(49,12,DRAWSTR);

  { FOR NOW, THE BUG OF INTRGRATION ONLY *****************}
  PRECISION := 'I';

  IF PRECISION = 'I'
    THEN LINE_OUT(41,14,'Integration')
    ELSE LINE_OUT(41,14,'Grid Cell Count');

  GOTOXY(45,16);
  WRITE (SECTION_VALUE:8:4);

  GOTOXY(71,18);
  WRITE (METER_VALUE:8:4);

  DISPLAY_COLOR(RED);

  LINE_OUT(37,21,'E  Exit to Main Menu');
  LINE_OUT(7,21,'P  Process Data');

  TEXTCOLOR(YELLOW);
  GOTOXY(1,24);

  KEYVALUE := ' ';

  WHILE NOT (UPCASE(KEYVALUE) IN ['E','R']) DO
    BEGIN

      KEYVALUE := ' ';
      WHILE NOT (UPCASE(KEYVALUE) IN ['1'..'8','E','P']) DO
        READ (KBD,KEYVALUE);
      KEYVALUE := UPCASE(KEYVALUE);

      GOTOXY(1,23);
      DELLINE;
      DELLINE;

      CASE KEYVALUE OF
        '1': MENU_CALL(1);        { INPUT DATA FILE }
        '2': MENU_CALL(2);        { STUDY AREA FILE }
        '3': MENU_CALL(3);        { REPLAY FILE }
        '4': MENU_CALL(4);        { TEXT ON B, D, P }
        '5': MENU_CALL(5);        { GRAPHICS ON SCREEN? }
  {     '6': MENU_CALL(6);  }     { TYPE OF AREA ACCURACY }
        '7': MENU_CALL(7);        { WIDTH OF GRID CELL }
        '8': MENU_CALL(8);        { DISTANCE FROM 1.0 TO 2.0 }
        'P': BEGIN
             KEEP_GOING := TRUE;
             STUDYOPEN := FALSE;
             REPLAYOPEN := FALSE;

             PROCESSING_VERIFY;

             IF (KEEP_GOING) AND (SECTION_VALUE <= 0.0) THEN
               BEGIN
                 LINE_OUT(1,23,'A grid cell value greater than 0 must be given.');
                 KEEP_GOING := FALSE;
               END;

             IF (KEEP_GOING) AND (METER_VALUE <= 0.0) THEN
               BEGIN
                 LINE_OUT(1,23,'The distance from 1.0 to 2.0 must be given.');
                 KEEP_GOING := FALSE;
               END;

             IF KEEP_GOING THEN
               BEGIN
                 CLRSCR;
                 CONVEX_HULL;
                 END;

             END; { CASE 'P' }

       END; { CASE KEYVALUE }

       GOTOXY(1,23);

   END; { WHILE NOT (KEYVALUE IN ['E','R'] }

 END; { WHILE KEYVALUE = 'R' }

 KEYVALUE := ' ';
END; { CONVEX_HULL_MENU PROCEDURE }

