diff --git a/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs b/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs index 3346bc5..f406bb5 100644 --- a/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs +++ b/Recyclable.Collections/RecyclablePriorityQueue.AddRange.cs @@ -448,6 +448,352 @@ namespace Recyclable.Collections } queue._size = newCount; + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableQueue items) + { + long longCount = items._count; + if (longCount == 0) + { + return; + } + + int count = checked((int)longCount); + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + var sourceChunk = items._head; + int destIndex = startIndex; + while (sourceChunk != null) + { + int sourceIndex = sourceChunk.Bottom; + int sourceTop = sourceChunk.Top; + T[] source = sourceChunk.Value; + + while (sourceIndex < sourceTop) + { + queue._heap[destIndex++] = source[sourceIndex++]; + } + + sourceChunk = sourceChunk.Next; + } + + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableStack items) + { + long longCount = items._count; + if (longCount == 0) + { + return; + } + + int count = checked((int)longCount); + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + var sourceChunk = items._current; + while (sourceChunk.Previous != null) + { + sourceChunk = sourceChunk.Previous; + } + + int destIndex = startIndex; + while (sourceChunk != null) + { + T[] source = sourceChunk.Value; + int sourceCount = sourceChunk.Index; + int sourceIndex = 0; + + while (sourceIndex < sourceCount) + { + queue._heap[destIndex++] = source[sourceIndex++]; + } + + sourceChunk = sourceChunk.Next; + } + + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableLinkedList items) + { + long longCount = items._count; + if (longCount == 0) + { + return; + } + + int count = checked((int)longCount); + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + var sourceChunk = items._head; + int destIndex = startIndex; + while (sourceChunk != null) + { + int sourceIndex = sourceChunk.Bottom; + int sourceTop = sourceChunk.Top; + T[] source = sourceChunk.Value; + + while (sourceIndex < sourceTop) + { + queue._heap[destIndex++] = source[sourceIndex++]; + } + + sourceChunk = sourceChunk.Next; + } + + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableSortedSet items) + { + int count = items._count; + if (count == 0) + { + return; + } + + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + T[] source = items._items; + for (int i = 0; i < count; i++) + { + queue._heap[startIndex + i] = source[i]; + } + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableHashSet items) + { + int count = items._count; + if (count == 0) + { + return; + } + + int startIndex = queue._size; + int newCount = startIndex + count; + EnsureCapacity(queue, newCount); + + var entries = items._entries; + int shift = items._blockShift, + mask = items._blockSizeMinus1; + + for (int i = 0; i < count; i++) + { + queue._heap[startIndex + i] = entries[i >> shift][i & mask].Value; + } + queue._size = newCount; + + int half = newCount >> 1; + for (int i = half - 1; i >= 0; i--) + { + int index = i; + T item = queue._heap[index]; + while (index < half) + { + int child = (index << 1) + 1; + int right = child + 1; + if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0) + { + child = right; + } + + if (queue._comparer.Compare(queue._heap[child], item) >= 0) + { + break; + } + + queue._heap[index] = queue._heap[child]; + index = child; + } + + queue._heap[index] = item; + } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + internal static void AddRange(this RecyclablePriorityQueue queue, RecyclableLongList items) + { + long longCount = items._longCount; + if (longCount == 0) + { + return; + } + + int blockSize = items._blockSize; + int fullBlocks = (int)(longCount >> items._blockSizePow2BitShift); + int lastBlockLength = (int)(longCount & items._blockSizeMinus1); + + int startIndex = queue._size; + int newCount = startIndex + checked((int)longCount); + EnsureCapacity(queue, newCount); + + int destIndex = startIndex; + for (int blockIndex = 0; blockIndex < fullBlocks; blockIndex++) + { + T[] block = items._memoryBlocks[blockIndex]; + int sourceIndex = 0; + while (sourceIndex < blockSize) + { + queue._heap[destIndex++] = block[sourceIndex++]; + } + } + + if (lastBlockLength > 0) + { + T[] block = items._memoryBlocks[fullBlocks]; + int sourceIndex = 0; + while (sourceIndex < lastBlockLength) + { + queue._heap[destIndex++] = block[sourceIndex++]; + } + } + + queue._size = newCount; + int half = newCount >> 1; for (int i = half - 1; i >= 0; i--) { @@ -532,6 +878,30 @@ namespace Recyclable.Collections { AddRange(queue, priorityQueue); } + else if (items is RecyclableQueue recyclableQueue) + { + AddRange(queue, recyclableQueue); + } + else if (items is RecyclableStack recyclableStack) + { + AddRange(queue, recyclableStack); + } + else if (items is RecyclableLinkedList linkedList) + { + AddRange(queue, linkedList); + } + else if (items is RecyclableSortedSet sortedSet) + { + AddRange(queue, sortedSet); + } + else if (items is RecyclableHashSet hashSet) + { + AddRange(queue, hashSet); + } + else if (items is RecyclableLongList longList) + { + AddRange(queue, longList); + } else if (items is T[] array) { AddRange(queue, array); diff --git a/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs b/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs index ac92e46..15903c3 100644 --- a/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs +++ b/Recyclable.CollectionsTests/RecyclablePriorityQueueTests.cs @@ -148,6 +148,81 @@ namespace Recyclable.CollectionsTests _ = queue.Should().HaveCount(2).And.Contain((long?)null); } + [Fact] + public void AddRangeQueueShouldAddItemsInSortedOrder() + { + using var source = new RecyclableQueue(_testData); + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(_testData.Order()); + } + + [Fact] + public void AddRangeQueueShouldNotOverrideItems() + { + using var source = new RecyclableQueue(_testData); + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + queue.AddRange(source); + + var expected = _testData.Concat(_testData).Order().ToArray(); + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(expected); + } + + [Fact] + public void AddRangeStackShouldAddItemsInSortedOrder() + { + using var source = new RecyclableStack(_testData); + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(_testData.Order()); + } + + [Fact] + public void AddRangeSortedSetShouldAddItemsInSortedOrder() + { + using var source = new RecyclableSortedSet(); + foreach (int item in _testData) + { + source.Add(item); + } + + using var queue = new RecyclablePriorityQueue(); + + queue.AddRange(source); + + var result = new List(); + while (queue.LongCount > 0) + { + result.Add(queue.Dequeue()); + } + + _ = result.Should().Equal(_testData.Order()); + } + } }