using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using ExtensionMethods; namespace CryptanalysisCore { public static class TextAnalysis { /// /// Vrátí dvojice ve tvaru znak:počet výskytů na začátku slova /// /// seznam slov /// public static Dictionary StartLetters(string[] words) { return SomeLetters(words, word => word[0]); } /// /// Vrátí dvojice ve tvaru znak:počet výskytů na konci slova /// /// seznam slov /// public static Dictionary EndLetters(string[] words) { return SomeLetters(words, word => word[word.Length - 1]); } /// /// Najde ve slově písmeno, které vrátí předaná selektivní funkce /// a spočítá, kolikrát se toto písmeno vyskytuje ve všech slov. /// Vrátí jako dvojice písmeno:počet výskytů /// /// seznam slov /// Selektivní funkce vracející písmeno ze slova /// public static Dictionary SomeLetters(string[] words, Func selectLetter) { var letters = new Dictionary(); Analyse.DoAlphabet(l => letters[l] = 0); words.ForEach(word => letters[selectLetter(word)]++); int count = 0; letters.ForEach(x => count += x.Value); return letters.OrderByDescending(x => x.Value) .ToDictionary(x => x.Key.ToString(), x => ((double)x.Value / (double)count) * 100); } /// /// Vrátí relativní výskyt jednotlivých ngramů v textu. /// /// Text, ve kterém se budou hledat ngramy /// Délka ngramu. 1+ /// public static Dictionary GetOccurrence(string text, int length) { Dictionary occurrence = new Dictionary(); string ngram; for (int i = 0; i < text.Length - length + 1; i++) { ngram = text.Substring(i, length); if (occurrence.ContainsKey(ngram)) occurrence[ngram]++; else occurrence[ngram] = 1; } double multiply = (double)text.Length / 100; Dictionary relativeOccurrence = new Dictionary(); foreach (KeyValuePair occPair in occurrence) relativeOccurrence[occPair.Key] = (double)occPair.Value / multiply; if (length == 1) { Analyse.DoAlphabet(x => { if (!relativeOccurrence.ContainsKey(x.ToString())) relativeOccurrence[x.ToString()] = 0; }); } return relativeOccurrence.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, y => y.Value); } /// /// Zjistí, zda je dané písmeno z anglické abecedy [a-zA-Z] /// /// Jakýkoliv znak /// true: znak anglické abecedy, false: jinak public static bool IsEnglishLetter(char character) { return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z'); } /// /// Vrátí řetězec, který obsahuje pouze písmena a mezery, /// přičemž nedovolí mít dvě mezery za sebou. /// /// /// public static string GetLetters(string s) { StringBuilder sb = new StringBuilder(); foreach (char c in s) { if (IsEnglishLetter(c)) sb.Append(c); else { if (c == ' ') { if (sb.Length == 0 || sb[sb.Length - 1] != ' ') sb.Append(' '); } } } return sb.ToString(); } /// /// Vrátí průměrný počet výskytů písmen před a po nějakém písmenu. /// /// /// public static NearbyLetters NearbyLetters(string[] words) { return NearbyLetters(words, 150); } /// /// Vrátí průměrný počet výskytů písmen před a po nějakém písmenu. /// /// /// Kolik slov mají mít jednotlivé části, /// ve kterých se to bude počítat. /// public static NearbyLetters NearbyLetters(string[] words, int partSize) { Dictionary nextLetters = new Dictionary(); Dictionary prevLetters = new Dictionary(); List foundNextLetters = new List(); List foundPrevLetters = new List(); char letter; for (int j = (int)'a'; j <= (int)'z'; j++) { nextLetters[(char)j] = 0; prevLetters[(char)j] = 0; } for (int part = 0; part <= words.Length / partSize; part++) { var takenarr = words.Take(part * partSize, partSize); for (int j = (int)'a'; j <= (int)'z'; j++) { letter = (char)j; foundNextLetters.Clear(); foundPrevLetters.Clear(); foreach (string word in takenarr) { for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { if ((i < word.Length - 1) && !foundNextLetters.Find(word[i + 1])) foundNextLetters.Add(word[i + 1]); if ((i > 0) && !foundPrevLetters.Find(word[i - 1])) foundPrevLetters.Add(word[i - 1]); } } } nextLetters[letter] += foundNextLetters.Count(); prevLetters[letter] += foundPrevLetters.Count(); } } int count = 0; nextLetters.ForEach(x => count += x.Value); double multiply = (double)count / 100; var resNext = nextLetters.ToDictionary(x => x.Key.ToString(), x => (double)x.Value / multiply); count = 0; nextLetters.ForEach(x => count += x.Value); multiply = (double)count / 100; var resPrev = prevLetters.ToDictionary(x => x.Key.ToString(), x => (double)x.Value / multiply); return new NearbyLetters(resPrev, resNext); } /// /// Zjišťuje, jestli je daný otevřený text validní vstup do šifrovací funkce /// /// Otevřený text /// true - text je v pořádku; false - v textu je nějaká chyba public static bool IsOpentextValid(string opentext) { return Regex.IsMatch(opentext, @"^[a-z ]*$"); } /// /// Zjišťuje, jestli je daný šifrový text validní vstup do dešifrovací funkce /// /// Šifrový text /// true - text je v pořádku; false - v textu je nějaká chyba public static bool IsCiphertextValid(string ciphertext) { return Regex.IsMatch(ciphertext, @"^[A-Z ]*$"); } /// /// Převede obyčejný string na pole slov. /// /// Libovolný text /// Pole slov. public static string[] ToWords(string text) { return Analyse.NormalizeText(text, Analyse.TextTypes.WithSpacesLower).Split(' ').Where(x => x.Length > 0).ToArray(); } /// /// Najde ty bigramy, pro které můžeme v seznamu bigramů najít i jejich převrácený tvar /// /// Pole bigramů /// public static List GetSymetricBigrams(string[] bigrams) { List symBigrams = new List(); foreach (string bigram in bigrams) { if ((Array.Find(bigrams, x => bigram[0] == x[1] && bigram[1] == x[0]) != null) && symBigrams.Find(pair => pair[0] == bigram || pair[1] == bigram) == null) symBigrams.Add(new string[] { bigram, bigram.ToCharArray().Reverse().ToList().Implode("") }); } return symBigrams; } /// /// Vrátí slovník reprezentující substituce mezi slovy /// /// /// public static Dictionary GetLetterSubst(Dictionary wordPairs) { Dictionary substitutions = new Dictionary(); foreach (var pair in wordPairs) { for (int i = 0; i < pair.Key.Length; i++) { substitutions[pair.Key[i]] = pair.Value[i]; } } return substitutions; } /// /// Vrátí slovník reprezentující substituce mezi slovy /// sloučený z již existujícími substitucemi /// /// /// /// public static Dictionary GetLetterSubst(Dictionary wordPairs, Dictionary substitutions) { Dictionary subsPairs = GetLetterSubst(wordPairs); substitutions.ForEach(pair => subsPairs[pair.Key] = pair.Value); return subsPairs; } /// /// Zjistí, zda dané dva slovníky představují stejný vzor substitucí. /// Kontroluje oba směry. (TODO: Je to vůbec nutné?) /// /// /// /// public static bool AreSubstMatch(Dictionary subst1, Dictionary subst2) { bool res = AreSubstsMatchHelp(subst1, subst2); if (res) return AreSubstsMatchHelp(subst2, subst1); else return false; } /// /// Zjistí, zda dané dva slovníky představují stejný vzor substitucí /// /// /// /// private static bool AreSubstsMatchHelp(Dictionary subst1, Dictionary subst2) { foreach (var pair in subst1) { if (subst2.ContainsKey(pair.Key) && pair.Value != subst2[pair.Key]) return false; } return true; } /// /// Vrátí slovník reprezentující substituce ve slovech /// /// /// public static Dictionary GetLettersSubst(KeyValuePair pair) { Dictionary subst = new Dictionary(); for (int i = 0; i < pair.Key.Length; i++) subst[pair.Key[i]] = pair.Value[i]; return subst; } public static List PolygonAttack(char[] topTextLetters, char[] topLetters, int complementSize) { var variations = Combinatorics.Variations(topTextLetters.ToList(), topLetters.Length); variations.Reverse(); List matchLetters = new List(); List notMatch = new List(); foreach (var variation in variations) { string cut = variation[0].ToString() + variation[1].ToString(); var permutations = Combinatorics.Permutation(variation); foreach (var permutation in permutations) { if (permutation[0] == 'f' && permutation[1] == 'b' && permutation[2] == 'p') complementSize++; if (LettersDiff(permutation.ToArray(), topLetters)) { matchLetters.Add(new string(permutation.ToArray())); break; } } } return matchLetters; } public static bool LettersDiff(char[] letters, char[] topLetters) { int[,] lettersMatrix = GetDiffMatrix(letters); int[,] topLettersMatrix = GetDiffMatrix(topLetters); return AreMatrixEqual(lettersMatrix, topLettersMatrix); } public static bool AreMatrixEqual(int[,] matrix1, int[,] matrix2) { for (int i = 0; i < matrix1.GetLength(0); i++) { for (int j = 0; j < matrix1.GetLength(1); j++) { if (matrix1[i, j] != matrix2[i, j]) return false; } } return true; } public static int[,] GetDiffMatrix(char[] letters) { int[,] matrix = new int[letters.Length, letters.Length]; for (int i = 0; i < letters.Length; i++) { for (int j = 0; j < letters.Length; j++) { matrix[i, j] = Analyse.MinDistance(letters[i], letters[j]); } } return matrix; } } }