diff --git a/4sem/isaip/01/PlayfairCypher/.gitignore b/4sem/isaip/01/PlayfairCypher/.gitignore new file mode 100644 index 0000000..a0d6a36 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/.gitignore @@ -0,0 +1,8 @@ +.idea/ +.vscode/ +.vs/ + +bin/ +obj/ + +*.user \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/App.axaml b/4sem/isaip/01/PlayfairCypher/App.axaml new file mode 100644 index 0000000..38963db --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/App.axaml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/4sem/isaip/01/PlayfairCypher/App.axaml.cs b/4sem/isaip/01/PlayfairCypher/App.axaml.cs new file mode 100644 index 0000000..eab8225 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/App.axaml.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using PlayfairCypher.ViewModels; +using PlayfairCypher.Views; + +namespace PlayfairCypher +{ + public class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow + { + DataContext = new MainWindowViewModel(), + }; + } + + base.OnFrameworkInitializationCompleted(); + } + } +} \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/Assets/avalonia-logo.ico b/4sem/isaip/01/PlayfairCypher/Assets/avalonia-logo.ico new file mode 100644 index 0000000..da8d49f Binary files /dev/null and b/4sem/isaip/01/PlayfairCypher/Assets/avalonia-logo.ico differ diff --git a/4sem/isaip/01/PlayfairCypher/Models/PlayfairCrypto.cs b/4sem/isaip/01/PlayfairCypher/Models/PlayfairCrypto.cs new file mode 100644 index 0000000..c416c65 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/Models/PlayfairCrypto.cs @@ -0,0 +1,161 @@ +// ReSharper disable StringLiteralTypo + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PlayfairCypher.Models { + public class PlayfairCrypto { + private static readonly HashSet EnAlphabet = new("ABCDEFGHIKLMNOPQRSTUVWXYZ"); + + public string Encrypt(string keyword, string text) { + var keyTable = GenerateTable(keyword); + var pairs = FormPairs(text); + + var data = new StringBuilder(32); + + foreach (var pair in pairs) { + var idx1 = FindCharIndex(ref keyTable, pair[0]); + var idx2 = FindCharIndex(ref keyTable, pair[1]); + + var x1 = idx1 / 5; // x for rows + var y1 = idx1 % 5; // and y for cols + var x2 = idx2 / 5; + var y2 = idx2 % 5; + + char c1, c2; + if (y1 == y2) { + c1 = keyTable[WrapIndex(x1 + 1), y1]; + c2 = keyTable[WrapIndex(x2 + 1), y1]; + } + else if (x1 == x2) { + c1 = keyTable[x1, WrapIndex(y1 + 1)]; + c2 = keyTable[x1, WrapIndex(y2 + 1)]; + } + else { + c1 = keyTable[x1, y2]; + c2 = keyTable[x2, y1]; + } + + data.Append(c1).Append(c2); + } + + return data.ToString(); + } + + public string Decrypt(string keyword, string text) { + var keyTable = GenerateTable(keyword); + var pairs = FormPairs(text); + + var data = new StringBuilder(32); + + foreach (var pair in pairs) { + var idx1 = FindCharIndex(ref keyTable, pair[0]); + var idx2 = FindCharIndex(ref keyTable, pair[1]); + + var x1 = idx1 / 5; // x for rows + var y1 = idx1 % 5; // and y for cols + var x2 = idx2 / 5; + var y2 = idx2 % 5; + + char c1, c2; + if (y1 == y2) { + c1 = keyTable[WrapIndex(x1 - 1), y1]; + c2 = keyTable[WrapIndex(x2 - 1), y1]; + } + else if (x1 == x2) { + c1 = keyTable[x1, WrapIndex(y1 - 1)]; + c2 = keyTable[x1, WrapIndex(y2 - 1)]; + } + else { + c1 = keyTable[x1, y2]; + c2 = keyTable[x2, y1]; + } + + data.Append(c1).Append(c2); + } + + return data.ToString(); + } + + private char[,] GenerateTable(string keyword) { + char[,] table = new char[5,5]; + + keyword = ClearKeyword(keyword); + for (var i = 0; i < keyword.Length; i++) { + table[i / 5,i % 5] = keyword[i]; + } + + var kwcSet = new HashSet(keyword); + var autoAlphabet = EnAlphabet.Except(kwcSet).ToList(); + for (var i = keyword.Length; i < 25; i++) { + table[i / 5,i % 5] = autoAlphabet[i-keyword.Length]; + } + + return table; + } + + private static string ClearKeyword(string keyword) { + string data = ""; + + keyword = keyword.Replace("J", "I"); + foreach (var kwc in keyword) { + if (!EnAlphabet.Contains(kwc) || kwc == ' ' || data.Contains(kwc)) { + continue; + } + + data += kwc; + } + + return data; + } + + private static int FindCharIndex(ref char[,] table, char c) { + for (var i = 0; i < 25; i++) { + if (table[i / 5, i % 5] == c) { + return i; + } + } + + throw new ArgumentOutOfRangeException($"Character '{c}' not fount in table"); + } + + private static List FormPairs(string text) { + List data = new (); + + text = text.Replace("J", "I"); + var cc = text[0]; + for (var i = 1; i < text.Length; i++) { + if (cc != '\0' && !EnAlphabet.Contains(cc)) { + cc = '\0'; + } + if (cc == '\0') { + cc = text[i]; + } + else if (cc == text[i]) { + data.Add("" + cc + "X"); + cc = text[i]; + } + else { + data.Add("" + cc + text[i]); + cc = '\0'; + } + } + + if (cc != '\0') { + data.Add("" + cc + "X"); + } + + return data; + } + + private static int WrapIndex(int idx) { + if (idx < 0) { + idx += 5; + } + + return idx % 5; + } + } +} \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/PlayfairCypher.csproj b/4sem/isaip/01/PlayfairCypher/PlayfairCypher.csproj new file mode 100644 index 0000000..39da8f3 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/PlayfairCypher.csproj @@ -0,0 +1,17 @@ + + + WinExe + net5.0 + enable + + + + + + + + + + + + diff --git a/4sem/isaip/01/PlayfairCypher/PlayfairCypher.sln b/4sem/isaip/01/PlayfairCypher/PlayfairCypher.sln new file mode 100644 index 0000000..dff9bc5 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/PlayfairCypher.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlayfairCypher", "PlayfairCypher.csproj", "{5AAEBCD9-0ADD-4C76-B1B4-92569D82970B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5AAEBCD9-0ADD-4C76-B1B4-92569D82970B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5AAEBCD9-0ADD-4C76-B1B4-92569D82970B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5AAEBCD9-0ADD-4C76-B1B4-92569D82970B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5AAEBCD9-0ADD-4C76-B1B4-92569D82970B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/4sem/isaip/01/PlayfairCypher/Program.cs b/4sem/isaip/01/PlayfairCypher/Program.cs new file mode 100644 index 0000000..8b03d21 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/Program.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.ReactiveUI; + +namespace PlayfairCypher { + internal static class Program { + public static void Main(string[] args) { + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + + private static AppBuilder BuildAvaloniaApp() { + return AppBuilder.Configure().UsePlatformDetect().LogToTrace().UseReactiveUI(); + } + } +} \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/ViewLocator.cs b/4sem/isaip/01/PlayfairCypher/ViewLocator.cs new file mode 100644 index 0000000..f7d27af --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/ViewLocator.cs @@ -0,0 +1,33 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using PlayfairCypher.ViewModels; +using ReactiveUI; + +namespace PlayfairCypher +{ + public class ViewLocator : IDataTemplate + { + public bool SupportsRecycling => false; + + public IControl Build(object data) + { + var name = data.GetType().FullName!.Replace("ViewModel", "View"); + var type = Type.GetType(name); + + if (type != null) + { + return (Control) Activator.CreateInstance(type)!; + } + else + { + return new TextBlock {Text = "Not Found: " + name}; + } + } + + public bool Match(object data) + { + return data is ReactiveObject; + } + } +} \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/ViewModels/MainWindowViewModel.cs b/4sem/isaip/01/PlayfairCypher/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..25a4687 --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using System.Text; +using Avalonia.Controls; +using PlayfairCypher.Models; +using ReactiveUI; + +namespace PlayfairCypher.ViewModels { + public class MainWindowViewModel : ReactiveObject { + private string _keyword = ""; + private string _inputText = ""; + private string _outputText = ""; + private bool _isEncrypting = true; + + private readonly PlayfairCrypto _cryptor; + + public MainWindowViewModel() { + _cryptor = new PlayfairCrypto(); + } + + public string Keyword { + get => _keyword; + set { + this.RaiseAndSetIfChanged(ref _keyword, value.ToUpper()); + CalculateCypher(); + } + } + + public string InputText { + get => _inputText; + set { + this.RaiseAndSetIfChanged(ref _inputText, value.ToUpper()); + CalculateCypher(); + } + } + + public string OutputText { + get => _outputText; + set => this.RaiseAndSetIfChanged(ref _outputText, value.ToUpper()); + } + + public bool IsEncrypting { + get => _isEncrypting; + set { + this.RaiseAndSetIfChanged(ref _isEncrypting, value); + InputText = OutputText; + } + } + + public void ClearForm() { + InputText = ""; + Keyword = ""; + } + + void CalculateCypher() { + if (InputText.Length == 0) { + OutputText = ""; + return; + } + if (Keyword.Length == 0) { + OutputText = "Error: Keyword is empty"; + return; + } + + OutputText = IsEncrypting? _cryptor.Encrypt(Keyword.Trim(), InputText.Trim()) : _cryptor.Decrypt(Keyword.Trim(), InputText.Trim()); + } + } +} \ No newline at end of file diff --git a/4sem/isaip/01/PlayfairCypher/Views/MainWindow.axaml b/4sem/isaip/01/PlayfairCypher/Views/MainWindow.axaml new file mode 100644 index 0000000..67bf65e --- /dev/null +++ b/4sem/isaip/01/PlayfairCypher/Views/MainWindow.axaml @@ -0,0 +1,33 @@ + + + + + + + + + + +