commit 16faa5ca7a00ea1043387e59c81c3850b83b2cef Author: retr0volkov Date: Sat Aug 22 19:04:22 2026 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4493f27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,352 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +.vscode/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ +/.dotnet +/.packages +/.tools/vswhere/2.5.2 +/.tools/vswhere/3.1.7 +/.tools/native/iltools + +### OSX ### + +.DS_Store +.AppleDouble +.LSOverride + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Exceptions to the exclusions +!src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/main/*.rsp +!src/Microsoft.DotNet.Wpf/src/WpfGfx/tools/csp/**/*.rsp \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..e2d6821 --- /dev/null +++ b/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..88d18a2 --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,12 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace HuffmanEditor; +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} + diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Huffman.cs b/Huffman.cs new file mode 100644 index 0000000..e4f08c9 --- /dev/null +++ b/Huffman.cs @@ -0,0 +1,346 @@ +using System.Text; + +namespace HuffmanEditor; + +public static class Huffman +{ + public static byte[] EncodeString(string str) + { + return GenerateBytes(str); + } + + public static string DecodeString(byte[] bytes) + { + return DecypherBytes(bytes); + } + + private static HuffmanNode GenerateTree(string str) + { + var frequencies = new int[65536]; + + foreach (var character in str) + frequencies[character]++; + + var priorityQueue = new PriorityQueue(); + + for (var i = 0; i < frequencies.Length; i++) + if (frequencies[i] != 0) + priorityQueue.Enqueue(new HuffmanNode((char)i, frequencies[i]), frequencies[i]); + + while (priorityQueue.Count > 1) + { + var left = priorityQueue.Dequeue(); + var right = priorityQueue.Dequeue(); + priorityQueue.Enqueue(new HuffmanNode(left, right), left.Frequency + right.Frequency); + } + + return priorityQueue.Dequeue(); + } + + private static SortedDictionary GenerateLengthDictionary(HuffmanNode node) + { + var dict = new SortedDictionary(); + + void TraverseDFS(HuffmanNode node, ushort depth) + { + if (node == null) return; + if (node.Symbol != null) + { + dict.Add(node.Symbol.Value, depth); + return; + } + TraverseDFS(node.Left!, (ushort)(depth + 1)); + TraverseDFS(node.Right!, (ushort)(depth + 1)); + } + + TraverseDFS(node.Left!, 1); + TraverseDFS(node.Right!, 1); + + return dict; + } + + private static Dictionary RecreateDictionary(SortedDictionary lenDict) + { + var sorted = lenDict.OrderBy(x => x.Value).ThenBy(x => x.Key).ToList(); + var codes = new Dictionary(); + + var currentCode = new HuffmanCode { Code = 0, Length = 0 }; + + foreach (var code in sorted) + { + if (code.Value > currentCode.Length) + { + currentCode.Code <<= (code.Value - currentCode.Length); + currentCode.Length = code.Value; + } + codes.Add(code.Key, currentCode); + currentCode.Code++; + } + + return codes; + } + + private static HuffmanNode RecreateTree(SortedDictionary lenDict) + { + var dict = RecreateDictionary(lenDict); + + var root = new HuffmanNode(); + + foreach (var code in dict) + { + var currentNode = root; + + for (var i = code.Value.Length - 1; i >= 0; i--) + { + if (((code.Value.Code >> i) & 1) == 1) + { + currentNode.Right ??= new HuffmanNode(); + currentNode = currentNode.Right; + } + else + { + currentNode.Left ??= new HuffmanNode(); + currentNode = currentNode.Left; + } + } + + currentNode.Symbol = code.Key; + } + + return root; + } + + private static byte[] GenerateBytes(string str) + { + var lenDict = GenerateLengthDictionary(GenerateTree(RSFFilter(str))); + //var lenDict = GenerateLengthDictionary(GenerateTree(str)); + + var bytes = new List + { + (byte)(lenDict.Count >> 8), + (byte)(lenDict.Count & 0xFF), + }; + + foreach (var kvp in lenDict) + bytes.AddRange( + [ + (byte)(kvp.Key >> 8), + (byte)(kvp.Key & 0xFF), + (byte)(kvp.Value >> 8), + (byte)(kvp.Value & 0xFF), + ] + ); + + bytes.AddRange( + [ + (byte)(str.Length >> 24), + (byte)(str.Length >> 16 & 0xFF), + (byte)(str.Length >> 8 & 0xFF), + (byte)(str.Length & 0xFF), + ] + ); + + var dict = RecreateDictionary(lenDict); + + byte writeBuffer = 0, counter = 0; + + foreach (var code in str) + { + var thisCode = dict[code]; + for (var i = 0; i < thisCode.Length; i++) + { + writeBuffer = (byte)((writeBuffer << 1) | ((thisCode.Code >> (thisCode.Length - i - 1)) & 1)); + counter++; + if (counter == 8) + { + counter = 0; + bytes.Add(writeBuffer); + writeBuffer = 0; + } + } + } + while (true) + { + writeBuffer = (byte)(writeBuffer << 1); + counter++; + if (counter == 8) + { + bytes.Add(writeBuffer); + break; + } + } + + return bytes.ToArray(); + } + + private static string DecypherBytes(byte[] bytes) + { + var currentByte = 0; + var letters = bytes[currentByte++] << 8 | bytes[currentByte++]; + var result = string.Empty; + + var lenDict = new SortedDictionary(); + + for (var i = 0; i < letters; i++) + lenDict.Add((char)(bytes[currentByte++] << 8 | bytes[currentByte++]), (ushort)(bytes[currentByte++] << 8 | bytes[currentByte++])); + + var characters = bytes[currentByte++] << 24 | bytes[currentByte++] << 16 | bytes[currentByte++] << 8 | bytes[currentByte++]; + + var tree = RecreateTree(lenDict); + var currentNode = tree; + + for (var i = 0; i < characters - 1; ) + { + var thisByte = bytes[currentByte++]; + + for (var j = 0; j < 8; j++) + { + if ((thisByte >> (7 - j) & 0x01) == 1) + currentNode = currentNode.Right; + else + currentNode = currentNode.Left; + if (currentNode == null) throw new ArgumentException("IDK man you done bad"); + if (currentNode.Symbol != null) + { + result += currentNode.Symbol.Value; + currentNode = tree; + i++; + } + } + } + + return RSFUnfilter(result); + //return result; + } + + private static string RSFFilter(string input) + { + if (string.IsNullOrEmpty(input)) return input; + + // Initialize with input length to reduce resizing overhead + var sb = new StringBuilder(input.Length); + var i = 0; + + while (i < input.Length) + { + var currentChar = input[i]; + var count = 1; + + // Count consecutive identical characters + while (i + count < input.Length && input[i + count] == currentChar) + { + count++; + } + + // Apply transformation if repeat count > 3 (4 or more occurrences) + if (count > 3) + { + sb.Append('\uE000'); + sb.Append(count); + sb.Append(currentChar); + } + else + { + // Append the characters as-is (1, 2, or 3 times) + sb.Append(currentChar, count); + } + + i += count; + } + + return sb.ToString(); + } + + private static string RSFUnfilter(string input) + { + if (string.IsNullOrEmpty(input)) return input; + + var sb = new StringBuilder(); + int i = 0; + + while (i < input.Length) + { + if (input[i] == '\uE000') + { + i++; // Skip the marker + int start = i; + + // Parse the numeric repeatCount + while (i < input.Length && char.IsDigit(input[i])) + { + i++; + } + + if (int.TryParse(input.AsSpan(start, i - start), out var count) && i < input.Length) + { + var repeatChar = input[i]; + sb.Append(repeatChar, count); + i++; // Move past the character being repeated + } + } + else + { + sb.Append(input[i]); + i++; + } + } + + return sb.ToString(); + } + + private struct HuffmanCode + { + public int Code; + public ushort Length; + } +} + +public class HuffmanNode : IComparable +{ + public char? Symbol + { + get; set; + } + + public int Frequency + { + get; set; + } + + public HuffmanNode? Left + { + get; set; + } + public HuffmanNode? Right + { + get; set; + } + + public HuffmanNode(char symbol, int frequency) + { + Symbol = symbol; + Frequency = frequency; + } + + public HuffmanNode(HuffmanNode left, HuffmanNode right) + { + Symbol = null; + Left = left; + Right = right; + Frequency = left.Frequency + right.Frequency; + } + + public HuffmanNode() + { + Frequency = 0; + Symbol = null; + Left = null; + Right = null; + } + + public int CompareTo(HuffmanNode? other) + { + if (other == null) return 1; + return Frequency.CompareTo(other.Frequency); + } +} \ No newline at end of file diff --git a/HuffmanEditor.csproj b/HuffmanEditor.csproj new file mode 100644 index 0000000..9e3d8cc --- /dev/null +++ b/HuffmanEditor.csproj @@ -0,0 +1,20 @@ + + + + WinExe + net10.0-windows + enable + enable + true + true + true + true + true + true + false + partial + <_SuppressWpfTrimError>true + true + + + diff --git a/HuffmanEditor.slnx b/HuffmanEditor.slnx new file mode 100644 index 0000000..eb3074c --- /dev/null +++ b/HuffmanEditor.slnx @@ -0,0 +1,3 @@ + + + diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..6a21e67 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs new file mode 100644 index 0000000..59fbecd --- /dev/null +++ b/MainWindow.xaml.cs @@ -0,0 +1,90 @@ +using System.IO; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using Microsoft.Win32; + +namespace HuffmanEditor; +/// +/// Interaction logic for MainWindow.xaml +/// +public partial class MainWindow : Window +{ + private string filePath = string.Empty; + + public MainWindow() + { + InitializeComponent(); + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + File.WriteAllBytes("C:\\Users\\retrd\\Documents\\АК.rt0", Huffman.EncodeString(File.ReadAllText("C:\\Users\\retrd\\Documents\\АК.txt"))); + //label.Content = Huffman.DecodeString(File.ReadAllBytes("C:\\Users\\retrd\\Documents\\АК.rt0")); + } + + private void OpenFileClick(object sender, RoutedEventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog() { Filter = "Text files (*.txt;*.rt0)|*.txt;*.rt0", InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) }; + if (openFileDialog.ShowDialog()!.Value) + { + if (openFileDialog.FileName.EndsWith(".rt0")) + { + textType.Content = "Zipped text (rt0)"; + textBox.Text = Huffman.DecodeString(File.ReadAllBytes(openFileDialog.FileName)); + } + else + { + textType.Content = "Plain text (txt)"; + textBox.Text = File.ReadAllText(openFileDialog.FileName); + } + filePath = openFileDialog.FileName; + } + } + + private void SaveFileClick(object sender, RoutedEventArgs e) + { + if (filePath == string.Empty) SaveAsClick(sender, e); + else + { + File.WriteAllBytes(filePath, Huffman.EncodeString(textBox.Text)); + } + } + + private void SaveAsClick(object sender, RoutedEventArgs e) + { + SaveFileDialog saveFileDialog = new SaveFileDialog() { Filter = "Text file (*.txt)|*.txt|Zipped file (*.rt0)|*.rt0", InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) }; + if (saveFileDialog.ShowDialog()!.Value) + { + if (saveFileDialog.FileName.EndsWith(".rt0")) + { + textType.Content = "Zipped text (rt0)"; + File.WriteAllBytes(saveFileDialog.FileName, Huffman.EncodeString(textBox.Text)); + } + else + { + textType.Content = "Plain text (txt)"; + File.WriteAllText(saveFileDialog.FileName, textBox.Text); + } + filePath = saveFileDialog.FileName; + } + } + + private void textBox_TextChanged(object sender, TextChangedEventArgs e) + { + characterCount.Content = $"Characters: {textBox.Text.Length}"; + var lineIndex = textBox.GetLineIndexFromCharacterIndex(textBox.CaretIndex); + var columnIndex = textBox.CaretIndex - textBox.GetCharacterIndexFromLineIndex(lineIndex); + cursorAddress.Content = $"Ln {lineIndex}, Col {columnIndex}"; + } + + private void WordWrapClick(object sender, RoutedEventArgs e) + { + textBox.TextWrapping = (sender as CheckBox)!.IsChecked!.Value ? TextWrapping.Wrap : TextWrapping.NoWrap; + } + + private void StatusStripClick(object sender, RoutedEventArgs e) + { + statusStrip.Height = (sender as CheckBox)!.IsChecked!.Value ? new GridLength(25, GridUnitType.Pixel) : new GridLength(0, GridUnitType.Pixel); + } +} \ No newline at end of file