using System; using System.Collections.Generic; using System.Linq; using System.Text; using ExtensionMethods; namespace CryptanalysisCore { public class UniqueWords { /// /// Seznam všech unikátních slov ze slovníku /// roztříděných do tříd ekvivalence. /// private Dictionary> uniqueWords; /// /// Unikátní slova nalezená v předaném textu. Viz metoda Find. /// private Dictionary uniquePairs; /// /// Vyfiltrované páry /// private Dictionary bestPairs; public UniqueWords(string[] uniqueWords) { this.uniqueWords = EquivalenceClass(uniqueWords); uniquePairs = null; } /// /// Vrátí všechna slova z words, která jsou unikátní /// (Tj. která mají vzor v uniqueWords) /// /// /// public Dictionary Find(string[] words) { int index; Dictionary unique = new Dictionary(); foreach (string word in words) { index = GetEqIndex(word); if (uniqueWords.ContainsKey(index)) { string uniqueWord = GetPattern(word, uniqueWords[index]); if (uniqueWord != null) { unique[word] = uniqueWord; } } } uniquePairs = unique; return uniquePairs; } /// /// Vrátí nejpravděpodobnější substituce písmen založené /// na unikátních slovech. /// /// /// public Dictionary GetLettersSubst(string[] words) { Find(words); var bestPairs = Filter(); return bestPairs != null ? TextAnalysis.GetLetterSubst(bestPairs) : null; } /// /// Projde nalezené unikátní dvojice a pokusí se z /// nich vyfiltrovat ty, které tam nesedí. /// /// public Dictionary Filter() { WordsFilter uniqueFilter = new WordsFilter(uniquePairs); bestPairs = uniqueFilter.Filter(ArePairsMatch); return uniqueFilter.Checked ? bestPairs : null; } /// /// Odpovídají si předané dvojice unikátních slov? /// /// /// /// private bool ArePairsMatch(KeyValuePair pair1, KeyValuePair pair2) { var subst1 = TextAnalysis.GetLettersSubst(pair1); var subst2 = TextAnalysis.GetLettersSubst(pair2); return TextAnalysis.AreSubstMatch(subst1, subst2); } /// /// Vrátí to slovo ze seznamu, které má stejný vzor jako předané slovo. /// /// /// /// private string GetPattern(string word, List uniqueWords) { foreach (string uniqueWord in uniqueWords) if (SamePattern(word, uniqueWord)) return uniqueWord; return null; } /// /// Zjistí, zda předaná slova mají stejný vzor. /// /// /// /// true v případě, že slova mají stejný vzor, false jinak public static bool SamePattern(string word1, string word2) { if (word1.Length != word2.Length) return false; var checkedChars = new List(); var sameChars = new List>(); List temp; char currentChar; // Nalezneme stejná písmena v prvním slově for (int i = 0; i < word1.Length; i++) { currentChar = word1[i]; if (checkedChars.Find(currentChar)) continue; checkedChars.Add(currentChar); temp = new List(); temp.Add(i); for (int j = i + 1; j < word1.Length; j++) if (word1[j] == currentChar) temp.Add(j); sameChars.Add(temp); } // Zjistíme, zda druhé slovo odpovídá vzoru List findedChars = new List(); char findedChar; foreach (var same in sameChars) { findedChar = word2[same[0]]; if (findedChars.Find(findedChar)) return false; findedChars.Add(findedChar); if (same.Count > 1) foreach (int index in same) { if (word2[index] != word2[same[0]]) return false; } } return true; } /// /// Zjistí, zda je dané slovo unikátní vzhledem k předaným slovům /// /// /// /// private bool IsWordUnique(string testWord, string[] words) { foreach (string word in words) { if (SamePattern(word, testWord) && word != testWord) return false; } return true; } /// /// Od začátku slova počítá písmena, dokud nenarazí na jedno písmeno dvakrát /// /// /// private int CountDiffFirstLetters(string word) { List finded = new List(); for (int i = 0; i < word.Length; i++) if (finded.Find(word[i])) return i; else finded.Add(word[i]); return word.Length; } private int CountDiffZiggzaggLetters(string word) { List finded = new List(); for (int i = 0; i < word.Length; i += 2) if (finded.Find(word[i])) return finded.Count(); else finded.Add(word[i]); return finded.Count(); } private int CountDiffLastLetters(string word) { return CountDiffFirstLetters(word.Reverse()); } /// /// Roztřídí předaná slova do tříd ekvivalence, /// aby se nemuselo každé slovo prohledávat s každým jiným. /// /// /// public Dictionary> EquivalenceClass(string[] words) { Dictionary> eqTexts = new Dictionary>(); for (int i = 0; i < 100; i++) eqTexts[i] = new List(); foreach (string text in words) eqTexts[text.Length].Add(text); var wordLengthEq = eqTexts.Where(x => x.Key <= 30 && x.Key > 7 && x.Value.Count > 0).Select(x => x.Value.Distinct().ToArray()).ToArray(); var difffirstletter = DifferentPosLetters(wordLengthEq); return difffirstletter; } /// /// Rozdělí slova do tříd ekvivalence na základě počtu různých písmen /// od začátku, od konce a ob dva. /// /// /// private Dictionary> DifferentPosLetters(string[][] texts) { Dictionary> eqTexts = new Dictionary>(); int index; foreach (string[] text in texts) { foreach (string word in text) { index = GetEqIndex(word); if (eqTexts.ContainsKey(index)) eqTexts[index].Add(word); else eqTexts[index] = new List() { word }; } } return eqTexts; } /// /// Spočítá index pro použití v třídách ekvivalence /// /// /// private int GetEqIndex(string word) { int firstDiff, lastDiff, ziggzaggDiff, index; firstDiff = CountDiffFirstLetters(word); lastDiff = CountDiffLastLetters(word); ziggzaggDiff = CountDiffZiggzaggLetters(word); index = firstDiff + 30 * lastDiff + 900 * ziggzaggDiff + 900 * 30 * word.Length; return index; } /// /// Vrátí seznam unikátních slov, použití ve statistice /// /// /// public List GetUniqueWords() { // upgrade test //var textsArr = texts.Where(x => x.Value.Count < 3000).Select(x => x.Value.ToArray()).ToArray(); var textsArr = uniqueWords.Where(x => x.Value.Count < 3000).Select(x => x.Value.ToArray()).ToArray(); List uniqueWords2 = new List(); int counter = 1; foreach (string[] words in textsArr) { string[] unique = GetUniqueWordsFromArray(words); if (unique.Length > 0) uniqueWords2.Add(unique); counter++; } return uniqueWords2; } /// /// Vrátí seznam unikátních slov v daném poli /// /// /// private string[] GetUniqueWordsFromArray(string[] words) { List uniqueWords = new List(); foreach (string word in words) { if (IsWordUnique(word, words)) uniqueWords.Add(word); } return uniqueWords.ToArray(); } } }