/*
  Playfair solving by Simulated Annealing with Log Tetragraphs,
  written by Michael J. Cowan using decrypting sequence from Jan Stumpel.
  In same folder requires:
        Table of Log tetragraphs, TetraLog_hank.bin;
        file named cipher.txt, containing cipher.
  August 2007.
*/


 #include <iostream>
 #include <condefs.h>
 #include <conio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <fstream.h>

  char codex[3000],mot[1000],primus;

  int code_int[1000],plain_int[1000],child[30],parent[30];
  int len,flag,buff,hi,good,counter,score,parentscore,bestscore,nok;

  short a[26][26][26][26];

  float e=2.718;
  float ratio,ds,temp,prob,limit;


  trans[8][25] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // norm
                  0,5,10,15,20,1,6,11,16,21,2,7,12,17,22,3,8,13,18,23,4,9,14,19,24,//NW-SE
                  20,21,22,23,24,15,16,17,18,19,10,11,12,13,14,5,6,7,8,9,0,1,2,3,4,//top-bot
                  4,3,2,1,0,9,8,7,6,5,14,13,12,11,10,19,18,17,16,15,24,23,22,21,20 //l to r
                   };

  time_t start_time,end_time;
  float lapse;

  FILE *InFile;

  void make_parent(void);
  void decrypt(int*x);
  void get_score(void);
  void make_child();
  void tetragraphs(void);
  void display(void);
  

 int main()
 {
    int j,m,p,q,t,x;
    int len_mot,sol;
    int *P_parent=parent, *P_child=child;

    tetragraphs();   // load tetragraph table
    //...load ciphertext....
    p=-1;
    ifstream infile ("cipher.txt");
    if (!infile) { cout<<"No file!"; getch(); return 0;  }

    while(!infile.eof())
      {
       infile.getline(mot,sizeof(mot));
       len_mot=strlen(mot);
       for(m=0;m<len_mot;m++) {p++;codex[p]=mot[m];}
      } // end while
    infile.close();
    len=strlen(codex);
    //...remove spaces from ciphertext.....
    t=-1;
    for(j=0;j<len;j++)
      if(codex[j]>32)
        { t++;code_int[t]=codex[j]-65; }
    len=t+1;
    randomize();
    start_time=time(NULL);  bestscore=0;

    while(1)
    {
     make_parent();
     decrypt(P_parent);
     get_score();
     parentscore=score;


     temp = 10  +  0.087 * (len-84);
     counter=0;
         do{
            make_child();
            decrypt(P_child);
            get_score();
            if(score>bestscore) {  bestscore=score;display(); }

            ds=score-parentscore;  flag=0;
            if(ds>0) flag=1; //child is better than parent
            else   // ds is negative, so child worse than parent
               {
                ratio=ds/temp;
                prob=pow(e,ratio);
                limit=1+random(1000);limit=limit/1000;
                if(prob>limit) flag=1; // child passes closeness test
               }

            if(flag==1) //child > parent, or is worse but passes closeness test
                      {
                       for(m=0;m<25;m++) parent[m]=child[m]; // replace parent with child
                       parentscore=score;

                      }
            else counter++;    // key not accepted
            
           }while(counter<50000 ); //end of do
    }
  cout<<endl<<"End ";
  getch();
  return 0;
  }

 /*------------------------------------------------------------------------*/

  void make_parent(void)

   {
      int i,j,Used[30];

      for ( i = 0; i < 25; i++ )  Used[i] = 0;
      for ( i = 0; i < 25; i++ )
          {
            j = rand( ) % 25;

            while ( Used[j] )  j = rand( ) % 25;

            parent[i] = j;
            Used[j] = 1;
          }
     for(j=0;j<25;j++) if (parent[j]>8) parent[j]++; // correct for omission of /J/
   }

 void decrypt(int*x)     // x is pointer to the key, parent or child
  {
     char primus;
     int j, n = 0, row[26], col[26];
     int c0, c1, p0, p1;

     for (j = 0; j < 25; j++)
       {
        row[*(x+j)] = j / 5;
        col[*(x+j)] = j % 5;
       }

     while (n < len)
      {
       c0 = code_int[n];
       c1 = code_int[n+1];
       if (row[c0] == row[c1])
	{
	  p0 = col[c0] ? *(x + 5 * row[c0] + col[c0] - 1)
	    : *(x + 5 * row[c0] + 4);
	  p1 = col[c1] ? *(x + 5 * row[c1] + col[c1] - 1)
	    : *(x + 5 * row[c1] + 4);
	}
      else if (col[c0] == col[c1])
	{
	  p0 = row[c0] ? *(x + 5 * (row[c0] - 1) + col[c0])
	    : *(x + 20 + col[c0]);
	  p1 = row[c1] ? *(x + 5 * (row[c1] - 1) + col[c1])
	    : *(x + 20 + col[c1]);
	}
      else
	{
	  p0 = *(x + 5 * row[c0] + col[c1]);
	  p1 = *(x + 5 * row[c1] + col[c0]);
	}
      plain_int[n] = p0;
      plain_int[n+1] = p1;
      n += 2;
    }
  }

 void get_score(void)
   {
    int j,m;

    score=0;  nok++;
    for(j=0;j<len-1;j++)
      score=score+a[plain_int[j]][plain_int[j+1]][plain_int[j+2]][plain_int[j+3]];

   }

  void make_child(void)
    {
     int m,p,q,x;
     int start1,start2,store[5];

     x=random(50)+1; // nr 1 to ()+1

            switch(x)
               {
                case 1: for(m=0;m<25;m++) child[m]=parent[trans[x][m]]; break;
                case 2: for(m=0;m<25;m++) child[m]=parent[trans[x][m]]; break;
                case 3: for(m=0;m<25;m++) child[m]=parent[trans[x][m]]; break;

                case 4: for(m=0;m<25;m++) child[m]=parent[m];     // swap 2 rows
                        start1=random(5)*5; start2=random(5)*5;
                        for(m=0;m<5;m++) store[m]=child[start1+m];
                        for(m=0;m<5;m++) child[start1+m]=child[start2+m];
                        for(m=0;m<5;m++) child[start2+m]=store[m];
                        break;
                case 5: for(m=0;m<25;m++) child[m]=parent[m];   //swap 2 columns
                        start1=random(5); start2=random(5);
                        for(m=0;m<5;m++) store[m]=child[start1+m*5];
                        for(m=0;m<5;m++) child[start1+m*5]=child[start2+m*5];
                        for(m=0;m<5;m++) child[start2+m*5]=store[m];
                        break;
                default:
                             for(m=0;m<25;m++) child[m]=parent[m];
                             p=random(25);q=random(25);
                             buff=child[p];child[p]=child[q];child[q]=buff;
              }

    }

  void tetragraphs(void)
   {
    InFile = fopen( "TetraLog_hank.bin", "rb" );
    fread( a, 2, 456976, InFile );  // read 456976 values, 2 bytes per value, into array a[][][][] from InFile
    fclose( InFile );
    cout<<"Tetragraphs loaded...."<<endl;
   }

  void display(void)
    {
        int m;
        
        end_time=time(NULL);   lapse=end_time-start_time;
        cout<<"Time="<<lapse<<" score="<<score<<" Temperature="<<temp<<endl;
        for(m=0;m<25;m++) {primus=child[m]+65; cout<<primus;} cout<<endl;
        for(m=0;m<len;m++)
           {primus=plain_int[m]+97;cout<<primus;}
        cout<<endl<<endl;


    }





