First, let me echo what's been said already a few times - I really like these puzzles you've been posting. It's been far too long since I challenged myself to do coding like this! This code needs some some clearing up, but I left much of the working in using seperate variables so that it's a bit easier to follow. This implements the Playfair cypher that you linked, both to encrypt and decrypt. I have not added a reversal for the additional "X" insertions on duplicate characters as I don't believe this can be done programatically in a way that's completely reliable. Following wikipedia's example of encrypting "Hide the gold in the tree stump" I also get the matching string "BMODZBXDNABEKUDMUIXMMOUVIF". The same then successfully decrypted, given the above limitation. It's written in SAS 9.1.3, although I don't think I've used anything that's different in later versions. %let keyword = 'playfair example'; %let plaintext = 'Hide the gold in the tree stump'; %let cyphertext_length = %eval(2*(%length(&plaintext.))); %put &cyphertext_length.; data cypher; /*covert keyword to uppercase, append alphabet, and remove all non uppercase letter characters*/ keyword = translate(compress(upcase("&keyword." || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'),,'kU'),'I','J'); /*output;*/ do i = 1 to length(keyword)-1; substr(keyword,i+1) = compress(substr(keyword,i+1),substr(keyword,i,1)); /* output;*/ end; format key1-key25 $1.; array key {25} key1-key25; do i = 1 to 25; key{i} = substr(keyword,i,1); end; drop i; run; data encrypt; set cypher; format plaintext $&cyphertext_length..; format cyphertext $&cyphertext_length..; plaintext = compress(upcase("&plaintext."),,'kU'); if _n_ > 1 then stop; array key {0:4,0:4} key1-key25; /*scan for pairs*/ i = 1; do until (1=0); if i = length(plaintext) then do; /*last character, and has no pair*/ plaintext=cats(plaintext,"X"); end; if (i >= length(plaintext)) then leave; if substr(plaintext,i,1) = substr(plaintext,i+1,1) then do; plaintext = cats(substr(plaintext,1,i),"X",substr(plaintext,i+1)); end; i = i + 2; end; do i = 1 to length(plaintext) by 2; /*can only have an even number of characters now*/ c1 = substr(plaintext,i,1); c2 = substr(plaintext,i+1,1); pos1 = whichc(substr(plaintext,i,1), of key ) - 1; row1 = int(pos1/5); col1 = mod(pos1,5); test1 = key[row1,col1]; pos2 = whichc(substr(plaintext,i+1,1), of key ) - 1; row2 = int(pos2/5); col2 = mod(pos2,5); test2 = key[row2,col2]; if row1 = row2 then do; /*same row*/ newrow1 = row1; newrow2 = row2; newcol1 = mod(col1+1,5); /*move one to the right*/ newcol2 = mod(col2+1,5); end; else if col1 = col2 then do; /*same column*/ newrow1 = mod(row1+1,5); newrow2 = mod(row2+1,5); newcol1 = col1; newcol2 = col2; end; else do; /*different row and column*/ newrow1 = row1; /*keep rows*/ newrow2 = row2; newcol1 = col2; /*swap columns*/ newcol2 = col1; end; newc1 = key[newrow1,newcol1]; newc2 = key[newrow2,newcol2]; substr(cyphertext,i,2) = cats(key[newrow1,newcol1],key[newrow2,newcol2]); /* output;*/ end; keep keyword plaintext cyphertext key1-key25; run; data decrypt; set encrypt (keep = key1-key25 keyword cyphertext); format plaintext $&cyphertext_length..; array key {0:4,0:4} key1-key25; do i = 1 to length(cyphertext) by 2; /*can only have an even number of characters now*/ c1 = substr(cyphertext,i,1); c2 = substr(cyphertext,i+1,1); pos1 = whichc(substr(cyphertext,i,1), of key ) - 1; row1 = int(pos1/5); col1 = mod(pos1,5); test1 = key[row1,col1]; pos2 = whichc(substr(cyphertext,i+1,1), of key ) - 1; row2 = int(pos2/5); col2 = mod(pos2,5); test2 = key[row2,col2]; if row1 = row2 then do; /*same row*/ newrow1 = row1; newrow2 = row2; newcol1 = mod(col1+4,5); /*move one to the left*/ newcol2 = mod(col2+4,5); end; else if col1 = col2 then do; /*same column*/ newrow1 = mod(row1+4,5); newrow2 = mod(row2+4,5); newcol1 = col1; newcol2 = col2; end; else do; /*different row and column*/ newrow1 = row1; /*keep rows*/ newrow2 = row2; newcol1 = col2; /*swap columns*/ newcol2 = col1; end; newc1 = key[newrow1,newcol1]; newc2 = key[newrow2,newcol2]; substr(plaintext,i,2) = cats(key[newrow1,newcol1],key[newrow2,newcol2]); /* output;*/ end; keep keyword plaintext cyphertext; run;
... View more