Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Build

on: [push]

jobs:
build-Windows:

runs-on: windows-latest

steps:
- uses: actions/checkout@v2
- name: Build
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.x'
- name: Restore
run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {nuget restore $file.FullName}
- name: Build
run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {dotnet build $file.FullName}
- name: Test
run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {dotnet test $file.FullName}

build-Ubuntu:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Build
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.x'
- name: Restore
run: for f in $(find . -name "*.sln"); do dotnet restore $f; done
- name: Build
run: for f in $(find . -name "*.sln"); do dotnet build $f; done
- name: Test
run: for f in $(find . -name "*.sln"); do dotnet test $f; done
31 changes: 31 additions & 0 deletions Homework3/LZW/LZW.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LZW", "LZW\LZW.csproj", "{5DAEADDF-7108-4028-90E1-5C05C2034360}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "LZWTest\Tests.csproj", "{455E760B-8592-4A15-BB37-A79DAE435B4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Release|Any CPU.Build.0 = Release|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7673691E-822C-4405-8F11-DE9C2BCBAE62}
EndGlobalSection
EndGlobal
162 changes: 162 additions & 0 deletions Homework3/LZW/LZW/LZW.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
namespace LZW;

using System;
using System.IO;
using Trie;

/// <summary>
/// A class representing the LZW algorithm
/// </summary>
public class LZW
{
/// <summary>
/// A function for determining the number of bytes needed to store a number
/// </summary>
private static int NumberOfBytes(int number)
{
if (number < 256)
{
return 1;
}
if (number < 65536)
{
return 2;
}
if (number < 16777216)
{
return 3;
}
return 4;
}

/// <summary>
/// Function for file compression
/// </summary>
/// <param name="pathToFileToCompress">The path to the file to compress</param>
public static void CompressFile(string pathToFileToCompress)
{
// Name of the compressed file
string fileName = $"{pathToFileToCompress}.zipped";

// Create file
using FileStream fs = new(fileName, FileMode.Create);

// Reading all bytes from a file
var stringToConvert = File.ReadAllBytes(pathToFileToCompress);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще, может быть плохой идеей читать файл в память целиком. Файлы бывают большие. Тем более что LZW может работать побайтово. Но это можно не править, для учебных целей вполне ок.


// Creating bor
Trie bor = new();

if (stringToConvert.Length == 0)
{
return;
}

for (int i = 0; i < stringToConvert.Length; i++)
{
// If the byte is contained in the bor
if (bor.Contains(stringToConvert[i]))
{
// Moving on to the next byte
bor.MoveIntoDesiredNode(stringToConvert[i]);
}

// Otherwise, we add it to the bor
else
{
// Taking the idex from the parent vertex and encode it
var bytes = BitConverter.GetBytes(bor.GetCode());

/* Cut off the extra bytes
The required number of bytes is selected according to the size of the bor
Since among the numbers that need to be encoded there may be a bor size (index of the maximum vertex),
the number of bytes to store depends on the size of the bor.
Even a number requires fewer bytes,
it is written in a large number of bytes so that it can be decoded later. */
Array.Resize(ref bytes, NumberOfBytes(bor.Size - 1));
bor.Add(stringToConvert[i]);
fs.Write(bytes);

// Going back to the last vertex
i--;
}
}

// The last bytes that are already in the dictionary and that are not written in the loop
var newbytes = BitConverter.GetBytes(bor.GetCode());
Array.Resize(ref newbytes, NumberOfBytes(bor.Size));
fs.Write(newbytes);
}

/// <summary>
/// Function for file decompression
/// </summary>
/// <param name="pathToFile">The path to the file to decompress</param>
public static void DecompressFile(string pathToFile)
{
var currentDirectoryName = Path.GetDirectoryName(pathToFile);
var fileName = Path.GetFileName(pathToFile);

// 7 = .zipped.length
fileName = $"{currentDirectoryName}//Decompressed{fileName[0..(fileName.Length - 7)]}";

// Create file
using FileStream fs = new(fileName, FileMode.Create);

// Reading all bytes from a file
byte[] stringToConvert = File.ReadAllBytes(pathToFile);

// Dictionary for decoding
var dictionary = new Dictionary<int, byte[]>();

for (int i = 0; i < 256; i++)
{
var byteArray = BitConverter.GetBytes(i);
dictionary.Add(i, byteArray[0..1]);
}

int rightBorder = 0;
int leftBorder = 0;
bool flag = false;

while (leftBorder < stringToConvert.Length)
{
// The right border of the current subarray
rightBorder = leftBorder + NumberOfBytes(dictionary.Count - 1) - 1;

// If the right border has gone beyond the edge, then we will make it the last byte
rightBorder = rightBorder > stringToConvert.Length - 1 ? stringToConvert.Length - 1 : rightBorder;

if (leftBorder > stringToConvert.Length - 1)
{
break;
}

// Converting bytes to int
var bytes = new byte[4];
Array.Copy(stringToConvert, leftBorder, bytes, 0, NumberOfBytes(dictionary.Count - 1));
leftBorder = rightBorder + 1;
int answer = BitConverter.ToInt32(bytes);

// If it is the first number
if (!flag)
{
flag = true;
}

// Otherwise, we concatenate the last element with the first byte of the current one
else
{
var newArray = new byte[dictionary[dictionary.Count - 1].Length + 1];
Array.Copy(dictionary[dictionary.Count - 1], newArray, dictionary[dictionary.Count - 1].Length);
newArray[newArray.Length - 1] = dictionary[answer][0];
// Changing the value in the dictionary to the value we need
dictionary[dictionary.Count - 1] = newArray;
}

// Adding a new element to the dictionary
dictionary.Add(dictionary.Count, dictionary[answer]);
fs.Write(dictionary[answer]);
}
}
}
10 changes: 10 additions & 0 deletions Homework3/LZW/LZW/LZW.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
28 changes: 28 additions & 0 deletions Homework3/LZW/LZW/Solution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
if (args.Length != 2)
{
Console.WriteLine("The first argument should be the path to the file, " +
"the second argument is the key -c if you need to compress the file, the key -u if you decompress");
return;
}

string pathToFile = args[0];

if (args[1] == "-c")
{
var file = new FileInfo(pathToFile);
long uncompressedFileSize = file.Length;
string fileName = $"{pathToFile}.zipped";
LZW.LZW.CompressFile(pathToFile);
file = new FileInfo(fileName);
long compressedFileSize = file.Length;
Console.WriteLine((float)(uncompressedFileSize) / (float)compressedFileSize);
return;
}
else if (args[1] == "-u")
{
LZW.LZW.DecompressFile(pathToFile);
}
else
{
Console.WriteLine("Invalid key entered");
}
93 changes: 93 additions & 0 deletions Homework3/LZW/LZW/Trie.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace Trie;

/// <summary>
/// A class representing the Trie
/// </summary>
public class Trie
{
/// <summary>
/// A class representing the Trie node
/// </summary>
private class Node
{
/// <summary>
/// Dictionary for storing characters for each node
/// </summary>
public Dictionary<byte, Node> Nodes = new();

/// <summary>
/// Code for each node
/// </summary>
public int Code { get; set;}
}

/// <summary>
/// Trie root
/// </summary>
private readonly Node root = new();

/// <summary>
/// Trie size
/// </summary>
public int Size { get; private set; }

private Node currentNode;

/// <summary>
/// Function for getting the code of the current element
/// </summary>
/// <returns>Code</returns>
public int GetCode() => currentNode.Code;

/// <summary>
/// Trie constructor
/// </summary>
public Trie()
{
root.Code = -1;

// Initialize the bor with numbers from 0 to 256
for (int i = 0; i < 256; i++)
{
Node node = new();
root.Nodes.Add((byte)i, node);
node.Code = Size;
Size++;
}

// The current node is the root
currentNode = root;
}

/// <summary>
/// Function to add a byte
/// </summary>
/// <param name="byteToAdd"> Byte to add </param>
public void Add(byte byteToAdd)
{
Node node = new();
currentNode?.Nodes.Add(byteToAdd, node);
currentNode = root;
node.Code = Size;
Size++;
}

/// <summary>
/// Is the byte contained in Current node
/// </summary>
/// <param name="element"> Element to search </param>
/// <returns> True if there is such a byte. False if there is no such byte </returns>
public bool Contains(byte byteToSearch) => currentNode.Nodes.ContainsKey(byteToSearch);

/// <summary>
/// Function for moving to another node
/// </summary>
/// <param name="byteT">the byte to be moved to</param>
public void MoveIntoDesiredNode(byte byteToBeMovedTo) => currentNode = currentNode.Nodes[byteToBeMovedTo];

/// <summary>
/// Function for moving to root
/// </summary>
public void MovedToRoot() => currentNode = root;
}

Binary file added Homework3/LZW/LZWTest/LZW.exe
Binary file not shown.
Loading