From 4684330d7e3aa44c3de1dab9e5eb31f98ba2b8ca Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sat, 21 Oct 2023 14:07:23 +0200 Subject: [PATCH] Initial version extracted from Recyclable.Collections --- LICENSE.md | 10 + README.md | 23 +- .../BinarySearch_BestAndWorstCases.cs | 67 ++ .../ConvertAll.cs | 50 + .../Exists_BestAndWorstCases.cs | 67 ++ .../FindAll_BestAndWorstCases.cs | 82 ++ .../FindLastIndex_BestAndWorstCases.cs | 55 + .../FindLast_BestAndWorstCases.cs | 55 + .../Find_BestAndWorstCases.cs | 55 + .../Program.cs | 2 + ...tions.Compatibility.List.Benchmarks.csproj | 15 + .../GlobalUsings.cs | 1 + ...ollections.Compatibility.List.Tests.csproj | 28 + .../RecyclableListCompatibilityListTests.cs | 953 ++++++++++++++++++ Recyclable.Collections.Compatibility.List.sln | 37 + ...able.Collections.Compatibility.List.csproj | 37 + .../RecyclableList.Compatibility.List.Find.cs | 271 +++++ ...cyclableList.Compatibility.List.IndexOf.cs | 25 + ...ableList.Compatibility.List.InsertRange.cs | 357 +++++++ .../RecyclableList.Compatibility.List.cs | 213 ++++ .../buy-me-a-coffee.svg | 1 + logo.png | Bin 0 -> 3244 bytes 22 files changed, 2403 insertions(+), 1 deletion(-) create mode 100644 LICENSE.md create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs create mode 100644 Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj create mode 100644 Recyclable.Collections.Compatibility.List.Tests/GlobalUsings.cs create mode 100644 Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj create mode 100644 Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs create mode 100644 Recyclable.Collections.Compatibility.List.sln create mode 100644 Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj create mode 100644 Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs create mode 100644 Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.IndexOf.cs create mode 100644 Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs create mode 100644 Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs create mode 100644 Recyclable.Collections.Compatibility.List/buy-me-a-coffee.svg create mode 100644 logo.png diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0da7cad --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,10 @@ +# License +The project & source code is free for educational & personal use. **Any kind** of commercial use requires a donation, as prescribed below. + +# Donation requirements for a life-time, commercial use +* Commercial license is paid one time. +* It can be obtained by donating $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package. +* Your license stays with you, if your developer moves somewhere else. + +# Link for donations +[![Donate](buy-me-a-coffee.svg)](https://www.buymeacoffee.com/mlemanczyk) \ No newline at end of file diff --git a/README.md b/README.md index f96ac62..99d0061 100644 --- a/README.md +++ b/README.md @@ -1 +1,22 @@ -# Recyclable.Collections.Compatibility.List \ No newline at end of file +# Recyclable.Collections.Compatibility.List# Recyclable.Collections +`Recyclable.Collections.Compatibility.List` project is an open source add-on for [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) providing `List` compatibility features to the recyclable collections framework. + +## Included compatibility packs +* `RecyclableList` + +# Milestones +1. 👉 Implement compatibility pack + 1. ✅ `RecyclableList` + 1. 👉 `RecyclableLongList` + 1. 🅿️ `RecyclableQueue` + 1. 🅿️ `RecyclableSortedList` + 1. 🅿️ `RecyclableStack` + 1. 🅿️ `RecyclableUnorderedList` +1. 🅿️ Review performance +1. 🅿️ Optimize performance + +# Characteristics of the classes +The package + +## Common +* All classes will internally or externally utilize [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections) to benefit from recyclable memory pools. diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs new file mode 100644 index 0000000..a664683 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/BinarySearch_BestAndWorstCases.cs @@ -0,0 +1,67 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public class BinarySearchBenchmarks : CollectionsBenchmarksBase + { + public void Array_BinarySearch_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.BinarySearch(list, data[i])); + DoNothing.With(Array.BinarySearch(list, data[^(i + 1)])); + } + } + + public void List_BinarySearch_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(data[i])); + DoNothing.With(list.BinarySearch(data[^(i + 1)])); + } + } + + public void PooledList_BinarySearch_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsPooledList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(data[i])); + DoNothing.With(list.BinarySearch(data[^(i + 1)])); + } + } + + public void RecyclableList_BinarySearch_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.BinarySearch(data[i])); + DoNothing.With(list.BinarySearch(data[^(i + 1)])); + } + } + + // public void RecyclableLongList_BinarySearch_BestAndWorstCases() + // { + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // DoNothing(list.BinarySearch(data[i])); + // DoNothing(list.BinarySearch(data[^(i + 1)])); + // } + // } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs new file mode 100644 index 0000000..b9e9a60 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/ConvertAll.cs @@ -0,0 +1,50 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public class ConvertAllBenchmarks : CollectionsBenchmarksBase + { + public void Array_ConvertAll() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + DoNothing.With(Array.ConvertAll(list, static (item) => (long)item)); + } + + public void List_ConvertAll() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + DoNothing.With(list.ConvertAll(static (item) => (long)item)); + } + + public void PooledList_ConvertAll() + { + var data = TestObjects; + var list = TestObjectsAsPooledList; + var dataCount = TestObjectCountForSlowMethods; + using var converted = list.ConvertAll(static (item) => (long)item); + DoNothing.With(converted); + } + + public void RecyclableList_ConvertAll() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + using RecyclableList converted = list.ConvertAll(static (item) => (long)item); + DoNothing.With(converted); + } + + // public void RecyclableLongList_ConvertAll() + // { + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // using RecyclableList converted = list.ConvertAll(static (item) => (long)item); + // DoNothing(converted); + // } + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs new file mode 100644 index 0000000..ee6158f --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Exists_BestAndWorstCases.cs @@ -0,0 +1,67 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public partial class RecyclableCollectionsCompatibilityListBenchmarks + { + public void Array_Exists_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.Exists(list, (x) => x == data[i])); + DoNothing.With(Array.Exists(list, (x) => x == data[^(i + 1)])); + } + } + + public void List_Exists_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.Exists((x) => x == data[i])); + DoNothing.With(list.Exists((x) => x == data[^(i + 1)])); + } + } + + public void PooledList_Exists_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsPooledList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.Exists((x) => x == data[i])); + DoNothing.With(list.Exists((x) => x == data[^(i + 1)])); + } + } + + public void RecyclableList_Exists_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.Exists((x) => x == data[i])); + DoNothing.With(list.Exists((x) => x == data[^(i + 1)])); + } + } + + //public void RecyclableLongList_Exists_BestAndWorstCases() + //{ + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // DoNothing.With(list.Exists((x) => x == data[i])); + // DoNothing.With(list.Exists((x) => x == data[^(i + 1)])); + // } + //} + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs new file mode 100644 index 0000000..1d13950 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindAll_BestAndWorstCases.cs @@ -0,0 +1,82 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public partial class RecyclableCollectionsCompatibilityListBenchmarks + { + public void Array_FindAll_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.FindAll(list, (item) => item == data[i])); + DoNothing.With(Array.FindAll(list, (item) => item == data[^(i + 1)])); + } + } + + public void List_FindAll_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.FindAll((item) => item == data[i])); + DoNothing.With(list.FindAll((item) => item == data[^(i + 1)])); + } + } + + public void PooledList_FindAll_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsPooledList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + var temp = list.FindAll((item) => item == data[i]); + DoNothing.With(temp); + temp.Dispose(); + + temp = list.FindAll((item) => item == data[^(i + 1)]); + DoNothing.With(temp); + temp.Dispose(); + } + } + + public void RecyclableList_FindAll_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + var temp = list.FindAll((item) => item == data[i]); + DoNothing.With(temp); + temp.Dispose(); + + temp = list.FindAll((item) => item == data[^(i + 1)]); + DoNothing.With(temp); + temp.Dispose(); + } + } + + //public void RecyclableLongList_FindAll_BestAndWorstCases() + //{ + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // var temp = list.FindAll((item) => item == data[i]); + // DoNothing.With(temp); + // temp.Dispose(); + + // temp = list.FindAll((item) => item == data[^(i + 1)]); + // DoNothing.With(temp); + // temp.Dispose(); + // } + //} + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs new file mode 100644 index 0000000..648cab9 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLastIndex_BestAndWorstCases.cs @@ -0,0 +1,55 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public partial class RecyclableCollectionsCompatibilityListBenchmarks : CollectionsBenchmarksBase + { + public void Array_FindLastIndex_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[i])); + DoNothing.With(Array.FindLastIndex(list, 0, (x) => x == data[^(i + 1)])); + } + } + + public void List_FindLastIndex_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.FindLastIndex(0, (x) => x == data[i])); + DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); + } + } + + public void RecyclableList_FindLastIndex_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.FindLastIndex(0, (x) => x == data[i])); + DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); + } + } + + //public void RecyclableLongList_FindLastIndex_BestAndWorstCases() + //{ + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // DoNothing.With(list.FindLastIndex(0, (x) => x == data[i])); + // DoNothing.With(list.FindLastIndex(0, (x) => x == data[^(i + 1)])); + // } + //} + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs new file mode 100644 index 0000000..5b89d10 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/FindLast_BestAndWorstCases.cs @@ -0,0 +1,55 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public partial class RecyclableCollectionsCompatibilityListBenchmarks + { + public void Array_FindLast_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.FindLast(list, (x) => x == data[i])); + DoNothing.With(Array.FindLast(list, (x) => x == data[^(i + 1)])); + } + } + + public void List_FindLast_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.FindLast((x) => x == data[i])); + DoNothing.With(list.FindLast((x) => x == data[^(i + 1)])); + } + } + + public void RecyclableList_FindLast_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.FindLast((x) => x == data[i])); + DoNothing.With(list.FindLast((x) => x == data[^(i + 1)])); + } + } + + //public void RecyclableLongList_FindLast_BestAndWorstCases() + //{ + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // DoNothing.With(list.FindLast((x) => x == data[i])); + // DoNothing.With(list.FindLast((x) => x == data[^(i + 1)])); + // } + //} + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs new file mode 100644 index 0000000..028e1a6 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Find_BestAndWorstCases.cs @@ -0,0 +1,55 @@ +using Collections.Benchmarks.Core; + +namespace Recyclable.Collections.Benchmarks +{ + public partial class RecyclableCollectionsCompatibilityListBenchmarks + { + public void Array_Find_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjects; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(Array.Find(list, (x) => x == data[i])); + DoNothing.With(Array.Find(list, (x) => x == data[^(i + 1)])); + } + } + + public void List_Find_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.Find((x) => x == data[i])); + DoNothing.With(list.Find((x) => x == data[^(i + 1)])); + } + } + + public void RecyclableList_Find_BestAndWorstCases() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = TestObjectCountForSlowMethods; + for (var i = 0; i < dataCount; i++) + { + DoNothing.With(list.Find((x) => x == data[i])); + DoNothing.With(list.Find((x) => x == data[^(i + 1)])); + } + } + + //public void RecyclableLongList_Find_BestAndWorstCases() + //{ + // var data = TestObjects; + // var list = TestObjectsAsRecyclableLongList; + // var dataCount = TestObjectCountForSlowMethods; + // for (var i = 0; i < dataCount; i++) + // { + // DoNothing.With(list.Find((x) => x == data[i])); + // DoNothing.With(list.Find((x) => x == data[^(i + 1)])); + // } + //} + } +} diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj b/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj new file mode 100644 index 0000000..4fbb469 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Benchmarks/Recyclable.Collections.Compatibility.List.Benchmarks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + diff --git a/Recyclable.Collections.Compatibility.List.Tests/GlobalUsings.cs b/Recyclable.Collections.Compatibility.List.Tests/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj b/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj new file mode 100644 index 0000000..d8e4e38 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Tests/Recyclable.Collections.Compatibility.List.Tests.csproj @@ -0,0 +1,28 @@ + + + + net6.0;net7.0 + enable + enable + + false + true + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs new file mode 100644 index 0000000..730e4b9 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableListCompatibilityListTests.cs @@ -0,0 +1,953 @@ +using FluentAssertions; +using Recyclable.Collections; +using Recyclable.Collections.TestData; +using Recyclable.Collections.Pools; +using System.Numerics; + +#pragma warning disable xUnit1026, RCS1163, IDE0039, IDE0060, RCS1235 + +namespace Recyclable.CollectionsTests +{ + public class RecyclableListCompatibilityListTests + { + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ConvertAllShouldConvertAllItems(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList().ConvertAll(item => item + 0.1); + + // Act + using var actual = list.ConvertAll(item => item + 0.1); + + // Validate + _ = actual.Count.Should().Be(itemsCount); + _ = actual.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenFromStart(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + long[] copiedItems = new long[itemsCount]; + + // Act + list.CopyTo(copiedItems); + + // Validate + long[] expectedData = new long[itemsCount]; + testData.ToList().CopyTo(expectedData); + + _ = copiedItems.Should().Equal(testData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ExistsShouldFindAllItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.Exists(item => item == expectedItem); + + // Act + var actual = list.Exists(item => item == expectedItem); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ExistsShouldNotFindNonExistingItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.Exists(item => item == -expectedItem); + + // Act + var actual = list.Exists(item => item == -expectedItem); + + // Validate + _ = actual.Should().Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindAllIndexesShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + //* There is no equivalent of .FindAllIndexes in List, so no comparison here. + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + + foreach (var itemIndex in itemIndexes) + { + // Act + using var actual = list.FindAllIndexes(item => item == itemIndex + 1); + + // Validate + _ = actual.Count.Should().Be(1); + if (itemsCount > 0) + { + _ = actual.Count.Should().Be(1); + _ = actual.Should().AllSatisfy(x => x.Should().Be((int)itemIndex)); + } + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))] + public void FindAllShouldReturnCorrectIndexes(int itemsCount) + { + const int ExpectedItem = 1; + + // Prepare + var testData = Enumerable.Range(0, itemsCount).Select(value => value % 2 == 0 ? ExpectedItem : -ExpectedItem); + using var list = new RecyclableList(testData, itemsCount); + + // Act + using var actual = list.FindAll(x => x == ExpectedItem); + + // Validate + var expectedData = testData.ToList().FindAll(x => x == ExpectedItem); + + _ = actual.Count.Should().Be(expectedData.Count); + _ = actual.Should().Equal(expectedData); + if (expectedData.Count > 0) + { + _ = actual.Should().AllSatisfy(x => x.Should().Be(ExpectedItem)).And.Equal(expectedData); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.ItemsCountTestCases), MemberType = typeof(RecyclableLongListTestData))] + public void FindAllShouldNotFindNonExistingItems(int itemsCount) + { + const int ExpectedItem = 1; + + // Prepare + var testData = Enumerable.Range(0, itemsCount).Select(_ => ExpectedItem); + using var list = new RecyclableList(testData, itemsCount); + + // Act + using var foundItems = list.FindAll(x => x == -ExpectedItem); + + // Validate + var expectedData = testData.ToList().FindAll(x => x == -ExpectedItem); + + _ = foundItems.Count.Should().Be(expectedData.Count); + _ = foundItems.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindIndexShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act + var actual = list.FindIndex(item => item == expectedItem); + + // Validate + var expected = expectedData.FindIndex(item => item == expectedItem); + + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem); + + // Act + var actual = list.FindIndex((int)itemIndex, (int)(itemsCount - itemIndex), item => item == expectedItem); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem); + + // Act + var actual = list.FindIndex(Math.Min(1, (int)itemIndex), item => item == expectedItem); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindLastIndexShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindLastIndex(x => x == expectedItem); + + // Act + var actual = list.FindLastIndex(x => x == expectedItem); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + + // Act & Validate + var expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem); + _ = list.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindLastIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindLastIndex(itemsCount - 1, x => x == expectedItem); + + // Act + var actual = list.FindLastIndex(itemsCount - 1, x => x == expectedItem); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindLastIndexShouldThrowArgumentOutOfRange(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + int expected = int.MinValue; + + // Act & Validate + if (itemsCount - 1 != -1) + { + Assert.Throws(() => expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem)); + Assert.Throws(() => _ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected)); + } + else + { + expected = expectedData.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem); + _ = list.FindLastIndex(itemsCount - 1, itemsCount + 3, x => x == expectedItem).Should().Be((int)itemIndex).And.Be(expected); + } + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindLastShouldReturnCorrectItem(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.FindLast(item => item == expectedItem); + + // Act + var actual = list.FindLast(item => item == expectedItem); + + // Validate + _ = actual.Should().Be(expectedData[(int)itemIndex]).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindShouldFindAllItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + long expectedItem = expectedData[(int)itemIndex]; + var expected = expectedData.Find(item => item == expectedItem); + + // Act + var actual = list.Find(item => item == expectedData[(int)itemIndex]); + + // Validate + _ = actual.Should().Be(expectedData[(int)itemIndex]); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindShouldNotFindNonExistingItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.Find(item => item == -expectedData[(int)itemIndex]); + + // Act + var actual = list.Find(item => item == -expectedData[(int)itemIndex]); + + // Validate + actual.Should().Be(0).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ForEachShouldGiveItemsInCorrectOrder(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + var expected = new List(itemsCount); + expectedData.ForEach(expected.Add); + + // Act + var yieldedItems = new List(itemsCount); + list.ForEach(yieldedItems.Add); + + // Validate + _ = yieldedItems.Should().Equal(testData).And.Equal(expected); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ForEachShouldDoNothingWhenEmptyList(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + var expected = new List(itemsCount); + expectedData.ForEach(expected.Add); + + // Act + var yieldedItems = new List(itemsCount); + list.ForEach(item => yieldedItems.Add(item)); + + // Validate + _ = yieldedItems.Should().BeEmpty().And.Equal(expected); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void GetRangeShouldReturnCorrectItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + int startingIndex = (int)Math.Min(1, itemIndex); + var expectedRangeItems = expectedData.GetRange(startingIndex, (int)itemIndex); + + // Act + using var rangeItems = list.GetRange(startingIndex, (int)itemIndex); + + // Validate + _ = rangeItems.Count.Should().Be(expectedRangeItems.Count); + _ = rangeItems.Should().Equal(expectedRangeItems); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex)); + + // Act + var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex, (int)(itemsCount - itemIndex)); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.IndexOf(expectedData[(int)itemIndex], (int)itemIndex); + + // Act + var actual = list.IndexOf(expectedData[(int)itemIndex], (int)itemIndex); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LastIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex]); + + // Act + var actual = list.LastIndexOf(expectedData[(int)itemIndex]); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedCount(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount); + + // Act + var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LastIndexOfShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + var expected = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1); + + // Act + var actual = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1); + + // Validate + _ = actual.Should().Be((int)itemIndex).And.Be(expected); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LastIndexOfShouldThrowArgumentOutOfRangeWhenCountTooBig(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + + // Act & Validate + foreach (var itemIndex in itemIndexes) + { + _ = Assert.Throws(() => _ = expectedData.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex)); + _ = Assert.Throws(() => _ = list.LastIndexOf(expectedData[(int)itemIndex], itemsCount - 1, itemsCount + 3).Should().Be((int)itemIndex)); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InsertRangeShouldMoveItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData.Reverse(), itemsCount); + var expectedItems = testData.Reverse().ToList(); + var item = expectedItems[^1]; + expectedItems.InsertRange((int)itemIndex, testData); + + // Act + list.InsertRange((int)itemIndex, testData); + + // Validate + _ = list[(int)itemIndex].Should().Be(item); + _ = list.Count.Should().Be(itemsCount << 1); + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount << 1); + _ = list.Should().Equal(expectedItems); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void RemoveAllShouldRemoveTheCorrectItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var itemsToRemove = itemIndexes.Select(x => list[(int)x]).ToArray(); + var expectedItems = testData.ToList(); + var expectedResult = expectedItems.RemoveAll(itemsToRemove.Contains); + + // Act + var actualResult = list.RemoveAll(itemsToRemove.Contains); + + // Validate + _ = actualResult.Should().Be(expectedResult); + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount); + _ = list.Count.Should().Be(itemsCount - itemsToRemove.Length); + _ = list.Should().Equal(expectedItems); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void RemoveRangeShouldRemoveCorrectItems(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) + { + _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); + + foreach (var itemIndex in itemIndexes) + { + // Prepare + int startingIndex = (int)Math.Min(1, itemIndex); + var expectedData = testData.ToList(); + expectedData.RemoveRange(startingIndex, (int)itemIndex); + + // Act + using var list = new RecyclableList(testData, itemsCount); + list.RemoveRange(startingIndex, (int)itemIndex); + + // Validate + _ = list.Count.Should().Be(expectedData.Count); + _ = list.Should().Equal(expectedData); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ReverseShouldSwapItemsInCorrectOrder(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedData = testData.ToList(); + expectedData.Reverse(); + + // Act + list.Reverse(); + + // Validate + _ = list.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ReverseShouldSwapItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + var expectedData = testData.ToArray(); + + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var startingItemIndex = (int)Math.Min(1, itemIndex); + var expectedRangeItems = expectedData.ToList(); + expectedRangeItems.Reverse(startingItemIndex, (int)itemIndex); + + // Act + list.Reverse(startingItemIndex, (int)itemIndex); + + // Validate + _ = list.Should().Equal(expectedRangeItems); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void SortShouldSortItemsInCorrectOrder(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData.Reverse(), itemsCount); + var expectedData = testData.Reverse().ToList(); + expectedData.Sort(); + + // Act + list.Sort(); + + // Validate + _ = list.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void SortShouldSortItemsInCorrectOrderWhenWithComparer(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData.Reverse(), itemsCount); + IComparer comparer = Comparer.Default; + var expectedData = testData.Reverse().ToList(); + expectedData.Sort(comparer); + + // Act + list.Sort(comparer); + + // Validate + _ = list.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void SortShouldSortItemsInCorrectOrderWhenWithComparison(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData.Reverse(), itemsCount); + + static int comparison(long x, long y) + { + return (x - y) switch + { + > 0 => 1, + < 0 => -1, + _ => 0, + }; + } + + var expectedData = testData.Reverse().ToList(); + expectedData.Sort(comparison); + + // Act + list.Sort(comparison); + + // Validate + _ = list.Should().Equal(expectedData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void SortShouldSortItemsInCorrectOrderWhenConstrained(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + IComparer comparer = Comparer.Default; + var expectedData = testData.Reverse().ToArray(); + + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData.Reverse(), itemsCount); + var startingIndex = (int)Math.Min(1, itemIndex); + + var expectedSortedData = expectedData.ToList(); + expectedSortedData.Sort(startingIndex, (int)itemIndex, comparer); + + // Act + list.Sort(startingIndex, (int)itemIndex, comparer); + + // Validate + _ = list.Should().Equal(expectedSortedData); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldDecreaseCapacity(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedCapacity = itemsCount >= RecyclableDefaults.InitialCapacity ? itemsCount : RecyclableDefaults.InitialCapacity; + + // Act + list.TrimExcess(); + + // Validate + _ = list.Capacity.Should().Be(expectedCapacity); + _ = list.Count.Should().Be(itemsCount); + _ = list.Should().Equal(testData); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakAdd(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1); + expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity; + + // Act + list.TrimExcess(); + list.Add(-1); + + // Validate + _ = list.Capacity.Should().Be(expectedCapacity); + _ = list.Count.Should().Be(itemsCount + 1); + _ = list.Should().Equal(testData.Append(-1)); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakAddRange(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2(checked((uint)itemsCount << 1)); + expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity; + + // Act + list.TrimExcess(); + list.AddRange(testData); + + // Validate + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity); + _ = list.Count.Should().Be(itemsCount << 1); + _ = list.Should().Equal(testData.Concat(testData)); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakClear(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + + // Act + list.TrimExcess(); + list.Clear(); + + // Validate + _ = list.Capacity.Should().Be(Math.Max(RecyclableDefaults.InitialCapacity, itemsCount)); + _ = list.Count.Should().Be(0); + _ = list.Should().BeEmpty(); + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakInsert(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + var expectedCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1); + expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity; + var expectedData = testData.ToArray(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, itemsCount); + var expectedItemsAfterInsert = expectedData.ToList(); + expectedItemsAfterInsert.Insert((int)itemIndex, -3); + + // Act + list.TrimExcess(); + list.Insert((int)itemIndex, -3); + + // Validate + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedCapacity); + _ = list.Count.Should().Be(expectedItemsAfterInsert.Count); + _ = list.Should().Equal(expectedItemsAfterInsert); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakRemove(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + var expectedData = testData.ToArray(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, itemsCount); + var expectedItemsAfterInsert = expectedData.ToList(); + expectedItemsAfterInsert.RemoveAt((int)itemIndex); + + // Act + list.TrimExcess(); + list.RemoveAt((int)itemIndex); + + // Validate + _ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount); + _ = list.Count.Should().Be(expectedItemsAfterInsert.Count); + _ = list.Should().Equal(expectedItemsAfterInsert); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimExcessShouldNotBreakIndexOf(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + var expectedData = testData.ToArray(); + + foreach (var itemIndex in itemIndexes) + { + using var list = new RecyclableList(testData, itemsCount); + + // Act + list.TrimExcess(); + var actual = list.IndexOf(expectedData[(int)itemIndex]); + + // Validate + _ = actual.Should().Be((int)itemIndex); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrimmedArraysShouldNotBePooled(string testCase, IEnumerable testData, int itemsCount) + { + // Prepare + using var list = new RecyclableList(testData, itemsCount); + + // Act + list.TrimExcess(); + var trimmedMemoryBlock = list.AsArray; + + list.Dispose(); + + // Validate + _ = list.Capacity.Should().Be(0); + _ = list.Count.Should().Be(0); + _ = list.Should().BeEmpty(); + + if (itemsCount >= RecyclableDefaults.MinPooledArrayLength) + { + var newMemoryBlock = RecyclableArrayPool.RentShared(itemsCount); + _ = BitOperations.IsPow2(itemsCount) + ? newMemoryBlock.Should().BeSameAs(trimmedMemoryBlock) + : newMemoryBlock.Should().NotBeSameAs(trimmedMemoryBlock); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrueForAllShouldBeFalseWhenNoMatch(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + var expectedData = testData.ToList(); + + foreach (var itemIndex in itemIndexes) + { + using RecyclableList list = new(testData, itemsCount); + Predicate condition = item => item == itemIndex; + var expected = expectedData.TrueForAll(condition); + + // Act + var actual = list.TrueForAll(condition); + + // Validate + _ = actual.Should().Be(expected).And.BeFalse(); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void TrueForAllShouldBeTrueWhenMatched(string testCase, IEnumerable testData, int itemsCount, long[] itemIndexes) + { + // Prepare + using RecyclableList list = new(testData, itemsCount); + int index = 0; + Predicate condition = item => item == ++index; + var expected = testData.ToList().TrueForAll(condition); + + index = 0; + + // Act + var actual = list.TrueForAll(condition); + + // Validate + _ = actual.Should().Be(expected).And.BeTrue(); + } + } +} \ No newline at end of file diff --git a/Recyclable.Collections.Compatibility.List.sln b/Recyclable.Collections.Compatibility.List.sln new file mode 100644 index 0000000..837756c --- /dev/null +++ b/Recyclable.Collections.Compatibility.List.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List", "Recyclable.Collections.Compatibility.List\Recyclable.Collections.Compatibility.List.csproj", "{779028EF-0584-4566-A80D-B5F3D6E25DD0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Recyclable.Collections.Compatibility.List.Tests", "Recyclable.Collections.Compatibility.List.Tests\Recyclable.Collections.Compatibility.List.Tests.csproj", "{1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recyclable.Collections.Compatibility.List.Benchmarks", "Recyclable.Collections.Compatibility.List.Benchmarks\Recyclable.Collections.Compatibility.List.Benchmarks.csproj", "{A1414948-E61B-4AE1-ACAF-33A8BB85041E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {779028EF-0584-4566-A80D-B5F3D6E25DD0}.Release|Any CPU.Build.0 = Release|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1501C2BD-BDAF-4C89-B131-16C7D9C1B41B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1414948-E61B-4AE1-ACAF-33A8BB85041E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {16B9D137-EEFC-487F-A158-3C6AEDFDDBD5} + EndGlobalSection +EndGlobal diff --git a/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj new file mode 100644 index 0000000..10c6017 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/Recyclable.Collections.Compatibility.List.csproj @@ -0,0 +1,37 @@ + + + + net6.0;net7.0 + enable + enable + true + Recyclable.Collections.Compatibility.List + 0.0.1 + Marek Lemańczyk + N/A + Recyclable.Collections.Compatibility.List + Recyclable ArrayPool Array Pool List Sortable Sort + Recyclable.Collections.Compatibility.List project is an open source framework for operating dynamic lists at performance close to raw arrays. It aims at providing minimal memory footprint. It implements IList<T>'s interface and is targeted as direct replacements of List<T>, SortableList<T>, PriorityQueue<T> and similar. + https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List + https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List + Marek Lemańczyk 2023 + LICENSE.md + README.md + git + Support for RecyclableList<T> & RecyclableLongList<T> added. + AnyCPU;x64 + + + + + Always + + + PreserveNewest + + + + + + + diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs new file mode 100644 index 0000000..e116201 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.Find.cs @@ -0,0 +1,271 @@ +using System.Numerics; + +namespace Recyclable.Collections +{ + public static class zzzRecyclableListCompatibilityListFind + { + public static bool Exists(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return false; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + return true; + } + } + + return false; + } + + public static T? Find(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return default; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + return sourceSpan[itemIndex]; + } + } + + return default; + } + + public static RecyclableList FindAll(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return new(); + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + RecyclableList result = new(sourceItemsCount >> 3); + Span resultSpan = result._memoryBlock; + int capacity = result._capacity, + resultItemsCount = 0; + + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + if (resultItemsCount + 1 > capacity) + { + capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1)); + _ = RecyclableListHelpers.EnsureCapacity(result, resultItemsCount, capacity); + resultSpan = result._memoryBlock; + } + + resultSpan[resultItemsCount++] = sourceSpan[itemIndex]; + } + } + + result._capacity = capacity; + result._count = resultItemsCount; + return result; + } + public static RecyclableList FindAllIndexes(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return new(); + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + RecyclableList result = new(sourceItemsCount >> 3); + Span resultSpan = result._memoryBlock; + int capacity = result._capacity, + resultItemsCount = 0; + + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + if (resultItemsCount + 1 > capacity) + { + capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)resultItemsCount + 1)); + _ = RecyclableListHelpers.EnsureCapacity(result, resultItemsCount, capacity); + resultSpan = result._memoryBlock; + } + + resultSpan[resultItemsCount++] = itemIndex; + } + } + + result._capacity = capacity; + result._count = resultItemsCount; + return result; + } + + public static int FindIndex(this RecyclableList list, int startIndex, int count, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0 || startIndex >= sourceItemsCount) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount); + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + + public static int FindIndex(this RecyclableList list, int startIndex, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0 || startIndex >= sourceItemsCount) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + sourceItemsCount = Math.Min(sourceItemsCount, startIndex + sourceItemsCount); + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = startIndex; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + + public static int FindIndex(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + + public static T? FindLast(this RecyclableList list, Predicate match) + { + if (list._count == 0) + { + return default; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return sourceSpan[itemIndex]; + } + } + + return default; + } + + public static int FindLastIndex(this RecyclableList list, int startIndex, int count, Predicate match) + { + if (count == 0) + { + if (startIndex != -1) + { + ThrowHelper.ThrowArgumentOutOfRangeException_Index(); + } + } + else if (count < 0 || startIndex - count + 1 < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException_Count(); + } + + if (startIndex < 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1; + if (sourceItemsCount == 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + int lastItemIndex = startIndex - sourceItemsCount; + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = startIndex; itemIndex > lastItemIndex; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + + public static int FindLastIndex(this RecyclableList list, int startIndex, Predicate match) + { + if (list._count == 0 || startIndex < 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + + public static int FindLastIndex(this RecyclableList list, Predicate match) + { + if (list._count == 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; + } + } +} diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.IndexOf.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.IndexOf.cs new file mode 100644 index 0000000..109d3f1 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.IndexOf.cs @@ -0,0 +1,25 @@ +namespace Recyclable.Collections +{ + public static class zzzRecyclableListCompatibilityListIndexOf + { + public static int IndexOf(this RecyclableList list, T item, int index) => list._count != 0 + ? Array.IndexOf(list._memoryBlock, item, index, list._count - index) + : RecyclableDefaults.ItemNotFoundIndex; + + public static int IndexOf(this RecyclableList list, T item, int index, int count) => list._count != 0 + ? Array.IndexOf(list._memoryBlock, item, index, count) + : RecyclableDefaults.ItemNotFoundIndex; + + public static int LastIndexOf(this RecyclableList list, T item) => list._count != 0 + ? Array.LastIndexOf(list._memoryBlock, item, list._count - 1) + : RecyclableDefaults.ItemNotFoundIndex; + + public static int LastIndexOf(this RecyclableList list, T item, int index) => list._count != 0 + ? Array.LastIndexOf(list._memoryBlock, item, index) + : RecyclableDefaults.ItemNotFoundIndex; + + public static int LastIndexOf(this RecyclableList list, T item, int index, int count) => list._count != 0 + ? Array.LastIndexOf(list._memoryBlock, item, index, count) + : RecyclableDefaults.ItemNotFoundIndex; + } +} diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs new file mode 100644 index 0000000..8d85795 --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.InsertRange.cs @@ -0,0 +1,357 @@ +using System.Collections; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Recyclable.Collections +{ + public static class zzzRecyclableListCompatibilityListInsertRange + { + private static void InsertRangeEnumerated(this RecyclableList list, int index, IEnumerable items, int growByCount) + { + var enumerator = items.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return; + } + + int capacity = list._capacity, + added = 0, + available = capacity - list._count, + i, + movingBlockLength = list._count - index; + + T[] memoryBlock = list._memoryBlock; + if (available > 0) + { + Array.Copy(memoryBlock, index, memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + + Span memorySpan = memoryBlock; + do + { + if (index + available + movingBlockLength > capacity) + { + var capacityBeforeIncrease = capacity; + capacity = checked((int)BitOperations.RoundUpToPowerOf2((uint)(capacity + growByCount))); + _ = RecyclableListHelpers.EnsureCapacity(list, memoryBlock.Length, capacity); + memorySpan = memoryBlock = list._memoryBlock; + available = capacity - index - movingBlockLength; + Array.Copy(memoryBlock, capacityBeforeIncrease - movingBlockLength, memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + + for (i = 0; i < available; i++) + { + memorySpan[index++] = enumerator.Current; + if (!enumerator.MoveNext()) + { + added++; + break; + } + } + + added += i; + } + while (i == available); + + if (i != available) + { + Array.Copy(memoryBlock, capacity - movingBlockLength, memoryBlock, index, movingBlockLength); + if (RecyclableList._needsClearing) + { + Array.Clear(memoryBlock, capacity - movingBlockLength, movingBlockLength); + } + } + + list._count += added; + list._capacity = capacity; + } + + private static void InsertRangeWithKnownCount(this RecyclableList list, int index, IEnumerable items, int currentItemsCount, int requiredAdditionalCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, currentItemsCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)(currentItemsCount + requiredAdditionalCapacity)))); + + Array.Copy(list._memoryBlock, index, list._memoryBlock, index + requiredAdditionalCapacity, list._count - index); + Span memorySpan = list._memoryBlock; + foreach (var item in items) + { + memorySpan[index++] = item; + } + + list._count += requiredAdditionalCapacity; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, in Array items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); + Array.Copy(items, items.GetLowerBound(0), memoryBlock, index, items.Length); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, in T[] items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); + new ReadOnlySpan(items).CopyTo(new Span(memoryBlock, index, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, ReadOnlySpan items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); + items.CopyTo(new Span(memoryBlock, index, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, Span items) + { + if (list._capacity < list._count + items.Length) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Length)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Length, list._count - index); + items.CopyTo(new Span(memoryBlock, index, items.Length)); + list._count += items.Length; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, List items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); + items.CopyTo(memoryBlock, index); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, ICollection items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); + items.CopyTo(list._memoryBlock, index); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, ICollection items) + { + if (list._capacity < list._count + items.Count) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items.Count)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); + items.CopyTo(memoryBlock, index); + list._count += items.Count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, RecyclableList items) + { + if (list._capacity < list._count + items._count) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + items._count)))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, list._count - index); + new ReadOnlySpan(items._memoryBlock, 0, items._count).CopyTo(new(list._memoryBlock, index, items._count)); + list._count += items._count; + list._version++; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, RecyclableLongList items) + { + if (items._longCount == 0) + { + return; + } + + int oldCount = list._count; + long sourceItemsCount = items._longCount; + if (oldCount + sourceItemsCount > int.MaxValue) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(items), $"The total number of items in source and target table exceeds the maximum capacity of {nameof(RecyclableList)}, equal {int.MaxValue}. Please consider using {nameof(RecyclableLongList)}, instead"); + } + + int targetCapacity = oldCount + (int)sourceItemsCount; + if (list._capacity < targetCapacity) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, oldCount, checked((int)BitOperations.RoundUpToPowerOf2((uint)targetCapacity))); + } + + T[] memoryBlock = list._memoryBlock; + Array.Copy(memoryBlock, index, memoryBlock, index + items.Count, oldCount - index); + Span targetSpan = new(list._memoryBlock, index, list._capacity - index), + itemsSpan; + + int blockIndex, + sourceBlockSize = items._blockSize, + lastBlockIndex = (int)(sourceItemsCount >> items._blockSizePow2BitShift) - 1; + + for (blockIndex = 0; blockIndex <= lastBlockIndex; blockIndex++) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, sourceBlockSize); + itemsSpan.CopyTo(targetSpan); + targetSpan = targetSpan[sourceBlockSize..]; + } + + if (blockIndex == 0) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, (int)sourceItemsCount); + itemsSpan.CopyTo(targetSpan); + } + else if (blockIndex <= items._lastBlockWithData) + { + itemsSpan = new(items._memoryBlocks[blockIndex], 0, items._nextItemIndex > 0 ? items._nextItemIndex : sourceBlockSize); + itemsSpan.CopyTo(targetSpan); + } + + list._count = targetCapacity; + list._version++; + } + + public static IEnumerator InsertRange(this RecyclableList list, int index, IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) + { + int targetItemIndex = list._count; + Span memorySpan; + + int i; + var enumerator = source.GetEnumerator(); + + int capacity = list._capacity; + memorySpan = list._memoryBlock; + if (enumerator.MoveNext()) + { + int available = capacity - targetItemIndex; + do + { + if (targetItemIndex + growByCount > capacity) + { + capacity = RecyclableListHelpers.EnsureCapacity(list, targetItemIndex, checked((int)BitOperations.RoundUpToPowerOf2((uint)(targetItemIndex + growByCount)))); + memorySpan = list._memoryBlock; + available = capacity - targetItemIndex; + } + + for (i = 0; i < available; i++) + { + memorySpan[targetItemIndex++] = (T)enumerator.Current; + if (!enumerator.MoveNext()) + { + break; + } + } + } + while (i >= available); + } + + list._capacity = capacity; + list._count = targetItemIndex; + return enumerator; + } + + public static void InsertRange(this RecyclableList list, int index, IReadOnlyList items) + { + int sourceItemsCount = items.Count; + if (list._capacity < list._count + sourceItemsCount) + { + list._capacity = RecyclableListHelpers.EnsureCapacity(list, list._count, checked((int)BitOperations.RoundUpToPowerOf2((uint)(list._count + sourceItemsCount)))); + } + + Span memorySpan = new(list._memoryBlock, list._count, sourceItemsCount); + for (var sourceItemIndex = 0; sourceItemIndex < sourceItemsCount; sourceItemIndex++) + { + memorySpan[sourceItemIndex] = items[sourceItemIndex]; + } + + list._count += sourceItemsCount; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static void InsertRange(this RecyclableList list, int index, IEnumerable items, int growByCount = RecyclableDefaults.MinPooledArrayLength) + { + if (items is RecyclableList sourceRecyclableList) + { + InsertRange(list, index, sourceRecyclableList); + } + else if (items is RecyclableLongList sourceRecyclableLongList) + { + InsertRange(list, index, sourceRecyclableLongList); + } + else if (items is T[] sourceArray) + { + InsertRange(list, index, sourceArray); + } + else if (items is Array sourceArrayWithObjects) + { + InsertRange(list, index, sourceArrayWithObjects); + } + else if (items is List sourceList) + { + InsertRange(list, index, sourceList); + } + else if (items is ICollection sourceICollection) + { + InsertRange(list, index, sourceICollection); + } + else if (items is ICollection sourceICollectionWithObjects) + { + InsertRange(list, index, sourceICollectionWithObjects); + } + else if (items is IReadOnlyList sourceIReadOnlyList) + { + InsertRange(list, index, sourceIReadOnlyList); + } + else if (items.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity) && requiredAdditionalCapacity != 0) + { + InsertRangeWithKnownCount(list, index, items, list._count, requiredAdditionalCapacity); + } + else + { + InsertRangeEnumerated(list, index, items, growByCount); + } + } + } +} \ No newline at end of file diff --git a/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs new file mode 100644 index 0000000..d43758b --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/RecyclableList.Compatibility.List.cs @@ -0,0 +1,213 @@ +using System.Collections.ObjectModel; +using System.Numerics; +using Recyclable.Collections.Pools; + +namespace Recyclable.Collections +{ + + public static class zzzRecyclableListCompatibilityList + { + public static ReadOnlyCollection AsReadOnly(this RecyclableList list) => new(list); + + public static int BinarySearch(this RecyclableList list, T item) + => Array.BinarySearch(list._memoryBlock, 0, list._count, item); + + public static int BinarySearch(this RecyclableList list, T item, IComparer? comparer) + => Array.BinarySearch(list._memoryBlock, 0, list._count, item, comparer); + + public static int BinarySearch(this RecyclableList list, int index, int count, T item, IComparer? comparer) + => Array.BinarySearch(list._memoryBlock, index, count, item, comparer); + + public static RecyclableList ConvertAll(this RecyclableList list, Converter converter) + { + int itemsCount = list._count; + if (itemsCount == 0) + { + return new(); + } + + RecyclableList result = new(itemsCount); + TOutput[] resultSpan = result._memoryBlock; + ReadOnlySpan sourceSpan = list._memoryBlock; + for (int bufferIndex = 0; bufferIndex < itemsCount; bufferIndex++) + { + resultSpan[bufferIndex] = converter(sourceSpan[bufferIndex]); + } + + result._count = itemsCount; + return result; + } + + public static void CopyTo(this RecyclableList list, T[] array) => Array.Copy(list._memoryBlock, array, list._count); + + public static void CopyTo(this RecyclableList list, int index, T[] array, int arrayIndex, int count) => Array.Copy(list._memoryBlock, index, array, arrayIndex, count); + + public static void ForEach(this RecyclableList list, Action action) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + action(sourceSpan[itemIndex]); + } + } + + public static RecyclableList GetRange(this RecyclableList list, int index, int count) + { + int sourceItemsCount = index + count <= list._count ? count : list._count - index; + if (sourceItemsCount <= 0) + { + return new(); + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + RecyclableList result = new(sourceItemsCount); + Span targetSpan = result._memoryBlock; + int resultItemsCount = 0; + sourceItemsCount += index; + for (var itemIndex = index; itemIndex < sourceItemsCount; itemIndex++) + { + targetSpan[resultItemsCount++] = sourceSpan[itemIndex]; + } + + result._count = resultItemsCount; + return result; + } + + public static int RemoveAll(this RecyclableList list, Predicate match) + { + using var allIndexes = zzzRecyclableListCompatibilityListFind.FindAllIndexes(list, match); + int foundItemsCount = allIndexes._count; + if (foundItemsCount == 0) + { + return foundItemsCount; + } + + for (int removed = 0; removed < foundItemsCount; removed++) + { + var indexToRemove = allIndexes[removed] - removed; + Array.Copy(list._memoryBlock, indexToRemove + 1, list._memoryBlock, indexToRemove, list._count - indexToRemove - removed - 1); + } + + if (RecyclableList._needsClearing) + { + Array.Clear(list._memoryBlock, list._count - foundItemsCount, foundItemsCount); + } + + list._count -= foundItemsCount; + list._version++; + return foundItemsCount; + } + + public static void RemoveRange(this RecyclableList list, int index, int count) + { + int oldCount = list._count; + count = count - index + 1 <= oldCount ? count : oldCount - index + 1; + if (count == 0) + { + return; + } + + if (index + count < oldCount) + { + Array.Copy(list._memoryBlock, index + count, list._memoryBlock, index, oldCount - count - index); + } + + if (RecyclableList._needsClearing) + { + Array.Clear(list._memoryBlock, oldCount - count, count); + } + + list._count -= count; + list._version++; + } + + public static void Reverse(this RecyclableList list) + { + Array.Reverse(list._memoryBlock, 0, list._count); + list._version++; + } + + public static void Reverse(this RecyclableList list, int index, int count) + { + Array.Reverse(list._memoryBlock, index, count); + list._version++; + } + + public static void Sort(this RecyclableList list) + { + Array.Sort(list._memoryBlock, 0, list._count); + list._version++; + } + + public static void Sort(this RecyclableList list, Comparison comparison) + { + Array.Sort(list._memoryBlock, 0, list._count, new ComparisonToComparerAdapter(comparison)); + list._version++; + } + + public static void Sort(this RecyclableList list, IComparer? comparer) + { + Array.Sort(list._memoryBlock, 0, list._count, comparer); + list._version++; + } + + public static void Sort(this RecyclableList list, int index, int count, IComparer? comparer) + { + Array.Sort(list._memoryBlock, index, count, comparer); + list._version++; + } + + public static T[] ToArray(this RecyclableList list) + { + T[] result = new T[list._count]; + new ReadOnlySpan(list._memoryBlock, 0, list._count).CopyTo(result); + return result; + } + + public static void TrimExcess(this RecyclableList list) + { + var requiredCapacity = list._count >= RecyclableDefaults.InitialCapacity ? list._count : RecyclableDefaults.InitialCapacity; + + T[] oldMemoryBlock = list._memoryBlock; + T[] result = requiredCapacity >= RecyclableDefaults.MinPooledArrayLength && BitOperations.IsPow2(requiredCapacity) + ? RecyclableArrayPool.RentShared(requiredCapacity) + : new T[requiredCapacity]; + + new ReadOnlySpan(oldMemoryBlock, 0, list._count).CopyTo(result); + + if (oldMemoryBlock.Length >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(oldMemoryBlock, RecyclableList._needsClearing); + } + + list._memoryBlock = result; + list._capacity = requiredCapacity; + } + + public static bool TrueForAll(this RecyclableList list, Predicate match) + { + int sourceItemsCount = list._count; + if (sourceItemsCount == 0) + { + return false; + } + + ReadOnlySpan sourceSpan = list._memoryBlock; + for (var itemIndex = 0; itemIndex < sourceItemsCount; itemIndex++) + { + if (!match(sourceSpan[itemIndex])) + { + return false; + } + } + + return true; + } + } +} diff --git a/Recyclable.Collections.Compatibility.List/buy-me-a-coffee.svg b/Recyclable.Collections.Compatibility.List/buy-me-a-coffee.svg new file mode 100644 index 0000000..5dac9fa --- /dev/null +++ b/Recyclable.Collections.Compatibility.List/buy-me-a-coffee.svg @@ -0,0 +1 @@ +Buy me a coffeeBuy me a coffee \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..144b38824a70c724a9d92303ca101ceded86ed19 GIT binary patch literal 3244 zcmV;d3{&%oP)EX>4Tx04R}tkv&MmKpe$iQ$;CN2P=p;WT;MdQ7Mk4ibb$c+6t{Ym|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfb#YR3krMxx6k5c1aNLh~_a1le0HIlBs@W3*RLwHd ziMW`{uZn%I2w)H+h{7*1Q%`0Vv+x{W_we!cF2b|C&;2lrQ95 zRyc2QR;zW^z9)ZSu%NAExK48%2`nLr6hz3Vqk<|dL}=AWF_EVIxQBnt@u$coldA$o zjs?`9LUR1zfAG6ovp6&DCI#a_?~84JOaOhmK&xTf-^aGyIsyF8z?IhV*P6iGC+Urj z7Cr)owtI=-`x4gW(XyyR&``X6qqC?{VeCZIz#>VUW%(3!uuCA`;=H`UjGQ{m$?kvex*u-0;eL=>UxFDL&l+*4Eb3`kU4Hsm#>H*ZH_rKeq)y5H*5+WEX{=m6XKto#1?()X;)`J31IxTV>2x%6>^>l=RR z7}fH+mh3^t^^jKJ1&Hk+$Irk}APq@<*xqN1Upp`D$boSdAcrKQyPv+V5bbLar->guhnt?20J|Ns8} z|Ns8~{c`C5b?E^9{{GbXufyq{pYl-L+}j?`1ApfbW9SE@?^I{#3A3}a@cqvG|NFt+ zrnB=f*u1z0Aza|Nj0^-v-6?n8x^y zf8rp_^_bWAvf}07VdDjv&t>H1;#KGZnCVN+>#^hS*8Tnbn(r`m>;}B$maea@i0d+s z>oC*!h-%~k-}}6+$CR(viGt@9OB9Kq00001bW%=J06^y0W&i*H0b)x>L;#2d9Y_EG z010qNS#tmY3ljhU3ljkVnw%H_000McNliru=L!xM3;@~We#QU*2bD=gK~z}7#aX|L z}8kqvWdA{;lvvVI?%AkI;g-mLmL`e4vagwXhERzBrBGq zI3aD)ibnrKGl5~jikuuMs>6X%a!_ipsKmzaRY__|-R&E0XXYv>s$Tv0x?a8d-lv(9 z>{tn3eQ~GdZS_Hxog4vwdv*8a)q`X3SNBrB_|?hJc6KZuocxT3zq3`S^_KVGe{()29fW`J@5Y0IPO0E_L2H$kO#2~7%aw!{1D}@Z8f$Hk zOpqc}r8Mp;v4^cvx0K&4@56uoY?yl?pExS;`z6p+_T8 zIyUhfW3WV2*bc*4kD-lg`UmDeQ zL@LLkq!M*iMTO@quxboEo^AymQh0>aGM*BP+i>p^?cH)Yi2cu=Z-%6!;aPk6HJQf3 zB4;6!;WXkRET|R2g($_ORlxofT--C%L8+v}@%KV6S2)hl-Yw_BapHN}z%LrsF9m&B zP=edba6Y9GCDilgR4h{;96yb6LM$h244jZkF&^6yc?ltdJtv+=4z`?jxuu50vl&Gm z!he}|mp>=DSHKbR4AuhU3J(K&_z)9CR1hv3L%N_v>a zziZ&LATi^(ymsP(gbqYjyinS;uv@8^l3p>NQ(g*VSfM+Fz7D9W1@>?~*Y<8XJ4!sS zI`~z4`8g@7asgo!IyEJ+5LU>c5ejZ`h{rMVI@jckbc1*as6 zs9md4+f}F&oc7?qJEC^oncJn= z{{!utJOA<0Bc3Nc_$(M6vf0i}kdJ(bVHIFC+APuW<)??nj>+AQc(OkHs&~Vf!zDwu zbP*v9YDixSt97y$j=*cdvWA#k-^}|h@puDxXSX5uFojoQ-9EUK!-HeG>Ke@xa?X~b|nKXYgT2dmBj+9bg86qO`x5`gbk!C zrQi#dF10R#`JjQML@M%I%33C~iGlxZLp-Mg_>V7J$x)pVi>;ZcTUq0jpdcc_uu55t zw#q3X(?Y}Z=*uaw^AL1K+RbMbiK$Hctld9q}-4j``GA3bRH9 z0tPY{W>iz5o6!b|ka&Vub%i32nX&%-3Pcd)@I$C2atj1PlDIRic(nJw$%gQ(C%AHD zBr|3%PR~MFgMwgKN$U)M|gpH-5hGSfc<-UZ&iz5t_mHr@zc zyxZm6P%AWMfrbMC1L8-gc&tE)%|VM35~IKaCxJr2kZ8sR^C1^+K@Us|5`_!x+(#E3 z@tls}ziUn(I1wauKZT$Hn*coimBZeHuXnU-gbM3@D(SB)T{Ywa#*%5`DRGiq z>9OH_=tk)4RraBsO;D z4?ny%E81U|T>N5t5I*ByyLbN=&dKL?b{IZ;DnGhw{Nzu=Kiv=ip8r*4{j`#EUg!Cb e&pRFc=iq-_XvdbWZQwQl0000#o`W literal 0 HcmV?d00001