using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExtensionMethods;
namespace CryptanalysisCore
{
public static class Analyse
{
public enum TextTypes
{
WithSpacesLower,
WithSpacesUpper,
WithoutSpacesLower,
WithoutSpacesUpper
}
public const int OneGram = 1;
public const int Bigram = 2;
public const int Trigram = 3;
public const char Unknown = '?';
///
/// Počet písmen v abecedě
///
public const int AlphabetLetterCount = 26;
///
/// Zjistí, jak moc daný text odpovídá zvyklostem daného jazyka.
///
/// Jakýkoliv text daného jazyka
/// Vlastnosti a charakteristiky jazyka
/// Procentuální vyjádření shody s vlastnostmi jazyka
public static double SimilarityIndex(string text, LangCharacteristic lang)
{
double similarity = 0;
similarity += 3 * SumSimilarity(text, lang.Letters);
similarity += SumSimilarity(text, lang.Bigrams);
similarity += SumSimilarity(text, lang.Trigrams);
return similarity;
}
///
/// Vrátí součet rozdílů očekávaných a nalezených procentuálních výskytů znaků
///
///
///
///
private static double SumSimilarity(string text, Dictionary occurrence)
{
Dictionary standardLettersOccurrence =
occurrence.Take(6).ToDictionary(x => x.Key, x => x.Value);
Dictionary inTextLettersOcc = TextAnalysis.GetOccurrence(text, occurrence.Keys.Take(1).ToArray()[0].Length);
double sum = 0;
foreach (KeyValuePair occ in standardLettersOccurrence)
{
if (inTextLettersOcc.ContainsKey(occ.Key))
{
sum += Math.Abs(occ.Value - inTextLettersOcc[occ.Key]);
}
else
{
sum += 10;
}
}
return sum;
}
///
/// Vrací vzdálenost dvou písmen
///
///
///
/// Vzdálenost dvou písmen
public static int Distance(char a, char b)
{
return Math.Abs((int)a - (int)b);
}
///
/// Vrací minimální vzdálenost, to jest minimální počet
/// záporných nebo kladných posunů z písmene a do b.
///
///
///
/// Minimální vzdálenost dvou písmen
public static int MinDistance(char a, char b)
{
int diff = Math.Abs((int)a - (int)b);
return diff < (double)AlphabetLetterCount / 2 ? diff : AlphabetLetterCount - diff;
}
///
/// Převede text do formy vhodné k šifrování a dešifrování
/// Odstraní diakritiku, převede text na malá/velká písmena,
/// odstraní všechna nepísmena včetně/mimo mezer.
///
/// Text, který chceme normalizovat
/// Typ normalizace
/// Normalizovaný řetězec
public static string NormalizeText(string text, TextTypes textType)
{
string normText = text.RemoveDiacritics().Replace(new string[] { ".", ",", "!", "?", "\n", "\r", ";", " " }, " ");
switch (textType)
{
case TextTypes.WithoutSpacesLower:
return normText.Filter(c => TextAnalysis.IsEnglishLetter(c)).ToLower();
case TextTypes.WithoutSpacesUpper:
return normText.Filter(c => TextAnalysis.IsEnglishLetter(c)).ToUpper();
case TextTypes.WithSpacesLower:
return TextAnalysis.GetLetters(normText).ToLower();
case TextTypes.WithSpacesUpper:
return TextAnalysis.GetLetters(normText).ToUpper();
default:
throw new NotImplementedException();
}
}
///
/// Předanou metodu spustí se všemi písmeny abecedy
///
/// Metoda, která se bude aplikovat na písmena
public static void DoAlphabet(Action action)
{
for (int i = 0; i < AlphabetLetterCount; i++)
{
action((char)(i + 'a'));
}
}
///
/// Zamění písmena v otevřeném textu za jejich ekvivalenty
///
/// Text, ve kterém budou provedeny záměny
/// Definice záměn
/// Nový řetězec se změněnými znaky
public static string SwitchLetters(string opentext, Dictionary lettersSubstitution)
{
opentext = opentext.ToLower();
char[] ciphertext = new char[opentext.Length];
for (int i = 0; i < opentext.Length; i++)
if (lettersSubstitution.ContainsKey(opentext[i]))
ciphertext[i] = lettersSubstitution[opentext[i]];
else
ciphertext[i] = opentext[i];
return new string(ciphertext);
}
///
/// Vrátí relativní (procentuální) výskyt znaků v řetězci.
///
/// Libovolný řetězec
/// Predikát, kterým budou testovány jednotlivé znaky
/// Slovník ve tvaru znak - počet výskytů vzhledem k celkovému počtu znaků
public static Dictionary CharsRelativeOccurrence(String s, Predicate predicate)
{
Dictionary occurrences = CharsOccurrence(s, predicate);
Dictionary relativeOccurences = new Dictionary();
foreach (KeyValuePair occurrence in occurrences)
{
relativeOccurences[occurrence.Key] = (double)occurrence.Value / (double)s.Length * 100;
}
DoAlphabet(letter =>
{
if (!relativeOccurences.ContainsKey(letter))
relativeOccurences[letter] = 0;
});
relativeOccurences = relativeOccurences.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, y => y.Value);
return relativeOccurences;
}
///
/// Vrátí relativní (procentuální) výskyt znaků v řetězci.
///
/// Libovolný řetězec
/// Slovník ve tvaru znak - počet výskytů vzhledem k celkovému počtu znaků
public static Dictionary CharsRelativeOccurrence(String s)
{
return CharsRelativeOccurrence(s, x => true);
}
///
/// Zjistí počet jednotlivých znaků v řetězci
///
/// Libovolný řetězec
/// Slovník ve tvaru znak - počet výskytů
public static Dictionary CharsOccurrence(String s)
{
return CharsOccurrence(s, (x) => true);
}
///
/// Zjistí počet jednotlivých znaků v řetězci, které odpovídají předanému predikátu
///
/// Libovolný řetězec
/// Predikát, kterým budou testovány jednotlivé znaky
/// Slovník ve tvaru znak - počet výskytů
public static Dictionary CharsOccurrence(String s, Predicate predicate)
{
Dictionary occurrence = new Dictionary();
foreach (char character in s)
{
if (occurrence.ContainsKey(character))
{
occurrence[character]++;
}
else
{
if (predicate(character))
{
occurrence[character] = 1;
}
}
}
return occurrence;
}
///
/// Posune předaný znak v abecedě podle parametru 'IndikátorPosunu'.
/// Pokud bude například Znak = 'e' a IndikátorPosunu = 'c', bude písmeno
/// 'e' posunuto o dvě pozice na 'g'.
///
/// Znak, který má být posunut.
/// Znak který určuje jak dlouhé má být posunutí.
/// Posunutý znak.
public static char MoveCharacter(char character, char offset)
{
if (char.IsLower(character) && char.IsUpper(offset) ||
char.IsUpper(character) && char.IsLower(offset))
throw new ArgumentException("Písmena musí být buď obě velká nebo obě malá");
// Sečteme ASCI hodnoty obou znaků
int MovedCharacter = (int)character + (int)offset;
MovedCharacter -= char.IsLower(character) ? (int)'a' : (int)'A';
// Jestli jsme se dostali za konec abecedy, začneme počítat od začátku abecedy
if ((char.IsLower(character) && MovedCharacter > (int)'z') || (char.IsUpper(character) && MovedCharacter > (int)'Z'))
MovedCharacter -= 26;
// Převedeme číslo zpět na char a vrátíme
return (char)MovedCharacter;
}
public static char MoveBackCharacter(char character, char offset)
{
if (offset == Analyse.Unknown)
return Analyse.Unknown;
int MovedCharacter = (int)character - (int)offset;
MovedCharacter += char.IsLower(character) ? (int)'a' : (int)'A';
if ((char.IsLower(character) && MovedCharacter < (int)'a') || (char.IsUpper(character) && MovedCharacter < (int)'A'))
MovedCharacter += 26;
return (char)MovedCharacter;
}
///
/// Zjistí, zda záměny písmen definované v letters odpovídají
/// transformaci openWord->cipherWord
///
///
///
///
///
public static bool MatchLetters(string openWord, string cipherWord, Dictionary letters)
{
for (int i = 0; i < openWord.Length; i++)
{
if (letters.ContainsKey(openWord[i]))
{
if (letters[openWord[i]] != cipherWord[i])
return false;
}
}
return true;
}
}
}