program AutoKeyTrialDecode (infile, outfile, KeyOutfile, input, output);

{******************************************************************************************}
												
{ This program combines an Autokey Encipher(e) and Decipher(d) mode of operation. Data is  
 read from a file specified "infile", and writes to a file named "outfile". For encoding 
 mode, the key and key length are written to a file called "keyOutfile", for reference for 
 the decoding process. Note this programs relies on the key for a satisfactory decode to 
 take place.}  
				 {Writen by Brett Avery}				
											
{*****************************************************************************************}

 const
  LIMIT = 60; 	                                 {maximum key length			  }	

 type
  storageText = array[1..2] of char;
  storageKey = array[1..LIMIT] of char;

 var
  StoreChar: storageText;
  Key: storageKey;
  infile, outfile, KeyOutfile: text;
  Ch, ChKey, mode: char;
  PositionKey, CountKey, CountLetter: integer;

{ Note, procedures that take the input from glabal variables are give the global variable's}
{ name and the suffix "Pro", in reference to procedure. Full stops in comments and        }
{ explanations  indicates their end. "Pre" and "Post" refer to preconditions and          }
{ postconditions respectively }

{******************************************************************************************}

 procedure CheckShift1 (var PrintLetter, EncodeLetter, TextLetter: char);

{Procedure designed to monitor the output of the encoding, and decoding processes,checking }
{the validity of the results. Results are True if Shift is TRUE				   }

  var
   Shift: Boolean;

{Pre: character inputs are lowercase}

 begin
  if ord(EncodeLetter) - ord('a') + 1 = abs(ord(TextLetter) - ord(PrintLetter)) then
   begin
    Shift := TRUE;		 {letter shifting within the program is correct		   }
   end
  else
   begin
    Shift := FALSE;		{letter shifting within the program is not correct	   }
    writeln('Error occurred in encoding sequence');
    assert(FALSE);
   end;
 end;

{Post: Letter shift is of expected value}

{*************************************************************}

 procedure CheckShift2 (var PrintLetter, EncodeLetter, TextLetter: char);

{Procedure designed to monitor the output of the encoding, and decoding processes, checking}
{the validity of the results. Results are True if Shift is TRUE				   }

  var
   Shift: Boolean;

{Pre: character inputs are lowercase}

 begin
  if ord(EncodeLetter) - ord('a') + 1 = abs(ord(TextLetter) - ord(PrintLetter) - 26) then
   begin
    Shift := TRUE;		{letter shifting within the program is correct		   }
   end
  else
   begin
    Shift := FALSE;		{letter shifting within the program is not correct	   }
    writeln;
    writeln('Error occurred in encoding sequence');
    assert(FALSE);
   end;
 end;

{Post: Letter shift is of expected value}

{*************************************************************}

 procedure CheckShift3 (var PrintLetter, EncodeLetter, TextLetter: char);

{Procedure designed to monitor the output of the encoding, and decoding processes, checking}
{the validity of the results. Results are True if Shift is TRUE				   }

  var
   Shift: Boolean;

{Pre: character inputs are lowercase}

 begin
  if ord(EncodeLetter) - ord('a') + 1 = abs(ord(TextLetter) - ord(PrintLetter) + 26) then
   begin
    Shift := TRUE;		{letter shifting within the program is correct		   }
   end
  else
   begin
    Shift := FALSE;		{letter shifting within the program is not correct	   }
    writeln;
    writeln('Error occurred in encoding sequence');
    assert(FALSE);
   end;
 end;

{Post: Letter shift is of expected value}

{*************************************************************}

 procedure LowerCase (var ChPro: char);

{Reduces all uppercase letters to lowercase letters before applying deciphering or enciphering sequence									           }

 begin
  if (ChPro) in ['A'..'Z'] then
{Pre: ChPro is an uppercase character}
   begin
    ChPro := chr(ord(ChPro) + 32);
   end;
 end; 	                                  {procedure Lowercase		                   }

{Post: ChPro is not an upercase character}

{*************************************************************}

 procedure Initialise;

{Sets glabal variables to zero in preparation of procedural calculations. Resets files 
 which will be read from and writen to						           }

{Pre: identifiers are integer values}

 begin
  reset(infile);
  rewrite(outfile);
  rewrite(KeyOutfile);
  PositionKey := 0;
  CountKey := 0;
  CountLetter := 0;
 end; 					{procedure Initialise.			           }

{Post: all integer values are set to zero}

{*************************************************************}

 procedure InputKey;

{Reads the Key used for the decoding or encoding.														}

  var
   PositionKey: integer;
   CountValue: Boolean;

 begin
  PositionKey := 0;
  writeln('Enter the key for the text');

{Pre1: array "key" contains only alphabetic characters}

  while not eoln do
   begin
    PositionKey := PositionKey + 1;        {increment array position.			   }
    read(ChKey);
    LowerCase(ChKey); 			   {procedure.					   }
    Key[PositionKey] := ChKey;		   {character read into array "Key".		   }
    CountKey := CountKey + 1;				{Count of Key length.									}
   end;

{Pre2: Countkey => 1}
  if CountKey >= 1 then			   {debug writes				   }
   begin
    CountValue := TRUE;			   {condition holds				   }
    writeln(CountKey);		           {write key length.		                   }
   end
  else
   begin
    CountValue := FALSE;		   {condition fails				   }
    writeln('No Key was inserted');
    assert(FALSE);
   end;
 end;					  {procedure InputKey.			           }

{Post: array "key" is composed of at least one lowercase character}

{*************************************************************}

 procedure ConfirmKey (var CountKeyPro: integer);

{Prints the key used in encoding/decoding function, writing to a  file called "keyOutfile".}

  var
   KeyPosition: integer;

{Pre: array key contains only lowercase letters in consecutive order}

 begin
  for KeyPosition := 1 to CountKeyPro do 	{cycles through the characters in the 
                                                 array: Key.	                           }
   begin
    write(KeyOutfile, Key[KeyPosition]);	{prints each character in the array        }
   end;
  writeln;
 end;						{procedure ConfirmKey.			   }

{Post: observed sequential order of lowercase charcters }

{*************************************************************}

 procedure SelectMode;

{Determines whether the program encrypts or decrypts as an AutoKey Cipher. If no mode, or 
incorrect character value is selected, the program stops operation.			   }

 begin
  writeln('Please select mode of operation, Encode(e) or Decode(d) ');
  readln(mode);

{Pre1: mode has charcter value of 'd' or 'e'}

  if mode in ['d', 'e'] then		      {assertion, mode equals 'd' or 'e'.	   }
   begin
    InputKey;				     {procedure.				   }
    ConfirmKey(CountKey);		     {procedure.				   }
   end
{Pre2: mode not 'd' or 'e'}
  else
   begin
    write('Invalid or no mode selection made');
    assert(FALSE);
   end;
 end;					     {procedure SelectMode.			   }

{Post: mode equals 'd' or 'e'}

{*************************************************************}

 procedure StoreCharacter (var ChPro: char);

{Inputs each successive character into the two dimensional array "StoreChar", were the
 latest(2) and previous(1) characters are compared for encryption/decryption.	           }

 begin
{Pre1: ChPro is a lowercase letter}
  if ChPro in ['a'..'z'] then		  {asserting that only lowercase, character values }
  begin				          {are stored in the array.			   }
    PositionKey := PositionKey + 1;	  {increments character position in   Key array.   }
    CountLetter := CountLetter + 1;	  {Count of text length.			   }
    StoreChar[1] := StoreChar[2];	  {moving previous character into position 1 of the}
    StoreChar[2] := ChPro;		  {array StoreChar, to allow insertion of latest   }
   end					  {character into position 2 of the array.         }
{Pre2: ChPro is not a lowercase letter}
  else
   write(outfile, ChPro);		  {non-character text are excluded from decoding   }
					  {process.					   }
 end;					  {procedure StoreCharacter.			   }

{Post: StoreChar contains lowercase letters and all other ASCII characters have been 	   }
{ written to outfile				                                           }
{******************************************************************************************}

 procedure EncodeKey (var PositionKeyPro: integer);

{Encodes the text according to the specified key of any length			           }

  var
   PrintChKey: char;

{Pre: array letters in StoreChar and Key exist and are of character type}

 begin
  if StoreChar[2] in [chr(ord('z')-(ord(Key[PositionKeyPro])-ord('a') + 1) + 1)..'z'] then

   begin				{set of characters where the shift retains the     }
					{character in the lowercase portion of ASCII code  }

    PrintChKey := chr(ord(StoreChar[2]) + (ord(Key[PositionKeyPro]) - ord('a') + 1) - 26);

    CheckShift2(PrintChKey, Key[PositionKey], StoreChar[2]);
   end
  else if StoreChar[2] in ['a'..chr(ord('z')-(ord(Key[PositionKeyPro])-ord('a') + 1))] then

   begin			        {set of characters where the shift would produce   }
					{ciphered characters outside the lowercase ASCII.  }

    PrintChKey := chr(ord(StoreChar[2]) + (ord(Key[PositionKeyPro]) - ord('a') + 1));

    CheckShift1(PrintChKey, Key[PositionKey], StoreChar[2]);
   end;
  write(outfile, PrintChKey);		{encoded character from Key of Autokey Cipher.	   }
 end;					{procedure EncodeKey.				   }

{Post: PrintChKey is of lowercase character type}

{******************************************************************************************}

 procedure EncodeText;

{Encodes the file using the original text as the key. All set theory were derived as in the  previous procedure.									   }

  var
   PrintChText: char;

{Pre: array letters in StoreChar and Key exist and are of character type}

 begin
  if StoreChar[2] in [chr(ord('z') - (ord(StoreChar[1]) - ord('a') + 1) + 1)..'z'] then
   begin
    PrintChText := chr(ord(StoreChar[2]) + (ord(StoreChar[1]) - ord('a') + 1) - 26);

    CheckShift2(PrintChText, StoreChar[1], StoreChar[2]);
   end
  else if StoreChar[2] in ['a'..chr(ord('z') - (ord(StoreChar[1]) - ord('a') + 1))] then
   begin
    PrintChText := chr(ord(StoreChar[2]) + (ord(StoreChar[1]) - ord('a') + 1));

    CheckShift1(PrintChText, StoreChar[1], StoreChar[2]);
   end;
  write(outfile, PrintChText);		{encoded character from Text of AutoKey Cipher.	   }
 end;					{procedure EncodeText.				   }

{Post: PrintChKey is of lowercase character type}

{******************************************************************************************}

 procedure DecodeKey (var PositionKeyPro: integer);

{Decodes the text according to the specified key of any length.	All set theory were derived as in the previous procedure.								   }

  var
   PrintChKey: char;

{Pre: array letters in StoreChar and Key exist and are of character type}

 begin
  if StoreChar[2] in [chr(ord('a') + (ord(Key[PositionKeyPro]) - ord('a') + 1))..'z'] then
   begin
    PrintChKey := chr(ord(StoreChar[2]) - (ord(Key[PositionKeyPro]) - ord('a') + 1));

    CheckShift1(PrintChKey, Key[PositionKeyPro], StoreChar[2]);
   end
  else if StoreChar[2] in ['a'..chr(ord('a')+(ord(Key[PositionKeyPro])-ord('a') + 1) - 1)] then
   begin
    PrintChKey := chr(ord(StoreChar[2]) - (ord(Key[PositionKeyPro]) - ord('a') + 1) + 26);

    CheckShift3(PrintChKey, Key[PositionKeyPro], StoreChar[2]);
   end;
  write(outfile, PrintChKey);	     {decoded character from Key ofAutoKey Cipher.         }
  StoreChar[2] := PrintChKey;
 end;				     {procedure DecodeKey.				   }

{Post: PrintChKey is of lowercase character type}

{******************************************************************************************}

 procedure DecodeText;

{Decodes the file using the original text as the key.	All set theory were derived as 	     in the previous procedure.								   }

  var
   PrintChText: char;

{Pre: array letters in StoreChar and Key exist and are of character type}

 begin
  if StoreChar[2] in [chr(ord('a') + (ord(StoreChar[1]) - ord('a') + 1))..'z'] then
   begin
    PrintChText := chr(ord(StoreChar[2]) - (ord(StoreChar[1]) - ord('a') + 1));

    CheckShift1(PrintChText, StoreChar[1], StoreChar[2]);
   end
  else if StoreChar[2] in ['a'..chr(ord('a') + (ord(StoreChar[1]) - ord('a') + 1) - 1)] then
   begin
    PrintChText := chr(ord(StoreChar[2]) - (ord(StoreChar[1]) - ord('a') + 1) + 26);

    CheckShift3(PrintChText, StoreChar[1], StoreChar[2]);
   end;
  write(outfile, PrintChText);		{decoded character from the Text of AutoKey Cipher.}
  StoreChar[2] := PrintChText;
 end;					{procedure DecodeText.				   }

{Post: PrintChKey is of lowercase character type}

{******************************************************************************************}

 procedure Encode (var ChPro: char);

{Encoding procedure which specifies the data used for the key: the inputted key or the 
 original text										   }

{Pre1: ch is of lowercase character type}

 begin
  if ChPro in ['a'..'z'] then		{ensures that the teat being encoded is of a 	   }
   begin				{lowercase character type. 			   }
{Pre2: CountLetter <= CountKey and CountKey > 0}
    if CountLetter <= CountKey then	{defines when the encoding has been completed using}
     begin				{the key.					   }
      EncodeKey(PositionKey);		{procedure.					   }
     end
    else
{Pre3: CountLetter > CountKey}
     begin
      EncodeText;			{procedure.				           }
     end;
   end;
 end;					{procedure Encode.				   }

{Post: an ASCII charcter was written to outfile}

{*************************************************************}

 procedure Decode (var ChPro: char);

{Decoding procudure which specifies the data used for the key: the inputted key or the 
original text										   }

{Pre1: ch is of lowercase character type}

 begin
  if ChPro in ['a'..'z'] then	       {ensures that the teat being encoded is of a        }
   begin			       {lowercase character type. 			   }
{Pre2: CountLetter <= CountKey and CountKey > 0}
    if CountLetter <= CountKey then    {defines when the encoding has been completed using }
     begin			       {the key.				       	   }
      DecodeKey(PositionKey);	       {procedure.					   }
     end
    else
{Pre3: CountLetter > CountKey}
     begin
      DecodeText;			{procedure.					   }
     end;
   end;
 end;					{procudure Decode.				   }

{Post: an ASCII charcter was written to outfile}

{*************************************************************}

{ MAIN PROGRAM }

begin					{main program.					   }
 Initialise;				{procedure. 					   }
 SelectMode;				{procedure.					   }

 while not eof(infile) do
  begin
   if eoln(infile) then
    begin			        {progression to the next line of text.		   }
     readln(infile);
     writeln(outfile);
    end
   else
    begin
     read(infile, Ch);			{reads the next ASCII character.		   }
     LowerCase(Ch);			{procedure.					   }
     StoreCharacter(Ch);		{procedure.					   }

     if mode = 'd' then		        {determines whether the program has been set to    }
      begin				{encoding or decoding mode.			   }
       Decode(Ch);			{procedure.					   }
      end
     else
      begin
       Encode(Ch);			{procedure.					   }
      end
    end;

  end;
end. 					{main program				           }

{******************************************************************************************}

