File: System\Linq\Parallel\Utils\FixedMaxHeap.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// FixedMaxHeap.cs
//
// <OWNER>Microsoft</OWNER>
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
using System.Collections.Generic;
using System.Diagnostics.Contracts;
#if SILVERLIGHT
using System.Core; // for System.Core.SR
#endif
 
namespace System.Linq.Parallel
{
    /// <summary>
    /// Very simple heap data structure, of fixed size.
    /// </summary>
    /// <typeparam name="TElement"></typeparam>
    internal class FixedMaxHeap<TElement>
    {
 
        private TElement[] m_elements; // Element array.
        private int m_count; // Current count.
        private IComparer<TElement> m_comparer; // Element comparison routine.
 
        //-----------------------------------------------------------------------------------
        // Create a new heap with the specified maximum size.
        //
 
        internal FixedMaxHeap(int maximumSize)
            : this(maximumSize, Util.GetDefaultComparer<TElement>())
        {
        }
 
        internal FixedMaxHeap(int maximumSize, IComparer<TElement> comparer)
        {
            Contract.Assert(comparer != null);
 
            m_elements = new TElement[maximumSize];
            m_comparer = comparer;
        }
 
        //-----------------------------------------------------------------------------------
        // Retrieve the count (i.e. how many elements are in the heap).
        //
 
        internal int Count
        {
            get { return m_count; }
        }
 
        //-----------------------------------------------------------------------------------
        // Retrieve the size (i.e. the maximum size of the heap).
        //
 
        internal int Size
        {
            get { return m_elements.Length; }
        }
 
        //-----------------------------------------------------------------------------------
        // Get the current maximum value in the max-heap.
        //
        // Note: The heap stores the maximumSize smallest elements that were inserted.
        // So, if the heap is full, the value returned is the maximumSize-th smallest
        // element that was inserted into the heap.
        //
 
        internal TElement MaxValue
        {
            get
            {
                if (m_count == 0)
                {
                    throw new InvalidOperationException(SR.GetString(SR.NoElements));
                }
 
                // The maximum element is in the 0th position.
                return m_elements[0];
            }
        }
 
 
        //-----------------------------------------------------------------------------------
        // Removes all elements from the heap.
        //
 
        internal void Clear()
        {
            m_count = 0;
        }
 
        //-----------------------------------------------------------------------------------
        // Inserts the new element, maintaining the heap property.
        //
        // Return Value:
        //     If the element is greater than the current max element, this function returns
        //     false without modifying the heap. Otherwise, it returns true.
        //
 
        internal bool Insert(TElement e)
        {
            if (m_count < m_elements.Length)
            {
                // There is room. We can add it and then max-heapify.
                m_elements[m_count] = e;
                m_count++;
                HeapifyLastLeaf();
                return true;
            }
            else
            {
                // No more room. The element might not even fit in the heap. The check
                // is simple: if it's greater than the maximum element, then it can't be
                // inserted. Otherwise, we replace the head with it and reheapify.
                if (m_comparer.Compare(e, m_elements[0]) < 0)
                {
                    m_elements[0] = e;
                    HeapifyRoot();
                    return true;
                }
 
                return false;
            }
        }
 
        //-----------------------------------------------------------------------------------
        // Replaces the maximum value in the heap with the user-provided value, and restores
        // the heap property.
        //
 
        internal void ReplaceMax(TElement newValue)
        {
            Contract.Assert(m_count > 0);
            m_elements[0] = newValue;
            HeapifyRoot();
        }
 
        //-----------------------------------------------------------------------------------
        // Removes the maximum value from the heap, and restores the heap property.
        //
 
        internal void RemoveMax()
        {
            Contract.Assert(m_count > 0);
            m_count--;
 
            if (m_count > 0)
            {
                m_elements[0] = m_elements[m_count];
                HeapifyRoot();
            }
        }
 
        //-----------------------------------------------------------------------------------
        // Private helpers to swap elements, and to reheapify starting from the root or
        // from a leaf element, depending on what is needed.
        //
 
        private void Swap(int i, int j)
        {
            TElement tmpElement = m_elements[i];
            m_elements[i] = m_elements[j];
            m_elements[j] = tmpElement;
        }
 
        private void HeapifyRoot()
        {
            // We are heapifying from the head of the list.
            int i = 0;
            int n = m_count;
 
            while (i < n)
            {
                // Calculate the current child node indexes.
                int n0 = ((i + 1) * 2) - 1;
                int n1 = n0 + 1;
 
                if (n0 < n && m_comparer.Compare(m_elements[i], m_elements[n0]) < 0)
                {
                    // We have to select the bigger of the two subtrees, and float
                    // the current element down. This maintains the max-heap property.
                    if (n1 < n && m_comparer.Compare(m_elements[n0], m_elements[n1]) < 0)
                    {
                        Swap(i, n1);
                        i = n1;
                    }
                    else
                    {
                        Swap(i, n0);
                        i = n0;
                    }
                }
                else if (n1 < n && m_comparer.Compare(m_elements[i], m_elements[n1]) < 0)
                {
                    // Float down the "right" subtree. We needn't compare this subtree
                    // to the "left", because if the element was smaller than that, the
                    // first if statement's predicate would have evaluated to true.
                    Swap(i, n1);
                    i = n1;
                }
                else
                {
                    // Else, the current key is in its final position. Break out
                    // of the current loop and return.
                    break;
                }
            }
        }
 
        private void HeapifyLastLeaf()
        {
            int i = m_count - 1;
            while (i > 0)
            {
                int j = ((i + 1) / 2) - 1;
 
                if (m_comparer.Compare(m_elements[i], m_elements[j]) > 0)
                {
                    Swap(i, j);
                    i = j;
                }
                else
                {
                    break;
                }
            }
        }
    }
}