| | 1 | | using MoreLinq.Extensions; |
| | 2 | |
|
| | 3 | | namespace MoreStructures.PriorityQueues.BinaryHeap; |
| | 4 | |
|
| | 5 | | /// <summary> |
| | 6 | | /// A wrapper around a <see cref="IList{T}"/>, which preserve the max heap property on the subset of items of the list, |
| | 7 | | /// at the beginning or at the end of it. |
| | 8 | | /// </summary> |
| | 9 | | /// <typeparam name="T">The type of items in the wrapped list.</typeparam> |
| | 10 | | /// <remarks> |
| | 11 | | /// Heap items are either stored: |
| | 12 | | /// <br/> |
| | 13 | | /// - from index 0 to <see cref="HeapCount"/> - 1, i.e. at the beginning of the list, |
| | 14 | | /// <br/> |
| | 15 | | /// - or from index <see cref="ListCount"/> - <see cref="HeapCount"/> to <see cref="ListCount"/> - 1, i.e. at the end. |
| | 16 | | /// <br/> |
| | 17 | | /// That leaves a buffer of <see cref="ListCount"/> - <see cref="HeapCount"/> items, either at the end or at the |
| | 18 | | /// beginning of the list. |
| | 19 | | /// <br/> |
| | 20 | | /// The functionalities of this wrapper can be used to support HeapSort or a priority queue based on a Max Binary Heap. |
| | 21 | | /// <br/> |
| | 22 | | /// In the first case the buffer area is used (to store already sorted items). In the second case it is not. |
| | 23 | | /// </remarks> |
| | 24 | | public sealed class BinaryHeapListWrapper<T> |
| | 25 | | { |
| 535390 | 26 | | private IList<T> Items { get; } |
| 184279 | 27 | | private IComparer<T> Comparer { get; } |
| 433747 | 28 | | private bool StoreHeapAtTheEnd { get; } |
| 214093 | 29 | | private int IndexDelta { get; } |
| | 30 | |
|
| | 31 | | /// <summary> |
| | 32 | | /// The number of items in the heap only, buffer area of the underlying list excluded. |
| | 33 | | /// </summary> |
| | 34 | | /// <remarks> |
| | 35 | | /// May be smaller than the current <see cref="ICollection{T}.Count"/> of the underlying <see cref="IList{T}"/>, |
| | 36 | | /// which in turn may be smaller than the current Length of the underlying <see cref="Array"/>. |
| | 37 | | /// </remarks> |
| 270117 | 38 | | public int HeapCount { get; private set; } |
| | 39 | |
|
| | 40 | | /// <summary> |
| | 41 | | /// Invoked just after an item has been pushed into <see cref="Items"/> (at the end of the heap), and before the |
| | 42 | | /// "sifting up" procedure is performed. |
| | 43 | | /// </summary> |
| | 44 | | /// <remarks> |
| | 45 | | /// The actual index of the item pushed will be different than <see cref="ListCount"/> - 1, if |
| | 46 | | /// <see cref="ListCount"/> is different than <see cref="HeapCount"/>, or if the heap is stored in reverse. |
| | 47 | | /// </remarks> |
| 30445 | 48 | | public Action<int> RaiseItemPushed { get; init; } = i => { }; |
| | 49 | |
|
| | 50 | | /// <summary> |
| | 51 | | /// Invoked just before an item is removed from <see cref="Items"/> (at the beginning of the heap), and before |
| | 52 | | /// "sifting down" procedure is performed. |
| | 53 | | /// </summary> |
| | 54 | | /// <remarks> |
| | 55 | | /// The actual index of the item pushed may be different than 0, if the heap is stored in reverse, in which case |
| | 56 | | /// it is equal to <see cref="ListCount"/> - 1. |
| | 57 | | /// <br/> |
| | 58 | | /// Same applies to the index of the item pushed out of the heap (but still in the list, in the buffer area). |
| | 59 | | /// </remarks> |
| 24660 | 60 | | public Action<int, int> RaiseItemPopping { get; init; } = (i1, i2) => { }; |
| | 61 | |
|
| | 62 | | /// <summary> |
| | 63 | | /// Invoked just after two items have been swapped of position in <see cref="Items"/>. |
| | 64 | | /// </summary> |
| 77141 | 65 | | public Action<int, int> RaiseItemsSwapped { get; init; } = (i1, i2) => { }; |
| | 66 | |
|
| | 67 | |
|
| | 68 | | /// <summary> |
| | 69 | | /// <inheritdoc cref="BinaryHeapListWrapper{T}"/> |
| | 70 | | /// <br/> |
| | 71 | | /// Built around the provided <see cref="IList{T}"/> <paramref name="items"/>. |
| | 72 | | /// </summary> |
| | 73 | | /// <param name="items">The <see cref="IList{T}"/> of items to be wrapped.</param> |
| | 74 | | /// <param name="comparer">The comparer to be used to establish a order relationship between items.</param> |
| | 75 | | /// <param name="count"> |
| | 76 | | /// The size of the subset of <paramref name="items"/> to be kept in order according to the max heap property. |
| | 77 | | /// <br/> |
| | 78 | | /// Goes from index 0 to index <paramref name="count"/> - 1. |
| | 79 | | /// <br/> |
| | 80 | | /// Current size must be non-bigger than the <see cref="ICollection{T}.Count"/>. |
| | 81 | | /// </param> |
| | 82 | | /// <param name="storeHeapAtTheEnd"> |
| | 83 | | /// Whether heap items should be stored at the beginning or at the end of the <paramref name="items"/> list. |
| | 84 | | /// <br/> |
| | 85 | | /// By default heap items are stored at the beginning (root at index 0) and buffer are is at the end. |
| | 86 | | /// </param> |
| | 87 | | /// <param name="indexDelta"> |
| | 88 | | /// The delta, positive or negative, with which heap items will be stored in the <paramref name="items"/> list, |
| | 89 | | /// w.r.t. the beginning (if <paramref name="storeHeapAtTheEnd"/> is <see langword="false"/>) or the end |
| | 90 | | /// (if <paramref name="storeHeapAtTheEnd"/> is <see langword="true"/>) of the list. |
| | 91 | | /// <br/> |
| | 92 | | /// By default it is zero (i.e. no displacement). |
| | 93 | | /// </param> |
| 6007 | 94 | | public BinaryHeapListWrapper( |
| 6007 | 95 | | IList<T> items, IComparer<T> comparer, int count, bool storeHeapAtTheEnd = false, int indexDelta = 0) |
| 6007 | 96 | | { |
| 6007 | 97 | | if (indexDelta < 0) |
| 2 | 98 | | throw new ArgumentException( |
| 2 | 99 | | $"Must be non-negative.", nameof(indexDelta)); |
| 6005 | 100 | | if (count < 0 || count + indexDelta > items.Count) |
| 86 | 101 | | throw new ArgumentException( |
| 86 | 102 | | $"Must be non-negative and at most size of {nameof(items)} - {nameof(indexDelta)}.", nameof(items)); |
| | 103 | |
|
| 5919 | 104 | | Items = items; |
| 5919 | 105 | | Comparer = comparer; |
| 5919 | 106 | | HeapCount = count; |
| 5919 | 107 | | StoreHeapAtTheEnd = storeHeapAtTheEnd; |
| 5919 | 108 | | IndexDelta = indexDelta; |
| | 109 | |
|
| 5919 | 110 | | RestoreHeapProperty(); |
| 5919 | 111 | | } |
| | 112 | |
|
| | 113 | | /// <summary> |
| | 114 | | /// <inheritdoc cref="BinaryHeapListWrapper{T}"/> |
| | 115 | | /// <br/> |
| | 116 | | /// Built from the provided <see cref="BinaryHeapListWrapper{T}"/> <paramref name="source"/>. |
| | 117 | | /// </summary> |
| 252 | 118 | | public BinaryHeapListWrapper(BinaryHeapListWrapper<T> source) |
| 252 | 119 | | { |
| 252 | 120 | | Items = new List<T>(source.Items); |
| 252 | 121 | | Comparer = source.Comparer; |
| 252 | 122 | | HeapCount = source.HeapCount; |
| 252 | 123 | | StoreHeapAtTheEnd = source.StoreHeapAtTheEnd; |
| 252 | 124 | | IndexDelta = source.IndexDelta; |
| | 125 | |
|
| 252 | 126 | | RestoreHeapProperty(); |
| 252 | 127 | | } |
| | 128 | |
|
| | 129 | |
|
| | 130 | | /// <summary> |
| | 131 | | /// Restores the max heap property, ensuring that each node of the heap is at least as big as its children, if any. |
| | 132 | | /// </summary> |
| | 133 | | public void RestoreHeapProperty() |
| 6407 | 134 | | { |
| 6407 | 135 | | var rootIndex = RootIndex(); |
| 6407 | 136 | | var lastLeafIndex = LastLeafIndex(); |
| 6407 | 137 | | if (StoreHeapAtTheEnd) |
| 175 | 138 | | { |
| 175 | 139 | | var halfHeapIndex = Math.Max(lastLeafIndex, rootIndex - HeapCount / 2 - 1); |
| 1402 | 140 | | for (var i = halfHeapIndex; i <= rootIndex; i++) |
| 526 | 141 | | SiftDown(i); |
| 175 | 142 | | } |
| | 143 | | else |
| 6232 | 144 | | { |
| 6232 | 145 | | var halfHeapIndex = Math.Min(lastLeafIndex, rootIndex + HeapCount / 2 + 1); |
| 26556 | 146 | | for (var i = halfHeapIndex; i >= rootIndex; i--) |
| 7046 | 147 | | SiftDown(i); |
| 6232 | 148 | | } |
| 6407 | 149 | | } |
| | 150 | |
|
| | 151 | | /// <summary> |
| | 152 | | /// Peeks the item with max priority from the root of the heap, if any. |
| | 153 | | /// </summary> |
| | 154 | | public T Peek() |
| 10277 | 155 | | { |
| 10277 | 156 | | if (HeapCount == 0) |
| 6 | 157 | | throw new InvalidOperationException($"Can't {nameof(Peek)} on an empty heap."); |
| 10271 | 158 | | return Items[RootIndex()]; |
| 10271 | 159 | | } |
| | 160 | |
|
| | 161 | | /// <summary> |
| | 162 | | /// Pops the item with max priority from the root of the heap, if any, moving the last leaf to the now vacant root |
| | 163 | | /// and sifting it down until the heap property is restored. |
| | 164 | | /// </summary> |
| | 165 | | /// <returns>The popped item.</returns> |
| | 166 | | public T Pop() |
| 9810 | 167 | | { |
| 9810 | 168 | | if (HeapCount == 0) |
| 6 | 169 | | throw new InvalidOperationException($"Can't {nameof(Pop)} on an empty heap."); |
| | 170 | |
|
| 9804 | 171 | | var rootHeapIndex = RootIndex(); |
| 9804 | 172 | | var lastHeapIndex = LastLeafIndex(); |
| | 173 | |
|
| 9804 | 174 | | RaiseItemPopping(rootHeapIndex, lastHeapIndex); |
| 9804 | 175 | | var result = Peek(); |
| | 176 | |
|
| 9804 | 177 | | (Items[rootHeapIndex], Items[lastHeapIndex]) = (Items[lastHeapIndex], Items[rootHeapIndex]); |
| 9804 | 178 | | HeapCount--; |
| | 179 | |
|
| 9804 | 180 | | if (HeapCount > 0) |
| 9212 | 181 | | SiftDown(rootHeapIndex); |
| 9804 | 182 | | return result; |
| 9804 | 183 | | } |
| | 184 | |
|
| | 185 | | /// <summary> |
| | 186 | | /// Pushes the provided <paramref name="item"/> into the heap, sifting it up until the max heap property is |
| | 187 | | /// restored. |
| | 188 | | /// </summary> |
| | 189 | | /// <param name="item">The item to be added to the heap.</param> |
| | 190 | | /// <param name="siftUp"> |
| | 191 | | /// Whether the sift up procedure should be executed or not. By default it is set to <see langword="true"/>. |
| | 192 | | /// <br/> |
| | 193 | | /// If it is not executed, the max heap property will be temporary violated. |
| | 194 | | /// <br/> |
| | 195 | | /// The push may fail if the heap is stored at the back of the list and grows backwards (which is the layout |
| | 196 | | /// applied when <see cref="StoreHeapAtTheEnd"/> is set to <see langword="true"/>), if the buffer is empty |
| | 197 | | /// (i.e. if the last leaf of the heap has reached the front of the list, and adding a new leaf would require |
| | 198 | | /// adding an element before the position 0 of the list). |
| | 199 | | /// <br/> |
| | 200 | | /// The push never fails when the heap is stored at the front of the list and grows forwards, because the list |
| | 201 | | /// is able to grow at the back. |
| | 202 | | /// </param> |
| | 203 | | public void Push(T item, bool siftUp = true) |
| 16837 | 204 | | { |
| 16837 | 205 | | int index = NextLeafIndex(); |
| 16837 | 206 | | if (index >= 0 && index < ListCount) |
| 817 | 207 | | { |
| 817 | 208 | | Items[index] = item; |
| 817 | 209 | | } |
| | 210 | | else |
| 16020 | 211 | | { |
| 16020 | 212 | | if (StoreHeapAtTheEnd) |
| 66 | 213 | | throw new InvalidOperationException( |
| 66 | 214 | | $"Cannot {nameof(Push)} on a heap which is stored at the end, when the buffer empty."); |
| | 215 | |
|
| 15954 | 216 | | Items.Add(item); |
| 15954 | 217 | | } |
| 16771 | 218 | | HeapCount++; |
| 16771 | 219 | | RaiseItemPushed(index); |
| | 220 | |
|
| 16771 | 221 | | if (siftUp) |
| 14141 | 222 | | SiftUp(index); |
| 16771 | 223 | | } |
| | 224 | |
|
| | 225 | | /// <summary> |
| | 226 | | /// Retrieves the item of the heap with priority <paramref name="k"/>, without extracting any of the items in the |
| | 227 | | /// heap. |
| | 228 | | /// </summary> |
| | 229 | | /// <param name="k">The non-negative priority rank: 0 means highest priority, 1 second highest, etc.</param> |
| | 230 | | /// <returns> |
| | 231 | | /// The item with k-th highest priority if any, with <see langword="true"/> as valid flag. |
| | 232 | | /// <br/> |
| | 233 | | /// The default for <typeparamref name="T"/> otherwise, with <see langword="false"/> as valid flag. |
| | 234 | | /// </returns> |
| | 235 | | public (T? result, bool valid) PeekKth(int k) |
| 48 | 236 | | { |
| 50 | 237 | | if (k < 0) throw new ArgumentException("Must be non-negative.", nameof(k)); |
| 54 | 238 | | if (k >= HeapCount) return (default, false); |
| 47 | 239 | | if (k == 0) return (Peek(), true); |
| | 240 | |
|
| 29 | 241 | | var candidates = new BinaryHeapListWrapper<(int, T)>(new List<(int, T)>(), new Item2Comparer(Comparer), 0); |
| 29 | 242 | | var rootIndex = RootIndex(); |
| 29 | 243 | | candidates.Push((rootIndex, Items[rootIndex])); |
| 142 | 244 | | while (k > 0) |
| 113 | 245 | | { |
| 113 | 246 | | var (maxIndex, _) = candidates.Pop(); |
| | 247 | |
|
| 113 | 248 | | var leftChildIndex = LeftChildOf(maxIndex); |
| 113 | 249 | | if (leftChildIndex >= 0) |
| 87 | 250 | | candidates.Push((leftChildIndex, Items[leftChildIndex])); |
| | 251 | |
|
| 113 | 252 | | var rightChildIndex = RightChildOf(maxIndex); |
| 113 | 253 | | if (rightChildIndex >= 0) |
| 75 | 254 | | candidates.Push((rightChildIndex, Items[rightChildIndex])); |
| | 255 | |
|
| 113 | 256 | | k--; |
| 113 | 257 | | } |
| | 258 | |
|
| 29 | 259 | | return (Items[candidates.Peek().Item1], true); |
| 46 | 260 | | } |
| | 261 | |
|
| | 262 | | /// <summary> |
| | 263 | | /// Pops all items of the heap in sequence, from the one with highest priority to the one with lowest priority. |
| | 264 | | /// </summary> |
| | 265 | | /// <returns>A sequence of <typeparamref name="T"/> instances.</returns> |
| | 266 | | public IEnumerable<T> PopAll() |
| 134 | 267 | | { |
| 1094 | 268 | | while (HeapCount > 0) |
| 960 | 269 | | yield return Pop(); |
| 134 | 270 | | } |
| | 271 | |
|
| 312 | 272 | | private sealed record Item2Comparer(IComparer<T> Comparer) : IComparer<(int, T)> |
| 29 | 273 | | { |
| 254 | 274 | | public int Compare((int, T) x, (int, T) y) => Comparer.Compare(x.Item2, y.Item2); |
| 29 | 275 | | } |
| | 276 | |
|
| | 277 | | /// <summary> |
| | 278 | | /// Restores the heap constraint on the item at the specified <paramref name="nodeIndex"/> w.r.t. its ancestors in |
| | 279 | | /// the tree. |
| | 280 | | /// </summary> |
| | 281 | | /// <param name="nodeIndex">The index of the item to check.</param> |
| | 282 | | internal void SiftUp(int nodeIndex) |
| 22451 | 283 | | { |
| 22451 | 284 | | var parentIndex = ParentOf(nodeIndex); |
| | 285 | |
|
| | 286 | | // If the node doesn't have a parent, it means we reached the root of the tree, so there is nothing to sift up. |
| 22451 | 287 | | if (parentIndex < 0) |
| 6940 | 288 | | return; |
| | 289 | |
|
| 15511 | 290 | | var parentValue = Items[parentIndex]; |
| 15511 | 291 | | var nodeValue = Items[nodeIndex]; |
| 15511 | 292 | | if (Comparer.Compare(parentValue, nodeValue) < 0) |
| 8169 | 293 | | { |
| 8169 | 294 | | Items[parentIndex] = nodeValue; |
| 8169 | 295 | | Items[nodeIndex] = parentValue; |
| 8169 | 296 | | RaiseItemsSwapped(parentIndex, nodeIndex); |
| | 297 | |
|
| 8169 | 298 | | SiftUp(parentIndex); |
| 8169 | 299 | | } |
| 22451 | 300 | | } |
| | 301 | |
|
| | 302 | | /// <summary> |
| | 303 | | /// Restores the heap constraint on the item at the specified <paramref name="nodeIndex"/> w.r.t. its descendants |
| | 304 | | /// in the tree. |
| | 305 | | /// </summary> |
| | 306 | | /// <param name="nodeIndex">The index of the item to check.</param> |
| | 307 | | private void SiftDown(int nodeIndex) |
| 70720 | 308 | | { |
| 70720 | 309 | | var leftChildIndex = LeftChildOf(nodeIndex); |
| | 310 | |
|
| | 311 | | // If the node doesn't have a left child, it definitely has no right child, since the tree is complete. |
| | 312 | | // Therefore the node is a leaf and there is nothing to sift down. |
| 70720 | 313 | | if (leftChildIndex < 0) |
| 9835 | 314 | | return; |
| | 315 | |
|
| 60885 | 316 | | var leftChildValue = Items[leftChildIndex]; |
| | 317 | |
|
| 60885 | 318 | | var rightChildIndex = RightChildOf(nodeIndex); |
| | 319 | |
|
| | 320 | | // Cases where heap property is respected: node > left > right, node > right > left |
| | 321 | | // Cases where heap property has to be restored: |
| | 322 | | // - left > node and no right or left >= right => left becomes the new parent of node and right |
| | 323 | | // - right > node and right > left => right becomes the new parent of left and node |
| 60885 | 324 | | var nodeValue = Items[nodeIndex]; |
| 60885 | 325 | | if (Comparer.Compare(leftChildValue, nodeValue) > 0 && |
| 60885 | 326 | | (rightChildIndex < 0 || Comparer.Compare(leftChildValue, Items[rightChildIndex]) >= 0)) |
| 28873 | 327 | | { |
| 28873 | 328 | | Items[nodeIndex] = leftChildValue; |
| 28873 | 329 | | Items[leftChildIndex] = nodeValue; |
| 28873 | 330 | | RaiseItemsSwapped(nodeIndex, leftChildIndex); |
| | 331 | |
|
| 28873 | 332 | | SiftDown(leftChildIndex); |
| 28873 | 333 | | } |
| 32012 | 334 | | else if (rightChildIndex >= 0 && |
| 32012 | 335 | | Comparer.Compare(Items[rightChildIndex], nodeValue) > 0 && |
| 32012 | 336 | | Comparer.Compare(Items[rightChildIndex], leftChildValue) > 0) |
| 25063 | 337 | | { |
| 25063 | 338 | | Items[nodeIndex] = Items[rightChildIndex]; |
| 25063 | 339 | | Items[rightChildIndex] = nodeValue; |
| 25063 | 340 | | RaiseItemsSwapped(nodeIndex, rightChildIndex); |
| | 341 | |
|
| 25063 | 342 | | SiftDown(rightChildIndex); |
| 25063 | 343 | | } |
| 70720 | 344 | | } |
| | 345 | |
|
| 213841 | 346 | | private int RootIndex() => StoreHeapAtTheEnd ? ListCount - 1 - IndexDelta : IndexDelta; |
| 33048 | 347 | | private int LastLeafIndex() => RootIndex() + (StoreHeapAtTheEnd ? 1 - HeapCount : HeapCount - 1); |
| 16837 | 348 | | private int NextLeafIndex() => LastLeafIndex() + (StoreHeapAtTheEnd ? -1 : +1); |
| | 349 | |
|
| | 350 | | private int ParentOf(int nodeIndex) |
| 22451 | 351 | | { |
| 22451 | 352 | | var rootIndex = RootIndex(); |
| 29391 | 353 | | if (nodeIndex == rootIndex) return -1; |
| | 354 | |
|
| 15511 | 355 | | if (StoreHeapAtTheEnd) |
| 232 | 356 | | return rootIndex - (rootIndex - nodeIndex - 1) / 2; |
| 15279 | 357 | | return rootIndex + (nodeIndex - rootIndex - 1) / 2; |
| 22451 | 358 | | } |
| | 359 | |
|
| | 360 | | private int LeftChildOf(int nodeIndex) |
| 70833 | 361 | | { |
| 70833 | 362 | | var rootIndex = RootIndex(); |
| | 363 | | int childIndex; |
| 70833 | 364 | | if (StoreHeapAtTheEnd) |
| 1685 | 365 | | { |
| 1685 | 366 | | childIndex = rootIndex - 2 * (rootIndex - nodeIndex) - 1; |
| 1685 | 367 | | return childIndex > rootIndex - HeapCount ? childIndex : -1; |
| | 368 | | } |
| | 369 | |
|
| 69148 | 370 | | childIndex = rootIndex + 2 * (nodeIndex - rootIndex) + 1; |
| 69148 | 371 | | return childIndex < rootIndex + HeapCount ? childIndex : -1; |
| 70833 | 372 | | } |
| | 373 | |
|
| | 374 | | private int RightChildOf(int nodeIndex) |
| 60998 | 375 | | { |
| 60998 | 376 | | var rootIndex = RootIndex(); |
| | 377 | | int childIndex; |
| 60998 | 378 | | if (StoreHeapAtTheEnd) |
| 1008 | 379 | | { |
| 1008 | 380 | | childIndex = rootIndex - 2 * (rootIndex - nodeIndex) - 2; |
| 1008 | 381 | | return childIndex > rootIndex - HeapCount ? childIndex : -1; |
| | 382 | | } |
| | 383 | |
|
| 59990 | 384 | | childIndex = rootIndex + 2 * (nodeIndex - rootIndex) + 2; |
| 59990 | 385 | | return childIndex < rootIndex + HeapCount ? childIndex : -1; |
| 60998 | 386 | | } |
| | 387 | |
|
| | 388 | | #region Access to Underlying List<T> |
| | 389 | |
|
| | 390 | | /// <summary> |
| | 391 | | /// The number of items in the underlying list, heap and buffer are included. |
| | 392 | | /// </summary> |
| | 393 | | /// <remarks> |
| | 394 | | /// It's always non-smaller than <see cref="HeapCount"/>, since all the heap items are contained in the underlying |
| | 395 | | /// list. |
| | 396 | | /// </remarks> |
| 23149 | 397 | | public int ListCount => Items.Count; |
| | 398 | |
|
| | 399 | | /// <summary> |
| | 400 | | /// Retrieves the <paramref name="index"/>-th item in the underlying list (heap or buffer). |
| | 401 | | /// </summary> |
| | 402 | | /// <param name="index"> |
| | 403 | | /// A non-negative <see cref="int"/>. It can be non-smaller than <see cref="HeapCount"/>, if an element of the |
| | 404 | | /// buffer is being accessed, but it necessarily has to be smaller than the <see cref="ICollection{T}.Count"/> of |
| | 405 | | /// the underlying <see cref="IList{T}"/>. |
| | 406 | | /// </param> |
| | 407 | | /// <returns>The item, an instance of type <typeparamref name="T"/>.</returns> |
| | 408 | | public T this[int index] |
| | 409 | | { |
| 34573 | 410 | | get => Items[index]; |
| 337 | 411 | | set => Items[index] = value; |
| | 412 | | } |
| | 413 | |
|
| | 414 | | /// <summary> |
| | 415 | | /// Clears the underlying list (heap and buffer), wiping all its items out, if the list is not readonly. |
| | 416 | | /// <br/> |
| | 417 | | /// If it is readonly (e.g. an array), it just resets the <see cref="HeapCount"/>. |
| | 418 | | /// </summary> |
| | 419 | | public void Clear() |
| 349 | 420 | | { |
| 349 | 421 | | if (!Items.IsReadOnly) |
| 347 | 422 | | Items.Clear(); |
| 349 | 423 | | HeapCount = 0; |
| 349 | 424 | | } |
| | 425 | |
|
| | 426 | | /// <summary> |
| | 427 | | /// Returns an enumerator of <typeparamref name="T"/> instances, going through all items of the underlying list, |
| | 428 | | /// not just the first <see cref="HeapCount"/> items which form the heap, but also the buffer area at the end of the |
| | 429 | | /// underlying list. |
| | 430 | | /// </summary> |
| | 431 | | /// <returns>An <see cref="IEnumerable{T}"/> of <typeparamref name="T"/>.</returns> |
| 238 | 432 | | public IEnumerator<T> GetEnumerator() => Items.GetEnumerator(); |
| | 433 | |
|
| | 434 | | #endregion |
| | 435 | | } |