using System.Diagnostics.CodeAnalysis; using LibraryModel; using LibraryQueryContracts; namespace MergeSortQuery; public class MergeSortQuery : IParallelQuery { public Library? Library { get; set; } public int ThreadCount { get; set; } [SuppressMessage("ReSharper", "StringCompareToIsCultureSpecific")] private int CompareCopiesForSort(Copy x, Copy y) { var dateCompare = x.OnLoan!.DueDate.CompareTo(y.OnLoan!.DueDate); if (dateCompare != 0) return dateCompare; var lastNameCompare = x.OnLoan.Client.LastName.CompareTo(y.OnLoan.Client.LastName); if (lastNameCompare != 0) return lastNameCompare; var firstNameCompare = x.OnLoan.Client.FirstName.CompareTo(y.OnLoan.Client.FirstName); if (firstNameCompare != 0) return firstNameCompare; var shelfCompare = x.Book.Shelf.CompareTo(y.Book.Shelf); if (shelfCompare != 0) return shelfCompare; return x.Id.CompareTo(y.Id); } private List FilterAndSortRange(int from, int to) { var copies = new List(); for (int i = from; i <= to; i++) { var c = Library!.Copies[i]; if (c.State == CopyState.OnLoan && c.Book.Shelf[2] <= 'Q') copies.Add(c); } copies.Sort(CompareCopiesForSort); return copies; } private List Merge(List list1, List list2) { var result = new List(list1.Count + list2.Count); int i1 = 0, i2 = 0; while (i1 < list1.Count && i2 < list2.Count) { if (CompareCopiesForSort(list1[i1], list2[i2]) <= 0) { result.Add(list1[i1]); i1++; } else { result.Add(list2[i2]); i2++; } } for (; i1 < list1.Count; ++i1) result.Add(list1[i1]); for (; i2 < list2.Count; ++i2) result.Add(list1[i1]); return result; } private List ProcessRange(int from, int to, int availableThreads) { if (availableThreads == 1) return FilterAndSortRange(from, to); List? half1 = null; var threadForHalf1 = new Thread(() => half1 = ProcessRange(from, from + (to - from) / 2, availableThreads / 2)); threadForHalf1.Start(); List half2 = ProcessRange(from + (to - from) / 2 + 1, to, availableThreads - availableThreads / 2); threadForHalf1.Join(); return Merge(half1!, half2); } public List ExecuteQuery() { if (Library is null) throw new InvalidOperationException($"{nameof(Library)} property not set and default null value is not valid."); if (ThreadCount == 0) throw new InvalidOperationException($"{nameof(ThreadCount)} property not set and default value 0 is not valid."); return ProcessRange(0, Library.Copies.Count - 1, ThreadCount); } }