// Profiling list implementation -*- C++ -*-

// Copyright (C) 2009-2016 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file profile/list
 *  This file is a GNU profile extension to the Standard C++ Library.
 */

#ifndef _GLIBCXX_PROFILE_LIST
#define _GLIBCXX_PROFILE_LIST 1

#include <list>
#include <profile/base.h>
#include <profile/iterator_tracker.h>

namespace std _GLIBCXX_VISIBILITY(default)
{
namespace __profile
{
  template<typename _List>
    class _List_profile
    {
      _List&
      _M_conjure()
      { return *static_cast<_List*>(this); }

    public:
      __gnu_profile::__list2slist_info* _M_list2slist_info;
      __gnu_profile::__list2vector_info* _M_list2vector_info;

      _List_profile() _GLIBCXX_NOEXCEPT
      { _M_profile_construct(); }

      void
      _M_profile_construct() _GLIBCXX_NOEXCEPT
      {
	_M_list2slist_info = __profcxx_list2slist_construct();
	_M_list2vector_info = __profcxx_list2vector_construct();
      }

      void
      _M_profile_destruct() _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2vector_destruct(_M_list2vector_info);
	_M_list2vector_info = 0;
	__profcxx_list2slist_destruct(_M_list2slist_info);
	_M_list2slist_info = 0;
      }

      void
      _M_swap(_List_profile& __other)
      {
	std::swap(_M_list2slist_info, __other._M_list2slist_info);
	std::swap(_M_list2vector_info, __other._M_list2vector_info);
      }

#if __cplusplus >= 201103L
      _List_profile(const _List_profile&) noexcept
      : _List_profile() { }
      _List_profile(_List_profile&& __other) noexcept
      : _List_profile()
      { _M_swap(__other); }

      _List_profile&
      operator=(const _List_profile&) noexcept
      {
	_M_profile_destruct();
	_M_profile_construct();
      }

      _List_profile&
      operator=(_List_profile&& __other) noexcept
      {
	_M_swap(__other);
	__other._M_profile_destruct();
	__other._M_profile_construct();
      }
#endif

      ~_List_profile()
      { _M_profile_destruct(); }
    };

  /** @brief List wrapper with performance instrumentation.  */
  template<typename _Tp, typename _Allocator = std::allocator<_Tp> >
    class list
    : public _GLIBCXX_STD_C::list<_Tp, _Allocator>,
      public _List_profile<list<_Tp, _Allocator> >
    {
      typedef _GLIBCXX_STD_C::list<_Tp, _Allocator>	_Base;

    public:
      typedef typename _Base::reference			reference;
      typedef typename _Base::const_reference		const_reference;

      typedef __iterator_tracker<typename _Base::iterator, list>
							iterator;
      typedef __iterator_tracker<typename _Base::const_iterator, list>
							const_iterator;

      typedef typename _Base::size_type			size_type;
      typedef typename _Base::difference_type		difference_type;

      typedef _Tp					value_type;
      typedef _Allocator				allocator_type;
      typedef typename _Base::pointer			pointer;
      typedef typename _Base::const_pointer		const_pointer;
      typedef std::reverse_iterator<iterator>		reverse_iterator;
      typedef std::reverse_iterator<const_iterator>	const_reverse_iterator;

      // 23.2.2.1 construct/copy/destroy:

#if __cplusplus < 201103L
      list() { }
      list(const list& __x)
      : _Base(__x) { }

      ~list() { }
#else
      list() = default;
      list(const list&) = default;
      list(list&&) = default;
      ~list() = default;

      list(initializer_list<value_type> __l,
	   const allocator_type& __a = allocator_type())
      : _Base(__l, __a) { }

      list(const list& __x, const allocator_type& __a)
      : _Base(__x, __a) { }

      list(list&& __x, const allocator_type& __a)
      : _Base(std::move(__x), __a) { }
#endif

      explicit
      list(const _Allocator& __a) _GLIBCXX_NOEXCEPT
      : _Base(__a) { }

#if __cplusplus >= 201103L
      explicit
      list(size_type __n, const allocator_type& __a = allocator_type())
      : _Base(__n, __a) { }

      list(size_type __n, const _Tp& __value,
	   const _Allocator& __a = _Allocator())
      : _Base(__n, __value, __a) { }
#else
      explicit
      list(size_type __n, const _Tp& __value = _Tp(),
	   const _Allocator& __a = _Allocator())
      : _Base(__n, __value, __a) { }
#endif

#if __cplusplus >= 201103L
      template<typename _InputIterator,
	       typename = std::_RequireInputIter<_InputIterator>>
#else
      template<class _InputIterator>
#endif
      list(_InputIterator __first, _InputIterator __last,
	   const _Allocator& __a = _Allocator())
      : _Base(__first, __last, __a) { }

      list(const _Base& __x)
      : _Base(__x) { }

#if __cplusplus < 201103L
      list&
      operator=(const list& __x)
      {
	this->_M_profile_destruct();
	_M_base() = __x;
	this->_M_profile_construct();
	return *this;
      }
#else
      list&
      operator=(const list&) = default;

      list&
      operator=(list&&) = default;

      list&
      operator=(initializer_list<value_type> __l)
      {
	this->_M_profile_destruct();
	_M_base() = __l;
	this->_M_profile_construct();
	return *this;
      }
#endif

      // iterators:
      iterator
      begin() _GLIBCXX_NOEXCEPT
      { return iterator(_Base::begin(), this); }

      const_iterator
      begin() const _GLIBCXX_NOEXCEPT
      { return const_iterator(_Base::begin(), this); }

      iterator
      end() _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return iterator(_Base::end(), this);
      }

      const_iterator
      end() const _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return const_iterator(_Base::end(), this);
      }

      reverse_iterator
      rbegin() _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return reverse_iterator(end());
      }

      const_reverse_iterator
      rbegin() const _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return const_reverse_iterator(end());
      }

      reverse_iterator
      rend() _GLIBCXX_NOEXCEPT
      { return reverse_iterator(begin()); }

      const_reverse_iterator
      rend() const _GLIBCXX_NOEXCEPT
      { return const_reverse_iterator(begin()); }

#if __cplusplus >= 201103L
      const_iterator
      cbegin() const noexcept
      { return const_iterator(_Base::cbegin(), this); }

      const_iterator
      cend() const noexcept
      { return const_iterator(_Base::cend(), this); }

      const_reverse_iterator
      crbegin() const noexcept
      { return const_reverse_iterator(end()); }

      const_reverse_iterator
      crend() const noexcept
      { return const_reverse_iterator(begin()); }
#endif

      // 23.2.2.2 capacity:
      reference
      back() _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return _Base::back();
      }

      const_reference
      back() const _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	return _Base::back();
      }

      // 23.2.2.3 modifiers:
      void
      push_front(const value_type& __x)
      {
	__profcxx_list2vector_invalid_operator(this->_M_list2vector_info);
	__profcxx_list2slist_operation(this->_M_list2slist_info);
	_Base::push_front(__x);
      }

      void
      pop_front() _GLIBCXX_NOEXCEPT
      {
	__profcxx_list2slist_operation(this->_M_list2slist_info);
	_Base::pop_front();
      }

      void
      pop_back() _GLIBCXX_NOEXCEPT
      {
	_Base::pop_back();
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
      }

#if __cplusplus >= 201103L
      template<typename... _Args>
	iterator
	emplace(const_iterator __position, _Args&&... __args)
	{
	  return iterator(_Base::emplace(__position.base(),
					 std::forward<_Args>(__args)...),
			  this);
	}
#endif

      iterator
#if __cplusplus >= 201103L
      insert(const_iterator __pos, const _Tp& __x)
#else
      insert(iterator __pos, const _Tp& __x)
#endif
      {
	_M_profile_insert(__pos, this->size());
	return iterator(_Base::insert(__pos.base(), __x), this);
      }

#if __cplusplus >= 201103L
      iterator
      insert(const_iterator __pos, _Tp&& __x)
      {
	_M_profile_insert(__pos, this->size());
	return iterator(_Base::emplace(__pos.base(), std::move(__x)),
			this);
      }

      iterator
      insert(const_iterator __pos, initializer_list<value_type> __l)
      {
	_M_profile_insert(__pos, this->size());
	return iterator(_Base::insert(__pos.base(), __l), this);
      }
#endif

#if __cplusplus >= 201103L
      iterator
      insert(const_iterator __pos, size_type __n, const _Tp& __x)
      {
	_M_profile_insert(__pos, this->size());
	return iterator(_Base::insert(__pos.base(), __n, __x), this);
      }
#else
      void
      insert(iterator __pos, size_type __n, const _Tp& __x)
      {
	_M_profile_insert(__pos, this->size());
	_Base::insert(__pos.base(), __n, __x);
      }
#endif

#if __cplusplus >= 201103L
      template<typename _InputIterator,
	       typename = std::_RequireInputIter<_InputIterator>>
	iterator
	insert(const_iterator __pos, _InputIterator __first,
	       _InputIterator __last)
	{
	  _M_profile_insert(__pos, this->size());
	  return iterator(_Base::insert(__pos.base(), __first, __last),
			  this);
	}
#else
      template<class _InputIterator>
	void
	insert(iterator __pos, _InputIterator __first,
	       _InputIterator __last)
	{
	  _M_profile_insert(__pos, this->size());
	  _Base::insert(__pos.base(), __first, __last);
	}
#endif

      iterator
#if __cplusplus >= 201103L
      erase(const_iterator __pos) noexcept
#else
      erase(iterator __pos)
#endif
      {	return iterator(_Base::erase(__pos.base()), this); }

      iterator
#if __cplusplus >= 201103L
      erase(const_iterator __pos, const_iterator __last) noexcept
#else
      erase(iterator __pos, iterator __last)
#endif
      {
	// _GLIBCXX_RESOLVE_LIB_DEFECTS
	// 151. can't currently clear() empty container
	return iterator(_Base::erase(__pos.base(), __last.base()), this);
      }

      void
      swap(list& __x)
      _GLIBCXX_NOEXCEPT_IF( noexcept(declval<_Base&>().swap(__x)) )
      {
	_Base::swap(__x);
	this->_M_swap(__x);
      }

      void
      clear() _GLIBCXX_NOEXCEPT
      {
	this->_M_profile_destruct();
	_Base::clear();
	this->_M_profile_construct();
      }

      // 23.2.2.4 list operations:
      void
#if __cplusplus >= 201103L
      splice(const_iterator __pos, list&& __x) noexcept
#else
      splice(iterator __pos, list& __x)
#endif
      { this->splice(__pos, _GLIBCXX_MOVE(__x), __x.begin(), __x.end()); }

#if __cplusplus >= 201103L
      void
      splice(const_iterator __pos, list& __x) noexcept
      { this->splice(__pos, std::move(__x)); }

      void
      splice(const_iterator __pos, list& __x, const_iterator __i)
      { this->splice(__pos, std::move(__x), __i); }
#endif

      void
#if __cplusplus >= 201103L
      splice(const_iterator __pos, list&& __x, const_iterator __i) noexcept
#else
      splice(iterator __pos, list& __x, iterator __i)
#endif
      {
	// We used to perform the splice_alloc check:  not anymore, redundant
	// after implementing the relevant bits of N1599.

	// _GLIBCXX_RESOLVE_LIB_DEFECTS
	_Base::splice(__pos.base(), _GLIBCXX_MOVE(__x._M_base()),
		      __i.base());
      }

      void
#if __cplusplus >= 201103L
      splice(const_iterator __pos, list&& __x, const_iterator __first,
	     const_iterator __last) noexcept
#else
      splice(iterator __pos, list& __x, iterator __first,
	     iterator __last)
#endif
      {
	_Base::splice(__pos.base(), _GLIBCXX_MOVE(__x._M_base()),
		      __first.base(), __last.base());
      }

#if __cplusplus >= 201103L
      void
      splice(const_iterator __pos, list& __x,
	     const_iterator __first, const_iterator __last) noexcept
      { this->splice(__pos, std::move(__x), __first, __last); }
#endif

      void
      remove(const _Tp& __value)
      {
	for (iterator __x = begin(); __x != end(); )
	  {
	    if (*__x == __value)
	      __x = erase(__x);
	    else
	      ++__x;
	  }
      }

      template<class _Predicate>
	void
	remove_if(_Predicate __pred)
	{
	  for (iterator __x = begin(); __x != end(); )
	    {
	      __profcxx_list2slist_operation(this->_M_list2slist_info);
	      if (__pred(*__x))
		__x = erase(__x);
	      else
		++__x;
	    }
	}

      void
      unique()
      {
	iterator __first = begin();
	iterator __last = end();
	if (__first == __last)
	  return;
	iterator __next = __first;
	while (++__next != __last)
	  {
	    __profcxx_list2slist_operation(this->_M_list2slist_info);
	    if (*__first == *__next)
	      erase(__next);
	    else
	      __first = __next;
	    __next = __first;
	  }
      }

      template<class _BinaryPredicate>
	void
	unique(_BinaryPredicate __binary_pred)
	{
	  iterator __first = begin();
	  iterator __last = end();
	  if (__first == __last)
	    return;
	  iterator __next = __first;
	  while (++__next != __last)
	    {
	      __profcxx_list2slist_operation(this->_M_list2slist_info);
	      if (__binary_pred(*__first, *__next))
		erase(__next);
	      else
		__first = __next;
	      __next = __first;
	    }
	}

      void
#if __cplusplus >= 201103L
      merge(list&& __x)
#else
      merge(list& __x)
#endif
      { _Base::merge(_GLIBCXX_MOVE(__x._M_base())); }

#if __cplusplus >= 201103L
      void
      merge(list& __x)
      { this->merge(std::move(__x)); }
#endif

      template<class _Compare>
	void
#if __cplusplus >= 201103L
	merge(list&& __x, _Compare __comp)
#else
	merge(list& __x, _Compare __comp)
#endif
	{ _Base::merge(_GLIBCXX_MOVE(__x._M_base()), __comp); }

#if __cplusplus >= 201103L
      template<typename _Compare>
	void
	merge(list& __x, _Compare __comp)
	{ this->merge(std::move(__x), __comp); }
#endif

      _Base&
      _M_base() _GLIBCXX_NOEXCEPT	{ return *this; }

      const _Base&
      _M_base() const _GLIBCXX_NOEXCEPT	{ return *this; }

      void _M_profile_iterate(int __rewind = 0) const
      {
	__profcxx_list2slist_operation(this->_M_list2slist_info);
	__profcxx_list2vector_iterate(this->_M_list2vector_info, __rewind);
	if (__rewind)
	  __profcxx_list2slist_rewind(this->_M_list2slist_info);
      }

    private:
      size_type
      _M_profile_insert(const_iterator __pos, size_type __size)
      {
	size_type __shift = 0;
	typename _Base::const_iterator __it = __pos.base();
	for (; __it != _Base::end(); ++__it)
	  __shift++;
	__profcxx_list2slist_rewind(this->_M_list2slist_info);
	__profcxx_list2slist_operation(this->_M_list2slist_info);
	__profcxx_list2vector_insert(this->_M_list2vector_info, __shift, __size);
      }
    };

  template<typename _Tp, typename _Alloc>
    inline bool
    operator==(const list<_Tp, _Alloc>& __lhs,
	       const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() == __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline bool
    operator!=(const list<_Tp, _Alloc>& __lhs,
	       const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() != __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline bool
    operator<(const list<_Tp, _Alloc>& __lhs,
	      const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() < __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline bool
    operator<=(const list<_Tp, _Alloc>& __lhs,
	       const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() <= __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline bool
    operator>=(const list<_Tp, _Alloc>& __lhs,
	       const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() >= __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline bool
    operator>(const list<_Tp, _Alloc>& __lhs,
	      const list<_Tp, _Alloc>& __rhs)
    { return __lhs._M_base() > __rhs._M_base(); }

  template<typename _Tp, typename _Alloc>
    inline void
    swap(list<_Tp, _Alloc>& __lhs, list<_Tp, _Alloc>& __rhs)
    _GLIBCXX_NOEXCEPT_IF(noexcept(__lhs.swap(__rhs)))
    { __lhs.swap(__rhs); }

} // namespace __profile
} // namespace std

#endif
