File: System\Activities\DurableInstancing\BinaryHeap.cs
Project: ndp\cdf\src\NetFx40\System.Activities.DurableInstancing\System.Activities.DurableInstancing.csproj (System.Activities.DurableInstancing)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.Activities.DurableInstancing
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
 
    sealed class BinaryHeap<TKey, TValue> where TKey : IComparable<TKey>
    {
        const int defaultCapacity = 128;
        readonly KeyValuePair<TKey, TValue> EmptyItem = new KeyValuePair<TKey, TValue>();
        KeyValuePair<TKey, TValue>[] items;
        int itemCount;
 
        public BinaryHeap() :
            this(defaultCapacity)
        {
        }
 
        public BinaryHeap(int capacity)
        {
            Fx.Assert(capacity > 0, "Capacity must be a positive value.");
            this.items = new KeyValuePair<TKey, TValue>[capacity];
        }
 
        public int Count
        {
            get { return this.itemCount; }
        }
 
        public bool IsEmpty
        {
            get { return this.itemCount == 0; }
        }
 
        public void Clear()
        {
            this.itemCount = 0;
            this.items = new KeyValuePair<TKey, TValue>[defaultCapacity];
        }
 
        public bool Enqueue(TKey key, TValue item)
        {
            if (this.itemCount == this.items.Length)
            {
                ResizeItemStore(this.items.Length * 2);
            }
 
            this.items[this.itemCount++] = new KeyValuePair<TKey, TValue>(key, item);
            int position = this.BubbleUp(this.itemCount - 1);
 
            return (position == 0);
        }
 
        public KeyValuePair<TKey, TValue> Dequeue()
        {
            return Dequeue(true);
        }
 
        KeyValuePair<TKey, TValue> Dequeue(bool shrink)
        {
            Fx.Assert(this.itemCount > 0, "Cannot dequeue empty queue.");
 
            KeyValuePair<TKey, TValue> result = items[0];
 
            if (this.itemCount == 1)
            {
                this.itemCount = 0;
                this.items[0] = this.EmptyItem;
            }
            else
            {
                --this.itemCount;
                this.items[0] = this.items[itemCount];
                this.items[itemCount] = this.EmptyItem;
 
                // Keep the structure of the heap valid.
                this.BubbleDown(0);
            }
 
            if (shrink)
            {
                ShrinkStore();
            }
 
            return result;
        }
 
        public KeyValuePair<TKey, TValue> Peek()
        {
            Fx.Assert(this.itemCount > 0, "Cannot peek at empty queue.");
            return this.items[0];
        }
 
        [SuppressMessage(FxCop.Category.Design, "CA1006:DoNotNestGenericTypesInMemberSignatures",
            Justification = "This is an internal only API.")]
        [SuppressMessage(FxCop.Category.MSInternal, "CA908:UseApprovedGenericsForPrecompiledAssemblies")]
        public ICollection<KeyValuePair<TKey, TValue>> RemoveAll(Predicate<KeyValuePair<TKey, TValue>> func)
        {
            ICollection<KeyValuePair<TKey, TValue>> result = new List<KeyValuePair<TKey, TValue>>();
 
            for (int position = 0; position < this.itemCount; position++)
            {
                while (func(this.items[position]) && position < this.itemCount)
                {
                    result.Add(this.items[position]);
 
                    int lastItem = this.itemCount - 1;
 
                    while (func(this.items[lastItem]) && position < lastItem)
                    {
                        result.Add(this.items[lastItem]);
                        this.items[lastItem] = EmptyItem;
                        --lastItem;
                    }
 
                    this.items[position] = this.items[lastItem];
                    this.items[lastItem] = EmptyItem;
                    this.itemCount = lastItem;
 
                    if (position < lastItem)
                    {
                        this.BubbleDown(this.BubbleUp(position));
                    }
                }
            }
 
            this.ShrinkStore();
 
            return result;
        }
 
        void ShrinkStore()
        {
            // If we are under half capacity and above default capacity size down.
            if (this.items.Length > defaultCapacity && this.itemCount < (this.items.Length >> 1))
            {
                int newSize = Math.Max(
                    defaultCapacity, (((this.itemCount / defaultCapacity) + 1) * defaultCapacity));
 
                this.ResizeItemStore(newSize);
            }
        }
 
        [SuppressMessage("Microsoft.MSInternal", "CA908:UseApprovedGenericsForPrecompiledAssemblies")]
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
            Justification = "This is an internal only API.")]
        public ICollection<KeyValuePair<TKey, TValue>> TakeWhile(Predicate<TKey> func)
        {
            ICollection<KeyValuePair<TKey, TValue>> result = new List<KeyValuePair<TKey, TValue>>();
 
            while (!this.IsEmpty && func(this.Peek().Key))
            {
                result.Add(this.Dequeue(false));
            }
 
            ShrinkStore();
 
            return result;
        }
 
        void ResizeItemStore(int newSize)
        {
            Fx.Assert(itemCount < newSize, "Shrinking now will lose data.");
            Fx.Assert(defaultCapacity <= newSize, "Can not shrink below the default capacity.");
 
            KeyValuePair<TKey, TValue>[] temp = new KeyValuePair<TKey, TValue>[newSize];
 
            Array.Copy(this.items, 0, temp, 0, this.itemCount);
 
            this.items = temp;
        }
 
        void BubbleDown(int startIndex)
        {
            int currentPosition = startIndex;
            int swapPosition = startIndex;
 
            while (true)
            {
                int leftChildPosition = (currentPosition << 1) + 1;
                int rightChildPosition = leftChildPosition + 1;
 
                if (leftChildPosition < itemCount)
                {
                    if (this.items[currentPosition].Key.CompareTo(this.items[leftChildPosition].Key) > 0)
                    {
                        swapPosition = leftChildPosition;
                    }
                }
                else
                {
                    break;
                }
 
                if (rightChildPosition < itemCount)
                {
                    if (this.items[swapPosition].Key.CompareTo(this.items[rightChildPosition].Key) > 0)
                    {
                        swapPosition = rightChildPosition;
                    }
                }
 
                if (currentPosition != swapPosition)
                {
                    KeyValuePair<TKey, TValue> temp = this.items[currentPosition];
                    this.items[currentPosition] = this.items[swapPosition];
                    this.items[swapPosition] = temp;
                }
                else
                {
                    break;
                }
 
                currentPosition = swapPosition;
            }
        }
 
        int BubbleUp(int startIndex)
        {
            while (startIndex > 0)
            {
                int parent = (startIndex - 1) >> 1;
 
                if (this.items[parent].Key.CompareTo(this.items[startIndex].Key) > 0)
                {
                    KeyValuePair<TKey, TValue> temp = this.items[startIndex];
                    this.items[startIndex] = this.items[parent];
                    this.items[parent] = temp;
                }
                else
                {
                    break;
                }
 
                startIndex = parent;
            }
 
            return startIndex;
        }
    }
}