www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Caesar Cipher Cracking

reply Antonio Corbi <amcb ggmail.com> writes:
Hi folks,

I was just following Graham Hutton's excellent book "Programming 
in Haskell" (http://www.cs.nott.ac.uk/~pszgmh/book.html) and in 
chapter 5 He implements a Caesar-Cipher cracking algorithm in a 
few lines of Haskell code 
(http://www.cs.nott.ac.uk/~pszgmh/cipher.lhs).

So, as I'm also learning D, I decided to port it to D using the 
very little functional-programming in D that I know.

No attempt to optimize it has been made, only following the 
original Haskell code and rewrite it into D so there's room 
(plenty) for optimization and usage of functional-programming 
(I'm just learning here) techniques to make it more D-ish.

Hope this is useful to someone that is also learning D.
Happy message-cracking!

Antonio

---------------8><---------------------------------------
import std.stdio, std.conv;
import std.algorithm, std.algorithm.searching, std.range;
import std.ascii, std.string : countchars;

int let2int (char c) {
   return cast(int) (c - 'a');
}

char int2let (int n) {
   if (n >= 0)
     return cast(char) ('a' + n);
   else
     return cast(char) ('z' + n + 1);
}

char shift (int n, char c) {
   return isLower (c) ? int2let ((let2int (c) + n) % 26) : c;
}

string encode (int n, string s) {
   string r;

   s.each!(c => r ~= shift(n, c));
   return r;
}

alias decode = (int n, string s) { return encode (-n, s); };

float percent (int n, int m) {
   return (n / cast(float) m) * 100;
}

int[] positions (float x, float[] fl) {
   int[] il;

   auto r = zip (fl, iota(fl.length)).filter!(t => t[0]==x).map!(t 
=> t[1]);
   r.each!(a => il ~= cast(int)a);

   return il;
}

int lowers (string s) {
   return cast(int) count!(a => a.isLower)(s);
}

int cccount (char c, string s) {
   return cast(int) countchars(s, to!string(c));
}

float[] freqs (string s) {
   float[] f;
   auto allChars = "abcdefghijklmnopqrstuvwxyz";
   auto n = lowers(s);

   auto r = allChars.map!(a => percent (cccount(to!char(a), s), n) 
);
   r.each!(a => f ~= a);

   return f;
}

float chisqr (float[] os, float[] es) {
   return zip(os, es).map!(t => ((t[0]-t[1])^^2)/t[1]).sum;
}

float[] rotate (int n, float[] fl) {
   float[] f;

   auto r = fl.drop(n) ~ fl.take(n);
   r.each!(a => f ~= a);

   return f;
}

string crack (string s) {
   // ASCII letters frequency from 'a'..'z'
   float[] table = [8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 
0.2, 0.8, 4.0, 2.4,
                    6.7, 7.5, 1.9, 0.1, 6.0,  6.3, 9.1, 2.8, 1.0, 
2.4, 0.2, 2.0, 0.1];
   float[] table2 = s.freqs;

   auto chitabr = iota(26).map!(n => rotate(n, 
table2).chisqr(table));
   float[] chitab;
   chitabr.each!(a => chitab ~= a);
   auto minval = reduce!(min)(chitab);
   auto factor = positions (minval, chitab)[0];

   return decode (factor, s);
}

void main()
{
    writeln ("wyvnyhttpun pu kshun pz clyf mbu! = ",
             crack ("wyvnyhttpun pu kshun pz clyf mbu!"));
}
Aug 14 2016
parent reply Stefan <dl ng.rocks> writes:
same code, just a little shorter.

usage of ".array"
more UFCS
replaced cast with ".to"

---------------8><---------------------------------------
import std.stdio, std.conv;
import std.algorithm, std.algorithm.searching, std.range;
import std.ascii, std.string : countchars;

int let2int(char c) {
   return (c - 'a').to!int;
}
char int2let(int n) {
   return (n >= 0 ? 'a' + n : 'z' + n + 1).to!char;
}
char shift(char c, int n) {
   return c.isLower ? ((c.let2int + n) % 26).int2let : c;
}
auto encode(int n, char[] s) {
   return s.map!(c => c.to!char.shift(n));
}
alias decode = (int n, char[] s) { return encode(-n, s); };
auto positions(float x, float[] fl) {
   return fl.zip(fl.length.iota).filter!(t => t[0]==x).map!(t => 
t[1]);
}
float chisqr(float[] os, float[] es) {
   return os.zip(es).map!(t => ((t[0]-t[1])^^2)/t[1]).sum;
}
float[] rotate(int n, float[] fl) {
   return (fl.drop(n) ~ fl.take(n)).array;
}

string crack(string s) {
   auto freqs(char[] s) {
     alias cccount = (char c, char[] s) {
       return countchars(s, to!string(c)).to!int;
     };
     alias percent = (int n, int m) { return n / m.to!float * 100; 
};
     alias lowers = (char[] s) { return count!isLower(s).to!int; };
	
     enum allChars = "abcdefghijklmnopqrstuvwxyz".to!(char[]);

     return allChars.map!(a =>
       percent(cccount(a.to!char, s), lowers(s)) ).array;
   }
   // ASCII letters frequency from 'a'..'z'
   float[] table = [
     8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.2, 0.8, 4.0, 
2.4,
     6.7, 7.5, 1.9, 0.1, 6.0,  6.3, 9.1, 2.8, 1.0, 2.4, 0.2, 2.0, 
0.1];
   auto table2 = freqs(s.to!(char[]));

   auto chitab = table.length.to!uint.iota.map!(
     n => n.rotate(table2).chisqr(table)).array;
   auto minval = chitab.reduce!min;
   auto factor = positions(minval, chitab).front.to!int;

   return decode(factor, s.to!(char[])).to!string;
}

void main() {
   writeln("wyvnyhttpun pu kshun pz clyf mbu! = ",
     crack("wyvnyhttpun pu kshun pz clyf mbu!"));
}
Aug 14 2016
parent Antonio Corbi <amcb ggmail.com> writes:
On Sunday, 14 August 2016 at 18:36:02 UTC, Stefan wrote:
 same code, just a little shorter.

 usage of ".array"
 more UFCS
 replaced cast with ".to"
Wow Stefan! Thanks for your time, I'll have a look at it! Antonio
Aug 15 2016