


{This program was written by a former CMSC 443 student}
{it's intended use is for CMSC 443 students}
{It is written in Pascal and should run on gl machines}
{Please feel to to make any appropriate changes.}
{You are encouraged to make an improved version of this program}
{Also please let us know of any bugs or problems that you}
{find with this program}
{ Program assists in breaking a substitution cipher }
{ Provides interactive menu and prompts for any possible substitution character}
{ Ciphertext read from input file - CIPHER1.TXT }
{ Write output to CIPHER1.OUT }


PROGRAM Project1 (Input, Indata, Output, Outdata);

CONST
     MaxRange = 100;         { Total length for Array_Main }
     Blank = ' ';            { Space }
     FiveSpace = '     ';    { Display five spaces }
     Space3 = '   ';
     TwoCol = 2;
     AlphaRow = 26;          { Total row length of Array }
     Zero = 0;
     

TYPE
    Range = 1 .. MaxRange;
    MainArray = ARRAY [Range, Range] OF CHAR;
    FreqArray = ARRAY [1 .. AlphaRow, 1 .. TwoCol] OF INTEGER;
    PCArray = ARRAY [1 .. AlphaRow, 1 .. TwoCol] OF CHAR;

    Key = RECORD                 { Array of record that hold CHAR and INT }
        P      : CHAR;
        C      : CHAR;
        Freq   : INTEGER;
          END;
    KeyTable = ARRAY [1 .. AlphaRow] OF Key;     { Total length of 26 }

    Frequency = RECORD           { Array of record that hold CHAR and INT }
        P     : CHAR;
        Freq  : INTEGER;
          END;
    FrequencyTable = ARRAY [1 .. AlphaRow] OF Frequency;  { Total length of 26 }

VAR
   Indata, Outdata : TEXT;             { Input/Output File }
   Array_Main : MainArray;             { Holds input file }
   Array_Freq : FrequencyTable;        { Array of record for total occurence of each letter }
   Array_PC : PCArray;                 { Array for plaintext/ciphertext table }
   Row_Main, Col_Main : INTEGER;       { Row/Col size for Array_Main }
   Data : CHAR;
   PC_Table : KeyTable;                { Array of record for plaintext/ciphertext table }
   Temp_PC_Table : KeyTable;           { Temporary array of record }
   Array_Temp : MainArray;   { Temporary Matrix : use for swapping letters }


PROCEDURE Initialize_Array_Main;  

{ Initialize Array_Main to all blank }
{ Array_Main : 100 x 100 matrix }
{ Matrix holds characters read from input file }

VAR
   Row, Col : INTEGER;

BEGIN
  FOR Row := 1 to MaxRange DO             { Loop thru the Array_Main until MaxRange }
     FOR Col := 1 to MaxRange DO          { Loop thru row and col, 133 times }
        Array_Main [Row, Col] := Blank;   { Set cell to blank }
END;

PROCEDURE Initialize_Array_Temp;

{ Initialize Array_Temp to all blank }
{ Array_Main : 100 x 100 matrix }
{ Matrix holds characters read from input file }

VAR
   Row, Col : INTEGER;

BEGIN
  FOR Row := 1 to MaxRange DO             { Loop thru the Array_Temp until MaxRange }
     FOR Col := 1 to MaxRange DO          { Loop thru row and col, 133 times }
        Array_Temp [Row, Col] := Blank;   { Set all cells to blank }
END;


PROCEDURE Initialize_Array_Freq;

{ Initialize Array_Freq to blank and zero }

VAR
   I : INTEGER;

BEGIN
  FOR I := 1 TO AlphaRow DO             { Loop thru 26 times }
    BEGIN
       Array_Freq[I].P     := Blank;    { Set to blank }
       Array_Freq[I].Freq  := Zero;     { Set to zero }
    END;
END;


PROCEDURE Set_Array_Freq_P_Column;

{ Initialize Array_Freq.P with letters }
{ Assigns Capital letter from A to Z to Array_Freq.P }

VAR
   I : INTEGER;

BEGIN
  FOR I := 1 TO AlphaRow DO            { Loop 26 times }
    Array_Freq[I].P := CHR(I + 64);    { Convert Decimal value to alphabet symbol }
END;



FUNCTION Find_Total_Row : INTEGER;

{ Function finds the total number of row from Array_Main }
{ Total value returns to procedure that invokes function }

VAR
   Row, Col, Count : INTEGER;

BEGIN
     Row := 1;      { Initialize variable to 1 };
     Col := 1;
     Count := 0;    { Reset counter to 0 value };
     WHILE Array_Main [Row, Col] <> Blank DO   { Loop thru Array_Main until blank }
        BEGIN
          Count := Count + 1;                  { Increment Count when row is not blank }
          Row := Row + 1;                      { Increment next row for inspection }
        END;
    Find_Total_Row := Count;                   { Return total count which is also total row }
END;


FUNCTION InRange (VAR Letter : Char) : BOOLEAN;

{ Function determines if value of Letter is within English alphabet }
{ Function returns a TRUE or FALSE when Letter is in alphabet }

BEGIN
   InRange := (('A' <= Letter) AND (Letter <= 'Z') OR
               ('a' <= Letter) AND (Letter <= 'z'));
END;


PROCEDURE Display_Array_Main;

{ Procedure display contents of Array_Main after reading input file }
{ Array_Main : holds input file }

Var
  Char1, Char2, Char3 : CHAR;
  Row, Col1, Col2, Col3, Row_Count : INTEGER;

BEGIN
 Row_Count := Find_Total_Row; 
 WRITELN (FiveSpace, 'Ciphertext read from input file - PROJ1.TXT');
 FOR Row := 1 TO Row_Count DO
   BEGIN                                      
    Col1 := 1;                                  { Initialize value }
    Col2 := 2;
    Col3 := 3;
    Char1 := Array_Main [Row, Col1];            { Set value of Char1 }
    Char2 := Array_Main [Row, Col2];            { Set value }
    Char3 := Array_Main [Row, Col3];
    WRITE (FiveSpace);
    WHILE (Char2 <> Blank) OR (Char3 <> Blank) DO    { Stay in row until two consecutive blank }
      BEGIN
        WRITE (Char1);
        Col1 := Col1 + 1;                           { Increment all Columns }
        Col2 := Col2 + 1;
        Col3 := Col3 + 1;
        Char1 := Array_Main [Row, Col1];            { Set value of Char1 }
        Char2 := Array_Main [Row, Col2];            { Set value }
        Char3 := Array_Main [Row, Col3];
      END;
    WRITE (Char1);
   WRITELN;
   END;
WRITELN;
END;


PROCEDURE Display_Array_Temp;

{ Procedure display contents of Array_Main after reading input file }
{ Array_Temp : holds input file temporarily }

Var
  Char1, Char2, Char3 : CHAR;
  Row, Col1, Col2, Col3, Row_Count : INTEGER;

BEGIN
 Row_Count := Find_Total_Row; 
 WRITELN (FiveSpace, 'Plaintext after substituting predicted letters.');
 WRITELN;
 FOR Row := 1 TO Row_Count DO
   BEGIN                                      
    Col1 := 1;                                  { Initialize value }
    Col2 := 2;
    Col3 := 3;
    Char1 := Array_Temp [Row, Col1];            { Set value of Char1 }
    Char2 := Array_Temp [Row, Col2];            { Set value }
    Char3 := Array_Temp [Row, Col3];
    WRITE (FiveSpace);
    WHILE (Char2 <> Blank) OR (Char3 <> Blank) DO    { Stay in row until two consecutive blank }
      BEGIN
        WRITE (Char1);
        Col1 := Col1 + 1;                           { Increment all Columns }
        Col2 := Col2 + 1;
        Col3 := Col3 + 1;
        Char1 := Array_Temp [Row, Col1];            { Set value of Char1 }
        Char2 := Array_Temp [Row, Col2];            { Set value }
        Char3 := Array_Temp [Row, Col3];
      END;
    WRITE (Char1);
   WRITELN;
   END;
WRITELN;
END;


PROCEDURE CopyToArray (VAR ArrayOne : MainArray;
                       VAR Row, Col : INTEGER;
                           InChar : CHAR);

{ Procedure copy one character from input file to Array_Main }
{ Returns new value of Col to caller }

BEGIN
    ArrayOne [Row, Col] := InChar;  { Copy value to Array_Main }
    Col := Col + 1;                 { Increment value of Col for next column }
END;


PROCEDURE Copyline (VAR Array_One : MainArray;
                    VAR Row, Col : Integer;
                    VAR Indata : TEXT);

{ Procedure copies one line from input file, PROJ2.TXT }
{ Read all characters until End Of Line }
{ Copies read value to Array_Main }

VAR
  Current_Char : CHAR;

BEGIN
 WHILE NOT EOLN (Indata) DO     { Read input file until End Of Line }
     BEGIN
        Read (Indata, Current_Char);       { Copy to Array_Main }
        CopyToArray (Array_One, Row, Col, Current_Char);
     END;
     READLN (Indata);                      { Done reading all characters from one line }
     Row := Row + 1;                   { Increment to next row }
     Col := 1;                        { Set column to first }
END;


PROCEDURE Count_Letter;

{ Procedure determines total number of letter }
{ Returns an Integer to the caller }

VAR
  Letter : CHAR;
  Row, Col : INTEGER;
  Total_Letter : INTEGER;      { Holds total number of letter found }

BEGIN
    Total_Letter := 0;
    FOR Row := 1 to MaxRange DO        { Loop thru Array_Main }
      FOR Col := 1 to MaxRange DO
        BEGIN
          Letter := Array_Main [Row, Col];    { Set value to Letter }
          IF InRange (Letter) THEN            { Determine if a valid letter }  
             Total_Letter := Total_Letter + 1;  { Increment total_letter since true }
        END;
    WRITELN (FiveSpace, 'Ciphertext has  ', Total_Letter, ' characters.');
    WRITELN;

END;


PROCEDURE Display_Menu (VAR Ch : CHAR);

{ main menu to prompt possible commands }
{ input from keyboard }

BEGIN
WRITELN (FiveSpace, 'SELECT A LETTER TO EXECUTE THE COMMAND AND PRESS ENTER.');
WRITELN (FiveSpace, 'A - COUNT TOTAL NUMBER OF CHARACTERS IN CIPHERTEXT.');
   WRITELN (FiveSpace, 'B - DISPLAY THE CIPHERTEXT READ FROM FILE - CIPHER1.TXT');
WRITELN (FiveSpace, 'C - FIND SINGLE LETTER FREQUENCIES.');
WRITELN (FiveSpace, 'D - PREDICT PLAINTEXT BASED ON LETTER FREQUENCY.');
WRITELN (FiveSpace, 'E - DISPLAY PLAINTEXT/CIPHERTEXT TABLE.');
WRITELN (FiveSpace, 'F - DISPLAY POSSIBLE PLAINTEXT BASED ON PLAINTEXT/CIPHERTEXT TABLE.');
WRITELN (FiveSpace, 'G - MODIFY PLAINTEXT/CIPHERTEXT TABLE.');
WRITELN (FiveSpace, 'H - UNDO PREDICTED PLAINTEXT/CIPHERTEXT TABLE.');
   WRITELN (FiveSpace, 'I - WRITE PLAINTEXT/CIPHERTEXT TABLE TO FILE - CIPHER1.OUT ');
WRITELN (FiveSpace, 'J - TO EXIT PROGRAM.');
WRITELN;
WRITE (FiveSpace, 'ENTER YOUR SELECTION: ');
READLN (Ch);             { Read selected letter from keyboard }
WRITELN;
END;


PROCEDURE Convert_To_Small_Letter (VAR PlainTxt, CiphrTxt : CHAR);

{ Convert PlainTxt and CiphrTxt to small letter }
{ Returns value of PlainTxt and CiphrTxt }

BEGIN
 IF PlainTxt IN ['A'..'Z'] THEN                 { Determine if letter is capital }
    PlainTxt := CHR(ORD(PlainTxt) - ORD('A') + ORD('a'));    { Find small letter }
 IF CiphrTxt IN ['A'..'Z'] THEN                 { Determine if letter is capital }
    CiphrTxt := CHR(ORD(CiphrTxt) - ORD('A') + ORD('a'));    { Find small letter }
END;                                                        



PROCEDURE Swap_Letter (PlainTxt, CiphrTxt : CHAR);

{ Procedure swap two characters, plaintxt and ciphrtxt }
{ Scan all row and column, looking for match in Array_Temp }
{ Exchange letter if match is found }

VAR
  R, C : INTEGER;
  Ch   : CHAR;

BEGIN
Convert_To_Small_Letter (PlainTxt, CiphrTxt);    { Convert to small letters }
FOR R := 1 TO MaxRange DO                        { Loop thru all rows }
 BEGIN
   FOR C := 1 TO MaxRange DO                     { Loop thru all columns }
      BEGIN
        Ch := Array_Main [R, C];                 { Assign to Ch }
        IF Ch = CiphrTxt THEN                    { Compare if similar, assign if true}
           Array_Temp [R, C] := PlainTxt;        { Assign possible plaintxt }
      END;
 END;
END;


PROCEDURE Swap_All_Predicted_Letter;

{ Procedure exchange all predicted letters as shown thru main menu, Step D }
{ Read input fro PC_Table and swap letters starting from E }
{ Output is kept in Array_Temp }

VAR
  I : INTEGER;                  { Variable for indexing PC_Table Array }
  PlainTxt, CiphrTxt : CHAR;    { Variable for plaintxt and ciphertxt }

BEGIN
FOR I := 1 TO AlphaRow DO
  BEGIN
    PlainTxt := PC_Table[I].P;           { Assign PlainText }
    CiphrTxt := PC_Table[I].C;           { Assign CipherText }
    Swap_Letter (PlainTxt, CiphrTxt);    { Swap the two letters }
  END;
END;



PROCEDURE Exchange_Predicted_Letter;

{ Procedure exchange predicted letters }
{ Display output }

BEGIN
Array_Temp := Array_Main;    { Copy Array Main to Array Temp. Modify Array_Temp }
Swap_All_Predicted_Letter;   { Exchange all predicted letters to Array Temp }
Display_Array_Temp;          { Display Array_Temp }
END;


PROCEDURE Total_Ltr (Cap_Ltr, Small_Ltr : INTEGER;
                     VAR Counter : INTEGER);

{ Procedure counts total existence of character in ciphertext }
{ Returns total number thru Counter variable }
                     
VAR
   Cap_Ch, Sml_Ch : CHAR;
   Row, Col : INTEGER;

BEGIN
     Cap_Ch := CHR(Cap_Ltr);       { Determine character equivalence }
     Sml_Ch := CHR(Small_Ltr);     { Determine character equivalence }
     FOR Row := 1 TO MaxRange DO     { Loop thru all rows }
       FOR Col := 1 TO MaxRange DO   { Loop thru all columns }
          BEGIN                      { find if letter exists }
            IF (Array_Main [Row, Col] = Sml_Ch) OR (Array_Main [Row, Col] = Cap_Ch) THEN
                Counter := Counter + 1;     { Increment counter }
          END;
END;


PROCEDURE Display_PC_Table;

{ Display contents of PC_Table in four columns }

VAR
   Counter, I : INTEGER;

BEGIN
Counter := 0;        { Initialize counter }
WRITELN (FiveSpace, 'PREDICTED LETTER ASSIGNMENT WHERE E HAS HIGHEST FREQUENCY.');
WRITELN;
     FOR I := 1 TO AlphaRow DO    { Loop 26 times }
      BEGIN
        IF Counter > 3 THEN       { Wrap around to next line if greater than 3 }
         BEGIN
          Counter := 0;           { Initialize counter }
          WRITELN;
          WRITE (PC_Table[I].P :5, PC_Table[I].C :5, PC_Table[I].Freq :5, '   ');
          Counter := Counter + 1;      { Increment counter }
         END
        ELSE                      { continue writing on same row }
         BEGIN
          WRITE (PC_Table[I].P :5, PC_Table[I].C :5, PC_Table[I].Freq :5, '   ');
          Counter := Counter + 1;       { Increment counter }
         END;
     END;
WRITELN;
WRITELN;
END;


PROCEDURE Sort_Array_Freq;

{ Sort Array_Freq from highest to lowest }

VAR
 I, N : INTEGER;
 Freq_A, Freq_B, Freq_Temp : INTEGER;
 P_A, P_B, P_Temp : CHAR;

BEGIN
FOR I := 1 TO AlphaRow DO       { Loop thru 26 times }
  BEGIN
    FOR N := I + 1 TO AlphaRow DO    { Set N one higher than I }
     BEGIN
      Freq_A := Array_Freq[I].Freq;    { Set number of frequency }
      Freq_B := Array_Freq[N].Freq;
      P_A := Array_Freq[I].P;          { Assign characters }
      P_B := Array_Freq[N].P;
      IF Freq_B > Freq_A THEN          { Determine which is bigger }
        BEGIN                          { Swap contents if Freq_B is bigger }
         Array_Freq[N].Freq   := Freq_A;    { Assign contents to new location }
         Array_Freq[I].Freq   := Freq_B;
         Array_Freq[N].P      := P_A;       { Assign contents to new location }
         Array_Freq[I].P      := P_B;
        END;
     END;
  END;
END;


PROCEDURE Display_Frequency_Table;

{ Displays contents of Array_Freq to screen }
{ Array_Freq : holds total occurence of each letter from A to Z }

VAR
   I, Counter : INTEGER;

BEGIN
Counter := 0;
WRITELN (FiveSpace,'Table of Frequency Letters.' :10);
WRITELN;
     FOR I := 1 TO AlphaRow DO    { Loop 26 times }
       BEGIN
        IF Counter > 3 THEN       { Wrap around if counter is 3 }
         BEGIN
          Counter := 0;           { Initialize counter }
          WRITELN;
          WRITE (Array_Freq[I].P :5);      { Display Array_Freq }
          WRITE (Array_Freq[I].Freq :5, FiveSpace);
          Counter := Counter + 1;          { Increment counter }
         END
        ELSE                      { continue writing on same row }
         BEGIN
          WRITE (Array_Freq[I].P :5);    { Display Array_Freq }
          WRITE (Array_Freq[I].Freq :5, FiveSpace);
          Counter := Counter + 1;        { Increment counter }
         END;
     END;
WRITELN;
WRITELN;
END;

PROCEDURE Merge_PCTable_Array_Freq;

{ Copy contents of Array_Freq to PC_Table array of records }
{ Array_Freq : 26 x 2 matrix that holds total number of occurence of each letter}

VAR
   I : INTEGER;        { Index variable }

BEGIN
 FOR I := 1 TO AlphaRow DO       { Loop thru 26 times }
   BEGIN
     PC_Table[I].C := Array_Freq[I].P;        { Copy plaintext to PC_Table }
     PC_Table[I].Freq := Array_Freq[I].Freq;  { Copy frequency to PC_Table }
   END;
END;



PROCEDURE Predict_Plaintext;

{ Procedure determines the number of existence of letter from ciphertext }
{ Each occurence is assigned to PC_Table record }
{ PC_Table : array of record with pre-assigned value from 1 to 26 }

VAR
   Occur : INTEGER;     { Number of occurrence in cipher }
   Letter : CHAR;       
   Col, I : INTEGER;
   Found : BOOLEAN;

BEGIN
  Found := False;       { Initialize value }
  Occur := 1;           { Initialize value }
  Col := 2;             { Initialize value }
  Sort_Array_Freq;   { Arrange Array_Freq from highest to lowest }
  Merge_PCTable_Array_Freq;  { Merge PC Table with Array_Freq }
  WRITELN (FiveSpace, 'Possible plaintext to ciphertext table has been predicted');
  WRITELN (FiveSpace, 'by assigning total number of occurence from cipher to plaintext letters.');
  WRITELN (FiveSpace, 'Select to display possible plaintext/ciphertext table.');
  WRITELN;
  WRITELN;
END;


PROCEDURE Initialize_Temp_PC_Table;

{ Initialize contents of Temp_PC_Table }

VAR
   I : INTEGER;

BEGIN
     FOR I := 1 TO AlphaRow DO
      BEGIN
        Temp_PC_Table[I].P := Blank;    { Initialize with blank }
        Temp_PC_Table[I].C := Blank;
        Temp_PC_Table[I].Freq := Zero;  { Initialize with zero }
      END;
END;


PROCEDURE Initialize_PC_Table;

{ Initialize PC_Table }

VAR
   I : INTEGER;

BEGIN
     FOR I := 1 TO AlphaRow DO
      BEGIN
        PC_Table[I].P := Blank;        { Initialize with blank }
        PC_Table[I].C := Blank;
        PC_Table[I].Freq := Zero;      { Initialize with zero }
      END;
END;

PROCEDURE Initialize_PC_Table_P_Column;

{ Procedure initialize P column of PC_Table }
{ Assignment serves as reference point }
{ PC_Table is arranged from highest frequencies as given in Textbook }
{ Character E would always have the highest frequency }

BEGIN
  PC_Table[1].P := 'E';      { Arranged based on Probabilities of Occurence}
  PC_Table[2].P := 'T';
  PC_Table[3].P := 'A';
  PC_Table[4].P := 'O';
  PC_Table[5].P := 'I';
  PC_Table[6].P := 'N';
  PC_Table[7].P := 'S';
  PC_Table[8].P := 'H';
  PC_Table[9].P := 'R';
  PC_Table[10].P := 'D';
  PC_Table[11].P := 'L';
  PC_Table[12].P := 'C';
  PC_Table[13].P := 'U';
  PC_Table[14].P := 'M';
  PC_Table[15].P := 'W';
  PC_Table[16].P := 'F';
  PC_Table[17].P := 'G';
  PC_Table[18].P := 'Y';
  PC_Table[19].P := 'P';
  PC_Table[20].P := 'B';
  PC_Table[21].P := 'V';
  PC_Table[22].P := 'K';
  PC_Table[23].P := 'J';
  PC_Table[24].P := 'X';
  PC_Table[25].P := 'Q';
  PC_Table[26].P := 'Z';
END;


PROCEDURE Search_Cipher (    Ch  : CHAR;
                         VAR Row : INTEGER);

{ Determine row location of given Ch from PC_Table }
{ Return found row to caller }
{ Searh for plaintext character }
{ Input Ch and Row }

VAR
 Index : INTEGER;
 Ch1, Dummy_Ch : CHAR;

BEGIN
 Index := 1;          { Initialize Index }
 Dummy_Ch := 'A';     { Set variable }
 Ch1 := PC_Table[Index].C;     { Assign character }
 Convert_To_Small_Letter(Ch1, Dummy_Ch);     { Convert Ch1 to lower case }
 WHILE Ch1 <> Ch DO            { loop thru while not equal }
   BEGIN
     Index := Index + 1;                            { Increment Index }
     Ch1 := PC_Table[Index].C;                      { Assign ciphertext character }
     Convert_To_Small_Letter(Ch1, Dummy_Ch);        { Convert Ch1 to lower case }
   END;
 Row := Index;                    { Return Index found }
END;

 
PROCEDURE Search_Plain (    Ch  : CHAR;
                      VAR Row : INTEGER);

{ Search for row location of PC_Table }
{ Searh for plaintext character }

VAR
 Index : INTEGER;
 Ch1, Dummy_Ch : CHAR;

BEGIN
 Index := 1;                 { Initialize Index }
 Dummy_Ch := 'A';            { Set variable }
 Ch1 := PC_Table[Index].P;   { Assign character }
 Convert_To_Small_Letter(Ch1, Dummy_Ch);     { Convert Ch1 to lower case }
 WHILE Ch1 <> Ch DO          { loop thru while not equal }
   BEGIN
     Index := Index + 1;            { Increment Index }
     Ch1 := PC_Table[Index].P;      { Assign character }
     Convert_To_Small_Letter(Ch1, Dummy_Ch);    { Convert Ch1 to lower case }
   END;
 Row := Index;             { Return index found }
END;


PROCEDURE Swap_Two_Letter (Ch1, Ch2 : CHAR);

{ Swap location of two ciphertext and plaintext from PC_Table }
{ Ch1 : ciphertext want to reassign }
{ Ch2 : plaintext }

VAR
  Row1, Row2, TempFreq : INTEGER;
  TempCiphr : CHAR;           { Temporary holds ciphertext }

BEGIN
 Search_Cipher (Ch1, Row1);      { Find row location of ch1 }
 Search_Plain  (Ch2, Row2);      { Find row location of ch2 }
 TempCiphr := PC_Table[Row1].C;           { Assign to temporary holder }
 TempFreq  := PC_Table[Row1].Freq;
 PC_Table[Row1].C := PC_Table[Row2].C;    { Copy ciphertext from row2 to row1 }
 PC_Table[Row1].Freq := PC_Table[Row2].Freq;  { Copy freq from row2 to row1 }
 PC_Table[Row2].C := TempCiphr;     { Copy ciphertext to row2 }
 PC_Table[Row2].Freq := TempFreq;   { Copy freq to row2 }
END;


PROCEDURE Ask_Letter_To_Modify (NumLtr : INTEGER);

{ Prompts which letter to modify }
{ NumLtr :used for defined iteration loop }

VAR
   I : INTEGER;
   Ch1, Ch2 : CHAR;        { Ch1 and Ch2 - ciphertext and plaintext assignment}

BEGIN
WRITELN;
WRITELN (FiveSpace, 'You will be asked with same questions  ', NumLtr, ' times.');
WRITELN;
  FOR I := 1 TO NumLtr DO    { loop thru value of NumLtr }
   BEGIN
     WRITE (FiveSpace, 'Which ciphertext you want to reassign?  ');
     READLN (Ch1);
     WRITE (FiveSpace, 'You want to assign ciphertext  ', CH1, ' to which possible plaintext?  ');
     READLN (Ch2);
     Swap_Two_Letter (Ch1, Ch2);       { Swap location of characters }
   END;
WRITELN (FiveSpace, 'Your request has been processed. Display new PlainText/CipherText table.');
END;


PROCEDURE Number_Letter_To_Modify (VAR NumLtr : INTEGER);

{ Prompts how many letters to modify }
{ Value used for defined number of iteration loop }
{ NumLtr : Total number of letter }

BEGIN
 WRITELN;
 WRITE (FiveSpace,'How many letters to change?  ');
 READLN (NumLtr);       
END;
  
PROCEDURE Modify_PC_Table;

{ Modify contents of PC_Table thru interactive menu }
{ PC_Table holds the possible plaintext/ciphertext substitution }

VAR
 NumLtr : INTEGER;

BEGIN
  Array_Temp := Array_Main;          { Initialize Array_Temp }
  Temp_PC_Table := PC_Table;         { Initialize Temp_PC_Table }
  Number_Letter_To_Modify (NumLtr);  { Determine number of letter to change }
  Ask_Letter_To_Modify (NumLtr);     { Prompt for which letter to modify }
  Display_PC_Table;                  { Display contents of PC_Table }
END;


PROCEDURE Find_Frequency_Letter;

{ Determine the number of occurrence of all letter in ciphertext }
{ Display the frequency table in columns of three}

VAR
   Cap_Ltr, Small_Ltr, Index, Total, Counter, Col : INTEGER;
   
BEGIN
Set_Array_Freq_P_Column;   { Set P of Array_Freq from 1 to 26 with A to Z }
Counter := 0;              { Initialize counter }
Col := 2;
FOR Index := 1 TO AlphaRow DO    { Loop 26 times }
 BEGIN
  Total := 0;                         { Initialize total }
  Cap_Ltr := Index + 64;              { Ordinal of Capital Letter. Start with A }
  Small_Ltr := Index + 96;            { Ordinal of Small Letter. Start with a }
  Total_Ltr (Cap_Ltr, Small_Ltr, Total);     { Total_Ltr is a Function returns total letter found from Array_Main }
  Array_Freq[Index].Freq :=  Total; { Copy total letter to Array_Freq }
 END;
{WRITELN (FiveSpace, 'Found total occurence of each letter. Type D to display frequency table.');}
{WRITELN;}
Display_Frequency_Table;            { Display the contents of Frequency Table }
END;

PROCEDURE Write_Array_Temp_To_File (VAR Outdata_A : TEXT);

{ Write contents of Array_Temp to output file - PROJ1.OUT }
{ Array_Temp : holds arranged copy of input file }

Var
  Char1, Char2, Char3 : CHAR;
  Row, Col1, Col2, Col3, Row_Count : INTEGER;

BEGIN
 Row_Count := Find_Total_Row; 
 WRITELN (Outdata_A, FiveSpace, 'Plaintext after substituting predicted letters.');
 WRITELN (Outdata_A);
 FOR Row := 1 TO Row_Count DO
   BEGIN                                      
    Col1 := 1;                                  { Initialize value }
    Col2 := 2;
    Col3 := 3;
    Char1 := Array_Temp [Row, Col1];            { Set value of Char1 }
    Char2 := Array_Temp [Row, Col2];            { Set value }
    Char3 := Array_Temp [Row, Col3];
    WRITE (Outdata_A, FiveSpace);
    WHILE (Char2 <> Blank) OR (Char3 <> Blank) DO    { Stay in row until two consecutive blank }
      BEGIN
        WRITE (Outdata_A, Char1);
        Col1 := Col1 + 1;                           { Increment all Columns }
        Col2 := Col2 + 1;
        Col3 := Col3 + 1;
        Char1 := Array_Temp [Row, Col1];            { Set value of Char1 }
        Char2 := Array_Temp [Row, Col2];            { Set value }
        Char3 := Array_Temp [Row, Col3];
      END;
    WRITE (Outdata_A, Char1);
   WRITELN (Outdata_A);
   END;
END;


PROCEDURE Write_PC_Table_To_File (VAR Outdata_A : TEXT);

{ Writes contents of PC_Table to output file - PROJ1.OUT }

VAR
   Counter, I : INTEGER;

BEGIN
Counter := 0;                  { Initialize counter }
WRITELN (Outdata_A, FiveSpace, 'PREDICTED LETTER ASSIGNMENT WHERE E HAS HIGHEST FREQUENCY.');
WRITELN (Outdata_A);
     FOR I := 1 TO AlphaRow DO    { Loop thru 26 times }
      BEGIN
        IF Counter > 3 THEN       { Wrap around if greater than 3 }
         BEGIN
          Counter := 0;           { Initialize counter }
          WRITELN (Outdata_A);
          WRITE (Outdata_A, PC_Table[I].P :5, PC_Table[I].C :5, PC_Table[I].Freq :5, '   ');
          Counter := Counter + 1;      { Increment counter }
         END
        ELSE                      { continue writing on same row }
         BEGIN
          WRITE (Outdata_A, PC_Table[I].P :5, PC_Table[I].C :5, PC_Table[I].Freq :5, '   ');
          Counter := Counter + 1;      { Increment counter }
         END;
     END;
WRITELN (Outdata_A);
WRITELN (Outdata_A);
Write_Array_Temp_To_File (Outdata_A);   { Write plaintext to output file }
END;


PROCEDURE Undo_Predict_Plaintext;

{ Clear contents of PC_Table }

BEGIN
  Initialize_PC_Table;            { Set all cells to blank and zero }
  Initialize_PC_Table_P_Column;   { Initialize plaintext column }
  WRITELN (FiveSpace, 'The PlainText/CipherText table has been initialized.');
  WRITELN;
END;


BEGIN
Row_Main := 1;                     { Initialize row and colfor Array_Main }
Col_Main := 1;
{ASSIGN (Indata, 'a:\proj1\proj1.txt');}   { used for Borland Pascal compiler }
   RESET (Indata, 'cipher1.txt');               { Reset input file - for Unix compiler }
{RESET (Indata);}                          { used for Borland Pascal compiler }
{ASSIGN (Outdata, 'a:\proj1\proj1.out');}  { used for Borland Pascal compiler }
   REWRITE (Outdata, 'cipher1.out');            { Rewrite output file - for Unix compiler }
{REWRITE (Outdata);}                       { used for Borland Pascal compiler }

Initialize_Array_Main;             { Initialize all Array and Records }
Initialize_Array_Freq;
Initialize_PC_Table;
Initialize_Temp_PC_Table;
Initialize_PC_Table_P_Column;
Initialize_Array_Temp;


WHILE NOT EOF (Indata) DO      { Copy oneline until end of line }
       Copyline (Array_Main, Row_Main, Col_Main, Indata);
WRITELN;

Data := 'A';                   { Initialize data }

  WHILE Data IN ['a'..'i', 'A'..'I'] DO      { Loop thru while data = a..i }
    BEGIN
      Display_Menu (Data);                   { Display main menu }
      IF Data IN ['a'..'i', 'A'..'I'] THEN   { Execute if either a or i }
       CASE Data OF
        'a', 'A' : Count_Letter;             { Count total letter inciphertext }
        'b', 'B' : Display_Array_Main;       { Display file that read from PROJ1.TXT }
        'c', 'C' : Find_Frequency_Letter;    { List frequency table }
        'd', 'D' : Predict_Plaintext;        { Initialize PC_Table based on frequency table }
        'e', 'E' : Display_PC_Table;         { Display contents of PC_Table }
        'f', 'F' : Exchange_Predicted_Letter;     { Display plaintext }
        'g', 'G' : Modify_PC_Table;          { Change contents of PC Table }
        'h', 'H' : Undo_Predict_PlainText;   { Initialize PC_Table }
        'i', 'I' : Write_PC_Table_To_File (Outdata);   { Write contents of PC_Table to file - PROJ1.OUT }
      END;
    END;
    IF (Data = 'j') OR (Data = 'J') THEN
         WRITELN (FiveSpace,'YOU CHOOSE TO EXIT. TERMINATING PROGRAM')
    ELSE
         WRITELN (FiveSpace, 'YOU ENTERED AN INVALID DATA. TERMINATING PROGRAM.');
CLOSE (Outdata);
END.


