home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / BAFGAB.ZIP / TRAVESTY.PAS
Encoding:
Pascal/Delphi Source File  |  1988-05-11  |  10.1 KB  |  281 lines

  1. PROGRAM travesty(input, output, f, o);                 {Kenner / O'Rourke 5/9/84}
  2.  
  3. {  Entered and modified by Sam Smith --- 3-Nov-1984                       }
  4.  
  5. {  This is based on Brian Hayes's article in Scientific American, 11/83.  }
  6. {  It scans a text and generates an n-order simulation of its letter      }
  7. {  combinations. For order n, the relation of input to output is exactly: }
  8. {            " Any pattern n characters long in the output                }
  9. {              has occurred somewhere in the input with                   }
  10. {              about the same frequency."                                 }
  11. {  Input should be ready on disk. Program asks how many characters of     }
  12. {  output you want. It asks for the "Order" - i.e. how long a string of   }
  13. {  characters will be cloned to the output when found. You are asked for  }
  14. {  the name of the input file, and offered a "Verse" option. If you       }
  15. {  select this, and if the input has a "|" character at the end of each   }
  16. {  line, words that end lines in the original will terminate output       }
  17. {  lines. Otherwise, output lines will average 50 characters in length.   }
  18. {  Output may be directed to a disk file, to the console (file "CON") or  }
  19. {  to a printer (file "PRN").                                             }
  20.  
  21. CONST
  22.   ArraySize = 21000;                {Maximum number of text characters}
  23.   MaxPat = 9;                       {Maximum pattern length}
  24.  
  25. VAR
  26.   BigArray: PACKED ARRAY [1..ArraySize] of CHAR;
  27.   FreqArray, StartSkip: ARRAY[' '..'|'] of INTEGER;
  28.   Pattern: PACKED ARRAY [1..MaxPat] of CHAR;
  29.   SkipArray: ARRAY [1..ArraySize] of INTEGER;
  30.   OutChars: INTEGER;                 {Number of characters to be output}
  31.   PatLength: INTEGER;
  32.   f: TEXT;
  33.   o: TEXT;
  34.   CharCount: INTEGER;               {Characters output so far}
  35.   Verse, NearEnd: BOOLEAN;
  36.   NewChar: CHAR;
  37.   TotalChars: INTEGER;              {Total chars input + wraparound}
  38.  
  39. PROCEDURE InParams;                 {Obtains user's instructions}
  40. VAR
  41.   InFile: STRING [12];
  42.   OutFile: STRING [12];
  43.   Response:CHAR;
  44. BEGIN
  45.   WRITELN('Number of characters to be output?');
  46.   READLN(OutChars);
  47.   REPEAT
  48.     WRITELN('What order? <2-',MaxPat,'>');
  49.     READLN(PatLength)
  50.   UNTIL (PatLength IN [2..MaxPat]);
  51.   PatLength := Patlength - 1;
  52.   WRITELN('Name of input file?');
  53.   READLN(InFile);
  54.   ASSIGN(f,InFile);
  55.   RESET(f);
  56.   WRITELN('Name of output file?');
  57.   READLN(OutFile);
  58.   ASSIGN(o,OutFile);
  59.   REWRITE(o);
  60.   WRITELN('Prose or Verse? <p/v>');
  61.   READLN(Response);
  62.   IF(Response = 'V') OR (Response = 'v') then
  63.     Verse := true
  64.   ELSE Verse := false
  65. END;                                {Procedure InParams}
  66.  
  67. PROCEDURE ClearFreq;
  68. { FreqArray is indexed by 93 probable ASCII characters, from " " to  }
  69. { "|". Its elements are all set to zero.                             }
  70. VAR
  71.   ch: CHAR;
  72. BEGIN
  73.   FOR ch := ' ' to '|' DO
  74.     FreqArray[ch] := 0
  75. END;                                {Procedure ClearFreq}
  76.  
  77. PROCEDURE NullArrays;
  78. { Fill BigArray and Pattern with nulls }
  79. VAR
  80.   j: INTEGER;
  81. BEGIN
  82.   FOR j := 1 TO ArraySize DO
  83.     BigArray[j] := CHR(0);
  84.   FOR j := 1 TO MaxPat DO
  85.     Pattern[j] := CHR(0);
  86. END;                                {Procedure NullArrays}
  87.  
  88. PROCEDURE FillArray;
  89. { Moves text file from disk into BigArray, cleaning it up and reducing  }
  90. { any run of blanks to a single blank. Then copies to end of array a    }
  91. { string of its opening characters as long as the Pattern, in effect    }
  92. { wrapping the end to the beginning.                                    }
  93. VAR
  94.   Blank : BOOLEAN;
  95.   ch: CHAR;
  96.   j: INTEGER;
  97.  
  98.   PROCEDURE Cleanup;
  99.   {  Cleans Carriage Returns, Linefeeds, and Tabs out of input stream.  }
  100.   {  All are changed to blanks.                                         }
  101.   BEGIN
  102.     IF ((ch = CHR(13))     {CR}
  103.        OR (ch = CHR(10))   {LF}
  104.        OR (ch = CHR(9))   {TAB}
  105.        OR (ch = '_'))      {underscore}
  106.     THEN ch := ' ';
  107.     IF NOT (ch in [' '..'|']) THEN ch := ' '
  108.   END;                              {Procedure Cleanup}
  109.  
  110. BEGIN                               {Procedure FillArray}
  111.   j := 1;
  112.   Blank := false;
  113.   WHILE (NOT EOF(f)) AND (j <= (ArraySize-MaxPat)) DO
  114.   BEGIN {While not EOF}
  115.     READ (f,ch);
  116.     ch := CHR(INTEGER(ch) MOD 128);
  117.     Cleanup;
  118.     BigArray[j] := ch;
  119.     IF ch = ' ' THEN Blank := true;
  120.     j := j + 1;
  121.     WHILE (Blank AND (NOT EOF(f))
  122.       AND (j <= (ArraySize-MaxPat))) DO
  123.     BEGIN {While Blank}             {When a blank has just been printed, }
  124.       READ(f,ch);                   {Blank is true. Succeeding blanks are}
  125.       ch := CHR(INTEGER(ch) MOD 128);
  126.       Cleanup;                      {skipped, thus stopping runs.        }
  127.       IF ch <> ' ' THEN
  128.       BEGIN {if}
  129.         Blank := false;
  130.         BigArray[j] := ch;          {To BigArray if not a blank          }
  131.         j := j + 1
  132.       END {if}
  133.     END   {While Blank}
  134.   END; {While Not EOF}
  135.   TotalChars := j-1;
  136.   IF BigArray[TotalChars] <> ' ' THEN
  137.   BEGIN  {If no blank at end of text, append one}
  138.     TotalChars := TotalChars + 1;
  139.     BigArray[TotalChars] := ' ';
  140.   END;
  141.   { Copy front of array to back to simulate wraparound }
  142.   FOR j := 1 to PatLength DO
  143.     BigArray[TotalChars+j] := BigArray[j];
  144.   TotalChars := TotalChars + PatLength;
  145.   WRITELN('Characters read, plus wraparound = ',TotalChars:4)
  146. END;                                {Procedure FillArray}
  147.  
  148. PROCEDURE FirstPattern;
  149. { User selects "order" of operation, an integer, n, in the range 1..9.    }
  150. { The input text will henceforth be scanned in n-sized chunks. The first  }
  151. { n-1 characters of the input file are placed in the "Pattern" array. The }
  152. { pattern is written at the head of output.                               }
  153. VAR
  154.   j: INTEGER;
  155. BEGIN
  156.   FOR j:= 1 TO PatLength DO
  157.     Pattern[j] := BigArray[j];
  158.   CharCount := PatLength;
  159.   NearEnd := false;
  160.   IF Verse THEN WRITE(o,' ');              { Align first line }
  161.   FOR j := 1 to PatLength DO
  162.     WRITE(o,Pattern[j])
  163. END;                                {Procedure FirstPattern}
  164.  
  165. PROCEDURE InitSkip;
  166. { The i-th entry of SkipArray contains the smallest index j > i such that }
  167. { BigArray[j] = BigArray[i]. Thus SkipArray links together all identical  }
  168. { characters in BigArray. StartSkip contains the index of the first oc-   }
  169. { currence of each character. These two arrays are used to skip the match-}
  170. { ing array through the text, stopping only at locations whose character  }
  171. { matches the first character in pattern.                                 }
  172. VAR
  173.   ch: CHAR;
  174.   j: INTEGER;
  175. BEGIN
  176.   FOR ch:= ' ' TO '|' DO
  177.     StartSkip[ch] := TotalChars + 1;
  178.   FOR j := TotalChars DOWNTO 1 DO
  179.   BEGIN
  180.     ch := BigArray[j];
  181.     SkipArray[j] := StartSkip[ch];
  182.     StartSkip[ch] := j
  183.   END
  184. END;                                {Procedure InitSkip}
  185.  
  186. PROCEDURE Match;
  187. { Checks BigArray for strings that match Pattern; for each match found,   }
  188. { notes following character and increments its count in FreqArray. Pos-   }
  189. { ition for first trial comes from StartSkip; thereafter positions are    }
  190. { taken from SkipArray. Thus no sequence is checked unless its first      }
  191. { character is already known to match first character of Pattern.         }
  192. VAR
  193.   i: INTEGER;       {One location before start of the match in BigArray}
  194.   j: INTEGER;       {Index into Pattern}
  195.   Found: BOOLEAN;   {True if there is a match from i+1 to i+j-1}
  196.   ch1: CHAR;        {The first character in Pattern; used for skipping}
  197.   NxtCh: CHAR;
  198. BEGIN {Procedure Match}
  199.   ch1 := Pattern[1];
  200.   i := StartSkip[ch1]-1; {i is one to the left of the match start}
  201.   WHILE (i <= TotalChars - PatLength - 1) DO
  202.   BEGIN {while}
  203.     j := 1;
  204.     Found := true;
  205.     WHILE (Found AND (j <= PatLength)) DO
  206.       IF BigArray[i+j] <> Pattern[j]
  207.         THEN Found := false   {Go through pattern till match fails}
  208.         ELSE j := j + 1;
  209.     IF Found THEN
  210.     BEGIN                   {Note next char and increment FreqArray}
  211.       NxtCh := BigArray[i + PatLength + 1];
  212.       FreqArray[NxtCh] := FreqArray[NxtCh] + 1
  213.     END;
  214.     i := SkipArray[i+1] -1  {Skip to next matching position}
  215.   END {while}
  216. END; {Procedure Match}
  217.  
  218. Procedure WriteCharacter;
  219. { The next character is written. It is chosen at random from characters }
  220. { accumulated in FreqArray during last scan of input. Output lines will }
  221. { average 50 characters in length. If "Verse" option has been selected, }
  222. { a new line will commence after any word that ends with "|" in the     }
  223. { input file. Thereafter, lines will be indented until the 50-character }
  224. { average has been made up.                                             }
  225. VAR
  226.   Counter, Total, Toss: INTEGER;
  227.   ch: CHAR;
  228. BEGIN {Procedure WriteCharacter}
  229.   Total := 0;
  230.   FOR ch := ' ' TO '|' DO
  231.     Total := Total + FreqArray[ch];  {Sum counts in FreqArray}
  232.   Toss := TRUNC (Total * RANDOM)+1;
  233.   Counter := 31;
  234.   REPEAT
  235.     Counter := Counter + 1;  {We begin with ' '}
  236.     Toss := Toss - FreqArray[CHR(Counter)]
  237.   UNTIL Toss <= 0;  {Char chosen by successive subtractions}
  238.   NewChar := CHR(Counter);
  239.   If NewChar <> '|' THEN
  240.     WRITE(o,NewChar);
  241.   CharCount := CharCount + 1;
  242.   IF CharCount MOD 50 = 0 THEN NearEnd := true;
  243.   IF ((Verse) AND (NewChar = '|')) THEN WRITELN(o);
  244.   IF ((NearEnd) AND (NewChar = ' ')) THEN
  245.   BEGIN  {If NearEnd}
  246.     WRITELN(o);
  247.     IF Verse THEN WRITE(o,'    ');
  248.     NearEnd := false
  249.   END {If NearEnd}
  250. END;  {Procedure WriteCharacter}
  251.  
  252. PROCEDURE NewPattern;
  253. { This removes the first character of the Pattern and appends the character }
  254. { just printed. FreqArray is zeroed in preparation for a new scan.          }
  255. VAR
  256.   j: INTEGER;
  257. BEGIN {Procedure NewPattern}
  258.   FOR j := 1 to PatLength -1 DO
  259.     Pattern[j] := Pattern[j+1];     {Move all chars leftward}
  260.   Pattern[PatLength] := NewChar;    {Append NewChar}
  261.   ClearFreq
  262. END; {Procedure NewPattern}
  263.  
  264. BEGIN {Main Program}
  265.   TextColor(White);
  266.   TextBackground(Blue);
  267.   ClrScr;
  268.   ClearFreq;
  269.   NullArrays;
  270.   InParams;
  271.   FillArray;
  272.   FirstPattern;
  273.   InitSkip;
  274.   REPEAT
  275.     Match;
  276.     WriteCharacter;
  277.     NewPattern
  278.   UNTIL CharCount >= OutChars;
  279.   CLOSE(o)
  280. END. {Main Program}
  281.