From 31e38a6273a6e3bd034240b4650f4f6f777628d4 Mon Sep 17 00:00:00 2001 From: MinyazevR Date: Mon, 9 May 2022 22:26:08 +0300 Subject: [PATCH 1/6] The SkipList class is written, which implements the IList interface. Comments have been added and tests have been written --- SkipList/SkipList/SkipList.sln | 31 ++ SkipList/SkipList/SkipList/SkipList.cs | 340 ++++++++++++++++++ SkipList/SkipList/SkipList/SkipList.csproj | 10 + .../SkipList/SkipListTest/SkipListTest.cs | 151 ++++++++ .../SkipList/SkipListTest/SkipListTest.csproj | 21 ++ 5 files changed, 553 insertions(+) create mode 100644 SkipList/SkipList/SkipList.sln create mode 100644 SkipList/SkipList/SkipList/SkipList.cs create mode 100644 SkipList/SkipList/SkipList/SkipList.csproj create mode 100644 SkipList/SkipList/SkipListTest/SkipListTest.cs create mode 100644 SkipList/SkipList/SkipListTest/SkipListTest.csproj diff --git a/SkipList/SkipList/SkipList.sln b/SkipList/SkipList/SkipList.sln new file mode 100644 index 0000000..db4ec90 --- /dev/null +++ b/SkipList/SkipList/SkipList.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32414.318 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkipList", "SkipList\SkipList.csproj", "{8C8B09D6-C645-4684-A359-F273CD5AC177}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkipListTest", "SkipListTest\SkipListTest.csproj", "{AA50A2D4-DC5F-42A6-B5D5-5458A6FC0EE2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8C8B09D6-C645-4684-A359-F273CD5AC177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C8B09D6-C645-4684-A359-F273CD5AC177}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C8B09D6-C645-4684-A359-F273CD5AC177}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C8B09D6-C645-4684-A359-F273CD5AC177}.Release|Any CPU.Build.0 = Release|Any CPU + {AA50A2D4-DC5F-42A6-B5D5-5458A6FC0EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA50A2D4-DC5F-42A6-B5D5-5458A6FC0EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA50A2D4-DC5F-42A6-B5D5-5458A6FC0EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA50A2D4-DC5F-42A6-B5D5-5458A6FC0EE2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9ED247B2-F2E3-450A-88CC-8E1DD175F6D2} + EndGlobalSection +EndGlobal diff --git a/SkipList/SkipList/SkipList/SkipList.cs b/SkipList/SkipList/SkipList/SkipList.cs new file mode 100644 index 0000000..57a035e --- /dev/null +++ b/SkipList/SkipList/SkipList/SkipList.cs @@ -0,0 +1,340 @@ +namespace SkipList; + +using System.Collections; + +// A class representing a skip list +public class SkipList : IList where T : IComparable +{ + + /// + /// Class for storing list items + /// + private class ListElement + { + public T? Value { get; set; } + public ListElement? Next { get; set; } + public ListElement? Down { get; set; } + } + + public SkipList() + { + heads.Add(new()); + } + + private readonly List heads = new(); + + public bool IsReadOnly { get; } + + public bool IsFixedSize { get; } + + public int Count { get; private set; } + + public T this[int index] + { + get + { + if (index < 0 || index >= Count) + { + throw new ArgumentOutOfRangeException(); + } + + int counter = 0; + ListElement element = heads[0].Next!; + while (counter != index) + { + element = element.Next!; + counter++; + } + + return element.Value!; + } + set => throw new NotSupportedException(); + } + + // A function that returns true with a probability of 0.5 + // (maybe not with a probability of 0.5, + // but in a naive solution we will assume that with a probability of 0.5) + private static bool SuccessfulOutcome(Random random) + { + if (random.Next(100) < 50) + { + return true; + } + + return false; + } + + // Function for building a new level based on the old one + private void BuildLevel(ListElement newHead) + { + newHead.Down = heads[^1]; + + ListElement? newNode = heads[^1].Next?.Next; + + ListElement currentNode = newHead; + + while (newNode != null && newNode.Next != null) + { + currentNode.Next = CreateNode(newNode.Value, newNode, currentNode.Next); + newNode = newNode.Next.Next; + } + } + + // Function for creating a new node + private static ListElement CreateNode(T? item, ListElement? downNode, ListElement? nextNode) + { + ListElement node = new(); + node.Next = nextNode; + node.Value = item; + node.Down = downNode; + return node; + } + + public void Add(T item) + { + if (heads[^1].Next != null) + { + UpdateLevel(); + } + else if (heads[^1].Next == null && Count > 1) + { + heads.RemoveAt(heads.Count - 1); + } + + Count++; + RecursiveAdd(item, new(), heads[^1]); + } + + // Function for adding an item to a list + private static ListElement? RecursiveAdd(T item, Random random, ListElement element) + { + while (element.Next != null && element.Next.Value!.CompareTo(item) < 0) + { + element = element.Next; + } + + ListElement? downNode; + + if (element.Down == null) + { + downNode = null; + } + + else + { + downNode = RecursiveAdd(item, random, element.Down); + } + + if (downNode != null || element.Down == null) + { + element.Next = CreateNode(item, downNode, element.Next); + if (SuccessfulOutcome(random)) + { + return element.Next; + } + } + + return null; + } + + public bool Contains(T item) + { + if (heads.Count == 0) + { + return false; + } + + return RecursiveFind(heads[^1], item).Value!.CompareTo(item) == 0; + } + + // Function for finding an item in a list + private static ListElement RecursiveFind(ListElement node, T item) + { + while (node.Next != null && node.Next.Value!.CompareTo(item) < 0) + { + node = node.Next; + } + + if (node.Next != null && node.Next.Value!.CompareTo(item) == 0) + { + return node.Next; + } + + if (node.Down == null) + { + return node; + } + + return RecursiveFind(node.Down, item); + } + + + public int IndexOf(T item) + { + int counter = 0; + ListElement? element = heads[0].Next; + while (element != null && element.Value!.CompareTo(item) != 0) + { + element = element.Next; + counter++; + } + + return element == null ? -1 : counter; + } + + public void Insert(int index, T item) => throw new NotSupportedException(); + + public bool Remove(T item) + { + bool answer = RecursiveDelete(heads[^1], item); + if (heads[^1].Next == null) + { + heads.RemoveAt(heads.Count - 1); + } + + return answer; + } + + // Function for deleting an element + private bool RecursiveDelete(ListElement element, T item) + { + while (element.Next != null && element.Next.Value!.CompareTo(item) < 0) + { + element = element.Next; + } + if (element.Down != null) + { + return RecursiveDelete(element.Down, item); + } + + + if (element.Next != null && element.Next.Value!.CompareTo(item) == 0) + { + element.Next = element.Next.Next; + return true; + } + + return false; + } + + public void RemoveAt(int index) + { + if (index < 0 || index >= Count) + { + throw new ArgumentOutOfRangeException(); + } + + ListElement element = heads[0].Next!; + int counter = 0; + + while (counter < index) + { + counter++; + element = element.Next!; + } + + Remove(element.Value!); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException(); + } + + if (arrayIndex < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (array.Length - arrayIndex < Count) + { + throw new ArgumentException(); + } + + ListElement element = heads[0]; + for (int counter = arrayIndex; counter < array.Length; counter++) + { + array[counter] = element.Value!; + element = element.Next!; + } + } + + public void UpdateLevel() + { + if (heads.Count <= (int)Math.Floor(Math.Log2(Count))) + { + var newLevel = new ListElement(); + BuildLevel(newLevel); + heads.Add(newLevel); + } + } + + public void Clear() + { + heads.Clear(); + Count = 0; + } + + // Implementation for the GetEnumerator method. + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); + + // Implementation for the GetEnumerator method. + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); + + public SkipListEnum GetEnumerator() + { + return new SkipListEnum(this); + } + + /// + /// Class for implementing the IEnumerator interface + /// + public class SkipListEnum : IEnumerator + { + private readonly SkipList list; + private ListElement? head; + + public SkipListEnum(SkipList inputList) + { + list = inputList; + head = list.heads[0]; + } + + public bool MoveNext() + { + if (head != null && head.Next != null) + { + head = head.Next; + return true; + } + else + { + head = null; + return false; + } + } + + public void Reset() => head = list.heads[0]; + + object IEnumerator.Current { get => Current; } + + public T Current + { + get + { + if (head != null) + { + return head.Value!; + } + else + { + return default!; + } + } + } + + public void Dispose() { } + } +} \ No newline at end of file diff --git a/SkipList/SkipList/SkipList/SkipList.csproj b/SkipList/SkipList/SkipList/SkipList.csproj new file mode 100644 index 0000000..16e62dd --- /dev/null +++ b/SkipList/SkipList/SkipList/SkipList.csproj @@ -0,0 +1,10 @@ + + + + Library + net6.0 + enable + enable + + + diff --git a/SkipList/SkipList/SkipListTest/SkipListTest.cs b/SkipList/SkipList/SkipListTest/SkipListTest.cs new file mode 100644 index 0000000..a9b77be --- /dev/null +++ b/SkipList/SkipList/SkipListTest/SkipListTest.cs @@ -0,0 +1,151 @@ +namespace SkipListTest; + +using NUnit.Framework; +using SkipList; +using System; +using System.Collections.Generic; + +public class Tests +{ + IList list = new SkipList(); + + [SetUp] + public void Setup() + { + list = new SkipList(); + } + + private static IEnumerable TestRemoveCaseData() => new TestCaseData[] + { + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.Remove(2), false), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 12, (IList list) => list.Remove(12), false), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123, 2}, 2,(IList list) => list.Remove(2), true), + }; + + private static IEnumerable TestRemoveFunctionValueCaseData() => new TestCaseData[] + { + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, (IList list) => list.Remove(2), true), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, (IList list) => list.Remove(12), true), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123, 2}, (IList list) => list.Remove(16), false), + new TestCaseData(new SkipList(){}, (IList list) => list.Remove(0), false), + }; + + private static IEnumerable TestIndexOfCaseData() => new TestCaseData[] + { + new TestCaseData(4, new SkipList(){2, 1, 14, 12, -123}.IndexOf(14)), + new TestCaseData(1, new SkipList(){2, 1, 14, 12, -123}.IndexOf(1)), + new TestCaseData(-1, new SkipList(){2, 1, 14, 12, -123}.IndexOf(-50)), + new TestCaseData(-1, new SkipList(){}.IndexOf(0)), + }; + + private static IEnumerable TestRemoveAtCaseData() => new TestCaseData[] + { + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.RemoveAt(2), false), + new TestCaseData(new SkipList(){5, 1, 18, 12, -123, 5}, 5, (IList list) => list.RemoveAt(2), true), + }; + + + [Test] + public void ShouldForeachWork() + { + list.Add(12); + list.Add(0); + list.Add(-1); + list.Add(123); + list.Add(5); + list.Add(124); + list.Add(70); + list.Add(85); + list.Add(19); + list.Add(42); + list.Add(53); + list.Add(68); + list.Add(14); + list.Add(80); + + int counter = 0; + foreach (int item in list) + { + Assert.AreEqual(list[counter], item); + counter++; + } + } + + [Test] + public void ShouldExpectedCountEqualZeroWhenDoNothing() + { + Assert.AreEqual(0, list.Count); + } + + [Test] + public void ShouldPropertyWorkCorrectly() + { + list.Add(12); + list.Add(0); + list.Add(-1); + list.Add(144); + Assert.AreEqual(-1, list[0]); + Assert.AreEqual(0, list[1]); + Assert.AreEqual(12, list[2]); + Assert.AreEqual(144, list[3]); + } + + [Test] + public void ShouldExpected0WhenCountAfterListClear() + { + list.Add(12); + list.Add(0); + list.Add(-1); + list.Add(144); + list.Clear(); + Assert.AreEqual(0, list.Count); + } + + [Test] + public void ShouldExpectedFalseWhenContainsForNonExistingElement() + { + Assert.False(list.Contains(12)); + } + + [TestCaseSource(nameof(TestIndexOfCaseData))] + public void ShouldExpectedMinus1OrRealIndexWhenContainsForNonExistingElement(int expectedValue, int value) + { + Assert.AreEqual(expectedValue, value); + } + + [TestCaseSource(nameof(TestRemoveCaseData))] + public void ShouldExpectedTrueOrFalseWhenContainsAfterRemove(SkipList list, int expectedValue, Func, bool> func, bool answer) + { + func(list); + Assert.AreEqual(answer, func(list)); + } + + [TestCaseSource(nameof(TestRemoveFunctionValueCaseData))] + public void ShouldExpectedTrueOrFalseWhenRemove(SkipList list, Func, bool> func, bool answer) + { + Assert.AreEqual(answer, func(list)); + } + + + [TestCaseSource(nameof(TestRemoveAtCaseData))] + public void ShouldExpectedTrueOrFalseWhenContainsAfterRemoveAt(SkipList list, int expectedValue, Action> func, bool answer) + { + func(list); + Assert.AreEqual(answer, list.Contains(expectedValue)); + } + + public void ShouldExpectedNextElementWhenRemoveElement() + { + list.Add(12); + list.Add(1); + list.Add(18); + list.Add(-123); + list.Remove(12); + Assert.AreEqual(18,list[2]); + } + + public void ShouldArgumentOutOfRangeExceptionWhenIndexIsNotValidIndex() + { + Assert.Throws(() => list.RemoveAt(13)); + } +} diff --git a/SkipList/SkipList/SkipListTest/SkipListTest.csproj b/SkipList/SkipList/SkipListTest/SkipListTest.csproj new file mode 100644 index 0000000..1bf3e5c --- /dev/null +++ b/SkipList/SkipList/SkipListTest/SkipListTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + From 4c9a141bd488153d724e98a8299f064b14aa6271 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 25 May 2022 02:01:18 +0300 Subject: [PATCH 2/6] fixed tests --- SkipList/SkipList/SkipListTest/SkipListTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SkipList/SkipList/SkipListTest/SkipListTest.cs b/SkipList/SkipList/SkipListTest/SkipListTest.cs index a9b77be..ccad5ff 100644 --- a/SkipList/SkipList/SkipListTest/SkipListTest.cs +++ b/SkipList/SkipList/SkipListTest/SkipListTest.cs @@ -38,9 +38,9 @@ public void Setup() new TestCaseData(-1, new SkipList(){}.IndexOf(0)), }; - private static IEnumerable TestRemoveAtCaseData() => new TestCaseData[] + private static IEnumerable TestRemoveAtFunctionValueCaseData() => new TestCaseData[] { - new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.RemoveAt(2), false), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.RemoveAt(2), true), new TestCaseData(new SkipList(){5, 1, 18, 12, -123, 5}, 5, (IList list) => list.RemoveAt(2), true), }; @@ -117,7 +117,7 @@ public void ShouldExpectedMinus1OrRealIndexWhenContainsForNonExistingElement(int public void ShouldExpectedTrueOrFalseWhenContainsAfterRemove(SkipList list, int expectedValue, Func, bool> func, bool answer) { func(list); - Assert.AreEqual(answer, func(list)); + Assert.AreEqual(answer, list.Contains(expectedValue)); } [TestCaseSource(nameof(TestRemoveFunctionValueCaseData))] @@ -127,7 +127,7 @@ public void ShouldExpectedTrueOrFalseWhenRemove(SkipList list, Func list, int expectedValue, Action> func, bool answer) { func(list); From 1cdbdbfcdd31321e69beaf513873bab027189dac Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Jun 2022 14:18:36 +0300 Subject: [PATCH 3/6] fixed tests --- SkipList/SkipList/SkipListTest/SkipListTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SkipList/SkipList/SkipListTest/SkipListTest.cs b/SkipList/SkipList/SkipListTest/SkipListTest.cs index ccad5ff..734808b 100644 --- a/SkipList/SkipList/SkipListTest/SkipListTest.cs +++ b/SkipList/SkipList/SkipListTest/SkipListTest.cs @@ -40,7 +40,7 @@ public void Setup() private static IEnumerable TestRemoveAtFunctionValueCaseData() => new TestCaseData[] { - new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.RemoveAt(2), true), + new TestCaseData(new SkipList(){2, 1, 18, 12, -123}, 2, (IList list) => list.RemoveAt(2), false), new TestCaseData(new SkipList(){5, 1, 18, 12, -123, 5}, 5, (IList list) => list.RemoveAt(2), true), }; From ee0e2dae7c5a4e36dd187bd199f2e66cfe9a8d51 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jun 2022 00:38:38 +0300 Subject: [PATCH 4/6] rename yml --- .github/workflows/{dotnet.yml => SkipList.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{dotnet.yml => SkipList.yml} (100%) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/SkipList.yml similarity index 100% rename from .github/workflows/dotnet.yml rename to .github/workflows/SkipList.yml From 908e567b42476fe781d1485b220ed5c837a4345a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jun 2022 01:26:21 +0300 Subject: [PATCH 5/6] rename yml --- .github/workflows/{SkipList.yml => dotnet.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{SkipList.yml => dotnet.yml} (100%) diff --git a/.github/workflows/SkipList.yml b/.github/workflows/dotnet.yml similarity index 100% rename from .github/workflows/SkipList.yml rename to .github/workflows/dotnet.yml From 93d61d9e46d2b775107001eb46e8548f79cc9bc3 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 2 Jun 2022 13:24:21 +0300 Subject: [PATCH 6/6] fixed test --- SkipList/SkipList/SkipList/SkipList.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/SkipList/SkipList/SkipList/SkipList.cs b/SkipList/SkipList/SkipList/SkipList.cs index 57a035e..5f66522 100644 --- a/SkipList/SkipList/SkipList/SkipList.cs +++ b/SkipList/SkipList/SkipList/SkipList.cs @@ -186,35 +186,34 @@ public int IndexOf(T item) public bool Remove(T item) { - bool answer = RecursiveDelete(heads[^1], item); + bool value = false; + RecursiveDelete(heads[^1], item, ref value); if (heads[^1].Next == null) { heads.RemoveAt(heads.Count - 1); } - return answer; + return value; } // Function for deleting an element - private bool RecursiveDelete(ListElement element, T item) + private void RecursiveDelete(ListElement element, T item, ref bool value) { while (element.Next != null && element.Next.Value!.CompareTo(item) < 0) { element = element.Next; } + if (element.Down != null) { - return RecursiveDelete(element.Down, item); + RecursiveDelete(element.Down, item, ref value); } - if (element.Next != null && element.Next.Value!.CompareTo(item) == 0) { element.Next = element.Next.Next; - return true; + value = true; } - - return false; } public void RemoveAt(int index)