(* Some functions for use in Math 5248 "Cryptology and number theory" *) LetterToNumber[letter_]:= (* Given a lower case letter enclosed in quotation marks, spits back a number in the range 0-25 *) ToCharacterCode[letter][[1]]-97 NumberToLetter[number_]:= (* Given a number in the range 0-25, spits back an upper case letter of the alphabet *) FromCharacterCode[number+65] ApplyShiftToLetter[letter_,k_]:= (* Given a letter enclosed in quotation marks, shift it forward k positions in the alphabet *) NumberToLetter[ Mod[ LetterToNumber[letter]+k , 26 ] ] ApplyShiftToCapsLetter[letter_,k_]:= (* Needed later when shifting ciphertext letters forward *) Module[{number}, number=ToCharacterCode[letter][[1]]-65; FromCharacterCode[ 65 + Mod[ number+k, 26 ] ] ] ApplyVigenere[plaintext_,keyword_]:= (* Given a plaintext and keyword both enclosed in quoation marks, spits back the Vigenere-encrypted ciphertext *) Module[{keylength,letter,shiftletter,shiftnumber,ciphertext,i}, keylength=StringLength[keyword]; ciphertext=StringJoin[ Table[ letter = StringTake[plaintext, {i}]; shiftletter = StringTake[keyword, {Mod[i-1,keylength]+1}]; shiftnumber = LetterToNumber[shiftletter]; ApplyShiftToLetter[letter,shiftnumber] ,{i,StringLength[plaintext]}] ]; ciphertext] plaintext="wethepeopleoftheunitedstatesinordertoformamoreperfectunionestablishjusticeinsuredomestictranquilityprovideforthecommondefensepromotethegeneralwelfareandsecuretheblessingsoflibertytoourselvesandourposteritydoordainandestablishthisconstitutionfortheunitedstatesofamerica" (* keyword="victor" *) keyword="paul" ciphertext=ApplyVigenere[plaintext,keyword] TextToShiftedStream[text_,shift_]:= (* Turns a text enclosed by quotation marks in to a list of letters *) Module[{textlength,i}, textlength=StringLength[text]; Table[ StringTake[text,{i+shift}] ,{i,textlength-shift}] ] IndexOfCoincidence[stream1_,stream2_]:= (* Computes the number of positions where the two streams have the same letter, then divides by the length of the streams *) Module[{length,i,index}, length=Min[Length[stream1],Length[stream2]]; index = 1/length* Sum[ If[ SameQ[ stream1[[i]], stream2[[i]] ], 1, 0] ,{i,length}] ] AvgIndexOfCoincidence[stream1_,stream2_]:= (* Computes the 26-vector of letter frequencies in the two streams, then takes their dot product *) Module[{letters,p1,p2}, letters=Map[NumberToLetter,Range[0,25]]; p1=1/Length[stream1] * Map[ Length[Position[stream1,#]]&, letters ]; p2=1/Length[stream2] * Map[ Length[Position[stream2,#]]&, letters ]; p1.p2] FriedmanTable[ciphertext_]:= (* Computes index of coincidence between ciphertext and its forward shifts by k positions for k ranging 0 to 50 *) Module[{textlength,i,stream1,stream2}, textlength=StringLength[ciphertext]; stream1=TextToShiftedStream[ciphertext,0]; Table[ stream2=TextToShiftedStream[ciphertext,i]; {i,100*N[IndexOfCoincidence[stream1,stream2]]} ,{i,0,Min[50,textlength-1]}] ] Slice[stream_,modulus_,residue_]:= (* Computes the substream consisting of the letters whose position index has fixed residue for that modulus *) Module[{i}, Table[ stream[[i]] ,{i,residue,Length[stream],modulus}] ] AvgIndexOfShifts[stream1_,stream2_]:= (* Computes the average index of coincidence for stream1 and the shift of letter values in stream2 backwards by shift values in range 0-25 *) Module[{shiftbackstream2,valueshift}, MatrixForm[ Table[ shiftbackstream2=Map[ApplyShiftToCapsLetter[#,-valueshift]&,stream2]; {valueshift, 100*N[AvgIndexOfCoincidence[stream1,shiftbackstream2]]} ,{valueshift,0,25}] ] ] stream=TextToShiftedStream[ciphertext,0]; slice1mod4=Slice[stream,4,1]; slice2mod4=Slice[stream,4,2]; slice3mod4=Slice[stream,4,3]; slice4mod4=Slice[stream,4,4];