//----------------------------------------------------------------------------
/// @file sort_basic.hpp
/// @brief Spin Sort algorithm
///
/// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\n
///         Distributed under the Boost Software License, Version 1.0.\n
///         ( See accompanying file LICENSE_1_0.txt or copy at
///           http://www.boost.org/LICENSE_1_0.txt  )
/// @version 0.1
///
/// @remarks
//-----------------------------------------------------------------------------
#ifndef __BOOST_SORT_COMMON_SORT_BASIC_HPP
#define __BOOST_SORT_COMMON_SORT_BASIC_HPP

#include <cstdlib>
#include <functional>
#include <iterator>
#include <memory>
#include <type_traits>
#include <vector>
#include <cstddef>
#include <boost/sort/insert_sort/insert_sort.hpp>
#include <boost/sort/common/util/traits.hpp>
#include <boost/sort/common/range.hpp>

namespace boost
{
namespace sort
{
namespace common
{

//----------------------------------------------------------------------------
//                USING SENTENCES
//----------------------------------------------------------------------------
using boost::sort::insert_sort;

//-----------------------------------------------------------------------------
//  function : is_stable_sorted_forward
/// @brief examine the elements in the range first, last if they are stable
///        sorted, and return an iterator to the first element not sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @return iterator to the first element not stable sorted. The number of
///         elements sorted is the iterator returned minus first
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
inline Iter_t is_stable_sorted_forward (Iter_t first, Iter_t last,
                                        Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return first;
    Iter_t it2 = first + 1;
    for (Iter_t it1 = first; it2 != last && ! comp(*it2, *it1); it1 = it2++);
    return it2;
}
//-----------------------------------------------------------------------------
//  function : is_reverse_stable_sorted_forward
/// @brief examine the elements in the range first, last if they are reverse
///        stable sorted, and return an iterator to the first element not
///        reverse stable sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @return iterator to the first element not  reverse stable sorted. The number
///         of elements sorted is the iterator returned minus first
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
inline Iter_t is_reverse_stable_sorted_forward(Iter_t first, Iter_t last,
                                               Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return first;
    Iter_t it2 = first + 1;
    for (Iter_t it1 = first; it2 != last && comp(*it2, *it1); it1 = it2++);
    return it2;
}
//-----------------------------------------------------------------------------
//  function : number_stable_sorted_forward
/// @brief examine the elements in the range first, last if they are stable
///        sorted, and return the number of elements sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @param min_process : minimal number of elements to be consideer
/// @return number of element sorted. I f the number is lower than min_process
///         return 0
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
size_t number_stable_sorted_forward (Iter_t first, Iter_t last,
		                             size_t min_process,
                                     Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return 0;

    // sorted elements
    Iter_t it2 = first + 1;
    for (Iter_t it1 = first; it2 != last && ! comp(*it2, *it1); it1 = it2++);
    size_t nsorted = size_t ( it2 - first);
    if ( nsorted != 1)
    	return (nsorted >= min_process) ? nsorted: 0;

    // reverse sorted elements
    it2 = first + 1;
    for (Iter_t it1 = first; it2 != last && comp(*it2, *it1); it1 = it2++);
    nsorted = size_t ( it2 - first);

    if ( nsorted < min_process) return 0 ;
    util::reverse ( first , it2);
    return nsorted;
}

//-----------------------------------------------------------------------------
//  function : is_stable_sorted_backward
/// @brief examine the elements in the range first, last beginning at end, and
///        if they are stablesorted, and return an iterator to the last element
///        sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @return iterator to the last element stable sorted. The number of
///         elements sorted is the last minus the iterator returned
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
inline Iter_t is_stable_sorted_backward(Iter_t first, Iter_t last,
                                        Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return first;
    Iter_t itaux = last - 1;
    while (itaux != first && ! comp(*itaux, *(itaux - 1))) {--itaux; };
    return itaux;
}
//-----------------------------------------------------------------------------
//  function : is_reverse_stable_sorted_backward
/// @brief examine the elements in the range first, last beginning at end, and
///        if they are stablesorted, and return an iterator to the last element
///        sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @return iterator to the last element stable sorted. The number of
///         elements sorted is the last minus the iterator returned
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
inline Iter_t is_reverse_stable_sorted_backward (Iter_t first, Iter_t last,
                                                 Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return first;
    Iter_t itaux = last - 1;
    for (; itaux != first && comp(*itaux, *(itaux - 1)); --itaux);
    return itaux;
}

//-----------------------------------------------------------------------------
//  function : number_stable_sorted_backward
/// @brief examine the elements in the range first, last if they are stable
///        sorted, and return the number of elements sorted
/// @param first : iterator to the first element in the range
/// @param last : ierator after the last element of the range
/// @param comp : object for to compare two elements
/// @param min_process : minimal number of elements to be consideer
/// @return number of element sorted. I f the number is lower than min_process
///         return 0
//-----------------------------------------------------------------------------
template<class Iter_t, class Compare = std::less<value_iter<Iter_t> > >
size_t number_stable_sorted_backward (Iter_t first, Iter_t last,
		                             size_t min_process,
                                     Compare comp = Compare())
{
#ifdef __BS_DEBUG
    assert ( (last- first) >= 0);
#endif
    if ((last - first) < 2) return 0;
    Iter_t itaux = last - 1;
    while (itaux != first && ! comp(*itaux, *(itaux - 1))) {--itaux; };
    size_t nsorted = size_t ( last - itaux);
    if ( nsorted != 1)
    	return ( nsorted >= min_process)?nsorted: 0 ;

    itaux = last - 1;
    for (; itaux != first && comp(*itaux, *(itaux - 1)); --itaux);
    nsorted = size_t ( last - itaux);
    if ( nsorted < min_process) return 0 ;
    util::reverse ( itaux, last );
    return nsorted;
}
//-----------------------------------------------------------------------------
//  function : internal_sort
/// @brief this function divide r_input in two parts, sort it,and merge moving
///        the elements to range_buf
/// @param range_input : range with the elements to sort
/// @param range_buffer : range with the elements sorted
/// @param comp : object for to compare two elements
/// @param level : when is 1, sort with the insertionsort algorithm
///                if not make a recursive call splitting the ranges
//
//-----------------------------------------------------------------------------
template <class Iter1_t, class Iter2_t, class Compare>
inline void internal_sort (const range<Iter1_t> &rng1,
		                   const range<Iter2_t> &rng2,
                           Compare comp, uint32_t level, bool even = true)
{
    //-----------------------------------------------------------------------
    //                  metaprogram
    //-----------------------------------------------------------------------
    typedef value_iter<Iter1_t> value_t;
    typedef value_iter<Iter2_t> value2_t;
    static_assert (std::is_same< value_t, value2_t>::value,
                    "Incompatible iterators\n");

    //-----------------------------------------------------------------------
    //                  program
    //-----------------------------------------------------------------------
#ifdef __BS_DEBUG
    assert (rng1.size ( ) == rng2.size ( ) );
#endif
    size_t nelem = (rng1.size() + 1) >> 1;

    range<Iter1_t> rng1_left(rng1.first, rng1.first + nelem), 
                   rng1_right(rng1.first + nelem, rng1.last);

    range<Iter2_t> rng2_left(rng2.first, rng2.first + nelem), 
                   rng2_right(rng2.first + nelem, rng2.last);

    if (nelem <= 32 && (level & 1) == even)
    {
        insert_sort(rng1_left.first, rng1_left.last, comp);
        insert_sort(rng1_right.first, rng1_right.last, comp);
    }
    else
    {
        internal_sort(rng2_left, rng1_left, comp, level + 1, even);
        internal_sort(rng2_right, rng1_right, comp, level + 1, even);
    };
    merge(rng2, rng1_left, rng1_right, comp);
}
//-----------------------------------------------------------------------------
//  function : range_sort_data
/// @brief this sort elements using the range_sort function and receiving a
///        buffer of initialized memory
/// @param rng_data : range with the elements to sort
/// @param rng_aux : range of at least the same memory than rng_data used as
///                  auxiliary memory in the sorting
/// @param comp : object for to compare two elements
//-----------------------------------------------------------------------------
template<class Iter1_t, class Iter2_t, class Compare>
static void range_sort_data (const range<Iter1_t> & rng_data,
                             const range<Iter2_t> & rng_aux, Compare comp)
{
    //-----------------------------------------------------------------------
    //                  metaprogram
    //-----------------------------------------------------------------------
    typedef value_iter<Iter1_t> value_t;
    typedef value_iter<Iter2_t> value2_t;
    static_assert (std::is_same< value_t, value2_t>::value,
                    "Incompatible iterators\n");

    //------------------------------------------------------------------------
    //                    program
    //------------------------------------------------------------------------
#ifdef __BS_DEBUG
    assert ( rng_data.size() == rng_aux.size());
#endif
    // minimal number of element before to jump to insertionsort
    const uint32_t sort_min = 32;
    if (rng_data.size() <= sort_min)
    {
        insert_sort(rng_data.first, rng_data.last, comp);
        return;
    };

    internal_sort(rng_aux, rng_data, comp, 0, true);
}
//-----------------------------------------------------------------------------
//  function : range_sort_buffer
/// @brief this sort elements using the range_sort function and receiving a
///        buffer of initialized memory
/// @param rng_data : range with the elements to sort
/// @param rng_aux : range of at least the same memory than rng_data used as
///                  auxiliary memory in the sorting
/// @param comp : object for to compare two elements
//-----------------------------------------------------------------------------
template<class Iter1_t, class Iter2_t, class Compare>
static void range_sort_buffer(const range<Iter1_t> & rng_data,
                              const range<Iter2_t> & rng_aux, Compare comp)
{
    //-----------------------------------------------------------------------
    //                  metaprogram
    //-----------------------------------------------------------------------
    typedef value_iter<Iter1_t> value_t;
    typedef value_iter<Iter2_t> value2_t;
    static_assert (std::is_same< value_t, value2_t>::value,
                    "Incompatible iterators\n");

    //------------------------------------------------------------------------
    //                    program
    //------------------------------------------------------------------------
#ifdef __BS_DEBUG
    assert ( rng_data.size() == rng_aux.size());
#endif
    // minimal number of element before to jump to insertionsort
    const uint32_t sort_min = 32;
    if (rng_data.size() <= sort_min)
    {
        insert_sort(rng_data.first, rng_data.last, comp);
        move_forward(rng_aux, rng_data);
        return;
    };

    internal_sort(rng_data, rng_aux, comp, 0, false);
}
//****************************************************************************
}//    End namespace common
}//    End namespace sort
}//    End namepspace boost
//****************************************************************************
//
#endif
