Program Listing for File heap.h

Return to documentation for file (necsim/eastl/heap.h)

// Copyright (c) Electronic Arts Inc. All rights reserved.

// This file implements heap functionality much like the std C++ heap algorithms.
// Such heaps are not the same thing as memory heaps or pools, but rather are
// semi-sorted random access containers which have the primary purpose of
// supporting the implementation of priority_queue and similar data structures.
//
// The primary distinctions between this heap functionality and std::heap are:
//    - This heap exposes some extra functionality such as is_heap and change_heap.
//    - This heap is more efficient than versions found in typical STL
//      implementations such as STLPort, Microsoft, and Metrowerks. This comes
//      about due to better use of array dereferencing and branch prediction.
//      You should expect of 5-30%, depending on the usage and platform.

// The publicly usable functions we define are:
//    push_heap     -- Adds an entry to a heap.                             Same as C++ std::push_heap.
//    pop_heap      -- Removes the top entry from a heap.                   Same as C++ std::pop_heap.
//    make_heap     -- Converts an array to a heap.                         Same as C++ std::make_heap.
//    sort_heap     -- Sorts a heap in place.                               Same as C++ std::sort_heap.
//    remove_heap   -- Removes an arbitrary entry from a heap.
//    change_heap   -- Changes the priority of an entry in the heap.
//    is_heap       -- Returns true if an array appears is in heap format.   Same as C++11 std::is_heap.
//    is_heap_until -- Returns largest part of the range which is a heap.    Same as C++11 std::is_heap_until.



#ifndef EASTL_HEAP_H
#define EASTL_HEAP_H

#include <stddef.h>
#include <utility>
#include <iterator>

namespace eastl
{

    // promote_heap (internal function)

    template <typename RandomAccessIterator, typename Distance, typename T, typename ValueType>
    inline void promote_heap_impl(RandomAccessIterator first, Distance topPosition, Distance position, T value)
    {
        for(Distance parentPosition = (position - 1) >> 1; // This formula assumes that (position > 0). // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
            (position > topPosition) && (*(first + parentPosition) < value);
            parentPosition = (position - 1) >> 1)
        {
            *(first + position) = std::forward<ValueType>(*(first + parentPosition)); // Swap the node with its parent.
            position = parentPosition;
        }

        *(first + position) = std::forward<ValueType>(value);
    }

    template <typename RandomAccessIterator, typename Distance, typename T>
    inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        promote_heap_impl<RandomAccessIterator, Distance, const T&, const value_type>(first, topPosition, position, value);
    }


    template <typename RandomAccessIterator, typename Distance, typename T>
    inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        promote_heap_impl<RandomAccessIterator, Distance, T&&, value_type>(first, topPosition, position, std::forward<T>(value));
    }


    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare, typename ValueType>
    inline void promote_heap_impl(RandomAccessIterator first, Distance topPosition, Distance position, T value, Compare compare)
    {
        for(Distance parentPosition = (position - 1) >> 1; // This formula assumes that (position > 0). // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
            (position > topPosition) && compare(*(first + parentPosition), value);
            parentPosition = (position - 1) >> 1)
        {
            *(first + position) = std::forward<ValueType>(*(first + parentPosition)); // Swap the node with its parent.
            position = parentPosition;
        }

        *(first + position) = std::forward<ValueType>(value);
    }


    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
    inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value, Compare compare)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        promote_heap_impl<RandomAccessIterator, Distance, const T&, Compare, const value_type>(first, topPosition, position, value, compare);
    }


    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
    inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value, Compare compare)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        promote_heap_impl<RandomAccessIterator, Distance, T&&, Compare, value_type>(first, topPosition, position, std::forward<T>(value), compare);
    }



    // adjust_heap (internal function)

    template <typename RandomAccessIterator, typename Distance, typename T, typename ValueType>
    void adjust_heap_impl(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T value)
    {
        // We do the conventional approach of moving the position down to the
        // bottom then inserting the value at the back and moving it up.
        Distance childPosition = (2 * position) + 2;

        for(; childPosition < heapSize; childPosition = (2 * childPosition) + 2)
        {
            if(*(first + childPosition) < *(first + (childPosition - 1))) // Choose the larger of the two children.
                --childPosition;
            *(first + position) = std::forward<ValueType>(*(first + childPosition)); // Swap positions with this child.
            position = childPosition;
        }

        if(childPosition == heapSize) // If we are at the very last index of the bottom...
        {
            *(first + position) = std::forward<ValueType>(*(first + (childPosition - 1)));
            position = childPosition - 1;
        }

        eastl::promote_heap<RandomAccessIterator, Distance, T>(first, topPosition, position, std::forward<ValueType>(value));
    }

    template <typename RandomAccessIterator, typename Distance, typename T>
    void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        adjust_heap_impl<RandomAccessIterator, Distance, const T&, const value_type>(first, topPosition, heapSize, position, std::forward<const T&>(value));
    }


    template <typename RandomAccessIterator, typename Distance, typename T>
    void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        adjust_heap_impl<RandomAccessIterator, Distance, T&&, value_type>(first, topPosition, heapSize, position, std::forward<T>(value));
    }


    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare, typename ValueType>
    void adjust_heap_impl(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T value, Compare compare)
    {
        // We do the conventional approach of moving the position down to the
        // bottom then inserting the value at the back and moving it up.
        Distance childPosition = (2 * position) + 2;

        for(; childPosition < heapSize; childPosition = (2 * childPosition) + 2)
        {
            if(compare(*(first + childPosition), *(first + (childPosition - 1)))) // Choose the larger of the two children.
                --childPosition;
            *(first + position) = std::forward<ValueType>(*(first + childPosition)); // Swap positions with this child.
            position = childPosition;
        }

        if(childPosition == heapSize) // If we are at the bottom...
        {
            *(first + position) = std::forward<ValueType>(*(first + (childPosition - 1)));
            position = childPosition - 1;
        }

        eastl::promote_heap<RandomAccessIterator, Distance, T, Compare>(first, topPosition, position, std::forward<ValueType>(value), compare);
    }

    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
    void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value, Compare compare)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        adjust_heap_impl<RandomAccessIterator, Distance, const T&, Compare, const value_type>(first, topPosition, heapSize, position, std::forward<const T&>(value), compare);
    }


    template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
    void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value, Compare compare)
    {
        typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
        adjust_heap_impl<RandomAccessIterator, Distance, T&&, Compare, value_type>(first, topPosition, heapSize, position, std::forward<T>(value), compare);
    }


    // push_heap

    template <typename RandomAccessIterator>
    inline void push_heap(RandomAccessIterator first, RandomAccessIterator last)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const value_type tempBottom(std::forward<value_type>(*(last - 1)));

        eastl::promote_heap<RandomAccessIterator, difference_type, value_type>
                           (first, (difference_type)0, (difference_type)(last - first - 1), std::forward<const value_type>(tempBottom));
    }


    template <typename RandomAccessIterator, typename Compare>
    inline void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const value_type tempBottom(*(last - 1));

        eastl::promote_heap<RandomAccessIterator, difference_type, value_type, Compare>
                           (first, (difference_type)0, (difference_type)(last - first - 1), tempBottom, compare);
    }




    // pop_heap

    template <typename RandomAccessIterator>
    inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        value_type tempBottom(std::forward<value_type>(*(last - 1)));
        *(last - 1) = std::forward<value_type>(*first);
        eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
                          (first, (difference_type)0, (difference_type)(last - first - 1), 0, std::forward<value_type>(tempBottom));
    }



    template <typename RandomAccessIterator, typename Compare>
    inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        value_type tempBottom(std::forward<value_type>(*(last - 1)));
        *(last - 1) = std::forward<value_type>(*first);
        eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
                          (first, (difference_type)0, (difference_type)(last - first - 1), 0, std::forward<value_type>(tempBottom), compare);
    }


    // make_heap


    template <typename RandomAccessIterator>
    void make_heap(RandomAccessIterator first, RandomAccessIterator last)
    {
        // We do bottom-up heap construction as per Sedgewick. Such construction is O(n).
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const difference_type heapSize = last - first;

        if(heapSize >= 2) // If there is anything to do... (we need this check because otherwise the math fails below).
        {
            difference_type parentPosition = ((heapSize - 2) >> 1) + 1; // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.

            do{
                --parentPosition;
                value_type temp(std::forward<value_type>(*(first + parentPosition)));
                eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
                                  (first, parentPosition, heapSize, parentPosition, std::forward<value_type>(temp));
            } while(parentPosition != 0);
        }
    }


    template <typename RandomAccessIterator, typename Compare>
    void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const difference_type heapSize = last - first;

        if(heapSize >= 2) // If there is anything to do... (we need this check because otherwise the math fails below).
        {
            difference_type parentPosition = ((heapSize - 2) >> 1) + 1; // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.

            do{
                --parentPosition;
                value_type temp(std::forward<value_type>(*(first + parentPosition)));
                eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
                                  (first, parentPosition, heapSize, parentPosition, std::forward<value_type>(temp), compare);
            } while(parentPosition != 0);
        }
    }


    // sort_heap

    template <typename RandomAccessIterator>
    inline void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
    {
        for(; (last - first) > 1; --last) // We simply use the heap to sort itself.
            eastl::pop_heap<RandomAccessIterator>(first, last);
    }


    template <typename RandomAccessIterator, typename Compare>
    inline void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        for(; (last - first) > 1; --last) // We simply use the heap to sort itself.
            eastl::pop_heap<RandomAccessIterator, Compare>(first, last, compare);
    }



    // remove_heap

    template <typename RandomAccessIterator, typename Distance>
    inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const value_type tempBottom(*(first + heapSize - 1));
        *(first + heapSize - 1) = *(first + position);
        eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
                          (first, (difference_type)0, (difference_type)(heapSize - 1), (difference_type)position, tempBottom);
    }


    template <typename RandomAccessIterator, typename Distance, typename Compare>
    inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        const value_type tempBottom(*(first + heapSize - 1));
        *(first + heapSize - 1) = *(first + position);
        eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
                          (first, (difference_type)0, (difference_type)(heapSize - 1), (difference_type)position, tempBottom, compare);
    }



    // change_heap

    template <typename RandomAccessIterator, typename Distance>
    inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        eastl::remove_heap<RandomAccessIterator, Distance>(first, heapSize, position);

        value_type tempBottom(*(first + heapSize - 1));

        eastl::promote_heap<RandomAccessIterator, difference_type, value_type>
                           (first, (difference_type)0, (difference_type)(heapSize - 1), tempBottom);
    }


    template <typename RandomAccessIterator, typename Distance, typename Compare>
    inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare)
    {
        typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
        typedef typename std::iterator_traits<RandomAccessIterator>::value_type      value_type;

        eastl::remove_heap<RandomAccessIterator, Distance, Compare>(first, heapSize, position, compare);

        value_type tempBottom(*(first + heapSize - 1));

        eastl::promote_heap<RandomAccessIterator, difference_type, value_type, Compare>
                           (first, (difference_type)0, (difference_type)(heapSize - 1), tempBottom, compare);
    }



    // is_heap_until

    template <typename RandomAccessIterator>
    inline RandomAccessIterator is_heap_until(RandomAccessIterator first, RandomAccessIterator last)
    {
        int counter = 0;

        for(RandomAccessIterator child = first + 1; child < last; ++child, counter ^= 1)
        {
            if(*first < *child)  // We must use operator <, and are not allowed to use > or >= here.
                return child;
            first += counter; // counter switches between 0 and 1 every time through.
        }

        return last;
    }


    template <typename RandomAccessIterator, typename Compare>
    inline RandomAccessIterator is_heap_until(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        int counter = 0;

        for(RandomAccessIterator child = first + 1; child < last; ++child, counter ^= 1)
        {
            if(compare(*first, *child))
                return child;
            first += counter; // counter switches between 0 and 1 every time through.
        }

        return last;
    }



    // is_heap

    template <typename RandomAccessIterator>
    inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last)
    {
        return (eastl::is_heap_until(first, last) == last);
    }


    template <typename RandomAccessIterator, typename Compare>
    inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
    {
        return (eastl::is_heap_until(first, last, compare) == last);
    }


    // To consider: The following may be a faster implementation for most cases.
    //
    // template <typename RandomAccessIterator>
    // inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last)
    // {
    //     if(((uintptr_t)(last - first) & 1) == 0) // If the range has an even number of elements...
    //         --last;
    //
    //     RandomAccessIterator parent = first, child = (first + 1);
    //
    //     for(; child < last; child += 2, ++parent)
    //     {
    //         if((*parent < *child) || (*parent < *(child + 1)))
    //             return false;
    //     }
    //
    //     if((((uintptr_t)(last - first) & 1) == 0) && (*parent < *child))
    //         return false;
    //
    //     return true;
    // }


} // namespace eastl


#endif // Header include guard