/*SOME FACTS WORTH KNOWING*/
/*long max is 2147483647,  unsigned long max is 4294967295, (97^5)/2 is
4293670128.5 */
/*sample time(0)=2937060310 seconds since 1/1/1970,
(long)time(0)=-1357906986*/

#include<stdio.h>
#include<math.h>
#include<time.h>

/*RANDOM NUMBER GENERATOR*/
#define MODMULT(a,b,c,m,s) q=s/a; s=b*(s-a*q)-c*q; if(s<0)s+=m;

void unscatind(int rlistbuf[], int rlistscat[], int kicklist[], int n);
void set_globals_plus(int xmask[]);
void decrypt(int rlistbuffer[], char buffer[],int rlistcypher[]);
double runup(int xrun);
double combine_lcg(void);
void init_lcg(long init_s1, long init_s2);
int getfilename(char *a);
void wildseeds(long *seed1, long *seed2, int r_seeds97[]);
int getcypher50(int rcypher[], FILE *fc, int cmask[]);

int count=1;
long s1=1, s2=1;
int loc_seeds_kicked[10];
int loc_text_kicked[40];


/*KEY IS STUFF BETWEEN LINES.  MUST BE THE SAME AS KEY FOR ENCRYPTION*/
/*-------------------------------------------------------------------------*/
#define M1_COUNT 87831L /* -50,000 < M1_COUNT < 250,000 */
#define M2_COUNT 126543L /* -50,000 < M2_COUNT < 250,000 */
#define A1_COUNT 78785L /* -.25x10^9 < A1_COUNT < 10^9 */
#define A2_COUNT -89897L /* -.25x10^9 < A2_COUNT < 10^9 */

char p_alphabet[97]={49,113,97,122,50,119,115,120,51,101,100,99,52,114,102,\
118,53,116,103,98,54,121,104,110,55,117,106,109,56,105,107,44,57,111,108,46,\
48,112,59,47,45,91,39,61,93,92,96,33,81,65,90,64,87,83,88,35,69,68,67,36,82,70\
,86,37,84,71,66,94,89,72,78,38,85,74,77,42,73,75,60,40,79,76,62,41,80,58,63,95,\
123,34,43,125,124,126,32,9,10};

char
c_alphabet[97]={113,97,122,50,119,115,120,51,101,100,99,52,114,102,118,53,\
116,103,98,54,121,104,110,55,117,106,109,56,105,107,44,57,111,108,46,48,112,59,\
47,45,91,39,61,93,92,96,33,81,65,90,64,87,83,88,35,69,68,67,36,82,70,86,37,84,71,\
66,94,89,72,78,38,85,74,77,42,73,75,60,40,79,76,62,41,80,58,63,95,123,34,43,125,\
124,126,49,32,9,10};

int
keyperm50[50]={41,4,21,33,17,37,2,32,14,30,0,15,36,1,44,34,3,48,12,25,16,27,\
42,45,29,22,49,31,47,40,8,43,5,19,23,6,10,28,18,46,35,11,20,7,39,24,9,38,13,26};

int runupseeds[10]={56,34,91,13,33,66,43,29,71,10};

int runuptext[40]={6,3,9,3,3,6,3,9,7,1,9,4,5,6,1,8,2,5,2,2,\
4,4,2,3,7,3,2,3,4,3,10,3,1,4,3,3,4,10,5,4};

int scatterseed[10]={51,44,61,11,39,27,51,19,34,8};

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

/*IN main() WE GET 50+ CHARACTERS OF CYPHERTEXT CORRESPONDING TO FORTY*/
/*CHARACTERS OF PLAINTEXT.  THE ARRAY c_alphabet IS USED TO PUT THIS INTO ASCI*/

main()
{
/*BASIC DECLARATIONS AND SETUP*/
  int i, j, count, cmask[127];
  char ch, plainfile[20], cypherfile[20];
  int rplainline[40]={0}, rcypherline[50]={0};
  char  plainline[40]={0};
  FILE *fp, *fc;
  set_globals_plus(cmask);
  for(i=0; i<10; ++i) loc_seeds_kicked[i]=keyperm50[i];
  for(i=0; i<40; ++i) loc_text_kicked[i]=keyperm50[10+i];

/*DESIGNATES CYPHERFILE AND PLAINTEXT FILE FOR DECRYPTION AND OUTPUT*/
  printf("\nEnter the name of the  cypherfile: ");
  getfilename(cypherfile);
  fc=fopen(cypherfile, "r");
  printf("\nEnter the name of the plainfile: ");
  getfilename(plainfile);
  printf("\n");
  fp=fopen(plainfile, "w");

/*GETS LINE OF CYPHERTEXT AND DECRYPTS IT AND WRITES IT TO PLAINTEXT FILE*/
  do{
        j=getcypher50(rcypherline, fc,  cmask);
        if(j>=50) {
        decrypt(rplainline, plainline, rcypherline);
                for(i=0; i<40; ++i) fprintf(fp,"%c", plainline[i]);

        }

/*IF YOU CUT AND PASTE CYPHERTEXT FOR MAILING ETC., THERE ARE TWO COMMON*/
/*ERRORS.  ONE IS TO REMOVE THE NEWLINE ON THE LAST LINE OF CYPHERTEXT*/
/*THE OTHER IS TO ADD ADDITIONAL BLANK OR SHORT LINES.  THIS IS A BIT*/
/*OF "FAULT-TOLERANCE DETECTION" FOR THESE COMMON ERRORS*/
        if(j==80) printf("\nCorrupt cyphertext: last line missing \\n\n");
        if(j==0) printf("\nCorrupt cyphertext: extra terminal \\n present\n");

  } while(j==50);

/*FINAL CLEAN UP AND NOTICE THAT WE ARE DONE*/
  fclose(fc);
  fclose(fp);
  printf("\nDECRYPTION DONE\n");
}
/*END OF main()*/

/*INVERSE FUNCTION OF MAP SPECIFIED BY GLOBAL ARRAY c_alphabet[]*/
void set_globals_plus(int xmask[])
{
  int i;
  for(i=0; i<127; ++i)xmask[i]=-1;
  for(i=0; i<97; ++i) xmask[c_alphabet[i]]=i;
}

/*HEART OF THE DECRYPTION PROCESS IN THE NEXT TWO FUNCTIONS*/
void decrypt(int rlistbuffer[], char buffer[], int rlistcypher[])
{
  int i, r_seeds_scat[10], r_seeds97[10],  ranscatter[40], scattertext[40];
  char ch;
  long x=0L, y=0L;

  for(i=0; i<10; ++i) r_seeds_scat[i]=rlistcypher[loc_seeds_kicked[i]];
  unscatind(r_seeds97, r_seeds_scat, scatterseed, 10);
  wildseeds(&x, &y, r_seeds97);
  init_lcg(x,y);
  for(i=0; i<40; ++i) ranscatter[i]=97*runup(runuptext[i]);
  for(i=0; i<40; ++i) scattertext[i]=rlistcypher[loc_text_kicked[i]];
  unscatind(rlistbuffer, scattertext, ranscatter, 40);
  for(i=0; i<40; ++i) buffer[i]= p_alphabet[rlistbuffer[i]];
}

void wildseeds(long *seed1, long *seed2, int r_seeds97[])
{
  int i;
  long x=0L, y=0L;
  unsigned long xul, yul;
  double xd=0.0, yd=0.0;
  for(i=0; i<5; ++i) xd+=r_seeds97[i]*pow(97,i);
  for(i=5; i<10; ++i) yd+=r_seeds97[i]*pow(97,(i-5));
  xul=xd/2; yul=yd/2;
  x=xul; y=yul;
  *seed1=x; *seed2=y;
}



/*NEXT TWO FUNTIONS PLUS MODMULT DEFINE LCG PROCESS FOR PSEUDORANDOM NUMBERS*/
/*THIS LCG PROCESS DUE TO L'ECUYER IS IN SCHNEIER, APPLIED CRYPTOGRAPHY*/
double combine_lcg(void)
{
        long q, z;
        MODMULT(53668L, 40014L, 12211L, 2147483563L, s1)
        MODMULT(52774L, 40629L, 3791L, 2147483562L, s2)
        z=s1-s2;
        if(z<1) z+=2147483562L;
        return z*4.656613e-10;
}

void init_lcg(long init_s1, long init_s2)
{
        s1=init_s1;
        s2=init_s2;
}


/*NEXT TWO FUNCTIONS ARE EXTRA, FOR OUR USE OF THESE LCGs*/
double runup(int xrun)
{
        int i;
        for(i=-1; i<xrun; ++i) combine_lcg();
        return combine_lcg();
}

void unscatind(int rlistbuf[], int rlistscat[], int kicklist[], int n)
{
  int i;
  for(i=0; i<n; ++i){
    rlistbuf[i]=rlistscat[i]-kicklist[i];
    if(rlistbuf[i]<0) rlistbuf[i]+=97;
  }
}

/*FINALLY, OUR INPUT-OUTPUT RELATED ROUTINES, THE SECOND BEING CRITICAL*/
int getfilename(char *a)
{
  int i=0;
  while((a[i]=getchar())!='\n')++i;
  a[i]=0;
  return i;
}

int getcypher50(int rcypher[], FILE *fc, int cmask[])
{
        int i=0, j=0, c='\0';
        char cypherbuffer[80]={'\0'};

        for(i=0; i<80; ++i){
           c=getc(fc);
           if(c==EOF){
               if(i<50) return EOF;
               else break;
               }

           if(c=='\n'){
               if(i>=50) break;
               else return 0;  /*cyphertext line too short*/
            }
           cypherbuffer[i]=c;
        }
        j=0;
        for(i=0; (i<80)&&(cypherbuffer[i]!='\0'); ++i){
                if(cypherbuffer[i]=='\\'){
                        if(cypherbuffer[i+1]=='\\') rcypher[j++]=cmask['\\'];
                        else if(cypherbuffer[i+1]=='n')
                             rcypher[j++]=cmask['\n'];
                        else if(cypherbuffer[i+1]=='t')
                             rcypher[j++]=cmask['\t'];
                        else if(cypherbuffer[i+1]=='s')
                             rcypher[j++]=cmask[' '];
                        i=i+1;
                }
                else rcypher[j++]=cmask[cypherbuffer[i]];
        }
    if(c==EOF) return 80;
        return j;  /* j should be 50 */
}

