digitalmars.D.learn - Caesar Cipher Cracking
- Antonio Corbi (91/91) Aug 14 2016 Hi folks,
- Stefan (61/61) Aug 14 2016 same code, just a little shorter.
- Antonio Corbi (4/8) Aug 15 2016 Wow Stefan!
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
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
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