using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace ZeroLevel.HNSW { /// /// Binary heap wrapper around the /// It's a max-heap implementation i.e. the maximum element is always on top. /// /// The type of the items in the source list. public class BinaryHeap : IEnumerable<(int, float)> { private static BinaryHeap _empty = new BinaryHeap(); public static BinaryHeap Empty => _empty; private readonly List<(int, float)> _data; private bool _frozen = false; public (int, float) Nearest => _data[_data.Count - 1]; public (int, float) Farthest => _data[0]; public (int, float) PopNearest() { if (this._data.Any()) { var result = this._data[this._data.Count - 1]; this._data.RemoveAt(this._data.Count - 1); return result; } return (-1, -1); } public (int, float) PopFarthest() { if (this._data.Any()) { var result = this._data.First(); this._data[0] = this._data.Last(); this._data.RemoveAt(this._data.Count - 1); this.SiftDown(0); return result; } return (-1, -1); } public int Count => _data.Count; public void Clear() => _data.Clear(); /// /// Initializes a new instance of the class. /// /// The buffer to store heap items. public BinaryHeap(int k = -1, bool frozen = false) { _frozen = frozen; if (k > 0) _data = new List<(int, float)>(k); else _data = new List<(int, float)>(); } /// /// Pushes item to the heap. /// /// The item to push. public void Push(int item, float distance) { this._data.Add((item, distance)); this.SiftUp(this._data.Count - 1); } /// /// Pops the item from the heap. /// /// The popped item. public (int, float) Pop() { if (this._data.Any()) { var result = this._data.First(); this._data[0] = this._data.Last(); this._data.RemoveAt(this._data.Count - 1); this.SiftDown(0); return result; } throw new InvalidOperationException("Heap is empty"); } /// /// Restores the heap property starting from i'th position down to the bottom /// given that the downstream items fulfill the rule. /// /// The position of item where heap property is violated. private void SiftDown(int i) { while (i < this._data.Count) { int l = (2 * i) + 1; int r = l + 1; if (l >= this._data.Count) { break; } int m = ((r < this._data.Count) && this._data[l].Item2 < this._data[r].Item2) ? r : l; if (this._data[m].Item2 <= this._data[i].Item2) { break; } this.Swap(i, m); i = m; } } /// /// Restores the heap property starting from i'th position up to the head /// given that the upstream items fulfill the rule. /// /// The position of item where heap property is violated. private void SiftUp(int i) { while (i > 0) { int p = (i - 1) / 2; if (this._data[i].Item2 <= this._data[p].Item2) { break; } this.Swap(i, p); i = p; } } /// /// Swaps items with the specified indicies. /// /// The first index. /// The second index. private void Swap(int i, int j) { var temp = this._data[i]; this._data[i] = this._data[j]; this._data[j] = temp; } public IEnumerator<(int, float)> GetEnumerator() { return _data.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _data.GetEnumerator(); } } }