Squashed 'third_party/boostorg/move/' content from commit d503fbe

Change-Id: I5f8ac37161a1044b02ffb1f59cf15622fc6acd17
git-subtree-dir: third_party/boostorg/move
git-subtree-split: d503fbe1c8334fa8885e67cb83c96aeaf3938555
diff --git a/include/boost/move/algo/adaptive_merge.hpp b/include/boost/move/algo/adaptive_merge.hpp
new file mode 100644
index 0000000..4de4007
--- /dev/null
+++ b/include/boost/move/algo/adaptive_merge.hpp
@@ -0,0 +1,343 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_MOVE_ADAPTIVE_MERGE_HPP
+#define BOOST_MOVE_ADAPTIVE_MERGE_HPP
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/algo/detail/adaptive_sort_merge.hpp>
+
+namespace boost {
+namespace movelib {
+
+///@cond
+namespace detail_adaptive {
+
+template<class RandIt, class Compare, class XBuf>
+inline void adaptive_merge_combine_blocks( RandIt first
+                                      , typename iterator_traits<RandIt>::size_type len1
+                                      , typename iterator_traits<RandIt>::size_type len2
+                                      , typename iterator_traits<RandIt>::size_type collected
+                                      , typename iterator_traits<RandIt>::size_type n_keys
+                                      , typename iterator_traits<RandIt>::size_type l_block
+                                      , bool use_internal_buf
+                                      , bool xbuf_used
+                                      , Compare comp
+                                      , XBuf & xbuf
+                                      )
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const len = len1+len2;
+   size_type const l_combine  = len-collected;
+   size_type const l_combine1 = len1-collected;
+
+    if(n_keys){
+      RandIt const first_data = first+collected;
+      RandIt const keys = first;
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A combine: ", len);
+      if(xbuf_used){
+         if(xbuf.size() < l_block){
+            xbuf.initialize_until(l_block, *first);
+         }
+         BOOST_ASSERT(xbuf.size() >= l_block);
+         size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
+         combine_params( keys, comp, l_combine
+                           , l_combine1, l_block, xbuf
+                           , n_block_a, n_block_b, l_irreg1, l_irreg2);   //Outputs
+         op_merge_blocks_with_buf
+            (keys, comp, first_data, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, move_op(), xbuf.data());
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   A mrg xbf: ", len);
+      }
+      else{
+         size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
+         combine_params( keys, comp, l_combine
+                           , l_combine1, l_block, xbuf
+                           , n_block_a, n_block_b, l_irreg1, l_irreg2);   //Outputs
+         if(use_internal_buf){
+            op_merge_blocks_with_buf
+               (keys, comp, first_data, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, swap_op(), first_data-l_block);
+            BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A mrg buf: ", len);
+         }
+         else{
+            merge_blocks_bufferless
+               (keys, comp, first_data, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp);
+            BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   A mrg nbf: ", len);
+         }
+      }
+   }
+   else{
+      xbuf.shrink_to_fit(l_block);
+      if(xbuf.size() < l_block){
+         xbuf.initialize_until(l_block, *first);
+      }
+      size_type *const uint_keys = xbuf.template aligned_trailing<size_type>(l_block);
+      size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
+      combine_params( uint_keys, less(), l_combine
+                     , l_combine1, l_block, xbuf
+                     , n_block_a, n_block_b, l_irreg1, l_irreg2, true);   //Outputs
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A combine: ", len);
+      BOOST_ASSERT(xbuf.size() >= l_block);
+      op_merge_blocks_with_buf
+         (uint_keys, less(), first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, move_op(), xbuf.data());
+      xbuf.clear();
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   A mrg buf: ", len);
+   }
+}
+
+template<class RandIt, class Compare, class XBuf>
+inline void adaptive_merge_final_merge( RandIt first
+                                      , typename iterator_traits<RandIt>::size_type len1
+                                      , typename iterator_traits<RandIt>::size_type len2
+                                      , typename iterator_traits<RandIt>::size_type collected
+                                      , typename iterator_traits<RandIt>::size_type l_intbuf
+                                      , typename iterator_traits<RandIt>::size_type l_block
+                                      , bool use_internal_buf
+                                      , bool xbuf_used
+                                      , Compare comp
+                                      , XBuf & xbuf
+                                      )
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   (void)l_block;
+   size_type n_keys = collected-l_intbuf;
+   size_type len = len1+len2;
+   if(use_internal_buf){
+      if(xbuf_used){
+         xbuf.clear();
+         //Nothing to do
+         if(n_keys){
+            unstable_sort(first, first+n_keys, comp, xbuf);
+            stable_merge(first, first+n_keys, first+len, comp, xbuf);
+            BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A key mrg: ", len);
+         }
+      }
+      else{
+         xbuf.clear();
+         unstable_sort(first, first+collected, comp, xbuf);
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A k/b srt: ", len);
+         stable_merge(first, first+collected, first+len, comp, xbuf);
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A k/b mrg: ", len);
+      }
+   }
+   else{
+      xbuf.clear();
+      unstable_sort(first, first+collected, comp, xbuf);
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A k/b srt: ", len);
+      stable_merge(first, first+collected, first+len1+len2, comp, xbuf);
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A k/b mrg: ", len);
+   }
+   BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   A fin mrg: ", len);
+}
+
+template<class SizeType>
+inline static SizeType adaptive_merge_n_keys_without_external_keys(SizeType l_block, SizeType len1, SizeType len2, SizeType l_intbuf)
+{
+   typedef SizeType size_type;
+   //This is the minimum number of keys to implement the ideal algorithm
+   size_type n_keys = len1/l_block+len2/l_block;
+   const size_type second_half_blocks = len2/l_block;
+   const size_type first_half_aux = len1-l_intbuf;
+   while(n_keys >= ((first_half_aux-n_keys)/l_block + second_half_blocks)){
+      --n_keys;
+   }
+   ++n_keys;
+   return n_keys;
+}
+
+template<class SizeType>
+inline static SizeType adaptive_merge_n_keys_with_external_keys(SizeType l_block, SizeType len1, SizeType len2, SizeType l_intbuf)
+{
+   typedef SizeType size_type;
+   //This is the minimum number of keys to implement the ideal algorithm
+   size_type n_keys = (len1-l_intbuf)/l_block + len2/l_block;
+   return n_keys;
+}
+
+template<class SizeType, class Xbuf>
+inline SizeType adaptive_merge_n_keys_intbuf(SizeType &rl_block, SizeType len1, SizeType len2, Xbuf & xbuf, SizeType &l_intbuf_inout)
+{
+   typedef SizeType size_type;
+   size_type l_block = rl_block;
+   size_type l_intbuf = xbuf.capacity() >= l_block ? 0u : l_block;
+
+   while(xbuf.capacity() >= l_block*2){
+      l_block *= 2;
+   }
+
+   //This is the minimum number of keys to implement the ideal algorithm
+   size_type n_keys = adaptive_merge_n_keys_without_external_keys(l_block, len1, len2, l_intbuf);
+   BOOST_ASSERT(n_keys >= ((len1-l_intbuf-n_keys)/l_block + len2/l_block));
+
+   if(xbuf.template supports_aligned_trailing<size_type>
+      ( l_block
+      , adaptive_merge_n_keys_with_external_keys(l_block, len1, len2, l_intbuf)))
+   {
+      n_keys = 0u;
+   }
+   l_intbuf_inout = l_intbuf;
+   rl_block = l_block;
+   return n_keys;
+}
+
+// Main explanation of the merge algorithm.
+//
+// csqrtlen = ceil(sqrt(len));
+//
+// * First, csqrtlen [to be used as buffer] + (len/csqrtlen - 1) [to be used as keys] => to_collect
+//   unique elements are extracted from elements to be sorted and placed in the beginning of the range.
+//
+// * Step "combine_blocks": the leading (len1-to_collect) elements plus trailing len2 elements
+//   are merged with a non-trivial ("smart") algorithm to form an ordered range trailing "len-to_collect" elements.
+//
+//   Explanation of the "combine_blocks" step:
+//
+//         * Trailing [first+to_collect, first+len1) elements are divided in groups of cqrtlen elements.
+//           Remaining elements that can't form a group are grouped in front of those elements.
+//         * Trailing [first+len1, first+len1+len2) elements are divided in groups of cqrtlen elements.
+//           Remaining elements that can't form a group are grouped in the back of those elements.
+//         * In parallel the following two steps are performed:
+//             *  Groups are selection-sorted by first or last element (depending whether they are going
+//                to be merged to left or right) and keys are reordered accordingly as an imitation-buffer.
+//             * Elements of each block pair are merged using the csqrtlen buffer taking into account
+//                if they belong to the first half or second half (marked by the key).
+//
+// * In the final merge step leading "to_collect" elements are merged with rotations
+//   with the rest of merged elements in the "combine_blocks" step.
+//
+// Corner cases:
+//
+// * If no "to_collect" elements can be extracted:
+//
+//    * If more than a minimum number of elements is extracted
+//      then reduces the number of elements used as buffer and keys in the
+//      and "combine_blocks" steps. If "combine_blocks" has no enough keys due to this reduction
+//      then uses a rotation based smart merge.
+//
+//    * If the minimum number of keys can't be extracted, a rotation-based merge is performed.
+//
+// * If auxiliary memory is more or equal than min(len1, len2), a buffered merge is performed.
+//
+// * If the len1 or len2 are less than 2*csqrtlen then a rotation-based merge is performed.
+//
+// * If auxiliary memory is more than csqrtlen+n_keys*sizeof(std::size_t),
+//   then no csqrtlen need to be extracted and "combine_blocks" will use integral
+//   keys to combine blocks.
+template<class RandIt, class Compare, class XBuf>
+void adaptive_merge_impl
+   ( RandIt first
+   , typename iterator_traits<RandIt>::size_type len1
+   , typename iterator_traits<RandIt>::size_type len2
+   , Compare comp
+   , XBuf & xbuf
+   )
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+
+   if(xbuf.capacity() >= min_value<size_type>(len1, len2)){
+      buffered_merge(first, first+len1, first+(len1+len2), comp, xbuf);
+   }
+   else{
+      const size_type len = len1+len2;
+      //Calculate ideal parameters and try to collect needed unique keys
+      size_type l_block = size_type(ceil_sqrt(len));
+
+      //One range is not big enough to extract keys and the internal buffer so a
+      //rotation-based based merge will do just fine
+      if(len1 <= l_block*2 || len2 <= l_block*2){
+         merge_bufferless(first, first+len1, first+len1+len2, comp);
+         return;
+      }
+
+      //Detail the number of keys and internal buffer. If xbuf has enough memory, no
+      //internal buffer is needed so l_intbuf will remain 0.
+      size_type l_intbuf = 0;
+      size_type n_keys = adaptive_merge_n_keys_intbuf(l_block, len1, len2, xbuf, l_intbuf);
+      size_type const to_collect = l_intbuf+n_keys;
+      //Try to extract needed unique values from the first range
+      size_type const collected  = collect_unique(first, first+len1, to_collect, comp, xbuf);
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("\n   A collect: ", len);
+
+      //Not the minimum number of keys is not available on the first range, so fallback to rotations
+      if(collected != to_collect && collected < 4){
+         merge_bufferless(first, first+collected, first+len1, comp);
+         merge_bufferless(first, first + len1, first + len1 + len2, comp);
+         return;
+      }
+
+      //If not enough keys but more than minimum, adjust the internal buffer and key count
+      bool use_internal_buf = collected == to_collect;
+      if (!use_internal_buf){
+         l_intbuf = 0u;
+         n_keys = collected;
+         l_block  = lblock_for_combine(l_intbuf, n_keys, len, use_internal_buf);
+         //If use_internal_buf is false, then then internal buffer will be zero and rotation-based combination will be used
+         l_intbuf = use_internal_buf ? l_block : 0u;
+      }
+
+      bool const xbuf_used = collected == to_collect && xbuf.capacity() >= l_block;
+      //Merge trailing elements using smart merges
+      adaptive_merge_combine_blocks(first, len1, len2, collected,   n_keys, l_block, use_internal_buf, xbuf_used, comp, xbuf);
+      //Merge buffer and keys with the rest of the values
+      adaptive_merge_final_merge   (first, len1, len2, collected, l_intbuf, l_block, use_internal_buf, xbuf_used, comp, xbuf);
+   }
+}
+
+}  //namespace detail_adaptive {
+
+///@endcond
+
+//! <b>Effects</b>: Merges two consecutive sorted ranges [first, middle) and [middle, last)
+//!   into one sorted range [first, last) according to the given comparison function comp.
+//!   The algorithm is stable (if there are equivalent elements in the original two ranges,
+//!   the elements from the first range (preserving their original order) precede the elements
+//!   from the second range (preserving their original order).
+//!
+//! <b>Requires</b>:
+//!   - RandIt must meet the requirements of ValueSwappable and RandomAccessIterator.
+//!   - The type of dereferenced RandIt must meet the requirements of MoveAssignable and MoveConstructible.
+//!
+//! <b>Parameters</b>:
+//!   - first: the beginning of the first sorted range. 
+//!   - middle: the end of the first sorted range and the beginning of the second
+//!   - last: the end of the second sorted range
+//!   - comp: comparison function object which returns true if the first argument is is ordered before the second.
+//!   - uninitialized, uninitialized_len: raw storage starting on "uninitialized", able to hold "uninitialized_len"
+//!      elements of type iterator_traits<RandIt>::value_type. Maximum performance is achieved when uninitialized_len
+//!      is min(std::distance(first, middle), std::distance(middle, last)).
+//!
+//! <b>Throws</b>: If comp throws or the move constructor, move assignment or swap of the type
+//!   of dereferenced RandIt throws.
+//!
+//! <b>Complexity</b>: Always K x O(N) comparisons and move assignments/constructors/swaps.
+//!   Constant factor for comparisons and data movement is minimized when uninitialized_len
+//!   is min(std::distance(first, middle), std::distance(middle, last)).
+//!   Pretty good enough performance is achieved when uninitialized_len is
+//!   ceil(sqrt(std::distance(first, last)))*2.
+//!
+//! <b>Caution</b>: Experimental implementation, not production-ready.
+template<class RandIt, class Compare>
+void adaptive_merge( RandIt first, RandIt middle, RandIt last, Compare comp
+                , typename iterator_traits<RandIt>::value_type* uninitialized = 0
+                , std::size_t uninitialized_len = 0)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+
+   ::boost::movelib::detail_adaptive::adaptive_xbuf<value_type> xbuf(uninitialized, uninitialized_len);
+   ::boost::movelib::detail_adaptive::adaptive_merge_impl(first, size_type(middle - first), size_type(last - middle), comp, xbuf);
+}
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif   //#define BOOST_MOVE_ADAPTIVE_MERGE_HPP
diff --git a/include/boost/move/algo/adaptive_sort.hpp b/include/boost/move/algo/adaptive_sort.hpp
new file mode 100644
index 0000000..4f50bf1
--- /dev/null
+++ b/include/boost/move/algo/adaptive_sort.hpp
@@ -0,0 +1,631 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_MOVE_ADAPTIVE_SORT_HPP
+#define BOOST_MOVE_ADAPTIVE_SORT_HPP
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/algo/detail/adaptive_sort_merge.hpp>
+
+namespace boost {
+namespace movelib {
+
+///@cond
+namespace detail_adaptive {
+
+template<class RandIt>
+void move_data_backward( RandIt cur_pos
+              , typename iterator_traits<RandIt>::size_type const l_data
+              , RandIt new_pos
+              , bool const xbuf_used)
+{
+   //Move buffer to the total combination right
+   if(xbuf_used){
+      boost::move_backward(cur_pos, cur_pos+l_data, new_pos+l_data);      
+   }
+   else{
+      boost::adl_move_swap_ranges_backward(cur_pos, cur_pos+l_data, new_pos+l_data);      
+      //Rotate does less moves but it seems slower due to cache issues
+      //rotate_gcd(first-l_block, first+len-l_block, first+len);
+   }
+}
+
+template<class RandIt>
+void move_data_forward( RandIt cur_pos
+              , typename iterator_traits<RandIt>::size_type const l_data
+              , RandIt new_pos
+              , bool const xbuf_used)
+{
+   //Move buffer to the total combination right
+   if(xbuf_used){
+      boost::move(cur_pos, cur_pos+l_data, new_pos);
+   }
+   else{
+      boost::adl_move_swap_ranges(cur_pos, cur_pos+l_data, new_pos);
+      //Rotate does less moves but it seems slower due to cache issues
+      //rotate_gcd(first-l_block, first+len-l_block, first+len);
+   }
+}
+
+// build blocks of length 2*l_build_buf. l_build_buf is power of two
+// input: [0, l_build_buf) elements are buffer, rest unsorted elements
+// output: [0, l_build_buf) elements are buffer, blocks 2*l_build_buf and last subblock sorted
+//
+// First elements are merged from right to left until elements start
+// at first. All old elements [first, first + l_build_buf) are placed at the end
+// [first+len-l_build_buf, first+len). To achieve this:
+// - If we have external memory to merge, we save elements from the buffer
+//   so that a non-swapping merge is used. Buffer elements are restored
+//   at the end of the buffer from the external memory.
+//
+// - When the external memory is not available or it is insufficient
+//   for a merge operation, left swap merging is used.
+//
+// Once elements are merged left to right in blocks of l_build_buf, then a single left
+// to right merge step is performed to achieve merged blocks of size 2K.
+// If external memory is available, usual merge is used, swap merging otherwise.
+//
+// As a last step, if auxiliary memory is available in-place merge is performed.
+// until all is merged or auxiliary memory is not large enough.
+template<class RandIt, class Compare, class XBuf>
+typename iterator_traits<RandIt>::size_type  
+   adaptive_sort_build_blocks
+      ( RandIt const first
+      , typename iterator_traits<RandIt>::size_type const len
+      , typename iterator_traits<RandIt>::size_type const l_base
+      , typename iterator_traits<RandIt>::size_type const l_build_buf
+      , XBuf & xbuf
+      , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   BOOST_ASSERT(l_build_buf <= len);
+   BOOST_ASSERT(0 == ((l_build_buf / l_base)&(l_build_buf/l_base-1)));
+
+   //Place the start pointer after the buffer
+   RandIt first_block = first + l_build_buf;
+   size_type const elements_in_blocks = len - l_build_buf;
+
+   //////////////////////////////////
+   // Start of merge to left step
+   //////////////////////////////////
+   size_type l_merged = 0u;
+
+   BOOST_ASSERT(l_build_buf);
+   //If there is no enough buffer for the insertion sort step, just avoid the external buffer
+   size_type kbuf = min_value<size_type>(l_build_buf, size_type(xbuf.capacity()));
+   kbuf = kbuf < l_base ? 0 : kbuf;
+
+   if(kbuf){
+      //Backup internal buffer values in external buffer so they can be overwritten
+      xbuf.move_assign(first+l_build_buf-kbuf, kbuf);
+      l_merged = op_insertion_sort_step_left(first_block, elements_in_blocks, l_base, comp, move_op());
+
+      //Now combine them using the buffer. Elements from buffer can be
+      //overwritten since they've been saved to xbuf
+      l_merged = op_merge_left_step_multiple
+         ( first_block - l_merged, elements_in_blocks, l_merged, l_build_buf, kbuf - l_merged, comp, move_op());
+
+      //Restore internal buffer from external buffer unless kbuf was l_build_buf,
+      //in that case restoration will happen later
+      if(kbuf != l_build_buf){
+         boost::move(xbuf.data()+kbuf-l_merged, xbuf.data() + kbuf, first_block-l_merged+elements_in_blocks);
+      }
+   }
+   else{
+      l_merged = insertion_sort_step(first_block, elements_in_blocks, l_base, comp);
+      rotate_gcd(first_block - l_merged, first_block, first_block+elements_in_blocks);
+   }
+
+   //Now combine elements using the buffer. Elements from buffer can't be
+   //overwritten since xbuf was not big enough, so merge swapping elements.
+   l_merged = op_merge_left_step_multiple
+      (first_block - l_merged, elements_in_blocks, l_merged, l_build_buf, l_build_buf - l_merged, comp, swap_op());
+
+   BOOST_ASSERT(l_merged == l_build_buf);
+
+   //////////////////////////////////
+   // Start of merge to right step
+   //////////////////////////////////
+
+   //If kbuf is l_build_buf then we can merge right without swapping
+   //Saved data is still in xbuf
+   if(kbuf && kbuf == l_build_buf){
+      op_merge_right_step_once(first, elements_in_blocks, l_build_buf, comp, move_op());
+      //Restore internal buffer from external buffer if kbuf was l_build_buf.
+      //as this operation was previously delayed.
+      boost::move(xbuf.data(), xbuf.data() + kbuf, first);
+   }
+   else{
+      op_merge_right_step_once(first, elements_in_blocks, l_build_buf, comp, swap_op());
+   }
+   xbuf.clear();
+   //2*l_build_buf or total already merged
+   return min_value(elements_in_blocks, 2*l_build_buf);
+}
+
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class XBuf>
+void adaptive_sort_combine_blocks
+   ( RandItKeys const keys
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const len
+   , typename iterator_traits<RandIt>::size_type const l_prev_merged
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , bool const use_buf
+   , bool const xbuf_used
+   , XBuf & xbuf
+   , Compare comp
+   , bool merge_left)
+{
+   (void)xbuf;
+   typedef typename iterator_traits<RandIt>::size_type   size_type;
+
+   size_type const l_reg_combined   = 2*l_prev_merged;
+   size_type l_irreg_combined = 0;
+   size_type const l_total_combined = calculate_total_combined(len, l_prev_merged, &l_irreg_combined);
+   size_type const n_reg_combined = len/l_reg_combined;
+   RandIt combined_first = first;
+
+   (void)l_total_combined;
+   BOOST_ASSERT(l_total_combined <= len);
+
+   size_type const max_i = n_reg_combined + (l_irreg_combined != 0);
+
+   if(merge_left || !use_buf) {
+      for( size_type combined_i = 0; combined_i != max_i; ++combined_i, combined_first += l_reg_combined) {
+         //Now merge blocks
+         bool const is_last = combined_i==n_reg_combined;
+         size_type const l_cur_combined = is_last ? l_irreg_combined : l_reg_combined;
+
+         range_xbuf<RandIt, move_op> rbuf( (use_buf && xbuf_used) ? (combined_first-l_block) : combined_first, combined_first);
+         size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
+         combine_params( keys, key_comp, l_cur_combined
+                        , l_prev_merged, l_block, rbuf
+                        , n_block_a, n_block_b, l_irreg1, l_irreg2);   //Outputs
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A combpar:            ", len + l_block);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(combined_first, combined_first + n_block_a*l_block+l_irreg1, comp));
+            BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(combined_first + n_block_a*l_block+l_irreg1, combined_first + n_block_a*l_block+l_irreg1+n_block_b*l_block+l_irreg2, comp));
+         if(!use_buf){
+            merge_blocks_bufferless
+               (keys, key_comp, combined_first, l_block, 0u, n_block_a, n_block_b, l_irreg2, comp);
+         }
+         else{
+            merge_blocks_left
+               (keys, key_comp, combined_first, l_block, 0u, n_block_a, n_block_b, l_irreg2, comp, xbuf_used);
+         }
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   After merge_blocks_L: ", len + l_block);
+      }
+   }
+   else{
+      combined_first += l_reg_combined*(max_i-1);
+      for( size_type combined_i = max_i; combined_i--; combined_first -= l_reg_combined) {
+         bool const is_last = combined_i==n_reg_combined;
+         size_type const l_cur_combined = is_last ? l_irreg_combined : l_reg_combined;
+
+         RandIt const combined_last(combined_first+l_cur_combined);
+         range_xbuf<RandIt, move_op> rbuf(combined_last, xbuf_used ? (combined_last+l_block) : combined_last);
+         size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
+         combine_params( keys, key_comp, l_cur_combined
+                        , l_prev_merged, l_block, rbuf
+                        , n_block_a, n_block_b, l_irreg1, l_irreg2);  //Outputs
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   A combpar:            ", len + l_block);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(combined_first, combined_first + n_block_a*l_block+l_irreg1, comp));
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(combined_first + n_block_a*l_block+l_irreg1, combined_first + n_block_a*l_block+l_irreg1+n_block_b*l_block+l_irreg2, comp));
+         merge_blocks_right
+            (keys, key_comp, combined_first, l_block, n_block_a, n_block_b, l_irreg2, comp, xbuf_used);
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   After merge_blocks_R: ", len + l_block);
+      }
+   }
+}
+
+//Returns true if buffer is placed in 
+//[buffer+len-l_intbuf, buffer+len). Otherwise, buffer is
+//[buffer,buffer+l_intbuf)
+template<class RandIt, class Compare, class XBuf>
+bool adaptive_sort_combine_all_blocks
+   ( RandIt keys
+   , typename iterator_traits<RandIt>::size_type &n_keys
+   , RandIt const buffer
+   , typename iterator_traits<RandIt>::size_type const l_buf_plus_data
+   , typename iterator_traits<RandIt>::size_type l_merged
+   , typename iterator_traits<RandIt>::size_type &l_intbuf
+   , XBuf & xbuf
+   , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   RandIt const first = buffer + l_intbuf;
+   size_type const l_data = l_buf_plus_data - l_intbuf;
+   size_type const l_unique = l_intbuf+n_keys;
+   //Backup data to external buffer once if possible
+   bool const common_xbuf = l_data > l_merged && l_intbuf && l_intbuf <= xbuf.capacity();
+   if(common_xbuf){
+      xbuf.move_assign(buffer, l_intbuf);
+   }
+
+   bool prev_merge_left = true;
+   size_type l_prev_total_combined = l_merged, l_prev_block = 0;
+   bool prev_use_internal_buf = true;
+
+   for( size_type n = 0; l_data > l_merged
+      ; l_merged*=2
+      , ++n){
+      //If l_intbuf is non-zero, use that internal buffer.
+      //    Implies l_block == l_intbuf && use_internal_buf == true
+      //If l_intbuf is zero, see if half keys can be reused as a reduced emergency buffer,
+      //    Implies l_block == n_keys/2 && use_internal_buf == true
+      //Otherwise, just give up and and use all keys to merge using rotations (use_internal_buf = false)
+      bool use_internal_buf = false;
+      size_type const l_block = lblock_for_combine(l_intbuf, n_keys, 2*l_merged, use_internal_buf);
+      BOOST_ASSERT(!l_intbuf || (l_block == l_intbuf));
+      BOOST_ASSERT(n == 0 || (!use_internal_buf || prev_use_internal_buf) );
+      BOOST_ASSERT(n == 0 || (!use_internal_buf || l_prev_block == l_block) );
+      
+      bool const is_merge_left = (n&1) == 0;
+      size_type const l_total_combined = calculate_total_combined(l_data, l_merged);
+      if(n && prev_use_internal_buf && prev_merge_left){
+         if(is_merge_left || !use_internal_buf){
+            move_data_backward(first-l_prev_block, l_prev_total_combined, first, common_xbuf);
+         }
+         else{
+            //Put the buffer just after l_total_combined
+            RandIt const buf_end = first+l_prev_total_combined;
+            RandIt const buf_beg = buf_end-l_block;
+            if(l_prev_total_combined > l_total_combined){
+               size_type const l_diff = l_prev_total_combined - l_total_combined;
+               move_data_backward(buf_beg-l_diff, l_diff, buf_end-l_diff, common_xbuf);
+            }
+            else if(l_prev_total_combined < l_total_combined){
+               size_type const l_diff = l_total_combined - l_prev_total_combined;
+               move_data_forward(buf_end, l_diff, buf_beg, common_xbuf);
+            }
+         }
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   After move_data     : ", l_data + l_intbuf);
+      }
+
+      //Combine to form l_merged*2 segments
+      if(n_keys){
+         size_type upper_n_keys_this_iter = 2*l_merged/l_block;
+         if(upper_n_keys_this_iter > 256){
+            adaptive_sort_combine_blocks
+               ( keys, comp, !use_internal_buf || is_merge_left ? first : first-l_block
+               , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
+         }
+         else{
+            unsigned char uint_keys[256];
+            adaptive_sort_combine_blocks
+               ( uint_keys, less(), !use_internal_buf || is_merge_left ? first : first-l_block
+               , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
+            }
+      }
+      else{
+         size_type *const uint_keys = xbuf.template aligned_trailing<size_type>();
+         adaptive_sort_combine_blocks
+            ( uint_keys, less(), !use_internal_buf || is_merge_left ? first : first-l_block
+            , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
+      }
+
+      BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(is_merge_left ? "   After comb blocks L:  " : "   After comb blocks R:  ", l_data + l_intbuf);
+      prev_merge_left = is_merge_left;
+      l_prev_total_combined = l_total_combined;
+      l_prev_block = l_block;
+      prev_use_internal_buf = use_internal_buf;
+   }
+   BOOST_ASSERT(l_prev_total_combined == l_data);
+   bool const buffer_right = prev_use_internal_buf && prev_merge_left;
+
+   l_intbuf = prev_use_internal_buf ? l_prev_block : 0u;
+   n_keys = l_unique - l_intbuf;
+   //Restore data from to external common buffer if used
+   if(common_xbuf){
+      if(buffer_right){
+         boost::move(xbuf.data(), xbuf.data() + l_intbuf, buffer+l_data);
+      }
+      else{
+         boost::move(xbuf.data(), xbuf.data() + l_intbuf, buffer);
+      }
+   }
+   return buffer_right;
+}
+
+
+template<class RandIt, class Compare, class XBuf>
+void adaptive_sort_final_merge( bool buffer_right
+                              , RandIt const first
+                              , typename iterator_traits<RandIt>::size_type const l_intbuf
+                              , typename iterator_traits<RandIt>::size_type const n_keys
+                              , typename iterator_traits<RandIt>::size_type const len
+                              , XBuf & xbuf
+                              , Compare comp)
+{
+   //BOOST_ASSERT(n_keys || xbuf.size() == l_intbuf);
+   xbuf.clear();
+
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   size_type const n_key_plus_buf = l_intbuf+n_keys;
+   if(buffer_right){
+      //Use stable sort as some buffer elements might not be unique (see non_unique_buf)
+      stable_sort(first+len-l_intbuf, first+len, comp, xbuf);
+      stable_merge(first+n_keys, first+len-l_intbuf, first+len, antistable<Compare>(comp), xbuf);
+      unstable_sort(first, first+n_keys, comp, xbuf);
+      stable_merge(first, first+n_keys, first+len, comp, xbuf);
+   }
+   else{
+      //Use stable sort as some buffer elements might not be unique (see non_unique_buf)
+      stable_sort(first, first+n_key_plus_buf, comp, xbuf);
+      if(xbuf.capacity() >= n_key_plus_buf){
+         buffered_merge(first, first+n_key_plus_buf, first+len, comp, xbuf);
+      }
+      else if(xbuf.capacity() >= min_value<size_type>(l_intbuf, n_keys)){
+         stable_merge(first+n_keys, first+n_key_plus_buf, first+len, comp, xbuf);
+         stable_merge(first, first+n_keys, first+len, comp, xbuf);
+      }
+      else{
+         stable_merge(first, first+n_key_plus_buf, first+len, comp, xbuf);
+      }
+   }
+   BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   After final_merge   : ", len);
+}
+
+template<class RandIt, class Compare, class Unsigned, class XBuf>
+bool adaptive_sort_build_params
+   (RandIt first, Unsigned const len, Compare comp
+   , Unsigned &n_keys, Unsigned &l_intbuf, Unsigned &l_base, Unsigned &l_build_buf
+   , XBuf & xbuf
+   )
+{
+   typedef Unsigned size_type;
+
+   //Calculate ideal parameters and try to collect needed unique keys
+   l_base = 0u;
+
+   //Try to find a value near sqrt(len) that is 2^N*l_base where
+   //l_base <= AdaptiveSortInsertionSortThreshold. This property is important
+   //as build_blocks merges to the left iteratively duplicating the
+   //merged size and all the buffer must be used just before the final
+   //merge to right step. This guarantees "build_blocks" produces 
+   //segments of size l_build_buf*2, maximizing the classic merge phase.
+   l_intbuf = size_type(ceil_sqrt_multiple(len, &l_base));
+
+   //The internal buffer can be expanded if there is enough external memory
+   while(xbuf.capacity() >= l_intbuf*2){
+      l_intbuf *= 2;
+   }
+
+   //This is the minimum number of keys to implement the ideal algorithm
+   //
+   //l_intbuf is used as buffer plus the key count
+   size_type n_min_ideal_keys = l_intbuf-1;
+   while(n_min_ideal_keys >= (len-l_intbuf-n_min_ideal_keys)/l_intbuf){
+      --n_min_ideal_keys;
+   }
+   n_min_ideal_keys += 1;
+   BOOST_ASSERT(n_min_ideal_keys <= l_intbuf);
+
+   if(xbuf.template supports_aligned_trailing<size_type>(l_intbuf, (len-l_intbuf-1)/l_intbuf+1)){
+      n_keys = 0u;
+      l_build_buf = l_intbuf;
+   }
+   else{
+      //Try to achieve a l_build_buf of length l_intbuf*2, so that we can merge with that
+      //l_intbuf*2 buffer in "build_blocks" and use half of them as buffer and the other half
+      //as keys in combine_all_blocks. In that case n_keys >= n_min_ideal_keys but by a small margin.
+      //
+      //If available memory is 2*sqrt(l), then only sqrt(l) unique keys are needed,
+      //(to be used for keys in combine_all_blocks) as the whole l_build_buf
+      //will be backuped in the buffer during build_blocks.
+      bool const non_unique_buf = xbuf.capacity() >= l_intbuf;
+      size_type const to_collect = non_unique_buf ? n_min_ideal_keys : l_intbuf*2;
+      size_type collected = collect_unique(first, first+len, to_collect, comp, xbuf);
+
+      //If available memory is 2*sqrt(l), then for "build_params" 
+      //the situation is the same as if 2*l_intbuf were collected.
+      if(non_unique_buf && collected == n_min_ideal_keys){
+         l_build_buf = l_intbuf;
+         n_keys = n_min_ideal_keys;
+      }
+      else if(collected == 2*l_intbuf){
+         //l_intbuf*2 elements found. Use all of them in the build phase 
+         l_build_buf = l_intbuf*2;
+         n_keys = l_intbuf;
+      }
+      else if(collected == (n_min_ideal_keys+l_intbuf)){ 
+         l_build_buf = l_intbuf;
+         n_keys = n_min_ideal_keys;
+      }
+      //If collected keys are not enough, try to fix n_keys and l_intbuf. If no fix
+      //is possible (due to very low unique keys), then go to a slow sort based on rotations.
+      else{
+         BOOST_ASSERT(collected < (n_min_ideal_keys+l_intbuf));
+         if(collected < 4){  //No combination possible with less that 4 keys
+            return false;
+         }
+         n_keys = l_intbuf;
+         while(n_keys&(n_keys-1)){
+            n_keys &= n_keys-1;  // make it power or 2
+         }
+         while(n_keys > collected){
+            n_keys/=2;
+         }
+         //AdaptiveSortInsertionSortThreshold is always power of two so the minimum is power of two
+         l_base = min_value<Unsigned>(n_keys, AdaptiveSortInsertionSortThreshold);
+         l_intbuf = 0;
+         l_build_buf = n_keys;
+      }
+      BOOST_ASSERT((n_keys+l_intbuf) >= l_build_buf);
+   }
+
+   return true;
+}
+
+// Main explanation of the sort algorithm.
+//
+// csqrtlen = ceil(sqrt(len));
+//
+// * First, 2*csqrtlen unique elements elements are extracted from elements to be
+//   sorted and placed in the beginning of the range.
+//
+// * Step "build_blocks": In this nearly-classic merge step, 2*csqrtlen unique elements
+//   will be used as auxiliary memory, so trailing len-2*csqrtlen elements are
+//   are grouped in blocks of sorted 4*csqrtlen elements. At the end of the step
+//   2*csqrtlen unique elements are again the leading elements of the whole range.
+//
+// * Step "combine_blocks": pairs of previously formed blocks are merged with a different
+//   ("smart") algorithm to form blocks of 8*csqrtlen elements. This step is slower than the
+//   "build_blocks" step and repeated iteratively (forming blocks of 16*csqrtlen, 32*csqrtlen
+//   elements, etc) of until all trailing (len-2*csqrtlen) elements are merged.
+//
+//   In "combine_blocks" len/csqrtlen elements used are as "keys" (markers) to
+//   know if elements belong to the first or second block to be merged and another 
+//   leading csqrtlen elements are used as buffer. Explanation of the "combine_blocks" step:
+//
+//   Iteratively until all trailing (len-2*csqrtlen) elements are merged:
+//      Iteratively for each pair of previously merged block:
+//         * Blocks are divided groups of csqrtlen elements and
+//           2*merged_block/csqrtlen keys are sorted to be used as markers
+//         * Groups are selection-sorted by first or last element (depending whether they are going
+//           to be merged to left or right) and keys are reordered accordingly as an imitation-buffer.
+//         * Elements of each block pair are merged using the csqrtlen buffer taking into account
+//           if they belong to the first half or second half (marked by the key).
+//
+// * In the final merge step leading elements (2*csqrtlen) are sorted and merged with
+//   rotations with the rest of sorted elements in the "combine_blocks" step.
+//
+// Corner cases:
+//
+// * If no 2*csqrtlen elements can be extracted:
+//
+//    * If csqrtlen+len/csqrtlen are extracted, then only csqrtlen elements are used
+//      as buffer in the "build_blocks" step forming blocks of 2*csqrtlen elements. This
+//      means that an additional "combine_blocks" step will be needed to merge all elements.
+//    
+//    * If no csqrtlen+len/csqrtlen elements can be extracted, but still more than a minimum,
+//      then reduces the number of elements used as buffer and keys in the "build_blocks"
+//      and "combine_blocks" steps. If "combine_blocks" has no enough keys due to this reduction
+//      then uses a rotation based smart merge.
+//
+//    * If the minimum number of keys can't be extracted, a rotation-based sorting is performed.
+//
+// * If auxiliary memory is more or equal than ceil(len/2), half-copying mergesort is used.
+//
+// * If auxiliary memory is more than csqrtlen+n_keys*sizeof(std::size_t),
+//   then only csqrtlen elements need to be extracted and "combine_blocks" will use integral
+//   keys to combine blocks.
+//
+// * If auxiliary memory is available, the "build_blocks" will be extended to build bigger blocks
+//   using classic merge and "combine_blocks" will use bigger blocks when merging.
+template<class RandIt, class Compare, class XBuf>
+void adaptive_sort_impl
+   ( RandIt first
+   , typename iterator_traits<RandIt>::size_type const len
+   , Compare comp
+   , XBuf & xbuf
+   )
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+
+   //Small sorts go directly to insertion sort
+   if(len <= size_type(AdaptiveSortInsertionSortThreshold)){
+      insertion_sort(first, first + len, comp);
+   }
+   else if((len-len/2) <= xbuf.capacity()){
+      merge_sort(first, first+len, comp, xbuf.data());
+   }
+   else{
+      //Make sure it is at least four
+      BOOST_STATIC_ASSERT(AdaptiveSortInsertionSortThreshold >= 4);
+
+      size_type l_base = 0;
+      size_type l_intbuf = 0;
+      size_type n_keys = 0;
+      size_type l_build_buf = 0;
+
+      //Calculate and extract needed unique elements. If a minimum is not achieved
+      //fallback to a slow stable sort
+      if(!adaptive_sort_build_params(first, len, comp, n_keys, l_intbuf, l_base, l_build_buf, xbuf)){
+         stable_sort(first, first+len, comp, xbuf);
+      }
+      else{
+         BOOST_ASSERT(l_build_buf);
+         //Otherwise, continue the adaptive_sort
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("\n   After collect_unique: ", len);
+         size_type const n_key_plus_buf = l_intbuf+n_keys;
+         //l_build_buf is always power of two if l_intbuf is zero
+         BOOST_ASSERT(l_intbuf || (0 == (l_build_buf & (l_build_buf-1))));
+
+         //Classic merge sort until internal buffer and xbuf are exhausted
+         size_type const l_merged = adaptive_sort_build_blocks
+            (first+n_key_plus_buf-l_build_buf, len-n_key_plus_buf+l_build_buf, l_base, l_build_buf, xbuf, comp);
+         BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("   After build_blocks:   ", len);
+
+         //Non-trivial merge
+         bool const buffer_right = adaptive_sort_combine_all_blocks
+            (first, n_keys, first+n_keys, len-n_keys, l_merged, l_intbuf, xbuf, comp);
+
+         //Sort keys and buffer and merge the whole sequence
+         adaptive_sort_final_merge(buffer_right, first, l_intbuf, n_keys, len, xbuf, comp);
+      }
+   }
+}
+
+}  //namespace detail_adaptive {
+
+///@endcond
+
+//! <b>Effects</b>: Sorts the elements in the range [first, last) in ascending order according
+//!   to comparison functor "comp". The sort is stable (order of equal elements
+//!   is guaranteed to be preserved). Performance is improved if additional raw storage is
+//!   provided.
+//!
+//! <b>Requires</b>:
+//!   - RandIt must meet the requirements of ValueSwappable and RandomAccessIterator.
+//!   - The type of dereferenced RandIt must meet the requirements of MoveAssignable and MoveConstructible.
+//!
+//! <b>Parameters</b>:
+//!   - first, last: the range of elements to sort
+//!   - comp: comparison function object which returns true if the first argument is is ordered before the second.
+//!   - uninitialized, uninitialized_len: raw storage starting on "uninitialized", able to hold "uninitialized_len"
+//!      elements of type iterator_traits<RandIt>::value_type. Maximum performance is achieved when uninitialized_len
+//!      is ceil(std::distance(first, last)/2).
+//!
+//! <b>Throws</b>: If comp throws or the move constructor, move assignment or swap of the type
+//!   of dereferenced RandIt throws.
+//!
+//! <b>Complexity</b>: Always K x O(Nxlog(N)) comparisons and move assignments/constructors/swaps.
+//!   Comparisons are close to minimum even with no additional memory. Constant factor for data movement is minimized
+//!   when uninitialized_len is ceil(std::distance(first, last)/2). Pretty good enough performance is achieved when
+//!   ceil(sqrt(std::distance(first, last)))*2.
+//!
+//! <b>Caution</b>: Experimental implementation, not production-ready.
+template<class RandIt, class RandRawIt, class Compare>
+void adaptive_sort( RandIt first, RandIt last, Compare comp
+               , RandRawIt uninitialized
+               , std::size_t uninitialized_len)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+
+   ::boost::movelib::detail_adaptive::adaptive_xbuf<value_type, RandRawIt> xbuf(uninitialized, uninitialized_len);
+   ::boost::movelib::detail_adaptive::adaptive_sort_impl(first, size_type(last - first), comp, xbuf);
+}
+
+template<class RandIt, class Compare>
+void adaptive_sort( RandIt first, RandIt last, Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+   adaptive_sort(first, last, comp, (value_type*)0, 0u);
+}
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif   //#define BOOST_MOVE_ADAPTIVE_SORT_HPP
diff --git a/include/boost/move/algo/detail/adaptive_sort_merge.hpp b/include/boost/move/algo/detail/adaptive_sort_merge.hpp
new file mode 100644
index 0000000..4c28086
--- /dev/null
+++ b/include/boost/move/algo/detail/adaptive_sort_merge.hpp
@@ -0,0 +1,1690 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// Stable sorting that works in O(N*log(N)) worst time
+// and uses O(1) extra memory
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// The main idea of the adaptive_sort algorithm was developed by Andrey Astrelin
+// and explained in the article from the russian collaborative blog
+// Habrahabr (http://habrahabr.ru/post/205290/). The algorithm is based on
+// ideas from B-C. Huang and M. A. Langston explained in their article
+// "Fast Stable Merging and Sorting in Constant Extra Space (1989-1992)"
+// (http://comjnl.oxfordjournals.org/content/35/6/643.full.pdf).
+//
+// This implementation by Ion Gaztanaga uses previous ideas with additional changes:
+// 
+// - Use of GCD-based rotation.
+// - Non power of two buffer-sizes.
+// - Tries to find sqrt(len)*2 unique keys, so that the merge sort
+//   phase can form up to sqrt(len)*4 segments if enough keys are found.
+// - The merge-sort phase can take advantage of external memory to
+//   save some additional combination steps.
+// - Combination phase: Blocks are selection sorted and merged in parallel.
+// - The combination phase is performed alternating merge to left and merge
+//   to right phases minimizing swaps due to internal buffer repositioning.
+// - When merging blocks special optimizations are made to avoid moving some
+//   elements twice.
+//
+// The adaptive_merge algorithm was developed by Ion Gaztanaga reusing some parts
+// from the sorting algorithm and implementing an additional block merge algorithm
+// without moving elements to left or right.
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
+#define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/detail/reverse_iterator.hpp>
+#include <boost/move/algo/move.hpp>
+#include <boost/move/algo/detail/merge.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/algo/detail/insertion_sort.hpp>
+#include <boost/move/algo/detail/merge_sort.hpp>
+#include <boost/move/algo/detail/heap_sort.hpp>
+#include <boost/move/algo/detail/merge.hpp>
+#include <boost/move/algo/detail/is_sorted.hpp>
+#include <boost/assert.hpp>
+#include <boost/cstdint.hpp>
+
+#ifndef BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL
+   #define BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL 1
+#endif
+
+#ifdef BOOST_MOVE_ADAPTIVE_SORT_STATS
+   #if BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL == 2
+      #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
+         print_stats(STR, L)\
+      //
+
+      #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L) \
+         print_stats(STR, L)\
+      //
+   #else
+      #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
+         print_stats(STR, L)\
+      //
+
+      #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
+   #endif
+#else
+   #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L)
+   #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
+#endif
+
+#ifdef BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
+   #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT  BOOST_ASSERT
+#else
+   #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(L)
+#endif
+
+namespace boost {
+namespace movelib {
+
+#if defined(BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS)
+
+bool is_sorted(::order_perf_type *first, ::order_perf_type *last, ::order_type_less)
+{
+   if (first != last) {
+      const order_perf_type *next = first, *cur(first);
+      while (++next != last) {
+         if (!(cur->key < next->key || (cur->key == next->key && cur->val < next->val)))
+            return false;
+         cur = next;
+      }
+   }
+   return true;
+}
+
+#endif   //BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
+
+namespace detail_adaptive {
+
+static const std::size_t AdaptiveSortInsertionSortThreshold = 16;
+//static const std::size_t AdaptiveSortInsertionSortThreshold = 4;
+BOOST_STATIC_ASSERT((AdaptiveSortInsertionSortThreshold&(AdaptiveSortInsertionSortThreshold-1)) == 0);
+
+#if defined BOOST_HAS_INTPTR_T
+   typedef ::boost::uintptr_t uintptr_t;
+#else
+   typedef std::size_t uintptr_t;
+#endif
+
+template<class T>
+const T &min_value(const T &a, const T &b)
+{
+   return a < b ? a : b;
+}
+
+template<class T>
+const T &max_value(const T &a, const T &b)
+{
+   return a > b ? a : b;
+}
+
+template<class ForwardIt, class Pred, class V>
+typename iterator_traits<ForwardIt>::size_type
+   count_if_with(ForwardIt first, ForwardIt last, Pred pred, const V &v)
+{
+   typedef typename iterator_traits<ForwardIt>::size_type size_type;
+   size_type count = 0;
+   while(first != last) {
+      count += static_cast<size_type>(0 != pred(*first, v));
+      ++first;
+   }
+   return count;
+}
+
+template<class T, class RandRawIt = T*>
+class adaptive_xbuf
+{
+   adaptive_xbuf(const adaptive_xbuf &);
+   adaptive_xbuf & operator=(const adaptive_xbuf &);
+
+   public:
+   typedef RandRawIt iterator;
+
+   adaptive_xbuf()
+      : m_ptr(), m_size(0), m_capacity(0)
+   {}
+
+   adaptive_xbuf(RandRawIt raw_memory, std::size_t capacity)
+      : m_ptr(raw_memory), m_size(0), m_capacity(capacity)
+   {}
+
+   template<class RandIt>
+   void move_assign(RandIt first, std::size_t n)
+   {
+      if(n <= m_size){
+         boost::move(first, first+n, m_ptr);
+         std::size_t size = m_size;
+         while(size-- != n){
+            m_ptr[size].~T();
+         }
+         m_size = n;
+      }
+      else{
+         RandRawIt result = boost::move(first, first+m_size, m_ptr);
+         boost::uninitialized_move(first+m_size, first+n, result);
+         m_size = n;
+      }
+   }
+
+   template<class RandIt>
+   void push_back(RandIt first, std::size_t n)
+   {
+      BOOST_ASSERT(m_capacity - m_size >= n);
+      boost::uninitialized_move(first, first+n, m_ptr+m_size);
+      m_size += n;
+   }
+
+   template<class RandIt>
+   iterator add(RandIt it)
+   {
+      BOOST_ASSERT(m_size < m_capacity);
+      RandRawIt p_ret = m_ptr + m_size;
+      ::new(&*p_ret) T(::boost::move(*it));
+      ++m_size;
+      return p_ret;
+   }
+
+   template<class RandIt>
+   void insert(iterator pos, RandIt it)
+   {
+      if(pos == (m_ptr + m_size)){
+         this->add(it);
+      }
+      else{
+         this->add(m_ptr+m_size-1);
+         //m_size updated
+         boost::move_backward(pos, m_ptr+m_size-2, m_ptr+m_size-1);
+         *pos = boost::move(*it);
+      }
+   }
+
+   void set_size(std::size_t size)
+   {
+      m_size = size;
+   }
+
+   void shrink_to_fit(std::size_t const size)
+   {
+      if(m_size > size){
+         for(std::size_t szt_i = size; szt_i != m_size; ++szt_i){
+            m_ptr[szt_i].~T();
+         }
+         m_size = size;
+      }
+   }
+
+   void initialize_until(std::size_t const size, T &t)
+   {
+      BOOST_ASSERT(m_size < m_capacity);
+      if(m_size < size){
+         ::new((void*)&m_ptr[m_size]) T(::boost::move(t));
+         ++m_size;
+         for(; m_size != size; ++m_size){
+            ::new((void*)&m_ptr[m_size]) T(::boost::move(m_ptr[m_size-1]));
+         }
+         t = ::boost::move(m_ptr[m_size-1]);
+      }
+   }
+
+   private:
+   template<class RIt>
+   static bool is_raw_ptr(RIt)
+   {
+      return false;
+   }
+
+   static bool is_raw_ptr(T*)
+   {
+      return true;
+   }
+
+   public:
+   template<class U>
+   bool supports_aligned_trailing(std::size_t size, std::size_t trail_count) const
+   {
+      if(this->is_raw_ptr(this->data()) && m_capacity){
+         uintptr_t u_addr_sz = uintptr_t(&*(this->data()+size));
+         uintptr_t u_addr_cp = uintptr_t(&*(this->data()+this->capacity()));
+         u_addr_sz = ((u_addr_sz + sizeof(U)-1)/sizeof(U))*sizeof(U);
+         return (u_addr_cp >= u_addr_sz) && ((u_addr_cp - u_addr_sz)/sizeof(U) >= trail_count);
+      }
+      return false;
+   }
+
+   template<class U>
+   U *aligned_trailing() const
+   {
+      return this->aligned_trailing<U>(this->size());
+   }
+
+   template<class U>
+   U *aligned_trailing(std::size_t pos) const
+   {
+      uintptr_t u_addr = uintptr_t(&*(this->data()+pos));
+      u_addr = ((u_addr + sizeof(U)-1)/sizeof(U))*sizeof(U);
+      return (U*)u_addr;
+   }
+
+   ~adaptive_xbuf()
+   {
+      this->clear();
+   }
+
+   std::size_t capacity() const
+   {  return m_capacity;   }
+
+   iterator data() const
+   {  return m_ptr;   }
+
+   iterator end() const
+   {  return m_ptr+m_size;   }
+
+   std::size_t size() const
+   {  return m_size;   }
+
+   bool empty() const
+   {  return !m_size;   }
+
+   void clear()
+   {
+      this->shrink_to_fit(0u);
+   }
+
+   private:
+   RandRawIt m_ptr;
+   std::size_t m_size;
+   std::size_t m_capacity;
+};
+
+template<class Iterator, class Op>
+class range_xbuf
+{
+   range_xbuf(const range_xbuf &);
+   range_xbuf & operator=(const range_xbuf &);
+
+   public:
+   typedef typename iterator_traits<Iterator>::size_type size_type;
+   typedef Iterator iterator;
+
+   range_xbuf(Iterator first, Iterator last)
+      : m_first(first), m_last(first), m_cap(last)
+   {}
+
+   template<class RandIt>
+   void move_assign(RandIt first, std::size_t n)
+   {
+      BOOST_ASSERT(size_type(n) <= size_type(m_cap-m_first));
+      m_last = Op()(forward_t(), first, first+n, m_first);
+   }
+
+   ~range_xbuf()
+   {}
+
+   std::size_t capacity() const
+   {  return m_cap-m_first;   }
+
+   Iterator data() const
+   {  return m_first;   }
+
+   Iterator end() const
+   {  return m_last;   }
+
+   std::size_t size() const
+   {  return m_last-m_first;   }
+
+   bool empty() const
+   {  return m_first == m_last;   }
+
+   void clear()
+   {
+      m_last = m_first;
+   }
+
+   template<class RandIt>
+   iterator add(RandIt it)
+   {
+      Iterator pos(m_last);
+      *pos = boost::move(*it);
+      ++m_last;
+      return pos;
+   }
+
+   void set_size(std::size_t size)
+   {
+      m_last  = m_first;
+      m_last += size;
+   }
+
+   private:
+   Iterator const m_first;
+   Iterator m_last;
+   Iterator const m_cap;
+};
+
+
+template<class RandIt, class Compare>
+RandIt skip_until_merge
+   ( RandIt first1, RandIt const last1
+   , const typename iterator_traits<RandIt>::value_type &next_key, Compare comp)
+{
+   while(first1 != last1 && !comp(next_key, *first1)){
+      ++first1;
+   }
+   return first1;
+}
+
+
+template<class RandItKeys, class RandIt>
+void swap_and_update_key
+   ( RandItKeys const key_next
+   , RandItKeys const key_range2
+   , RandItKeys &key_mid
+   , RandIt const begin
+   , RandIt const end
+   , RandIt const with)
+{
+   if(begin != with){
+      ::boost::adl_move_swap_ranges(begin, end, with);
+      ::boost::adl_move_swap(*key_next, *key_range2);
+      if(key_next == key_mid){
+         key_mid = key_range2;
+      }
+      else if(key_mid == key_range2){
+         key_mid = key_next;
+      }
+   }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//                         MERGE BUFFERLESS
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// [first1, last1) merge [last1,last2) -> [first1,last2)
+template<class RandIt, class Compare>
+RandIt partial_merge_bufferless_impl
+   (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
+{
+   if(last1 == last2){
+      return first1;
+   }
+   bool const is_range1_A = *pis_range1_A;
+   if(first1 != last1 && comp(*last1, last1[-1])){
+      do{
+         RandIt const old_last1 = last1;
+         last1  = boost::movelib::lower_bound(last1, last2, *first1, comp);
+         first1 = rotate_gcd(first1, old_last1, last1);//old_last1 == last1 supported
+         if(last1 == last2){
+            return first1;
+         }
+         do{
+            ++first1;
+         } while(last1 != first1 && !comp(*last1, *first1) );
+      } while(first1 != last1);
+   }
+   *pis_range1_A = !is_range1_A;
+   return last1;
+}
+
+// [first1, last1) merge [last1,last2) -> [first1,last2)
+template<class RandIt, class Compare>
+RandIt partial_merge_bufferless
+   (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
+{
+   return *pis_range1_A ? partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, comp)
+                        : partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, antistable<Compare>(comp));
+}
+
+template<class SizeType>
+static SizeType needed_keys_count(SizeType n_block_a, SizeType n_block_b)
+{
+   return n_block_a + n_block_b;
+}
+
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
+typename iterator_traits<RandIt>::size_type
+   find_next_block
+      ( RandItKeys key_first
+      , KeyCompare key_comp
+      , RandIt const first
+      , typename iterator_traits<RandIt>::size_type const l_block
+      , typename iterator_traits<RandIt>::size_type const ix_first_block
+      , typename iterator_traits<RandIt>::size_type const ix_last_block
+      , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type      size_type;
+   typedef typename iterator_traits<RandIt>::value_type     value_type;
+   typedef typename iterator_traits<RandItKeys>::value_type key_type;
+   BOOST_ASSERT(ix_first_block <= ix_last_block);
+   size_type ix_min_block = 0u;
+   for (size_type szt_i = ix_first_block; szt_i < ix_last_block; ++szt_i) {
+      const value_type &min_val = first[ix_min_block*l_block];
+      const value_type &cur_val = first[szt_i*l_block];
+      const key_type   &min_key = key_first[ix_min_block];
+      const key_type   &cur_key = key_first[szt_i];
+
+      bool const less_than_minimum = comp(cur_val, min_val) ||
+         (!comp(min_val, cur_val) && key_comp(cur_key, min_key));
+
+      if (less_than_minimum) {
+         ix_min_block = szt_i;
+      }
+   }
+   return ix_min_block;
+}
+
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
+void merge_blocks_bufferless
+   ( RandItKeys key_first
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type const l_irreg1
+   , typename iterator_traits<RandIt>::size_type const n_block_a
+   , typename iterator_traits<RandIt>::size_type const n_block_b
+   , typename iterator_traits<RandIt>::size_type const l_irreg2
+   , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const key_count = needed_keys_count(n_block_a, n_block_b); (void)key_count;
+   //BOOST_ASSERT(n_block_a || n_block_b);
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
+
+   size_type n_bef_irreg2 = 0;
+   bool l_irreg_pos_count = true;
+   RandItKeys key_mid(key_first + n_block_a);
+   RandIt const first_irr2 = first + l_irreg1 + (n_block_a+n_block_b)*l_block;
+   RandIt const last_irr2  = first_irr2 + l_irreg2;
+
+   {  //Selection sort blocks
+      size_type n_block_left = n_block_b + n_block_a;
+      RandItKeys key_range2(key_first);
+
+      size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
+      size_type max_check = min_value(min_check+1, n_block_left);
+      for (RandIt f = first+l_irreg1; n_block_left; --n_block_left, ++key_range2, f += l_block, min_check -= min_check != 0, max_check -= max_check != 0) {
+         size_type const next_key_idx = find_next_block(key_range2, key_comp, f, l_block, min_check, max_check, comp);
+         RandItKeys const key_next(key_range2 + next_key_idx);
+         max_check = min_value(max_value(max_check, next_key_idx+2), n_block_left);
+
+         RandIt const first_min = f + next_key_idx*l_block;
+
+         //Check if irregular b block should go here.
+         //If so, break to the special code handling the irregular block
+         if (l_irreg_pos_count && l_irreg2 && comp(*first_irr2, *first_min)){
+            l_irreg_pos_count = false;
+         }
+         n_bef_irreg2 += l_irreg_pos_count;
+
+         swap_and_update_key(key_next, key_range2, key_mid, f, f + l_block, first_min);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(f, f+l_block, comp));
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_min, first_min + l_block, comp));
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((f == (first+l_irreg1)) || !comp(*f, *(f-l_block)));
+      }
+   }
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first+l_irreg1+n_bef_irreg2*l_block, first_irr2, comp));
+
+   RandIt first1 = first;
+   RandIt last1  = first+l_irreg1;
+   RandItKeys const key_end (key_first+n_bef_irreg2);
+   bool is_range1_A = true;
+
+   for( ; key_first != key_end; ++key_first){
+      bool is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_first, *key_mid);
+      first1 = is_range1_A == is_range2_A
+         ? last1 : partial_merge_bufferless(first1, last1, last1 + l_block, &is_range1_A, comp);
+      last1 += l_block;
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, first1, comp));
+   }
+
+   merge_bufferless(is_range1_A ? first1 : last1, first_irr2, last_irr2, comp);
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, last_irr2, comp));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//                            BUFFERED MERGE
+//
+///////////////////////////////////////////////////////////////////////////////
+template<class RandIt, class Compare, class Op, class Buf>
+void op_buffered_merge
+      ( RandIt first, RandIt const middle, RandIt last
+      , Compare comp, Op op
+      , Buf &xbuf)
+{
+   if(first != middle && middle != last && comp(*middle, middle[-1])){
+      typedef typename iterator_traits<RandIt>::size_type   size_type;
+      size_type const len1 = size_type(middle-first);
+      size_type const len2 = size_type(last-middle);
+      if(len1 <= len2){
+         first = boost::movelib::upper_bound(first, middle, *middle, comp);
+         xbuf.move_assign(first, size_type(middle-first));
+         op_merge_with_right_placed
+            (xbuf.data(), xbuf.end(), first, middle, last, comp, op);
+      }
+      else{
+         last = boost::movelib::lower_bound(middle, last, middle[-1], comp);
+         xbuf.move_assign(middle, size_type(last-middle));
+         op_merge_with_left_placed
+            (first, middle, last, xbuf.data(), xbuf.end(), comp, op);
+      }
+   }
+}
+
+template<class RandIt, class Compare, class XBuf>
+void buffered_merge
+      ( RandIt first, RandIt const middle, RandIt last
+      , Compare comp
+      , XBuf &xbuf)
+{
+   op_buffered_merge(first, middle, last, comp, move_op(), xbuf);
+}
+
+// Complexity: 2*distance(first, last)+max_collected^2/2
+//
+// Tries to collect at most n_keys unique elements from [first, last),
+// in the begining of the range, and ordered according to comp
+// 
+// Returns the number of collected keys
+template<class RandIt, class Compare, class XBuf>
+typename iterator_traits<RandIt>::size_type
+   collect_unique
+      ( RandIt const first, RandIt const last
+      , typename iterator_traits<RandIt>::size_type const max_collected, Compare comp
+      , XBuf & xbuf)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type h = 0;
+   if(max_collected){
+      ++h;  // first key is always here
+      RandIt h0 = first;
+      RandIt u = first; ++u;
+      RandIt search_end = u;
+
+      if(xbuf.capacity() >= max_collected){
+         typename XBuf::iterator const ph0 = xbuf.add(first);
+         while(u != last && h < max_collected){
+            typename XBuf::iterator const r = boost::movelib::lower_bound(ph0, xbuf.end(), *u, comp);
+            //If key not found add it to [h, h+h0)
+            if(r == xbuf.end() || comp(*u, *r) ){
+               RandIt const new_h0 = boost::move(search_end, u, h0);
+               search_end = u;
+               ++search_end;
+               ++h;
+               xbuf.insert(r, u);
+               h0 = new_h0;
+            }
+            ++u;
+         }
+         boost::move_backward(first, h0, h0+h);
+         boost::move(xbuf.data(), xbuf.end(), first);
+      }
+      else{
+         while(u != last && h < max_collected){
+            RandIt const r = boost::movelib::lower_bound(h0, search_end, *u, comp);
+            //If key not found add it to [h, h+h0)
+            if(r == search_end || comp(*u, *r) ){
+               RandIt const new_h0 = rotate_gcd(h0, search_end, u);
+               search_end = u;
+               ++search_end;
+               ++h;
+               rotate_gcd(r+(new_h0-h0), u, search_end);
+               h0 = new_h0;
+            }
+            ++u;
+         }
+         rotate_gcd(first, h0, h0+h);
+      }
+   }
+   return h;
+}
+
+template<class Unsigned>
+Unsigned floor_sqrt(Unsigned const n)
+{
+   Unsigned x = n;
+   Unsigned y = x/2 + (x&1);
+   while (y < x){
+      x = y;
+      y = (x + n / x)/2;
+   }
+   return x;
+}
+
+template<class Unsigned>
+Unsigned ceil_sqrt(Unsigned const n)
+{
+   Unsigned r = floor_sqrt(n);
+   return r + Unsigned((n%r) != 0);
+}
+
+template<class Unsigned>
+Unsigned floor_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
+{
+   Unsigned s = n;
+   Unsigned p = 0;
+   while(s > AdaptiveSortInsertionSortThreshold){
+      s /= 2;
+      ++p;
+   }
+   base = s;
+   pow = p;
+   return s << p;
+}
+
+template<class Unsigned>
+Unsigned ceil_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
+{
+   Unsigned fm = floor_merge_multiple(n, base, pow);
+
+   if(fm != n){
+      if(base < AdaptiveSortInsertionSortThreshold){
+         ++base;
+      }
+      else{
+         base = AdaptiveSortInsertionSortThreshold/2 + 1;
+         ++pow;
+      }
+   }
+   return base << pow;
+}
+
+template<class Unsigned>
+Unsigned ceil_sqrt_multiple(Unsigned const n, Unsigned *pbase = 0)
+{
+   Unsigned const r = ceil_sqrt(n);
+   Unsigned pow = 0;
+   Unsigned base = 0;
+   Unsigned const res = ceil_merge_multiple(r, base, pow);
+   if(pbase) *pbase = base;
+   return res;
+}
+
+struct less
+{
+   template<class T>
+   bool operator()(const T &l, const T &r)
+   {  return l < r;  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//                            MERGE BLOCKS
+//
+///////////////////////////////////////////////////////////////////////////////
+
+//#define ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
+
+#if defined ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
+template<class RandIt, class Compare>
+void slow_stable_sort
+   ( RandIt const first, RandIt const last, Compare comp)
+{
+   boost::movelib::inplace_stable_sort(first, last, comp);
+}
+
+#else //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
+
+template<class RandIt, class Compare>
+void slow_stable_sort
+   ( RandIt const first, RandIt const last, Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type L = size_type(last - first);
+   {  //Use insertion sort to merge first elements
+      size_type m = 0;
+      while((L - m) > size_type(AdaptiveSortInsertionSortThreshold)){
+         insertion_sort(first+m, first+m+size_type(AdaptiveSortInsertionSortThreshold), comp);
+         m += AdaptiveSortInsertionSortThreshold;
+      }
+      insertion_sort(first+m, last, comp);
+   }
+
+   size_type h = AdaptiveSortInsertionSortThreshold;
+   for(bool do_merge = L > h; do_merge; h*=2){
+      do_merge = (L - h) > h;
+      size_type p0 = 0;
+      if(do_merge){
+         size_type const h_2 = 2*h;
+         while((L-p0) > h_2){
+            merge_bufferless(first+p0, first+p0+h, first+p0+h_2, comp);
+            p0 += h_2;
+         }
+      }
+      if((L-p0) > h){
+         merge_bufferless(first+p0, first+p0+h, last, comp);
+      }
+   }
+}
+
+#endif   //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
+
+//Returns new l_block and updates use_buf
+template<class Unsigned>
+Unsigned lblock_for_combine
+   (Unsigned const l_block, Unsigned const n_keys, Unsigned const l_data, bool &use_buf)
+{
+   BOOST_ASSERT(l_data > 1);
+
+   //We need to guarantee lblock >= l_merged/(n_keys/2) keys for the combination.
+   //We have at least 4 keys guaranteed (which are the minimum to merge 2 ranges)
+   //If l_block != 0, then n_keys is already enough to merge all blocks in all
+   //phases as we've found all needed keys for that buffer and length before.
+   //If l_block == 0 then see if half keys can be used as buffer and the rest
+   //as keys guaranteeing that n_keys >= (2*l_merged)/lblock = 
+   if(!l_block){
+      //If l_block == 0 then n_keys is power of two
+      //(guaranteed by build_params(...))
+      BOOST_ASSERT(n_keys >= 4);
+      //BOOST_ASSERT(0 == (n_keys &(n_keys-1)));
+
+      //See if half keys are at least 4 and if half keys fulfill
+      Unsigned const new_buf  = n_keys/2;
+      Unsigned const new_keys = n_keys-new_buf;
+      use_buf = new_keys >= 4 && new_keys >= l_data/new_buf;
+      if(use_buf){
+         return new_buf;
+      }
+      else{
+         return l_data/n_keys;
+      }
+   }
+   else{
+      use_buf = true;
+      return l_block;
+   }
+}
+
+template<class RandIt, class Compare, class XBuf>
+void stable_sort( RandIt first, RandIt last, Compare comp, XBuf & xbuf)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const len = size_type(last - first);
+   size_type const half_len = len/2 + (len&1);
+   if(std::size_t(xbuf.capacity() - xbuf.size()) >= half_len) {
+      merge_sort(first, last, comp, xbuf.data()+xbuf.size());
+   }
+   else{
+      slow_stable_sort(first, last, comp);
+   }
+}
+
+template<class RandIt, class Comp, class XBuf>
+void unstable_sort( RandIt first, RandIt last
+                    , Comp comp
+                    , XBuf & xbuf)
+{
+   heap_sort(first, last, comp);(void)xbuf;
+}
+
+template<class RandIt, class Compare, class XBuf>
+void stable_merge
+      ( RandIt first, RandIt const middle, RandIt last
+      , Compare comp
+      , XBuf &xbuf)
+{
+   BOOST_ASSERT(xbuf.empty());
+   typedef typename iterator_traits<RandIt>::size_type   size_type;
+   size_type const len1  = size_type(middle-first);
+   size_type const len2  = size_type(last-middle);
+   size_type const l_min = min_value(len1, len2);
+   if(xbuf.capacity() >= l_min){
+      buffered_merge(first, middle, last, comp, xbuf);
+      xbuf.clear();
+   }
+   else{
+      merge_bufferless(first, middle, last, comp);
+   }
+}
+
+template<class RandIt, class Comp, class XBuf>
+void initialize_keys( RandIt first, RandIt last
+                    , Comp comp
+                    , XBuf & xbuf)
+{
+   unstable_sort(first, last, comp, xbuf);
+   BOOST_ASSERT(boost::movelib::is_sorted_and_unique(first, last, comp));
+}
+
+template<class RandIt, class U>
+void initialize_keys( RandIt first, RandIt last
+                    , less
+                    , U &)
+{
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+   std::size_t count = std::size_t(last - first);
+   for(std::size_t i = 0; i != count; ++i){
+      *first = static_cast<value_type>(i);
+      ++first;
+   }
+}
+
+template <class Unsigned>
+Unsigned calculate_total_combined(Unsigned const len, Unsigned const l_prev_merged, Unsigned *pl_irreg_combined = 0)
+{
+   typedef Unsigned size_type;
+
+   size_type const l_combined = 2*l_prev_merged;
+   size_type l_irreg_combined = len%l_combined;
+   size_type l_total_combined = len;
+   if(l_irreg_combined <= l_prev_merged){
+      l_total_combined -= l_irreg_combined;
+      l_irreg_combined = 0;
+   }
+   if(pl_irreg_combined)
+      *pl_irreg_combined = l_irreg_combined;
+   return l_total_combined;
+}
+
+template<class RandItKeys, class KeyCompare, class SizeType, class XBuf>
+void combine_params
+   ( RandItKeys const keys
+   , KeyCompare key_comp
+   , SizeType l_combined
+   , SizeType const l_prev_merged
+   , SizeType const l_block
+   , XBuf & xbuf
+   //Output
+   , SizeType &n_block_a
+   , SizeType &n_block_b
+   , SizeType &l_irreg1
+   , SizeType &l_irreg2
+   //Options
+   , bool do_initialize_keys = true)
+{
+   typedef SizeType   size_type;
+
+   //Initial parameters for selection sort blocks
+   l_irreg1 = l_prev_merged%l_block;
+   l_irreg2 = (l_combined-l_irreg1)%l_block;
+   BOOST_ASSERT(((l_combined-l_irreg1-l_irreg2)%l_block) == 0);
+   size_type const n_reg_block = (l_combined-l_irreg1-l_irreg2)/l_block;
+   n_block_a = l_prev_merged/l_block;
+   n_block_b = n_reg_block - n_block_a;
+   BOOST_ASSERT(n_reg_block>=n_block_a);
+
+   //Key initialization
+   if (do_initialize_keys) {
+      initialize_keys(keys, keys + needed_keys_count(n_block_a, n_block_b), key_comp, xbuf);
+   }
+}
+
+
+
+//////////////////////////////////
+//
+//          partial_merge
+//
+//////////////////////////////////
+template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
+OutputIt op_partial_merge_impl
+   (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op)
+{
+   InputIt1 first1(r_first1);
+   InputIt2 first2(r_first2);
+   if(first2 != last2 && last1 != first1)
+   while(1){
+      if(comp(*first2, *first1)) {
+         op(first2++, d_first++);
+         if(first2 == last2){
+            break;
+         }
+      }
+      else{
+         op(first1++, d_first++);
+         if(first1 == last1){
+            break;
+         }
+      }
+   }
+   r_first1 = first1;
+   r_first2 = first2;
+   return d_first;
+}
+
+template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
+OutputIt op_partial_merge
+   (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op, bool is_stable)
+{
+   return is_stable ? op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, comp, op)
+                    : op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, antistable<Compare>(comp), op);
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    op_partial_merge_and_save
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
+OutputIt op_partial_merge_and_swap_impl
+   (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, InputIt2 &r_first_min, OutputIt d_first, Compare comp, Op op)
+{
+   InputIt1 first1(r_first1);
+   InputIt2 first2(r_first2);
+   
+   if(first2 != last2 && last1 != first1) {
+      InputIt2 first_min(r_first_min);
+      bool non_empty_ranges = true;
+      do{
+         if(comp(*first_min, *first1)) {
+            op(three_way_t(), first2++, first_min++, d_first++);
+            non_empty_ranges = first2 != last2;
+         }
+         else{
+            op(first1++, d_first++);
+            non_empty_ranges = first1 != last1;
+         }
+      } while(non_empty_ranges);
+      r_first_min = first_min;
+      r_first1 = first1;
+      r_first2 = first2;
+   }
+   return d_first;
+}
+
+template<class RandIt, class InputIt2, class OutputIt, class Compare, class Op>
+OutputIt op_partial_merge_and_swap
+   (RandIt &r_first1, RandIt const last1, InputIt2 &r_first2, InputIt2 const last2, InputIt2 &r_first_min, OutputIt d_first, Compare comp, Op op, bool is_stable)
+{
+   return is_stable ? op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, comp, op)
+                    : op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, antistable<Compare>(comp), op);
+}
+
+template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
+RandItB op_buffered_partial_merge_and_swap_to_range1_and_buffer
+   ( RandIt1 first1, RandIt1 const last1
+   , RandIt2 &rfirst2, RandIt2 const last2, RandIt2 &rfirst_min
+   , RandItB &rfirstb, Compare comp, Op op )
+{
+   RandItB firstb = rfirstb;
+   RandItB lastb  = firstb;
+   RandIt2 first2 = rfirst2;
+
+   //Move to buffer while merging
+   //Three way moves need less moves when op is swap_op so use it
+   //when merging elements from range2 to the destination occupied by range1
+   if(first1 != last1 && first2 != last2){
+      RandIt2 first_min = rfirst_min;
+      op(four_way_t(), first2++, first_min++, first1++, lastb++);
+
+      while(first1 != last1){
+         if(first2 == last2){
+            lastb = op(forward_t(), first1, last1, firstb);
+            break;
+         }
+
+         if(comp(*first_min, *firstb)){
+            op( four_way_t(), first2++, first_min++, first1++, lastb++);
+         }
+         else{
+            op(three_way_t(), firstb++, first1++, lastb++);
+         }
+      }
+      rfirst2 = first2;
+      rfirstb = firstb;
+      rfirst_min = first_min;
+   }
+
+   return lastb;
+}
+
+template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
+RandItB op_buffered_partial_merge_to_range1_and_buffer
+   ( RandIt1 first1, RandIt1 const last1
+   , RandIt2 &rfirst2, RandIt2 const last2
+   , RandItB &rfirstb, Compare comp, Op op )
+{
+   RandItB firstb = rfirstb;
+   RandItB lastb  = firstb;
+   RandIt2 first2 = rfirst2;
+
+   //Move to buffer while merging
+   //Three way moves need less moves when op is swap_op so use it
+   //when merging elements from range2 to the destination occupied by range1
+   if(first1 != last1 && first2 != last2){
+      op(three_way_t(), first2++, first1++, lastb++);
+
+      while(true){
+         if(first1 == last1){
+            break;
+         }
+         if(first2 == last2){
+            lastb = op(forward_t(), first1, last1, firstb);
+            break;
+         }
+         if (comp(*first2, *firstb)) {
+            op(three_way_t(), first2++, first1++, lastb++);
+         }
+         else {
+            op(three_way_t(), firstb++, first1++, lastb++);
+         }
+      }
+      rfirst2 = first2;
+      rfirstb = firstb;
+   }
+
+   return lastb;
+}
+
+template<class RandIt, class RandItBuf, class Compare, class Op>
+RandIt op_partial_merge_and_save_impl
+   ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
+   , RandItBuf &buf_first1_in_out, RandItBuf &buf_last1_in_out
+   , Compare comp, Op op
+   )
+{
+   RandItBuf buf_first1 = buf_first1_in_out;
+   RandItBuf buf_last1  = buf_last1_in_out;
+   RandIt first2(rfirst2);
+
+   bool const do_swap = first2 != first_min;
+   if(buf_first1 == buf_last1){
+      //Skip any element that does not need to be moved
+      RandIt new_first1 = skip_until_merge(first1, last1, *first_min, comp);
+      buf_first1 += (new_first1-first1);
+      first1 = new_first1;
+      buf_last1  = do_swap ? op_buffered_partial_merge_and_swap_to_range1_and_buffer(first1, last1, first2, last2, first_min, buf_first1, comp, op)
+                           : op_buffered_partial_merge_to_range1_and_buffer    (first1, last1, first2, last2, buf_first1, comp, op);
+      first1 = last1;
+   }
+   else{
+      BOOST_ASSERT((last1-first1) == (buf_last1 - buf_first1));
+   }
+
+   //Now merge from buffer
+   first1 = do_swap ? op_partial_merge_and_swap_impl(buf_first1, buf_last1, first2, last2, first_min, first1, comp, op)
+                    : op_partial_merge_impl    (buf_first1, buf_last1, first2, last2, first1, comp, op);
+   buf_first1_in_out = buf_first1;
+   buf_last1_in_out  = buf_last1;
+   rfirst2 = first2;
+   return first1;
+}
+
+template<class RandIt, class RandItBuf, class Compare, class Op>
+RandIt op_partial_merge_and_save
+   ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
+   , RandItBuf &buf_first1_in_out
+   , RandItBuf &buf_last1_in_out
+   , Compare comp
+   , Op op
+   , bool is_stable)
+{
+   return is_stable
+      ? op_partial_merge_and_save_impl
+         (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, comp, op)
+      : op_partial_merge_and_save_impl
+         (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, antistable<Compare>(comp), op)
+      ;
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    op_merge_blocks_with_irreg
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+
+template<class RandItKeys, class KeyCompare, class RandIt, class RandIt2, class OutputIt, class Compare, class Op>
+OutputIt op_merge_blocks_with_irreg
+   ( RandItKeys key_first
+   , RandItKeys key_mid
+   , KeyCompare key_comp
+   , RandIt first_reg
+   , RandIt2 &first_irr
+   , RandIt2 const last_irr
+   , OutputIt dest
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type n_block_left
+   , typename iterator_traits<RandIt>::size_type min_check
+   , typename iterator_traits<RandIt>::size_type max_check
+   , Compare comp, bool const is_stable, Op op)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+
+   for(; n_block_left; --n_block_left, ++key_first, min_check -= min_check != 0, max_check -= max_check != 0){
+      size_type next_key_idx = find_next_block(key_first, key_comp, first_reg, l_block, min_check, max_check, comp);  
+      max_check = min_value(max_value(max_check, next_key_idx+2), n_block_left);
+      RandIt const last_reg  = first_reg + l_block;
+      RandIt first_min = first_reg + next_key_idx*l_block;
+      RandIt const last_min  = first_min + l_block; (void)last_min;
+
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_reg, last_reg, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!next_key_idx || is_sorted(first_min, last_min, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((!next_key_idx || !comp(*first_reg, *first_min )));
+
+      OutputIt orig_dest = dest; (void)orig_dest;
+      dest = next_key_idx ? op_partial_merge_and_swap(first_irr, last_irr, first_reg, last_reg, first_min, dest, comp, op, is_stable)
+                          : op_partial_merge         (first_irr, last_irr, first_reg, last_reg, dest, comp, op, is_stable);
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(orig_dest, dest, comp));
+
+      if(first_reg == dest){
+         dest = next_key_idx ? ::boost::adl_move_swap_ranges(first_min, last_min, first_reg)
+                             : last_reg;
+      }
+      else{
+         dest = next_key_idx ? op(three_way_forward_t(), first_reg, last_reg, first_min, dest)
+                             : op(forward_t(), first_reg, last_reg, dest);
+      }
+
+      RandItKeys const key_next(key_first + next_key_idx);
+      swap_and_update_key(key_next, key_first, key_mid, last_reg, last_reg, first_min);
+
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(orig_dest, dest, comp));
+      first_reg = last_reg;
+   }
+   return dest;
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    op_merge_blocks_left/right
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op>
+void op_merge_blocks_left
+   ( RandItKeys const key_first
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type const l_irreg1
+   , typename iterator_traits<RandIt>::size_type const n_block_a
+   , typename iterator_traits<RandIt>::size_type const n_block_b
+   , typename iterator_traits<RandIt>::size_type const l_irreg2
+   , Compare comp, Op op)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const key_count = needed_keys_count(n_block_a, n_block_b); (void)key_count;
+//   BOOST_ASSERT(n_block_a || n_block_b);
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
+
+   size_type n_block_b_left = n_block_b;
+   size_type n_block_a_left = n_block_a;
+   size_type n_block_left = n_block_b + n_block_a;
+   RandItKeys key_mid(key_first + n_block_a);
+
+   RandIt buffer = first - l_block;
+   RandIt first1 = first;
+   RandIt last1  = first1 + l_irreg1;
+   RandIt first2 = last1;
+   RandIt const irreg2 = first2 + n_block_left*l_block;
+   bool is_range1_A = true;
+
+   RandItKeys key_range2(key_first);
+
+   ////////////////////////////////////////////////////////////////////////////
+   //Process all regular blocks before the irregular B block
+   ////////////////////////////////////////////////////////////////////////////
+   size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
+   size_type max_check = min_value(min_check+1, n_block_left);
+   for (; n_block_left; --n_block_left, ++key_range2, min_check -= min_check != 0, max_check -= max_check != 0) {
+      size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
+      max_check = min_value(max_value(max_check, next_key_idx+2), n_block_left);
+      RandIt const first_min = first2 + next_key_idx*l_block;
+      RandIt const last_min  = first_min + l_block; (void)last_min;
+      RandIt const last2  = first2 + l_block;
+
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first1, last1, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first2, last2, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || is_sorted(first_min, last_min, comp));
+
+      //Check if irregular b block should go here.
+      //If so, break to the special code handling the irregular block
+      if (!n_block_b_left &&
+            ( (l_irreg2 && comp(*irreg2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
+         break;
+      }
+
+      RandItKeys const key_next(key_range2 + next_key_idx);
+      bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
+
+      bool const is_buffer_middle = last1 == buffer;
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( ( is_buffer_middle && size_type(first2-buffer) == l_block && buffer == last1) ||
+                                          (!is_buffer_middle && size_type(first1-buffer) == l_block && first2 == last1));
+
+      if(is_range1_A == is_range2_A){
+         BOOST_ASSERT((first1 == last1) || !comp(*first_min, last1[-1]));
+         if(!is_buffer_middle){
+            buffer = op(forward_t(), first1, last1, buffer);
+         }
+         swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
+         first1 = first2;
+         last1  = last2;
+      }
+      else {
+         RandIt unmerged;
+         RandIt buf_beg;
+         RandIt buf_end;
+         if(is_buffer_middle){
+            buf_end = buf_beg = first2 - (last1-first1);
+            unmerged = op_partial_merge_and_save( first1, last1, first2, last2, first_min
+                                                , buf_beg, buf_end, comp, op, is_range1_A);
+         }  
+         else{
+            buf_beg = first1;
+            buf_end = last1;
+            unmerged = op_partial_merge_and_save
+               (buffer, buffer+(last1-first1), first2, last2, first_min, buf_beg, buf_end, comp, op, is_range1_A);
+         }
+         (void)unmerged;
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first-l_block, unmerged, comp));
+
+         swap_and_update_key( key_next, key_range2, key_mid, first2, last2
+                            , last_min - size_type(last2 - first2));
+
+         if(buf_beg != buf_end){  //range2 exhausted: is_buffer_middle for the next iteration
+            first1 = buf_beg;
+            last1  = buf_end;
+            BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buf_end == (last2-l_block));
+            buffer = last1;
+         }
+         else{ //range1 exhausted: !is_buffer_middle for the next iteration
+            first1 = first2;
+            last1  = last2;
+            buffer = first2 - l_block;
+            is_range1_A = is_range2_A;
+         }
+      }
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
+      is_range2_A ? --n_block_a_left : --n_block_b_left;
+      first2 = last2;
+   }
+
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_range2 + n_block_left, key_comp, *key_mid));
+   BOOST_ASSERT(!n_block_b_left);
+
+   ////////////////////////////////////////////////////////////////////////////
+   //Process remaining range 1 left before the irregular B block
+   ////////////////////////////////////////////////////////////////////////////
+   bool const is_buffer_middle = last1 == buffer;
+   RandIt first_irr2 = irreg2;
+   RandIt const last_irr2  = first_irr2 + l_irreg2;
+   if(l_irreg2 && is_range1_A){
+      if(is_buffer_middle){
+         first1 = skip_until_merge(first1, last1, *first_irr2, comp);
+         //Even if we copy backward, no overlapping occurs so use forward copy
+         //that can be faster specially with trivial types
+         RandIt const new_first1 = first2 - (last1 - first1);
+         op(forward_t(), first1, last1, new_first1);
+         first1 = new_first1;
+         last1 = first2;
+         buffer = first1 - l_block;
+      }
+      buffer = op_partial_merge_impl(first1, last1, first_irr2, last_irr2, buffer, comp, op);
+      buffer = op(forward_t(), first1, last1, buffer);
+   }
+   else if(!is_buffer_middle){
+      buffer = op(forward_t(), first1, last1, buffer);
+   }
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first-l_block, buffer, comp));
+
+   ////////////////////////////////////////////////////////////////////////////
+   //Process irregular B block and remaining A blocks
+   ////////////////////////////////////////////////////////////////////////////
+   buffer = op_merge_blocks_with_irreg
+      ( key_range2, key_mid, key_comp, first2, first_irr2, last_irr2
+      , buffer, l_block, n_block_left, min_check, max_check, comp, false, op);
+   buffer = op(forward_t(), first_irr2, last_irr2, buffer);(void)buffer;
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first-l_block, buffer, comp));
+}
+
+// first - first element to merge.
+// first[-l_block, 0) - buffer (if use_buf == true)
+// l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
+// keys - sequence of keys, in same order as blocks. key<midkey means stream A
+// n_bef_irreg2/n_aft_irreg2 are regular blocks
+// l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
+// If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
+void merge_blocks_left
+   ( RandItKeys const key_first
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type const l_irreg1
+   , typename iterator_traits<RandIt>::size_type const n_block_a
+   , typename iterator_traits<RandIt>::size_type const n_block_b
+   , typename iterator_traits<RandIt>::size_type const l_irreg2
+   , Compare comp
+   , bool const xbuf_used)
+{
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + needed_keys_count(n_block_a, n_block_b), key_comp, key_first[n_block_a]));
+   if(xbuf_used){
+      op_merge_blocks_left
+         (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, move_op());
+   }
+   else{
+      op_merge_blocks_left
+         (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, swap_op());
+   }
+}
+
+// first - first element to merge.
+// [first+l_block*(n_bef_irreg2+n_aft_irreg2)+l_irreg2, first+l_block*(n_bef_irreg2+n_aft_irreg2+1)+l_irreg2) - buffer
+// l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
+// keys - sequence of keys, in same order as blocks. key<midkey means stream A
+// n_bef_irreg2/n_aft_irreg2 are regular blocks
+// l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
+// If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
+void merge_blocks_right
+   ( RandItKeys const key_first
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type const n_block_a
+   , typename iterator_traits<RandIt>::size_type const n_block_b
+   , typename iterator_traits<RandIt>::size_type const l_irreg2
+   , Compare comp
+   , bool const xbuf_used)
+{
+   merge_blocks_left
+      ( (make_reverse_iterator)(key_first + needed_keys_count(n_block_a, n_block_b))
+      , inverse<KeyCompare>(key_comp)
+      , (make_reverse_iterator)(first + ((n_block_a+n_block_b)*l_block+l_irreg2))
+      , l_block
+      , l_irreg2
+      , n_block_b
+      , n_block_a
+      , 0
+      , inverse<Compare>(comp), xbuf_used);
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    op_merge_blocks_with_buf
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op, class RandItBuf>
+void op_merge_blocks_with_buf
+   ( RandItKeys key_first
+   , KeyCompare key_comp
+   , RandIt const first
+   , typename iterator_traits<RandIt>::size_type const l_block
+   , typename iterator_traits<RandIt>::size_type const l_irreg1
+   , typename iterator_traits<RandIt>::size_type const n_block_a
+   , typename iterator_traits<RandIt>::size_type const n_block_b
+   , typename iterator_traits<RandIt>::size_type const l_irreg2
+   , Compare comp
+   , Op op
+   , RandItBuf const buf_first)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const key_count = needed_keys_count(n_block_a, n_block_b); (void)key_count;
+   //BOOST_ASSERT(n_block_a || n_block_b);
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
+
+   size_type n_block_b_left = n_block_b;
+   size_type n_block_a_left = n_block_a;
+   size_type n_block_left = n_block_b + n_block_a;
+   RandItKeys key_mid(key_first + n_block_a);
+
+   RandItBuf buffer = buf_first;
+   RandItBuf buffer_end = buffer;
+   RandIt first1 = first;
+   RandIt last1  = first1 + l_irreg1;
+   RandIt first2 = last1;
+   RandIt const first_irr2 = first2 + n_block_left*l_block;
+   bool is_range1_A = true;
+
+   RandItKeys key_range2(key_first);
+
+   ////////////////////////////////////////////////////////////////////////////
+   //Process all regular blocks before the irregular B block
+   ////////////////////////////////////////////////////////////////////////////
+   size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
+   size_type max_check = min_value(min_check+1, n_block_left);
+   for (; n_block_left; --n_block_left, ++key_range2, min_check -= min_check != 0, max_check -= max_check != 0) {
+      size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
+      max_check = min_value(max_value(max_check, next_key_idx+2), n_block_left);
+      RandIt       first_min = first2 + next_key_idx*l_block;
+      RandIt const last_min  = first_min + l_block; (void)last_min;
+      RandIt const last2  = first2 + l_block;
+
+      bool const buffer_empty = buffer == buffer_end; (void)buffer_empty;
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buffer_empty ? is_sorted(first1, last1, comp) : is_sorted(buffer, buffer_end, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first2, last2, comp));
+      BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || is_sorted(first_min, last_min, comp));
+
+      //Check if irregular b block should go here.
+      //If so, break to the special code handling the irregular block
+      if (!n_block_b_left &&
+            ( (l_irreg2 && comp(*first_irr2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
+         break;
+      }
+
+      RandItKeys const key_next(key_range2 + next_key_idx);
+      bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
+
+      if(is_range1_A == is_range2_A){
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((first1 == last1) || (buffer_empty ? !comp(*first_min, last1[-1]) : !comp(*first_min, buffer_end[-1])));
+         //If buffered, put those elements in place
+         RandIt res = op(forward_t(), buffer, buffer_end, first1);
+         buffer    = buffer_end = buf_first;
+         BOOST_ASSERT(buffer_empty || res == last1); (void)res;
+         swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first2, last2, comp));
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_min, last_min, comp));
+         first1 = first2;
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, first1, comp));
+      }
+      else {
+         RandIt const unmerged = op_partial_merge_and_save(first1, last1, first2, last2, first_min, buffer, buffer_end, comp, op, is_range1_A);
+         bool const is_range_1_empty = buffer == buffer_end;
+         BOOST_ASSERT(is_range_1_empty || (buffer_end-buffer) == (last1+l_block-unmerged));
+         if(is_range_1_empty){
+            buffer    = buffer_end = buf_first;
+            first_min = last_min - (last2 - first2);
+         }
+         else{
+            first_min = last_min;
+         }
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!is_range_1_empty || (last_min-first_min) == (last2-unmerged));
+         swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
+
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_min, last_min, comp));
+         is_range1_A ^= is_range_1_empty;
+         first1 = unmerged;
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, unmerged, comp));
+      }
+      BOOST_ASSERT( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
+      is_range2_A ? --n_block_a_left : --n_block_b_left;
+      last1 += l_block;
+      first2 = last2;
+   }
+
+   RandIt res = op(forward_t(), buffer, buffer_end, first1); (void)res;
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, res, comp));
+
+   ////////////////////////////////////////////////////////////////////////////
+   //Process irregular B block and remaining A blocks
+   ////////////////////////////////////////////////////////////////////////////
+   RandIt const last_irr2 = first_irr2 + l_irreg2;
+   op(forward_t(), first_irr2, first_irr2+l_irreg2, buf_first);
+   buffer = buf_first;
+   buffer_end = buffer+l_irreg2;
+
+   reverse_iterator<RandItBuf> rbuf_beg(buffer_end);
+   RandIt dest = op_merge_blocks_with_irreg
+      ((make_reverse_iterator)(key_first + n_block_b + n_block_a), (make_reverse_iterator)(key_mid), inverse<KeyCompare>(key_comp)
+      , (make_reverse_iterator)(first_irr2), rbuf_beg
+      , (make_reverse_iterator)(buffer), (make_reverse_iterator)(last_irr2)
+      , l_block, n_block_left, 0, n_block_left
+      , inverse<Compare>(comp), true, op).base();
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(dest, last_irr2, comp));
+
+   buffer_end = rbuf_beg.base();
+   BOOST_ASSERT((dest-last1) == (buffer_end-buffer));
+   op_merge_with_left_placed(is_range1_A ? first1 : last1, last1, dest, buffer, buffer_end, comp, op);
+
+   BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, last_irr2, comp));
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//  op_insertion_sort_step_left/right
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+
+template<class RandIt, class Compare, class Op>
+typename iterator_traits<RandIt>::size_type
+   op_insertion_sort_step_left
+      ( RandIt const first
+      , typename iterator_traits<RandIt>::size_type const length
+      , typename iterator_traits<RandIt>::size_type const step
+      , Compare comp, Op op)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
+   size_type m = 0;
+
+   while((length - m) > s){
+      insertion_sort_op(first+m, first+m+s, first+m-s, comp, op);
+      m += s;
+   }
+   insertion_sort_op(first+m, first+length, first+m-s, comp, op);
+   return s;
+}
+
+template<class RandIt, class Compare, class Op>
+void op_merge_right_step_once
+      ( RandIt first_block
+      , typename iterator_traits<RandIt>::size_type const elements_in_blocks
+      , typename iterator_traits<RandIt>::size_type const l_build_buf
+      , Compare comp
+      , Op op)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type restk = elements_in_blocks%(2*l_build_buf);
+   size_type p = elements_in_blocks - restk;
+   BOOST_ASSERT(0 == (p%(2*l_build_buf)));
+
+   if(restk <= l_build_buf){
+      op(backward_t(),first_block+p, first_block+p+restk, first_block+p+restk+l_build_buf);
+   }
+   else{
+      op_merge_right(first_block+p, first_block+p+l_build_buf, first_block+p+restk, first_block+p+restk+l_build_buf, comp, op);
+   }
+   while(p>0){
+      p -= 2*l_build_buf;
+      op_merge_right(first_block+p, first_block+p+l_build_buf, first_block+p+2*l_build_buf, first_block+p+3*l_build_buf, comp, op);
+   }
+}
+
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    insertion_sort_step
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+template<class RandIt, class Compare>
+typename iterator_traits<RandIt>::size_type
+   insertion_sort_step
+      ( RandIt const first
+      , typename iterator_traits<RandIt>::size_type const length
+      , typename iterator_traits<RandIt>::size_type const step
+      , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
+   size_type m = 0;
+
+   while((length - m) > s){
+      insertion_sort(first+m, first+m+s, comp);
+      m += s;
+   }
+   insertion_sort(first+m, first+length, comp);
+   return s;
+}
+
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+//
+//    op_merge_left_step_multiple
+//
+//////////////////////////////////
+//////////////////////////////////
+//////////////////////////////////
+template<class RandIt, class Compare, class Op>
+typename iterator_traits<RandIt>::size_type  
+   op_merge_left_step_multiple
+      ( RandIt first_block
+      , typename iterator_traits<RandIt>::size_type const elements_in_blocks
+      , typename iterator_traits<RandIt>::size_type l_merged
+      , typename iterator_traits<RandIt>::size_type const l_build_buf
+      , typename iterator_traits<RandIt>::size_type l_left_space
+      , Compare comp
+      , Op op)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   for(; l_merged < l_build_buf && l_left_space >= l_merged; l_merged*=2){
+      size_type p0=0;
+      RandIt pos = first_block;
+      while((elements_in_blocks - p0) > 2*l_merged) {
+         op_merge_left(pos-l_merged, pos, pos+l_merged, pos+2*l_merged, comp, op);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(pos-l_merged, pos+l_merged, comp));
+         p0 += 2*l_merged;
+         pos = first_block+p0;
+      }
+      if((elements_in_blocks-p0) > l_merged) {
+         op_merge_left(pos-l_merged, pos, pos+l_merged, first_block+elements_in_blocks, comp, op);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(pos-l_merged, pos-l_merged+(first_block+elements_in_blocks-pos), comp));
+      }
+      else {
+         op(forward_t(), pos, first_block+elements_in_blocks, pos-l_merged);
+         BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(pos-l_merged, first_block+elements_in_blocks-l_merged, comp));
+      }
+      first_block -= l_merged;
+      l_left_space -= l_merged;
+   }
+   return l_merged;
+}
+
+
+}  //namespace detail_adaptive {
+}  //namespace movelib {
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif   //#define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
diff --git a/include/boost/move/algo/detail/basic_op.hpp b/include/boost/move/algo/detail/basic_op.hpp
new file mode 100644
index 0000000..ea5faf0
--- /dev/null
+++ b/include/boost/move/algo/detail/basic_op.hpp
@@ -0,0 +1,121 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_MOVE_ALGO_BASIC_OP
+#define BOOST_MOVE_ALGO_BASIC_OP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/utility_core.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+
+namespace boost {
+namespace movelib {
+
+struct forward_t{};
+struct backward_t{};
+struct three_way_t{};
+struct three_way_forward_t{};
+struct four_way_t{};
+
+struct move_op
+{
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE void operator()(SourceIt source, DestinationIt dest)
+   {  *dest = ::boost::move(*source);  }
+
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE DestinationIt operator()(forward_t, SourceIt first, SourceIt last, DestinationIt dest_begin)
+   {  return ::boost::move(first, last, dest_begin);  }
+
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE DestinationIt operator()(backward_t, SourceIt first, SourceIt last, DestinationIt dest_last)
+   {  return ::boost::move_backward(first, last, dest_last);  }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2>
+   BOOST_MOVE_FORCEINLINE void operator()(three_way_t, SourceIt srcit, DestinationIt1 dest1it, DestinationIt2 dest2it)
+   {
+      *dest2it = boost::move(*dest1it);
+      *dest1it = boost::move(*srcit);
+   }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2>
+   DestinationIt2 operator()(three_way_forward_t, SourceIt srcit, SourceIt srcitend, DestinationIt1 dest1it, DestinationIt2 dest2it)
+   {
+      //Destination2 range can overlap SourceIt range so avoid boost::move
+      while(srcit != srcitend){
+         this->operator()(three_way_t(), srcit++, dest1it++, dest2it++);
+      }
+      return dest2it;
+   }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2, class DestinationIt3>
+   BOOST_MOVE_FORCEINLINE void operator()(four_way_t, SourceIt srcit, DestinationIt1 dest1it, DestinationIt2 dest2it, DestinationIt3 dest3it)
+   {
+      *dest3it = boost::move(*dest2it);
+      *dest2it = boost::move(*dest1it);
+      *dest1it = boost::move(*srcit);
+   }
+};
+
+struct swap_op
+{
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE void operator()(SourceIt source, DestinationIt dest)
+   {  boost::adl_move_swap(*dest, *source);  }
+
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE DestinationIt operator()(forward_t, SourceIt first, SourceIt last, DestinationIt dest_begin)
+   {  return boost::adl_move_swap_ranges(first, last, dest_begin);  }
+
+   template <class SourceIt, class DestinationIt>
+   BOOST_MOVE_FORCEINLINE DestinationIt operator()(backward_t, SourceIt first, SourceIt last, DestinationIt dest_begin)
+   {  return boost::adl_move_swap_ranges_backward(first, last, dest_begin);  }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2>
+   BOOST_MOVE_FORCEINLINE void operator()(three_way_t, SourceIt srcit, DestinationIt1 dest1it, DestinationIt2 dest2it)
+   {
+      typename ::boost::movelib::iterator_traits<SourceIt>::value_type tmp(boost::move(*dest2it));
+      *dest2it = boost::move(*dest1it);
+      *dest1it = boost::move(*srcit);
+      *srcit = boost::move(tmp);
+   }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2>
+   DestinationIt2 operator()(three_way_forward_t, SourceIt srcit, SourceIt srcitend, DestinationIt1 dest1it, DestinationIt2 dest2it)
+   {
+      while(srcit != srcitend){
+         this->operator()(three_way_t(), srcit++, dest1it++, dest2it++);
+      }
+      return dest2it;
+   }
+
+   template <class SourceIt, class DestinationIt1, class DestinationIt2, class DestinationIt3>
+   BOOST_MOVE_FORCEINLINE void operator()(four_way_t, SourceIt srcit, DestinationIt1 dest1it, DestinationIt2 dest2it, DestinationIt3 dest3it)
+   {
+      typename ::boost::movelib::iterator_traits<SourceIt>::value_type tmp(boost::move(*dest3it));
+      *dest3it = boost::move(*dest2it);
+      *dest2it = boost::move(*dest1it);
+      *dest1it = boost::move(*srcit);
+      *srcit = boost::move(tmp);
+   }
+};
+
+
+}} //namespace boost::movelib
+
+#endif   //BOOST_MOVE_ALGO_BASIC_OP
diff --git a/include/boost/move/algo/detail/heap_sort.hpp b/include/boost/move/algo/detail/heap_sort.hpp
new file mode 100644
index 0000000..5474d9f
--- /dev/null
+++ b/include/boost/move/algo/detail/heap_sort.hpp
@@ -0,0 +1,111 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2017-2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//! \file
+
+#ifndef BOOST_MOVE_DETAIL_HEAP_SORT_HPP
+#define BOOST_MOVE_DETAIL_HEAP_SORT_HPP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/detail/workaround.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/algo/detail/is_sorted.hpp>
+#include <boost/move/utility_core.hpp>
+
+namespace boost {  namespace movelib{
+
+template <class RandomAccessIterator, class Compare>
+class heap_sort_helper
+{
+   typedef typename boost::movelib::iterator_traits<RandomAccessIterator>::size_type  size_type;
+   typedef typename boost::movelib::iterator_traits<RandomAccessIterator>::value_type value_type;
+
+   static void adjust_heap(RandomAccessIterator first, size_type hole_index, size_type const len, value_type &value, Compare comp)
+   {
+      size_type const top_index = hole_index;
+      size_type second_child = 2 * (hole_index + 1);
+
+      while (second_child < len) {
+         if (comp(*(first + second_child), *(first + (second_child - 1))))
+            second_child--;
+         *(first + hole_index) = boost::move(*(first + second_child));
+         hole_index = second_child;
+         second_child = 2 * (second_child + 1);
+      }
+      if (second_child == len) {
+         *(first + hole_index) = boost::move(*(first + (second_child - 1)));
+         hole_index = second_child - 1;
+      }
+
+      {  //push_heap-like ending
+         size_type parent = (hole_index - 1) / 2;
+         while (hole_index > top_index && comp(*(first + parent), value)) {
+            *(first + hole_index) = boost::move(*(first + parent));
+            hole_index = parent;
+            parent = (hole_index - 1) / 2;
+         }    
+         *(first + hole_index) = boost::move(value);
+      }
+   }
+
+   static void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp)
+   {
+      size_type const len = size_type(last - first);
+      if (len > 1) {
+         size_type parent = len/2u - 1u;
+
+         do {
+            value_type v(boost::move(*(first + parent)));
+            adjust_heap(first, parent, len, v, comp);
+         }while (parent--);
+      }
+   }
+
+   static void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp)
+   {
+      size_type len = size_type(last - first);
+      while (len > 1) {
+         //move biggest to the safe zone
+         --last;
+         value_type v(boost::move(*last));
+         *last = boost::move(*first);
+         adjust_heap(first, size_type(0), --len, v, comp);
+      }
+   }
+
+   public:
+   static void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp)
+   {
+      make_heap(first, last, comp);
+      sort_heap(first, last, comp);
+      BOOST_ASSERT(boost::movelib::is_sorted(first, last, comp));
+   }
+};
+
+template <class RandomAccessIterator, class Compare>
+BOOST_MOVE_FORCEINLINE void heap_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp)
+{
+   heap_sort_helper<RandomAccessIterator, Compare>::sort(first, last, comp);
+}
+
+}} //namespace boost {  namespace movelib{
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif //#ifndef BOOST_MOVE_DETAIL_HEAP_SORT_HPP
diff --git a/include/boost/move/algo/detail/insertion_sort.hpp b/include/boost/move/algo/detail/insertion_sort.hpp
new file mode 100644
index 0000000..5c378c3
--- /dev/null
+++ b/include/boost/move/algo/detail/insertion_sort.hpp
@@ -0,0 +1,128 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2014-2014.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//! \file
+
+#ifndef BOOST_MOVE_DETAIL_INSERT_SORT_HPP
+#define BOOST_MOVE_DETAIL_INSERT_SORT_HPP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/utility_core.hpp>
+#include <boost/move/algo/move.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/utility_core.hpp>
+#include <boost/move/detail/placement_new.hpp>
+#include <boost/move/detail/destruct_n.hpp>
+#include <boost/move/algo/detail/basic_op.hpp>
+#include <boost/move/detail/placement_new.hpp>
+#include <boost/move/detail/iterator_to_raw_pointer.hpp>
+
+namespace boost {  namespace movelib{
+
+// @cond
+
+template <class Compare, class ForwardIterator, class BirdirectionalIterator, class Op>
+void insertion_sort_op(ForwardIterator first1, ForwardIterator last1, BirdirectionalIterator first2, Compare comp, Op op)
+{
+   if (first1 != last1){
+      BirdirectionalIterator last2 = first2;
+      op(first1, last2);
+      for (++last2; ++first1 != last1; ++last2){
+         BirdirectionalIterator j2 = last2;
+         BirdirectionalIterator i2 = j2;
+         if (comp(*first1, *--i2)){
+            op(i2, j2);
+            for (--j2; i2 != first2 && comp(*first1, *--i2); --j2) {
+               op(i2, j2);
+            }
+         }
+         op(first1, j2);
+      }
+   }
+}
+
+template <class Compare, class ForwardIterator, class BirdirectionalIterator>
+void insertion_sort_swap(ForwardIterator first1, ForwardIterator last1, BirdirectionalIterator first2, Compare comp)
+{
+   insertion_sort_op(first1, last1, first2, comp, swap_op());
+}
+
+
+template <class Compare, class ForwardIterator, class BirdirectionalIterator>
+void insertion_sort_copy(ForwardIterator first1, ForwardIterator last1, BirdirectionalIterator first2, Compare comp)
+{
+   insertion_sort_op(first1, last1, first2, comp, move_op());
+}
+
+// @endcond
+
+template <class Compare, class BirdirectionalIterator>
+void insertion_sort(BirdirectionalIterator first, BirdirectionalIterator last, Compare comp)
+{
+   typedef typename boost::movelib::iterator_traits<BirdirectionalIterator>::value_type value_type;
+   if (first != last){
+      BirdirectionalIterator i = first;
+      for (++i; i != last; ++i){
+         BirdirectionalIterator j = i;
+         if (comp(*i,  *--j)) {
+            value_type tmp(::boost::move(*i));
+            *i = ::boost::move(*j);
+            for (BirdirectionalIterator k = j; k != first && comp(tmp,  *--k); --j) {
+               *j = ::boost::move(*k);
+            }
+            *j = ::boost::move(tmp);
+         }
+      }
+   }
+}
+
+template <class Compare, class BirdirectionalIterator, class BirdirectionalRawIterator>
+void insertion_sort_uninitialized_copy
+   (BirdirectionalIterator first1, BirdirectionalIterator const last1
+   , BirdirectionalRawIterator const first2
+   , Compare comp)
+{
+   typedef typename iterator_traits<BirdirectionalIterator>::value_type value_type;
+   if (first1 != last1){
+      BirdirectionalRawIterator last2 = first2;
+      ::new((iterator_to_raw_pointer)(last2), boost_move_new_t()) value_type(::boost::move(*first1));
+      destruct_n<value_type, BirdirectionalRawIterator> d(first2);
+      d.incr();
+      for (++last2; ++first1 != last1; ++last2){
+         BirdirectionalRawIterator j2 = last2;
+         BirdirectionalRawIterator k2 = j2;
+         if (comp(*first1, *--k2)){
+            ::new((iterator_to_raw_pointer)(j2), boost_move_new_t()) value_type(::boost::move(*k2));
+            d.incr();
+            for (--j2; k2 != first2 && comp(*first1, *--k2); --j2)
+               *j2 = ::boost::move(*k2);
+            *j2 = ::boost::move(*first1);
+         }
+         else{
+            ::new((iterator_to_raw_pointer)(j2), boost_move_new_t()) value_type(::boost::move(*first1));
+            d.incr();
+         }
+      }
+      d.release();
+   }
+}
+
+}} //namespace boost {  namespace movelib{
+
+#endif //#ifndef BOOST_MOVE_DETAIL_INSERT_SORT_HPP
diff --git a/include/boost/move/algo/detail/is_sorted.hpp b/include/boost/move/algo/detail/is_sorted.hpp
new file mode 100644
index 0000000..d3dccfc
--- /dev/null
+++ b/include/boost/move/algo/detail/is_sorted.hpp
@@ -0,0 +1,55 @@
+#ifndef BOOST_MOVE_DETAIL_IS_SORTED_HPP
+#define BOOST_MOVE_DETAIL_IS_SORTED_HPP
+///////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2017-2018. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/container for documentation.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+namespace boost {
+namespace movelib {
+
+template<class ForwardIt, class Pred>
+bool is_sorted(ForwardIt const first, ForwardIt last, Pred pred)
+{
+   if (first != last) {
+      ForwardIt next = first, cur(first);
+      while (++next != last) {
+         if (pred(*next, *cur))
+            return false;
+         cur = next;
+      }
+   }
+   return true;
+}
+
+template<class ForwardIt, class Pred>
+bool is_sorted_and_unique(ForwardIt first, ForwardIt last, Pred pred)
+{
+   if (first != last) {
+      ForwardIt next = first;
+      while (++next != last) {
+         if (!pred(*first, *next))
+            return false;
+         first = next;
+      }
+   }
+   return true;
+}
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#endif   //BOOST_MOVE_DETAIL_IS_SORTED_HPP
diff --git a/include/boost/move/algo/detail/merge.hpp b/include/boost/move/algo/detail/merge.hpp
new file mode 100644
index 0000000..8607735
--- /dev/null
+++ b/include/boost/move/algo/detail/merge.hpp
@@ -0,0 +1,553 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_MOVE_MERGE_HPP
+#define BOOST_MOVE_MERGE_HPP
+
+#include <boost/move/algo/move.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/algo/detail/basic_op.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/detail/destruct_n.hpp>
+#include <boost/move/algo/predicate.hpp>
+#include <boost/move/detail/iterator_to_raw_pointer.hpp>
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace movelib {
+
+// @cond
+
+/*
+template<typename Unsigned>
+inline Unsigned gcd(Unsigned x, Unsigned y)
+{
+   if(0 == ((x &(x-1)) | (y & (y-1)))){
+      return x < y ? x : y;
+   }
+   else{
+      do
+      {
+         Unsigned t = x % y;
+         x = y;
+         y = t;
+      } while (y);
+      return x;
+   }
+}
+*/
+
+//Modified version from "An Optimal In-Place Array Rotation Algorithm", Ching-Kuang Shene
+template<typename Unsigned>
+Unsigned gcd(Unsigned x, Unsigned y)
+{
+   if(0 == ((x &(x-1)) | (y & (y-1)))){
+      return x < y ? x : y;
+   }
+   else{
+      Unsigned z = 1;
+      while((!(x&1)) & (!(y&1))){
+         z <<=1, x>>=1, y>>=1;
+      }
+      while(x && y){
+         if(!(x&1))
+            x >>=1;
+         else if(!(y&1))
+            y >>=1;
+         else if(x >=y)
+            x = (x-y) >> 1;
+         else
+            y = (y-x) >> 1;
+      }
+      return z*(x+y);
+   }
+}
+
+template<typename RandIt>
+RandIt rotate_gcd(RandIt first, RandIt middle, RandIt last)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+
+   if(first == middle)
+      return last;
+   if(middle == last)
+      return first;
+   const size_type middle_pos = size_type(middle - first);
+   RandIt ret = last - middle_pos;
+   if (middle == ret){
+      boost::adl_move_swap_ranges(first, middle, middle);
+   }
+   else{
+      const size_type length = size_type(last - first);
+      for( RandIt it_i(first), it_gcd(it_i + gcd(length, middle_pos))
+         ; it_i != it_gcd
+         ; ++it_i){
+         value_type temp(boost::move(*it_i));
+         RandIt it_j = it_i;
+         RandIt it_k = it_j+middle_pos;
+         do{
+            *it_j = boost::move(*it_k);
+            it_j = it_k;
+            size_type const left = size_type(last - it_j);
+            it_k = left > middle_pos ? it_j + middle_pos : first + (middle_pos - left);
+         } while(it_k != it_i);
+         *it_j = boost::move(temp);
+      }
+   }
+   return ret;
+}
+
+template <class RandIt, class T, class Compare>
+RandIt lower_bound
+   (RandIt first, const RandIt last, const T& key, Compare comp)
+{
+   typedef typename iterator_traits
+      <RandIt>::size_type size_type;
+   size_type len = size_type(last - first);
+   RandIt middle;
+
+   while (len) {
+      size_type step = len >> 1;
+      middle = first;
+      middle += step;
+
+      if (comp(*middle, key)) {
+         first = ++middle;
+         len -= step + 1;
+      }
+      else{
+         len = step;
+      }
+   }
+   return first;
+}
+
+template <class RandIt, class T, class Compare>
+RandIt upper_bound
+   (RandIt first, const RandIt last, const T& key, Compare comp)
+{
+   typedef typename iterator_traits
+      <RandIt>::size_type size_type;
+   size_type len = size_type(last - first);
+   RandIt middle;
+
+   while (len) {
+      size_type step = len >> 1;
+      middle = first;
+      middle += step;
+
+      if (!comp(key, *middle)) {
+         first = ++middle;
+         len -= step + 1;
+      }
+      else{
+         len = step;
+      }
+   }
+   return first;
+}
+
+
+template<class RandIt, class Compare, class Op>
+void op_merge_left( RandIt buf_first
+                    , RandIt first1
+                    , RandIt const last1
+                    , RandIt const last2
+                    , Compare comp
+                    , Op op)
+{
+   for(RandIt first2=last1; first2 != last2; ++buf_first){
+      if(first1 == last1){
+         op(forward_t(), first2, last2, buf_first);
+         return;
+      }
+      else if(comp(*first2, *first1)){
+         op(first2, buf_first);
+         ++first2;
+      }
+      else{
+         op(first1, buf_first);
+         ++first1;
+      }
+   }
+   if(buf_first != first1){//In case all remaining elements are in the same place
+                           //(e.g. buffer is exactly the size of the second half
+                           //and all elements from the second half are less)
+      op(forward_t(), first1, last1, buf_first);
+   }
+}
+
+// [buf_first, first1) -> buffer
+// [first1, last1) merge [last1,last2) -> [buf_first,buf_first+(last2-first1))
+// Elements from buffer are moved to [last2 - (first1-buf_first), last2)
+// Note: distance(buf_first, first1) >= distance(last1, last2), so no overlapping occurs
+template<class RandIt, class Compare>
+void merge_left
+   (RandIt buf_first, RandIt first1, RandIt const last1, RandIt const last2, Compare comp)
+{
+   op_merge_left(buf_first, first1, last1, last2, comp, move_op());
+}
+
+// [buf_first, first1) -> buffer
+// [first1, last1) merge [last1,last2) -> [buf_first,buf_first+(last2-first1))
+// Elements from buffer are swapped to [last2 - (first1-buf_first), last2)
+// Note: distance(buf_first, first1) >= distance(last1, last2), so no overlapping occurs
+template<class RandIt, class Compare>
+void swap_merge_left
+   (RandIt buf_first, RandIt first1, RandIt const last1, RandIt const last2, Compare comp)
+{
+   op_merge_left(buf_first, first1, last1, last2, comp, swap_op());
+}
+
+template<class RandIt, class Compare, class Op>
+void op_merge_right
+   (RandIt const first1, RandIt last1, RandIt last2, RandIt buf_last, Compare comp, Op op)
+{
+   RandIt const first2 = last1;
+   while(first1 != last1){
+      if(last2 == first2){
+         op(backward_t(), first1, last1, buf_last);
+         return;
+      }
+      --last2;
+      --last1;
+      --buf_last;
+      if(comp(*last2, *last1)){
+         op(last1, buf_last);
+         ++last2;
+      }
+      else{
+         op(last2, buf_last);
+         ++last1;
+      }
+   }
+   if(last2 != buf_last){  //In case all remaining elements are in the same place
+                           //(e.g. buffer is exactly the size of the first half
+                           //and all elements from the second half are less)
+      op(backward_t(), first2, last2, buf_last);
+   }
+}
+
+// [last2, buf_last) - buffer
+// [first1, last1) merge [last1,last2) -> [first1+(buf_last-last2), buf_last)
+// Note: distance[last2, buf_last) >= distance[first1, last1), so no overlapping occurs
+template<class RandIt, class Compare>
+void merge_right
+   (RandIt first1, RandIt last1, RandIt last2, RandIt buf_last, Compare comp)
+{
+   op_merge_right(first1, last1, last2, buf_last, comp, move_op());
+}
+
+// [last2, buf_last) - buffer
+// [first1, last1) merge [last1,last2) -> [first1+(buf_last-last2), buf_last)
+// Note: distance[last2, buf_last) >= distance[first1, last1), so no overlapping occurs
+template<class RandIt, class Compare>
+void swap_merge_right
+   (RandIt first1, RandIt last1, RandIt last2, RandIt buf_last, Compare comp)
+{
+   op_merge_right(first1, last1, last2, buf_last, comp, swap_op());
+}
+
+//Complexity: min(len1,len2)^2 + max(len1,len2)
+template<class RandIt, class Compare>
+void merge_bufferless_ON2(RandIt first, RandIt middle, RandIt last, Compare comp)
+{
+   if((middle - first) < (last - middle)){
+      while(first != middle){
+         RandIt const old_last1 = middle;
+         middle = boost::movelib::lower_bound(middle, last, *first, comp);
+         first = rotate_gcd(first, old_last1, middle);
+         if(middle == last){
+            break;
+         }
+         do{
+            ++first;
+         } while(first != middle && !comp(*middle, *first));
+      }
+   }
+   else{
+      while(middle != last){
+         RandIt p = boost::movelib::upper_bound(first, middle, last[-1], comp);
+         last = rotate_gcd(p, middle, last);
+         middle = p;
+         if(middle == first){
+            break;
+         }
+         --p;
+         do{
+            --last;
+         } while(middle != last && !comp(last[-1], *p));
+      }
+   }
+}
+
+static const std::size_t MergeBufferlessONLogNRotationThreshold = 32;
+
+template <class RandIt, class Distance, class Compare>
+void merge_bufferless_ONlogN_recursive
+   (RandIt first, RandIt middle, RandIt last, Distance len1, Distance len2, Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type size_type;
+
+   while(1) {
+      //trivial cases
+      if (!len2) {
+         return;
+      }
+      else if (!len1) {
+         return;
+      }
+      else if (size_type(len1 | len2) == 1u) {
+         if (comp(*middle, *first))
+            adl_move_swap(*first, *middle);  
+         return;
+      }
+      else if(size_type(len1+len2) < MergeBufferlessONLogNRotationThreshold){
+         merge_bufferless_ON2(first, middle, last, comp);
+         return;
+      }
+
+      RandIt first_cut = first;
+      RandIt second_cut = middle;
+      Distance len11 = 0;
+      Distance len22 = 0;
+      if (len1 > len2) {
+         len11 = len1 / 2;
+         first_cut +=  len11;
+         second_cut = boost::movelib::lower_bound(middle, last, *first_cut, comp);
+         len22 = size_type(second_cut - middle);
+      }
+      else {
+         len22 = len2 / 2;
+         second_cut += len22;
+         first_cut = boost::movelib::upper_bound(first, middle, *second_cut, comp);
+         len11 = size_type(first_cut - first);
+      }
+      RandIt new_middle = rotate_gcd(first_cut, middle, second_cut);
+
+      //Avoid one recursive call doing a manual tail call elimination on the biggest range
+      const Distance len_internal = len11+len22;
+      if( len_internal < (len1 + len2 - len_internal) ) {
+         merge_bufferless_ONlogN_recursive(first,      first_cut,  new_middle, len11,        len22,        comp);
+         first = new_middle;
+         middle = second_cut;
+         len1 -= len11;
+         len2 -= len22;
+      }
+      else {
+         merge_bufferless_ONlogN_recursive(new_middle, second_cut, last,       len1 - len11, len2 - len22, comp);
+         middle = first_cut;
+         last = new_middle;
+         len1 = len11;
+         len2 = len22;
+      }
+   }
+}
+
+//Complexity: NlogN
+template<class RandIt, class Compare>
+void merge_bufferless_ONlogN(RandIt first, RandIt middle, RandIt last, Compare comp)
+{
+   merge_bufferless_ONlogN_recursive
+      (first, middle, last, middle - first, last - middle, comp); 
+}
+
+template<class RandIt, class Compare>
+void merge_bufferless(RandIt first, RandIt middle, RandIt last, Compare comp)
+{
+   #define BOOST_ADAPTIVE_MERGE_NLOGN_MERGE
+   #ifdef BOOST_ADAPTIVE_MERGE_NLOGN_MERGE
+   merge_bufferless_ONlogN(first, middle, last, comp);
+   #else
+   merge_bufferless_ON2(first, middle, last, comp);
+   #endif   //BOOST_ADAPTIVE_MERGE_NLOGN_MERGE
+}
+
+// [r_first, r_last) are already in the right part of the destination range.
+template <class Compare, class InputIterator, class InputOutIterator, class Op>
+void op_merge_with_right_placed
+   ( InputIterator first, InputIterator last
+   , InputOutIterator dest_first, InputOutIterator r_first, InputOutIterator r_last
+   , Compare comp, Op op)
+{
+   BOOST_ASSERT((last - first) == (r_first - dest_first));
+   while ( first != last ) {
+      if (r_first == r_last) {
+         InputOutIterator end = op(forward_t(), first, last, dest_first);
+         BOOST_ASSERT(end == r_last);
+         (void)end;
+         return;
+      }
+      else if (comp(*r_first, *first)) {
+         op(r_first, dest_first);
+         ++r_first;
+      }
+      else {
+         op(first, dest_first);
+         ++first;
+      }
+      ++dest_first;
+   }
+   // Remaining [r_first, r_last) already in the correct place
+}
+
+template <class Compare, class InputIterator, class InputOutIterator>
+void swap_merge_with_right_placed
+   ( InputIterator first, InputIterator last
+   , InputOutIterator dest_first, InputOutIterator r_first, InputOutIterator r_last
+   , Compare comp)
+{
+   op_merge_with_right_placed(first, last, dest_first, r_first, r_last, comp, swap_op());
+}
+
+// [first, last) are already in the right part of the destination range.
+template <class Compare, class Op, class BidirIterator, class BidirOutIterator>
+void op_merge_with_left_placed
+   ( BidirOutIterator const first, BidirOutIterator last, BidirOutIterator dest_last
+   , BidirIterator const r_first, BidirIterator r_last
+   , Compare comp, Op op)
+{
+   BOOST_ASSERT((dest_last - last) == (r_last - r_first));
+   while( r_first != r_last ) {
+      if(first == last) {
+         BidirOutIterator res = op(backward_t(), r_first, r_last, dest_last);
+         BOOST_ASSERT(last == res);
+         (void)res;
+         return;
+      }
+      --r_last;
+      --last;
+      if(comp(*r_last, *last)){
+         ++r_last;
+         --dest_last;
+         op(last, dest_last);
+      }
+      else{
+         ++last;
+         --dest_last;
+         op(r_last, dest_last);
+      }
+   }
+   // Remaining [first, last) already in the correct place
+}
+
+// @endcond
+
+// [irst, last) are already in the right part of the destination range.
+template <class Compare, class BidirIterator, class BidirOutIterator>
+void merge_with_left_placed
+   ( BidirOutIterator const first, BidirOutIterator last, BidirOutIterator dest_last
+   , BidirIterator const r_first, BidirIterator r_last
+   , Compare comp)
+{
+   op_merge_with_left_placed(first, last, dest_last, r_first, r_last, comp, move_op());
+}
+
+// [r_first, r_last) are already in the right part of the destination range.
+template <class Compare, class InputIterator, class InputOutIterator>
+void merge_with_right_placed
+   ( InputIterator first, InputIterator last
+   , InputOutIterator dest_first, InputOutIterator r_first, InputOutIterator r_last
+   , Compare comp)
+{
+   op_merge_with_right_placed(first, last, dest_first, r_first, r_last, comp, move_op());
+}
+
+// [r_first, r_last) are already in the right part of the destination range.
+// [dest_first, r_first) is uninitialized memory
+template <class Compare, class InputIterator, class InputOutIterator>
+void uninitialized_merge_with_right_placed
+   ( InputIterator first, InputIterator last
+   , InputOutIterator dest_first, InputOutIterator r_first, InputOutIterator r_last
+   , Compare comp)
+{
+   BOOST_ASSERT((last - first) == (r_first - dest_first));
+   typedef typename iterator_traits<InputOutIterator>::value_type value_type;
+   InputOutIterator const original_r_first = r_first;
+
+   destruct_n<value_type, InputOutIterator> d(dest_first);
+
+   while ( first != last && dest_first != original_r_first ) {
+      if (r_first == r_last) {
+         for(; dest_first != original_r_first; ++dest_first, ++first){
+            ::new((iterator_to_raw_pointer)(dest_first)) value_type(::boost::move(*first));
+            d.incr();
+         }
+         d.release();
+         InputOutIterator end = ::boost::move(first, last, original_r_first);
+         BOOST_ASSERT(end == r_last);
+         (void)end;
+         return;
+      }
+      else if (comp(*r_first, *first)) {
+         ::new((iterator_to_raw_pointer)(dest_first)) value_type(::boost::move(*r_first));
+         d.incr();
+         ++r_first;
+      }
+      else {
+         ::new((iterator_to_raw_pointer)(dest_first)) value_type(::boost::move(*first));
+         d.incr();
+         ++first;
+      }
+      ++dest_first;
+   }
+   d.release();
+   merge_with_right_placed(first, last, original_r_first, r_first, r_last, comp);
+}
+
+/*
+// [r_first, r_last) are already in the right part of the destination range.
+// [dest_first, r_first) is uninitialized memory
+template <class Compare, class BidirOutIterator, class BidirIterator>
+void uninitialized_merge_with_left_placed
+   ( BidirOutIterator dest_first, BidirOutIterator r_first, BidirOutIterator r_last
+   , BidirIterator first, BidirIterator last
+   , Compare comp)
+{
+   BOOST_ASSERT((last - first) == (r_last - r_first));
+   typedef typename iterator_traits<BidirOutIterator>::value_type value_type;
+   BidirOutIterator const original_r_last = r_last;
+
+   destruct_n<value_type> d(&*dest_last);
+
+   while ( first != last && dest_first != original_r_first ) {
+      if (r_first == r_last) {
+         for(; dest_first != original_r_first; ++dest_first, ++first){
+            ::new(&*dest_first) value_type(::boost::move(*first));
+            d.incr();
+         }
+         d.release();
+         BidirOutIterator end = ::boost::move(first, last, original_r_first);
+         BOOST_ASSERT(end == r_last);
+         (void)end;
+         return;
+      }
+      else if (comp(*r_first, *first)) {
+         ::new(&*dest_first) value_type(::boost::move(*r_first));
+         d.incr();
+         ++r_first;
+      }
+      else {
+         ::new(&*dest_first) value_type(::boost::move(*first));
+         d.incr();
+         ++first;
+      }
+      ++dest_first;
+   }
+   d.release();
+   merge_with_right_placed(first, last, original_r_first, r_first, r_last, comp);
+}
+*/
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#endif   //#define BOOST_MOVE_MERGE_HPP
diff --git a/include/boost/move/algo/detail/merge_sort.hpp b/include/boost/move/algo/detail/merge_sort.hpp
new file mode 100644
index 0000000..62d185a
--- /dev/null
+++ b/include/boost/move/algo/detail/merge_sort.hpp
@@ -0,0 +1,139 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//! \file
+
+#ifndef BOOST_MOVE_DETAIL_MERGE_SORT_HPP
+#define BOOST_MOVE_DETAIL_MERGE_SORT_HPP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/detail/workaround.hpp>
+
+#include <boost/move/utility_core.hpp>
+#include <boost/move/algo/move.hpp>
+#include <boost/move/algo/detail/merge.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/detail/destruct_n.hpp>
+#include <boost/move/algo/detail/insertion_sort.hpp>
+#include <cassert>
+
+namespace boost {
+namespace movelib {
+
+// @cond
+
+static const unsigned MergeSortInsertionSortThreshold = 16;
+
+template <class RandIt, class Compare>
+void inplace_stable_sort(RandIt first, RandIt last, Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   if (size_type(last - first) <= size_type(MergeSortInsertionSortThreshold)) {
+      insertion_sort(first, last, comp);
+      return;
+   }
+   RandIt middle = first + (last - first) / 2;
+   inplace_stable_sort(first, middle, comp);
+   inplace_stable_sort(middle, last, comp);
+   merge_bufferless_ONlogN_recursive
+      (first, middle, last, size_type(middle - first), size_type(last - middle), comp);
+}
+
+// @endcond
+
+template<class RandIt, class RandIt2, class Compare>
+void merge_sort_copy( RandIt first, RandIt last
+                   , RandIt2 dest, Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+
+   size_type const count = size_type(last - first);
+   if(count <= MergeSortInsertionSortThreshold){
+      insertion_sort_copy(first, last, dest, comp);
+   }
+   else{
+      size_type const half = count/2;
+      merge_sort_copy(first + half, last        , dest+half   , comp);
+      merge_sort_copy(first       , first + half, first + half, comp);
+      merge_with_right_placed
+         ( first + half, first + half + half
+         , dest, dest+half, dest + count
+         , comp);
+   }
+}
+
+template<class RandIt, class RandItRaw, class Compare>
+void merge_sort_uninitialized_copy( RandIt first, RandIt last
+                                 , RandItRaw uninitialized
+                                 , Compare comp)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+
+   size_type const count = size_type(last - first);
+   if(count <= MergeSortInsertionSortThreshold){
+      insertion_sort_uninitialized_copy(first, last, uninitialized, comp);
+   }
+   else{
+      size_type const half = count/2;
+      merge_sort_uninitialized_copy(first + half, last, uninitialized + half, comp);
+      destruct_n<value_type, RandItRaw> d(uninitialized+half);
+      d.incr(count-half);
+      merge_sort_copy(first, first + half, first + half, comp);
+      uninitialized_merge_with_right_placed
+         ( first + half, first + half + half
+         , uninitialized, uninitialized+half, uninitialized+count
+         , comp);
+      d.release();
+   }
+}
+
+template<class RandIt, class RandItRaw, class Compare>
+void merge_sort( RandIt first, RandIt last, Compare comp
+               , RandItRaw uninitialized)
+{
+   typedef typename iterator_traits<RandIt>::size_type  size_type;
+   typedef typename iterator_traits<RandIt>::value_type value_type;
+
+   size_type const count = size_type(last - first);
+   if(count <= MergeSortInsertionSortThreshold){
+      insertion_sort(first, last, comp);
+   }
+   else{
+      size_type const half = count/2;
+      size_type const rest = count -  half;
+      RandIt const half_it = first + half;
+      RandIt const rest_it = first + rest;
+
+      merge_sort_uninitialized_copy(half_it, last, uninitialized, comp);
+      destruct_n<value_type, RandItRaw> d(uninitialized);
+      d.incr(rest);
+      merge_sort_copy(first, half_it, rest_it, comp);
+      merge_with_right_placed
+         ( uninitialized, uninitialized + rest
+         , first, rest_it, last, antistable<Compare>(comp));
+   }
+}
+
+}} //namespace boost {  namespace movelib{
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif //#ifndef BOOST_MOVE_DETAIL_MERGE_SORT_HPP
diff --git a/include/boost/move/algo/detail/pdqsort.hpp b/include/boost/move/algo/detail/pdqsort.hpp
new file mode 100644
index 0000000..b6a1278
--- /dev/null
+++ b/include/boost/move/algo/detail/pdqsort.hpp
@@ -0,0 +1,334 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Orson Peters  2017.
+// (C) Copyright Ion Gaztanaga 2017-2018.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// This implementation of Pattern-defeating quicksort (pdqsort) was written
+// by Orson Peters, and discussed in the Boost mailing list:
+// http://boost.2283326.n4.nabble.com/sort-pdqsort-td4691031.html
+//
+// This implementation is the adaptation by Ion Gaztanaga of code originally in GitHub
+// with permission from the author to relicense it under the Boost Software License
+// (see the Boost mailing list for details).
+//
+// The original copyright statement is pasted here for completeness:
+//
+//  pdqsort.h - Pattern-defeating quicksort.
+//  Copyright (c) 2015 Orson Peters
+//  This software is provided 'as-is', without any express or implied warranty. In no event will the
+//  authors be held liable for any damages arising from the use of this software.
+//  Permission is granted to anyone to use this software for any purpose, including commercial
+//  applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//  1. The origin of this software must not be misrepresented; you must not claim that you wrote the
+//     original software. If you use this software in a product, an acknowledgment in the product
+//     documentation would be appreciated but is not required.
+//  2. Altered source versions must be plainly marked as such, and must not be misrepresented as
+//     being the original software.
+//  3. This notice may not be removed or altered from any source distribution.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_MOVE_ALGO_PDQSORT_HPP
+#define BOOST_MOVE_ALGO_PDQSORT_HPP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/detail/workaround.hpp>
+#include <boost/move/utility_core.hpp>
+#include <boost/move/algo/detail/insertion_sort.hpp>
+#include <boost/move/algo/detail/heap_sort.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+
+#include <boost/move/adl_move_swap.hpp>
+#include <cstddef>
+
+namespace boost {
+namespace movelib {
+
+namespace pdqsort_detail {
+
+   //A simple pair implementation to avoid including <utility>
+   template<class T1, class T2>
+   struct pair
+   {
+      pair()
+      {}
+
+      pair(const T1 &t1, const T2 &t2)
+         : first(t1), second(t2)
+      {}
+
+      T1 first;
+      T2 second;
+   };
+
+    enum {
+        // Partitions below this size are sorted using insertion sort.
+        insertion_sort_threshold = 24,
+
+        // Partitions above this size use Tukey's ninther to select the pivot.
+        ninther_threshold = 128,
+
+        // When we detect an already sorted partition, attempt an insertion sort that allows this
+        // amount of element moves before giving up.
+        partial_insertion_sort_limit = 8,
+
+        // Must be multiple of 8 due to loop unrolling, and < 256 to fit in unsigned char.
+        block_size = 64,
+
+        // Cacheline size, assumes power of two.
+        cacheline_size = 64
+
+    };
+
+    // Returns floor(log2(n)), assumes n > 0.
+    template<class Unsigned>
+    Unsigned log2(Unsigned n) {
+        Unsigned log = 0;
+        while (n >>= 1) ++log;
+        return log;
+    }
+
+    // Attempts to use insertion sort on [begin, end). Will return false if more than
+    // partial_insertion_sort_limit elements were moved, and abort sorting. Otherwise it will
+    // successfully sort and return true.
+    template<class Iter, class Compare>
+    inline bool partial_insertion_sort(Iter begin, Iter end, Compare comp) {
+        typedef typename boost::movelib::iterator_traits<Iter>::value_type T;
+        typedef typename boost::movelib::iterator_traits<Iter>::size_type  size_type;
+        if (begin == end) return true;
+        
+        size_type limit = 0;
+        for (Iter cur = begin + 1; cur != end; ++cur) {
+            if (limit > partial_insertion_sort_limit) return false;
+
+            Iter sift = cur;
+            Iter sift_1 = cur - 1;
+
+            // Compare first so we can avoid 2 moves for an element already positioned correctly.
+            if (comp(*sift, *sift_1)) {
+                T tmp = boost::move(*sift);
+
+                do { *sift-- = boost::move(*sift_1); }
+                while (sift != begin && comp(tmp, *--sift_1));
+
+                *sift = boost::move(tmp);
+                limit += size_type(cur - sift);
+            }
+        }
+
+        return true;
+    }
+
+    template<class Iter, class Compare>
+    inline void sort2(Iter a, Iter b, Compare comp) {
+        if (comp(*b, *a)) boost::adl_move_iter_swap(a, b);
+    }
+
+    // Sorts the elements *a, *b and *c using comparison function comp.
+    template<class Iter, class Compare>
+    inline void sort3(Iter a, Iter b, Iter c, Compare comp) {
+        sort2(a, b, comp);
+        sort2(b, c, comp);
+        sort2(a, b, comp);
+    }
+
+    // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal
+    // to the pivot are put in the right-hand partition. Returns the position of the pivot after
+    // partitioning and whether the passed sequence already was correctly partitioned. Assumes the
+    // pivot is a median of at least 3 elements and that [begin, end) is at least
+    // insertion_sort_threshold long.
+    template<class Iter, class Compare>
+    pdqsort_detail::pair<Iter, bool> partition_right(Iter begin, Iter end, Compare comp) {
+        typedef typename boost::movelib::iterator_traits<Iter>::value_type T;
+        
+        // Move pivot into local for speed.
+        T pivot(boost::move(*begin));
+
+        Iter first = begin;
+        Iter last = end;
+
+        // Find the first element greater than or equal than the pivot (the median of 3 guarantees
+        // this exists).
+        while (comp(*++first, pivot));
+
+        // Find the first element strictly smaller than the pivot. We have to guard this search if
+        // there was no element before *first.
+        if (first - 1 == begin) while (first < last && !comp(*--last, pivot));
+        else                    while (                !comp(*--last, pivot));
+
+        // If the first pair of elements that should be swapped to partition are the same element,
+        // the passed in sequence already was correctly partitioned.
+        bool already_partitioned = first >= last;
+        
+        // Keep swapping pairs of elements that are on the wrong side of the pivot. Previously
+        // swapped pairs guard the searches, which is why the first iteration is special-cased
+        // above.
+        while (first < last) {
+            boost::adl_move_iter_swap(first, last);
+            while (comp(*++first, pivot));
+            while (!comp(*--last, pivot));
+        }
+
+        // Put the pivot in the right place.
+        Iter pivot_pos = first - 1;
+        *begin = boost::move(*pivot_pos);
+        *pivot_pos = boost::move(pivot);
+
+        return pdqsort_detail::pair<Iter, bool>(pivot_pos, already_partitioned);
+    }
+
+    // Similar function to the one above, except elements equal to the pivot are put to the left of
+    // the pivot and it doesn't check or return if the passed sequence already was partitioned.
+    // Since this is rarely used (the many equal case), and in that case pdqsort already has O(n)
+    // performance, no block quicksort is applied here for simplicity.
+    template<class Iter, class Compare>
+    inline Iter partition_left(Iter begin, Iter end, Compare comp) {
+        typedef typename boost::movelib::iterator_traits<Iter>::value_type T;
+
+        T pivot(boost::move(*begin));
+        Iter first = begin;
+        Iter last = end;
+        
+        while (comp(pivot, *--last));
+
+        if (last + 1 == end) while (first < last && !comp(pivot, *++first));
+        else                 while (                !comp(pivot, *++first));
+
+        while (first < last) {
+            boost::adl_move_iter_swap(first, last);
+            while (comp(pivot, *--last));
+            while (!comp(pivot, *++first));
+        }
+
+        Iter pivot_pos = last;
+        *begin = boost::move(*pivot_pos);
+        *pivot_pos = boost::move(pivot);
+
+        return pivot_pos;
+    }
+
+
+   template<class Iter, class Compare>
+   void pdqsort_loop( Iter begin, Iter end, Compare comp
+                    , typename boost::movelib::iterator_traits<Iter>::size_type bad_allowed
+                    , bool leftmost = true)
+   {
+        typedef typename boost::movelib::iterator_traits<Iter>::size_type size_type;
+
+        // Use a while loop for tail recursion elimination.
+        while (true) {
+            size_type size = size_type(end - begin);
+
+            // Insertion sort is faster for small arrays.
+            if (size < insertion_sort_threshold) {
+                insertion_sort(begin, end, comp);
+                return;
+            }
+
+            // Choose pivot as median of 3 or pseudomedian of 9.
+            size_type s2 = size / 2;
+            if (size > ninther_threshold) {
+                sort3(begin, begin + s2, end - 1, comp);
+                sort3(begin + 1, begin + (s2 - 1), end - 2, comp);
+                sort3(begin + 2, begin + (s2 + 1), end - 3, comp);
+                sort3(begin + (s2 - 1), begin + s2, begin + (s2 + 1), comp);
+                boost::adl_move_iter_swap(begin, begin + s2);
+            } else sort3(begin + s2, begin, end - 1, comp);
+
+            // If *(begin - 1) is the end of the right partition of a previous partition operation
+            // there is no element in [begin, end) that is smaller than *(begin - 1). Then if our
+            // pivot compares equal to *(begin - 1) we change strategy, putting equal elements in
+            // the left partition, greater elements in the right partition. We do not have to
+            // recurse on the left partition, since it's sorted (all equal).
+            if (!leftmost && !comp(*(begin - 1), *begin)) {
+                begin = partition_left(begin, end, comp) + 1;
+                continue;
+            }
+
+            // Partition and get results.
+            pdqsort_detail::pair<Iter, bool> part_result = partition_right(begin, end, comp);
+            Iter pivot_pos = part_result.first;
+            bool already_partitioned = part_result.second;
+
+            // Check for a highly unbalanced partition.
+            size_type l_size = size_type(pivot_pos - begin);
+            size_type r_size = size_type(end - (pivot_pos + 1));
+            bool highly_unbalanced = l_size < size / 8 || r_size < size / 8;
+
+            // If we got a highly unbalanced partition we shuffle elements to break many patterns.
+            if (highly_unbalanced) {
+                // If we had too many bad partitions, switch to heapsort to guarantee O(n log n).
+                if (--bad_allowed == 0) {
+                    boost::movelib::heap_sort(begin, end, comp);
+                    return;
+                }
+
+                if (l_size >= insertion_sort_threshold) {
+                    boost::adl_move_iter_swap(begin,             begin + l_size / 4);
+                    boost::adl_move_iter_swap(pivot_pos - 1, pivot_pos - l_size / 4);
+
+                    if (l_size > ninther_threshold) {
+                        boost::adl_move_iter_swap(begin + 1,         begin + (l_size / 4 + 1));
+                        boost::adl_move_iter_swap(begin + 2,         begin + (l_size / 4 + 2));
+                        boost::adl_move_iter_swap(pivot_pos - 2, pivot_pos - (l_size / 4 + 1));
+                        boost::adl_move_iter_swap(pivot_pos - 3, pivot_pos - (l_size / 4 + 2));
+                    }
+                }
+                
+                if (r_size >= insertion_sort_threshold) {
+                    boost::adl_move_iter_swap(pivot_pos + 1, pivot_pos + (1 + r_size / 4));
+                    boost::adl_move_iter_swap(end - 1,                   end - r_size / 4);
+                    
+                    if (r_size > ninther_threshold) {
+                        boost::adl_move_iter_swap(pivot_pos + 2, pivot_pos + (2 + r_size / 4));
+                        boost::adl_move_iter_swap(pivot_pos + 3, pivot_pos + (3 + r_size / 4));
+                        boost::adl_move_iter_swap(end - 2,             end - (1 + r_size / 4));
+                        boost::adl_move_iter_swap(end - 3,             end - (2 + r_size / 4));
+                    }
+                }
+            } else {
+                // If we were decently balanced and we tried to sort an already partitioned
+                // sequence try to use insertion sort.
+                if (already_partitioned && partial_insertion_sort(begin, pivot_pos, comp)
+                                        && partial_insertion_sort(pivot_pos + 1, end, comp)) return;
+            }
+                
+            // Sort the left partition first using recursion and do tail recursion elimination for
+            // the right-hand partition.
+            pdqsort_loop<Iter, Compare>(begin, pivot_pos, comp, bad_allowed, leftmost);
+            begin = pivot_pos + 1;
+            leftmost = false;
+        }
+    }
+}
+
+
+template<class Iter, class Compare>
+void pdqsort(Iter begin, Iter end, Compare comp)
+{
+   if (begin == end) return;
+   typedef typename boost::movelib::iterator_traits<Iter>::size_type size_type;
+   pdqsort_detail::pdqsort_loop<Iter, Compare>(begin, end, comp, pdqsort_detail::log2(size_type(end - begin)));
+}
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif   //BOOST_MOVE_ALGO_PDQSORT_HPP
diff --git a/include/boost/move/algo/detail/set_difference.hpp b/include/boost/move/algo/detail/set_difference.hpp
new file mode 100644
index 0000000..51d0475
--- /dev/null
+++ b/include/boost/move/algo/detail/set_difference.hpp
@@ -0,0 +1,207 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2017-2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_MOVE_SET_DIFFERENCE_HPP
+#define BOOST_MOVE_SET_DIFFERENCE_HPP
+
+#include <boost/move/algo/move.hpp>
+#include <boost/move/iterator.hpp>
+#include <boost/move/utility_core.hpp>
+
+namespace boost {
+
+namespace move_detail{
+
+template<class InputIt, class OutputIt>
+OutputIt copy(InputIt first, InputIt last, OutputIt result)
+{
+   while (first != last) {
+      *result++ = *first;
+      ++result;
+      ++first;
+   }
+   return result;
+}
+
+}  //namespace move_detail{
+
+namespace movelib {
+
+//Moves the elements from the sorted range [first1, last1) which are not found in the sorted
+//range [first2, last2) to the range beginning at result.
+//The resulting range is also sorted. Equivalent elements are treated individually,
+//that is, if some element is found m times in [first1, last1) and n times in [first2, last2),
+//it will be moved to result exactly max(m-n, 0) times.
+//The resulting range cannot overlap with either of the input ranges.
+template<class InputIt1, class InputIt2,
+         class OutputIt, class Compare>
+OutputIt set_difference
+   (InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt result, Compare comp)
+{
+   while (first1 != last1) {
+      if (first2 == last2)
+         return boost::move_detail::copy(first1, last1, result);
+
+      if (comp(*first1, *first2)) {
+         *result = *first1;
+         ++result;
+         ++first1;
+      }
+      else {
+         if (!comp(*first2, *first1)) {
+            ++first1;
+         }
+         ++first2;
+      }
+   }
+   return result;
+}
+
+//Moves the elements from the sorted range [first1, last1) which are not found in the sorted
+//range [first2, last2) to the range beginning at first1 (in place operation in range1).
+//The resulting range is also sorted. Equivalent elements are treated individually,
+//that is, if some element is found m times in [first1, last1) and n times in [first2, last2),
+//it will be moved to result exactly max(m-n, 0) times.
+template<class InputOutputIt1, class InputIt2, class Compare>
+InputOutputIt1 inplace_set_difference
+   (InputOutputIt1 first1, InputOutputIt1 last1, InputIt2 first2, InputIt2 last2, Compare comp )
+{
+   while (first1 != last1) {
+      //Skip copying from range 1 if no element has to be skipped
+      if (first2 == last2){
+         return last1;
+      }
+      else if (comp(*first1, *first2)){
+         ++first1;
+      }
+      else{
+         if (!comp(*first2, *first1)) {
+            InputOutputIt1 result = first1;
+            //An element from range 1 must be skipped, no longer an inplace operation
+            return boost::movelib::set_difference
+               ( boost::make_move_iterator(++first1)
+               , boost::make_move_iterator(last1)
+               , ++first2, last2, result, comp);
+         }
+         ++first2;
+      }
+   }
+   return first1;
+}
+
+//Moves the elements from the sorted range [first1, last1) which are not found in the sorted
+//range [first2, last2) to the range beginning at first1.
+//The resulting range is also sorted. Equivalent elements from range 1 are moved past to end
+//of the result,
+//that is, if some element is found m times in [first1, last1) and n times in [first2, last2),
+//it will be moved to result exactly max(m-n, 0) times.
+//The resulting range cannot overlap with either of the input ranges.
+template<class ForwardIt1, class InputIt2,
+         class OutputIt, class Compare>
+OutputIt set_unique_difference
+   (ForwardIt1 first1, ForwardIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt result, Compare comp)
+{
+   while (first1 != last1) {
+      if (first2 == last2){
+         //unique_copy-like sequence with forward iterators but don't write i
+         //to result before comparing as moving *i could alter the value in i.
+         ForwardIt1 i = first1;
+         while (++first1 != last1) {
+            if (comp(*i, *first1)) {
+               *result = *i;
+               ++result;
+               i = first1;
+            }
+         }
+         *result = *i;
+         ++result;
+         break;
+      }
+
+      if (comp(*first1, *first2)) {
+         //Skip equivalent elements in range1 but don't write i
+         //to result before comparing as moving *i could alter the value in i.
+         ForwardIt1 i = first1;
+         while (++first1 != last1) {
+            if (comp(*i, *first1)) {
+               break;
+            }
+         }
+         *result = *i;
+         ++result;
+      }
+      else {
+         if (comp(*first2, *first1)) {
+            ++first2;
+         }
+         else{
+            ++first1;
+         }
+      }
+   }
+   return result;
+}
+
+//Moves the elements from the sorted range [first1, last1) which are not found in the sorted
+//range [first2, last2) to the range beginning at first1 (in place operation in range1).
+//The resulting range is also sorted. Equivalent elements are treated individually,
+//that is, if some element is found m times in [first1, last1) and n times in [first2, last2),
+//it will be moved to result exactly max(m-n, 0) times.
+template<class ForwardOutputIt1, class ForwardIt2, class Compare>
+ForwardOutputIt1 inplace_set_unique_difference
+   (ForwardOutputIt1 first1, ForwardOutputIt1 last1, ForwardIt2 first2, ForwardIt2 last2, Compare comp )
+{
+   while (first1 != last1) {
+      //Skip copying from range 1 if no element has to be skipped
+      if (first2 == last2){
+         //unique-like algorithm for the remaining range 1
+         ForwardOutputIt1 result = first1;
+         while (++first1 != last1) {
+            if (comp(*result, *first1) && ++result != first1) {
+               *result = boost::move(*first1);
+            }
+         }
+         return ++result;
+      }
+      else if (comp(*first2, *first1)) {
+         ++first2;
+      }
+      else if (comp(*first1, *first2)){
+         //skip any adjacent equivalent elementin range 1
+         ForwardOutputIt1 result = first1;
+         if (++first1 != last1 && !comp(*result, *first1)) {
+            //Some elements from range 1 must be skipped, no longer an inplace operation
+            while (++first1 != last1 && !comp(*result, *first1)){}
+            return boost::movelib::set_unique_difference
+               ( boost::make_move_iterator(first1)
+               , boost::make_move_iterator(last1)
+               , first2, last2, ++result, comp);
+         }
+      }
+      else{
+         ForwardOutputIt1 result = first1;
+         //Some elements from range 1 must be skipped, no longer an inplace operation
+         while (++first1 != last1 && !comp(*result, *first1)){}
+         //An element from range 1 must be skipped, no longer an inplace operation
+         return boost::movelib::set_unique_difference
+            ( boost::make_move_iterator(first1)
+            , boost::make_move_iterator(last1)
+            , first2, last2, result, comp);
+      }
+   }
+   return first1;
+}
+
+
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#endif   //#define BOOST_MOVE_SET_DIFFERENCE_HPP
diff --git a/include/boost/move/algo/move.hpp b/include/boost/move/algo/move.hpp
new file mode 100644
index 0000000..2390877
--- /dev/null
+++ b/include/boost/move/algo/move.hpp
@@ -0,0 +1,156 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2012-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//! \file
+
+#ifndef BOOST_MOVE_ALGO_MOVE_HPP
+#define BOOST_MOVE_ALGO_MOVE_HPP
+
+#ifndef BOOST_CONFIG_HPP
+#  include <boost/config.hpp>
+#endif
+#
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+#  pragma once
+#endif
+
+#include <boost/move/detail/config_begin.hpp>
+
+#include <boost/move/utility_core.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/detail/iterator_to_raw_pointer.hpp>
+#include <boost/detail/no_exceptions_support.hpp>
+
+namespace boost {
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//                               move
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#if !defined(BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
+
+   //! <b>Effects</b>: Moves elements in the range [first,last) into the range [result,result + (last -
+   //!   first)) starting from first and proceeding to last. For each non-negative integer n < (last-first),
+   //!   performs *(result + n) = ::boost::move (*(first + n)).
+   //!
+   //! <b>Effects</b>: result + (last - first).
+   //!
+   //! <b>Requires</b>: result shall not be in the range [first,last).
+   //!
+   //! <b>Complexity</b>: Exactly last - first move assignments.
+   template <typename I, // I models InputIterator
+            typename O> // O models OutputIterator
+   O move(I f, I l, O result)
+   {
+      while (f != l) {
+         *result = ::boost::move(*f);
+         ++f; ++result;
+      }
+      return result;
+   }
+
+   //////////////////////////////////////////////////////////////////////////////
+   //
+   //                               move_backward
+   //
+   //////////////////////////////////////////////////////////////////////////////
+
+   //! <b>Effects</b>: Moves elements in the range [first,last) into the range
+   //!   [result - (last-first),result) starting from last - 1 and proceeding to
+   //!   first. For each positive integer n <= (last - first),
+   //!   performs *(result - n) = ::boost::move(*(last - n)).
+   //!
+   //! <b>Requires</b>: result shall not be in the range [first,last).
+   //!
+   //! <b>Returns</b>: result - (last - first).
+   //!
+   //! <b>Complexity</b>: Exactly last - first assignments.
+   template <typename I, // I models BidirectionalIterator
+   typename O> // O models BidirectionalIterator
+   O move_backward(I f, I l, O result)
+   {
+      while (f != l) {
+         --l; --result;
+         *result = ::boost::move(*l);
+      }
+      return result;
+   }
+
+#else
+
+   using ::std::move_backward;
+
+#endif   //!defined(BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//                               uninitialized_move
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//! <b>Effects</b>:
+//!   \code
+//!   for (; first != last; ++result, ++first)
+//!      new (static_cast<void*>(&*result))
+//!         typename iterator_traits<ForwardIterator>::value_type(boost::move(*first));
+//!   \endcode
+//!
+//! <b>Returns</b>: result
+template
+   <typename I, // I models InputIterator
+    typename F> // F models ForwardIterator
+F uninitialized_move(I f, I l, F r
+   /// @cond
+//   ,typename ::boost::move_detail::enable_if<has_move_emulation_enabled<typename boost::movelib::iterator_traits<I>::value_type> >::type* = 0
+   /// @endcond
+   )
+{
+   typedef typename boost::movelib::iterator_traits<I>::value_type input_value_type;
+
+   F back = r;
+   BOOST_TRY{
+      while (f != l) {
+         void * const addr = static_cast<void*>(::boost::move_detail::addressof(*r));
+         ::new(addr) input_value_type(::boost::move(*f));
+         ++f; ++r;
+      }
+   }
+   BOOST_CATCH(...){
+      for (; back != r; ++back){
+         boost::movelib::iterator_to_raw_pointer(back)->~input_value_type();
+      }
+      BOOST_RETHROW;
+   }
+   BOOST_CATCH_END
+   return r;
+}
+
+/// @cond
+/*
+template
+   <typename I,   // I models InputIterator
+    typename F>   // F models ForwardIterator
+F uninitialized_move(I f, I l, F r,
+   typename ::boost::move_detail::disable_if<has_move_emulation_enabled<typename boost::movelib::iterator_traits<I>::value_type> >::type* = 0)
+{
+   return std::uninitialized_copy(f, l, r);
+}
+*/
+
+/// @endcond
+
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif //#ifndef BOOST_MOVE_ALGO_MOVE_HPP
diff --git a/include/boost/move/algo/predicate.hpp b/include/boost/move/algo/predicate.hpp
new file mode 100644
index 0000000..0287d66
--- /dev/null
+++ b/include/boost/move/algo/predicate.hpp
@@ -0,0 +1,86 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2015-2016.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_MOVE_ALGO_PREDICATE_HPP
+#define BOOST_MOVE_ALGO_PREDICATE_HPP
+
+#include <boost/move/algo/move.hpp>
+#include <boost/move/adl_move_swap.hpp>
+#include <boost/move/algo/detail/basic_op.hpp>
+#include <boost/move/detail/iterator_traits.hpp>
+#include <boost/move/detail/destruct_n.hpp>
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace movelib {
+
+template<class Comp>
+struct antistable
+{
+   explicit antistable(Comp &comp)
+      : m_comp(comp)
+   {}
+
+   template<class U, class V>
+   bool operator()(const U &u, const V & v)
+   {  return !m_comp(v, u);  }
+
+   private:
+   antistable & operator=(const antistable &);
+   Comp &m_comp;
+};
+
+template <class Comp>
+class negate
+{
+   public:
+   negate()
+   {}
+
+   explicit negate(Comp comp)
+      : m_comp(comp)
+   {}
+
+   template <class T1, class T2>
+   bool operator()(const T1& l, const T2& r)
+   {
+      return !m_comp(l, r);
+   }
+
+   private:
+   Comp m_comp;
+};
+
+
+template <class Comp>
+class inverse
+{
+   public:
+   inverse()
+   {}
+
+   explicit inverse(Comp comp)
+      : m_comp(comp)
+   {}
+
+   template <class T1, class T2>
+   bool operator()(const T1& l, const T2& r)
+   {
+      return m_comp(r, l);
+   }
+
+   private:
+   Comp m_comp;
+};
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#endif   //#define BOOST_MOVE_ALGO_PREDICATE_HPP
diff --git a/include/boost/move/algo/unique.hpp b/include/boost/move/algo/unique.hpp
new file mode 100644
index 0000000..8022a65
--- /dev/null
+++ b/include/boost/move/algo/unique.hpp
@@ -0,0 +1,55 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2017-2017.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/move for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_MOVE_ALGO_UNIQUE_HPP
+#define BOOST_MOVE_ALGO_UNIQUE_HPP
+
+#include <boost/move/detail/config_begin.hpp>
+#include <boost/move/utility_core.hpp>
+
+namespace boost {
+namespace movelib {
+
+//! <b>Requires</b>: The comparison function shall be an equivalence relation. The type of *first shall satisfy
+//! the MoveAssignable requirements
+//!
+//! <b>Effects</b>: For a nonempty range, eliminates all but the first element from every consecutive group
+//!   of equivalent elements referred to by the iterator i in the range [first + 1, last) for which the
+//!   following conditions hold: pred(*(i - 1), *i) != false.
+//!
+//! <b>Returns</b>: The end of the resulting range.
+//!
+//! <b>Complexity</b>: For nonempty ranges, exactly (last - first) - 1 applications of the corresponding predicate.
+template<class ForwardIterator, class BinaryPredicate>
+ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate pred)
+{
+    if (first != last) {
+      ForwardIterator next(first);
+      ++next;
+      for (; next != last; ++next, ++first) {
+         if (pred(*first, *next)) { //Find first equal element
+            while (++next != last)
+               if (!pred(*first, *next))
+                  *++first = ::boost::move(*next);
+            break;
+         }
+      }
+      ++first;
+   }
+   return first;
+}
+
+}  //namespace movelib {
+}  //namespace boost {
+
+#include <boost/move/detail/config_end.hpp>
+
+#endif   //#define BOOST_MOVE_ALGO_UNIQUE_HPP