using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace AniNIX.Crypto {
public class Analysis : Cipher {
private Substitution _sb;
public override String Description() { return "These are analysis tools to help understand ciphers."; }
public override String Command() { return "analysis"; }
public Analysis(Workbench w) : base (w) {
//Analysis needs to be able to read the EngCommon array from the Substition class
this._sb = new Substitution(w);
}
///
/// Decode the user's input and relate to an existing function.
///
/// The working copy of the cipher
/// The original cipher
/// The user input
/// The modified workspace string
public override String RunCommand(String workSpace, String inputText, String[] line) {
if (workSpace == null || inputText == null || line == null || line.Length < 2) {
Console.Error.WriteLine("Malformed!");
return workSpace;
}
switch (line[1]) {
case "freq":
Frequency(workSpace);
break;
case "freqinfo":
FrequencyInfo();
break;
case "one-to-one":
OneToOneQuery(workSpace,inputText);
break;
case "free":
FindFreeCharacters(workSpace,inputText);
break;
case "diff":
Diff(line);
break;
case "charinfo":
CharInfo(line);
break;
case "hexprint":
HexPrint(workSpace);
break;
case "help":
case "":
GetHelp();
break;
default:
Console.Error.WriteLine("Invalid command. Type 'analysis help' for more.");
break;
}
return workSpace;
}
///
/// Show this help text
///
public override void GetHelp() {
Console.WriteLine("Analysis tools help:\nanalysis freq -- Get frequency of characters.\nanalysis freqinfo -- Return the most common English frequencies.\nanalysis one-to-one -- See if there is a direct correspondence of characters between cipher and workspace.\nanalysis diff a b -- get the difference between two characters\nanalysis charinfo c -- get the info about a character c.\nanalysis hexprint -- print the hex and decimal value of each character in the workspace.");
}
///
/// Return a dictionary of letters mapped to the number of letters found in the workSpace
///
/// the current workSpace
/// The dictionary of frequencies
public static Dictionary FindFrequencies(String workSpace) {
Dictionary frequencies = new Dictionary();
// For each letter in the workSpace,...
for (int i = 0; i < workSpace.Length; i++) {
if (!Char.IsLetter(workSpace[i])) { continue; }
String charStr = String.Format("{0}",workSpace[i]);
// If the letter already exists, increment its frequency.
if (frequencies.ContainsKey(charStr)) {
frequencies[charStr] = frequencies[charStr] + 1;
} else {
// Otherwise add it with a frequency of one.
frequencies.Add(charStr,1);
}
}
return frequencies;
}
///
/// Return an ordered list of the most common letters in the workSpace
///
/// The user workSpace
/// An ordered list
public static List GetMostCommonLetters(String workSpace) {
// Find the frequencies.
List> freqList = FindFrequencies(workSpace).ToList();
// Sort the frequencies
freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value));
// Pull out the letters in sorted order and return.
List returnL = new List();
foreach (var item in freqList) {
returnL.Add(item.Key);
}
return returnL;
}
/// Find the doubles in a string
/// the string to analyze
/// a list of doubles
public static List GetDoubles(String workSpace) {
List theList = new List();
// For each character in the input, if the previous character is the same, it's a double. Add it to the list.
for (int i=1; i
/// Find the substrings of a given length in the workSpace.
///
/// the workSpace to analyze
/// the length of the substrings to look for
/// the dictionary of substrings by frequency
public static Dictionary GetSubstrings(String workSpace, int length) {
Dictionary theList = new Dictionary();
// Start at the beginning of the string, and advance the substring window by one until the substring segment would be outside the workSpace.
for (int i=1; i
/// find words of a given length.
///
/// The length to look for
/// A string broken down by spaces
/// the words of the length with frequencies
public static Dictionary FindWordsOfLength(int length,String[] bySpace) {
Dictionary wordsFreq = new Dictionary();
// TODO Replace this with whitespace and punctuation removal
// If the word ends in punctuation and is longer than the length or is equal to the length, add it to the list and track frequency.
for (int i = 0; i < bySpace.Length; i++) {
if (bySpace[i].Length == length || (bySpace[i].Length == length+1 && Char.IsPunctuation(bySpace[i][length]))) {
if (Char.IsPunctuation(bySpace[i][bySpace[i].Length-1])) {
bySpace[i] = bySpace[i].Substring(0,bySpace[i].Length-1);
}
if (wordsFreq.ContainsKey(bySpace[i])) {
wordsFreq[bySpace[i]] += 1;
} else {
wordsFreq.Add(bySpace[i],1);
}
}
}
return wordsFreq;
}
///
/// Get the top entries in a frequency map
///
/// Frequency map
/// A list of a given length with the top entries
public static List Top(Dictionary theList) {
List> freqList = theList.ToList();
List returnL = new List();
freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value));
for (int i = 0; i < 5 && i < freqList.Count; i++) {
returnL.Add(freqList[i].Key);
}
return returnL;
}
///
/// Print an ordered list with the frequency
///
/// the frequency map
/// String header
public static void PrintOrdered(Dictionary theList,String header) {
List> freqList = theList.ToList();
freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value));
Console.Write(header);
for (int i = 0; i < 5 && i < freqList.Count; i++) {
Console.Write(String.Format("({0}){1} ",freqList[i].Key,freqList[i].Value));
}
Console.WriteLine("");
}
///
/// Print an ordered list
///
/// the frequency map
/// String header
public static void PrintOrdered(List theList,String header) {
Console.Write(header);
foreach (String str in theList) {
Console.Write(str);
Console.Write(" ");
}
Console.WriteLine();
}
///
/// Analyze a workspace
///
/// workSpace to analyze
public void Frequency(String workSpace) {
//Show the individual letter frequeuncy.
Console.ForegroundColor = ConsoleColor.Cyan;
PrintOrdered(FindFrequencies(workSpace),"Top letters by frequency: ");
//Show the doubled letters
Console.ForegroundColor = ConsoleColor.Green;
PrintOrdered(GetDoubles(workSpace),"The following letters are doubled in the workspace: ");
Console.ForegroundColor = ConsoleColor.Yellow;
PrintOrdered(GetSubstrings(workSpace,2),"Top substrings of length 2: ");
Console.ForegroundColor = ConsoleColor.Magenta;
PrintOrdered(GetSubstrings(workSpace,3),"Top substrings of length 3: ");
String[] bySpace = workSpace.Split(' ');
//Find the words of a given length
Console.ForegroundColor = ConsoleColor.White;
PrintOrdered(FindWordsOfLength(1,bySpace),"Words of length 1: ");
Console.ForegroundColor = ConsoleColor.Yellow;
PrintOrdered(FindWordsOfLength(2,bySpace),"Words of length 2: ");
Console.ForegroundColor = ConsoleColor.Magenta;
PrintOrdered(FindWordsOfLength(3,bySpace),"Words of length 3: ");
Console.ResetColor();
}
///
/// Show the statistical frequencies.
///
public void FrequencyInfo() {
// Thanks to http://norvig.com/mayzner.html for this info.
// By letter
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Letters by frequency:");
for (int i=0; i < this._sb.EngCommon.Length; i++) {
Console.Write(this._sb.EngCommon[i]);
Console.Write(" ");
}
Console.Write('\n');
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Possible doubles: ll ee ss oo tt ff rr nn pp cc bb mm gg uu zz aa");
// By Substring 2,3 characters in length
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Top sequences of N characters where N=...");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("2: th he in er an re on at en nd");
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine("3: the and ing ion tio end ati for her ter");
// By word 1,2,3 chars in length
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Top words of length...");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("1: I a");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("2: of to in is it as be by on he");
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine("3: the and for was not are his but had you");
Console.ResetColor();
}
///
/// Find out if the workSpace has a one-to-one relationship with the input.
///
/// the workSpace
/// the user input
/// Should the program write to stdout
/// A boolean if the query is one-to-one
public bool OneToOneQuery(String workSpace, String inputText, bool shouldPrint=true) {
Dictionary relation = new Dictionary();
//Seed the keys so that we print efficiently.
StringBuilder subKey = new StringBuilder();
StringBuilder encKey = new StringBuilder();
subKey.Append("True. These are one-to-one.\n");
subKey.Append("\nFinal-to-input key:\n");
subKey.Append("sub decrypt ");
encKey.Append("sub encrypt ");
for (int i = 0; i < workSpace.Length; i++) {
// For each non-whitespace character, if the relation is known, ...
if (!Char.IsWhiteSpace(workSpace[i])) {
if (relation.ContainsKey(workSpace[i])) {
// if the relation doesn't match up, we found the mismatch and should return false.
if (relation[workSpace[i]] != inputText[i]) {
if (shouldPrint) Console.Error.WriteLine(String.Format("Character {0} repeated. These are not one-to-one.",workSpace[i]));
return false;
}
// Otherwise add the new relation pairing.
} else {
if ( workSpace[i] != inputText[i] ) {
relation.Add(workSpace[i],inputText[i]);
encKey.Append(String.Format("{0}={1} ",inputText[i],workSpace[i]));
subKey.Append(String.Format("{0}={1} ",workSpace[i],inputText[i]));
}
}
}
}
// Print the keys and return true.
if (shouldPrint) {
subKey.Append("\nInput-to-final key:");
Console.WriteLine(subKey.ToString());
Console.WriteLine(encKey.ToString());
}
return true;
}
///
/// Find the characters unused by the encryption key.
///
/// the workSpace
/// the user input
public void FindFreeCharacters(String workSpace, String inputText) {
// Start with a list of all the alphanum characters.
List alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".ToCharArray().OfType().ToList();
// Eliminate all of the ones we can.
for (int i = 0; i < workSpace.Length; i++) {
if (alphanum.Contains(workSpace[i])) alphanum.Remove(workSpace[i]);
}
// Print the remaining elements.
Console.WriteLine("Remaining characters to use in keys:");
foreach (char c in alphanum) {
Console.Write(c);
}
Console.WriteLine();
}
///
/// Show the numeric difference between two characters -- useful for identifying Caesarian ciphers
///
/// The user's input
/// -99 if malformated or the difference between characters.
public int Diff(String[] line) {
// If the number of arguments or format is wrong, return -99
if (line.Length != 4 || line[2].Length != 1 || line[3].Length != 1) {
Console.Error.WriteLine("Bad formatting");
return -99;
}
//Otherwise return -99
char first = line[2][0];
char second = line[3][0];
Console.WriteLine(String.Format("These are different by {0}.",first-second));
return (first-second);
}
///
/// Printout the info of a character
///
/// line to analyze
public void CharInfo(String[] line) {
if (line == null || line.Length != 3 || line[2].Length != 1) {
Console.Error.WriteLine("Malformed");
return;
}
// Print the ascii value of a character.
Console.WriteLine(String.Format("Character: {0}\nASCII Value: {1}",line[2][0],(int)line[2][0]));
if (Char.IsLetter(line[2][0])) {
// If the character is a letter, include the alphabet index
Console.WriteLine(String.Format("Alphabet index: {0}",(Char.IsUpper(line[2][0])) ? (int)line[2][0] - (int)'A' : (int)line[2][0] - (int)'a'));
}
}
public void HexPrint(String line) {
Console.WriteLine("Char - Dec - Hex");
foreach (char i in line.ToCharArray()) {
Console.WriteLine("{0} -- {1} -- {2}",i,(int)i,Convert.ToByte(i));
}
}
//Analysis doesn't handle encryption or decryption, but we want to use the same code for subscribing.
public override String Encrypt(string workSpace,String ciphetText,String[] line) { return workSpace; }
public override String Decrypt(string workSpace,String ciphetText,String[] line) { return workSpace; }
}
}