RosettaCodeData/Task/Vigen-re-cipher-Cryptanalysis/Ada/vigen-re-cipher-cryptanalys...

168 lines
5.8 KiB
Ada

with Ada.Text_IO;
procedure Vignere_Cryptanalysis is
subtype Letter is Character range 'A' .. 'Z';
function "+"(X, Y: Letter) return Letter is
begin
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
+ (Character'Pos(Y)-Character'Pos('A')) ) mod 26
+ Character'Pos('A'));
end;
function "-"(X, Y: Letter) return Letter is
begin
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
- (Character'Pos(Y)-Character'Pos('A')) ) mod 26
+ Character'Pos('A'));
end;
type Frequency_Array is array (Letter) of Float;
English: Frequency_Array :=
( 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 );
function Get_Frequency(S: String) return Frequency_Array is
Result: Frequency_Array := (others => 0.0);
Offset: Float := 1.0/Float(S'Length);
begin
for I in S'Range loop
if S(I) in Letter then
Result(S(I)) := Result(S(I)) + Offset;
end if;
end loop;
return Result;
end Get_Frequency;
function Remove_Whitespace(S: String) return String is
begin
if S="" then
return "";
elsif S(S'First) in Letter then
return S(S'First) & Remove_Whitespace(S(S'First+1 .. S'Last));
else
return Remove_Whitespace(S(S'First+1 .. S'Last));
end if;
end Remove_Whitespace;
function Distance(A, B: Frequency_Array;
Offset: Character := 'A') return Float is
Result: Float := 0.0;
Diff: Float;
begin
for C in A'Range loop
Diff := A(C+Offset) - B(C);
Result := Result + (Diff * Diff);
end loop;
return Result;
end Distance;
function Find_Key(Cryptogram: String; Key_Length: Positive) return String is
function Find_Caesar_Key(S: String) return Letter is
Frequency: Frequency_Array := Get_Frequency(S);
Candidate: Letter := 'A'; -- a fake candidate
Candidate_Dist : Float := Distance(Frequency, English, 'A');
New_Dist: Float;
begin
for L in Letter range 'B' .. 'Z' loop
New_Dist := Distance(Frequency, English, L);
if New_Dist <= Candidate_Dist then
Candidate_Dist := New_Dist;
Candidate := L;
end if;
end loop;
return Candidate;
end Find_Caesar_Key;
function Get_Slide(S: String; Step: Positive) return String is
begin
if S'Length= 0 then
return "";
else
return S(S'First) & Get_Slide(S(S'First+Step .. S'Last), Step);
end if;
end Get_Slide;
Key: String(1 .. Key_Length);
S: String renames Cryptogram;
begin
for I in Key'Range loop
Key(I) := Find_Caesar_Key(Get_Slide(S(S'First+I-1 .. S'Last),
Key_Length));
end loop;
return Key;
end Find_Key;
function Key_Char(Key: String; Index: Positive) return Letter is
begin
if Index > Key'Last then
return Key_Char(Key, Index-Key'Last);
else
return Key(Index);
end if;
end Key_Char;
Ciphertext: String := Remove_Whitespace(
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" &
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" &
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" &
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" &
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" &
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" &
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" &
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" &
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" &
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" &
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" &
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" &
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" &
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" &
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" &
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" &
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK");
Best_Plain: String := Ciphertext;
Best_Dist: Float := Distance(English, Get_Frequency(Best_Plain));
Best_Key: String := Ciphertext;
Best_Key_L: Natural := 0;
begin -- Vignere_Cryptanalysis
for I in 1 .. Ciphertext'Length/10 loop
declare
Key: String(1 .. I) := Find_Key(Ciphertext, I);
Plaintext: String(Ciphertext'Range);
begin
for I in Ciphertext'Range loop
Plaintext(I) := Ciphertext(I) - Key_Char(Key, I);
end loop;
if Distance(English, Get_Frequency(Plaintext)) < Best_Dist then
Best_Plain := Plaintext;
Best_Dist := Distance(English, Get_Frequency(Plaintext));
Best_Key(1 .. I) := Key;
Best_Key_L := I;
if Best_dist < 0.01 then
declare
use Ada.Text_IO;
begin
Put_Line("Key =" & Best_Key(1 .. Best_Key_L));
Put_Line("Distance = " & Float'Image(Best_Dist));
New_Line;
Put_Line("Plaintext =");
Put_Line(Best_Plain);
New_Line; New_Line;
end;
end if;
end if;
end;
end loop;
end Vignere_Cryptanalysis;