using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ZeroLevel.HNSW.Services { /// /// Binary heap wrapper around the /// It's a max-heap implementation i.e. the maximum element is always on top. /// But the order of elements can be customized by providing instance. /// /// The type of the items in the source list. public class BinaryHeap { /// /// Initializes a new instance of the class. /// /// The buffer to store heap items. public BinaryHeap(IList buffer) : this(buffer, Comparer.Default) { } /// /// Initializes a new instance of the class. /// /// The buffer to store heap items. /// The comparer which defines order of items. public BinaryHeap(IList buffer, IComparer comparer) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } this.Buffer = buffer; this.Comparer = comparer; for (int i = 1; i < this.Buffer.Count; ++i) { this.SiftUp(i); } } /// /// Gets the heap comparer. /// public IComparer Comparer { get; private set; } /// /// Gets the buffer of the heap. /// public IList Buffer { get; private set; } /// /// Pushes item to the heap. /// /// The item to push. public void Push(T item) { this.Buffer.Add(item); this.SiftUp(this.Buffer.Count - 1); } /// /// Pops the item from the heap. /// /// The popped item. public T Pop() { if (this.Buffer.Any()) { var result = this.Buffer.First(); this.Buffer[0] = this.Buffer.Last(); this.Buffer.RemoveAt(this.Buffer.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.Buffer.Count) { int l = (2 * i) + 1; int r = l + 1; if (l >= this.Buffer.Count) { break; } int m = r < this.Buffer.Count && this.Comparer.Compare(this.Buffer[l], this.Buffer[r]) < 0 ? r : l; if (this.Comparer.Compare(this.Buffer[m], this.Buffer[i]) <= 0) { 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.Comparer.Compare(this.Buffer[i], this.Buffer[p]) <= 0) { 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.Buffer[i]; this.Buffer[i] = this.Buffer[j]; this.Buffer[j] = temp; } } }