From b3c8e49600588776df1390b58bfd1395438eb78e Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 17 Dec 2023 18:42:27 +0100 Subject: [PATCH] RecyclableLongList.Compatibility.List.FindLastIndex() implemented. Tests extended. --- ...ecyclableLongListCompatibilityListTests.cs | 54 ++--- ...yclableLongList.Compatibility.List.Find.cs | 192 ++++++++++++------ 2 files changed, 160 insertions(+), 86 deletions(-) diff --git a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs index b99d9ae..2dbe68a 100644 --- a/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs +++ b/Recyclable.Collections.Compatibility.List.Tests/RecyclableLongListCompatibilityListTests.cs @@ -444,6 +444,32 @@ namespace Recyclable.CollectionsTests } } + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))] + public void FindIndexShouldNotAnythingWhenRangeExcludesItem(string testCase, IEnumerable testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemRanges) + { + // Prepare + using var list = new RecyclableLongList(testData, initialCapacity: itemsCount); + var expectedData = testData.ToList(); + + foreach (var (itemIndex, rangedItemsCount) in itemRanges) + { + TestWith(itemsCount, list, expectedData, itemIndex, rangedItemsCount, expectedData[Math.Max((int)(itemIndex - 1), 0)]); + TestWith(itemsCount, list, expectedData, itemIndex, rangedItemsCount, expectedData[Math.Min((int)(itemIndex + rangedItemsCount), itemsCount - 1)]); + } + + static void TestWith(int itemsCount, RecyclableLongList list, List expectedData, long itemIndex, long rangedItemsCount, long expectedItem) + { + var expected = expectedData.FindIndex((int)itemIndex, (int)rangedItemsCount, item => item == expectedItem); + + // Act + var actual = list.FindIndex((int)itemIndex, (int)rangedItemsCount, item => item == expectedItem); + + // Validate + _ = actual.Should().Be(expected); + } + } + [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void FindIndexShouldReturnCorrectIndexes(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) @@ -493,32 +519,6 @@ namespace Recyclable.CollectionsTests } } - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))] - public void FindIndexShouldNotAnythingWhenRangeExcludesItem(string testCase, IEnumerable testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemRanges) - { - // Prepare - using var list = new RecyclableLongList(testData, initialCapacity: itemsCount); - var expectedData = testData.ToList(); - - foreach (var (itemIndex, rangedItemsCount) in itemRanges) - { - TestWith(itemsCount, list, expectedData, itemIndex, rangedItemsCount, expectedData[Math.Max((int)(itemIndex - 1), 0)]); - TestWith(itemsCount, list, expectedData, itemIndex, rangedItemsCount, expectedData[Math.Min((int)(itemIndex + rangedItemsCount), itemsCount - 1)]); - } - - static void TestWith(int itemsCount, RecyclableLongList list, List expectedData, long itemIndex, long rangedItemsCount, long expectedItem) - { - var expected = expectedData.FindIndex((int)itemIndex, (int)rangedItemsCount, item => item == expectedItem); - - // Act - var actual = list.FindIndex((int)itemIndex, (int)rangedItemsCount, item => item == expectedItem); - - // Validate - _ = actual.Should().Be(expected); - } - } - [Theory] [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] public void FindIndexShouldReturnCorrectIndexesWhenConstrainedIndex(string testCase, IEnumerable testData, int itemsCount, in long[] itemIndexes) @@ -579,7 +579,7 @@ namespace Recyclable.CollectionsTests static void TestWith(int itemsCount, RecyclableLongList list, List expectedData, long itemIndex, long expectedItem) { 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); + _ = list.FindLastIndex(itemsCount - 1, itemsCount, x => x == expectedItem).Should().Be(expected); } } diff --git a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs index a8c22f3..938b16a 100644 --- a/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs +++ b/Recyclable.Collections.Compatibility.List/RecyclableLongList.Compatibility.List.Find.cs @@ -495,84 +495,158 @@ namespace Recyclable.Collections public static int FindLastIndex(this RecyclableLongList 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 (count == 0) + { + if (startIndex != -1) + { + ThrowHelper.ThrowArgumentOutOfRangeException_Index(); + } - //if (startIndex < 0) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} + return RecyclableDefaults.ItemNotFoundIndex; + } + + if (count < 0 || startIndex - count + 1 < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException_Count(); + } - //int sourceItemsCount = count; // startIndex - count + 1 >= 0 ? count : startIndex + 1; - //if (sourceItemsCount == 0) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} + if (startIndex < 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; - // } - //} + int blockSizeMinus1 = list._blockSizeMinus1, + firstBlockIndex = startIndex >> list._blockSizePow2BitShift, + firstItemIndex = startIndex & blockSizeMinus1, + blockIndex = (startIndex + count) >> list._blockSizePow2BitShift, + itemIndex; - //return RecyclableDefaults.ItemNotFoundIndex; + ReadOnlySpan memoryBlocksSpan = new(list._memoryBlocks); + ReadOnlySpan sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = ((startIndex + count) & blockSizeMinus1) - 1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } - throw new NotImplementedException(); + while(blockIndex > firstBlockIndex) + { + sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = blockSizeMinus1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + blockIndex--; + } + + sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = blockSizeMinus1; itemIndex >= firstItemIndex; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; } public static int FindLastIndex(this RecyclableLongList list, int startIndex, Predicate match) { - //if (list._count == 0 || startIndex < 0) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} + if (list._longCount == 0) + { + if (startIndex != -1) + { + ThrowHelper.ThrowArgumentOutOfRangeException_Index(); + } - //ReadOnlySpan sourceSpan = list._memoryBlock; - //for (var itemIndex = startIndex; itemIndex >= 0; itemIndex--) - //{ - // if (match(sourceSpan[itemIndex])) - // { - // return itemIndex; - // } - //} + return RecyclableDefaults.ItemNotFoundIndex; + } + + if (startIndex < 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } - //return RecyclableDefaults.ItemNotFoundIndex; + int blockSizeMinus1 = list._blockSizeMinus1, + firstBlockIndex = startIndex >> list._blockSizePow2BitShift, + firstItemIndex = startIndex & blockSizeMinus1, + blockIndex = (int)(list._longCount >> list._blockSizePow2BitShift), + itemIndex; - throw new NotImplementedException(); + ReadOnlySpan memoryBlocksSpan = new(list._memoryBlocks); + ReadOnlySpan sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = list._nextItemIndex > 0 ? list._nextItemIndex - 1 : blockSizeMinus1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + for (; blockIndex > firstBlockIndex; blockIndex--) + { + sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = blockSizeMinus1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + } + + sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = blockSizeMinus1; itemIndex >= firstItemIndex; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + + return RecyclableDefaults.ItemNotFoundIndex; } public static int FindLastIndex(this RecyclableLongList list, Predicate match) { - //if (list._count == 0) - //{ - // return RecyclableDefaults.ItemNotFoundIndex; - //} + if (list._longCount == 0) + { + return RecyclableDefaults.ItemNotFoundIndex; + } - //ReadOnlySpan sourceSpan = list._memoryBlock; - //for (var itemIndex = list._count - 1; itemIndex >= 0; itemIndex--) - //{ - // if (match(sourceSpan[itemIndex])) - // { - // return itemIndex; - // } - //} + int blockSizeMinus1 = list._blockSizeMinus1; + ReadOnlySpan memoryBlocksSpan = new(list._memoryBlocks); - //return RecyclableDefaults.ItemNotFoundIndex; + ReadOnlySpan sourceSpan = memoryBlocksSpan[list._lastBlockWithData]; + int itemIndex; + for (itemIndex = list._nextItemIndex == 0 ? blockSizeMinus1 : list._nextItemIndex - 1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } - throw new NotImplementedException(); + for (var blockIndex = list._lastBlockWithData - 1; blockIndex >= 0; blockIndex--) + { + sourceSpan = memoryBlocksSpan[blockIndex]; + for (itemIndex = blockSizeMinus1; itemIndex >= 0; itemIndex--) + { + if (match(sourceSpan[itemIndex])) + { + return itemIndex; + } + } + } + + return RecyclableDefaults.ItemNotFoundIndex; } } }