├── .gitignore ├── CourseWork.sln └── CourseWork ├── CourseWork.csproj ├── Program.cs └── Structures ├── Node.cs ├── Tree.ConcurrentPassThrough.cs ├── Tree.ParallelPassThrough.cs └── Tree.cs /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | ./CourseWork/bin/ 3 | ./CourseWork/obj/ 4 | *.json 5 | *.targets 6 | -------------------------------------------------------------------------------- /CourseWork.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CourseWork", "CourseWork\CourseWork.csproj", "{54FF4B0A-57BF-4C82-AA57-E1A37C0FBA1F}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {54FF4B0A-57BF-4C82-AA57-E1A37C0FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {54FF4B0A-57BF-4C82-AA57-E1A37C0FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {54FF4B0A-57BF-4C82-AA57-E1A37C0FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {54FF4B0A-57BF-4C82-AA57-E1A37C0FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /CourseWork/CourseWork.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CourseWork/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using CourseWork.Structures; 3 | 4 | namespace CourseWork; 5 | 6 | public static class Program 7 | { 8 | public static async Task Main() 9 | { 10 | int size = 10_000_000; 11 | List<(int, int)> src = new List<(int, int)>(size); 12 | for (int i = size-1; i >= 0; i--) 13 | { 14 | src.Add((i, size-i)); 15 | } 16 | 17 | Tree tree = new Tree(src); 18 | Console.WriteLine($"Tree building done ({size})"); 19 | Stopwatch sw = Stopwatch.StartNew(); 20 | var arr1 = await tree.ToSortedArrayConcurrent(); 21 | sw.Stop(); 22 | Console.WriteLine("Time: " + sw.ElapsedMilliseconds + "ms"); 23 | 24 | tree.ClearNodesUsages(); 25 | sw = Stopwatch.StartNew(); 26 | var arr2 = await tree.ToSortedArrayParallel(); 27 | sw.Stop(); 28 | Console.WriteLine("Time: " + sw.ElapsedMilliseconds + "ms"); 29 | 30 | Console.WriteLine("Correctness: "+Check(arr1, arr2)); 31 | Console.ReadLine(); 32 | } 33 | 34 | private static bool Check((int, int)[] arr1, (int, int)[] arr2, bool printArray = false) 35 | { 36 | for (int i = 0; i < arr1.Length; i++) 37 | { 38 | var el1 = arr1[i]; 39 | var el2 = arr2[i]; 40 | if (printArray) Console.WriteLine(el1); 41 | if (el1 != el2) 42 | return false; 43 | } 44 | 45 | return true; 46 | } 47 | } -------------------------------------------------------------------------------- /CourseWork/Structures/Node.cs: -------------------------------------------------------------------------------- 1 | namespace CourseWork.Structures; 2 | 3 | public class Node where TKey: IEquatable, IComparable 4 | { 5 | public TKey Key { get; } 6 | public TVal Value { get; set; } 7 | 8 | public Node? FirstChild { get; set; } 9 | public Node? SecondChild { get; set; } 10 | public int SubtreeSize { get; private set; } 11 | public Node? Parent { get; set; } 12 | public bool IsPassed { get; protected set; } 13 | 14 | public (TKey, TVal)? Pass() 15 | { 16 | lock (this) 17 | { 18 | if (IsPassed) 19 | return null; 20 | IsPassed = true; 21 | } 22 | return (Key, Value); 23 | } 24 | 25 | public Node(Node? parent, TKey key, TVal value) 26 | { 27 | Key = key; 28 | Value = value; 29 | Parent = parent; 30 | IsPassed = false; 31 | SubtreeSize = 1; 32 | } 33 | 34 | public void Add((TKey Key, TVal Value) child) 35 | { 36 | SubtreeSize++; 37 | if (child.Key.CompareTo(this.Key) == 1) 38 | { 39 | if (SecondChild is null) 40 | SecondChild = new Node(this, child.Key, child.Value); 41 | else SecondChild.Add(child); 42 | } 43 | else 44 | { 45 | if (FirstChild is null) 46 | FirstChild = new Node(this, child.Key, child.Value); 47 | else FirstChild.Add(child); 48 | } 49 | } 50 | 51 | public void Clear() 52 | { 53 | IsPassed = false; 54 | if (FirstChild is not null) 55 | FirstChild.Clear(); 56 | if (SecondChild is not null) 57 | SecondChild.Clear(); 58 | } 59 | } -------------------------------------------------------------------------------- /CourseWork/Structures/Tree.ConcurrentPassThrough.cs: -------------------------------------------------------------------------------- 1 | namespace CourseWork.Structures; 2 | 3 | public partial class Tree 4 | { 5 | public async Task<(TKey, TValue)[]> ToSortedArrayConcurrent(Node? subtreeRoot = null) 6 | { 7 | Node workingTreeRoot = subtreeRoot ?? Root!; 8 | (TKey, TValue)[] array = new (TKey, TValue)[workingTreeRoot.SubtreeSize]; 9 | if (Count == 0) 10 | return array; 11 | 12 | int ctr = 0; 13 | Node? current = workingTreeRoot; 14 | while (current != workingTreeRoot!.Parent) 15 | { 16 | if (current!.FirstChild is not null && !current.FirstChild.IsPassed) 17 | { 18 | current = current.FirstChild; 19 | continue; 20 | } 21 | 22 | if (current.SecondChild is null || !current.SecondChild.IsPassed) 23 | array[ctr++] = current.Pass()!.Value; 24 | 25 | if (current.SecondChild is not null && !current.SecondChild.IsPassed) 26 | { 27 | current = current.SecondChild; 28 | continue; 29 | } 30 | 31 | current = current.Parent; 32 | } 33 | 34 | return array; 35 | } 36 | } -------------------------------------------------------------------------------- /CourseWork/Structures/Tree.ParallelPassThrough.cs: -------------------------------------------------------------------------------- 1 | namespace CourseWork.Structures; 2 | 3 | public partial class Tree 4 | { 5 | private (TKey, TValue)[] finalResult; 6 | public async Task<(TKey, TValue)[]> ToSortedArrayParallel(Node? subtreeRoot = null, int threads = 8) 7 | { 8 | Node workingTreeRoot = subtreeRoot! ?? Root!; 9 | if (Count == 0) 10 | { 11 | return Array.Empty<(TKey, TValue)>(); 12 | } 13 | 14 | finalResult = new (TKey, TValue)[workingTreeRoot.SubtreeSize]; 15 | 16 | await ParallelPassThrough(workingTreeRoot, threads, 0); 17 | return finalResult; 18 | } 19 | 20 | private async Task ParallelPassThrough(Node subtreeRoot, int threads, int arrayStartingIndex) 21 | { 22 | if (threads < 2) 23 | { 24 | (await ToSortedArrayConcurrent(subtreeRoot)).CopyTo(finalResult, arrayStartingIndex); 25 | return; 26 | } 27 | 28 | if (HasTwoChildren(subtreeRoot)) 29 | { 30 | Task task1 = Task.Run(async () => 31 | await ParallelPassThrough(subtreeRoot.FirstChild!, threads / 2, arrayStartingIndex)); 32 | Task task2 = Task.Run(async () => await ParallelPassThrough(subtreeRoot.SecondChild!, 33 | threads - threads / 2, arrayStartingIndex + subtreeRoot.FirstChild!.SubtreeSize + 1)); 34 | 35 | finalResult[arrayStartingIndex + subtreeRoot.FirstChild!.SubtreeSize] = 36 | (subtreeRoot.Key, subtreeRoot.Value); 37 | await task1; 38 | await task2; 39 | return; 40 | } 41 | 42 | finalResult[arrayStartingIndex] = (subtreeRoot.Key, subtreeRoot.Value); 43 | if (HasOneChild(subtreeRoot)) 44 | { 45 | if (subtreeRoot.FirstChild is not null) 46 | { 47 | Task task1 =Task.Run(async () => await ParallelPassThrough(subtreeRoot.FirstChild!, threads, arrayStartingIndex)); 48 | 49 | finalResult[arrayStartingIndex + subtreeRoot.FirstChild!.SubtreeSize] = 50 | (subtreeRoot.Key, subtreeRoot.Value); 51 | await task1; 52 | return; 53 | } 54 | 55 | await ParallelPassThrough(subtreeRoot.SecondChild!, threads, arrayStartingIndex+1); 56 | } 57 | } 58 | 59 | private bool HasTwoChildren(Node node) 60 | { 61 | return (node.FirstChild is not null && node.SecondChild is not null); 62 | } 63 | 64 | private bool HasOneChild(Node node) 65 | { 66 | return node.SubtreeSize > 1; 67 | } 68 | } -------------------------------------------------------------------------------- /CourseWork/Structures/Tree.cs: -------------------------------------------------------------------------------- 1 | namespace CourseWork.Structures; 2 | 3 | public partial class Tree where TKey: IComparable, IEquatable 4 | { 5 | public Node? Root { get; private set; } 6 | public int Count { get; private set; } 7 | public Tree() 8 | { 9 | Count = 0; 10 | } 11 | 12 | public Tree(IEnumerable<(TKey, TValue)> source) 13 | { 14 | var sourceList = source.ToList(); 15 | sourceList.Sort((p1, p2) => p1.Item1.CompareTo(p2.Item1)); 16 | Count = 0; 17 | 18 | foreach (int index in GetBalancingIndexes(sourceList.Count)) 19 | { 20 | Add(sourceList[index]); 21 | } 22 | } 23 | 24 | public void Add((TKey, TValue) element) 25 | { 26 | Count++; 27 | if (Root is null) 28 | { 29 | Root = new Node(null, element.Item1, element.Item2); 30 | } 31 | else 32 | { 33 | Root.Add(element); 34 | } 35 | } 36 | 37 | public void Add(TKey key, TValue value) => Add((key, value)); 38 | 39 | private static IEnumerable GetBalancingIndexes(int elementsNumber) 40 | { 41 | if (elementsNumber == 0) 42 | yield break; 43 | int ownInd = elementsNumber / 2; 44 | yield return ownInd; 45 | if (elementsNumber == 1) 46 | yield break; 47 | foreach (var ind in GetBalancingIndexes(ownInd)) 48 | { 49 | yield return ind; 50 | } 51 | 52 | foreach (var ind in GetBalancingIndexes(elementsNumber - ownInd - 1)) 53 | { 54 | yield return ownInd + 1 + ind; 55 | } 56 | } 57 | 58 | public void ClearNodesUsages() 59 | { 60 | Root?.Clear(); 61 | } 62 | } --------------------------------------------------------------------------------