Created
February 13, 2022 15:41
-
-
Save DenchiSoft/ddda0bc2f3ed43b36bf8888ba64eaa53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Collections.Generic; | |
| using System.Linq; | |
| /// <summary> | |
| /// Queue for calculating sliding averages. Not thread safe. | |
| /// </summary> | |
| public class SlidingWindowQueue | |
| { | |
| /// <summary> | |
| /// The queue wrapped by this class. | |
| /// </summary> | |
| private Queue<float> wrappedQueue = new Queue<float>(); | |
| /// <summary> | |
| /// True if the exact average has to be recalculated on next access. | |
| /// </summary> | |
| private bool exactAverageDirty; | |
| /// <summary> | |
| /// Backing field for capacity. | |
| /// </summary> | |
| private int _capacity; | |
| /// <summary> | |
| /// Window size. If the queue is bigger than this, old items will be dequeued. | |
| /// Will be at least 1. | |
| /// </summary> | |
| public int Capacity | |
| { | |
| get { return _capacity; } | |
| set { _capacity = value > 0 ? value : 1; } | |
| } | |
| /// <summary> | |
| /// The average of all queue items. May be not be as exact as AverageExact() due to | |
| /// floating point rounding errors. | |
| /// </summary> | |
| public float Average { get; private set; } | |
| /// <summary> | |
| /// Backing field for exact average. | |
| /// </summary> | |
| private float _averageExact; | |
| /// <summary> | |
| /// The exact average of all queue items. | |
| /// Calling this every frame for large queues (10k+ items) can be expensive. | |
| /// </summary> | |
| public float AverageExact | |
| { | |
| get | |
| { | |
| // Recalculate average only if queue was modified. | |
| if (exactAverageDirty) | |
| { | |
| _averageExact = wrappedQueue.Average(); | |
| exactAverageDirty = false; | |
| } | |
| return _averageExact; | |
| } | |
| } | |
| /// <summary> | |
| /// Constructor for SlidingWindowQueue. | |
| /// </summary> | |
| /// <param name="queueCapacity">The sliding window size.</param> | |
| public SlidingWindowQueue(int queueCapacity) | |
| { | |
| Capacity = queueCapacity; | |
| } | |
| /// <summary> | |
| /// Resets this SlidingWindowQueue by removing all elements. | |
| /// </summary> | |
| public void Reset() | |
| { | |
| wrappedQueue.Clear(); | |
| Average = 0; | |
| _averageExact = 0; | |
| exactAverageDirty = false; | |
| } | |
| /// <summary> | |
| /// Add value to queue and return average. | |
| /// </summary> | |
| /// <param name="newValue">The value to add.</param> | |
| /// <returns>The average of all values in queue.</returns> | |
| public float Enqueue(float newValue) | |
| { | |
| // Recalculate average including new value. | |
| int queueLenBeforeEnqueue = wrappedQueue.Count(); | |
| int queueLenAfterEnqueue = queueLenBeforeEnqueue + 1; | |
| wrappedQueue.Enqueue(newValue); | |
| float averageAfterEnqueue = (queueLenBeforeEnqueue * Average + newValue) / queueLenAfterEnqueue; | |
| // Get sum of dequeued elements. | |
| float dequeuedSum = 0; | |
| bool dequeuedItems = false; | |
| while (wrappedQueue.Count > _capacity) | |
| { | |
| dequeuedSum += wrappedQueue.Dequeue(); | |
| dequeuedItems = true; | |
| } | |
| // Recalculate average after removing old elements. | |
| Average = dequeuedItems ? (queueLenAfterEnqueue * averageAfterEnqueue - dequeuedSum) / wrappedQueue.Count() : averageAfterEnqueue; | |
| // Set exact average dirty so it's recalculated on next access. | |
| exactAverageDirty = true; | |
| return Average; | |
| } | |
| /// <summary> | |
| /// Add value to queue and return average. | |
| /// </summary> | |
| /// <param name="newValue">The value to add.</param> | |
| /// <returns>The average of all values in queue.</returns> | |
| private float Enqueue(double newValue) => Enqueue((float) newValue); | |
| /// <summary> | |
| /// Add value to queue and return average. | |
| /// </summary> | |
| /// <param name="newValue">The value to add.</param> | |
| /// <returns>The average of all values in queue.</returns> | |
| private float Enqueue(int newValue) => Enqueue((float) newValue); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment