// labeled TRIGRAM and total tallies of text from TokenReader class Tally { final static String ALPHABET = Crypt.ALPHABET; final static int NSYMBOLS = Crypt.NSYMBOLS; final static int SPACE = Crypt.SPACE; private final int OFFSET = 'a'-1; // offset to shift ASCII to array index private int[][][] trigram = new int[NSYMBOLS][NSYMBOLS][NSYMBOLS]; private int total = 0; private char[] labels = ALPHABET.toCharArray(); // return labels String labels() { return new String(labels); } // return L1 distance between own and t's trigram frequencies (fractions) double distance(Tally t) { double d = 0; // distance so far // denominators to convert tallies to fractions double n = total, nt = t.total; for (int i = 0; i=0) s[i] = (char) (bottom.charAt(where) + bias); } return new String(s); } // unscramble trigram frequences of cipher to match those of train static void unscramble(Tally train, Tally cipher) { // repeat as long as there are improvements // for each position i (other than space) // find best position j // commit "swap i&j" boolean go = true; // improvements were made double d = cipher.distance(train); // current distance while (go) { go = false; for (int i = SPACE+1; i" + s.charAt(bestj) + "; distance " + (int) (bestd*10000) + "; labels " + s); } } } } // crack the ciphertext in file cipherfile by unscrambling its frequencies // according to the training text in file trainfile static void crack(String trainfile, String cipherfile) { System.out.println("Using " + trainfile + " to crack " + cipherfile); Tally train = new Tally(new TokenReader(trainfile)); Tally cipher = new Tally(new TokenReader(cipherfile)); unscramble(train,cipher); TokenReader in = new TokenReader(cipherfile); for (String s = in.readLine(); !in.eof(); s = in.readLine()) System.out.println(transform(cipher.labels(),train.labels(),s)); } } // target class public class Trigram { public static void main(String[] args) { Crypt.crack("acc1.txt","scramble.txt"); } }