diff --git a/SkipList/SkipList.sln b/SkipList/SkipList.sln new file mode 100644 index 0000000..74f7220 --- /dev/null +++ b/SkipList/SkipList.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33403.182 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkipList", "SkipList\SkipList.csproj", "{4E18C267-E75F-45DB-B932-31593D09697E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsForSkipList", "TestsForSkipList\TestsForSkipList.csproj", "{6DA9CE59-BE2C-4893-8881-7D2BC3B808BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4E18C267-E75F-45DB-B932-31593D09697E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E18C267-E75F-45DB-B932-31593D09697E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E18C267-E75F-45DB-B932-31593D09697E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E18C267-E75F-45DB-B932-31593D09697E}.Release|Any CPU.Build.0 = Release|Any CPU + {6DA9CE59-BE2C-4893-8881-7D2BC3B808BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DA9CE59-BE2C-4893-8881-7D2BC3B808BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DA9CE59-BE2C-4893-8881-7D2BC3B808BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DA9CE59-BE2C-4893-8881-7D2BC3B808BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9FBA4961-6031-4E7B-87CC-E0F7812D9517} + EndGlobalSection +EndGlobal diff --git a/SkipList/SkipList/IncorrectIndexException.cs b/SkipList/SkipList/IncorrectIndexException.cs new file mode 100644 index 0000000..ac7df67 --- /dev/null +++ b/SkipList/SkipList/IncorrectIndexException.cs @@ -0,0 +1,3 @@ +namespace SkipList; + +internal class IncorrectIndexException : Exception {} \ No newline at end of file diff --git a/SkipList/SkipList/IncorrectMethodException.cs b/SkipList/SkipList/IncorrectMethodException.cs new file mode 100644 index 0000000..0d462e5 --- /dev/null +++ b/SkipList/SkipList/IncorrectMethodException.cs @@ -0,0 +1,3 @@ +namespace SkipList; + +internal class IncorrectMethodException : Exception {} \ No newline at end of file diff --git a/SkipList/SkipList/Program.cs b/SkipList/SkipList/Program.cs new file mode 100644 index 0000000..5bd3922 --- /dev/null +++ b/SkipList/SkipList/Program.cs @@ -0,0 +1,17 @@ +namespace SkipList; + +class Program +{ + public static void Main(string[] args) + { + var list = new SkipList(); + list.Add(1); + list.Add(2); + list.Add(3); + var listForCheck = new List { 1, 2, 3 }; + int i = 0; + foreach (var item in list) + { + } + } +} \ No newline at end of file diff --git a/SkipList/SkipList/SkipList.cs b/SkipList/SkipList/SkipList.cs new file mode 100644 index 0000000..e0380fa --- /dev/null +++ b/SkipList/SkipList/SkipList.cs @@ -0,0 +1,443 @@ +namespace SkipList; + +using System.Collections; + +public class SkipList : IList where T : IComparable +{ + private class List + { + public ElementList? element; + } + + private class MainList + { + public MainList? nextList; + public List? list; + public int size; + } + + private MainList? MajorList; + + public void Add(T item) + { + if (MajorList == null) + { + MajorList = new MainList(); + MajorList.list = new List(); + MajorList.list.element = new ElementList(-1, default(T), 0); + MajorList.list.element.next = new ElementList(0, item, 0); + MajorList.size = 1; + return; + } + + if (MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + + var walker = MajorList.list.element; + var stack = new Stack(); + + ++MajorList.size; + + while (walker != null) + { + if (walker.next == null) + { + if (walker.level == 0) + { + var copy = walker.next; + walker.next = new ElementList(walker.position + 1, item, 0); + walker.next.next = copy; + var randomNumber = new Random(); + int position = 1; + var previousLevelItem = walker.next; + while (randomNumber.Next(0, 2) == 0) + { + if (stack.Count > 0) + { + var walkerFromStack = stack.Pop(); + var copyForStack = walkerFromStack.next; + walkerFromStack.next = new ElementList(walker.position + 1, item, position); + walkerFromStack.next.next = copyForStack; + walkerFromStack.next.down = previousLevelItem; + previousLevelItem = walkerFromStack.next; + position++; + } + else + { + var newList = new List(); + newList.element = new ElementList(-1, default(T), MajorList.list.element.level + 1); + newList.element.next = new ElementList(walker.position + 1, item, position); + newList.element.next.down = previousLevelItem; + newList.element.down = MajorList.list.element; + previousLevelItem = newList.element.next; + var copyMajorList = MajorList; + MajorList = new MainList(); + MajorList.list = newList; + MajorList.nextList = copyMajorList; + MajorList.size = copyMajorList.size; + position++; + } + } + return; + } + else + { + stack.Push(walker); + walker = walker.down; + } + } + + if (walker != null && walker.next != null && item.CompareTo(walker.next.value) >= 0) + { + walker = walker.next; + } + else if (walker != null && walker.next != null && item.CompareTo(walker.next.value) < 0) + { + walker = walker.down; + } + } + } + + private T FindElementByIndex(int index) + { + if (MajorList == null || MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + + var walker = MajorList.list.element; + while (walker != null) + { + if (walker != null && walker.position == index) + { + return walker.value; + } + if (walker != null && walker.next != null && index >= walker.next.position) + { + walker = walker.next; + } + else if (walker != null && walker.next != null && index <= walker.next.position) + { + walker = walker.down; + } + } + throw new IncorrectIndexException(); + } + + public T this[int index] { get => FindElementByIndex(index); set => throw new NotImplementedException(); } + + public int Count => MajorList == null ? throw new NullReferenceException() : MajorList.size; + + public bool IsReadOnly => false; + + + public void Clear() + { + MajorList = null; + } + + public bool Contains(T item) + { + if (MajorList == null || MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + + var walker = MajorList.list.element; + while (walker != null) + { + if (walker.level == 0) + { + while (walker.next != null && item.CompareTo(walker.next.value) >= 0) + { + walker = walker.next; + } + return item.CompareTo(walker.value) == 0; + } + if (walker != null && walker.next != null && item.CompareTo(walker.next.value) >= 0) + { + walker = walker.next; + } + else + { + if (walker == null) + { + throw new NullReferenceException(); + } + walker = walker.down; + } + } + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (MajorList == null || MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + var walker = MajorList; + while (walker.nextList != null) + { + walker = walker.nextList; + } + + if (walker.list == null || walker.list.element == null) + { + throw new NullReferenceException(); + } + + + var walkerForArray = walker.list.element.next; + var listArray = new List(); + + if (walkerForArray == null) + { + throw new NullReferenceException(); + } + + while (walkerForArray != null) + { + listArray.Add(walkerForArray.value); + walkerForArray = walkerForArray.next; + } + + int sizeListArray = listArray.Count; + if (arrayIndex > array.Length) + { + throw new IndexOutOfRangeException(); + } + + int j = 0; + while (j < sizeListArray) + { + if (arrayIndex >= array.Length) + { + Array.Resize(ref array, array.Length + 1); + } + array[arrayIndex] = listArray[j]; + ++arrayIndex; + ++j; + } + + array = listArray.ToArray(); + } + + private class ListEnum : IEnumerator + { + private T[] listEnum; + + int position = -1; + + public ListEnum(T[] list) + { + listEnum = list; + } + + public object Current + { + get + { + try + { + return listEnum[position]; + } + catch (IndexOutOfRangeException) + { + throw new IncorrectIndexException(); + } + } + + } + + T IEnumerator.Current + { + get + { + return (T)Current; + } + } + + public void Dispose() + { + GC.SuppressFinalize(this); + } + + public bool MoveNext() + { + position++; + return (position < listEnum.Length); + } + + public void Reset() + { + position = -1; + } + } + + public IEnumerator GetEnumerator() + { + if (MajorList == null || MajorList.list == null) + { + throw new NullReferenceException(); + } + var walker = MajorList; + while (walker.nextList != null) + { + walker = walker.nextList; + } + var arrayValue = new T[MajorList.size]; + + if (walker.list == null || walker.list.element == null) + { + throw new NullReferenceException(); + } + + var walkerList = walker.list.element.next; + if (walkerList == null) + { + throw new NullReferenceException(); + } + int i = 0; + while (walkerList != null) + { + arrayValue[i] = walkerList.value; + ++i; + walkerList = walkerList.next; + } + return new ListEnum(arrayValue); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + + public int IndexOf(T item) + { + if (MajorList == null || MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + + var walker = MajorList.list.element; + while (walker != null) + { + if (walker.level == 0) + { + return item.CompareTo(walker.value) == 0 ? walker.position : -1; + } + if (walker != null && walker.next != null && item.CompareTo(walker.next.value) >= 0) + { + walker = walker.next; + } + else if (walker != null && walker.next != null && item.CompareTo(walker.next.value) < 0) + { + walker = walker.down; + } + } + return -1; + } + + public void Insert(int index, T item) + { + throw new IncorrectMethodException(); + } + + public bool RemoveByIndexOrItem(T item, int index, bool byIndex) + { + if (MajorList == null || MajorList.list == null || MajorList.list.element == null) + { + throw new NullReferenceException(); + } + var walker = MajorList.list.element; + var previousWalker = walker; + var mainLevel = MajorList; + while (walker != null) + { + if (!byIndex && item.CompareTo(walker.value) == 0 || byIndex && index == walker.position) + { + var copy = walker.next; + var copyCopy = copy; + + while (copyCopy != null) + { + --copyCopy.position; + copyCopy = copyCopy.next; + } + + if (previousWalker == null) + { + throw new NullReferenceException(); + } + previousWalker.next = copy; + walker = walker.down; + previousWalker = previousWalker.down; + while (previousWalker != null && previousWalker.next != null + && (item.CompareTo(previousWalker.next.value) != 0 && !byIndex + || byIndex && index != previousWalker.next.position)) + { + previousWalker = previousWalker.next; + } + if (walker == null) + { + return true; + } + } + else if (walker != null && walker.next != null && + (item.CompareTo(walker.next.value) >= 0 && !byIndex + || index >= walker.next.position && byIndex)) + { + previousWalker = walker; + walker = walker.next; + } + else + { + previousWalker = walker; + if (mainLevel == null) + { + throw new NullReferenceException(); + } + mainLevel = mainLevel.nextList; + if (walker == null) + { + throw new NullReferenceException(); + } + walker = walker.down; + } + } + return false; + } + + public bool Remove(T item) + { + return RemoveByIndexOrItem(item, 0, false); + } + + public void RemoveAt(int index) + { + RemoveByIndexOrItem(default(T), index, true); + } + + private class ElementList + { + public ElementList(int index, T item, int standart) + { + value = item; + position = index; + level = standart; + } + + public ElementList? next { get; set; } + + public ElementList? down { get; set; } + + public T? value { get; set; } + + public int position { get; set; } + + public int level { get; set; } + } +} diff --git a/SkipList/SkipList/SkipList.csproj b/SkipList/SkipList/SkipList.csproj new file mode 100644 index 0000000..f02677b --- /dev/null +++ b/SkipList/SkipList/SkipList.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + diff --git a/SkipList/TestsForSkipList/TestsForSkipList.cs b/SkipList/TestsForSkipList/TestsForSkipList.cs new file mode 100644 index 0000000..4dbc768 --- /dev/null +++ b/SkipList/TestsForSkipList/TestsForSkipList.cs @@ -0,0 +1,108 @@ +namespace TestsForSkipList; + +using SkipList; +using System; +using static System.Runtime.InteropServices.JavaScript.JSType; + +public class Tests +{ + SkipList list; + [SetUp] + public void Setup() + { + list = new SkipList(); + } + + [Test] + public void ListWithOmissionsShouldCorrectAdd() + { + list.Add(1); + list.Add(2); + list.Add(3); + var listForCheck = new List { 1, 2, 3 }; + int i = 0; + foreach (var item in list) + { + if (item != listForCheck[i]) + { + Assert.Fail(); + } + ++i; + } + Assert.Pass(); + } + + [Test] + public void AnUninitializedListShouldThrowExceptionWhenTryingToGetSize() + { + Assert.Throws(() => list.Count()); + } + + [Test] + public void NullListShouldThrowExceptionThenWhenTryingToCopyValuesToAnArray() + { + var arrayCopy = new int[5]; + Assert.Throws(() => list.CopyTo(arrayCopy, 0)); + } + + [Test] + public void NullListShouldThrowExceptionThenWhenTryingToRemoveValue() + { + Assert.Throws(() => list.Remove(3)); + } + + [Test] + public void NullListShouldThrowExceptionThenWhenTryingToRemoveByIndexValue() + { + Assert.Throws(() => list.RemoveAt(3)); + } + + [Test] + public void NullListShouldThrowExceptionThenWhenTryingToUseMethodContains() + { + Assert.Throws(() => list.Contains(3)); + } + + + [Test] + public void ListWithMethodContainsShouldWorkCorrectly() + { + list.Add(3); + list.Add(4); + list.Add(5); + Assert.True(list.Contains(4) && !list.Contains(7)); + } + + + [Test] + public void ListWithMethodCopyToShouldWorkCorrectly() + { + list.Add(3); + list.Add(4); + list.Add(5); + var arrayToCopy = new int[3]; + var arrayCheck = new int[3]{3, 4, 5 }; + list.CopyTo(arrayToCopy, 0); + Assert.True(arrayToCopy.SequenceEqual(arrayCheck)); + } + + [Test] + public void ListWithMethodRemoveShouldWorkCorrectly() + { + list.Add(3); + list.Add(4); + list.Add(5); + list.Remove(4); + Assert.True(!list.Contains(4)); + } + + [Test] + public void ListWithMethodRemoveAtShouldWorkCorrectly() + { + list.Add(3); + list.Add(4); + list.Add(5); + list.RemoveAt(1); + Assert.True(!list.Contains(4)); + } +} \ No newline at end of file diff --git a/SkipList/TestsForSkipList/TestsForSkipList.csproj b/SkipList/TestsForSkipList/TestsForSkipList.csproj new file mode 100644 index 0000000..6f7a828 --- /dev/null +++ b/SkipList/TestsForSkipList/TestsForSkipList.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + + + + + + + + diff --git a/SkipList/TestsForSkipList/Usings.cs b/SkipList/TestsForSkipList/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/SkipList/TestsForSkipList/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file