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;
}
}
}