// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections; using System.Collections.Generic; namespace osu.Game.Rulesets.Difficulty.Utils { /// /// An indexed stack with limited depth. Indexing starts at the top of the stack. /// public class LimitedCapacityStack : IEnumerable { /// /// The number of elements in the stack. /// public int Count { get; private set; } private readonly T[] array; private readonly int capacity; private int marker; // Marks the position of the most recently added item. /// /// Constructs a new . /// /// The number of items the stack can hold. public LimitedCapacityStack(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); this.capacity = capacity; array = new T[capacity]; marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. } /// /// Retrieves the item at an index in the stack. /// /// The index of the item to retrieve. The top of the stack is returned at index 0. public T this[int i] { get { if (i < 0 || i > Count - 1) throw new ArgumentOutOfRangeException(nameof(i)); i += marker; if (i > capacity - 1) i -= capacity; return array[i]; } } /// /// Pushes an item to this . /// /// The item to push. public void Push(T item) { // Overwrite the oldest item instead of shifting every item by one with every addition. if (marker == 0) marker = capacity - 1; else --marker; array[marker] = item; if (Count < capacity) ++Count; } /// /// Returns an enumerator which enumerates items in the history starting from the most recently added one. /// public IEnumerator GetEnumerator() { for (int i = marker; i < capacity; ++i) yield return array[i]; if (Count == capacity) { for (int i = 0; i < marker; ++i) yield return array[i]; } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } }