// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Collections; using System.Collections.Generic; namespace osu.Game.Rulesets.Difficulty.Utils { /// /// An indexed queue where items are indexed beginning from the most recently enqueued item. /// Enqueuing an item pushes all existing indexes up by one and inserts the item at index 0. /// Dequeuing an item removes the item from the highest index and returns it. /// public class ReverseQueue : IEnumerable { /// /// The number of elements in the . /// public int Count { get; private set; } private T[] items; private int capacity; private int start; public ReverseQueue(int initialCapacity) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialCapacity); items = new T[initialCapacity]; capacity = initialCapacity; start = 0; Count = 0; } /// /// Retrieves the item at an index in the . /// /// The index of the item to retrieve. The most recently enqueued item is at index 0. public T this[int index] { get { if (index < 0 || index > Count - 1) throw new ArgumentOutOfRangeException(nameof(index)); int reverseIndex = Count - 1 - index; return items[(start + reverseIndex) % capacity]; } } /// /// Enqueues an item to this . /// /// The item to enqueue. public void Enqueue(T item) { if (Count == capacity) { // Double the buffer size var buffer = new T[capacity * 2]; // Copy items to new queue for (int i = 0; i < Count; i++) { buffer[i] = items[(start + i) % capacity]; } // Replace array with new buffer items = buffer; capacity *= 2; start = 0; } items[(start + Count) % capacity] = item; Count++; } /// /// Dequeues the least recently enqueued item from the and returns it. /// /// The item dequeued from the . public T Dequeue() { var item = items[start]; start = (start + 1) % capacity; Count--; return item; } /// /// Clears the of all items. /// public void Clear() { start = 0; Count = 0; } /// /// Returns an enumerator which enumerates items in the starting from the most recently enqueued item. /// public IEnumerator GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator : IEnumerator { private ReverseQueue reverseQueue; private int currentIndex; internal Enumerator(ReverseQueue reverseQueue) { this.reverseQueue = reverseQueue; currentIndex = -1; // The first MoveNext() should bring the iterator to 0 } public bool MoveNext() => ++currentIndex < reverseQueue.Count; public void Reset() => currentIndex = -1; public readonly T Current => reverseQueue[currentIndex]; readonly object IEnumerator.Current => Current; public void Dispose() { reverseQueue = null; } } } }