#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

template< class Key>
class hash
{
private:
    hash() {}
};

template<>
class hash< char>:
    public unary_function< char, size_t>
{
public:
    result_type operator()( argument_type Arg) const
    { return static_cast<result_type>(Arg); }
};
template<>
class hash< unsigned char>:
    public unary_function< unsigned char, size_t>
{
public:
    result_type operator()( argument_type Arg) const
    { return static_cast<result_type>(Arg); }
};
template<>
class hash< signed char>:
    public unary_function< signed char, size_t>
{
public:
    result_type operator()( argument_type Arg) const
    { return static_cast<result_type>(Arg); }
};
template<>
class hash< short>:
    public unary_function< short, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};
template<>
class hash< unsigned short>:
    public unary_function< unsigned short, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};
template<>
class hash< int>:
    public unary_function< int, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};
template<>
class hash< unsigned int>:
    public unary_function< unsigned int, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};
template<>
class hash< long>:
    public unary_function< long, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};
template<>
class hash< unsigned long>:
    public unary_function< unsigned long, size_t>
{
public:
  result_type operator()( argument_type Arg) const
  { return static_cast<result_type>(Arg); }
};

template< class T, const size_t Buckets, class Key, class HashFun,
    class ExtractKey, class EqualKey, class Allocator>
class static_hash_table:
    public list< T, Allocator>
{
public: // Types
    typedef static_hash_table< T, Buckets, Key, HashFun, ExtractKey, EqualKey,
        Allocator> table_type;
    typedef list< T, Allocator> list_type;

    typedef Key key_type;
    using list_type::value_type;
    typedef HashFun hasher;
    typedef EqualKey key_equal;
    typedef ExtractKey key_extract;
    using list_type::pointer;
    using list_type::const_pointer;
    using list_type::reference;
    using list_type::const_reference;
    using list_type::size_type;
    using list_type::difference_type;
    using list_type::iterator;
    using list_type::const_iterator;
    using list_type::reverse_iterator;
    using list_type::const_reverse_iterator;

protected: // Types
    typedef block< iterator, Buckets+ 1> table_buckets_type;

protected: // Variables
    table_buckets_type m_buckets;
    hasher m_hasher;
    key_equal m_key_equal;
    key_extract m_key_extract;

public: // Functions
    using list_type::begin;
    using list_type::end;
    using list_type::rbegin;
    using list_type::rend;
    using list_type::size;
    using list_type::max_size;
    using list_type::empty;
    size_type bucket_count() const
    { return m_buckets.size()- 1; }
    void resize( size_type n)
    { const bool NYI( false); assert( NYI); /* TODO: NYI */ }
    hasher hash_funct() const
    { return m_hasher; }
    key_equal key_eq() const
    { return m_key_equal; }
    static_hash_table( const HashFun& H, const EqualKey& EqK,
        const ExtractKey& ExK, const allocator_type& A): list_type( A),
        m_hasher( H), m_key_equal( EqK), m_key_extract( ExK)
    {
        fill( m_buckets.begin(), m_buckets.end(), end());
    }
    static_hash_table( const table_type& HT): list_type( HT.get_allocator())
    {
        fill( m_buckets.begin(), m_buckets.end(), end());
        (*this)= HT;
    }
    ~static_hash_table()
    {
    }
    table_type& operator=( const table_type& Other)
    {
        if( this!= &Other)
        {
            clear();
            m_hasher= Other.m_hasher;
            m_key_equal= Other.m_key_equal;
            m_key_extract= Other.m_key_extract;
            // TODO: insert_equal( Other.begin(), Other.end());
            insert_unique( Other.begin(), Other.end());
        }
        return *this;
    }
    using list_type::get_allocator;
    void swap( table_type& Other)
    {
        const bool NYI( false); assert( NYI); 
        /* TODO: NYI
        swap( m_hasher, Other.m_hasher);
        swap( m_key_equal, Other.m_key_equal);
        swap( m_key_extract, Other.m_key_extract);
        if( m_Allocator== Other.m_Allocator)
        {
            list_type::swap( Other);
            m_buckets.swap( Other.m_buckets);
        }
        else
            insert_equal( Other.begin(), Other.end());
        */
    }
    pair< iterator, bool> insert_unique( const value_type& NewVal)
    {
        const key_type NewKey( m_key_extract( NewVal));
        const size_t uiBucket( m_hasher( NewKey)% bucket_count());

        table_buckets_type::iterator itBucket( m_buckets.begin());
        itBucket+= uiBucket;
        table_buckets_type::iterator itNextBucket( itBucket);
        ++itNextBucket;

        for( iterator itFound( *itBucket); itFound!= *itNextBucket; ++itFound)
            if( m_key_equal( NewKey, m_key_extract( *itFound)))
                return pair< iterator, bool>( itFound, false);
        
        iterator itInsertPos( *itNextBucket);

        table_buckets_type::iterator itBeginFill( m_buckets.begin());
        if( begin()!= itInsertPos)
        {
            iterator itPrevNode( itInsertPos);
            itBeginFill+= ( m_hasher( m_key_extract( *(--itPrevNode)))%
                bucket_count())+ 1;
        }
        
        iterator itNewNode( insert( itInsertPos, NewVal));
        fill( itBeginFill, ++itBucket, itNewNode);
        return pair< iterator, bool>( itNewNode, true);
    }
    template <class InputIterator>
    void insert_unique( InputIterator f, InputIterator l)
    {
        while( f!= l)
        {
            insert_unique( *f);
            ++f;
        }
    }
    iterator insert_equal( const value_type& NewVal)
    {
        // TODO: NYI
        const bool NYI( false); assert( NYI); 
        return end();
    }
    template< class InputIterator>
    void insert_equal( InputIterator f, InputIterator l)
    {
        while( f!= l)
        {
            insert_equal( *f);
            ++f;
        }
    }
    void erase( iterator pos)
    {
        table_buckets_type::iterator itBeginFill( m_buckets.begin());
        if( begin()!= pos)
        {
            iterator itPrevNode( pos);
            itBeginFill+= ( m_hasher( m_key_extract( *(--itPrevNode)))%
                bucket_count())+ 1;
        }
        table_buckets_type::iterator itEndFill( m_buckets.begin());
        itEndFill+= ( m_hasher( m_key_extract( *pos))% bucket_count())+ 1;

        iterator itNextNode( pos);
        ++itNextNode;
        fill( itBeginFill, itEndFill, itNextNode);
        list_type::erase( pos);
    }
    size_type erase( const key_type& k)
    {
        size_type uiErased( 0);
        const size_type uiBucket( m_hasher( k)% bucket_count());

        table_buckets_type::iterator itBucket( m_buckets.begin());
        itBucket+= uiBucket;
        table_buckets_type::iterator itNextBucket( itBucket);
        ++itNextBucket;

        iterator itChk( *itBucket);
        // Only the first in the bucket could modify the bucket values.
        if( itChk!= *itNextBucket)
        {
            if( m_key_equal( k, m_key_extract( *itChk)))
            {
                iterator itDel( itChk);
                ++itChk;
                erase( itDel);
                ++uiErased;
            }
            else
                ++itChk;
        }
        while( itChk!= *itNextBucket)
        {
            if( m_key_equal( k, m_key_extract( *itChk)))
            {
                iterator itDel( itChk);
                ++itChk;
                list_type::erase( itDel);
                ++uiErased;
            }
            else
                ++itChk;
        }
        return uiErased;
    }
    void erase( iterator f, iterator l)
    {
        while( f!= l)
        {
            erase( f);
            ++f;
        }
    }
    iterator find( const key_type& k)
    {
        const size_type uiBucket( m_hasher( k)% bucket_count());

        table_buckets_type::iterator itBucket( m_buckets.begin());
        itBucket+= uiBucket;
        table_buckets_type::iterator itNextBucket( itBucket);
        ++itNextBucket;

        for( iterator itFound( *itBucket); itFound!= *itNextBucket; ++itFound)
            if( m_key_equal( k, m_key_extract( *itFound)))
                return itFound;

        return end();
    } 
    const_iterator find( const key_type& k) const
    {
        const size_type uiBucket( m_hasher( k)% bucket_count());

        table_buckets_type::const_iterator itBucket( m_buckets.begin());
        itBucket+= uiBucket;
        table_buckets_type::const_iterator itNextBucket( itBucket);
        ++itNextBucket;

        for( const_iterator itFound( *itBucket); itFound!= *itNextBucket; ++itFound)
            if( m_key_equal( k, m_key_extract( *itFound)))
                return itFound;

        return end();
    }
    size_type count( const key_type& k) const
    {
        // TODO: NYI
        const bool NYI( false); assert( NYI); 
        return 0;
    }
    pair< iterator, iterator> equal_range( const key_type& k)
    {
        // TODO: NYI
        const bool NYI( false); assert( NYI); 
        return pair< iterator, iterator>( end(), end());
    }
    pair< const_iterator, const_iterator> equal_range( const key_type& k) const
    {
        // TODO: NYI
        const bool NYI( false); assert( NYI); 
        return pair< const_iterator, const_iterator>( end(), end());
    }
    void clear()
    {
        if( 0!= size())
        {
            fill( m_buckets.begin(), m_buckets.end(), end());
            list_type::clear();
        }
    }
};

// TODO: Global operators.