program Swagman;	{		Displays the cipher, divided into blocks and written out in columns.  }
{	It then calculates permutations and extracts plaintext with high trigram scores. }
{ Program tested on Mac with GNU Pascal and FreePascal (www.freepascal.org) compilers 3/12/08. }
{ Also tested on Windows with FreePascal compiler. }

 const
  nbuf = 25;	{ number of candidates displayed. You can set it yourself }
 type
  vec = array[1..10] of integer;
  ind = array[1..10] of integer;
 type
  scorerec = record
    score: integer;		{ score part }
    vec: string[20];  { string giving vector values }
    sol: string[255];	{ sol part }
   end;

 var
  str, str1, sol, title: string[255];
  i, j, jj, k, len, nb, nfac, ncol, score, ncl: integer;
  a: array[1..26, 1..26, 1..26] of 0..99;
  col: array[1..20] of integer;
  buff: array[1..100] of scorerec;
  temp: scorerec;
  block: array[1..10, 1..50] of char;		{ array of ciphertext written out by column changed 3/12/08 }
  Infile, datafile: text;
  x: vec;
  sig, inc: ind;
  C: char;
  firstp1, flag: boolean;

 function TriAvg (string1: string): integer;
  var
   i, j, k, nt, kkk: integer;
   sum, avg: real;
 begin
   nt := length(string1) - 2; { number of trigrams }
   sum := 0;
   for kkk := 1 to nt do
      begin
         i := ord(string1[kkk]) - 64;
         j := ord(string1[kkk + 1]) - 64;
         k := ord(string1[kkk + 2]) - 64;
         sum := sum + a[i, j, k];
      end;
   avg := 14 * sum / nt;
   TriAvg := trunc(avg);
 end;    { of function Triavg }

 procedure init (n: integer; var inc, sig: ind; var firstp: boolean);
  var
   j: integer;
 begin
  for j := 1 to n - 1 do
   begin
    sig[j] := 0;
    inc[j] := 1
   end;
  firstp := false
 end;		{ of procedure init }

 procedure exch (l: integer; var x: vec);
  var
   t: integer;
 begin
  t := x[l];
  x[l] := x[l + 1];
  x[l + 1] := t
 end;    { of procedure exch }

 procedure nextperm (n: integer; var x: vec; var inc, sig: ind; var firstp: boolean);
  var
   k, m, sigm: integer;
 begin
   if firstp then
      init(n, inc, sig, firstp);
   k := 0;
   m := 1;
   sigm := sig[m] + inc[m];
   sig[m] := sigm;
   while (sigm = n - m + 1) or (sigm = 0) do
      begin
         if sigm = 0 then
            begin
               inc[m] := 1;
               k := k + 1
            end
         else
            inc[m] := -1;
         if m = n - 1 then
            begin
               exch(n - 1, x);
               firstp := true;
               sigm := 1
            end
         else
            begin
               m := m + 1;
               sigm := sig[m] + inc[m];
               sig[m] := sigm
            end
      end;  { of while }
   if not firstp then
      exch(k + sigm, x)
 end;    { of procedure nextperm }

 procedure AddRecord;	{ puts current score into buffer.  Enter with current scorerec=temp }
  var
   i, j: integer;
 begin
   for i := 1 to nbuf do
      if temp.score >= buff[i].score then
         begin
            for j := nbuf - 1 downto i do	{ move down remaining entries }
               buff[j + 1] := buff[j];
						buff[i] := temp;		{ insert current one }
						exit;		{ finished, get out of AddRecord changed 3/12/08 }
         end;
 end;	{ of procedure AddRecord }

{     Main Program }

begin
 assign(datafile, 'trigramp.dat');
 reset(datafile);	{ open datafile with title trigramp.dat changed 3/12/08 }
 for k := 1 to 26 do
  for j := 1 to 26 do
   begin
    readln(datafile, str);			{ read in a line of trigram data }
    for i := 1 to 26 do
     a[k, j, i] := 10 * (ord(str[3 * i - 2]) - 48) + ord(str[3 * i - 1]) - 48;
   end;

{			Now get con  }

 title := '      SWAGMAN HELPER';
 write('ENTER THE FILE NAME (''-1'' to quit): ');
 readln(str1);                { read in filename }
 if str1 = '-1' then
  halt;
 assign(Infile, str1);		{ open Infile with title str1 changed 3/12 08 }
 reset(Infile);	
 readln(Infile, str);			{ read puzzle into str }
 Writeln(title, '   ', str1);
 len := length(str);
 writeln('Length=', len:3);

{		Find factors of len }

 j := 0;
 for i := 2 to len div 2 do
  if len mod i = 0 then
   begin
    j := j + 1;			{ advance counter }
    col[j] := i;			{ store factor in array }
   end;
 nfac := j;					{ number of factors found }
 write('Factors:');
 for i := 1 to nfac do
  write(col[i] : 3);
 writeln;

 write('ENTER DESIRED NUMBER OF COLUMNS (''-1'' to quit): ');
 readln(ncol);
 if ncol = -1 then
  halt;
 flag := false;			{ factor not found flag  }
 for i := 1 to nfac do
  if ncol = col[i] then
   flag := true;
 if not (flag) then
  begin
   write('Column number ', ncol : 2, ' not allowed');
   halt;
  end;
 nb := len div(ncol * ncol);		{ number of complete blocks }
 ncl := (len mod (ncol * ncol)) div ncol; { number of remaining columns }
 writeln;
   for i := 1 to nbuf do
      begin		{ initialize buffer }
         buff[i].score := 0;
         buff[i].vec := copy(str, 1, ncol);
         buff[i].sol := copy(str, 1, len div ncol);
      end;
 temp.score := 0;	{ initialize temporary buffer used for each square }
 temp.vec := copy(str, 1, ncol);
 temp.sol := copy(str, 1, len div ncol);

{		Now write out cipher by columns in blocks of size ncol X ncol }

 for i := 1 to ncol do		{ this is how many lines there will be }
   begin
		 for j := 1 to nb do	{ write out full blocks first }
				begin
					for k := 1 to ncol do
						begin
							C := str[i + ncol*ncol*(j - 1) + ncol*(k - 1)];	{ get character }
							write(C);
							block[i, k + ncol*(j - 1)] := C;		{ store in array }
						end;
					write(' ');
				end;	{ of j loop }
		 for k := 1 to ncl do	{ now write out partial block }
				begin
					C := str[i + ncol*ncol*nb + ncol*(k - 1)];
					write(C);
					block[i, k + ncol*nb] := C;		{ store in array }
				end;
		 writeln;
   end;	{ of i loop }
 writeln;

{		Get permutations of row numbers, put in vector x[i], where i is column }

 firstp1 := true;
 for i := 1 to ncol do
   x[i] := i;		{ initialize array x }
 repeat
  sol := '';
  nextperm(ncol, x, inc, sig, firstp1);	{ get next permutation of x }

{		Construct solution with this permutation }

  for j := 1 to nb do { full blocks }
		for i := 1 to ncol do 	{ columns }
			sol := concat(sol, block[x[i], i + ncol * (j - 1)]);
  for i := 1 to ncl do 	{ remaining columns }
		sol := concat(sol, block[x[i], i + ncol * nb]);
  score := Triavg(sol);
  temp.score := score;
  temp.sol := sol;
  temp.vec := '';
  for i := 1 to ncol do
		temp.vec := concat(temp.vec, ' ', chr(x[i] + 48));
  AddRecord;
 until firstp1;
 for i := 1 to nbuf do
		writeln(buff[i].vec,' ',' ', buff[i].sol,'  ', buff[i].score);
end.
