home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / music / 194 / pascal / xyplot.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1987-11-19  |  39.1 KB  |  1,158 lines

  1. PROGRAM XYPlotter;
  2.                         {By: Merlin Hanson
  3.                         Genie: M.L.Hanson
  4.                         Version 1.0
  5.                         Date: 08/16/87}
  6.   CONST
  7.     {$i gemconst.pas}
  8.   TYPE
  9.     {$i gemtype.pas}
  10.  
  11.   {$i gemsubs.pas}
  12.  
  13.    {----------- Following imported from CURSOR.PAS ----------------}
  14.  
  15.   { Put a single character to the console device (the character's value is
  16.     received as an integer!) }
  17.   PROCEDURE out_char( c: integer );
  18.     CONST
  19.       screen = 2;
  20.     PROCEDURE bconout( device, c: integer );
  21.       BIOS(3);
  22.     BEGIN
  23.       bconout( screen, c );
  24.     END;
  25.  
  26.   { Put a two-character escape sequence to the console device (an escape
  27.     followed by a single character) }
  28.   PROCEDURE out_escape( c: char );
  29.     CONST
  30.       escape = 27;
  31.     BEGIN
  32.       out_char( escape );
  33.       out_char( ord(c) );
  34.     END;
  35.  
  36.   { Clear the screen and move the cursor to the upper left position }
  37.   PROCEDURE ClrScr;
  38.     BEGIN out_escape( 'E' ) END;
  39.  
  40.   { Move the cursor up one line }
  41.   PROCEDURE CursUp;
  42.     BEGIN out_escape( 'A' ) END;
  43.  
  44.   { Move the cursor to the upper left corner of the screen }
  45.   PROCEDURE CursHome;
  46.     BEGIN out_escape( 'H' ) END;
  47.  
  48.    {---------------------- end of import ---------------------------}
  49.  
  50.   FUNCTION ColorMonitor : boolean;
  51.     {I couldn't test this. Hope it works.}
  52.     FUNCTION GetRez : integer;
  53.       XBIOS (4);
  54.     BEGIN {colormonitor}
  55.       CASE GetRez OF
  56.         0,1 : ColorMonitor := TRUE;
  57.         3   : ColorMonitor := FALSE;
  58.       END {case}
  59.     END {colormonitor};
  60.  
  61.   PROCEDURE XYPlotDemo;
  62.     CONST
  63.       Blk = 384;      {Replace mode, color = Black.  (256 * Black + 128)}
  64.       NoBorder = 0;
  65.       LB = 1;
  66.       UB = 10;
  67.     TYPE
  68.       T1 = (FunctionKind,ArrKind);
  69.       T2 = ARRAY [LB..UB] OF real;
  70.     VAR
  71.       XMax,XMin         : real;
  72.       PlotArr           : T2;
  73.       PlotKind          : T1;
  74.       OK1,OK2,NumbersOK : boolean;
  75.       Done : boolean;
  76.       Func : (Sinusoid,Parabola,Polynomial,NormalDist,ArrayPlot);
  77.       NumbersFudged : boolean;
  78.  
  79.     PROCEDURE ChooseFunction;
  80.       CONST
  81.         W = 44;
  82.         H = 10;
  83.       VAR
  84.         Box : dialog_ptr;
  85.         Line : ARRAY [1..16] OF integer;
  86.         Msg : ARRAY [1..16] OF string;
  87.         LineNbr : integer;
  88.         Choice : tree_index;
  89.         HeaderLine : integer;
  90.       BEGIN {choosefunction}
  91.         Box := New_Dialog(20,0,0,W,H);
  92.         {Headers}
  93.         HeaderLine := Add_DItem(Box,G_String,None,
  94.                       1,1,30,1,    0,Blk);
  95.         Set_DText(Box,HeaderLine,
  96.                  '     FUNCTION          BOUNDS',
  97.                  System_Font,TE_Left);
  98.         {Left side buttons}
  99.         Msg[1] := '   Sinusoid   ';
  100.         Msg[2] := '   Parabola   ';
  101.         Msg[3] := '  Polynomial  ';
  102.         Msg[4] := ' Normal dist. ';
  103.         Msg[5] := '    Array     ';
  104.         FOR LineNbr := 1 TO 5 DO
  105.           BEGIN
  106.             Line[LineNbr] := Add_DItem(Box,G_Button,Selectable | Exit_Btn,
  107.                              4,LineNbr + 2,14,1,    0,384);
  108.             Set_DText(Box,Line[LineNbr],Msg[lineNbr], System_Font,TE_Left);
  109.           END;
  110.         {Now the crib sheet on the right side}
  111.         Msg[11] := '  0   6.28  (radians)    ' ;
  112.         Msg[12] := ' -7    4                  ' ;
  113.         Msg[13] := ' -6    5                  ' ;
  114.         Msg[14] := ' -4    4                  ' ;
  115.         Msg[15] := '  1   10   (dimension)  ' ;
  116.         FOR lineNbr := 11 TO 15 DO
  117.           BEGIN
  118.             Line[LineNbr] := Add_DItem(Box,G_String,None,
  119.                              21,LineNbr + 2 - 10,25,1,    0,384);
  120.             Set_DText(Box,Line[LineNbr],Msg[LineNbr], System_Font,TE_Left);
  121.           END;
  122.         Center_Dialog(Box);
  123.         Choice := Do_Dialog(Box,0);
  124.         CASE Choice - Line[1] + 1 OF
  125.           1 : Func := Sinusoid;
  126.           2 : Func := Parabola;
  127.           3 : Func := Polynomial;
  128.           4 : Func := NormalDist;
  129.           5 : Func := ArrayPlot;
  130.         END {case};
  131.         End_Dialog(Box);
  132.       END {choosefunction};
  133.  
  134.     PROCEDURE DisplayArrayMessage;
  135.       CONST
  136.         W = 41;
  137.         H = 9;
  138.       VAR
  139.         Msg : ARRAY[1..3]OF string;
  140.         Line : ARRAY [1..3] OF integer;
  141.         Box : dialog_ptr;
  142.         LineNumber,ExitButton : integer;
  143.         Pushed : tree_index;
  144.       BEGIN
  145.         Box := New_Dialog(3, 0,0,W,H);
  146.         Msg[1] := '  The array function plots an array '    ;
  147.         Msg[2] := '   of 10 elements.  The values are  '    ;
  148.         Msg[3] := 'provided by a random number generator ';
  149.         FOR LineNumber := 1 TO 3 DO
  150.           BEGIN
  151.             Line[LineNumber] := Add_DItem(Box,G_String,None,2,LineNumber + 1,
  152.                                 30,1,   0,Blk);
  153.             Set_DText(Box,Line[lineNumber],Msg[LineNumber],
  154.                      System_Font,TE_Left);
  155.           END;
  156.         ExitButton := Add_DItem(Box,G_Button,
  157.                                 Selectable | Default |Exit_Btn,
  158.                                 15,6,10,1,        0,Blk);
  159.         Set_DText(Box,ExitButton,' So be it ', System_Font,TE_Left);
  160.         Center_Dialog(Box);
  161.         Pushed := Do_Dialog(Box,0);
  162.         End_Dialog(Box);
  163.       END {displayarraymessage};
  164.  
  165.     PROCEDURE FillArray;
  166.       VAR
  167.         i    :  integer;
  168.         Seed : long_integer;
  169.  
  170.       FUNCTION Random : long_integer;
  171.         XBIOS (17);
  172.  
  173.       BEGIN  {fillarray}
  174.         FOR i := LB To UB DO
  175.           PlotArr[i] := Random;
  176.       END{fillarray};
  177.  
  178.     PROCEDURE GetBoundsOfX
  179.           (VAR LowerBound : real;
  180.            VAR UpperBound : real);
  181.       CONST
  182.         Width = 25;
  183.         Height = 7;
  184.       TYPE
  185.         T118 = (integertype,longtype,realtype);
  186.       VAR
  187.         Box : Dialog_Ptr;
  188.         Line1,Line2,Line3 : integer;
  189.         LinePushed : integer;
  190.         UserString : str255;
  191.         dummyint :integer;
  192.         dummylong : long_integer;
  193.  
  194.       PROCEDURE ConvertStringToNumber
  195.                         (S   : string;  {ASCII string to convert}
  196.                  VAR Success : boolean; {All errors reflected here}
  197.                   ResultType : T118;    {Select 1 of 3 possible types}
  198.           VAR  IntegerResult : integer;
  199.           VAR     LongResult : long_integer;
  200.           VAR     RealResult : real);
  201.         CONST
  202.           TerminatorChar = '%';
  203.         TYPE
  204.           T1 = ARRAY [1..80] OF char;
  205.         VAR
  206.           C : ARRAY[1..80] OF char;
  207.           LeftPart,RightPart,ExponentArray : T1;
  208.           LeftPartIx,RightPartIx,ExpIx : integer;
  209.           ParsingError : boolean;
  210.           PositiveNumber,PositiveExponent : boolean;
  211.           i : integer;
  212.           Exponent : integer;
  213.           X1    : real;
  214.  
  215.         PROCEDURE ProcessError
  216.                    (Signature : integer); {A unique, meaningless number
  217.                                            Large enough so that a search
  218.                                            with a text editor can find it
  219.                                            easily.  The numbers are all
  220.                                            of the form '12dd'         .}
  221.           {Sets the global error flag and records the signature
  222.           for possible debug ouput.                          }
  223.           BEGIN
  224.             (*WriteLn('Error in ConvertStringToNumber. Error code:',
  225.                        Signature);*)
  226.             Success     := FALSE;
  227.           END;
  228.  
  229.         PROCEDURE TestTrailingBlank(i : integer);
  230.           VAR
  231.             j : integer;
  232.           BEGIN
  233.             IF c[i] = ' '
  234.               THEN TestTrailingBlank(i + 1)
  235.             ELSE
  236.               IF c[i] = TerminatorChar
  237.                 THEN BEGIN  {do nothing}  END
  238.                 ELSE
  239.                   BEGIN
  240.                     ParsingError := TRUE;
  241.                     ProcessError(1201);
  242.                   END;
  243.           END{TestTrailingBlank};
  244.  
  245.         PROCEDURE TestExponentDigit(i : integer);
  246.           BEGIN
  247.             IF c[i] IN ['0'..'9'] THEN
  248.               BEGIN
  249.                 ExpIx := ExpIx + 1;
  250.                 IF ExpIx > 2 THEN
  251.                   BEGIN
  252.                     ParsingError := TRUE;
  253.                     ProcessError(1202);
  254.                   END;
  255.                 ExponentArray[ExpIx] := C[i];
  256.                 TestExponentDigit(i + 1);
  257.               END
  258.             ELSE
  259.               TestTrailingBlank(i);
  260.           END{testexponentdigit};
  261.  
  262.         PROCEDURE TestSignOfExponent(i : integer);
  263.           BEGIN
  264.             CASE c[i] OF
  265.               '-' : BEGIN
  266.                       PositiveExponent := FALSE;
  267.                       TestExponentDigit(i + 1);
  268.                     END;
  269.               '+' : TestExponentDigit(i + 1);
  270.               OTHERWISE : TestExponentDigit(i);
  271.             END{case};
  272.           END{testsignofexponent};
  273.  
  274.         PROCEDURE TestLetterE(i : integer);
  275.           BEGIN
  276.             IF c[i] IN ['e','E']
  277.               THEN TestSignOfExponent(i + 1)
  278.               ELSE TestTrailingBlank(i);
  279.           END{TestLetterE};
  280.  
  281.         PROCEDURE Test4TrailingDigit(i : integer);
  282.           BEGIN
  283.             IF c[i] IN ['0'..'9'] THEN
  284.               BEGIN
  285.                 RightPartIx := RightPartIx + 1;
  286.                 RightPart[RightPartIx] := c[i];
  287.                 Test4TrailingDigit(i + 1);
  288.               END
  289.             ELSE
  290.               TestLetterE(i);
  291.           END{test4trailingdigit};
  292.  
  293.         PROCEDURE TestDecimal(i : integer);
  294.           BEGIN
  295.             IF c[i]= '.'
  296.             THEN Test4TrailingDigit(i + 1)
  297.             ELSE TestLetterE(i);
  298.           END{testdecimal};
  299.  
  300.         PROCEDURE TestLeadingDigit(i : integer);
  301.           BEGIN
  302.             IF c[i] IN ['0'..'9']
  303.               THEN
  304.                 BEGIN
  305.                   LeftPartIx := LeftPartIx + 1;
  306.                   LeftPart[LeftPartIx] := c[i];
  307.                   TestLeadingDigit(i + 1);
  308.                 END
  309.               ELSE
  310.                 TestDecimal(i);
  311.           END{testleadingdigit};
  312.  
  313.         PROCEDURE Test2SignOfNumber(i : integer);
  314.           BEGIN
  315.             CASE C[i] OF
  316.               '-' : BEGIN
  317.                       PositiveNumber := FALSE;
  318.                       TestLeadingDigit(i + 1);
  319.                     END;
  320.               '+' : TestLeadingDigit (i + 1);
  321.               OTHERWISE : TestLeadingDigit(i);
  322.             END{case};
  323.           END{test2signofnumber};
  324.  
  325.         PROCEDURE Test3LeadingBlank(i: integer);
  326.           BEGIN
  327.             IF c[i] = ' '
  328.             THEN Test3LeadingBlank(i+1)
  329.             ELSE Test2SignOfNumber(i);
  330.           END {test3leadingblank};
  331.  
  332.         FUNCTION StringToLongInteger
  333.                                (Arr : T1;
  334.                                   N : integer
  335.                                    ): long_integer;
  336.           VAR
  337.             Temp : long_integer;
  338.             i    : integer;
  339.           BEGIN
  340.             Temp := 0;
  341.             {Don't count excess digits in real as an error.
  342.             Simply discard them.  They are treated as an
  343.             error in the case of integers and long_integers.
  344.             Its harder for the user to
  345.             deal with real numbers so give him /her a little leeway.}
  346.             IF (N > 10) THEN
  347.               N := 10;
  348.             IF (N = 10) AND (Arr[1] > '2') THEN
  349.               N := 9;
  350.             FOR i := 1 TO N DO
  351.               BEGIN
  352.                 Temp := Temp * 10;
  353.                 Temp := Temp + ORD(Arr[i]) - ORD('0');
  354.               END;
  355.             StringToLongInteger := Temp;
  356.           END{stringtolonginteger};
  357.  
  358.         PROCEDURE ConvertLong;
  359.           VAR
  360.             SignChange : boolean;
  361.           BEGIN
  362.             IF (LeftPartIx > 10)
  363.               OR
  364.                 ((LeftPartIx = 10) AND (LeftPart[1] > '2'))
  365.               OR
  366.                 (RightPartIx > 0)
  367.               OR
  368.                 (ExpIx > 0)
  369.               THEN ProcessError(1203)
  370.               ELSE
  371.                 BEGIN
  372.                   LongResult := StringToLongInteger(LeftPart,LeftPartIx);
  373.                   IF ResultType = IntegerType
  374.                     THEN
  375.                       IF (LongResult > 32767) OR (LongResult < -32767)
  376.                         THEN ProcessError(1204)
  377.                         ELSE IntegerResult := INT(LongResult);
  378.                 END {success};
  379.             IF NOT(PositiveNumber) THEN
  380.               BEGIN   {Affix the proper sign.}
  381.                 IntegerResult := - IntegerResult;
  382.                 LongResult    := - LongResult;
  383.               END;
  384.             {Overflow will cause sign inversion on
  385.             numbers > 2,147,483,647 but only 10 digits long.}
  386.             SignChange := NOT PositiveNumber AND (LongResult > 0);
  387.             SignChange := SignChange
  388.                              OR
  389.                           PositiveNumber AND (LongResult < 0);
  390.             IF (ResultType = LongType) AND SignChange
  391.               THEN ProcessError(1205);
  392.           END{ConvertLong};
  393.  
  394.         PROCEDURE ConvertReal
  395.                    (VAR X1 : real);
  396.           VAR
  397.             i,j        : integer;
  398.             LongTemp   : long_integer;
  399.  
  400.           PROCEDURE ScalePositiveExponent;
  401.             VAR
  402.               BigNumber : real;
  403.             BEGIN
  404.               {Next statement due to flaw in level 1.0 compiler.
  405.               Compiler tries to generate large constants and fails.
  406.               Have to make a big number out of little numbers.     }
  407.               BigNumber := 1.7E7 * 1.0E10 * 1.0E10
  408.                            * 1.0E10 ;     {(2 ^ 127 - 1) / 10.0}
  409.               WHILE (X1 < BigNumber) AND (Exponent > 0) DO
  410.                 BEGIN
  411.                   X1 := X1 * 10.0;
  412.                   Exponent := Exponent - 1;
  413.                 END;
  414.               IF Exponent > 0 THEN
  415.                 ProcessError(1206);
  416.             END {sclaepositiveexponent};
  417.  
  418.           PROCEDURE ScaleNegativeExponent;
  419.             BEGIN
  420.               {Don't detect underflows.}
  421.               WHILE (Exponent <> 0) DO
  422.                 BEGIN
  423.                   X1 := X1 / 10.0;
  424.                   Exponent := Exponent + 1;
  425.                 END;
  426.             END {scalenegativeexponent};
  427.  
  428.           PROCEDURE JoinTwoNumbers;
  429.             VAR
  430.               i, j : integer;
  431.               LeftNonZero,RightNonZero : boolean;
  432.  
  433.             FUNCTION NonZeroDigit
  434.                             (Arr : T1;
  435.                                N : integer)
  436.                                  : boolean;
  437.               VAR
  438.                 i    : integer;
  439.                 Find : boolean;
  440.               BEGIN {nonzerodigit}
  441.                 Find := FALSE;
  442.                 FOR i := 1 TO N DO
  443.                   IF Arr[i] In ['1'..'9'] THEN
  444.                     Find := TRUE;
  445.                 NonZeroDigit := Find;
  446.               END {nonzerodigit};
  447.  
  448.             BEGIN   {jointwonumbers}
  449.               {The combined result will appear in LeftPart,LeftPartIx
  450.               and Exponent}
  451.               LeftNonZero  := NonZeroDigit(LeftPart,LeftPartIx);
  452.               RightNonZero := NonZeroDigit(RightPart,RightPartIx);
  453.               CASE LeftNonZero OF
  454.                 TRUE : CASE RightNonZero OF
  455.                          TRUE  : BEGIN
  456.                                    {Append the part after the
  457.                                    decimal to the part
  458.                                    before the decimal.}
  459.                                    j := 0;
  460.                                    FOR i := LeftPartIx + 1 TO
  461.                                      LeftPartIx + RightPartIx DO
  462.                                      BEGIN
  463.                                        j := j + 1;
  464.                                        LeftPart[i] := RightPart[j];
  465.                                        Exponent := Exponent - 1;
  466.                                      END;
  467.                                    LeftPartIx := LeftPartIx + RightPartIx;
  468.                                  END {true};
  469.                          FALSE : {Nothing to do} ;
  470.                        END {case};
  471.                 FALSE : CASE RightNonZero OF
  472.                           TRUE : BEGIN
  473.                                    {A once in a lifetime chance to use
  474.                                    the following statement!}
  475.                                    LeftPart := RightPart;
  476.                                    LeftPartIx := RightPartIx;
  477.                                    Exponent := Exponent - RightPartIx;
  478.                                  END;
  479.  
  480.                           FALSE : BEGIN {'0.0' and its variants}
  481.                                     LeftPart[1] := '0';
  482.                                     LeftPartIx := 1;
  483.                                     Exponent := 0;
  484.                                   END;
  485.                         END {case};
  486.               END {case};
  487.             END {jointwonumbers};
  488.  
  489.           FUNCTION StringToReal
  490.                           (Arr : T1;
  491.                              N : integer)
  492.                                : real;
  493.             VAR
  494.               i : integer;
  495.               x : real;
  496.             BEGIN {stringtoreal}
  497.               X := 0;
  498.               FOR i := 1 TO N DO
  499.                 BEGIN
  500.                   x := x * 10.0;
  501.                   x := x + ORD(Arr[i]) - ORD('0');
  502.                 END;
  503.               StringToReal := X;
  504.             END {stringtoreal};
  505.  
  506.           BEGIN {convertreal}
  507.             IF PositiveExponent
  508.               THEN Exponent :=
  509.                             INT(StringToLongInteger(ExponentArray,ExpIx))
  510.               ELSE Exponent :=
  511.                           - INT(StringToLongInteger(ExponentArray,ExpIx));
  512.             JoinTwoNumbers;
  513.             {Now convert an array of characters into a positive real number.}
  514.             X1 := StringToReal(LeftPart,LeftPartIx);
  515.             IF Exponent <> 0 THEN
  516.               CASE Exponent >= 0 OF
  517.                 TRUE  : ScalePositiveExponent;
  518.                 FALSE : ScaleNegativeExponent;
  519.               END {case};
  520.             {Now affix the proper sign.}
  521.             IF NOT PositiveNumber THEN
  522.               X1 := - X1;
  523.           END{convertreal};
  524.  
  525.         PROCEDURE ParseTheString;
  526.           BEGIN
  527.       {The next statement is the first in a series of calls to evaluate
  528.       the input.  They all work on array c and use i as an index into the
  529.       array.  If, on return to this point, the error flag is not set,
  530.       the components of the number have been isolated into sign flags and
  531.       arrays containing digits. Many of the calls are recursive. }
  532.             Test3LeadingBlank(i + 1);
  533.       {Parsing is done.  The string has been 'atomized' into its
  534.       fundamental components.                                    }
  535.           END {parsethestring};
  536.  
  537.         BEGIN {convertstringtonumber}
  538.           {Move string to array since Personal Pascal strings in level 1.0
  539.           are flakey.}
  540.           FOR i := 1 TO LENGTH(S) DO
  541.             C[i] := S[i];
  542.           C[LENGTH(S) + 1] := TerminatorChar;
  543.           i := 0;
  544.           LeftPartIx := 0;   RightPartIx := 0;   ExpIx := 0;
  545.           ParsingError     := FALSE;
  546.           PositiveNumber   := TRUE;     PositiveExponent := TRUE;
  547.           ParseTheString;
  548.           Success := NOT ParsingError AND
  549.                      ( (LeftPartIx > 0) OR (RightPartIx > 0)  );
  550.           {There is a valid Pascal number.  It is not necessarily
  551.           convertible to the type wanted.}
  552.           IF Success THEN
  553.             CASE ResultType OF
  554.               IntegerType, LongType : ConvertLong;
  555.               RealType              : ConvertReal(RealResult);
  556.             END;
  557.         END {Convertstringtonumber};
  558.  
  559.       BEGIN   {getboundsofx}
  560.         Box := New_Dialog(4,0,0,Width,Height);
  561.         Line1 := Add_DItem(Box,G_String,None,7,2,11,1,0,Black);
  562.         Set_DText(Box,Line1,'RANGE OF X',System_Font,TE_Left);
  563.         Line2 := Add_DItem(Box,G_FText,Editable,1,3,21,1,   NoBorder,Blk);
  564.         Set_DEdit(Box,Line2,
  565.            'Lower bound:_________', 'xxxxxxxxx', '',
  566.            System_Font,TE_Center);
  567.         Line3 := Add_DItem(Box,G_FText,Editable | Default,
  568.                  1,4,21,1,   noBorder,Blk);
  569.         Set_DEdit(Box,Line3,
  570.              'Upper bound:_________', 'xxxxxxxxx', '',
  571.              System_Font,TE_Center);
  572.         Center_Dialog(Box);
  573.         End_Dialog(Box);
  574.         LinePushed := Do_Dialog(Box,Line2);
  575.         Get_DEdit(Box,Line2,UserString);
  576.         ConvertStringToNumber(UserString,OK1,realtype,dummyint,
  577.                               dummylong,LowerBound);
  578.         Get_DEdit(Box,Line3,UserString);
  579.         ConvertStringToNumber(UserString,OK2,realtype,dummyint,
  580.                               dummylong,UpperBound);
  581.       END{getboundsofx};
  582.  
  583.     PROCEDURE FudgeNumber(VAR X : real);
  584.       BEGIN
  585.         {This is needed becuase of the rather cavalier way
  586.         Personal Pascal treats error messages on ROUND(X).}
  587.         IF X > MAXINT THEN
  588.           BEGIN
  589.             X := MAXINT;
  590.             NumbersFudged := TRUE;
  591.           END;
  592.         IF X < - MAXINT THEN
  593.           BEGIN
  594.             X := - MAXINT;
  595.             NumbersFudged := TRUE;
  596.           END;
  597.       END {fudgenumbers};
  598.  
  599.     PROCEDURE DisplayInputRules;
  600.       CONST
  601.         W = 45;
  602.         H = 10;
  603.       VAR
  604.         Box : dialog_ptr;
  605.         Line : ARRAY [1..10] OF integer;
  606.         Msg : ARRAY [1..10] OF string;
  607.         LineNbr : integer;
  608.         Choice : tree_index;
  609.         Retry,Abort : integer;
  610.       BEGIN {displayinputrules}
  611.         Msg[1] := '        Faulty format on input data.        ';
  612.         Msg[2] := '        Use the rules of Pascal for         ';
  613.         Msg[3] := 'integers, long integers, or real variables. ';
  614.         Msg[4] := '            Valid examples:                 ';
  615.         Msg[5] := '     25          18.3            -85.34     ';
  616.         Msg[6] := '   139.e5      -37E-18          1234567.8   ';
  617.         Box := New_Dialog(10,0,0,W,H);
  618.         FOR LineNbr := 1 TO 6 DO
  619.           BEGIN
  620.             Line[LineNbr] := Add_DItem(Box,G_String,None,
  621.                          1,LineNbr,30,1,       NoBorder,Blk);
  622.             Set_DText(Box,Line[LineNbr],Msg[lineNbr], System_Font,TE_Left);
  623.           END;
  624.         Abort :=  Add_Ditem(Box,G_Button,Selectable | Exit_Btn,
  625.                 8,8,7,1,     NoBorder,Blk);
  626.         Set_DText(Box,Abort,' ABORT ',System_Font,TE_CEnter);
  627.         Retry := Add_DItem(Box,G_Button,Selectable | Default | Exit_btn,
  628.                  30,8,7,1,     NoBorder,Blk);
  629.         Set_DText(Box,Retry,' RETRY ',System_Font,TE_Center);
  630.         Center_Dialog(Box);
  631.         Choice := Do_Dialog(Box,0);
  632.         End_Dialog(Box);
  633.         IF choice = Abort THEN
  634.           HALT;
  635.       END {displayinputrules};
  636.  
  637.     PROCEDURE DisplayUpperVsLower;
  638.       CONST
  639.         W = 45;
  640.         H = 10;
  641.       VAR
  642.         Box : dialog_ptr;
  643.         Line : ARRAY [1..10] OF integer;
  644.         Msg : ARRAY [1..10] OF string;
  645.         LineNbr : integer;
  646.         Choice : tree_index;
  647.         Retry,Abort : integer;
  648.       BEGIN {displayinputrules}
  649.         Msg[3] := '          The upper bound must be           ';
  650.         Msg[4] := '       greater than the lower bound.        ';
  651.         Box := New_Dialog(10,0,0,W,H);
  652.         FOR LineNbr := 3 TO 4 DO
  653.           BEGIN
  654.             Line[LineNbr] := Add_DItem(Box,G_String,None,
  655.                          1,LineNbr,30,1,    NoBorder,Blk);
  656.             Set_DText(Box,Line[LineNbr],Msg[lineNbr],
  657.                       System_Font,TE_Left);
  658.           END;
  659.         Abort :=  Add_Ditem(Box,G_Button,Selectable | Exit_Btn,
  660.                  8,7,7,1,   NoBorder,Blk);
  661.         Set_DText(Box,Abort,' ABORT ',System_Font,TE_CEnter);
  662.         Retry := Add_DItem(Box,G_Button,Selectable | Default | Exit_btn,
  663.                 30,7,7,1,   NoBorder,Blk);
  664.         Set_DText(Box,Retry,' RETRY ',System_Font,TE_Center);
  665.         Center_Dialog(Box);
  666.         Choice := Do_Dialog(Box,0);
  667.         End_Dialog(Box);
  668.         IF choice = Abort THEN
  669.            HALT;
  670.       END {displayuppervslower};
  671.  
  672.     PROCEDURE Display2ArrayBounds;
  673.       CONST
  674.         W = 45;
  675.         H = 10;
  676.       TYPE
  677.         S06 = string[6];
  678.       VAR
  679.         Box  : dialog_ptr;
  680.         Line : ARRAY [1..10] OF integer;
  681.         Msg  : ARRAY [1..10] OF string;
  682.         LineNbr : integer;
  683.         Choice : tree_index;
  684.         Retry,Abort : integer;
  685.         S1, S2 : S06;
  686.  
  687.       PROCEDURE IntegerToString
  688.                        (VAR S : S06;
  689.                             N : integer);
  690.         VAR
  691.           M, i, Digit : integer;
  692.           SIndex : integer;
  693.           NbrDeleted : integer;
  694.         BEGIN
  695.           IF N < 0
  696.            THEN
  697.              BEGIN
  698.                IntegerToString(S, - N);   {Note recursion.}
  699.                S[1] := '-';
  700.              END
  701.            ELSE
  702.              BEGIN  {conversion of positive number}
  703.                S := '' ;
  704.                M := 10000;
  705.                FOR i := 1 TO 5 DO
  706.                  BEGIN
  707.                    Digit := N DIV M;
  708.                    S := CONCAT(S, CHR(Digit + ORD ('0'))  );
  709.                    N := N MOD M;
  710.                    M := M DIV 10;
  711.                  END;
  712.                {Create a space for a possible minus sign.}
  713.                S := CONCAT('0', S);
  714.                {Change leading zeros to blanks.}
  715.                i := 1;
  716.                WHILE (i < 6) AND (S[i] = '0') DO
  717.                  BEGIN
  718.                    s[i] := ' ';
  719.                    i := i + 1;
  720.                  END;
  721.              END;
  722.         END {integertostring};
  723.  
  724.       BEGIN {display2arraybounds}
  725.         Msg[1] := '             BOUNDS OF ARRAY';
  726.         Msg[2] := '           LOWER           UPPER';
  727.         IntegerToString(S1,LB);
  728.         IntegerToString(S2,UB);
  729.         Msg[3] := CONCAT('Actual     ',  S1, '          ',   S2 );
  730.         IntegerToString(S1,ROUND(XMin));
  731.         IntegerToString(S2,ROUND(XMax));
  732.         IF NumbersFudged THEN
  733.           BEGIN
  734.             S1 := '   ???';
  735.             S2 := '   ???';
  736.           END;
  737.         Msg[4] := CONCAT('You want   ', S1,  '          ',   S2 );
  738.         Msg[5] := '  ';
  739.         Msg[6] := '    ';
  740.         Box := New_Dialog(10,0,0,W,H);
  741.         FOR LineNbr := 1 TO 6 DO
  742.           BEGIN
  743.             Line[LineNbr] := Add_DItem(Box,G_String,None,
  744.                          1,LineNbr,30,1,       NoBorder,Blk);
  745.             Set_DText(Box,Line[LineNbr],Msg[lineNbr], System_Font,TE_Left);
  746.           END;
  747.         Abort :=  Add_Ditem(Box,G_Button,Selectable | Exit_Btn,
  748.                11,6,7,1,     NoBorder,Blk);
  749.         Set_DText(Box,Abort,' ABORT ',System_Font,TE_CEnter);
  750.         Retry := Add_DItem(Box,G_Button,Selectable | Default | Exit_btn,
  751.                  27,6,7,1,     NoBorder,Blk);
  752.         Set_DText(Box,Retry,' RETRY ',System_Font,TE_Center);
  753.         Center_Dialog(Box);
  754.         Choice := Do_Dialog(Box,0);
  755.         End_Dialog(Box);
  756.         IF choice = Abort THEN
  757.           HALT;
  758.       END {display2arraybounds};
  759.  
  760.     FUNCTION GofX
  761.               (X : real)
  762.                  : real;
  763.       {Caution: You might try other functions here so be warned.
  764.                 1. Personal Pascal sometimes prints a dissembling message
  765.                    on the computation of LN.
  766.                 2. TAN(X) does not detect overflow. }
  767.  
  768.  
  769.       FUNCTION PolyFunction
  770.                         (X : real
  771.                           ): real;
  772.         VAR
  773.           OddPower : boolean;
  774.  
  775.         FUNCTION XToTheY
  776.                      (X : real;
  777.                       Y : integer)
  778.                         : real;
  779.           VAR
  780.             Product : real;
  781.             i : integer;
  782.           BEGIN     {xtothey}
  783.             IF Y < 0 THEN
  784.               XToTheY := 1.0 / XToTheY(X, - Y);
  785.             Product := 1;
  786.             FOR i := 1 TO Y DO
  787.               Product := Product * X;
  788.             IF X < 0
  789.               THEN
  790.                 IF ODD(Y)
  791.                   THEN XToTheY := - Product
  792.                   ELSE XToTheY :=   Product
  793.               ELSE
  794.                 XToTheY := Product;
  795.           END {xtothey};
  796.  
  797.         BEGIN {polyfunction}
  798.           {x^4 -7x^3 +11x^2 - 12 }
  799.           PolyFunction := xtothey(x,4) -7*xtothey(x,3) + 11*x*x + 7*x -12;
  800.         END {polyfunction};
  801.  
  802.       FUNCTION NormalFunction
  803.                           (X : real)
  804.                              : real;
  805.         VAR
  806.           K : real;
  807.         BEGIN
  808.           K := 1.0 / SQRT(2.0* 3.1415926536);
  809.           IF x < 0.0
  810.             THEN NormalFunction := NormalFunction( - X)
  811.             ELSE NormalFunction := K * EXP(-(X * X * 0.5));
  812.         END {normaldistfunction} ;
  813.  
  814.  
  815.       BEGIN {GofX}
  816.         CASE Func OF
  817.           Sinusoid   : GofX := SIN(X);
  818.           Parabola   : GofX := X * X + 3 * X - 4;
  819.           Polynomial : GofX := PolyFunction(X);
  820.           NormalDist : GofX := NormalFunction(X);
  821.         END {case};
  822.       END {GofX};
  823.  
  824.     {---------- This is the procedure being demonstrated -------------
  825.     In addition to the parameters listed, There must be available a
  826.     real FUNCTION, GofX which will evaluate the value of the function
  827.     at any point in the interval: lower bound .. upper bound.  Provide
  828.     a dummy function if plotting an array.
  829.     Sample Function:
  830.                 FUNCTION GofX(X : real):real;
  831.  
  832.  
  833.     Editorial.  ISO standard Pascal permits the inclusion of FUNCTIONs
  834.     and PROCEDUREs as parameters to procedures. See, for example,
  835.     'Standard Pascal Reference Manual' by Doug Cooper, pp 83-86.
  836.     Personal Pascal claims to meet the ISO standard, but doesn't permit
  837.     this. (At least, Level 1.0 doesn't).  As a matter of fact, it responds
  838.     in a rather ugly fashion.  Despite this, the guys at OSS did a
  839.     good job!
  840.     }
  841.  
  842.     PROCEDURE XYPlot(
  843.               X1, X2 : real;
  844.              PlotArr : T2;   {Provide a dummy array if plotting a function}
  845.      FunctionOrArray : T1);
  846.       CONST
  847.         TopMargin = 8;
  848.       VAR
  849.         X,Y,W,H : integer;
  850.         mwindow : integer;
  851.         XScaleFactor,YScaleFactor : real;
  852.         XCorrection,YCorrection : long_integer;
  853.         LineNumber : integer;
  854.  
  855.       PROCEDURE MakeWindow;
  856.         VAR
  857.          Title : string;
  858.  
  859.         PROCEDURE PaintWhite;
  860.           BEGIN
  861.             Paint_Color(White);
  862.             Paint_Style(1);     {Normal GEM desktop dither pattern is 5.}
  863.             Hide_Mouse;
  864.             Paint_Rect(X,Y,W,H);
  865.           END {paintwhite};
  866.  
  867.         BEGIN {makewindow}
  868.           ClrScr;
  869.           Text_Color(Black);
  870.           Line_Color(Black);
  871.           X := 100;
  872.           Y := 20;
  873.           W := 540;
  874.           H := 380;
  875.           Title := ' X-Y Plotter ';
  876.           mwindow := new_window(G_Name | G_Close,title,X,Y,W,H);
  877.           open_window(mwindow,X,Y,W,H);
  878.           work_rect(mwindow,X,Y,W,H);
  879.           set_clip(x,y,w,h);
  880.           {The net drawing area is W = 538, H = 360 on monochrome monitor}
  881.           PaintWhite;
  882.           {Allow a margin of 8 pixels on top and bottom.}
  883.           H := H - (2 * TopMargin);
  884.         END {makewindow};
  885.  
  886.       PROCEDURE FillWindow;
  887.         VAR
  888.           RangeX,DeltaX,Biggest,Smallest,Y,RangeY,X : real;
  889.           Xbias,YBias : long_integer;
  890.  
  891.         FUNCTION FofX(x : real) : real;
  892.  
  893.           FUNCTION Interpolate
  894.                            (X : real;
  895.                            X1 : integer;
  896.                            X2 : integer)
  897.                               : real;
  898.             VAR
  899.               Xinc,Y,YQuantum,LowerValue,UpperValue : real;
  900.               LowerIndex,UpperIndex : integer;
  901.             BEGIN
  902.               LowerIndex := TRUNC(X);
  903.               IF (LowerIndex + 1) > X2
  904.                 THEN UpperIndex := X2
  905.                 ELSE UpperIndex := LowerIndex + 1;
  906.               LowerValue := PlotArr[LowerIndex];
  907.               UpperValue := PlotArr[UpperIndex];
  908.               Xinc       := x - LowerIndex;
  909.               YQuantum := PlotArr[UpperIndex] - PlotArr[LowerIndex];
  910.               Y := PlotArr[LowerIndex] + XInc * YQuantum;
  911.               Interpolate := Y;
  912.             END {arrayfunction};
  913.  
  914.           BEGIN   {FofX}
  915.             IF FunctionOrArray = ArrKind
  916.               THEN FofX := Interpolate(X,ROUND(X1),ROUND(X2))
  917.               ELSE FofX := GofX(X);
  918.           END {FofX};
  919.  
  920.         PROCEDURE ComputeRangeXandY;
  921.           BEGIN
  922.             RangeX := XMax - XMin;
  923.             DeltaX := RangeX / W;
  924.             X := XMin;
  925.             Biggest := FofX(XMin);
  926.             Smallest := Biggest;
  927.             REPEAT
  928.               x := x + DeltaX;
  929.               y := FofX(x);
  930.               IF y > Biggest THEN
  931.                 Biggest := y;
  932.               IF y < Smallest THEN
  933.                 Smallest := y;
  934.             UNTIL x >= XMax;
  935.             RangeY := Biggest - Smallest;
  936.           END {computerangexandy};
  937.  
  938.         BEGIN  {fillwindow}
  939.           ComputeRangeXandY;
  940.           {Use absolute addressing on the window.}
  941.           XBias := 640 - W;
  942.           YBias := 400 - H ;
  943.           {Note negtive sign. It corrects for the fact that more 
  944.           positive numbers
  945.           yield a downward, i.e., negative, deflection on the screen.}
  946.           YScaleFactor := - H / RangeY;
  947.           XScaleFactor := W / RangeX;
  948.           {Now plot the curve.}
  949.           x := XMin;
  950.           XCorrection := XBias - LONG_ROUND(XMin * XScaleFactor);
  951.           YCorrection := YBias - (TopMargin) -
  952.                                LONG_ROUND(Biggest * YScaleFactor);
  953.           REPEAT
  954.             y := FofX(x);
  955.             PLOT (
  956.               INT( LONG_ROUND(x * XScaleFactor) + XCorrection ),
  957.               INT( LONG_ROUND(y * YScaleFactor) + YCorrection )       );
  958.             x := x + DeltaX;
  959.           UNTIL x >= XMax;
  960.           {Draw a Y axis.  If its outside the boundary established 
  961.           by set clip,
  962.           it won't show, but no harm done.  We tried.}
  963.           Line_Style(4);  {dash, dot}
  964.           Line(INT(Xbias),INT(YCorrection),  640, INT(YCorrection));
  965.         END{fillwindow};
  966.  
  967.       PROCEDURE TidyScreen;
  968.         BEGIN
  969.           Show_Mouse;
  970.           {Erase the 'compute' message.'}
  971.           CursUp;
  972.           WriteLn('         ');
  973.           CursHome;
  974.           LineNumber := 1;
  975.         END {tidyscreen};
  976.  
  977.       PROCEDURE DetermineCoordinates;
  978.         VAR
  979.           MouseX,MouseY : integer;
  980.           ExitPressed : boolean;
  981.           XatMouse,YatMouse : real;
  982.  
  983.         PROCEDURE GetMEvent
  984.             (VAR MouseX : integer;
  985.              VAR MouseY : integer;
  986.              VAR Stop   : boolean);
  987.           LABEL
  988.             100;
  989.           VAR
  990.             event,dummy,mx,my,emask:integer;
  991.             longdummy : long_integer;
  992.             bdummy   : boolean;
  993.             msg      : message_buffer;
  994.             c        : char;
  995.             key      : integer;
  996.           BEGIN {getmevent}
  997.             100:
  998.             emask := E_Button | E_Message;
  999.             event := get_event(
  1000.                     emask,
  1001.                     $1,     {left button}
  1002.                     $1,     {wait for button down}
  1003.                     $1,     {wait for 1 event}
  1004.                     longdummy,
  1005.                     bdummy,dummy,dummy,dummy,dummy,
  1006.                     bdummy,dummy,dummy,dummy,dummy,
  1007.                     msg,
  1008.                     key,
  1009.                     dummy,dummy,
  1010.                     MouseX,MouseY,
  1011.                     dummy);
  1012.             IF (  ((Event & E_Message) > 0 ) AND (Msg[0] = WM_Closed)  )
  1013.               THEN
  1014.                 Stop := TRUE
  1015.               ELSE
  1016.                 Stop := FALSE;
  1017.             IF ((MouseX < 100) AND (NOT(Stop))) THEN {discard it}
  1018.               GOTO 100;
  1019.           END {getmevent};
  1020.  
  1021.         PROCEDURE PrintupArrows;
  1022.           CONST
  1023.             Device  = 5;   {display control characters on console}
  1024.             UpArrow = 1;        {upward pointing arrow head.}
  1025.           VAR
  1026.             ChPosition : integer;
  1027.             dummy      : integer;
  1028.  
  1029.           FUNCTION BiosConsoleOutput
  1030.                   (DeviceNumber : integer;
  1031.                    ChCode       : integer)
  1032.                                 : integer;
  1033.             BIOS (3);
  1034.  
  1035.           BEGIN {printuparrows}
  1036.             FOR ChPosition := 1 TO 11 Do
  1037.               dummy := BiosConsoleOutput(Device,UpArrow);
  1038.               WriteLn;
  1039.           END {printuparrows};
  1040.  
  1041.         PROCEDURE PrintNice
  1042.                     (X : real);
  1043.           {Minimize the chance that the printing will use the 'E' format.}
  1044.           BEGIN
  1045.             IF (X > -1000) AND (X < 1000)
  1046.               THEN WriteLn(X:9:4)
  1047.               ELSE WriteLn(X:9);
  1048.             END {printnice};
  1049.  
  1050.         BEGIN {determinecoordinates}
  1051.           Set_Mouse(M_Outln_Cross);
  1052.           {Empty the trash.}
  1053.           GetMEvent(MouseX,MouseY,ExitPressed);
  1054.           LineNumber := 1;
  1055.           REPEAT
  1056.             GetMEvent(MouseX,MouseY,ExitPressed);
  1057.             XatMouse := (MouseX - XCorrection) / XScaleFactor;
  1058.             YatMouse := ( MouseY - YCorrection) / YScaleFactor;
  1059.             {Erase old line of arrowheads.}
  1060.             IF LineNumber > 1 THEN
  1061.               BEGIN
  1062.                 CursUp;
  1063.                 WriteLn('            ');
  1064.               END;
  1065.             IF LineNumber > 23 THEN
  1066.               BEGIN
  1067.                 CursHome;
  1068.                 LineNumber := 1;
  1069.               END;
  1070.             PrintNice(XatMouse);
  1071.             PrintNice(YatMouse);
  1072.             PrintUpArrows;
  1073.             LineNumber := LineNumber + 3;
  1074.           UNTIL ExitPressed;
  1075.         END {determinecoordinates};
  1076.  
  1077.       PROCEDURE CleanUp;
  1078.         BEGIN
  1079.           Close_window(MWindow);
  1080.           Delete_Window(MWindow);
  1081.           ClrScr;
  1082.           {Return mouse to its usual form.}
  1083.           Set_Mouse(M_Arrow);
  1084.         END {cleanup};
  1085.  
  1086.       BEGIN {XYPlot  procedure}
  1087.         MakeWindow;
  1088.         WriteLn('computing');
  1089.         FillWindow;
  1090.         TidyScreen;
  1091.         DetermineCoordinates;
  1092.         CleanUp;
  1093.       END{xyplotprocedure};
  1094.  
  1095.      {------------- end of procedure being demonstrated -------------}
  1096.  
  1097.     BEGIN {XYPlotDemo}
  1098.       ChooseFunction;
  1099.       IF Func = ArrayPlot THEN
  1100.         BEGIN
  1101.           DisplayArrayMessage;
  1102.           FillArray;
  1103.         END;
  1104.       LOOP
  1105.         GetBoundsOfX(XMin,XMax);
  1106.         NumbersOK := OK1 AND OK2;
  1107.         IF Func = ArrayPlot
  1108.           THEN
  1109.             BEGIN
  1110.               NumbersFudged := FALSE;
  1111.               FudgeNumber(XMin);
  1112.               FudgeNumber(XMax);
  1113.               IF NumbersOK AND (XMax > XMin) AND
  1114.               NOT NumbersFudged AND
  1115.               (ROUND(XMin) >= LB) AND (ROUND(XMax) <= UB)
  1116.                 THEN
  1117.                   Done := TRUE
  1118.                 ELSE
  1119.                   Done := FALSE
  1120.             END  {array plot}
  1121.           ELSE {function plot}
  1122.             IF NumbersOK AND (XMax > XMin)
  1123.               THEN
  1124.                 Done := TRUE
  1125.               ELSE
  1126.                 Done := FALSE;
  1127.         EXIT IF Done;
  1128.         IF NOT NumbersOK
  1129.           THEN
  1130.             DisplayInputRules
  1131.           ELSE
  1132.             IF XMax <= XMin
  1133.               THEN
  1134.                 DisplayUppervsLower
  1135.               ELSE         {out of bounds and array plot wanted}
  1136.                 Display2ArrayBounds;
  1137.       END {loop};
  1138.       IF Func = ArrayPlot
  1139.         THEN PlotKind := ArrKind
  1140.         ELSE PlotKind := FunctionKind;
  1141.       XYPlot(XMin,XMax,PlotArr,PlotKind);
  1142.     END {xyplotdemo};
  1143.  
  1144.   BEGIN     {main program}
  1145.     IF ColorMonitor THEN
  1146.       BEGIN
  1147.         WriteLn('Program only works with monochrome monitor.');
  1148.         WriteLn('Sorry.');
  1149.         ReadLn;
  1150.         HALT;
  1151.       END;
  1152.     IF init_gem >= 0 THEN
  1153.       BEGIN
  1154.         XYPlotDemo;
  1155.         Exit_GEM;
  1156.       END{of gem};
  1157.   END{program}.
  1158.