#include <iostream>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <memory>
#include <functional>

template <typename Allocator, typename... Args>
typename Allocator::pointer allocator_new(Allocator& allocator, Args&&... args)
{
    auto p = std::allocator_traits<Allocator>::allocate( allocator, 1 );
    try
    {
        std::allocator_traits<Allocator>::construct( allocator, p , std::forward<Args>( args )... );
    }
    catch (...)
    {
        std::allocator_traits<Allocator>::destroy( allocator, p );
        throw;
    }
    return p;
}

template <typename Allocator>
void allocator_delete(Allocator& allocator, typename Allocator::pointer p)
{
    std::allocator_traits<Allocator>::destroy( allocator, p );
    std::allocator_traits<Allocator>::deallocate( allocator, p, 1 );
}

class ring_node
{
public:
    struct linkup_tag {};
    ring_node(ring_node* prev, ring_node* next) noexcept
    : next_( next ), prev_( prev ) {}
    ring_node(ring_node* prev, ring_node* next, linkup_tag ) noexcept
    : ring_node( prev, next )
    {
        prev_->next_ = this;
        next_->prev_ = this;
    }

    ring_node(const ring_node&) = delete;
    ring_node& operator=(const ring_node&) = delete;

    ring_node* next() const noexcept { return next_; }
    ring_node* prev() const noexcept { return prev_; }

    friend void link(ring_node* prev, ring_node* next) noexcept
    {
        next->prev_ = prev;
        prev->next_ = next;
    }
    friend void intersect(ring_node* a, ring_node* b) noexcept
    {
        ring_node* a_prev = a->prev();
        link( b->prev(), a );
        link( a_prev, b );
    }
    friend void flip(ring_node* p)
    {
        std::swap( p->prev_, p->next_ );
    }
private:
    ring_node* next_;
    ring_node* prev_;
};

template <typename T>
class list_node : public ring_node
{
public:
    using type = T;
    T data;

    template <typename... Args>
    list_node(ring_node* next, Args&&... args) noexcept( noexcept( T( std::declval<Args>()... ) ) )
       : ring_node( next->prev(), next, linkup_tag() ), data( std::forward<Args>( args )... )
    {}

    template <typename Allocator, typename... Args>
    static list_node* make(Allocator& alloc, ring_node* next, Args&&... args)
    {
        return allocator_new( alloc, next, std::forward<Args>( args )... );
    }
    template <typename Allocator>
    static void retire(Allocator& alloc, list_node* node)
    {
        link( node->prev(), node->next() );
        allocator_delete( alloc, node );
    }
};

template <typename Allocator>
class list_iterator;
template <typename Allocator>
class const_list_iterator;
template <typename Allocator>
void splice(const_list_iterator<Allocator> pos, const_list_iterator<Allocator> first, const_list_iterator<Allocator> last);

template <typename Allocator>
class const_list_iterator
: public std::iterator<std::bidirectional_iterator_tag,
                       typename Allocator::value_type::type,
                       typename Allocator::difference_type,
                       typename Allocator::value_type::type*,
                       typename Allocator::value_type::type&>
{
    template <typename, typename> friend class list;
    friend void splice<>(const_list_iterator, const_list_iterator, const_list_iterator);
public:
    using value_type = typename Allocator::value_type::type;
    const_list_iterator() noexcept
    {}
    const value_type& operator*() const noexcept  { return static_cast<list_node<value_type>*>( node )->data; }
    const value_type* operator->() const noexcept { return &**this; }
    const_list_iterator& operator++() noexcept    { return node = node->next(), *this; }
    const_list_iterator operator++(int) noexcept  { return node = node->next(), node->prev(); }
    const_list_iterator& operator--() noexcept    { return node = node->prev(), *this; }
    const_list_iterator operator--(int) noexcept  { return node = node->prev(), node->next(); }

    friend bool operator==(const_list_iterator a, const_list_iterator b) noexcept
    {
        return a.node == b.node;
    }
    friend bool operator!=(const_list_iterator a, const_list_iterator b) noexcept
    {
        return !( a == b );
    }
protected:
    const_list_iterator(ring_node* node) noexcept
    : node( node )
    {}
    ring_node* node;
};

template <typename Allocator>
class list_iterator
: public const_list_iterator<Allocator>
{
    template <typename, typename> friend class list;
public:
    using value_type = typename Allocator::value_type::type;
    list_iterator() noexcept
    {}
private:
    list_iterator(ring_node* node) noexcept
    : const_list_iterator<Allocator>( node )
    {}
public:
    value_type& operator*() const noexcept  { return static_cast<list_node<value_type>*>( this->node )->data; }
    value_type* operator->() const noexcept { return &**this; }
    list_iterator& operator++() noexcept    { return this->node = this->node->next(), *this; }
    list_iterator operator++(int) noexcept  { return this->node = this->node->next(), this->node->prev(); }
    list_iterator& operator--() noexcept    { return this->node = this->node->prev(), *this; }
    list_iterator operator--(int) noexcept  { return this->node = this->node->prev(), this->node->next(); }
};

template <typename Allocator>
void splice(const_list_iterator<Allocator> pos, const_list_iterator<Allocator> first, const_list_iterator<Allocator> last)
{
    intersect( last.node, pos.node );
    intersect( last.node, first.node );
}
template <typename Allocator>
void splice(const_list_iterator<Allocator> pos, const_list_iterator<Allocator> i)
{
    splice( pos, i, std::next( i ) );
}

template <typename T, typename Allocator = std::allocator<T>>
class list_base : protected std::allocator_traits<Allocator>::template rebind_alloc<list_node<T>>
{
protected:
    using node_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<list_node<T>>;
    ring_node anchor;
    explicit list_base(const Allocator& allocator)
    : node_allocator_type( allocator ), anchor( &anchor, &anchor )
    {}

    ~list_base() noexcept
    {
        for ( ring_node* p = anchor.prev(); p != &anchor; )
        {
            list_node<T>* q = static_cast<list_node<T>*>( p );
            p = p->prev();
            allocator_delete<node_allocator_type>( *this, q );
        }
    }
};

template <typename T, typename Allocator = std::allocator<T>>
class list : private list_base<T, Allocator>
{
    using base = list_base<T, Allocator>;
    using node_type = list_node<T>;
    using node_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<node_type>;
public:
    // types:
    typedef T value_type;
    typedef Allocator allocator_type;
    typedef typename allocator_type::reference reference;
    typedef typename allocator_type::const_reference const_reference;
    typedef typename allocator_type::pointer pointer;
    typedef typename allocator_type::const_pointer const_pointer;
    typedef typename allocator_type::size_type size_type;
    typedef typename allocator_type::difference_type difference_type;
    typedef list_iterator<node_allocator_type> iterator;
    typedef list_iterator<node_allocator_type> const_iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

// construct/copy/destroy:
    explicit list(const allocator_type& allocator = allocator_type())
        : base( allocator )
    {}
    explicit list(size_type n, const value_type& value = value_type(), const allocator_type& allocator = allocator_type())
        : list( allocator )
    {
        while ( n-- )
           push_back( value );
    }
    template <typename InputIterator, typename std::enable_if<!std::is_arithmetic<InputIterator>::value, int>::type = 0>
    list(InputIterator first, InputIterator last, const allocator_type& allocator = allocator_type())
        : list( allocator )
    {
        for ( ; first != last; ++first )
            push_back( *first );
    }
    list(const list& other)
        : list( other.begin(), other.end(),
                std::allocator_traits<node_allocator_type>::select_on_container_copy_construction( other.node_alloc() ) )
    {}
    list(list&& other)
        : base( other.node_alloc() )
    {
        swap( other );
    }
    list(const list& other, const allocator_type& allocator)
        : list( other.begin(), other.end(), allocator )
    {}
    list(list&& other, const allocator_type& allocator)
        : list( allocator )
    {
        swap( other );
    }
    list(std::initializer_list<T> init_list, const allocator_type& allocator = allocator_type())
        : list( init_list.begin(), init_list.end(), allocator )
    {}

    list& operator=(list rhs) noexcept
    {
        swap( rhs );
        return *this;
    }

    list& operator=(std::initializer_list<T> init_list)
    {
        assign( init_list );
        return *this;
    }

    template <typename InputIterator, typename std::enable_if<!std::is_arithmetic<InputIterator>::value, int>::type = 0>
    void assign(InputIterator first, InputIterator last)
    {
        for ( auto it = begin(); it != end(); ++it )
            if ( first == last )
                return (void)erase( it, end() );
            else
                *it = *first++;
        insert( end(), first, last );
    }
    void assign(size_type n, const value_type& value)
    {
        for ( auto it = begin(); it != end(); ++it )
            if ( n-- == 0 )
                return erase( it, end() );
            else
                *it = value;
        insert( end(), n, value );
    }
    void assign(std::initializer_list<T> init)
    {
        assign( init.begin(), init.end() );
    }

    allocator_type get_allocator() const noexcept { return node_alloc(); }

// iterators:
    iterator begin() noexcept                       { return this->anchor.next(); }
    const_iterator begin() const noexcept           { return this->anchor.next(); }
    const_iterator cbegin() const noexcept          { return this->anchor.next(); }
    iterator end() noexcept                         { return &this->anchor; }
    const_iterator end() const noexcept             { return const_cast<ring_node*>( &this->anchor ); }
    const_iterator cend() const noexcept            { return const_cast<ring_node*>( &this->anchor ); }
    reverse_iterator rbegin() noexcept              { return reverse_iterator( begin() ); }
    const_reverse_iterator rbegin() const noexcept  { return const_reverse_iterator( begin() ); }
    const_reverse_iterator crbegin() const noexcept { return crbegin(); }
    reverse_iterator rend() noexcept                { return reverse_iterator( end() ); }
    const_reverse_iterator rend() const noexcept    { return const_reverse_iterator( end() ); }
    const_reverse_iterator crend() const noexcept   { return crend(); }

// 23.3.5.3, capacity:
    bool empty() const noexcept { return begin() == end(); }
    size_type size() const noexcept { return std::distance( begin(), end() ); }
    size_type max_size() const noexcept { return node_alloc().max_size(); }
    void resize(size_type n) { return resize( n, value_type() ); }
    void resize(size_type n, const value_type& value)
    {
        for ( auto it = begin(); it != end(); ++it )
            if ( n-- == 0 )
                return (void)erase( it, end() );
        while ( n-- != 0 )
            push_back( value );
    }

// element access:
    value_type& front() noexcept             { return *begin(); }
    const value_type& front() const noexcept { return *begin(); }
    value_type& back() noexcept              { return *std::prev( end() ); }
    const value_type& back() const noexcept  { return *std::prev( end() ); }

    void pop_front()                         { erase( begin() ); }
    template <class... Args>
    void emplace_front(Args&&... args)       { return emplace( cbegin(), std::forward<Args>( args )... ); }
    void push_front(const value_type& value) { insert( cbegin(), value ); }
    void push_front(value_type&& value)      { insert( cbegin(), std::move( value ) ); }
    void pop_back()                          { erase( std::prev( end() ) ); }
    template <class... Args>
    void emplace_back(Args&&... args)        { return emplace( cend(), std::forward<Args>( args )... ); }
    void push_back(const value_type& value)  { insert( cend(), value ); }
    void push_back(value_type&& value)       { insert( cend(), std::move( value ) ); }


    template <class... Args>
    iterator emplace(const_iterator position, Args&&... args)
    {
        return node_type::make( node_alloc(), position.node, std::forward<Args>( args )... );
    }
    iterator insert(const_iterator pos, const T& value) { return emplace( pos, value ); }

    iterator insert(const_iterator position, T&& x) { return emplace( position, std::move( x ) ); }
    iterator insert(const_iterator position, size_type n, const T& x)
    {
        return splice( position--, list( n, x, get_allocator() ) ), ++position;
    }

    template <class InputIterator, typename std::enable_if<!std::is_arithmetic<InputIterator>::value, int>::type = 0>
    iterator insert(const_iterator position, InputIterator first, InputIterator last)
    {
        return splice( position--, list( first, last, get_allocator() ) ), ++position;
    }

    iterator insert(const_iterator position, std::initializer_list<T> il)
    {
        return splice( position--, list( il.begin(), il.end(), get_allocator() ) ), ++position;
    }

    iterator erase(const_iterator pos) noexcept
    {
        return node_type::retire( node_alloc(), static_cast<node_type*>( pos++.node ) ), pos;
    }
    iterator erase(const_iterator pos, const_iterator last) noexcept
    {
        splice( list( get_allocator() ).end(), pos, last );
        return last;
    }

    void swap(list& other) noexcept
    {
        intersect( &this->anchor, &other.anchor );
        intersect( this->anchor.next(), other.anchor.next() );
    }
    friend void swap(list& a, list& b)
    {
        a.swap( b );
    }

    void clear() noexcept
    {
        erase( begin(), end() );
    }

// 23.3.5.5, list operations:
    friend void splice(const_iterator pos, list& x)
    {
        splice( pos, x.begin(), x.end() );
    }
    friend void splice(const_iterator pos, list&& x)
    {
        splice( pos, x.begin(), x.end() );
    }

    void remove(const T& value)
    {
         remove_if( [&](const T& v) { return v == value; } );
    }

    template <typename Predicate>
    void remove_if(Predicate pred)
    {
         for ( auto it = begin(); it != end(); )
             if ( pred( *it ) )
                 it = erase( it );
             else
                 ++it;
    }

    void unique()
    {
        unique( std::equal_to<T>() );
    }
    template <typename BinaryPredicate>
    void unique(BinaryPredicate binary_pred)
    {
         auto it = cbegin();
         if ( it != cend() )
             for ( ;; )
             {
                 auto it2 = std::next( it );
                 if ( it2 == cend() )
                     return;
                 else if ( binary_pred( *it, *it2 ) )
                     erase( it2 );
                 else
                     ++it;
            }
    }

    void merge(list& x) { merge( x, std::less<T>() ); }
    void merge(list&& x) { merge( x ); }
    template <typename Compare>
    void merge(list& x, Compare comp)
    {
        auto it = cbegin(), it2 = x.cbegin();
        for ( ;; )
        {
            if ( it == cend() )
                return splice( it, x );
            else if ( it2 == x.cend() )
                return;
            else if ( !comp( *it2, *it ) )
                ++it;
            else
                splice( it, it2++ );
        }
    }
    template <typename Compare>
    void merge(list&& x, Compare comp) { merge( x, comp ); };

    void reverse() noexcept
    {
        ring_node* p = const_cast<ring_node*>( &this->anchor );
        do {
            flip( p );
            p = p->next();
        } while ( p != &this->anchor );
    }

    void sort() { sort( std::less<T>() ); }
    template <typename Compare>
    void sort(Compare comp);
private:
    node_allocator_type& node_alloc()
    {
        return *this;
    }
    const node_allocator_type& node_alloc() const
    {
        return *this;
    }
};

#include <iostream>
#include <iterator>
#include <algorithm>

struct foo_t {
   
    unsigned value;
 
    foo_t() : value() {}
    foo_t( unsigned value ) : value( value ) {}
    operator unsigned() const { return value; }
   
    void tell() { std::cout << value; }
};
 
int main()
{
    // ein paar Tests ...
   
    list< foo_t > list_1;
 
    for( unsigned i = 0; i < 20; ++i )
        list_1.push_back( foo_t( i ) );
   
    for( auto it = list_1.begin(); it != list_1.end(); ++it ) {
   
        it->tell();
        std::cout << " ";
    }
    std::cout.put( '\n' );
 
    list< foo_t > list_2;
 
    std::copy( list_1.begin(), list_1.end(), std::back_inserter( list_2 ) );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    std::for_each( std::begin( list_2 ), std::end( list_2 ), []( foo_t &foo ) { foo.value *= 2; } );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );

    list_1 = list_2;
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );

    list_1.reverse(); 
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );

    auto head = list_1.cbegin();
    auto tail = list_1.cend();
 
    for( unsigned i = 0; i < 3; ++i ) {
 
        ++head;
        --tail;
    }
   
    list_1.erase( head, tail );
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_1.resize( 12, 42 );
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_1.resize( 7 );
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_1.erase( list_1.cbegin() );
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_1.erase( --list_1.cend() );
    std::copy( list_1.begin(), list_1.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    auto it = list_2.begin();
    while( it != list_2.end() ) {
       
        if( *it > 15 )
            it = list_2.erase( it );
        else
            ++it;
    }
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    std::vector< foo_t > vec( list_2.cbegin(), list_2.cend() );
    std::copy( vec.begin(), vec.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( ++list_2.cbegin(), 1 );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( --list_2.cend(), 13 );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( list_2.cbegin(), 4, foo_t( 42 ) );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( list_2.cend(), 4, foo_t( 42 ) );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( ++list_2.cbegin(), 3, foo_t( 17 ) );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( --list_2.cend(), 2, foo_t( 17 ) );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( --list_2.cend(), vec.cbegin(), vec.cend() );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list_2.insert( list_2.cbegin(), --vec.cend(), vec.cend() );
    std::copy( list_2.begin(), list_2.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
 
    list< foo_t > list_3( vec.cbegin(), vec.cend() );
    std::copy( list_3.begin(), list_3.end(), std::ostream_iterator< unsigned >( std::cout, " " ) );
    std::cout.put( '\n' );
}
