//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___CXX03___ALGORITHM_STABLE_PARTITION_H
#define _LIBCPP___CXX03___ALGORITHM_STABLE_PARTITION_H

#include <__cxx03/__algorithm/iterator_operations.h>
#include <__cxx03/__algorithm/rotate.h>
#include <__cxx03/__config>
#include <__cxx03/__iterator/advance.h>
#include <__cxx03/__iterator/distance.h>
#include <__cxx03/__iterator/iterator_traits.h>
#include <__cxx03/__memory/destruct_n.h>
#include <__cxx03/__memory/temporary_buffer.h>
#include <__cxx03/__memory/unique_ptr.h>
#include <__cxx03/__utility/move.h>
#include <__cxx03/__utility/pair.h>
#include <__cxx03/new>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__cxx03/__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

template <class _AlgPolicy, class _Predicate, class _ForwardIterator, class _Distance, class _Pair>
_LIBCPP_HIDE_FROM_ABI _ForwardIterator __stable_partition_impl(
    _ForwardIterator __first,
    _ForwardIterator __last,
    _Predicate __pred,
    _Distance __len,
    _Pair __p,
    forward_iterator_tag __fit) {
  using _Ops = _IterOps<_AlgPolicy>;

  // *__first is known to be false
  // __len >= 1
  if (__len == 1)
    return __first;
  if (__len == 2) {
    _ForwardIterator __m = __first;
    if (__pred(*++__m)) {
      _Ops::iter_swap(__first, __m);
      return __m;
    }
    return __first;
  }
  if (__len <= __p.second) { // The buffer is big enough to use
    typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
    __destruct_n __d(0);
    unique_ptr<value_type, __destruct_n&> __h(__p.first, __d);
    // Move the falses into the temporary buffer, and the trues to the front of the line
    // Update __first to always point to the end of the trues
    value_type* __t = __p.first;
    ::new ((void*)__t) value_type(_Ops::__iter_move(__first));
    __d.template __incr<value_type>();
    ++__t;
    _ForwardIterator __i = __first;
    while (++__i != __last) {
      if (__pred(*__i)) {
        *__first = _Ops::__iter_move(__i);
        ++__first;
      } else {
        ::new ((void*)__t) value_type(_Ops::__iter_move(__i));
        __d.template __incr<value_type>();
        ++__t;
      }
    }
    // All trues now at start of range, all falses in buffer
    // Move falses back into range, but don't mess up __first which points to first false
    __i = __first;
    for (value_type* __t2 = __p.first; __t2 < __t; ++__t2, (void)++__i)
      *__i = _Ops::__iter_move(__t2);
    // __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer
    return __first;
  }
  // Else not enough buffer, do in place
  // __len >= 3
  _ForwardIterator __m = __first;
  _Distance __len2     = __len / 2; // __len2 >= 2
  _Ops::advance(__m, __len2);
  // recurse on [__first, __m), *__first know to be false
  // F?????????????????
  // f       m         l
  _ForwardIterator __first_false =
      std::__stable_partition_impl<_AlgPolicy, _Predicate&>(__first, __m, __pred, __len2, __p, __fit);
  // TTTFFFFF??????????
  // f  ff   m         l
  // recurse on [__m, __last], except increase __m until *(__m) is false, *__last know to be true
  _ForwardIterator __m1           = __m;
  _ForwardIterator __second_false = __last;
  _Distance __len_half            = __len - __len2;
  while (__pred(*__m1)) {
    if (++__m1 == __last)
      goto __second_half_done;
    --__len_half;
  }
  // TTTFFFFFTTTF??????
  // f  ff   m  m1     l
  __second_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(__m1, __last, __pred, __len_half, __p, __fit);
__second_half_done:
  // TTTFFFFFTTTTTFFFFF
  // f  ff   m    sf   l
  return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false).first;
  // TTTTTTTTFFFFFFFFFF
  //         |
}

template <class _AlgPolicy, class _Predicate, class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI _ForwardIterator
__stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred, forward_iterator_tag) {
  typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
  typedef typename iterator_traits<_ForwardIterator>::value_type value_type;

  const difference_type __alloc_limit = 3; // might want to make this a function of trivial assignment
  // Either prove all true and return __first or point to first false
  while (true) {
    if (__first == __last)
      return __first;
    if (!__pred(*__first))
      break;
    ++__first;
  }
  // We now have a reduced range [__first, __last)
  // *__first is known to be false
  difference_type __len = _IterOps<_AlgPolicy>::distance(__first, __last);
  pair<value_type*, ptrdiff_t> __p(0, 0);
  unique_ptr<value_type, __return_temporary_buffer> __h;
  if (__len >= __alloc_limit) {
    // TODO: Remove the use of std::get_temporary_buffer
    _LIBCPP_SUPPRESS_DEPRECATED_PUSH
    __p = std::get_temporary_buffer<value_type>(__len);
    _LIBCPP_SUPPRESS_DEPRECATED_POP
    __h.reset(__p.first);
  }
  return std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
      std::move(__first), std::move(__last), __pred, __len, __p, forward_iterator_tag());
}

template <class _AlgPolicy, class _Predicate, class _BidirectionalIterator, class _Distance, class _Pair>
_BidirectionalIterator __stable_partition_impl(
    _BidirectionalIterator __first,
    _BidirectionalIterator __last,
    _Predicate __pred,
    _Distance __len,
    _Pair __p,
    bidirectional_iterator_tag __bit) {
  using _Ops = _IterOps<_AlgPolicy>;

  // *__first is known to be false
  // *__last is known to be true
  // __len >= 2
  if (__len == 2) {
    _Ops::iter_swap(__first, __last);
    return __last;
  }
  if (__len == 3) {
    _BidirectionalIterator __m = __first;
    if (__pred(*++__m)) {
      _Ops::iter_swap(__first, __m);
      _Ops::iter_swap(__m, __last);
      return __last;
    }
    _Ops::iter_swap(__m, __last);
    _Ops::iter_swap(__first, __m);
    return __m;
  }
  if (__len <= __p.second) { // The buffer is big enough to use
    typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
    __destruct_n __d(0);
    unique_ptr<value_type, __destruct_n&> __h(__p.first, __d);
    // Move the falses into the temporary buffer, and the trues to the front of the line
    // Update __first to always point to the end of the trues
    value_type* __t = __p.first;
    ::new ((void*)__t) value_type(_Ops::__iter_move(__first));
    __d.template __incr<value_type>();
    ++__t;
    _BidirectionalIterator __i = __first;
    while (++__i != __last) {
      if (__pred(*__i)) {
        *__first = _Ops::__iter_move(__i);
        ++__first;
      } else {
        ::new ((void*)__t) value_type(_Ops::__iter_move(__i));
        __d.template __incr<value_type>();
        ++__t;
      }
    }
    // move *__last, known to be true
    *__first = _Ops::__iter_move(__i);
    __i      = ++__first;
    // All trues now at start of range, all falses in buffer
    // Move falses back into range, but don't mess up __first which points to first false
    for (value_type* __t2 = __p.first; __t2 < __t; ++__t2, (void)++__i)
      *__i = _Ops::__iter_move(__t2);
    // __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer
    return __first;
  }
  // Else not enough buffer, do in place
  // __len >= 4
  _BidirectionalIterator __m = __first;
  _Distance __len2           = __len / 2; // __len2 >= 2
  _Ops::advance(__m, __len2);
  // recurse on [__first, __m-1], except reduce __m-1 until *(__m-1) is true, *__first know to be false
  // F????????????????T
  // f       m        l
  _BidirectionalIterator __m1          = __m;
  _BidirectionalIterator __first_false = __first;
  _Distance __len_half                 = __len2;
  while (!__pred(*--__m1)) {
    if (__m1 == __first)
      goto __first_half_done;
    --__len_half;
  }
  // F???TFFF?????????T
  // f   m1  m        l
  __first_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(__first, __m1, __pred, __len_half, __p, __bit);
__first_half_done:
  // TTTFFFFF?????????T
  // f  ff   m        l
  // recurse on [__m, __last], except increase __m until *(__m) is false, *__last know to be true
  __m1                                  = __m;
  _BidirectionalIterator __second_false = __last;
  ++__second_false;
  __len_half = __len - __len2;
  while (__pred(*__m1)) {
    if (++__m1 == __last)
      goto __second_half_done;
    --__len_half;
  }
  // TTTFFFFFTTTF?????T
  // f  ff   m  m1    l
  __second_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(__m1, __last, __pred, __len_half, __p, __bit);
__second_half_done:
  // TTTFFFFFTTTTTFFFFF
  // f  ff   m    sf  l
  return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false).first;
  // TTTTTTTTFFFFFFFFFF
  //         |
}

template <class _AlgPolicy, class _Predicate, class _BidirectionalIterator>
_LIBCPP_HIDE_FROM_ABI _BidirectionalIterator __stable_partition_impl(
    _BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred, bidirectional_iterator_tag) {
  typedef typename iterator_traits<_BidirectionalIterator>::difference_type difference_type;
  typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
  const difference_type __alloc_limit = 4; // might want to make this a function of trivial assignment
  // Either prove all true and return __first or point to first false
  while (true) {
    if (__first == __last)
      return __first;
    if (!__pred(*__first))
      break;
    ++__first;
  }
  // __first points to first false, everything prior to __first is already set.
  // Either prove [__first, __last) is all false and return __first, or point __last to last true
  do {
    if (__first == --__last)
      return __first;
  } while (!__pred(*__last));
  // We now have a reduced range [__first, __last]
  // *__first is known to be false
  // *__last is known to be true
  // __len >= 2
  difference_type __len = _IterOps<_AlgPolicy>::distance(__first, __last) + 1;
  pair<value_type*, ptrdiff_t> __p(0, 0);
  unique_ptr<value_type, __return_temporary_buffer> __h;
  if (__len >= __alloc_limit) {
    // TODO: Remove the use of std::get_temporary_buffer
    _LIBCPP_SUPPRESS_DEPRECATED_PUSH
    __p = std::get_temporary_buffer<value_type>(__len);
    _LIBCPP_SUPPRESS_DEPRECATED_POP
    __h.reset(__p.first);
  }
  return std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
      std::move(__first), std::move(__last), __pred, __len, __p, bidirectional_iterator_tag());
}

template <class _AlgPolicy, class _Predicate, class _ForwardIterator, class _IterCategory>
_LIBCPP_HIDE_FROM_ABI _ForwardIterator __stable_partition(
    _ForwardIterator __first, _ForwardIterator __last, _Predicate&& __pred, _IterCategory __iter_category) {
  return std::__stable_partition_impl<_AlgPolicy, __remove_cvref_t<_Predicate>&>(
      std::move(__first), std::move(__last), __pred, __iter_category);
}

template <class _ForwardIterator, class _Predicate>
inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator
stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) {
  using _IterCategory = typename iterator_traits<_ForwardIterator>::iterator_category;
  return std::__stable_partition<_ClassicAlgPolicy, _Predicate&>(
      std::move(__first), std::move(__last), __pred, _IterCategory());
}

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP___CXX03___ALGORITHM_STABLE_PARTITION_H
