#ifndef __lst_h__
#define __lst_h__

#ifndef ASSERT
    #define ASSERT( x ) 
#endif // #ifndef ASSERT

#include <functional>


// lst bidirectional linked-list template class
// Here are some examples of the usage:
//
//    lst< int > MyList;
//
//    for( int i = 0; i < 10; i++ ) {        
//        MyList . push_front( i );
//    }
//
//   
//    lst< int > TestList;
//    TestList . insert( TestList . begin(), MyList . begin(), MyList . end() ); 
//
//    const lst< int > cList = MyList;
//
//    lst< int >::const_iterator I = cList . begin();
//    while( I != cList . end() ) {
//        int Num = *I;
//        I++;
//    }
//
//
//  the const_iterator is used to iterate through a const List
// 
//  

template< class T, class Operator_Eq = std::equal_to<T> >
class lst {

private: // Data types and typedefs

    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef lst< value_type > self;
	Operator_Eq _FnEq;

    class node {
    public:
      node( node* pP, node* pN, const_reference t ) : pNext( pN ), pPrev( pP ), data( t ) { ; }
      node( void ) : pNext( NULL ), pPrev( NULL ) { ; }
      node* pNext;
      node* pPrev;
      value_type data;
    };            

public:
        // iterator class for iterating through the list
    class iterator {
    friend lst;        
    private:
        typedef iterator self;
        node* pNode;

        iterator( node* pN ) : pNode( pN ) { ; }

    public:
        iterator( void ) : pNode( NULL ) { ; }
        ~iterator( void ) { ; }

        iterator( self& r ) { *this = r; }
        
        iterator& operator=( iterator& r ) { pNode = r . pNode; return *this; }
        bool operator==( const self& r ) const { return pNode == r . pNode; }
        operator!=( const self& r ) const { return pNode != r . pNode; }
        reference operator*() { return pNode -> data; }
        self& operator++() { 
            pNode = pNode -> pNext;
            return *this;
        }
        
        self operator++( int ) { 
            self tmp = *this;
            ++*this;
            return tmp;
        }
        
        self& operator--() { 
            pNode = pNode -> pPrev;
            return *this;
        }
        
        self operator--(int) { 
            self tmp = *this;
            --*this;
            return tmp;
        }

    };

        // const_iterator class for iterating through a const list
    class const_iterator {
    friend lst;        

    private:

        typedef const_iterator self;
        const node* pNode;
        const_iterator( const node* pN ) : pNode( pN ) { ; }

    public:
        const_iterator( void ) : pNode( NULL ) { ; }
        ~const_iterator( void ) { ; }

        const_iterator( const self& r ) { *this = r; }
        
        const_iterator& operator=( const const_iterator& r ) { pNode = r . pNode; return *this;}
        bool operator==( const self& r ) const { return pNode == r . pNode; }
        operator!=( const self& r ) const { return pNode != r . pNode; }
        const_reference operator*() const { return pNode -> data; }

        self& operator++() { 
            pNode = pNode -> pNext;
            return *this;
        }
        
        self operator++( int ) { 
            self tmp = *this;
            ++*this;
            return tmp;
        }
        
        self& operator--() { 
            pNode = pNode -> pPrev;
            return *this;
        }
        
        self operator--(int) { 
            self tmp = *this;
            --*this;
            return tmp;
        }

    };


    // Data
    node*   m_pNode;
    size_t  m_nItems;

public: 
        // construction / destruction
    lst( void ) {
      empty_initialize();
    };

    lst( const self& rList ) { empty_initialize(); *this = rList; }
    ~lst( void ) { clear(); delete m_pNode; m_pNode = NULL; }

    bool operator==( const self& rList ) const {
        if( size() != rList . size() ) { return false; }

        self::const_iterator IThis = begin();
        self::const_iterator IThat = rList . begin();

        while( IThis != end() ) {
            if( !_FnEq( *IThis, *IThat ) ) {
                return false;
            }
            ++IThat;
            ++IThis;
         }

         return true;
    }        
    
        // Member Fns
    self& operator=( const self& rList ) {
        clear();
        insert( begin(), rList . begin(), rList . end() );
        return *this;
    }

    void empty_initialize( void ) {
      m_pNode = new node;
      m_pNode -> pNext = m_pNode;
      m_pNode -> pPrev = m_pNode;
      m_nItems = 0;
    }

    void clear( void ) {
      node* pCur = m_pNode -> pNext;
      while( pCur != m_pNode ) {
        node* pTmp = pCur;
        pCur = pCur -> pNext;
        --m_nItems;
        delete pTmp;
        pTmp = NULL;
      }
      m_pNode -> pNext = m_pNode;
      m_pNode -> pPrev = m_pNode;

    }

        // Return the size of the list
    size_t size( void ) const             { return m_nItems; }
    bool empty( void ) const              { return 0 == size(); }

        // Return an iterator to the position after the last element in the list
        // N.B. ---- Don't dereference end()!!!!!!
        // N.B. ---- end()++ is undefined!!!!!!
    iterator end( void )                  { return iterator( m_pNode ); }
    const_iterator end( void ) const      { return const_iterator( m_pNode ); }

        // Return an iterator to the position of the first element of the list
        // You may dereference begin()
    iterator begin( void )                { return iterator( m_pNode -> pNext ); }
    const_iterator begin( void ) const    { return const_iterator( m_pNode -> pNext ); }

        // Returns a reference to the first element in the list
    reference front( void )               { return *begin(); }
    const_reference front( void ) const   { return *begin(); }

        // Returns a reference to the last element in the list
    reference back( void )                { return *(--end()); }
    const_reference back( void ) const    { return *(--end()); }
    
        // add an object to the front of the list
    void push_front( const_reference x )  { insert(begin(), x); }

        // add an object to the end of the list
    void push_back( const_reference x )   { insert(end(), x); }

        // Insert an item before the item that position points to
    void insert( iterator position, const_reference r ) {
      node* pTmp = new node( position . pNode -> pPrev, position . pNode, r );
      ( position . pNode -> pPrev ) -> pNext = pTmp;
      position . pNode -> pPrev = pTmp;
      ++m_nItems;
    }

        // Insert items first through last to the list at position position
    void insert( iterator position, iterator first, iterator last ) {
        for ( ; first != last; ++first) {
            insert(position, *first);
        }
    }

        // Insert items first through last to the list at position position
    void insert( iterator position, const_iterator first, const_iterator last ) {
        for ( ; first != last; ++first) {
            insert(position, *first);
        }
    }

        // Pop the first element from the list
    void pop_front( void )              { erase(begin()); }
    

        // Pop the last element from the list
    void pop_back( void ) {
        iterator tmp = end();
        erase(--tmp);
    }

    
        // erase the item at position pos in the list
    void erase( iterator pos ) {
        ASSERT( pos != end() );
        ( pos . pNode -> pPrev ) -> pNext = pos . pNode -> pNext;
        ( pos . pNode -> pNext ) -> pPrev = pos . pNode -> pPrev;
        --m_nItems;
        delete pos . pNode;
        pos . pNode = NULL;
        
    }

       // erase the items in the range first through last
    void erase( iterator first, iterator last ) {
        while (first != last) erase(first++);
    }

  
    const_iterator find( const_reference x ) const {
        return find( begin(), end(), x );
    }

    iterator find( const_reference x ) {
        return find( begin(), end(), x );
    }

    iterator find( iterator first, iterator last, const_reference x ) {
        while( first != last ) {
            if( _FnEq(*first, x) ) {
                return first;
            }
            first++;
        }
        return end();
    }

    const_iterator find( const_iterator first, const_iterator last, const_reference x ) const {
        while( first != last ) {
            if( _FnEq(*first, x) ) {
                return first;
            }
            first++;
        }
        return end();
    }

};

template< class T, class F >
lst< T >::iterator find( lst< T >& rLst, F& f ) {
    lst< T >::iterator I = rLst . begin();
    while( rLst . end() != I ) {
        if( f( *I ) ) {
            return I;
        }
        ++I;
    }
    return I;
}

template< class T, class F >
void for_each( lst< T >& rLst, F& f ) {
    lst< T >::iterator I = rLst . begin();
    while( rLst . end() != I ) {
        f( *I );
        ++I;
    }
}

#endif //__lst_h__