using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace coding_encoding
{
class Crypto
{
private string key;
private char[] temp;
public Crypto(string key)
{
this.key = key;
temp = this.key.ToCharArray();
Array.Sort(temp);
}
/// <summary>
/// Метод, позволяющий расшифровать строку по ключу.
/// </summary>
/// <param name="message">Строка, которую нужно расшифровать.</param>
/// <param name="key">Ключ, по которому будет производиться расшифровка.</param>
/// <returns>Расшифрованная строка.</returns>
public string Decrypt(string message)
{
string sortedKey = string.Empty;
string result = string.Empty;
Dictionary<int, char> dictionaryIntChar = new Dictionary<int, char>();
Dictionary<char, List<char>> keyValuePairs;
Dictionary<char, List<char>> resultDictionary = new Dictionary<char, List<char>>();
for (int i = 0; i < key.Length; i++)
dictionaryIntChar[i] = key[i];
foreach (char ch in temp)
sortedKey += ch;
//здесь идет добавление символа для кратности
while (message.Length % key.Length != 0)
message += " ";
keyValuePairs = GetDictionary(message, sortedKey);
for (int i = 0; i < key.Length; i++)
{
for (int j = i; j < message.Length; j += key.Length)
{
resultDictionary[dictionaryIntChar[i]] = keyValuePairs[dictionaryIntChar[i]];
}
}
//компановка полученного сообщения
return Composition(key.ToCharArray(), resultDictionary);
}
/// <summary>
/// Метод, позволяющий зашифровать строку по ключу.
/// </summary>
/// <param name="message">Строка, которую нужно зашифровать.</param>
/// <param name="key">Ключ, по которому будет производиться шифрование.</param>
/// <returns>Зашифрованная строка.</returns>
public string Encrypt(string message)
{
Dictionary<char, List<char>> keyValuePairs;
string result = string.Empty;
//добавляем в строку пустые символы для сохранения целостности столбцов
while (message.Length % key.Length != 0)
message += " ";
keyValuePairs = GetDictionary(message, key);
//компановка полученного сообщения
return Composition(temp, keyValuePairs);
}
/// <summary>
/// Компановка полученного сообщения.
/// </summary>
/// <param name="editKey">Массив из букв ключа.</param>
/// <param name="keyValuePairs">Словарь ключ-значение</param>
/// <returns>Скомпонованное сообщение.</returns>
private string Composition(char[] editKey, Dictionary<char, List<char>> keyValuePairs)
{
string result = string.Empty;
for (int i = 0; i < keyValuePairs[editKey[0]].Count; i++)
for (int j = 0; j < editKey.Length; j++)
result += keyValuePairs[editKey[j]][i].ToString();
return result;
}
/// <summary>
/// Получение словаря ключ-значение.
/// </summary>
/// <param name="message">Сообщение, которое нужно привязать к буквам ключа</param>
/// <param name="key">Буквы ключа, к которым будет привязываться сообщение</param>
/// <returns>Словарь</returns>
private Dictionary<char, List<char>> GetDictionary(string message, string key)
{
Dictionary<char, List<char>> result = new Dictionary<char, List<char>>();
for (int i = 0; i < key.Length; i++)
{
List<char> listChar = new List<char>();
//добавление символов столбца ключу
for (int j = i; j < message.Length; j += key.Length)
{
listChar.Add(message[j]);
}
//связывание ключ-значение
result.Add(key[i], listChar);
}
return result;
}
}
}
class Crypto2
{
private readonly int[] _key;
private readonly int[] _inversedKey;
public Crypto2(string key)
{
var indexPairs = key
.Select((chr, idx1) => new { chr, idx1 }) // присваиваем каждому символу индекс
.OrderBy(arg => arg.chr) // сортируем по символам
.Select((arg, idx2) => new { arg.idx1, idx2 }) // и теперь создаём массив из пар "первоначальный индекс" <=> "отсортированный индекс".
.ToArray();
// Генерируем прямой ключ: чем "меньше" символ (в алфавитном порядке), тем меньше его индекс в конечном массиве.
// "а" => 0, ... , "ь" => 5
// Получаем: "цезарь" => [4, 1, 2, 0, 3, 5]
_key = indexPairs
.OrderBy(arg => arg.idx1)
.Select(arg => arg.idx2)
.ToArray();
// Обратная операция для декодирования
_inversedKey = indexPairs
.OrderBy(arg => arg.idx2)
.Select(arg => arg.idx1)
.ToArray();
}
public string Encrypt(string message)
{
return EncryptDecrypt(message, _key);
}
public string Decrypt(string message)
{
return EncryptDecrypt(message, _inversedKey);
}
private static string EncryptDecrypt(string message, int[] key)
{
var keyLength = key.Length;
var messageLength = message.Length;
if (messageLength % keyLength > 0)
{
// Дополняем строку пробелами, если необходимо
message = message.PadRight(messageLength + keyLength - messageLength % keyLength, ' ');
}
// Никогда не используйте конкатенацию строк в цикле (типа result += keyValuePairs[editKey[j]][i].ToString();). Это очень медленно.
// Для этих целей есть специальный класс StringBuilder.
// Мы заранее знаем длину конечной строки, поэтому инициализируем StringBuilder правильно.
var sb = new StringBuilder(messageLength) {Length = message.Length};
for (var i = 0; i < message.Length; i++)
{
// Вычисляем конечный индекс для вставки символа.
// Первая часть выражения "i / keyLength * keyLength" - округление i вниз до ближайшего значения, делящегося на keyLength
// Вторая часть "key[i % keyLength]" - вычисление новой позиции символа на основе ключа.
var idx = i / keyLength * keyLength + key[i % keyLength];
sb[idx] = message[i];
}
return sb.ToString();
}
}
class Crypto3
{
private readonly int[] _key;
private readonly int[] _inversedKey;
public Crypto3(string key)
{
var indexPairs = key
.Select((chr, idx1) => new {chr, idx1})
.OrderBy(arg => arg.chr)
.Select((arg, idx2) =>
new
{
arg.idx1, idx2
})
.ToArray();
_key = indexPairs
.OrderBy(arg => arg.idx1)
.Select(arg => arg.idx2)
.ToArray();
_inversedKey = indexPairs
.OrderBy(arg => arg.idx2)
.Select(arg => arg.idx1)
.ToArray();
}
public void Encrypt(string sourceFile, string destinationFile)
{
EncryptDecrypt(sourceFile, destinationFile, _key);
}
public void Decrypt(string sourceFile, string destinationFile)
{
EncryptDecrypt(sourceFile, destinationFile, _inversedKey);
}
private static void EncryptDecrypt(string sourceFile, string destinationFile, int[] key)
{
var keyLength = key.Length;
var buffer1 = new byte[keyLength];
var buffer2 = new byte[keyLength];
using (var source = new FileStream(sourceFile, FileMode.Open))
using (var destination = new FileStream(destinationFile, FileMode.OpenOrCreate))
{
while (true)
{
var read = source.Read(buffer1, 0, keyLength);
if (read == 0)
{
return;
}
else if (read < keyLength)
{
for (int i = read; i < keyLength; i++)
{
buffer1[i] = (byte) ' ';
}
}
for (var i = 0; i < keyLength; i++)
{
var idx = i / keyLength * keyLength + key[i % keyLength];
buffer2[idx] = buffer1[i];
}
destination.Write(buffer2, 0, keyLength);
}
}
}
}