Utility/HeapSort.cs
namespace Boxfish.Utility;

public class Heap<T> where T : IHeapItem<T>
{
	public int Count => currentItemCount;

	private readonly T[] items;
	private int currentItemCount;

	public Heap( int maxHeapSize )
	{
		items = ArrayPool<T>.Shared.Rent( maxHeapSize );
	}

	~Heap()
	{
		ArrayPool<T>.Shared.Return( items );
	}

	public void Add( T item )
	{
		item.HeapIndex = currentItemCount;
		items[currentItemCount] = item;
		SortUp( item );
		currentItemCount++;
	}

	public T RemoveFirst()
	{
		T firstItem = items[0];
		currentItemCount--;
		items[0] = items[currentItemCount];
		items[0].HeapIndex = 0;
		SortDown( items[0] );
		return firstItem;
	}

	public void UpdateItem( T item ) => SortUp( item );

	public bool Contains( T item ) => Equals( items[item.HeapIndex], item );

	private void SortDown( T item )
	{
		var iterations = 0;
		while ( true && iterations <= currentItemCount )
		{
			iterations++;
			int childIndexLeft = item.HeapIndex * 2 + 1;
			int childIndexRight = item.HeapIndex * 2 + 2;
			int swapIndex = 0;

			if ( childIndexLeft < currentItemCount )
			{
				swapIndex = childIndexLeft;

				if ( childIndexRight < currentItemCount )
					if ( items[childIndexLeft].CompareTo( items[childIndexRight] ) < 0 )
						swapIndex = childIndexRight;

				if ( item.CompareTo( items[swapIndex] ) < 0 )
					Swap( item, items[swapIndex] );
				else
					return;

			}
			else
				return;

		}
	}

	private void SortUp( T item )
	{
		int parentIndex = (item.HeapIndex - 1) / 2;

		var iterations = 0;
		while ( true && iterations <= currentItemCount )
		{
			iterations++;
			T parentItem = items[parentIndex];
			if ( item.CompareTo( parentItem ) > 0 )
				Swap( item, parentItem );
			else
				break;

			parentIndex = (item.HeapIndex - 1) / 2;
		}
	}

	private void Swap( T itemA, T itemB )
	{
		items[itemA.HeapIndex] = itemB;
		items[itemB.HeapIndex] = itemA;
		int itemAIndex = itemA.HeapIndex;
		itemA.HeapIndex = itemB.HeapIndex;
		itemB.HeapIndex = itemAIndex;
	}
}

public interface IHeapItem<T> : IComparable<T>
{
	int HeapIndex { get; set; }
}