    // Copyright (C) 2005  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SORt_
    #define DLIB_SORt_

    #include "algs.h"
    #include <functional>

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        inline void qsort_array (
            T& array,
            unsigned long left,
            unsigned long right,
            const compare& comp
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by comp where comp is a function
                  object with the same syntax as std::less<>
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using a quick sort algorithm
        !*/

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        void hsort_array (
            T& array,
            unsigned long left,
            unsigned long right,
            const compare& comp
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by comp where comp is a function
                  object with the same syntax as std::less<>
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using a heapsort algorithm
        !*/

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        void isort_array (
            T& array,
            unsigned long left,
            unsigned long right,
            const compare& comp
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by comp where comp is a function
                  object with the same syntax as std::less<>
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using an insertion sort algorithm
        !*/

    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        inline void qsort_array (
            T& array,
            unsigned long left,
            unsigned long right
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by std::less
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using a quick sort algorithm
        !*/

    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        void hsort_array (
            T& array,
            unsigned long left,
            unsigned long right
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by std::less
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using a heapsort algorithm
        !*/

    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        void isort_array (
            T& array,
            unsigned long left,
            unsigned long right
        );
        /*!
            requires
                - T implements operator[]
                - the items in array must be comparable by std::less
                - the items in array must be swappable by a global swap()
                - left and right are within the bounds of array
                  i.e. array[left] and array[right] are valid elements
                - left <= right
            ensures
                - for all elements in #array between and including left and right the
                  ith element is < the i+1 element
                - sorts using an insertion sort algorithm
        !*/

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    //                            IMPLEMENTATION DETAILS
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        namespace sort_helpers
        {
            template <typename T>
            inline const std::less<T> comp (const T&)
            {
                return std::less<T>();
            }

            template <
                typename T,
                typename Y,
                typename compare
                >
            inline unsigned long qsort_partition (
                T& array,
                Y& pivot,
                const unsigned long left,
                const unsigned long right,
                const compare& comp
            )
            /*!
                requires
                    - &pivot == &array[right]
                    - T implements operator[]
                    - the items in array must be comparable by comp
                    - left and right are within the bounts of the array
                    - left < right
                ensures
                    - returns a number called partition_element such that:
                        - left <= partition_element <= right
                        - all elements in #array < #array[partition_element] have
                        indices >= left and < partition_element
                        - all elements in #array > #array[partition_element] have
                        indices > partition_element and <= right
            !*/
            {
                DLIB_ASSERT (&pivot == &array[right] && left < right,
                        "\tunsigned long qsort_partition()"
                        << "\n\t&pivot:        " << &pivot
                        << "\n\t&array[right]: " << &array[right]
                        << "\n\tleft:          " << left
                        << "\n\tright:         " << right );

                exchange(array[(right-left)/2 +left],pivot);

                unsigned long i = left;
                for (unsigned long j = left; j < right; ++j)
                {
                    if (comp(array[j] , pivot))
                    {
                        swap(array[i],array[j]);
                        ++i;
                    }
                }
                exchange(array[i],pivot);

                return i;
            }

    // ----------------------------------------------------------------------------------------

            template <
                typename T,
                typename compare
                >
            void qsort_array_main (
                T& array,
                const unsigned long left,
                const unsigned long right,
                unsigned long depth_check,
                const compare& comp
            )
            /*!
                requires
                    - T implements operator[]
                    - the items in array must be comparable by comp
                    - the items in array must be swappable by a global swap()
                    - left and right are within the bounds of array
                      i.e. array[left] and array[right] are valid elements
                ensures
                    - for all elements in #array between and including left and right the
                      ith element is < the i+1 element
                    - will only recurse about as deep as log(depth_check) calls
                    - sorts using a quick sort algorithm
            !*/
            {
                if ( left < right)
                {
                    if (right-left < 30 || depth_check == 0)
                    {
                        hsort_array(array,left,right,comp);
                    }
                    else
                    {
                        // The idea here is to only let quick sort go about log(N)
                        // calls deep before it kicks into something else.
                        depth_check >>= 1;
                        depth_check += (depth_check>>4);

                        unsigned long partition_element =
                            qsort_partition(array,array[right],left,right,comp);

                        if (partition_element > 0)
                            qsort_array_main(array,left,partition_element-1,depth_check,comp);
                        qsort_array_main(array,partition_element+1,right,depth_check,comp);
                    }
                }
            }

    // ----------------------------------------------------------------------------------------

            template <
                typename T,
                typename compare
                >
            void heapify (
                T& array,
                const unsigned long start,
                const unsigned long end,
                unsigned long i,
                const compare& comp
            )
            /*!
                requires
                    - T implements operator[]
                    - the items in array must be comparable by comp
                    - the items in array must be swappable by a global swap()
                    - start, end, and i are within the bounds of array
                      i.e. array[start], array[end], and array[i] are valid elements
                    - start <= i <= end
                    - array[i/2] is a max heap
                    - array[i/2+1] is a max heap
                    - start and end specify the range of the array we are working with.
                ensures
                    - array[i] is now a max heap
            !*/
            {
                DLIB_ASSERT (start <= i && i <= end,
                        "\tvoid heapify()"
                        << "\n\tstart:   " << start
                        << "\n\tend:     " << end
                        << "\n\ti:       " << i );

                bool keep_going = true;
                unsigned long left;
                unsigned long right;
                unsigned long largest;
                while (keep_going)
                {
                    keep_going = false;
                    left = (i<<1)+1-start;
                    right = left+1;

                    if (left <= end && comp(array[i] , array[left]))
                        largest = left;
                    else
                        largest = i;

                    if (right <= end && comp(array[largest] , array[right]))
                        largest = right;

                    if (largest != i)
                    {
                        exchange(array[i],array[largest]);
                        i = largest;
                        keep_going = true;
                    }
                }
            }

    // ----------------------------------------------------------------------------------------
        }
    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        inline void qsort_array (
            T& array,
            unsigned long left,
            unsigned long right
        )
        {
            using namespace sort_helpers;
            qsort_array(array,left,right,comp(array[left]));
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        void hsort_array (
            T& array,
            unsigned long left,
            unsigned long right
        )
        {
            using namespace sort_helpers;
            hsort_array(array,left,right,comp(array[left]));
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T
            >
        void isort_array (
            T& array,
            unsigned long left,
            unsigned long right
        )
        {
            using namespace sort_helpers;
            isort_array(array,left,right,comp(array[left]));
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        void isort_array (
            T& array,
            const unsigned long left,
            const unsigned long right,
            const compare& comp
        )
        {
            DLIB_ASSERT (left <= right,
                    "\tvoid isort_array()"
                    << "\n\tleft:          " << left
                    << "\n\tright:         " << right );
            using namespace sort_helpers;

            unsigned long pos;
            for (unsigned long i = left+1; i <= right; ++i)
            {
                // everything from left to i-1 is sorted.
                pos = i;
                for (unsigned long j = i-1; comp(array[pos] , array[j]); --j)
                {
                    exchange(array[pos],array[j]);
                    pos = j;

                    if (j == left)
                        break;
                }
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        void qsort_array (
            T& array,
            const unsigned long left,
            const unsigned long right,
            const compare& comp
        )
        {
            DLIB_ASSERT (left <= right,
                    "\tvoid qsort_array()"
                    << "\n\tleft:          " << left
                    << "\n\tright:         " << right );

            sort_helpers::qsort_array_main(array,left,right,right-left,comp);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename compare
            >
        void hsort_array (
            T& array,
            const unsigned long left,
            const unsigned long right,
            const compare& comp
        )
        {
            DLIB_ASSERT (left <= right,
                    "\tvoid hsort_array()"
                    << "\n\tleft:          " << left
                    << "\n\tright:         " << right );

            if (right-left < 30)
            {
                isort_array(array,left,right,comp);
                return;
            }

            // turn array into a max heap
            for (unsigned long i = left+((right-left)>>1);; --i)
            {
                sort_helpers::heapify(array,left,right,i,comp);
                if (i == left)
                    break;
            }

            // now sort the array
            for (unsigned long i = right; i > left;)
            {
                exchange(array[i],array[left]);
                sort_helpers::heapify(array,left,--i,left,comp);
            }
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_SORt_