// bslstl_hashtable.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLSTL_HASHTABLE
#define INCLUDED_BSLSTL_HASHTABLE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a hash-container with support for duplicate values.
//
//@CLASSES:
//   bslstl::HashTable : hashed-table container for user-supplied object types
//
//@SEE_ALSO: package bos+stdhdrs in the bos package group
//
//@DESCRIPTION: This component defines a single class template, 'HashTable',
// implementing a value-semantic container that can be used to easily implement
// the four 'unordered' containers specified by the C++11 standard.
//
// An instantiation of 'HashTable' is an allocator-aware, value-semantic type
// whose salient attributes are its size (number of keys) and the ordered
// sequence of keys the 'HashTable' contains.  If 'HashTable' is instantiated
// with a key type that is not itself value-semantic, then it will not retain
// all of its value-semantic qualities.  In particular, if the key type cannot
// be tested for equality, then a HashTable containing that type cannot be
// tested for equality.  It is even possible to instantiate 'HashTable' with a
// key type that does not have a copy-constructor, in which case the
// 'HashTable' will not be copyable.
//
///Requirements on 'KEY_CONFIG'
///----------------------------
// The elements stored in a 'HashTable' and the key by which they are indexed
// are defined by a 'KEY_CONFIG' template type parameter.  The user-supplied
// 'KEY_CONFIG' type must provide two type aliases named 'ValueType' and
// 'KeyType' that name the type of element stored and its associated key type
// respectively.  In addition, a 'KEY_CONFIG' class shall provide a static
// member function which may be called as if it had the following signature:
//..
//  static const KeyType& extractKey(const ValueType& value);
//      // Return a reference offering non-modifiable access to the key for the
//      // specified 'value'.
//..
// Optionally, the 'KEY_CONFIG' class might provide an 'extractKey' function
// with the alternative signature:
//..
//  static KeyType& extractKey(ValueType& value);
//      // Return a reference to the key for the specified 'value'.
//..
// This alternative signature is necessary to support the rare case that a hash
// function or comparator used to configure the 'HashTable' template below take
// their arguments by non-'const' reference.  This is subject to additional
// constraints that these functions may not modify the passed arguments, and is
// inherently a fragile interface and not recommended.  It is supported only
// for C++ Standard conformance.
//
// A 'HashTable' is a "Value-Semantic Type" (see {'bsldoc_glossary'}) only if
// the configured 'ValueType' is value-semantic.  It is possible to instantiate
// a 'HashTable' configured with a 'ValueType' that does not provide a full
// 'HashTable' of value-semantic operations, but then some methods of the
// container may not be instantiable.  The following terminology, adopted from
// the C++11 standard, is used in the function documentation of 'HashTable' to
// describe a function's requirements for the 'KEY' template parameter.  These
// terms are also defined in [utility.arg.requirements] (section 17.6.3.1 of
// the C++11 standard).  Note that, in the context of a 'HashTable'
// instantiation, the requirements apply specifically to the 'HashTable's
// element type, 'ValueType'.
//
// Legend
// ------
// 'X'    - denotes an allocator-aware container type (e.g., 'unordered_set')
// 'T'    - 'value_type' associated with 'X'
// 'A'    - type of the allocator used by 'X'
// 'm'    - lvalue of type 'A' (allocator)
// 'p'    - address ('T *') of uninitialized storage for a 'T' within an 'X'
// 'rv'   - rvalue of type (non-'const') 'T'
// 'v'    - rvalue or lvalue of type (possibly 'const') 'T'
// 'args' - 0 or more arguments
//
// The following terms are used to more precisely specify the requirements on
// template parameter types in function-level documentation.
//:
//: *default-insertable*: 'T' has a default constructor.  More precisely, 'T'
//:     is 'default-insertable' into 'X' means that the following expression is
//:     well-formed:
//:
//:      'allocator_traits<A>::construct(m, p)'
//:
//: *move-insertable*: 'T' provides a constructor that takes an rvalue of type
//:     (non-'const') 'T'.  More precisely, 'T' is 'move-insertable' into 'X'
//:     means that the following expression is well-formed:
//:
//:      'allocator_traits<A>::construct(m, p, rv)'
//:
//: *copy-insertable*: 'T' provides a constructor that takes an lvalue or
//:     rvalue of type (possibly 'const') 'T'.  More precisely, 'T' is
//:     'copy-insertable' into 'X' means that the following expression is
//:     well-formed:
//:
//:      'allocator_traits<A>::construct(m, p, v)'
//:
//: *move-assignable*: 'T' provides an assignment operator that takes an rvalue
//:     of type (non-'const') 'T'.
//:
//: *copy-assignable*: 'T' provides an assignment operator that takes an lvalue
//:     or rvalue of type (possibly 'const') 'T'.
//:
//: *emplace-constructible*: 'T' is 'emplace-constructible' into 'X' from
//:     'args' means that the following expression is well-formed:
//:
//:      'allocator_traits<A>::construct(m, p, args)'
//:
//: *erasable*: 'T' provides a destructor.  More precisely, 'T' is 'erasable'
//:     from 'X' means that the following expression is well-formed:
//:
//:      'allocator_traits<A>::destroy(m, p)'
//:
//: *equality-comparable*: The type provides an equality-comparison operator
//:     that defines an equivalence relationship and is both reflexive and
//:     transitive.
//
///Memory Allocation
///-----------------
// The type supplied as a HashTable's 'ALLOCATOR' template parameter determines
// how that HashTable will allocate memory.  The 'HashTable' template supports
// allocators meeting the requirements of the C++ standard allocator
// requirements ([allocator.requirements], C++11 17.6.3.5); in addition it
// supports scoped-allocators derived from the 'bslma::Allocator' memory
// allocation protocol.  Clients intending to use 'bslma' style allocators
// should use the template's default 'ALLOCATOR' type: The default type for the
// 'ALLOCATOR' template parameter, 'bsl::allocator', provides a C++11
// standard-compatible adapter for a 'bslma::Allocator' object.
//
///'bslma'-Style Allocators
/// - - - - - - - - - - - -
// If the parameterized 'ALLOCATOR' type of an 'HashTable' instantiation is
// 'bsl::allocator', then objects of that HashTable type will conform to the
// standard behavior of a 'bslma'-allocator-enabled type.  Such a HashTable
// accepts an optional 'bslma::Allocator' argument at construction.  If the
// address of a 'bslma::Allocator' object is explicitly supplied at
// construction, it will be used to supply memory for the HashTable throughout
// its lifetime; otherwise, the HashTable will use the default allocator
// installed at the time of the HashTable's construction (see 'bslma_default').
// In addition to directly allocating memory from the indicated
// 'bslma::Allocator', a HashTable supplies that allocator's address to the
// constructors of contained objects of the configured 'ValueType' with the
// 'bslalg::TypeTraitUsesBslmaAllocator' trait.
//
///Exception Safety
///----------------
// The operations of a 'HashTable' provide the strong exception guarantee (see
// {'bsldoc_glossary'}) except in the presence of a hash-functor or
// equality-comparator that throws exceptions.  If either the hash-functor or
// equality-comparator throws an exception from a non-'const' method,
// 'HashTable' provides only the basic exception guarantee, and the operation
// will leave the container in a valid but unspecified (potentially empty)
// state.
//
///Internal Data Structure
///-----------------------
// This implementation of a hash-table uses a single bidirectional list, to
// hold all the elements stored in the container, and the elements in this list
// are indexed by a dynamic array of buckets, each of which holds a pointer to
// the first and last element in the linked-list whose adjusted hash-values are
// equal to that bucket's index.
//
// As we do not cache the hashed value, if any hash function throws we will
// either do nothing and allow the exception to propagate, or, if some change
// of state has already been made, clear the whole container to provide the
// basic exception guarantee.  There are similar concerns for the 'COMPARATOR'
// predicate.
//
///Usage
///-----
// This section illustrates intended use of this component.  The
// 'bslstl::HashTable' class template provides a common foundation for
// implementing the four standard unordered containers:
//: o 'bsl::unordered_map'
//: o 'bsl::unordered_multiset'
//: o 'bsl::unordered_multimap'
//: o 'bsl::unordered_set'
// This and the subsequent examples in this component use the
// 'bslstl::HashTable' class to implement several model container classes, each
// providing a small but representative sub-set of the functionality of one of
// the standard unordered containers.
//
///Example 1: Implementing a Hashed Set Container
///----------------------------------------------
// Suppose we wish to implement, 'MyHashedSet', a greatly abbreviated version
// of 'bsl::unordered_set'.  The 'bslstl::HashTable' class template can be used
// as the basis of that implementation.
//
// First, we define 'UseEntireValueAsKey', a class template we can use to
// configure 'bslstl::HashTable' to use its entire elements as keys for its
// hasher, a policy suitable for a set container.  (Later, in {Example 2}, we
// will define 'UseFirstValueOfPairAsKey' for use in a map container.  Note
// that, in practice, developers can use the existing classes in
// {'bslstl_unorderedmapkeyconfiguration'} and
// {'bslstl_unorderedsetkeyconfiguration'}.)
//..
//                          // ==========================
//                          // struct UseEntireValueAsKey
//                          // ==========================
//
//  template <class VALUE_TYPE>
//  struct UseEntireValueAsKey {
//      // This 'struct' provides a namespace for types and methods that define
//      // the policy by which the key value of a hashed container (i.e., the
//      // value passed to the hasher) is extracted from the objects stored in
//      // the hashed container (the 'value' type).
//
//      typedef VALUE_TYPE ValueType;
//          // Alias for 'VALUE_TYPE', the type stored in the hashed container.
//
//      typedef ValueType KeyType;
//          // Alias for the type passed to the hasher by the hashed container.
//          // In this policy, that type is 'ValueType'.
//
//      static const KeyType& extractKey(const ValueType& value);
//          // Return the key value for the specified 'value'.  In this policy,
//          // that is 'value' itself.
//  };
//
//                          // --------------------------
//                          // struct UseEntireValueAsKey
//                          // --------------------------
//
//  template <class VALUE_TYPE>
//  inline
//  const typename UseEntireValueAsKey<VALUE_TYPE>::KeyType&
//                 UseEntireValueAsKey<VALUE_TYPE>::extractKey(
//                                                      const ValueType& value)
//  {
//      return value;
//  }
//..
// Next, we define 'MyPair', a class template that can hold a pair of values of
// arbitrary types.  This will be used to in 'MyHashedSet' to return the status
// of the 'insert' method, which must provide an iterator to the inserted value
// and a boolean value indicating if the value is newly inserted if it
// previously exiting in the set.  The 'MyPair' class template will also appear
// in {Example 2} and {Example 3}.  Note that in practice, users can use the
// standard 'bsl::pair' in this role; the 'MyPair' class template is used in
// these examples to avoid creating a dependency of 'bslstl_hashtable' on
// 'bslstl_pair'.
//..
//                      // =============
//                      // struct MyPair
//                      // =============
//
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  struct MyPair {
//      // PUBLIC TYPES
//      typedef  FIRST_TYPE  first_type;
//      typedef SECOND_TYPE second_type;
//
//      // DATA
//      first_type  first;
//      second_type second;
//
//      // CREATORS
//      MyPair();
//          // Create a 'MyPair' object with a default constructed 'first'
//          // member and a default constructed 'second' member.
//
//      MyPair(first_type firstValue, second_type secondValue);
//          // Create a 'MyPair' object with a 'first' member equal to the
//          // specified 'firstValue' and the 'second' member equal to the
//          // specified 'secondValue'.
//  };
//
//  // FREE OPERATORS
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  bool operator==(const MyPair<FIRST_TYPE, SECOND_TYPE>& lhs,
//                  const MyPair<FIRST_TYPE, SECOND_TYPE>& rhs);
//      // Return 'true' if the specified 'lhs' and 'rhs' MyPair objects have
//      // the same value, and 'false' otherwise.  'lhs' has the same value as
//      // 'rhs' if 'lhs.first == rhs.first' and 'lhs.second == rhs.second'.
//
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  bool operator!=(const MyPair<FIRST_TYPE, SECOND_TYPE>& lhs,
//                  const MyPair<FIRST_TYPE, SECOND_TYPE>& rhs);
//      // Return 'true' if the specified 'lhs' and 'rhs' MyPair objects do not
//      // have the same value, and 'false' otherwise.  'lhs' does not have the
//      // same value as 'rhs' if 'lhs.first != rhs.first' or
//      // 'lhs.second != rhs.second'.
//
//                      // -------------
//                      // struct MyPair
//                      // -------------
//
//  // CREATORS
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  MyPair<FIRST_TYPE,SECOND_TYPE>::MyPair()
//  : first()
//  , second()
//  {
//  }
//
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  MyPair<FIRST_TYPE,SECOND_TYPE>::MyPair( first_type firstValue,
//                                         second_type secondValue)
//  : first(firstValue)
//  , second(secondValue)
//  {
//  }
//
//  // FREE OPERATORS
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  bool operator==(const MyPair<FIRST_TYPE, SECOND_TYPE>& lhs,
//                  const MyPair<FIRST_TYPE, SECOND_TYPE>& rhs)
//  {
//      return lhs.first == rhs.first && lhs.second == rhs.second;
//  }
//
//  template <class FIRST_TYPE, class SECOND_TYPE>
//  inline
//  bool operator!=(const MyPair<FIRST_TYPE, SECOND_TYPE>& lhs,
//                  const MyPair<FIRST_TYPE, SECOND_TYPE>& rhs)
//  {
//      return lhs.first != rhs.first || lhs.second != rhs.second;
//  }
//..
// Then, we define our 'MyHashedSet' class template with an instance of
// 'bslstl::HashTable' (configured using 'UseEntireValueAsKey') as its sole
// data member.  We provide 'insert' method, to allow us to populate these
// sets, and the 'find' method to allow us to examine those elements.  We also
// provide 'size' and 'bucket_count' accessor methods to let us check the inner
// workings of our class.
//
// Note that the standard classes define aliases for the templated parameters
// and other types.  In the interest of brevity, this model class (and the
// classes in the subsequent examples) do not define such aliases except where
// strictly needed for the example.
//..
//                          // =================
//                          // class MyHashedSet
//                          // =================
//
//  template <class KEY,
//            class HASH      = bsl::hash<KEY>,
//            class EQUAL     = bsl::equal_to<KEY>,
//            class ALLOCATOR = bsl::allocator<KEY> >
//  class MyHashedSet
//  {
//    private:
//      // PRIVATE TYPES
//      typedef bsl::allocator_traits<ALLOCATOR>          AllocatorTraits;
//      typedef typename AllocatorTraits::difference_type difference_type;
//      typedef BloombergLP::bslstl::HashTableIterator<const KEY,
//                                                     difference_type>
//                                                        iterator;
//
//      // DATA
//      BloombergLP::bslstl::HashTable<UseEntireValueAsKey<KEY>,
//                                     HASH,
//                                     EQUAL,
//                                     ALLOCATOR> d_impl;
//    public:
//      // TYPES
//      typedef typename AllocatorTraits::size_type size_type;
//      typedef iterator                            const_iterator;
//
//      // CREATORS
//      explicit MyHashedSet(size_type        initialNumBuckets = 0,
//                           const HASH&      hash              = HASH(),
//                           const EQUAL&     keyEqual          = EQUAL(),
//                           const ALLOCATOR& allocator         = ALLOCATOR());
//          // Create an empty 'MyHashedSet' object having a maximum load
//          // factor of 1.  Optionally specify at least 'initialNumBuckets' in
//          // this container's initial array of buckets.  If
//          // 'initialNumBuckets' is not supplied, an implementation defined
//          // value is used.  Optionally specify a 'hash' used to generate the
//          // hash values associated to the keys extracted from the values
//          // contained in this object.  If 'hash' is not supplied, a
//          // default-constructed object of type 'HASH' is used.  Optionally
//          // specify a key-equality functor 'keyEqual' used to verify that
//          // two key values are the same.  If 'keyEqual' is not supplied, a
//          // default-constructed object of type 'EQUAL' is used.  Optionally
//          // specify an 'allocator' used to supply memory.  If 'allocator' is
//          // not supplied, a default-constructed object of the (template
//          // parameter) type 'ALLOCATOR' is used.  If the 'ALLOCATOR' is
//          // 'bsl::allocator' (the default), then 'allocator' shall be
//          // convertible to 'bslma::Allocator *'.  If the 'ALLOCATOR' is
//          // 'bsl::allocator' and 'allocator' is not supplied, the currently
//          // installed default allocator is used to supply memory.
//
//      //! ~MyHashedSet() = default;
//          // Destroy this object.
//
//      // MANIPULATORS
//      MyPair<const_iterator, bool> insert(const KEY& value);
//          // Insert the specified 'value' into this set if the 'value' does
//          // not already exist in this set; otherwise, this method has no
//          // effect.  Return a pair whose 'first' member is an iterator
//          // providing non-modifiable access to the (possibly newly inserted)
//          // 'KEY' object having 'value' (according to 'EQUAL') and whose
//          // 'second' member is 'true' if a new element was inserted, and
//          // 'false' if 'value' was already present.
//
//      // ACCESSORS
//      size_type bucket_count() const;
//          // Return the number of buckets in this set.
//
//      const_iterator cend() const;
//          // Return an iterator providing non-modifiable access to the
//          // past-the-end element (in the sequence of 'KEY' objects)
//          // maintained by this set.
//
//      const_iterator find(const KEY& value) const;
//          // Return an iterator providing non-modifiable access to the 'KEY'
//          // object in this set having the specified 'value', if such an
//          // entry exists, and the iterator returned by the 'cend' method
//          // otherwise.
//
//      size_type size() const;
//          // Return the number of elements in this set.
//  };
//..
// Next, we implement the methods of 'MyHashedSet'.  In many cases, the
// implementations consist mainly in forwarding arguments to and returning
// values from the underlying 'bslstl::HashTable'.
//..
//                          // =================
//                          // class MyHashedSet
//                          // =================
//
//  // CREATORS
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::MyHashedSet(
//                                          size_type        initialNumBuckets,
//                                          const HASH&      hash,
//                                          const EQUAL&     keyEqual,
//                                          const ALLOCATOR& allocator)
//  : d_impl(hash, keyEqual, initialNumBuckets, allocator)
//  {
//  }
//..
// Note that the 'insertIfMissing' method of 'bslstl::HashTable' provides the
// semantics needed for adding values (unique values only) to sets.
//..
//  // MANIPULATORS
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  MyPair<typename MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::iterator,
//         bool>    MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::insert(
//                                                            const KEY& value)
//  {
//      typedef MyPair<iterator, bool> ResultType;
//
//      bool                       isInsertedFlag = false;
//      bslalg::BidirectionalLink *result         = d_impl.insertIfMissing(
//                                                             &isInsertedFlag,
//                                                             value);
//      return ResultType(iterator(result), isInsertedFlag);
//  }
//
//  // ACCESSORS
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  typename MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::size_type
//           MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::bucket_count() const
//  {
//      return d_impl.numBuckets();
//  }
//
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  typename MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::const_iterator
//           MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::cend() const
//  {
//      return const_iterator();
//  }
//
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  typename MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::const_iterator
//           MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::find(const KEY& key)
//                                                                        const
//  {
//      return const_iterator(d_impl.find(key));
//  }
//
//  template <class KEY, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  typename MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::size_type
//           MyHashedSet<KEY, HASH, EQUAL, ALLOCATOR>::size() const
//  {
//      return d_impl.size();
//  }
//..
// Finally, we create 'mhs', an instance of 'MyHashedSet', exercise it, and
// confirm that it behaves as expected.
//..
//  MyHashedSet<int> mhs;
//  assert( 0    == mhs.size());
//  assert( 1    == mhs.bucket_count());
//..
// Notice that the newly created set is empty and has a single bucket.
//
// Inserting a value (10) succeeds the first time but correctly fails on the
// second attempt.
//..
//  MyPair<MyHashedSet<int>::const_iterator, bool> status;
//
//  status = mhs.insert(10);
//  assert( 1    ==  mhs.size());
//  assert(10    == *status.first);
//  assert(true  ==  status.second);
//
//  status = mhs.insert(10);
//  assert( 1    ==  mhs.size());
//  assert(10    == *status.first);
//  assert(false ==  status.second);
//..
// We can insert a different value (20) and thereby increase the set size to 2.
//..
//  status = mhs.insert(20);
//  assert( 2    ==  mhs.size());
//  assert(20    == *status.first);
//  assert(true  ==  status.second);
//..
// Each of the inserted values (10, 20) can be found in the set.
//..
//  MyHashedSet<int>::const_iterator itr, end = mhs.cend();
//
//  itr = mhs.find(10);
//  assert(end !=  itr);
//  assert(10  == *itr);
//
//  itr = mhs.find(20);
//  assert(end !=  itr);
//  assert(20  == *itr);
//..
// However, a value known to absent from the set (0), is correctly reported as
// not there.
//..
//  itr = mhs.find(0);
//  assert(end ==  itr);
//..
//
///Example 2: Implementing a Hashed Map Container
///----------------------------------------------
// Suppose we wish to implement, 'MyHashedMap', a greatly abbreviated version
// of 'bsl::unordered_map'.  As with 'MyHashedSet' (see {Example 1}), the
// 'bslstl::HashTable' class template can be used as the basis of our
// implementation.
//
// First, we define 'UseFirstValueOfPairAsKey', a class template we can use to
// configure 'bslstl::HashTable' to use the 'first' member of each element,
// each a 'MyPair', as the key-value for hashing.  Note that, in practice,
// developers can use class defined in {'bslstl_unorderedmapkeyconfiguration'}.
//..
//                          // ===============================
//                          // struct UseFirstValueOfPairAsKey
//                          // ===============================
//
//  template <class VALUE_TYPE>
//  struct UseFirstValueOfPairAsKey {
//      // This 'struct' provides a namespace for types and methods that define
//      // the policy by which the key value of a hashed container (i.e., the
//      // value passed to the hasher) is extracted from the objects stored in
//      // the hashed container (the 'value' type).
//
//      typedef VALUE_TYPE ValueType;
//          // Alias for 'VALUE_TYPE', the type stored in the hashed container.
//          // For this policy 'ValueType' must define a public member named
//          // 'first' of type 'first_type'.
//
//      typedef typename ValueType::first_type KeyType;
//          // Alias for the type passed to the hasher by the hashed container.
//          // In this policy, that type is the type of the 'first' element of
//          // 'ValueType'.
//
//      static const KeyType& extractKey(const ValueType& value);
//          // Return the key value for the specified 'value'.  In this policy,
//          // that is the value of the 'first' member of 'value'.
//  };
//
//                          // -------------------------------
//                          // struct UseFirstValueOfPairAsKey
//                          // -------------------------------
//
//  template <class VALUE_TYPE>
//  inline
//  const typename UseFirstValueOfPairAsKey<VALUE_TYPE>::KeyType&
//                 UseFirstValueOfPairAsKey<VALUE_TYPE>::extractKey(
//                                                      const ValueType& value)
//  {
//      return value.first;
//  }
//..
// Next, we define our 'MyHashedMap' class template with an instance of
// 'bslstl::HashTable' (configured using 'UseFirstValueOfPairAsKey') as its
// sole data member.  In this example, we choose to implement 'operator[]'
// (corresponding to the signature method of 'bsl::unordered_map') to allow us
// to populate our maps and to examine their elements.
//..
//                          // =================
//                          // class MyHashedMap
//                          // =================
//
//  template <class KEY,
//            class VALUE,
//            class HASH      = bsl::hash<KEY>,
//            class EQUAL     = bsl::equal_to<KEY>,
//            class ALLOCATOR = bsl::allocator<KEY> >
//  class MyHashedMap
//  {
//    private:
//      // PRIVATE TYPES
//      typedef bsl::allocator_traits<ALLOCATOR>          AllocatorTraits;
//
//      typedef BloombergLP::bslstl::HashTable<
//                      UseFirstValueOfPairAsKey<MyPair<const KEY, VALUE> >,
//                      HASH,
//                      EQUAL,
//                      ALLOCATOR>                     HashTable;
//
//      // DATA
//      HashTable d_impl;
//
//    public:
//      // TYPES
//      typedef typename AllocatorTraits::size_type size_type;
//
//      // CREATORS
//      explicit MyHashedMap(size_type        initialNumBuckets = 0,
//                           const HASH&      hash              = HASH(),
//                           const EQUAL&     keyEqual          = EQUAL(),
//                           const ALLOCATOR& allocator         = ALLOCATOR());
//      // Create an empty 'MyHashedMap' object having a maximum load factor
//      // of 1.  Optionally specify at least 'initialNumBuckets' in this
//      // container's initial array of buckets.  If 'initialNumBuckets' is not
//      // supplied, one empty bucket shall be used and no memory allocated.
//      // Optionally specify 'hash' to generate the hash values associated
//      // with the key-value pairs contained in this unordered map.  If 'hash'
//      // is not supplied, a default-constructed object of (template
//      // parameter) 'HASH' is used.  Optionally specify a key-equality
//      // functor 'keyEqual' used to determine whether two keys have the same
//      // value.  If 'keyEqual' is not supplied, a default-constructed object
//      // of (template parameter) 'EQUAL' is used.  Optionally specify an
//      // 'allocator' used to supply memory.  If 'allocator' is not supplied,
//      // a default-constructed object of the (template parameter) type
//      // 'ALLOCATOR' is used.  If 'ALLOCATOR' is 'bsl::allocator' (the
//      // default), then 'allocator' shall be convertible to
//      // 'bslma::Allocator *'.  If 'ALLOCATOR' is 'bsl::allocator' and
//      // 'allocator' is not supplied, the currently installed default
//      // allocator is used to supply memory.  Note that more than
//      // 'initialNumBuckets' buckets may be created in order to preserve the
//      // bucket allocation strategy of the hash-table (but never fewer).
//
//      //! ~MyHashedMap() = default;
//          // Destroy this object.
//
//      // MANIPULATORS
//      VALUE& operator[](const KEY& key);
//          // Return a reference providing modifiable access to the
//          // mapped-value associated with the specified 'key' in this
//          // unordered map; if this unordered map does not already contain a
//          // 'value_type' object with 'key', first insert a new 'value_type'
//          // object having 'key' and a default-constructed 'VALUE' object.
//          // Note that this method requires that the (template parameter)
//          // type 'KEY' is "copy-constructible" and the (template parameter)
//          // 'VALUE' is "default-constructible".
//  };
//..
// Then, we implement the methods 'MyHashedMap'.  The construct need merely
// forward its arguments to the constructor of 'd_impl',
//..
//                          // =================
//                          // class MyHashedMap
//                          // =================
//
//  // CREATORS
//  template <class KEY, class VALUE, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  MyHashedMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::MyHashedMap(
//                                          size_type        initialNumBuckets,
//                                          const HASH&      hash,
//                                          const EQUAL&     keyEqual,
//                                          const ALLOCATOR& allocator)
//  : d_impl(hash, keyEqual, initialNumBuckets, allocator)
//  {
//  }
//..
// As with 'MyHashedSet', the 'insertIfMissing' method of 'bslstl::HashTable'
// provides the semantics we need: an element is inserted only if no such
// element (no element with the same key) in the container, and a reference to
// that element ('node') is returned.  Here, we use 'node' to obtain and return
// a reference offering modifiable access to the 'second' member of the
// (possibly newly added) element.  Note that the 'static_cast' from
// 'HashTableLink *' to 'HashTableNode *' is valid because the nodes derive
// from the link type (see 'bslalg_bidirectionallink' and
// 'bslalg_hashtableimputil').
//..
//  // MANIPULATORS
//  template <class KEY, class VALUE, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  VALUE& MyHashedMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::operator[](
//                                                              const KEY& key)
//  {
//      typedef typename HashTable::NodeType           HashTableNode;
//      typedef BloombergLP::bslalg::BidirectionalLink HashTableLink;
//
//      HashTableLink *node = d_impl.insertIfMissing(key);
//      return static_cast<HashTableNode *>(node)->value().second;
//  }
//..
// Finally, we create 'mhm', an instance of 'MyHashedMap', exercise it, and
// confirm that it behaves as expected.  We can add an element (with key value
// of 0).
//..
//  MyHashedMap<int, double> mhm;
//
//  mhm[0] = 1.234;
//  assert(1.234 == mhm[0]);
//..
// We can change the value of the element with key value 0.
//..
//  mhm[0] = 4.321;
//  assert(4.321 == mhm[0]);
//..
// We can add a new element (key value 1), without changing the previously
// existing element (key value 0).
//..
//  mhm[1] = 5.768;
//  assert(5.768 == mhm[1]);
//  assert(4.321 == mhm[0]);
//..
// Accessing a non-existing element (key value 2) creates that element and
// populates it with the default value of the mapped value (0.0).
//..
//  assert(0.000 == mhm[2]);
//..
//
///Example 3: Implementing a Hashed Multi-Map Container
///----------------------------------------------------
// Suppose we wish to implement, 'MyHashedMultiMap', a greatly abbreviated
// version of 'bsl::unordered_multimap'.  As with 'MyHashedSet' and
// 'MyHashedMap' (see {Example 1}, and {Example 2}, respectively), the
// 'bslstl::HashTable' class template can be used as the basis of our
// implementation.
//
// First, we need a class template to configure 'bslstl::HashTable' to extract
// key values in manner appropriate for maps.  The previously defined
// 'UseFirstValueOfPairAsKey' class template (see {Example 2}) suits perfectly.
//
// Next, we define our 'MyHashedMultiMap' class template with an instance of
// 'bslstl::HashTable' (configured using 'UseFirstValueOfPairAsKey') as its
// sole data member.  In this example, we choose to implement an 'insert'
// method to populate our container, and an 'equal_range' method (a signature
// method of the multi containers) to provide access to those elements.
//..
//                          // ======================
//                          // class MyHashedMultiMap
//                          // ======================
//
//  template <class KEY,
//            class VALUE,
//            class HASH      = bsl::hash<KEY>,
//            class EQUAL     = bsl::equal_to<KEY>,
//            class ALLOCATOR = bsl::allocator<KEY> >
//  class MyHashedMultiMap
//  {
//    private:
//      // PRIVATE TYPES
//      typedef MyPair<const KEY, VALUE>                  value_type;
//      typedef bsl::allocator_traits<ALLOCATOR>          AllocatorTraits;
//      typedef typename AllocatorTraits::difference_type difference_type;
//
//      typedef BloombergLP::bslstl::HashTable<
//                         UseFirstValueOfPairAsKey<MyPair<const KEY, VALUE> >,
//                         HASH,
//                         EQUAL,
//                         ALLOCATOR>                     HashTable;
//
//      // DATA
//      HashTable d_impl;
//
//    public:
//      // TYPES
//      typedef typename AllocatorTraits::size_type  size_type;
//
//      typedef BloombergLP::bslstl::HashTableIterator<value_type,
//                                                     difference_type>
//                                                                    iterator;
//      typedef BloombergLP::bslstl::HashTableIterator<const value_type,
//                                                     difference_type>
//                                                              const_iterator;
//
//      // CREATORS
//      explicit MyHashedMultiMap(
//                           size_type        initialNumBuckets = 0,
//                           const HASH&      hash              = HASH(),
//                           const EQUAL&     keyEqual          = EQUAL(),
//                           const ALLOCATOR& allocator         = ALLOCATOR());
//      // Create an empty 'MyHashedMultiMap' object having a maximum load
//      // factor of 1.  Optionally specify at least 'initialNumBuckets' in
//      // this container's initial array of buckets.  If 'initialNumBuckets'
//      // is not supplied, an implementation defined value is used.
//      // Optionally specify a 'hash', a hash-functor used to generate the
//      // hash values associated to the key-value pairs contained in this
//      // object.  If 'hash' is not supplied, a default-constructed object of
//      // (template parameter) 'HASH' type is used.  Optionally specify a
//      // key-equality functor 'keyEqual' used to verify that two key values
//      // are the same.  If 'keyEqual' is not supplied, a default-constructed
//      // object of (template parameter) 'EQUAL' type is used.  Optionally
//      // specify an 'allocator' used to supply memory.  If 'allocator' is not
//      // supplied, a default-constructed object of the (template parameter)
//      // 'ALLOCATOR' type is used.  If 'ALLOCATOR' is 'bsl::allocator' (the
//      // default), then 'allocator' shall be convertible to
//      // 'bslma::Allocator *'.  If the 'ALLOCATOR' is 'bsl::allocator' and
//      // 'allocator' is not supplied, the currently installed default
//      // allocator is used to supply memory.
//
//      //! ~MyHashedMultiMap() = default;
//          // Destroy this object.
//
//      // MANIPULATORS
//      template <class SOURCE_TYPE>
//      iterator insert(const SOURCE_TYPE& value);
//          // Insert the specified 'value' into this multi-map, and return an
//          // iterator to the newly inserted element.  Note that this method
//          // requires that the (class template parameter) types 'KEY' and
//          // 'VALUE' types both be "copy-constructible", and that the
//          // (function template parameter) 'SOURCE_TYPE' be convertible to
//          // the (class template parameter) 'VALUE' type.
//
//      // ACCESSORS
//      MyPair<const_iterator, const_iterator> equal_range(const KEY& key)
//                                                                       const;
//          // Return a pair of iterators providing non-modifiable access to
//          // the sequence of 'value_type' objects in this container matching
//          // the specified 'key', where the first iterator is positioned at
//          // the start of the sequence and the second iterator is positioned
//          // one past the end of the sequence.  If this container contains no
//          // 'value_type' objects matching 'key', then the two returned
//          // iterators will have the same value.
//  };
//..
// Then, we implement the methods 'MyHashedMultiMap'.  The construct need
// merely forward its arguments to the constructor of 'd_impl',
//..
//                          // ======================
//                          // class MyHashedMultiMap
//                          // ======================
//
//  // CREATORS
//  template <class KEY, class VALUE, class HASH, class EQUAL, class ALLOCATOR>
//  inline
//  MyHashedMultiMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::MyHashedMultiMap(
//                                         size_type        initialNumBuckets,
//                                         const HASH&      hash,
//                                         const EQUAL&     keyEqual,
//                                         const ALLOCATOR& allocator)
//  : d_impl(hash, keyEqual, initialNumBuckets, allocator)
//  {
//  }
//..
// Note that here we forgo use of the 'insertIfMissing' method and use the
// 'insert' method of 'bslstl::HashTable'.  This method supports the semantics
// of the multi containers: there can be more than one element with the same
// key value.
//..
//  // MANIPULATORS
//  template <class KEY, class VALUE, class HASH, class EQUAL, class ALLOCATOR>
//  template <class SOURCE_TYPE>
//  inline
//  typename MyHashedMultiMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::iterator
//           MyHashedMultiMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::insert(
//                                                    const SOURCE_TYPE& value)
//  {
//      return iterator(d_impl.insert(value));
//  }
//..
// The 'equal_range' method need only convert the values returned by the
// 'findRange' method to the types expected by the caller.
//..
//  // ACCESSORS
//  template <class KEY, class VALUE, class HASH, class EQUAL, class ALLOCATOR>
//  MyPair<typename MyHashedMultiMap<KEY,
//                                   VALUE,
//                                   HASH,
//                                   EQUAL,
//                                   ALLOCATOR>::const_iterator,
//         typename MyHashedMultiMap<KEY,
//                                   VALUE,
//                                   HASH,
//                                   EQUAL, ALLOCATOR>::const_iterator>
//  MyHashedMultiMap<KEY, VALUE, HASH, EQUAL, ALLOCATOR>::equal_range(
//                                                        const KEY& key) const
//  {
//      typedef MyPair<const_iterator, const_iterator> ResultType;
//      typedef BloombergLP::bslalg::BidirectionalLink HashTableLink;
//
//      HashTableLink *first;
//      HashTableLink *last;
//      d_impl.findRange(&first, &last, key);
//      return ResultType(const_iterator(first), const_iterator(last));
//  }
//..
// Finally, we create 'mhmm', an instance of 'MyHashedMultiMap', exercise it,
// and confirm that it behaves as expected.
//
// We define several aliases to make our code more concise.
//..
//  typedef MyHashedMultiMap<int, double>::iterator       Iterator;
//  typedef MyHashedMultiMap<int, double>::const_iterator ConstIterator;
//  typedef MyPair<ConstIterator, ConstIterator>          ConstRange;
//..
// Searching for an element (key value 10) in a newly created, empty container
// correctly shows the absence of any such element.
//..
//  MyHashedMultiMap<int, double> mhmm;
//
//  ConstRange range;
//  range = mhmm.equal_range(10);
//  assert(range.first == range.second);
//..
// We can insert a value (the pair 10, 100.00) into the container...
//..
//  MyPair<const int, double> value(10, 100.00);
//
//  Iterator itr;
//
//  itr = mhmm.insert(value);
//  assert(value == *itr);
//..
// ... and we can do so again.
//..
//  itr = mhmm.insert(value);
//  assert(value == *itr);
//..
// We can now find elements with the key value of 10.
//..
//  range = mhmm.equal_range(10);
//  assert(range.first != range.second);
//..
// As expected, there are two such elements, and both are identical in key
// value (10) and mapped value (100.00).
//..
//  int count = 0;
//  for (ConstIterator cur  = range.first,
//                     end  = range.second;
//                     end != cur; ++cur, ++count) {
//      assert(value == *cur);
//  }
//  assert(2 == count);
//..
//  }
//
///Example 4: Implementing a Custom Container
///------------------------------------------
// Although the 'bslstl::HashTable' class was created to be a common
// implementation for the standard unordered classes, this class can also be
// used in its own right to address other user problems.
//
// Suppose that we wish to retain a record of sales orders, that each record is
// characterized by several integer attributes, and that we must be able to
// find records based on *any* of those attributes.  We can use
// 'bslstl::HashTable' to implement a custom container supporting multiple
// key-values.
//
// First, we define 'MySalesRecord', our record class:
//..
//  enum { MAX_DESCRIPTION_SIZE = 16 };
//
//  typedef struct MySalesRecord {
//      int  orderNumber;                        // unique
//      int  customerId;                         // no constraint
//      int  vendorId;                           // no constraint
//      char description[MAX_DESCRIPTION_SIZE];  // ASCII string
//  } MySalesRecord;
//..
// Notice that only each 'orderNumber' is unique.  We expect multiple sales to
// any given customer ('customerId') and multiple sales by any given vendor
// ('vendorId').
//
// We will use a 'bslstl::HashTable' object (a hashtable) to save record values
// based on the unique 'orderNumber', and two auxiliary hashtables to provide
// map 'customerId' and 'vendorId' values to the addresses of the records in
// the first 'bslstl::HashTable' object.  Note that this implementation relies
// on the fact that nodes in our hashtables remain stable until they are
// removed and that in this application we do *not* allow the removal (or
// modification) of records once they are inserted.
//
// To configure these hashtables, we will need several policy objects to
// extract relevant portions the 'MySalesRecord' objects for hashing.
//
// Next, define 'UseOrderNumberAsKey', a policy class for the hashtable holding
// the sales record objects.  Note that the 'ValueType' is 'MySalesRecord' and
// that the 'extractKey' method selects the 'orderNumber' attribute:
//..
//                          // ==========================
//                          // struct UseOrderNumberAsKey
//                          // ==========================
//
//  struct UseOrderNumberAsKey {
//      // This 'struct' provides a namespace for types and methods that define
//      // the policy by which the key value of a hashed container (i.e., the
//      // value passed to the hasher) is extracted from the objects stored in
//      // the hashed container (the 'value' type).
//
//      typedef MySalesRecord ValueType;
//          // Alias for 'MySalesRecord', the type stored in the first
//          // hashtable.
//
//      typedef int KeyType;
//          // Alias for the type passed to the hasher by the hashed container.
//          // In this policy, the value passed to the hasher is the
//          // 'orderNumber' attribute, an 'int' type.
//
//      static const KeyType& extractKey(const ValueType& value);
//          // Return the key value for the specified 'value'.  In this policy,
//          // that is the 'orderNumber' attribute of 'value'.
//  };
//
//                          // --------------------------
//                          // struct UseOrderNumberAsKey
//                          // --------------------------
//
//  inline
//  const UseOrderNumberAsKey::KeyType&
//        UseOrderNumberAsKey::extractKey(const ValueType& value)
//  {
//      return value.orderNumber;
//  }
//..
// Then, we define 'UseCustomerIdAsKey', the policy class for the hashtable
// that will multiply map 'customerId' to the addresses of records in the first
// hashtable.  Note that in this policy class the 'ValueType' is
// 'const MySalesRecord *'.
//..
//                          // =========================
//                          // struct UseCustomerIdAsKey
//                          // =========================
//
//  struct UseCustomerIdAsKey {
//      // This 'struct' provides a namespace for types and methods that define
//      // the policy by which the key value of a hashed container (i.e., the
//      // value passed to the hasher) is extracted from the objects stored in
//      // the hashed container (the 'value' type).
//
//      typedef const MySalesRecord *ValueType;
//          // Alias for 'const MySalesRecord *', the type stored in second
//          // hashtable, a pointer to the record stored in the first
//          // hashtable.
//
//      typedef int KeyType;
//          // Alias for the type passed to the hasher by the hashed container.
//          // In this policy, the value passed to the hasher is the
//          // 'orderNumber' attribute, an 'int' type.
//
//      static const KeyType& extractKey(const ValueType& value);
//          // Return the key value for the specified 'value'.  In this policy,
//          // that is the 'customerId' attribute of 'value'.
//  };
//
//                          // -------------------------
//                          // struct UseCustomerIdAsKey
//                          // -------------------------
//
//  inline
//  const UseCustomerIdAsKey::KeyType&
//        UseCustomerIdAsKey::extractKey(const ValueType& value)
//  {
//      return value->customerId;
//  }
//..
// Notice that, since the values in the second hashtable are addresses, the
// key-value is extracted by reference.  This second hashtable allows what
// map-like semantics, *without* having to store key-values; those reside in
// the records in the first hashtable.
//
// The 'UseVendorIdAsKey' class, the policy class for the hashtable providing
// an index by 'vendorId', is almost a near clone of 'UseCustomerIdAsKey'.  It
// is shown for completeness:
//..
//                          // =======================
//                          // struct UseVendorIdAsKey
//                          // ========================
//
//  struct UseVendorIdAsKey {
//      // This 'struct' provides a namespace for types and methods that define
//      // the policy by which the key value of a hashed container (i.e., the
//      // value passed to the hasher) is extracted from the objects stored in
//      // the hashed container (the 'value' type).
//
//      typedef const MySalesRecord *ValueType;
//          // Alias for 'const MySalesRecord *', the type stored in second
//          // hashtable, a pointer to the record stored in the first
//          // hashtable.
//
//      typedef int KeyType;
//          // Alias for the type passed to the hasher by the hashed container.
//          // In this policy, the value passed to the hasher is the
//          // 'vendorId' attribute, an 'int' type.
//
//      static const KeyType& extractKey(const ValueType& value);
//          // Return the key value for the specified 'value'.  In this policy,
//          // that is the 'vendorId' attribute of 'value'.
//  };
//
//                          // -----------------------
//                          // struct UseVendorIdAsKey
//                          // -----------------------
//
//  inline
//  const UseVendorIdAsKey::KeyType&
//        UseVendorIdAsKey::extractKey(const ValueType& value)
//  {
//      return value->vendorId;
//  }
//..
// Next, we define 'MySalesRecordContainer', our customized container:
//..
//                          // ----------------------------
//                          // class MySalesRecordContainer
//                          // ----------------------------
//
//  class MySalesRecordContainer
//  {
//    private:
//      // PRIVATE TYPES
//      typedef BloombergLP::bslstl::HashTable<
//                    UseOrderNumberAsKey,
//                    bsl::hash<    UseOrderNumberAsKey::KeyType>,
//                    bsl::equal_to<UseOrderNumberAsKey::KeyType> >
//                                                        RecordsByOrderNumber;
//      typedef bsl::allocator_traits<
//            bsl::allocator<UseOrderNumberAsKey::ValueType> > AllocatorTraits;
//      typedef AllocatorTraits::difference_type               difference_type;
//..
// The 'ItrByOrderNumber' type is used to provide access to the elements of the
// first hash table, the one that stores the records.
//..
//
//      typedef BloombergLP::bslstl::HashTableIterator<const MySalesRecord,
//                                                     difference_type>
//                                                            ItrByOrderNumber;
//..
// The 'ItrPtrById' type is used to provide access to the elements of the other
// hashtables, the ones that store pointers into the first hashtable.
//..
//      typedef BloombergLP::bslstl::HashTableIterator<const MySalesRecord *,
//                                                     difference_type>
//                                                                  ItrPtrById;
//..
// If we were to provide iterators of type 'ItrPtrById' to our users,
// dereferencing the iterator would provide a 'MySalesRecord' pointer, which
// would then have to be dereferences.  Instead, we use 'ItrPtrById' to define
// 'ItrById' in which accessors have been overridden to provide that extra
// dereference implicitly.
//..
//      class ItrById : public ItrPtrById
//      {
//        public:
//          // CREATORS
//          explicit ItrById(bslalg::BidirectionalLink *node)
//          : ItrPtrById(node)
//          {
//          }
//
//          // ACCESSORS
//          const MySalesRecord& operator*() const
//          {
//              return *ItrPtrById::operator*();
//          }
//
//          const MySalesRecord *operator->() const
//          {
//              return &(*ItrPtrById::operator*());
//          }
//      };
//
//      typedef BloombergLP::bslstl::HashTable<
//                    UseCustomerIdAsKey,
//                    bsl::hash<    UseCustomerIdAsKey::KeyType>,
//                    bsl::equal_to<UseCustomerIdAsKey::KeyType> >
//                                                     RecordsPtrsByCustomerId;
//      typedef BloombergLP::bslstl::HashTable<
//                    UseVendorIdAsKey,
//                    bsl::hash<    UseVendorIdAsKey::KeyType>,
//                    bsl::equal_to<UseVendorIdAsKey::KeyType> >
//                                                       RecordsPtrsByVendorId;
//      // DATA
//      RecordsByOrderNumber    d_recordsByOrderNumber;
//      RecordsPtrsByCustomerId d_recordptrsByCustomerId;
//      RecordsPtrsByVendorId   d_recordptrsByVendorId;
//
//    public:
//      // PUBLIC TYPES
//      typedef ItrByOrderNumber  ConstItrByOrderNumber;
//      typedef ItrById           ConstItrById;
//
//      // CREATORS
//      explicit MySalesRecordContainer(bslma::Allocator *basicAllocator = 0);
//          // Create an empty 'MySalesRecordContainer' object.  If
//          // 'basicAllocator' is 0, the currently installed default allocator
//          // is used.
//
//      //! ~MySalesRecordContainer() = default;
//          // Destroy this object.
//
//      // MANIPULATORS
//      MyPair<ConstItrByOrderNumber, bool> insert(const MySalesRecord& value);
//          // Insert the specified 'value' into this set if the 'value' does
//          // not already exist in this set; otherwise, this method has no
//          // effect.  Return a pair whose 'first' member is an iterator
//          // providing non-modifiable access to the (possibly newly inserted)
//          // 'MySalesRecord' object having 'value' and whose 'second' member
//          // is 'true' if a new element was inserted, and 'false' if 'value'
//          // was already present.
//
//      // ACCESSORS
//      ConstItrByOrderNumber cend() const;
//          // Return an iterator providing non-modifiable access to the
//          // past-the-end element (in the sequence of 'MySalesRecord'
//          // objects) maintained by this set.
//
//      ConstItrByOrderNumber findByOrderNumber(int value) const;
//          // Return an iterator providing non-modifiable access to the
//          // 'MySalesRecord' object in this set having the specified 'value',
//          // if such an entry exists, and the iterator returned by the 'cend'
//          // method otherwise.
//..
// Notice that this interface provides map-like semantics for finding records.
// We need only specify the 'orderNumber' attribute of the record of interest;
// however, the return value is set-like: we get access to the record, not the
// more complicated key-value/record pair that a map would have provided.
//
// Internally, the hash table need only store the records themselves.  A map
// would have had to manage key-value/record pairs, where the key-value would
// be a copy of part of the record.
//..
//      MyPair<ConstItrById, ConstItrById> findByCustomerId(int value) const;
//          // Return a pair of iterators providing non-modifiable access to
//          // the sequence of 'MySalesRecord' objects in this container having
//          // a 'customerId' attribute equal to the specified 'value' where
//          // the first iterator is positioned at the start of the sequence
//          // and the second iterator is positioned one past the end of the
//          // sequence.  If this container has no such objects, then the two
//          // iterators will be equal.
//
//      MyPair<ConstItrById, ConstItrById> findByVendorId(int value) const;
//          // Return a pair of iterators providing non-modifiable access to
//          // the sequence of 'MySalesRecord' objects in this container having
//          // a 'vendorId' attribute equal to the specified 'value' where the
//          // first iterator is positioned at the start of the sequence and
//          // the second iterator is positioned one past the end of the
//          // sequence.  If this container has no such objects, then the two
//          // iterators will be equal.
//  };
//..
// Then, we implement the methods of 'MySalesRecordContainer', our customized
// container:
//..
//                          // ----------------------------
//                          // class MySalesRecordContainer
//                          // ----------------------------
//
//  // CREATORS
//  inline
//  MySalesRecordContainer::MySalesRecordContainer(
//                                            bslma::Allocator *basicAllocator)
//  : d_recordsByOrderNumber(basicAllocator)
//  , d_recordptrsByCustomerId(basicAllocator)
//  , d_recordptrsByVendorId(basicAllocator)
//  {
//  }
//
//  // MANIPULATORS
//  inline
//  MyPair<MySalesRecordContainer::ConstItrByOrderNumber, bool>
//  MySalesRecordContainer::insert(const MySalesRecord& value)
//  {
//      // Insert into internal container that will own the record.
//
//      bool                                    isInsertedFlag = false;
//      BloombergLP::bslalg::BidirectionalLink *result         =
//              d_recordsByOrderNumber.insertIfMissing(&isInsertedFlag, value);
//
//      // Index by other record attributes
//
//      RecordsByOrderNumber::NodeType *nodePtr =
//                       static_cast<RecordsByOrderNumber::NodeType *>(result);
//
//      d_recordptrsByCustomerId.insert(&nodePtr->value());
//        d_recordptrsByVendorId.insert(&nodePtr->value());
//
//      // Return of insertion.
//
//      return MyPair<ConstItrByOrderNumber, bool>(
//                                               ConstItrByOrderNumber(result),
//                                               isInsertedFlag);
//  }
//
//  // ACCESSORS
//  inline
//  MySalesRecordContainer::ConstItrByOrderNumber
//  MySalesRecordContainer::cend() const
//  {
//      return ConstItrByOrderNumber();
//  }
//
//  inline
//  MySalesRecordContainer::ConstItrByOrderNumber
//  MySalesRecordContainer::findByOrderNumber(int value) const
//  {
//      return ConstItrByOrderNumber(d_recordsByOrderNumber.find(value));
//  }
//
//  inline
//  MyPair<MySalesRecordContainer::ConstItrById,
//         MySalesRecordContainer::ConstItrById>
//  MySalesRecordContainer::findByCustomerId(int value) const
//  {
//      typedef BloombergLP::bslalg::BidirectionalLink HashTableLink;
//
//      HashTableLink *first;
//      HashTableLink *last;
//      d_recordptrsByCustomerId.findRange(&first, &last, value);
//
//      return MyPair<ConstItrById, ConstItrById>(ConstItrById(first),
//                                                ConstItrById(last));
//  }
//
//  inline
//  MyPair<MySalesRecordContainer::ConstItrById,
//         MySalesRecordContainer::ConstItrById>
//  MySalesRecordContainer::findByVendorId(int value) const
//  {
//      typedef BloombergLP::bslalg::BidirectionalLink HashTableLink;
//
//      HashTableLink *first;
//      HashTableLink *last;
//      d_recordptrsByVendorId.findRange(&first, &last, value);
//
//      return MyPair<ConstItrById, ConstItrById>(ConstItrById(first),
//                                                ConstItrById(last));
//  }
//..
// Now, create an empty container and load it with some sample data.
//..
//      MySalesRecordContainer msrc;
//
//      const MySalesRecord DATA[] = {
//          { 1000, 100, 10, "hello" },
//          { 1001, 100, 20, "world" },
//          { 1002, 200, 10, "how" },
//          { 1003, 200, 20, "are" },
//          { 1004, 100, 10, "you" },
//          { 1005, 100, 20, "today" }
//      };
//      const int numDATA = sizeof DATA / sizeof *DATA;
//
//      printf("Insert sales records into container.\n");
//
//      for (int i = 0; i < numDATA; ++i) {
//          const int orderNumber   = DATA[i].orderNumber;
//          const int customerId    = DATA[i].customerId;
//          const int vendorId      = DATA[i].vendorId;
//          const char *description = DATA[i].description;
//
//          printf("%d: %d %d %s\n",
//                 orderNumber,
//                 customerId,
//                 vendorId,
//                 description);
//          MyPair<MySalesRecordContainer::ConstItrByOrderNumber,
//                 bool> status = msrc.insert(DATA[i]);
//          assert(msrc.cend() != status.first);
//          assert(true        == status.second);
//      }
//..
// We find on standard output:
//..
//  Insert sales records into container.
//  1000: 100 10 hello
//  1001: 100 20 world
//  1002: 200 10 how
//  1003: 200 20 are
//  1004: 100 10 you
//  1005: 100 20 today
//..
// We can search our container by order number and find the expected records.
//..
//      printf("Find sales records by order number.\n");
//      for (int i = 0; i < numDATA; ++i) {
//          const int orderNumber   = DATA[i].orderNumber;
//          const int customerId    = DATA[i].customerId;
//          const int vendorId      = DATA[i].vendorId;
//          const char *description = DATA[i].description;
//
//          printf("%d: %d %d %s\n",
//                 orderNumber,
//                 customerId,
//                 vendorId,
//                 description);
//          MySalesRecordContainer::ConstItrByOrderNumber itr =
//                                         msrc.findByOrderNumber(orderNumber);
//          assert(msrc.cend() != itr);
//          assert(orderNumber == itr->orderNumber);
//          assert(customerId  == itr->customerId);
//          assert(vendorId    == itr->vendorId);
//          assert(0 == strcmp(description, itr->description));
//      }
//..
// We find on standard output:
//..
//  Find sales records by order number.
//  1000: 100 10 hello
//  1001: 100 20 world
//  1002: 200 10 how
//  1003: 200 20 are
//  1004: 100 10 you
//  1005: 100 20 today
//..
// We can search our container by customer identifier and find the expected
// records.
//..
//      printf("Find sales records by customer identifier.\n");
//
//      for (int customerId = 100; customerId <= 200; customerId += 100) {
//          MyPair<MySalesRecordContainer::ConstItrById,
//                 MySalesRecordContainer::ConstItrById> result =
//                                           msrc.findByCustomerId(customerId);
//          int count = std::distance(result.first, result.second);
//          printf("customerId %d, count %d\n", customerId, count);
//
//          for (MySalesRecordContainer::ConstItrById itr  = result.first,
//                                                    end  = result.second;
//                                                    end != itr; ++itr) {
//              printf("\t\t%d %d %d %s\n",
//                     itr->orderNumber,
//                     itr->customerId,
//                     itr->vendorId,
//                     itr->description);
//          }
//      }
//..
// We find on standard output:
//..
//  Find sales records by customer identifier.
//  customerId 100, count 4
//              1005 100 20 today
//              1004 100 10 you
//              1001 100 20 world
//              1000 100 10 hello
//  customerId 200, count 2
//              1003 200 20 are
//              1002 200 10 how
//..
// Lastly, we can search our container by vendor identifier and find the
// expected records.
//..
//      printf("Find sales records by vendor identifier.\n");
//
//      for (int vendorId = 10; vendorId <= 20; vendorId += 10) {
//          MyPair<MySalesRecordContainer::ConstItrById,
//                 MySalesRecordContainer::ConstItrById> result =
//                                               msrc.findByVendorId(vendorId);
//          int count = std::distance(result.first, result.second);
//          printf("vendorId %d, count %d\n", vendorId, count);
//
//          for (MySalesRecordContainer::ConstItrById itr  = result.first,
//                                                    end  = result.second;
//                                                    end != itr; ++itr) {
//              printf("\t\t%d %d %d %s\n",
//                     (*itr).orderNumber,
//                     (*itr).customerId,
//                     (*itr).vendorId,
//                     (*itr).description);
//          }
//      }
//..
// We find on standard output:
//..
//  Find sales records by vendor identifier.
//  vendorId 10, count 3
//              1004 100 10 you
//              1002 200 10 how
//              1000 100 10 hello
//  vendorId 20, count 3
//              1005 100 20 today
//              1003 200 20 are
//              1001 100 20 world
//..

#include <bslscm_version.h>

#include <bslstl_bidirectionalnodepool.h>

#include <bslalg_bidirectionallink.h>
#include <bslalg_bidirectionalnode.h>
#include <bslalg_functoradapter.h>
#include <bslalg_hashtableanchor.h>
#include <bslalg_hashtablebucket.h>
#include <bslalg_hashtableimputil.h>

#include <bslma_allocatortraits.h>
#include <bslma_destructorguard.h>
#include <bslma_stdallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_addlvaluereference.h>
#include <bslmf_assert.h>
#include <bslmf_conditional.h>
#include <bslmf_enableif.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isfunction.h>
#include <bslmf_ispointer.h>
#include <bslmf_istransparentpredicate.h>
#include <bslmf_movableref.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_bslexceptionutil.h>
#include <bsls_compilerfeatures.h>
#include <bsls_objectbuffer.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <algorithm>  // for fill_n, max, swap (C++03)
#include <cstddef>    // for 'size_t'
#include <cstring>    // for 'memset'
#include <limits>     // for numeric_limits
#if defined(BSLS_LIBRARYFEATURES_HAS_CPP11_PAIR_PIECEWISE_CONSTRUCTOR)
#include <tuple>      // for forward_as_tuple (C++11)
#endif
#include <utility>    // for swap (C++17)

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsls_nativestd.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// Include version that can be compiled with C++03
// Generated on Thu Oct 21 10:11:37 2021
// Command line: sim_cpp11_features.pl bslstl_hashtable.h
# define COMPILING_BSLSTL_HASHTABLE_H
# include <bslstl_hashtable_cpp03.h>
# undef COMPILING_BSLSTL_HASHTABLE_H
#else

namespace BloombergLP {

namespace bslstl {

template <class KEY_CONFIG,
          class HASHER,
          class COMPARATOR,
          class ALLOCATOR = ::bsl::allocator<typename KEY_CONFIG::ValueType> >
class HashTable;

template <class FACTORY>
class HashTable_ArrayProctor;

template <class FACTORY>
class HashTable_NodeProctor;

template <class FUNCTOR>
class HashTable_ComparatorWrapper;

template <class FUNCTOR>
class HashTable_ComparatorWrapper<const FUNCTOR>;

template <class FUNCTOR>
class HashTable_ComparatorWrapper<FUNCTOR &>;

template <class FUNCTOR>
class HashTable_HashWrapper;

template <class FUNCTOR>
class HashTable_HashWrapper<const FUNCTOR>;

template <class FUNCTOR>
class HashTable_HashWrapper<FUNCTOR &>;

struct HashTable_ImpDetails;
struct HashTable_Util;

                       // ======================
                       // class CallableVariable
                       // ======================

template <class CALLABLE>
struct CallableVariable {
    // This metafunction returns a 'type' that is an alias for 'CALLABLE'
    // unless that is a function type, in which case it is an alias for
    // 'CALLABLE &'.  This should be used to declare variables of an arbitrary
    // callable type, typically a template type parameter, that may turn out to
    // be a function type.  Note that this metafunction is necessary as the C++
    // language does not allow variables of function type, nor may functions
    // return a function type.

    // TYPES
    typedef typename bsl::conditional<
                            bsl::is_function<CALLABLE>::value,
                            typename bsl::add_lvalue_reference<CALLABLE>::type,
                            CALLABLE>::type type;
};

                           // ===========================
                           // class HashTable_HashWrapper
                           // ===========================

template <class FUNCTOR>
class HashTable_HashWrapper {
    // This class provides a wrapper around a functor satisfying the 'Hash'
    // requirements ({'bslstl_hash'}) such that the function call operator is
    // always declared as 'const' qualified.
    //
    // TBD Provide an optimization for the case of an empty base functor, where
    //     we can safely const_cast want calling the base class operator.
    //
    // Note that we would only one class, not two, with C++11 variadic
    // templates and perfect forwarding, and we could also easily detect
    // whether or not 'FUNCTOR' provided a const-qualified 'operator()'.

  private:
    mutable FUNCTOR d_functor;

  public:
    // CREATORS
    HashTable_HashWrapper();
        // Create a 'HashTable_HashWrapper' object wrapping a 'FUNCTOR' that
        // has its default value.

    explicit HashTable_HashWrapper(const FUNCTOR& fn);
        // Create a 'HashTable_HashWrapper' object wrapping a 'FUNCTOR' that is
        // a copy of the specified 'fn'.

    // MANIPULATORS
    void swap(HashTable_HashWrapper &other);
        // Exchange the value of this object with the specified 'other' object.

    // ACCESSORS
    template <class ARG_TYPE>
    std::size_t operator()(ARG_TYPE& arg) const;
        // Call the wrapped 'functor' with the specified 'arg' and return the
        // result.  Note that 'ARG_TYPE' will typically be deduced as a 'const'
        // type.

    const FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
class HashTable_HashWrapper<const FUNCTOR> {
    // This partial specialization handles 'const' qualified functors, that may
    // not be stored as a 'mutable' member in the primary template.  The need
    // to wrap such functors diminishes greatly, as there is no need to play
    // mutable tricks to invoke the function call operator.  An alternative to
    // providing this specialization would be to skip the wrapper entirely if
    // using a 'const' qualified functor in a 'HashTable'.  Note that this type
    // has a 'const' qualified data member, so is neither assignable nor
    // swappable.

  private:
    const FUNCTOR d_functor;

  public:
    // CREATORS
    HashTable_HashWrapper();
        // Create a 'HashTable_HashWrapper' object wrapping a 'FUNCTOR' that
        // has its default value.

    explicit HashTable_HashWrapper(const FUNCTOR& fn);
        // Create a 'HashTable_HashWrapper' object wrapping a 'FUNCTOR' that is
        // a copy of the specified 'fn'.

    // ACCESSORS
    template <class ARG_TYPE>
    std::size_t operator()(ARG_TYPE& arg) const;
        // Call the wrapped 'functor' with the specified 'arg' and return the
        // result.  Note that 'ARG_TYPE' will typically be deduced as a 'const'
        // type.

    const FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
class HashTable_HashWrapper<FUNCTOR &> {
    // This partial specialization handles 'const' qualified functors, that may
    // not be stored as a 'mutable' member in the primary template.  Note that
    // the 'FUNCTOR' type itself may be 'const'-qualified, so this one partial
    // template specialization also handles 'const FUNCTOR&' references.  In
    // order to correctly parse with the reference-binding rules, we drop the
    // 'const' in front of many of the references to 'FUNCTOR' seen in the
    // primary template definition.  Note that this type has a reference data
    // member, so is not default constructible, assignable or swappable.

  private:
    FUNCTOR& d_functor;

  public:
    // CREATORS
    explicit HashTable_HashWrapper(FUNCTOR& fn);
        // Create a 'HashTable_HashWrapper' object wrapping a 'FUNCTOR' that is
        // a copy of the specified 'fn'.

    // ACCESSORS
    template <class ARG_TYPE>
    std::size_t operator()(ARG_TYPE& arg) const;
        // Call the wrapped 'functor' with the specified 'arg' and return the
        // result.  Note that 'ARG_TYPE' will typically be deduced as a 'const'
        // type.

    FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
void swap(HashTable_HashWrapper<FUNCTOR> &a,
          HashTable_HashWrapper<FUNCTOR> &b);
    // Swap the functor wrapped by the specified 'a' object with the functor
    // wrapped by the specified 'b' object.

                           // =================================
                           // class HashTable_ComparatorWrapper
                           // =================================

template <class FUNCTOR>
class HashTable_ComparatorWrapper {
    // This class provides a wrapper around a functor that can compare two
    // values and return a 'bool', so that the function call operator is always
    // declared as 'const' qualified.
    //
    // TBD Provide an optimization for the case of an empty base functor, where
    //     we can safely const_cast want calling the base class operator.

  private:
    mutable FUNCTOR d_functor;

  public:
    // CREATORS
    HashTable_ComparatorWrapper();
        // Create a 'HashTable_ComparatorWrapper' object wrapping a 'FUNCTOR'
        // that has its default value.

    explicit HashTable_ComparatorWrapper(const FUNCTOR& fn);
        // Create a 'HashTable_ComparatorWrapper' object wrapping a 'FUNCTOR'
        // that is a copy of the specified 'fn'.

    // MANIPULATORS
    void swap(HashTable_ComparatorWrapper &other);
        // Exchange the value of this object with the specified 'other' object.

    // ACCESSORS
    template <class ARG1_TYPE, class ARG2_TYPE>
    bool operator()(ARG1_TYPE& arg1, ARG2_TYPE& arg2) const;
        // Call the wrapped 'functor' with the specified 'arg1' and 'arg2' (in
        // that order) and return the result.  Note that 'ARGn_TYPE' will
        // typically be deduced as a 'const' type.

    const FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
class HashTable_ComparatorWrapper<const FUNCTOR> {
    // This partial specialization handles 'const' qualified functors, that may
    // not be stored as a 'mutable' member in the primary template.  The need
    // to wrap such functors diminishes greatly, as there is no need to play
    // mutable tricks to invoke the function call operator.  An alternative to
    // providing this specialization would be to skip the wrapper entirely if
    // using a 'const' qualified functor in a 'HashTable'.  Note that this type
    // has a 'const' qualified data member, so is neither assignable nor
    // swappable.

  private:
    const FUNCTOR d_functor;

  public:
    // CREATORS
    HashTable_ComparatorWrapper();
        // Create a 'HashTable_ComparatorWrapper' object wrapping a 'FUNCTOR'
        // that has its default value.

    explicit HashTable_ComparatorWrapper(const FUNCTOR& fn);
        // Create a 'HashTable_ComparatorWrapper' object wrapping a 'FUNCTOR'
        // that is a copy of the specified 'fn'.

    // ACCESSORS
    template <class ARG1_TYPE, class ARG2_TYPE>
    bool operator()(ARG1_TYPE& arg1, ARG2_TYPE& arg2) const;
        // Call the wrapped 'functor' with the specified 'arg1' and 'arg2' (in
        // that order) and return the result.  Note that 'ARGn_TYPE' will
        // typically be deduced as a 'const' type.


    const FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
class HashTable_ComparatorWrapper<FUNCTOR &> {
    // This partial specialization handles 'const' qualified functors, that may
    // not be stored as a 'mutable' member in the primary template.  Note that
    // the 'FUNCTOR' type itself may be 'const'-qualified, so this one partial
    // template specialization also handles 'const FUNCTOR&' references.  In
    // order to correctly parse with the reference-binding rules, we drop the
    // 'const' in front of many of the references to 'FUNCTOR' seen in the
    // primary template definition.  Note that this type has a reference data
    // member, so is not default constructible, assignable or swappable.

  private:
    FUNCTOR& d_functor;

  public:
    // CREATORS
    explicit HashTable_ComparatorWrapper(FUNCTOR& fn);
        // Create a 'HashTable_ComparatorWrapper' object wrapping a 'FUNCTOR'
        // that is a copy of the specified 'fn'.

    // ACCESSORS
    template <class ARG1_TYPE, class ARG2_TYPE>
    bool operator()(ARG1_TYPE& arg1, ARG2_TYPE& arg2) const;
        // Call the wrapped 'functor' with the specified 'arg1' and 'arg2' (in
        // that order) and return the result.  Note that 'ARGn_TYPE' will
        // typically be deduced as a 'const' type.

    FUNCTOR& functor() const;
        // Return a reference providing non-modifiable access to the hash
        // functor wrapped by this object.
};

template <class FUNCTOR>
void swap(HashTable_ComparatorWrapper<FUNCTOR> &lhs,
          HashTable_ComparatorWrapper<FUNCTOR> &rhs);
    // Swap the functor wrapped by the specified 'lhs' object with the functor
    // wrapped by the specified 'rhs' object.

                           // ===============
                           // class HashTable
                           // ===============

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
class HashTable_ImplParameters;

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
class HashTable {
    // This class template implements a value-semantic container type holding
    // an unordered sequence of (possibly duplicate) elements, that can be
    // rapidly accessed using their key, with the constraint on the container
    // that elements whose keys compare equal according to the specified
    // 'COMPARATOR' will be stored in a stable, contiguous sequence within the
    // container.  The value type and key type of the elements maintained by a
    // 'HashTable' are determined by aliases provided through the (template
    // parameter) type 'KEY_CONFIG'.  Elements in a 'HashTable' are stored in
    // "nodes" that are allocated using an allocator of the specified
    // 'ALLOCATOR' type (rebound to the node type), and elements are
    // constructed directly in the node using the allocator as described in the
    // C++11 standard under the allocator-aware container requirements in
    // ([container.requirements.general], C++11 23.2.1).  The (template
    // parameter) types 'HASHER' and 'COMPARATOR' shall be 'copy-constructible'
    // function-objects.  'HASHER' shall support a function call operator
    // compatible with the following statements:
    //..
    //  HASHER              hash;
    //  KEY_CONFIG::KeyType key;
    //  std::size_t result = hash(key);
    //..
    // where the definition of the called function meets the requirements of a
    // hash function, as specified in {'bslstl_hash'}.  'COMPARATOR' shall
    // support the a function call operator compatible with the following
    // statements:
    //..
    //  COMPARATOR          compare;
    //  KEY_CONFIG::KeyType key1, key2;
    //  bool result = compare(key1, key2);
    //..
    // where the definition of the called function defines an equivalence
    // relationship on keys that is both reflexive and transitive.  The
    // 'HASHER' and 'COMPARATOR' attributes of this class are further
    // constrained, such for any two objects whose keys compare equal by the
    // comparator, shall produce the same value from the hasher.
    //
    // This class:
    //: o supports a complete set of *value-semantic* operations
    //:   o except for 'bdex' serialization
    //: o is *exception-neutral* (agnostic except for the 'at' method)
    //: o is *alias-safe*
    //: o is 'const' *thread-safe*
    // For terminology see {'bsldoc_glossary'}.

  public:
    // TYPES
    typedef ALLOCATOR                              AllocatorType;
    typedef ::bsl::allocator_traits<AllocatorType> AllocatorTraits;
    typedef typename KEY_CONFIG::KeyType           KeyType;
    typedef typename KEY_CONFIG::ValueType         ValueType;
    typedef bslalg::BidirectionalNode<ValueType>   NodeType;
    typedef typename AllocatorTraits::size_type    SizeType;

  private:
    // PRIVATE TYPES
    typedef
    HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>
                                                                ImplParameters;

    typedef bslmf::MovableRefUtil                               MoveUtil;
        // This typedef is a convenient alias for the utility associated with
        // movable references.

    // CONSISTENCY CHECKS

    // Assert consistency checks against Machiavellian users, specializing an
    // allocator for a specific type to have different propagation traits to
    // the primary template.

    typedef typename AllocatorTraits::template rebind_traits<NodeType>
        ReboundTraits;

    BSLMF_ASSERT(
        ReboundTraits::propagate_on_container_copy_assignment::value ==
        AllocatorTraits::propagate_on_container_copy_assignment::value);

    BSLMF_ASSERT(
        ReboundTraits::propagate_on_container_move_assignment::value ==
        AllocatorTraits::propagate_on_container_move_assignment::value);

    BSLMF_ASSERT(ReboundTraits::propagate_on_container_swap::value ==
                 AllocatorTraits::propagate_on_container_swap::value);

  private:
    // DATA
    ImplParameters      d_parameters;    // policies governing table behavior
    bslalg::HashTableAnchor
                        d_anchor;        // list root and bucket array
    SizeType            d_size;          // number of elements in this table
    SizeType            d_capacity;      // max number of elements before a
                                         // rehash is required (computed from
                                         // 'd_maxLoadFactor')
    float               d_maxLoadFactor; // maximum permitted load factor

  private:
    // PRIVATE MANIPULATORS
    void copyDataStructure(bslalg::BidirectionalLink *cursor);
        // Copy the sequence of elements from the list starting at the
        // specified 'cursor' and having 'size' elements.  Allocate a bucket
        // array sufficiently large to store 'size' elements while respecting
        // the 'maxLoadFactor', and index the copied list into that new array
        // of hash buckets.  This hash table then takes ownership of the list
        // and bucket array.  Note that this method is intended to be called
        // from copy constructors, which will have assigned some initial values
        // for the 'size' and other attributes that may not be consistent with
        // the class invariants until after this method is called.

    void moveDataStructure(bslalg::BidirectionalLink *cursor);
        // Recreate the sequence of elements from the list starting at the
        // specified 'cursor' and having (member) 'd_size' elements, ensuring
        // that each 'ValueType' object in the source list is move-inserted
        // into the new sequence.  Allocate a bucket array sufficiently large
        // to store 'd_size' elements, while respecting the 'maxLoadFactor',
        // and index the new list into that new array of hash buckets.  This
        // hash table then takes ownership of the list and bucket array.  Note
        // that this method is intended to be called from move constructors
        // (where the source and target allocators do not match), which will
        // have assigned some initial values for the 'size' and other
        // attributes that may not be consistent with the class invariants
        // until after this method completes.  If an exception is thrown during
        // this operation, this object is left in a valid but unspecified
        // state; it is the caller's responsibility, however, to ensure the
        // source hash-table is in a valid state if an exception is thrown

    void quickSwapExchangeAllocators(HashTable *other);
        // Efficiently exchange the value, functors, and allocator of this
        // object with those of the specified 'other' object.  This method
        // provides the no-throw exception-safety guarantee.

    void quickSwapRetainAllocators(HashTable *other);
        // Efficiently exchange the value and functors this object with those
        // of the specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    void rehashIntoExactlyNumBuckets(SizeType newNumBuckets,
                                     SizeType capacity);
        // Re-organize this hash-table to have exactly the specified
        // 'newNumBuckets', which will then be able to store the specified
        // 'capacity' number of elements without exceeding the 'maxLoadFactor'.
        // This operation provides the strong exception guarantee (see
        // {'bsldoc_glossary'}) unless the 'hasher' throws, in which case this
        // operation provides the basic exception guarantee, leaving the
        // hash-table in a valid, but otherwise unspecified (and potentially
        // empty), state.  The behavior is undefined unless
        // 'size / newNumBuckets <= maxLoadFactor'.  Note that the caller is
        // responsible for correctly computing the 'capacity' supported by the
        // new number of buckets.  This allows for a minor optimization where
        // the value is computed only once per rehash.

    void removeAllAndDeallocate();
        // Erase all the nodes in this hash-table, and deallocate their memory
        // via the supplied node-factory.  Destroy the array of buckets owned
        // by this hash-table.  If 'd_anchor.bucketAddress()' is the default
        // bucket address ('HashTable_ImpDetails::defaultBucketAddress'), then
        // this hash-table does not own its array of buckets, and it will not
        // be destroyed.

    void removeAllImp();
        // Erase all the nodes in this table and deallocate their memory via
        // the node factory, without performing the necessary bookkeeping to
        // reflect such change.  Note that this (private) method explicitly
        // leaves the HashTable in an inconsistent state, and is expected to be
        // useful when the anchor of this hash table is about to be overwritten
        // with a new value, or when the hash table is going out of scope and
        // the extra bookkeeping is not necessary.

    // PRIVATE ACCESSORS
    template <class DEDUCED_KEY>
    bslalg::BidirectionalLink *find(DEDUCED_KEY& key,
                                    std::size_t  hashValue) const;
        // Return the address of the first node in this hash table having a key
        // that compares equal (according to this hash-table's 'comparator') to
        // the specified 'key'.  The behavior is undefined unless the specified
        // 'hashValue' is the hash code for the 'key' according to the 'hasher'
        // functor of this hash table.  Note that this function's
        // implementation relies on the supplied 'hashValue' rather than
        // recomputing it, eliminating some redundant computation for the
        // public methods.

    bslalg::HashTableBucket *getBucketAddress(SizeType bucketIndex) const;
        // Return the address of the bucket at the specified 'bucketIndex' in
        // bucket array of this hash table.  The behavior is undefined unless
        // 'bucketIndex < this->numBuckets()'.

    std::size_t hashCodeForNode(bslalg::BidirectionalLink *node) const;
        // Return the hash code for the element stored in the specified 'node'
        // using a copy of the hash functor supplied at construction.  The
        // behavior is undefined unless 'node' points to a list-node of type
        // 'bslalg::BidirectionalNode<KEY_CONFIG::ValueType>'.

  public:
    // CREATORS
    explicit HashTable(const ALLOCATOR& basicAllocator = ALLOCATOR());
        // Create an empty hash-table.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is not supplied, a
        // default-constructed object of the (template parameter) type
        // 'ALLOCATOR' is used.  If the type 'ALLOCATOR' is 'bsl::allocator'
        // and 'basicAllocator' is not supplied, the currently installed
        // default allocator is used to supply memory.  Use 1.0 for the
        // 'maxLoadFactor'.  Use a default constructed object of the (template
        // parameter) type 'HASHER' and a default constructed object of the
        // (template parameter) type 'COMPARATOR' to organize elements in the
        // table.  No memory is allocated unless the 'HASHER' or 'COMPARATOR'
        // types allocate memory in their default constructor.  Note that a
        // 'bslma::Allocator *' can be supplied for 'basicAllocator' if the
        // type 'ALLOCATOR' is 'bsl::allocator' (the default).

    HashTable(const HASHER&     hash,
              const COMPARATOR& compare,
              SizeType          initialNumBuckets,
              float             initialMaxLoadFactor,
              const ALLOCATOR&  basicAllocator = ALLOCATOR());
        // Create an empty hash-table using the specified 'hash' and 'compare'
        // functors to organize elements in the table, which will initially
        // have at least the specified 'initialNumBuckets' and a
        // 'maxLoadFactor' of the specified 'initialMaxLoadFactor'.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not supplied, a default-constructed object of
        // the (template parameter) type 'ALLOCATOR' is used.  If the type
        // 'ALLOCATOR' is 'bsl::allocator' and 'basicAllocator' is not
        // supplied, the currently installed default allocator is used to
        // supply memory.  If this constructor tries to allocate a number of
        // buckets larger than can be represented by this hash-table's
        // 'SizeType', a 'std::length_error' exception is thrown.  The behavior
        // is undefined unless '0 < initialMaxLoadFactor'.  Note that more than
        // 'initialNumBuckets' buckets may be created in order to preserve the
        // bucket allocation strategy of the hash-table (but never fewer).
        // Also note that a 'bslma::Allocator *' can be supplied for
        // 'basicAllocator' if the type 'ALLOCATOR' is 'bsl::allocator' (the
        // default).

    HashTable(const HashTable& original);
        // Create a hash-table having the same value (and 'maxLoadFactor') as
        // the specified 'original' object.  Use a copy of 'original.hasher()'
        // and a copy of 'original.comparator()' to organize elements in this
        // hash-table.  Use the allocator returned by
        // 'bsl::allocator_traits<ALLOCATOR>::
        //  select_on_container_copy_construction(original.allocator())'
        // to allocate memory.  This method requires that the 'ValueType'
        // defined by the (template parameter) type 'KEY_CONFIG' be
        // 'copy-insertable' into this hash-table (see '{Requirements on
        // 'KEY_CONFIG'}).   Note that this hash-table may have fewer buckets
        // than 'original', and a correspondingly higher 'loadFactor', so long
        // as 'maxLoadFactor' is not exceeded.  Also note that the created hash
        // table may have a different 'numBuckets' than 'original', and a
        // correspondingly different 'loadFactor', as long as 'maxLoadFactor'
        // is not exceeded.

    HashTable(BloombergLP::bslmf::MovableRef<HashTable> original);
        // Create a hash-table having the same value (and 'maxLoadFactor') as
        // the specified 'original' object by moving (in constant time) the
        // contents of 'original' to the new hash-table.  Use a copy of
        // 'original.hasher()' and a copy of 'original.comparator()' to
        // organize elements in this hash-table.  The allocator associated with
        // 'original' is propagated for use in the newly created hash-table.
        // 'original' is left in a valid but unspecified state.

    HashTable(const HashTable& original, const ALLOCATOR& basicAllocator);
        // Create a hash-table having the same value (and 'maxLoadFactor') as
        // the specified 'original' object that uses the specified
        // 'basicAllocator' to supply memory.  Use a copy of
        // 'original.hasher()' and a copy of 'original.comparator()' to
        // organize elements in this hash-table.  This method requires that the
        // 'ValueType' defined by the (template parameter) type 'KEY_CONFIG' be
        // 'move-insertable' into this hash-table.  Note that this hash-table
        // may have a different 'numBuckets' than 'original', and a
        // correspondingly different 'loadFactor', as long as 'maxLoadFactor'
        // is not exceeded.

    HashTable(BloombergLP::bslmf::MovableRef<HashTable> original,
              const ALLOCATOR&                          basicAllocator);
        // Create a hash table having the same value (and 'maxLoadFactor') as
        // the specified 'original' object that uses the specified
        // 'basicAllocator' to supply memory.  The contents of 'original' are
        // moved (in constant time) to the new hash-table if
        // 'basicAllocator == original.get_allocator()', and are move-inserted
        // (in linear time) using 'basicAllocator' otherwise.  'original' is
        // left in a valid but unspecified state.  Use a copy of
        // 'original.hasher()' and a copy of 'original.comparator()' to
        // organize elements in this hash-table.  This method requires that the
        // 'ValueType' defined by the (template parameter) type 'KEY_CONFIG' be
        // 'move-insertable' into this hash-table.  Note that this hash-table
        // may have a different 'numBuckets' than 'original', and a
        // correspondingly different 'loadFactor', as long as 'maxLoadFactor'
        // is not exceeded.  Also note that a 'bslma::Allocator *' can be
        // supplied for 'basicAllocator' if the (template parameter)
        // 'ALLOCATOR' is 'bsl::allocator' (the default).

    ~HashTable();
        // Destroy this object.

    // MANIPULATORS
    HashTable& operator=(const HashTable& rhs);
        // Assign to this object the value, hasher, comparator and
        // 'maxLoadFactor' of the specified 'rhs' object, propagate to this
        // object the allocator of 'rhs' if the 'ALLOCATOR' type has trait
        // 'propagate_on_container_copy_assignment', and return a reference
        // providing modifiable access to this object.  This method requires
        // that the 'ValueType' defined by the (template parameter) type
        // 'KEY_CONFIG' be 'copy-assignable' and 'copy-insertable' into this
        // hash-table (see {Requirements on 'KEY_CONFIG'}).  This method
        // requires that the (template parameter) types 'HASHER' and
        // 'COMPARATOR' be 'copy-constructible' and 'copy-assignable'.  Note
        // that these requirements are modeled after the unordered container
        // requirements table in the C++11 standard, which is imprecise on this
        // operation; these requirements might simplify in the future, if the
        // standard is updated.

    HashTable& operator=(BloombergLP::bslmf::MovableRef<HashTable> rhs);
        // Assign to this object the value, hasher, comparator, and
        // 'maxLoadFactor' of the specified 'rhs' object, propagate to this
        // object the allocator of 'rhs' if the 'ALLOCATOR' type has trait
        // 'propagate_on_container_move_assignment', and return a reference
        // providing modifiable access to this object.  If this hash-table and
        // 'rhs' use the same allocator (after considering the aforementioned
        // trait), all of the contents of 'rhs' are moved to this hash-table in
        // constant time; otherwise, all elements in this hash table are either
        // destroyed or move-assigned to and each additional element in 'rhs'
        // is move-inserted into this hash-table.  'rhs' is left in a valid but
        // unspecified state.  This method requires that the 'ValueType'
        // defined by the (template parameter) type 'KEY_CONFIG' be both
        // 'move-assignable' and 'move-insertable' into this hash-table (see
        // {Requirements on 'KEY_CONFIG'}).

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class... Args>
    bslalg::BidirectionalLink *emplace(Args&&... arguments);
        // Insert into this hash-table a newly-created 'ValueType' object,
        // constructed by forwarding the specified (variable number of)
        // 'arguments' to the corresponding constructor of 'ValueType', and
        // return the address of the newly inserted node.  If a key equivalent
        // to that of the newly-created object already exists in this
        // hash-table, then insert the newly-created object immediately before
        // the first such element.  Additional buckets are allocated, as
        // needed, to preserve the invariant 'loadFactor <= maxLoadFactor'.  If
        // this function tries to allocate a number of buckets larger than can
        // be represented by this hash-table's 'SizeType', a
        // 'std::length_error' exception is thrown.  This method requires that
        // the 'ValueType' defined in the (template parameter) type
        // 'KEY_CONFIG' be 'emplace-constructible' into this hash-table from
        // 'arguments' (see {Requirements on 'KEY_CONFIG'});

    template <class... Args>
    bslalg::BidirectionalLink *emplaceWithHint(
                                         bslalg::BidirectionalLink *hint,
                                         Args&&...                  arguments);
        // Insert into this hash-table a newly-created 'ValueType' object,
        // constructed by forwarding the specified (variable number of)
        // 'arguments' to the corresponding constructor of 'ValueType'
        // (immediately preceding the specified 'hint' if 'hint' is not null
        // and the key of the node pointed to by 'hint' is equivalent to that
        // of the newly-created object), and return the address of the newly
        // inserted node.  If 'hint' is null or the key of the node pointed to
        // by 'hint' is not equivalent to that of the newly created object, and
        // a key equivalent to that of the newly-created object already exists
        // in this hash-table, then insert the newly-created object immediately
        // before the first such element.  Additional buckets will be
        // allocated, as needed, to preserve the invariant
        // 'loadFactor <= maxLoadFactor'.  If this function tries to allocate a
        // number of buckets larger than can be represented by this hash
        // table's 'SizeType', a 'std::length_error' exception is thrown.  This
        // method requires that 'ValueType' defined in the (template parameter)
        // type 'KEY_CONFIG' be 'emplace-constructible' into this hash-table
        // from 'arguments' (see {Requirements on 'KEY_CONFIG'}).  The behavior
        // is undefined unless 'hint' is either null or points to a node in
        // this hash table.

    template <class... Args>
    bslalg::BidirectionalLink *emplaceIfMissing(bool     *isInsertedFlag,
                                                Args&&... arguments);
        // Insert into this hash-table a newly-created 'ValueType' object,
        // constructed by forwarding the specified (variable number of)
        // 'arguments' to the corresponding constructor of 'ValueType', if a
        // key equivalent to that of the newly-created object does not already
        // exist in this hash-table.  Return the address of the (possibly newly
        // created and inserted) element in this hash table whose key is
        // equivalent to that of an object created from 'arguments'.  Load
        // 'true' into the specified 'isInsertedFlag' if a new value was
        // inserted, and 'false' if an equivalent key was already present.  If
        // this hash-table contains more than one element with an equivalent
        // key, return the first such element (from the contiguous sequence of
        // elements having a matching key).  Additional buckets are allocated,
        // as needed, to preserve the invariant 'loadFactor <= maxLoadFactor'.
        // If this function tries to allocate a number of buckets larger than
        // can be represented by this hash-table's 'SizeType', a
        // 'std::length_error' exception is thrown.  This method requires that
        // the 'ValueType' defined in the (template parameter) type
        // 'KEY_CONFIG' be 'emplace-constructible' into this hash-table from
        // 'arguments' (see {Requirements on 'KEY_CONFIG'});
#endif

    bslalg::BidirectionalLink *insertIfMissing(const KeyType& key);
        // Insert into this hash-table a newly-created 'ValueType' object,
        // constructed by forwarding the specified 'key' and a
        // default-constructed object of the type 'ValueType::second_type', to
        // the corresponding constructor of 'ValueType', if 'key' does not
        // already exist in this hash-table.  Return the address of the
        // (possibly newly created and inserted) element in this hash-table
        // whose key is equivalent to 'key'.  If this hash-table contains more
        // than one element with the supplied 'key', return the first such
        // element (from the contiguous sequence of elements having a matching
        // key).  Additional buckets are allocated, as needed, to preserve the
        // invariant 'loadFactor <= maxLoadFactor'.  If this function tries to
        // allocate a number of buckets larger than can be represented by this
        // hash table's 'SizeType', a 'std::length_error' exception is thrown.
        // This method requires that the 'ValueType' defined in the (template
        // parameter) type 'KEY_CONFIG' be 'emplace-constructible' into this
        // hash-table from a 'pair' of arguments representing the key and
        // value, respectively (see {Requirements on 'KEY_CONFIG'});

    bslalg::BidirectionalLink *insertIfMissing(
                                              bool             *isInsertedFlag,
                                              const ValueType&  value);
        // Insert the specified 'value' into this hash-table if a key
        // equivalent to that of 'value' does not already exist in this
        // hash-table.  Return the address of the (possibly newly inserted)
        // element in this hash-table whose key is equivalent to that of
        // 'value'.  If this hash-table contains more than one element with a
        // matching key, return the first such element (from the contiguous
        // sequence of elements having a matching key).  Additional buckets are
        // allocated, as needed, to preserve the invariant
        // 'loadFactor <= maxLoadFactor'.  If this function tries to allocate a
        // number of buckets larger than can be represented by this
        // hash-table's 'SizeType', a 'std::length_error' exception is thrown.
        // This method requires that the 'ValueType' defined in the (template
        // parameter) type 'KEY_CONFIG' be 'copy-insertable' into this
        // hash-table (see {Requirements on 'KEY_CONFIG'});

    bslalg::BidirectionalLink *insertIfMissing(
                                   bool                        *isInsertedFlag,
                                   bslmf::MovableRef<ValueType> value);
        // Insert the specified 'value' into this hash-table if a key
        // equivalent to that of 'value' does not already exist in this
        // hash-table.  Return the address of the (possibly newly inserted)
        // element in this hash-table whose key is equivalent to that of
        // 'value'.  'value' is left in a valid but unspecified state.  If this
        // hash-table contains more than one element with a matching key,
        // return the first such element (from the contiguous sequence of
        // elements having a matching key).  Additional buckets are allocated,
        // as needed, to preserve the invariant 'loadFactor <= maxLoadFactor'.
        // If this function tries to allocate a number of buckets larger than
        // can be represented by this hash-table's 'SizeType', a
        // 'std::length_error' exception is thrown.  This method requires that
        // the 'ValueType' defined in the (template parameter) type
        // 'KEY_CONFIG' be 'move-insertable' into this hash-table (see
        // {Requirements on 'KEY_CONFIG'});

    template <class SOURCE_TYPE>
    bslalg::BidirectionalLink *
    insertIfMissing(
                 bool                                          *isInsertedFlag,
                 BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value);
        // Insert into this hash-table a 'ValueType' object created from the
        // specified 'value' if a key equivalent to that of such an object does
        // not already exist in this hash-table.  Return the address of the
        // (possibly newly inserted) element in this hash-table whose key is
        // equivalent to that of the object created from 'value'.  Load 'true'
        // into the specified 'isInsertedFlag' if a new value was inserted, and
        // 'false' if an equivalent key was already present.  If this
        // hash-table contains more than one element with an equivalent key,
        // return the first such element (from the contiguous sequence of
        // elements having a matching key).  Additional buckets are allocated,
        // as needed, to preserve the invariant 'loadFactor <= maxLoadFactor'.
        // If this function tries to allocate a number of buckets larger than
        // can be represented by this hash-table's 'SizeType', a
        // 'std::length_error' exception is thrown.  This method requires that
        // the 'ValueType' defined in the (template parameter) type
        // 'KEY_CONFIG' be 'move-insertable' into this hash-table (see
        // {Requirements on 'KEY_CONFIG'}) and the (template parameter) type
        // 'SOURCE_TYPE' be implicitly convertible to 'ValueType'.

    template <class SOURCE_TYPE>
    bslalg::BidirectionalLink *insert(
                         BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value);
        // Insert into this hash-table a 'ValueType' object created from the
        // specified 'value' and return the address of the newly inserted node.
        // If a key equivalent to that of the newly-created object already
        // exists in this hash-table, then insert the new object immediately
        // before the first such element.  Additional buckets are allocated, as
        // needed, to preserve the invariant 'loadFactor <= maxLoadFactor'.  If
        // this function tries to allocate a number of buckets larger than can
        // be represented by this hash-table's 'SizeType', a
        // 'std::length_error' exception is thrown.  This method requires that
        // the 'ValueType' defined in the (template parameter) type
        // 'KEY_CONFIG' be 'move-insertable' into this hash-table (see
        // {Requirements on 'KEY_CONFIG'}) and the (template parameter) type
        // 'SOURCE_TYPE' be implicitly convertible to 'ValueType'.  Note that
        // this method is deprecated is provided only to ensure backward
        // compatibility with existing clients; use the 'emplace' method
        // instead.

    template <class SOURCE_TYPE>
    bslalg::BidirectionalLink *insert(
                          BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value,
                          bslalg::BidirectionalLink                     *hint);
        // Insert into this hash-table a 'ValueType' object created from the
        // specified 'value' (immediately preceding the specified 'hint' if
        // 'hint' is not null and the key of the node pointed to by 'hint' is
        // equivalent to that of the newly-created object), and return the
        // address of the newly inserted node.  If 'hint' is null or the key of
        // the node pointed to by 'hint' is not equivalent to that of the newly
        // created object, and a key equivalent to that of the newly-created
        // object already exists in this hash-table, then insert the
        // newly-created object immediately before the first such element.
        // Additional buckets will be allocated, as needed, to preserve the
        // invariant 'loadFactor <= maxLoadFactor'.  If this function tries to
        // allocate a number of buckets larger than can be represented by this
        // hash-table's 'SizeType', a 'std::length_error' exception is thrown.
        // This method requires that 'ValueType' defined in the (template
        // parameter) type 'KEY_CONFIG' be 'move-insertable' into this
        // hash-table (see {Requirements on 'KEY_CONFIG'}) and the (template
        // parameter) type 'SOURCE_TYPE' be implicitly convertible to
        // 'ValueType'.  Note that this method is deprecated and is provided
        // only to ensure backward compatibility with existing clients; use the
        // 'emplaceWithHint' method instead.

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class KEY_ARG, class BDE_OTHER_TYPE>
    bslalg::BidirectionalLink *insertOrAssign(
                    bool                                       *isInsertedFlag,
                    bslalg::BidirectionalLink                  *hint,
                    BSLS_COMPILERFEATURES_FORWARD_REF(KEY_ARG)  key,
                    BDE_OTHER_TYPE&&                            obj);
        // If a key equivalent to the specified 'key' already exists in this
        // hash-table, assign the specified 'obj' to the value associated with
        // that key, load 'false' into the specified 'isInsertedFlag' and
        // return a pointer to the existing entry.  Otherwise, insert into this
        // hash-table a newly-created 'value_type' object, constructed from
        // 'key' and 'obj', load 'true' into 'isInsertedFlag', and return a
        // pointer to the newly-created entry.  Use the optionally specified
        // 'hint' as a starting place for the search for the existing key.
#endif

    void rehashForNumBuckets(SizeType newNumBuckets);
        // Re-organize this hash-table to have at least the specified
        // 'newNumBuckets', preserving the invariant
        // 'loadFactor <= maxLoadFactor'.  If this function tries to allocate a
        // number of buckets larger than can be represented by this hash
        // table's 'SizeType', a 'std::length_error' exception is thrown.  This
        // operation provides the strong exception guarantee (see
        // {'bsldoc_glossary'}) unless the 'hasher' throws, in which case this
        // operation provides the basic exception guarantee, leaving the
        // hash-table in a valid, but otherwise unspecified (and potentially
        // empty), state.  Note that more buckets than requested may be
        // allocated in order to preserve the bucket allocation strategy of the
        // hash table (but never fewer).

    bslalg::BidirectionalLink *remove(bslalg::BidirectionalLink *node);
        // Remove the specified 'node' from this hash-table, and return the
        // address of the node immediately after 'node' in this hash-table
        // (prior to its removal), or a null pointer value if 'node' is the
        // last node in the table.  This method invalidates only iterators and
        // references to the removed node and previously saved values of the
        // 'end()' iterator, and preserves the relative order of the nodes not
        // removed.  The behavior is undefined unless 'node' refers to a node
        // in this hash-table.

    void removeAll();
        // Remove all the elements from this hash-table.  Note that this
        // hash-table is empty after this call, but allocated memory may be
        // retained for future use.  The destructor of each (non-trivial)
        // element that is remove shall be run.

    void reserveForNumElements(SizeType numElements);
        // Re-organize this hash-table to have a sufficient number of buckets
        // to accommodate at least the specified 'numElements' without
        // exceeding the 'maxLoadFactor', and ensure that there are sufficient
        // nodes pre-allocated in this object's node pool.  If this function
        // tries to allocate a number of buckets larger than can be represented
        // by this hash table's 'SizeType', a 'std::length_error' exception is
        // thrown.  This operation provides the strong exception guarantee (see
        // {'bsldoc_glossary'}) unless the 'hasher' throws, in which case this
        // operation provides the basic exception guarantee, leaving the
        // hash-table in a valid, but otherwise unspecified (and potentially
        // empty), state.

    void setMaxLoadFactor(float newMaxLoadFactor);
        // Set the maximum load factor permitted by this hash table to the
        // specified 'newMaxLoadFactor', where load factor is the statistical
        // mean number of elements per bucket.  If 'newMaxLoadFactor <
        // loadFactor', allocate at least enough buckets to re-establish the
        // invariant 'loadFactor <= maxLoadFactor'.  If this function tries to
        // allocate a number of buckets larger than can be represented by this
        // hash table's 'SizeType', a 'std::length_error' exception is thrown.
        // The behavior is undefined unless '0 < maxLoadFactor'.

    void swap(HashTable& other);
        // Exchange the value of this object, its 'comparator' functor, its
        // 'hasher' functor, and its 'maxLoadFactor' with those of the
        // specified 'other' object.  Additionally, if
        // 'bslstl::AllocatorTraits<ALLOCATOR>::propagate_on_container_swap' is
        // 'true', then exchange the allocator of this object with that of the
        // 'other' object, and do not modify either allocator otherwise.  This
        // method provides the no-throw exception-safety guarantee unless any
        // of the 'comparator' or 'hasher' functors throw when swapped, leaving
        // both objects in a safely destructible, but otherwise unusable,
        // state.  The operation guarantees 'O[1]' complexity.  The behavior is
        // undefined unless either this object has an allocator that compares
        // equal to the allocator of 'other', or the trait
        // 'bslstl::AllocatorTraits<ALLOCATOR>::propagate_on_container_swap' is
        // 'true'.

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class KEY_ARG, class... Args>
    bslalg::BidirectionalLink *tryEmplace(
                    bool                                       *isInsertedFlag,
                    bslalg::BidirectionalLink                  *hint,
                    BSLS_COMPILERFEATURES_FORWARD_REF(KEY_ARG)  key,
                    Args&&...                                   args);
        // If a key equivalent to the specified 'key' already exists in this
        // hash-table, load 'false' into the specified 'isInsertedFlag' and
        // return a pointer to the existing entry.  Otherwise, insert into this
        // hash-table a newly-created 'value_type' object, constructed from
        // 'key' and the specified 'args', load 'true' into 'isInsertedFlag'
        // and return a pointer to the newly created entry.  Use the optionally
        // specified 'hint' as a starting place for the search for the existing
        // key.
#endif

    // ACCESSORS
    ALLOCATOR allocator() const;
        // Return a copy of the allocator used to construct this hash table.
        // Note that this is not the allocator used to allocate elements for
        // this hash table, which is instead a copy of that allocator rebound
        // to allocate the nodes used by the internal data structure of this
        // hash table.

    const bslalg::HashTableBucket& bucketAtIndex(SizeType index) const;
        // Return a reference offering non-modifiable access to the
        // 'HashTableBucket' at the specified 'index' position in the array of
        // buckets of this table.  The behavior is undefined unless 'index <
        // numBuckets()'.

    SizeType bucketIndexForKey(const KeyType& key) const;
        // Return the index of the bucket that would contain all the elements
        // having the specified 'key'.

    const COMPARATOR& comparator() const;
        // Return a reference providing non-modifiable access to the
        // key-equality comparison functor used by this hash table.

    SizeType countElementsInBucket(SizeType index) const;
        // Return the number elements contained in the bucket at the specified
        // 'index'.  Note that this operation has linear run-time complexity
        // with respect to the number of elements in the indexed bucket.

    bslalg::BidirectionalLink *elementListRoot() const;
        // Return the address of the first element in this hash table, or a
        // null pointer value if this hash table is empty.

    template <class LOOKUP_KEY>
    typename bsl::enable_if<
      BloombergLP::bslmf::IsTransparentPredicate<HASHER,    LOOKUP_KEY>::value
   && BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,LOOKUP_KEY>::value,
                  bslalg::BidirectionalLink *>::type
    find(const LOOKUP_KEY& key) const
        // Return the address of a link whose key is equivalent to the
        // specified 'key' (according to this hash-table's 'comparator'), and a
        // null pointer value if no such link exists.  If this hash-table
        // contains more than one element having the supplied 'key', return the
        // first such element (from the contiguous sequence of elements having
        // the same key).  The behavior is undefined unless 'key' is equivalent
        // to the elements of at most one equivalent-key group.
        {
            return bslalg::HashTableImpUtil::findTransparent<KEY_CONFIG>(
                                             d_anchor,
                                             key,
                                             d_parameters.comparator(),
                                             d_parameters.hashCodeForKey(key));
        }

    bslalg::BidirectionalLink *find(const KeyType& key) const;
        // Return the address of a link whose key has the same value as the
        // specified 'key' (according to this hash-table's 'comparator'), and a
        // null pointer value if no such link exists.  If this hash-table
        // contains more than one element having the supplied 'key', return the
        // first such element (from the contiguous sequence of elements having
        // the same key).

    bslalg::BidirectionalLink *findEndOfRange(
                                       bslalg::BidirectionalLink *first) const;
        // Return the address of the first node after any nodes holding a value
        // having the same key as the specified 'first' node (according to this
        // hash-table's 'comparator'), and a null pointer value if all nodes
        // following 'first' hold values with the same key as 'first'.  The
        // behavior is undefined unless 'first' is a link in this hash-table.
        // Note that this hash-table ensures all elements having the same key
        // form a contiguous sequence.

    template <class LOOKUP_KEY>
    typename bsl::enable_if<
      BloombergLP::bslmf::IsTransparentPredicate<HASHER,    LOOKUP_KEY>::value
   && BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,LOOKUP_KEY>::value,
                   void>::type
    findRange(bslalg::BidirectionalLink **first,
              bslalg::BidirectionalLink **last,
              const LOOKUP_KEY&           key) const
        // Load into the specified 'first' and 'last' pointers the respective
        // addresses of the first and last link (in the list of elements owned
        // by this hash table) where the contained elements have a key that is
        // equivalent to the specified 'key' using the 'comparator' of this
        // hash-table, and null pointer values if there are no elements
        // matching 'key'.  The behavior is undefined unless 'key' is
        // equivalent to the elements of at most one equivalent-key group.
        // Note that the output values will form a closed range, where both
        // 'first' and 'last' point to links satisfying the predicate (rather
        // than a semi-open range where 'last' would point to the element
        // following the range).  Also note that this hash-table ensures all
        // elements having the same key form a contiguous sequence.
        //
        // Note: implemented inline due to Sun CC compilation error.
        {
            BSLS_ASSERT_SAFE(first);
            BSLS_ASSERT_SAFE(last);

            *first = this->find(key);
            *last  = *first ? this->findEndOfRange(*first) : 0;
        }

    void findRange(bslalg::BidirectionalLink **first,
                   bslalg::BidirectionalLink **last,
                   const KeyType&              key) const;
        // Load into the specified 'first' and 'last' pointers the respective
        // addresses of the first and last link (in the list of elements owned
        // by this hash table) where the contained elements have a key that
        // compares equal to the specified 'key' using the 'comparator' of this
        // hash-table, and null pointer values if there are no elements
        // matching 'key'.  Note that the output values will form a closed
        // range, where both 'first' and 'last' point to links satisfying the
        // predicate (rather than a semi-open range where 'last' would point to
        // the element following the range).  Also note that this hash-table
        // ensures all elements having the same key form a contiguous sequence.

    bool hasSameValue(const HashTable& other) const;
        // Return 'true' if the specified 'other' has the same value as this
        // object, and 'false' otherwise.  Two 'HashTable' objects have the
        // same value if they have the same number of elements, and for every
        // subset of elements in this object having keys that compare equal
        // (according to that hash table's 'comparator'), a corresponding
        // subset of elements exists in the 'other' object, having the same
        // number of elements, where, for some permutation of the subset in
        // this object, every element in that subset compares equal (using
        // 'operator==') to the corresponding element in the 'other' subset.
        // The behavior is undefined unless both the 'hasher' and 'comparator'
        // of this object and the 'other' return the same value for every valid
        // input.  Note that this method requires that the 'ValueType' of the
        // parameterized 'KEY_CONFIG' be "equality-comparable" (see
        // {Requirements on 'KEY_CONFIG'}).

    const HASHER& hasher() const;
        // Return a reference providing non-modifiable access to the hash
        // functor used by this hash-table.

    float loadFactor() const;
        // Return the current load factor for this table.  The load factor is
        // the statistical mean number of elements per bucket.

    float maxLoadFactor() const;
        // Return the maximum load factor permitted by this hash table object,
        // where the load factor is the statistical mean number of elements per
        // bucket.  Note that this hash table will enforce the maximum load
        // factor by rehashing into a larger array of buckets on any any
        // insertion operation where a successful insertion would exceed the
        // maximum load factor.  The maximum load factor may actually be less
        // than the current load factor if the maximum load factor has been
        // reset, but no insert operations have yet occurred.

    SizeType maxNumBuckets() const;
        // Return a theoretical upper bound on the largest number of buckets
        // that this hash-table could possibly have.  Note that there is no
        // guarantee that the hash-table can successfully maintain that number
        // of buckets, or even close to that number of buckets without running
        // out of resources.

    SizeType maxSize() const;
        // Return a theoretical upper bound on the largest number of elements
        // that this hash-table could possibly hold.  Note that there is no
        // guarantee that the hash-table can successfully grow to the returned
        // size, or even close to that size without running out of resources.

    SizeType numBuckets() const;
        // Return the number of buckets contained in this hash table.

    SizeType rehashThreshold() const;
        // Return the number of elements this hash table can hold without
        // requiring a rehash operation in order to respect the
        // 'maxLoadFactor'.

    SizeType size() const;
        // Return the number of elements in this hash table.
};

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void swap(HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& x,
          HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& y);
    // Swap both the value, the hasher, the comparator, and the 'maxLoadFactor'
    // of the specified 'x' object with the value, the hasher, the comparator,
    // and the 'maxLoadFactor' of the specified 'y' object.  Additionally, if
    // 'bslstl::AllocatorTraits<ALLOCATOR>::propagate_on_container_swap' is
    // 'true', then exchange the allocator of 'x' with that of 'y', and do not
    // modify either allocator otherwise.  This method guarantees 'O[1]'
    // complexity if 'x' and 'y' have the same allocator or if the allocators
    // propagate on swap, otherwise this operation will typically pay the cost
    // of two copy constructors, which may in turn throw.  If the allocators
    // are the same or propagate, then this method provides the no-throw
    // exception-safety guarantee unless the 'swap' function of the hasher or
    // comparator throw.  Otherwise this method offers only the basic exception
    // safety guarantee.

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bool operator==(
              const HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& lhs,
              const HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'HashTable' objects have the same
    // value if they have the same number of elements, and for every subset of
    // elements in 'lhs' having keys that compare equal (according to that hash
    // table's 'comparator'), a corresponding subset of elements exists in
    // 'rhs', having the same number of elements, where, for some permutation
    // of the 'lhs' subset, every element in that subset compares equal (using
    // 'operator==') to the corresponding element in the 'rhs' subset.  The
    // behavior is undefined unless both the 'hasher' and 'comparator' of 'lhs'
    // and 'rhs' return the same value for every valid input.  Note that this
    // method requires that the 'ValueType' of the parameterized 'KEY_CONFIG'
    // be "equality-comparable" (see {Requirements on 'KEY_CONFIG'}).

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bool operator!=(
              const HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& lhs,
              const HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'HashTable' objects do not have
    // the same value if they do not have the same number of elements, or if,
    // for any key found in 'lhs', the subset of elements having that key
    // (according to the hash-table's 'comparator') in 'lhs' either (1) does
    // not have the same number of elements as the subset of elements having
    // that key in 'rhs', or (2) there exists no permutation of the 'lhs'
    // subset where each element compares equal (using 'operator==') to the
    // corresponding element in the 'rhs' subset.  The behavior is undefined
    // unless both the 'hasher' and 'comparator' of 'lhs' and 'rhs' return the
    // same value for every valid input.  Note that this method requires that
    // the 'ValueType' of the parameterized 'KEY_CONFIG' be
    // "equality-comparable" (see {Requirements on 'KEY_CONFIG'}).

                    // ============================
                    // class HashTable_ArrayProctor
                    // ============================

template <class FACTORY>
class HashTable_ArrayProctor {
    // This class probably already exists in 'bslalg'

  private:
    // DATA
    FACTORY                 *d_factory_p;
    bslalg::HashTableAnchor *d_anchor_p;

  private:
    // NOT IMPLEMENTED
    HashTable_ArrayProctor(const HashTable_ArrayProctor&);
    HashTable_ArrayProctor& operator=(const HashTable_ArrayProctor&);

  public:
    // CREATORS
    HashTable_ArrayProctor(FACTORY                 *factory,
                           bslalg::HashTableAnchor *anchor);
        // Create a 'HashTable_ArrayProctor' managing the hash-table data
        // structure owned by the specified 'anchor' that was created using the
        // specified 'factory'.

    ~HashTable_ArrayProctor();
        // Destroy the hash-table data structure managed by this proctor and
        // reclaim all of its resources, unless there was a call to 'release'
        // this proctor.

    // MANIPULATORS
    void release();
        // Release from management the object currently managed by this
        // proctor.  If no object is currently being managed, this method has
        // no effect.
};

                    // ===========================
                    // class HashTable_NodeProctor
                    // ===========================

template <class FACTORY>
class HashTable_NodeProctor {
    // This class implements a proctor that, unless its 'release' method has
    // previously been invoked, automatically deallocates a managed list of
    // nodes upon destruction by recursively invoking the 'deleteNode' method
    // of a supplied factory on each node.  The (template parameter) type
    // 'FACTORY' shall be provide a member function that can be called as if it
    // had the following signature:
    //..
    //  void deleteNode(bslalg::BidirectionalLink *node);
    //..

  private:
    // DATA
    FACTORY                   *d_factory_p;
    bslalg::BidirectionalLink *d_node_p;

  private:
    // NOT IMPLEMENTED
    HashTable_NodeProctor(const HashTable_NodeProctor&);
    HashTable_NodeProctor& operator=(const HashTable_NodeProctor&);

  public:
    // CREATORS
    HashTable_NodeProctor(FACTORY                   *factory,
                          bslalg::BidirectionalLink *node);
        // Create a new node-proctor that conditionally manages the specified
        // 'node' (if non-zero), and that uses the specified 'factory' to
        // destroy the node (unless released) upon its destruction.  The
        // behavior is undefined unless 'node' was created by the 'factory'.

    ~HashTable_NodeProctor();
        // Destroy this node proctor, and delete the node that it manages (if
        // any) by invoking the 'deleteNode' method of the factory supplied at
        // construction.  If no node is currently being managed, this method
        // has no effect.

    // MANIPULATORS
    void release();
        // Release from management the node currently managed by this proctor.
        // If no object is currently being managed, this method has no effect.
};

                    // ==========================
                    // class HashTable_ImpDetails
                    // ==========================

struct HashTable_ImpDetails {
    // This utility 'struct' provides a namespace for functions that are useful
    // when implementing a hash table.

    // CLASS METHODS
    static bslalg::HashTableBucket *defaultBucketAddress();
        // Return the address of a statically initialized empty bucket that can
        // be shared as the (un-owned) bucket array by all empty hash tables.

    static size_t growBucketsForLoadFactor(size_t *capacity,
                                           size_t  minElements,
                                           size_t  requestedBuckets,
                                           double  maxLoadFactor);
        // Return the suggested number of buckets to index a linked list that
        // can hold as many as the specified 'minElements' without exceeding
        // the specified 'maxLoadFactor', and supporting at least the specified
        // number of 'requestedBuckets'.  Set the specified '*capacity' to the
        // maximum length of linked list that the returned number of buckets
        // could index without exceeding the 'maxLoadFactor'.  The behavior is
        // undefined unless '0 < maxLoadFactor', '0 < minElements' and
        // '0 < requestedBuckets'.

    static bslma::Allocator *incidentalAllocator();
        // Return that address of an allocator that can be used to allocate
        // temporary storage, but that is neither the default nor global
        // allocator.  Note that this function is intended to support detailed
        // checks in 'SAFE_2' builds, that may need additional storage for the
        // evaluation of a validity check on a large data structure, but that
        // should not change the expected values computed for regular allocator
        // usage of the component as validated by the test driver.

    static size_t nextPrime(size_t n);
        // Return the next prime number greater-than or equal to the specified
        // 'n' in the increasing sequence of primes chosen to disperse hash
        // codes across buckets as uniformly as possible.  Throw a
        // 'std::length_error' exception if 'n' is greater than the last prime
        // number in the sequence.  Note that, typically, prime numbers in the
        // sequence have increasing values that reflect a growth factor (e.g.,
        // each value in the sequence may be, approximately, two times the
        // preceding value).
};

                    // ====================
                    // class HashTable_Util
                    // ====================

struct HashTable_Util {
    // This utility 'struct' provide utilities for initializing and destroying
    // bucket lists in anchors that are managed by a 'HashTable'.  They cannot
    // migrate down to 'bslalg::HashTableImpUtil' as they rely on the standard
    // library 'bslma_allocatortraits' for their implementation.

    // CLASS METHODS
    template <class TYPE>
    static void assertNotNullPointer(TYPE&);
    template <class TYPE>
    static void assertNotNullPointer(TYPE * const& ptr);
    template <class TYPE>
    static void assertNotNullPointer(TYPE * & ptr);
        // Assert that the passed argument (the specified 'ptr') is not a null
        // pointer value.  Note that this utility is necessary as the
        // 'HashTable' class template may be instantiated with function
        // pointers for the hasher or comparator policies, but there is no easy
        // way to assert in general that the value of a generic type passed to
        // a function is not a null pointer value.

    template<class ALLOCATOR>
    static void destroyBucketArray(bslalg::HashTableBucket *data,
                                   std::size_t              bucketArraySize,
                                   const ALLOCATOR&         allocator);
        // Destroy the specified 'data' array of the specified length
        // 'bucketArraySize', that was allocated by the specified 'allocator'.

    template<class ALLOCATOR>
    static void initAnchor(bslalg::HashTableAnchor *anchor,
                           std::size_t              bucketArraySize,
                           const ALLOCATOR&         allocator);
        // Load into the specified 'anchor' a (contiguous) array of buckets of
        // the specified 'bucketArraySize' using memory supplied by the
        // specified 'allocator'.  The behavior is undefined unless
        // '0 < bucketArraySize' and '0 == anchor->bucketArraySize()'.  Note
        // that this operation has no effect on 'anchor->listRootAddress()'.
};

                   // ==============================
                   // class HashTable_ImplParameters
                   // ==============================

    // It looks like the 'CallableVariable' adaptation would be more
    // appropriately addressed as part of the 'bslalg::FunctorAdapter' wrapper
    // than intrusively in this component, and in similar ways by any other
    // container trying to support the full range of standard conforming
    // functors.  Given that our intent is to support standard predicates, it
    // may be appropriate to handle calling non-'const' 'operator()' overloads
    // (via a 'mutable' member) too.

template <class HASHER>
struct HashTable_BaseHasher
     : bslalg::FunctorAdapter<HashTable_HashWrapper<
                                     typename CallableVariable<HASHER>::type> >
{
};

template <class COMPARATOR>
struct HashTable_Comparator
     : bslalg::FunctorAdapter<HashTable_ComparatorWrapper<
                                 typename CallableVariable<COMPARATOR>::type> >
{
};

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
class HashTable_ImplParameters
    : private HashTable_BaseHasher<HASHER>::Type
    , private HashTable_Comparator<COMPARATOR>::Type
{
    // This class holds all the parameterized parts of a 'HashTable' class,
    // efficiently exploiting the empty base optimization without adding
    // unforeseen namespace associations to the 'HashTable' class itself due to
    // the structural inheritance.

    // PRIVATE TYPES
    typedef typename HashTable_BaseHasher<HASHER>::Type        BaseHasher;
    typedef typename HashTable_Comparator<COMPARATOR>::Type    BaseComparator;

    typedef bslmf::MovableRefUtil                              MoveUtil;
        // This typedef is a convenient alias for the utility associated with
        // movable references.

    // typedefs stolen from HashTable
    typedef ALLOCATOR                                          AllocatorType;
    typedef ::bsl::allocator_traits<AllocatorType>             AllocatorTraits;
    typedef typename KEY_CONFIG::ValueType                     ValueType;
    typedef bslalg::BidirectionalNode<ValueType>               NodeType;

  public:
    // PUBLIC TYPES
    typedef HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR> HashTableType;
    typedef typename HashTableType::AllocatorTraits::
                              template rebind_traits<NodeType> ReboundTraits;
    typedef typename ReboundTraits::allocator_type             NodeAllocator;

    typedef
    BidirectionalNodePool<typename HashTableType::ValueType, NodeAllocator>
                                                               NodeFactory;

  private:
    // DATA
    NodeFactory  d_nodeFactory;     // nested 'struct's have public data by
                                    // convention, but should always be
                                    // accessed through the public methods.

  private:
    // NOT IMPLEMENTED
    HashTable_ImplParameters(const HashTable_ImplParameters&); // = delete;
    HashTable_ImplParameters& operator=(const HashTable_ImplParameters&);
        // = delete;

  public:
    // CREATORS
    explicit HashTable_ImplParameters(const ALLOCATOR& allocator);
        // Create a 'HashTable_ImplParameters' object having default
        // constructed 'HASHER' and 'COMPARATOR' functors, and using the
        // specified 'allocator' to provide a 'BidirectionalNodePool'.

    HashTable_ImplParameters(const HASHER&     hash,
                             const COMPARATOR& compare,
                             const ALLOCATOR&  allocator);
        // Create a 'HashTable_ImplParameters' object having the specified
        // 'hash' and 'compare' functors, and using the specified 'allocator'
        // to provide a 'BidirectionalNodePool'.

    HashTable_ImplParameters(const HashTable_ImplParameters& original,
                             const ALLOCATOR&                allocator);
        // Create a 'HashTable_ImplParameters' object having the same 'hasher'
        // and 'comparator' attributes as the specified 'original', and
        // providing a 'BidirectionalNodePool' using the specified 'allocator'.

    HashTable_ImplParameters(
                         bslmf::MovableRef<HashTable_ImplParameters> original);
        // Create a 'HashTable_ImplParameters' object with a copy of the
        // 'hasher' and 'comparator' attributes associated with the specified
        // 'original' parameters object, and adopting all outstanding memory
        // allocations and the allocator associated with 'original'.  Note that
        // 'original' is left in a valid but unspecified state.

    // MANIPULATORS
    NodeFactory& nodeFactory();
        // Return a reference offering modifiable access to the 'nodeFactory'
        // owned by this object.

    void quickSwapExchangeAllocators(HashTable_ImplParameters *other);
        // Efficiently exchange the value, functor, and allocator of this
        // object with those of the specified 'other' object.  This method
        // provides the no-throw exception-safety guarantee.

    void quickSwapRetainAllocators(HashTable_ImplParameters *other);
        // Efficiently exchange the value and functors this object with those
        // of the specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    const BaseComparator& comparator() const;
        // Return a reference offering non-modifiable access to the
        // 'comparator' functor owned by this object.

    template <class DEDUCED_KEY>
    std::size_t hashCodeForKey(DEDUCED_KEY& key) const;
        // Return the hash code for the specified 'key' using a copy of the
        // hash functor supplied at construction.  Note that this function is
        // provided as a common way to resolve 'const_cast' issues in the case
        // that the stored hash functor has a function call operator that is
        // not declared as 'const'.

    const BaseHasher& hasher() const;
        // Return a reference offering non-modifiable access to the 'hasher'
        // functor owned by this object.

    const NodeFactory& nodeFactory() const;
        // Return a reference offering non-modifiable access to the
        // 'nodeFactory' owned by this object.

    const COMPARATOR& originalComparator() const;
        // Return a reference offering non-modifiable access to the
        // 'comparator' functor owned by this object.

    const HASHER& originalHasher() const;
        // Return a reference offering non-modifiable access to the 'hasher'
        // functor owned by this object.
};

// ============================================================================
//                  TEMPLATE AND INLINE FUNCTION DEFINITIONS
// ============================================================================

                   // ---------------------------
                   // class HashTable_HashWrapper
                   // ---------------------------

template <class FUNCTOR>
inline
HashTable_HashWrapper<FUNCTOR>::HashTable_HashWrapper()
: d_functor()
{
}

template <class FUNCTOR>
inline
HashTable_HashWrapper<FUNCTOR>::HashTable_HashWrapper(const FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG_TYPE>
inline
std::size_t
HashTable_HashWrapper<FUNCTOR>::operator()(ARG_TYPE& arg) const
{
    return d_functor(arg);
}

template <class FUNCTOR>
inline
const FUNCTOR& HashTable_HashWrapper<FUNCTOR>::functor() const
{
    return d_functor;
}

template <class FUNCTOR>
inline
void HashTable_HashWrapper<FUNCTOR>::swap(HashTable_HashWrapper &other)
{
    using std::swap;
    swap(d_functor, other.d_functor);
}

                 // 'const FUNCTOR' partial specialization

template <class FUNCTOR>
inline
HashTable_HashWrapper<const FUNCTOR>::HashTable_HashWrapper()
: d_functor()
{
}

template <class FUNCTOR>
inline
HashTable_HashWrapper<const FUNCTOR>::HashTable_HashWrapper(const FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG_TYPE>
inline
std::size_t
HashTable_HashWrapper<const FUNCTOR>::operator()(ARG_TYPE& arg) const
{
    return d_functor(arg);
}

template <class FUNCTOR>
inline
const FUNCTOR& HashTable_HashWrapper<const FUNCTOR>::functor() const
{
    return d_functor;
}

                 // 'FUNCTOR &' partial specialization

template <class FUNCTOR>
inline
HashTable_HashWrapper<FUNCTOR &>::HashTable_HashWrapper(FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG_TYPE>
inline
std::size_t
HashTable_HashWrapper<FUNCTOR &>::operator()(ARG_TYPE& arg) const
{
    return d_functor(arg);
}

template <class FUNCTOR>
inline
FUNCTOR& HashTable_HashWrapper<FUNCTOR &>::functor() const
{
    return d_functor;
}

                   // ---------------------------------
                   // class HashTable_ComparatorWrapper
                   // ---------------------------------

template <class FUNCTOR>
inline
HashTable_ComparatorWrapper<FUNCTOR>::HashTable_ComparatorWrapper()
: d_functor()
{
}

template <class FUNCTOR>
inline
HashTable_ComparatorWrapper<FUNCTOR>::
HashTable_ComparatorWrapper(const FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG1_TYPE, class ARG2_TYPE>
inline
bool
HashTable_ComparatorWrapper<FUNCTOR>::operator()(ARG1_TYPE& arg1,
                                                 ARG2_TYPE& arg2) const
{
    return d_functor(arg1, arg2);
}

template <class FUNCTOR>
const FUNCTOR& HashTable_ComparatorWrapper<FUNCTOR>::functor() const
{
    return d_functor;
}

template <class FUNCTOR>
inline
void
HashTable_ComparatorWrapper<FUNCTOR>::swap(HashTable_ComparatorWrapper &other)
{
    using std::swap;
    swap(d_functor, other.d_functor);
}

                 // 'const FUNCTOR' partial specialization


template <class FUNCTOR>
inline
HashTable_ComparatorWrapper<const FUNCTOR>::HashTable_ComparatorWrapper()
: d_functor()
{
}

template <class FUNCTOR>
inline
HashTable_ComparatorWrapper<const FUNCTOR>::
HashTable_ComparatorWrapper(const FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG1_TYPE, class ARG2_TYPE>
inline
bool
HashTable_ComparatorWrapper<const FUNCTOR>::operator()(ARG1_TYPE& arg1,
                                                       ARG2_TYPE& arg2) const
{
    return d_functor(arg1, arg2);
}

template <class FUNCTOR>
const FUNCTOR& HashTable_ComparatorWrapper<const FUNCTOR>::functor() const
{
    return d_functor;
}

                 // 'FUNCTOR &' partial specialization

template <class FUNCTOR>
inline
HashTable_ComparatorWrapper<FUNCTOR &>::
HashTable_ComparatorWrapper(FUNCTOR& fn)
: d_functor(fn)
{
}

template <class FUNCTOR>
template <class ARG1_TYPE, class ARG2_TYPE>
inline
bool
HashTable_ComparatorWrapper<FUNCTOR &>::operator()(ARG1_TYPE& arg1,
                                                   ARG2_TYPE& arg2) const
{
    return d_functor(arg1, arg2);
}

template <class FUNCTOR>
inline
FUNCTOR& HashTable_ComparatorWrapper<FUNCTOR &>::functor() const
{
    return d_functor;
}

                    // ---------------------------
                    // class HashTable_NodeProctor
                    // ---------------------------

// CREATORS
template <class FACTORY>
inline
HashTable_NodeProctor<FACTORY>::HashTable_NodeProctor(
                                            FACTORY                   *factory,
                                            bslalg::BidirectionalLink *node)
: d_factory_p(factory)
, d_node_p(node)
{
    BSLS_ASSERT_SAFE(factory);
}

template <class FACTORY>
inline
HashTable_NodeProctor<FACTORY>::~HashTable_NodeProctor()
{
    if (d_node_p) {
        d_factory_p->deleteNode(d_node_p);
    }
}

// MANIPULATORS
template <class FACTORY>
inline
void HashTable_NodeProctor<FACTORY>::release()
{
    d_node_p = 0;
}

                    // ----------------------------
                    // class HashTable_ArrayProctor
                    // ----------------------------

// CREATORS
template <class FACTORY>
inline
HashTable_ArrayProctor<FACTORY>::HashTable_ArrayProctor(
                                              FACTORY                 *factory,
                                              bslalg::HashTableAnchor *anchor)
: d_factory_p(factory)
, d_anchor_p(anchor)
{
    BSLS_ASSERT_SAFE(factory);
    BSLS_ASSERT_SAFE(anchor);
}

template <class FACTORY>
inline
HashTable_ArrayProctor<FACTORY>::~HashTable_ArrayProctor()
{
    if (d_anchor_p) {
        HashTable_Util::destroyBucketArray(d_anchor_p->bucketArrayAddress(),
                                           d_anchor_p->bucketArraySize(),
                                           d_factory_p->allocator());

        bslalg::BidirectionalLink *root = d_anchor_p->listRootAddress();
        while (root) {
            bslalg::BidirectionalLink *next = root->nextLink();
            d_factory_p->deleteNode(root);
            root = next;
        }
    }
}

// MANIPULATORS
template <class FACTORY>
inline
void HashTable_ArrayProctor<FACTORY>::release()
{
    d_anchor_p = 0;
}

                    // --------------------
                    // class HashTable_Util
                    // --------------------

template <class TYPE>
inline
void HashTable_Util::assertNotNullPointer(TYPE&)
{
}

template <class TYPE>
inline
void HashTable_Util::assertNotNullPointer(TYPE * const& ptr)
{
    // silence "unused parameter" warning in release builds:
    (void) ptr;
    BSLS_ASSERT(ptr);
}

template <class TYPE>
inline
void HashTable_Util::assertNotNullPointer(TYPE * & ptr)
{
    // silence "unused parameter" warning in release builds:
    (void) ptr;
    BSLS_ASSERT(ptr);
}

template <class ALLOCATOR>
inline
void HashTable_Util::destroyBucketArray(
                                     bslalg::HashTableBucket  *data,
                                     std::size_t               bucketArraySize,
                                     const ALLOCATOR&          allocator)
{
    BSLS_ASSERT_SAFE(data);
    BSLS_ASSERT_SAFE(
                  (1  < bucketArraySize
                     && HashTable_ImpDetails::defaultBucketAddress() != data)
               || (1 == bucketArraySize
                     && HashTable_ImpDetails::defaultBucketAddress() == data));

    typedef ::bsl::allocator_traits<ALLOCATOR>               ParamAllocTraits;
    typedef typename ParamAllocTraits::template
                      rebind_traits<bslalg::HashTableBucket> BucketAllocTraits;
    typedef typename BucketAllocTraits::allocator_type       ArrayAllocator;
    typedef ::bsl::allocator_traits<ArrayAllocator>       ArrayAllocatorTraits;
    typedef typename ArrayAllocatorTraits::size_type         SizeType;

    BSLS_ASSERT_SAFE(bucketArraySize <= std::numeric_limits<SizeType>::max());

    if (HashTable_ImpDetails::defaultBucketAddress() != data) {
        ArrayAllocator reboundAllocator(allocator);
        ArrayAllocatorTraits::deallocate(
                                       reboundAllocator,
                                       data,
                                       static_cast<SizeType>(bucketArraySize));
    }
}

template <class ALLOCATOR>
inline
void HashTable_Util::initAnchor(bslalg::HashTableAnchor *anchor,
                                std::size_t              bucketArraySize,
                                const ALLOCATOR&         allocator)
{
    BSLS_ASSERT_SAFE(anchor);
    BSLS_ASSERT_SAFE(0 != bucketArraySize);

    typedef ::bsl::allocator_traits<ALLOCATOR>               ParamAllocTraits;
    typedef typename ParamAllocTraits::template
                      rebind_traits<bslalg::HashTableBucket> BucketAllocTraits;
    typedef typename BucketAllocTraits::allocator_type       ArrayAllocator;
    typedef ::bsl::allocator_traits<ArrayAllocator>       ArrayAllocatorTraits;
    typedef typename ArrayAllocatorTraits::size_type         SizeType;

    BSLS_ASSERT_SAFE(bucketArraySize <= std::numeric_limits<SizeType>::max());

    ArrayAllocator reboundAllocator(allocator);

    // This test is necessary to avoid undefined behavior in the non-standard
    // narrow contract of 'bsl::allocator', although it seems like a reasonable
    // assumption to pre-empt other allocators too.

    if (ArrayAllocatorTraits::max_size(reboundAllocator) < bucketArraySize) {
        bsls::BslExceptionUtil::throwBadAlloc();
    }

    // Conversion to exactly the correct type resolves compiler warnings.  The
    // assertions above are a loose safety check that this conversion can never
    // overflow - which would require an allocator using a 'size_type' larger
    // than 'std::size_t', with the requirement that a standard conforming
    // allocator must use a 'size_type' that is a built-in unsigned integer
    // type.

    const SizeType newArraySize = static_cast<SizeType>(bucketArraySize);

    bslalg::HashTableBucket *data = ArrayAllocatorTraits::allocate(
                                       reboundAllocator,
                                       newArraySize);

    std::fill_n(data, bucketArraySize, bslalg::HashTableBucket());

    anchor->setBucketArrayAddressAndSize(data, newArraySize);
}

                //-------------------------------
                // class HashTable_ImplParameters
                //-------------------------------

    // CREATORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable_ImplParameters(const ALLOCATOR& allocator)
: BaseHasher()
, BaseComparator()
, d_nodeFactory(allocator)
{
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable_ImplParameters(const HASHER&     hash,
                         const COMPARATOR& compare,
                         const ALLOCATOR&  allocator)
: BaseHasher(hash)
, BaseComparator(compare)
, d_nodeFactory(allocator)
{
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable_ImplParameters(const HashTable_ImplParameters& original,
                         const ALLOCATOR&                allocator)
: BaseHasher(static_cast<const BaseHasher&>(original))
, BaseComparator(static_cast<const BaseComparator&>(original))
, d_nodeFactory(allocator)
{
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable_ImplParameters(bslmf::MovableRef<HashTable_ImplParameters> original)
: BaseHasher(static_cast<const BaseHasher&>(original))
, BaseComparator(static_cast<const BaseComparator&>(original))
, d_nodeFactory(MoveUtil::move(MoveUtil::access(original).d_nodeFactory))
{
}

// MANIPULATORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable_ImplParameters<KEY_CONFIG,
                                  HASHER,
                                  COMPARATOR,
                                  ALLOCATOR>::NodeFactory &
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
nodeFactory()
{
    return d_nodeFactory;
}



template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
quickSwapExchangeAllocators(HashTable_ImplParameters *other)
{
    BSLS_ASSERT_SAFE(other);

    using std::swap;
    swap(*static_cast<BaseHasher*>(this), *static_cast<BaseHasher*>(other));

    swap(*static_cast<BaseComparator*>(this),
         *static_cast<BaseComparator*>(other));

    nodeFactory().swapExchangeAllocators(other->nodeFactory());
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
quickSwapRetainAllocators(HashTable_ImplParameters *other)
{
    BSLS_ASSERT_SAFE(other);

    using std::swap;
    swap(*static_cast<BaseHasher*>(this), *static_cast<BaseHasher*>(other));

    swap(*static_cast<BaseComparator*>(this),
         *static_cast<BaseComparator*>(other));

    nodeFactory().swapRetainAllocators(other->nodeFactory());
}

// ACCESSORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const typename HashTable_ImplParameters<KEY_CONFIG,
                                        HASHER,
                                        COMPARATOR,
                                        ALLOCATOR>::BaseComparator &
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
comparator() const
{
    return *this;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class DEDUCED_KEY>
inline
std::size_t HashTable_ImplParameters<KEY_CONFIG,
                                     HASHER,
                                     COMPARATOR,
                                     ALLOCATOR>::
hashCodeForKey(DEDUCED_KEY& key) const
{
    return static_cast<const BaseHasher &>(*this)(key);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const typename HashTable_ImplParameters<KEY_CONFIG,
                                        HASHER,
                                        COMPARATOR,
                                        ALLOCATOR>::BaseHasher &
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::hasher()
                                                                         const
{
    return *this;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const typename HashTable_ImplParameters<KEY_CONFIG,
                                        HASHER,
                                        COMPARATOR,
                                        ALLOCATOR>::NodeFactory &
HashTable_ImplParameters<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
nodeFactory() const
{
    return d_nodeFactory;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const COMPARATOR&
HashTable_ImplParameters<KEY_CONFIG,
                         HASHER,
                         COMPARATOR,
                         ALLOCATOR>::originalComparator() const
{
    return static_cast<const BaseComparator *>(this)->functor();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const HASHER& HashTable_ImplParameters<KEY_CONFIG,
                                       HASHER,
                                       COMPARATOR,
                                       ALLOCATOR>::originalHasher() const
{
    return static_cast<const BaseHasher *>(this)->functor();
}

                        //----------------
                        // class HashTable
                        //----------------

// CREATORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable(const ALLOCATOR& basicAllocator)
: d_parameters(basicAllocator)
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size()
, d_capacity()
, d_maxLoadFactor(1.0)
{
    BSLMF_ASSERT(!bsl::is_pointer<HASHER>::value &&
                 !bsl::is_pointer<COMPARATOR>::value);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable(const HASHER&     hash,
          const COMPARATOR& compare,
          SizeType          initialNumBuckets,
          float             initialMaxLoadFactor,
          const ALLOCATOR&  basicAllocator)
: d_parameters(hash, compare, basicAllocator)
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size()
, d_capacity(0)
, d_maxLoadFactor(initialMaxLoadFactor)
{
    BSLS_ASSERT_SAFE(0.0f < initialMaxLoadFactor);

    if (bsl::is_pointer<HASHER>::value) {
        HashTable_Util::assertNotNullPointer(hash);
    }
    if (bsl::is_pointer<COMPARATOR>::value) {
        HashTable_Util::assertNotNullPointer(compare);
    }

    if (0 != initialNumBuckets) {
        size_t capacity;  // This may be a different type than SizeType.
        size_t numBuckets = HashTable_ImpDetails::growBucketsForLoadFactor(
                                        &capacity,
                                        1,
                                        static_cast<size_t>(initialNumBuckets),
                                        d_maxLoadFactor);
        HashTable_Util::initAnchor(&d_anchor, numBuckets, basicAllocator);
        d_capacity = static_cast<SizeType>(capacity);
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable(const HashTable& original)
: d_parameters(
  original.d_parameters,
  AllocatorTraits::select_on_container_copy_construction(original.allocator()))
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size(original.d_size)
, d_capacity(0)
, d_maxLoadFactor(original.d_maxLoadFactor)
{
    if (0 < d_size) {
        d_parameters.nodeFactory().reserveNodes(original.d_size);
        this->copyDataStructure(original.d_anchor.listRootAddress());
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::HashTable(
                            BloombergLP::bslmf::MovableRef<HashTable> original)
: d_parameters(MoveUtil::move(MoveUtil::access(original).d_parameters))
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size()
, d_capacity()
, d_maxLoadFactor(1.0)
{
    HashTable& lvalue = original;
    using std::swap;
    swap(d_anchor,        lvalue.d_anchor);
    swap(d_size,          lvalue.d_size);
    swap(d_capacity,      lvalue.d_capacity);
    swap(d_maxLoadFactor, lvalue.d_maxLoadFactor);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable(const HashTable& original, const ALLOCATOR& basicAllocator)
: d_parameters(original.d_parameters, basicAllocator)
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size(original.d_size)
, d_capacity(0)
, d_maxLoadFactor(original.d_maxLoadFactor)
{
    if (0 < d_size) {
        d_parameters.nodeFactory().reserveNodes(original.d_size);
        this->copyDataStructure(original.d_anchor.listRootAddress());
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
HashTable(bslmf::MovableRef<HashTable> original,
          const ALLOCATOR&             basicAllocator)
: d_parameters(MoveUtil::access(original).d_parameters.originalHasher(),
               MoveUtil::access(original).d_parameters.originalComparator(),
               basicAllocator)
, d_anchor(HashTable_ImpDetails::defaultBucketAddress(), 1, 0)
, d_size()
, d_capacity()
, d_maxLoadFactor(1.0)
{
    HashTable& lvalue = original;
    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(
                                    basicAllocator == lvalue.allocator())) {
        d_parameters.nodeFactory().adopt(
                            MoveUtil::move(lvalue.d_parameters.nodeFactory()));
        using std::swap;
        swap(d_anchor,        lvalue.d_anchor);
        swap(d_size,          lvalue.d_size);
        swap(d_capacity,      lvalue.d_capacity);
        swap(d_maxLoadFactor, lvalue.d_maxLoadFactor);
    }
    else {
        d_size = lvalue.d_size;
        d_maxLoadFactor = lvalue.d_maxLoadFactor;
        if (0 < d_size) {
            // 'original' left in the default state
            bslalg::HashTableAnchor anchor(
                           HashTable_ImpDetails::defaultBucketAddress(), 1, 0);
            using std::swap;
            swap(anchor, lvalue.d_anchor);

            lvalue.d_size = 0;
            lvalue.d_capacity = 0;
            lvalue.d_maxLoadFactor = 1.0f;

            HashTable_ArrayProctor<typename ImplParameters::NodeFactory>
                          arrayProctor(&lvalue.d_parameters.nodeFactory(),
                                       &anchor);

            d_parameters.nodeFactory().reserveNodes(d_size);
            this->moveDataStructure(anchor.listRootAddress());

            // 'arrayProctor' will care of deleting the nodes
        }
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::~HashTable()
{
#if defined(BDE_BUILD_TARGET_SAFE_2)
    // ASSERT class invariant only in SAFE_2 builds.  Note that we specifically
    // use the MallocFree allocator, rather than allowing the default allocator
    // to supply memory to this state-checking function, in case the object
    // allocator *is* the default allocator, and so may be restricted during
    // testing.  This would cause the test below to fail by throwing a bad
    // allocation exception, and so result in a throwing destructor.  While the
    // MallocFree allocator might also run out of resources, that is not the
    // kind of catastrophic failure we are concerned with handling in an
    // invariant check that runs only in SAFE_2 builds from a destructor.

    BSLS_ASSERT_SAFE(bslalg::HashTableImpUtil::isWellFormed<KEY_CONFIG>(
                                 this->d_anchor,
                                 this->d_parameters.hasher(),
                                 HashTable_ImpDetails::incidentalAllocator()));
#endif

    this->removeAllAndDeallocate();
}

// PRIVATE MANIPULATORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::copyDataStructure(
                                             bslalg::BidirectionalLink *cursor)
{
    BSLS_ASSERT(0 != cursor);
    BSLS_ASSERT(0 < d_size);

    // This function will completely replace 'this->d_anchor's state.  It is
    // the caller's responsibility to ensure this will not leak resources owned
    // only by the previous state, such as the linked list.

    // Allocate an appropriate number of buckets

    size_t capacity;
    size_t numBuckets = HashTable_ImpDetails::growBucketsForLoadFactor(
                                                   &capacity,
                                                   static_cast<size_t>(d_size),
                                                   2,
                                                   d_maxLoadFactor);

    d_anchor.setListRootAddress(0);
    HashTable_Util::initAnchor(&d_anchor, numBuckets, this->allocator());

    // create a proctor for d_anchor's allocated array, and the list to follow.

    HashTable_ArrayProctor<typename ImplParameters::NodeFactory>
                          arrayProctor(&d_parameters.nodeFactory(), &d_anchor);

    d_capacity = static_cast<SizeType>(capacity);

    do {
        // Computing hash code depends on user-supplied code, and may throw.
        // Therefore, obtain the hash code from the node we are about to copy,
        // before any memory is allocated, so there is no risk of leaking an
        // object.  The hash code must be the same for both elements.

        size_t hashCode = this->hashCodeForNode(cursor);
        bslalg::BidirectionalLink *newNode =
                                 d_parameters.nodeFactory().cloneNode(*cursor);

        bslalg::HashTableImpUtil::insertAtBackOfBucket(&d_anchor,
                                                       newNode,
                                                       hashCode);
    }
    while (0 != (cursor = cursor->nextLink()));

    // release the proctor

    arrayProctor.release();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::moveDataStructure(
                                             bslalg::BidirectionalLink *cursor)
{
    BSLS_ASSERT(0 != cursor);
    BSLS_ASSERT(0 < d_size);

    // This function will completely replace 'this->d_anchor's state.  It is
    // the caller's responsibility to ensure this will not leak resources owned
    // only by the previous state, such as the linked list.

    // Allocate an appropriate number of buckets

    size_t capacity;
    size_t numBuckets = HashTable_ImpDetails::growBucketsForLoadFactor(
                                                   &capacity,
                                                   static_cast<size_t>(d_size),
                                                   2,
                                                   d_maxLoadFactor);

    d_anchor.setListRootAddress(0);
    HashTable_Util::initAnchor(&d_anchor, numBuckets, this->allocator());

    d_capacity = static_cast<SizeType>(capacity);

    // create a proctor for d_anchor's allocated array, and the list to follow.

    HashTable_ArrayProctor<typename ImplParameters::NodeFactory>
                          arrayProctor(&d_parameters.nodeFactory(), &d_anchor);

    do {
        // Computing hash code depends on user-supplied code, and may throw.
        // Therefore, obtain the hash code from the node we are about to copy,
        // before any memory is allocated, so there is no risk of leaking an
        // object.  The hash code must be the same for both elements.

        size_t hashCode = this->hashCodeForNode(cursor);
        bslalg::BidirectionalLink *newNode =
                            d_parameters.nodeFactory().moveIntoNewNode(cursor);

        bslalg::HashTableImpUtil::insertAtBackOfBucket(&d_anchor,
                                                       newNode,
                                                       hashCode);
    }
    while (0 != (cursor = cursor->nextLink()));

    // release the proctor

    arrayProctor.release();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
quickSwapExchangeAllocators(HashTable *other)
{
    BSLS_ASSERT_SAFE(other);

    d_parameters.quickSwapExchangeAllocators(&other->d_parameters);


    using std::swap;
    swap(d_anchor,        other->d_anchor);
    swap(d_size,          other->d_size);
    swap(d_capacity,      other->d_capacity);
    swap(d_maxLoadFactor, other->d_maxLoadFactor);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
quickSwapRetainAllocators(HashTable *other)
{
    BSLS_ASSERT_SAFE(other);
    BSLS_ASSERT_SAFE(this->allocator() == other->allocator());

    d_parameters.quickSwapRetainAllocators(&other->d_parameters);

    using std::swap;
    swap(d_anchor,        other->d_anchor);
    swap(d_size,          other->d_size);
    swap(d_capacity,      other->d_capacity);
    swap(d_maxLoadFactor, other->d_maxLoadFactor);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
rehashIntoExactlyNumBuckets(SizeType newNumBuckets, SizeType capacity)
{
    class Proctor {
        // An object of this proctor class guarantees that, if an exception is
        // thrown by a user-supplied hash functor, the container remains in a
        // valid, usable (but unspecified) state.  In fact, that state will be
        // empty, as there is no reliable way to re-index a bucket array if the
        // hash functor is throwing, and the array is potentially corrupted
        // following a failed ImpUtil::rehash call.

      private:
        HashTable               *d_table_p;
        bslalg::HashTableAnchor *d_originalAnchor_p;
        bslalg::HashTableAnchor *d_newAnchor_p;

#if !defined(BSLS_PLATFORM_CMP_MSVC)
        // Microsoft warns if these methods are declared private.

      private:
        // NOT IMPLEMENTED
        Proctor(const Proctor&); // = delete;
        Proctor& operator=(const Proctor&); // = delete;
#endif

      public:
        // CREATORS
        Proctor(HashTable               *table,
                bslalg::HashTableAnchor *originalAnchor,
                bslalg::HashTableAnchor *newAnchor)
        : d_table_p(table)
        , d_originalAnchor_p(originalAnchor)
        , d_newAnchor_p(newAnchor)
        {
            BSLS_ASSERT_SAFE(table);
            BSLS_ASSERT_SAFE(originalAnchor);
            BSLS_ASSERT_SAFE(newAnchor);
        }

        ~Proctor()
        {
            if (d_originalAnchor_p) {
                // Not dismissed, and the newAnchor now holds the correct
                // list-root.

                d_originalAnchor_p->setListRootAddress(
                                             d_newAnchor_p->listRootAddress());
                d_table_p->removeAll();
            }

            // Always destroy the spare anchor's bucket array at the end of
            // scope.  On a non-exceptional run, this will effectively be the
            // original bucket-array, as the anchors are swapped.

            HashTable_Util::destroyBucketArray(
                                           d_newAnchor_p->bucketArrayAddress(),
                                           d_newAnchor_p->bucketArraySize(),
                                           d_table_p->allocator());
        }

        // MANIPULATORS
        void dismiss()
        {
            d_originalAnchor_p = 0;
        }
    };

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    // Now that 'anchor' is not default constructible, we take a copy of the
    // anchor in the table.  Would it be better for 'initAnchor' to be replaced
    // with a 'createArrayOfEmptyBuckets' function, and we use the result to
    // construct the 'newAnchor'?

    bslalg::HashTableAnchor newAnchor(0, 0, 0);
    HashTable_Util::initAnchor(&newAnchor,
                               static_cast<size_t>(newNumBuckets),
                               this->allocator());

    Proctor cleanUpIfUserHashThrows(this, &d_anchor, &newAnchor);

    if (d_anchor.listRootAddress()) {
        bslalg::HashTableImpUtil::rehash<KEY_CONFIG>(
                                          &newAnchor,
                                          this->d_anchor.listRootAddress(),
                                          this->d_parameters.hasher());
    }

    cleanUpIfUserHashThrows.dismiss();

    d_anchor.swap(newAnchor);
    d_capacity = capacity;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::removeAllAndDeallocate()
{
    this->removeAllImp();
    HashTable_Util::destroyBucketArray(d_anchor.bucketArrayAddress(),
                                       d_anchor.bucketArraySize(),
                                       this->allocator());
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::removeAllImp()
{
    typedef bslalg::BidirectionalLink BidirectionalLink;

    // Doing too much book-keeping of hash table - look for a more efficient
    // dispose-as-we-walk, that simply resets table.Anchor.next = 0, and
    // assigns the buckets index all null pointers

    if (BidirectionalLink *root = d_anchor.listRootAddress()) {
        BidirectionalLink *next;
        do {
            next = root->nextLink();
            d_parameters.nodeFactory().deleteNode(
                                                static_cast<NodeType *>(root));
        }
        while(0 != (root = next));
    }
}

// PRIVATE ACCESSORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class DEDUCED_KEY>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::find(
                                                  DEDUCED_KEY& key,
                                                  std::size_t  hashValue) const
{
    return bslalg::HashTableImpUtil::find<KEY_CONFIG>(
                                                     d_anchor,
                                                     key,
                                                     d_parameters.comparator(),
                                                     hashValue);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
bslalg::HashTableBucket *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::getBucketAddress(
                                                    SizeType bucketIndex) const
{
    BSLS_ASSERT_SAFE(bucketIndex < this->numBuckets());

    return d_anchor.bucketArrayAddress() + bucketIndex;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
std::size_t
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::hashCodeForNode(
                                         bslalg::BidirectionalLink *node) const
{
    BSLS_ASSERT_SAFE(node);

    return d_parameters.hashCodeForKey(
                       bslalg::HashTableImpUtil::extractKey<KEY_CONFIG>(node));
}

// MANIPULATORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>&
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::operator=(
                                                          const HashTable& rhs)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(this != &rhs)) {

        if (AllocatorTraits::propagate_on_container_copy_assignment::value) {
            HashTable other(rhs, rhs.allocator());
            quickSwapExchangeAllocators(&other);
        }
        else {
            HashTable other(rhs, this->allocator());
            quickSwapRetainAllocators(&other);
        }
    }
    return *this;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>&
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::operator=(
                                              bslmf::MovableRef<HashTable> rhs)
{
    HashTable& lvalue = rhs;
    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(this != &lvalue)) {
        if (allocator() == lvalue.allocator()) {
            HashTable other(MoveUtil::move(lvalue));
            quickSwapRetainAllocators(&other);
        }
        else if (
              AllocatorTraits::propagate_on_container_move_assignment::value) {
            HashTable other(MoveUtil::move(lvalue));
            quickSwapExchangeAllocators(&other);
        }
        else {
            HashTable other(MoveUtil::move(lvalue), allocator());
            quickSwapRetainAllocators(&other);
        }
    }
    return *this;
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class... Args>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::emplace(
                                                           Args&&... arguments)
{
    typedef bslalg::HashTableImpUtil ImpUtil;

    // Rehash (if appropriate) first as it will reduce load factor and so
    // potentially improve the 'find' time.

    if (d_size >= d_capacity) {
        this->rehashForNumBuckets(numBuckets() * 2);
    }

    // Next we must create the node from the constructor arguments provided.

    bslalg::BidirectionalLink *newNode =
        d_parameters.nodeFactory().emplaceIntoNewNode(
                            BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);

    // This node needs wrapping in a proctor, in case either of the user-
    // supplied functors throws an exception.

    HashTable_NodeProctor<typename ImplParameters::NodeFactory>
                             nodeProctor(&d_parameters.nodeFactory(), newNode);

    // Now we can search for the node in the table, being careful to compute
    // the hash value only once.

    size_t hashCode = this->d_parameters.hashCodeForKey(
                                     ImpUtil::extractKey<KEY_CONFIG>(newNode));
    bslalg::BidirectionalLink *position = this->find(
                                      ImpUtil::extractKey<KEY_CONFIG>(newNode),
                                      hashCode);

    if (!position) {
        ImpUtil::insertAtFrontOfBucket(&d_anchor, newNode, hashCode);
    }
    else {
        ImpUtil::insertAtPosition(&d_anchor, newNode, hashCode, position);
    }
    nodeProctor.release();

    ++d_size;

    return newNode;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class... Args>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::emplaceWithHint(
                                          bslalg::BidirectionalLink *hint,
                                          Args&&...                  arguments)
{
    typedef bslalg::HashTableImpUtil ImpUtil;

    // Rehash (if appropriate) first as it will reduce load factor and so
    // potentially improve the potential 'find' time later.

    if (d_size >= d_capacity) {
        this->rehashForNumBuckets(numBuckets() * 2);
    }

    // Next we must create the node from the constructor arguments provided.

    bslalg::BidirectionalLink *newNode =
        d_parameters.nodeFactory().emplaceIntoNewNode(
                            BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);

    // There is potential for the user-supplied hasher and comparator to throw,
    // so now we need to manage our 'newNode' with a proctor.

    HashTable_NodeProctor<typename ImplParameters::NodeFactory>
                             nodeProctor(&d_parameters.nodeFactory(), newNode);

    // Insert logic, first test the hint

    size_t hashCode = this->d_parameters.hashCodeForKey(
                                     ImpUtil::extractKey<KEY_CONFIG>(newNode));
    if (!hint
     || !d_parameters.comparator()(ImpUtil::extractKey<KEY_CONFIG>(newNode),
                                   ImpUtil::extractKey<KEY_CONFIG>(hint))) {
        hint = this->find(ImpUtil::extractKey<KEY_CONFIG>(newNode), hashCode);
    }

    if (!hint) {
        ImpUtil::insertAtFrontOfBucket(&d_anchor, newNode, hashCode);
    }
    else {
        ImpUtil::insertAtPosition(&d_anchor, newNode, hashCode, hint);
    }
    nodeProctor.release();

    ++d_size;

    return newNode;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class... Args>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::emplaceIfMissing(
                                              bool             *isInsertedFlag,
                                              Args&&...         arguments)
{
    BSLS_ASSERT(isInsertedFlag);

    typedef bslalg::HashTableImpUtil ImpUtil;

    // Rehash (if appropriate) first as it will reduce load factor and so
    // potentially improve the potential 'find' time later.

    if (d_size >= d_capacity) {
        this->rehashForNumBuckets(numBuckets() * 2);
    }

    // Next we must create the node from the constructor arguments provided.

    bslalg::BidirectionalLink *newNode =
        d_parameters.nodeFactory().emplaceIntoNewNode(
                            BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);

    // There is potential for the user-supplied hasher and comparator to throw,
    // so now we need to manage our 'newNode' with a proctor.

    HashTable_NodeProctor<typename ImplParameters::NodeFactory>
                             nodeProctor(&d_parameters.nodeFactory(), newNode);

    // Insert logic, first test the hint

    size_t hashCode = this->d_parameters.hashCodeForKey(
                                     ImpUtil::extractKey<KEY_CONFIG>(newNode));
    bslalg::BidirectionalLink *position = this->find(
                                      ImpUtil::extractKey<KEY_CONFIG>(newNode),
                                      hashCode);

    *isInsertedFlag = (!position);

    if(!position) {
        if (d_size >= d_capacity) {
            this->rehashForNumBuckets(numBuckets() * 2);
        }

        ImpUtil::insertAtFrontOfBucket(&d_anchor, newNode, hashCode);
        nodeProctor.release();

        ++d_size;
        position = newNode;
    }

    return position;
}
#endif

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insertIfMissing(
                                                            const KeyType& key)
{
    size_t hashCode = this->d_parameters.hashCodeForKey(key);
    bslalg::BidirectionalLink *position = this->find(key, hashCode);
    if (!position) {
        if (d_size >= d_capacity) {
            this->rehashForNumBuckets(numBuckets() * 2);
        }

        typedef typename ValueType::second_type MappedType;

        // TBD: make 'this->allocator()' return the allocator by reference with
        // modifiable access rather than by value.

        AllocatorType alloc = this->allocator();

        bsls::ObjectBuffer<MappedType> defaultMapped;
        AllocatorTraits::construct(alloc, defaultMapped.address());
        bslma::DestructorGuard<MappedType> mappedGuard(
                                                      defaultMapped.address());

#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
        position = d_parameters.nodeFactory().emplaceIntoNewNode(
                                       key,
                                       MoveUtil::move(defaultMapped.object()));
#else
        position = d_parameters.nodeFactory().emplaceIntoNewNode(
                                                       key,
                                                       defaultMapped.object());
#endif

        bslalg::HashTableImpUtil::insertAtFrontOfBucket(&d_anchor,
                                                        position,
                                                        hashCode);
        ++d_size;
    }
    return position;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insertIfMissing(
                                              bool             *isInsertedFlag,
                                              const ValueType&  value)
{
    BSLS_ASSERT(isInsertedFlag);

    size_t hashCode = this->d_parameters.hashCodeForKey(
                                                KEY_CONFIG::extractKey(value));
    bslalg::BidirectionalLink *position = this->find(
                                                 KEY_CONFIG::extractKey(value),
                                                 hashCode);

    *isInsertedFlag = (!position);

    if(!position) {
        if (d_size >= d_capacity) {
            this->rehashForNumBuckets(numBuckets() * 2);
        }

        position = d_parameters.nodeFactory().emplaceIntoNewNode(value);
        bslalg::HashTableImpUtil::insertAtFrontOfBucket(&d_anchor,
                                                        position,
                                                        hashCode);
        ++d_size;
    }

    return position;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insertIfMissing(
                                   bool                        *isInsertedFlag,
                                   bslmf::MovableRef<ValueType> value)
{
    ValueType& lvalue = value;

    BSLS_ASSERT(isInsertedFlag);

    size_t hashCode = this->d_parameters.hashCodeForKey(
                                               KEY_CONFIG::extractKey(lvalue));
    bslalg::BidirectionalLink *position = this->find(
                                                KEY_CONFIG::extractKey(lvalue),
                                                hashCode);

    *isInsertedFlag = (!position);

    if(!position) {
        if (d_size >= d_capacity) {
            this->rehashForNumBuckets(numBuckets() * 2);
        }

        position = d_parameters.nodeFactory().emplaceIntoNewNode(
                                                       MoveUtil::move(lvalue));
        bslalg::HashTableImpUtil::insertAtFrontOfBucket(&d_anchor,
                                                        position,
                                                        hashCode);
        ++d_size;
    }

    return position;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class SOURCE_TYPE>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insertIfMissing(
                 bool                                          *isInsertedFlag,
                 BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value)
{
    BSLS_ASSERT(isInsertedFlag);

    return emplaceIfMissing(isInsertedFlag,
                            BSLS_COMPILERFEATURES_FORWARD(SOURCE_TYPE, value));

}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class SOURCE_TYPE>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insert(
                          BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value)
{
    return emplace(BSLS_COMPILERFEATURES_FORWARD(SOURCE_TYPE, value));
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class SOURCE_TYPE>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insert(
                          BSLS_COMPILERFEATURES_FORWARD_REF(SOURCE_TYPE) value,
                          bslalg::BidirectionalLink                     *hint)
{
    return emplaceWithHint(hint,
                           BSLS_COMPILERFEATURES_FORWARD(SOURCE_TYPE, value));
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class KEY_ARG, class BDE_OTHER_TYPE>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::insertOrAssign(
                    bool                                       *isInsertedFlag,
                    bslalg::BidirectionalLink                  *hint,
                    BSLS_COMPILERFEATURES_FORWARD_REF(KEY_ARG)  key,
                    BDE_OTHER_TYPE&&                            obj)
{
    typedef bslalg::HashTableImpUtil ImpUtil;

    const KEY_ARG& lvalue = key;
    size_t         hashCode = this->d_parameters.hashCodeForKey(lvalue);
    // Use the hint, if we can
    if (!hint
        || !d_parameters.comparator()(lvalue,
                                      ImpUtil::extractKey<KEY_CONFIG>(hint))) {
        hint = this->find(lvalue, hashCode);
    }

    if (hint) { // assign
        static_cast<NodeType *>(hint)->value().second =
            BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, obj);
        *isInsertedFlag = false;
        return hint;                                                  // RETURN
    }

    // insert
    if (d_size >= d_capacity) {
        this->rehashForNumBuckets(numBuckets() * 2);
    }

    // Make a new node
    hint = d_parameters.nodeFactory().emplaceIntoNewNode(
        BSLS_COMPILERFEATURES_FORWARD(KEY_ARG, key),
        BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, obj));

    // Add it to the hash table
    HashTable_NodeProctor<typename ImplParameters::NodeFactory>
                            nodeProctor(&d_parameters.nodeFactory(), hint);
    ImpUtil::insertAtFrontOfBucket(&d_anchor, hint, hashCode);
    nodeProctor.release();
    ++d_size;

    *isInsertedFlag = true;
    return hint;
}
#endif

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::rehashForNumBuckets(
                                                        SizeType newNumBuckets)
{
    if (newNumBuckets > this->numBuckets()) {
        // Compute a "good" number of buckets, e.g., pick a prime number from a
        // sorted array of exponentially increasing primes.

        size_t capacity;
        SizeType numBuckets = static_cast<SizeType>(
                              HashTable_ImpDetails::growBucketsForLoadFactor(
                                            &capacity,
                                            d_size + 1u,
                                            static_cast<size_t>(newNumBuckets),
                                            d_maxLoadFactor));

        this->rehashIntoExactlyNumBuckets(numBuckets,
                                          static_cast<SizeType>(capacity));
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::remove(
                                               bslalg::BidirectionalLink *node)
{
    BSLS_ASSERT_SAFE(node);
    BSLS_ASSERT_SAFE(node->previousLink()
                  || d_anchor.listRootAddress() == node);

    bslalg::BidirectionalLink *result = node->nextLink();

    bslalg::HashTableImpUtil::remove(&d_anchor,
                                     node,
                                     hashCodeForNode(node));
    --d_size;

    d_parameters.nodeFactory().deleteNode(static_cast<NodeType *>(node));

    return result;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::removeAll()
{
    this->removeAllImp();
    if (HashTable_ImpDetails::defaultBucketAddress() !=
        d_anchor.bucketArrayAddress()) {
        std::memset(d_anchor.bucketArrayAddress(),
                    0,
                    sizeof(bslalg::HashTableBucket) *
                    d_anchor.bucketArraySize());
    }

    d_anchor.setListRootAddress(0);
    d_size = 0;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::reserveForNumElements(
                                                          SizeType numElements)
{
    if (numElements < 1) { // Return avoids undefined behavior in node factory.
        return;                                                       // RETURN
    }

    if (numElements > d_capacity) {
        // Compute a "good" number of buckets, e.g., pick a prime number from a
        // sorted array of exponentially increasing primes.

        size_t capacity;
        SizeType numBuckets = static_cast<SizeType>(
                              HashTable_ImpDetails::growBucketsForLoadFactor(
                                       &capacity,
                                       numElements,
                                       static_cast<size_t>(this->numBuckets()),
                                       d_maxLoadFactor));

        this->rehashIntoExactlyNumBuckets(numBuckets,
                                          static_cast<SizeType>(capacity));
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::setMaxLoadFactor(
                                                        float newMaxLoadFactor)
{
    BSLS_ASSERT_SAFE(0.0f < newMaxLoadFactor);

    size_t capacity;
    SizeType numBuckets = static_cast<SizeType>(
             HashTable_ImpDetails::growBucketsForLoadFactor(
                                       &capacity,
                                       std::max<SizeType>(d_size, 1u),
                                       static_cast<size_t>(this->numBuckets()),
                                       newMaxLoadFactor));

    this->rehashIntoExactlyNumBuckets(numBuckets,
                                      static_cast<SizeType>(capacity));

    // Always set this last, as there is potential to throw exceptions above.

    d_maxLoadFactor = newMaxLoadFactor;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::swap(HashTable& other)
{
    // This trait should perform 'if' at compile-time.

    if (AllocatorTraits::propagate_on_container_swap::value) {
        quickSwapExchangeAllocators(&other);
    }
    else {
        // C++11 behavior: undefined for unequal allocators
        // BSLS_ASSERT(allocator() == other.allocator());

        BSLS_ASSERT(d_parameters.nodeFactory().allocator() ==
                    other.d_parameters.nodeFactory().allocator());
        quickSwapRetainAllocators(&other);
    }
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
template <class KEY_ARG, class... Args>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::tryEmplace(
                    bool                                       *isInsertedFlag,
                    bslalg::BidirectionalLink                  *hint,
                    BSLS_COMPILERFEATURES_FORWARD_REF(KEY_ARG)  key,
                    Args&&...                                   args)
{
    typedef bslalg::HashTableImpUtil ImpUtil;

    const KEY_ARG& lvalue = key;
    const size_t   hashCode = this->d_parameters.hashCodeForKey(lvalue);

    // Use the hint, if we can
    if (!hint
        || !d_parameters.comparator()(lvalue,
                                      ImpUtil::extractKey<KEY_CONFIG>(hint))) {
        hint = this->find(lvalue, hashCode);
    }

    // If the key exists, we're done
    if (hint) {
        *isInsertedFlag = false;
        return hint;                                                  // RETURN
    }

    if (d_size >= d_capacity) {
        this->rehashForNumBuckets(numBuckets() * 2);
    }

    // Make a new node
#if defined(BSLS_LIBRARYFEATURES_HAS_CPP11_PAIR_PIECEWISE_CONSTRUCTOR)
    hint = d_parameters.nodeFactory().emplaceIntoNewNode(
          std::piecewise_construct,
          std::forward_as_tuple(BSLS_COMPILERFEATURES_FORWARD(KEY_ARG, key)),
          std::forward_as_tuple(BSLS_COMPILERFEATURES_FORWARD(Args, args)...));
#else
    typedef typename ValueType::second_type MappedType;
    hint = d_parameters.nodeFactory().emplaceIntoNewNode(
                     BSLS_COMPILERFEATURES_FORWARD(KEY_ARG, key),
                     MappedType(BSLS_COMPILERFEATURES_FORWARD(Args, args)...));
#endif

    // Add it to the hash table
    HashTable_NodeProctor<typename ImplParameters::NodeFactory>
                            nodeProctor(&d_parameters.nodeFactory(), hint);
    ImpUtil::insertAtFrontOfBucket(&d_anchor, hint, hashCode);
    nodeProctor.release();
    ++d_size;

    *isInsertedFlag = true;
    return hint;
}
#endif

// ACCESSORS
template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
ALLOCATOR HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::
                                                              allocator() const
{
    return d_parameters.nodeFactory().allocator();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const bslalg::HashTableBucket&
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::bucketAtIndex(
                                                          SizeType index) const
{
    BSLS_ASSERT_SAFE(index < this->numBuckets());

    return d_anchor.bucketArrayAddress()[index];
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::bucketIndexForKey(
                                                      const KeyType& key) const
{
    typedef typename
       HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType SizeType;

    // The following cast will not discard any useful bits, unless 'SizeType'
    // is larger than 'size_t', as the bucket computation takes a mod on the
    // supplied number of buckets.  We use the following 'BSLMF_ASSERT' to
    // assert that assumption at compile time.

    BSLMF_ASSERT(sizeof(SizeType) <= sizeof(size_t));

    size_t hashCode = this->d_parameters.hashCodeForKey(key);
    return static_cast<SizeType>(bslalg::HashTableImpUtil::computeBucketIndex(
                                                  hashCode,
                                                  d_anchor.bucketArraySize()));
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const COMPARATOR&
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::comparator() const
{
    return d_parameters.originalComparator();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::countElementsInBucket(
                                                          SizeType index) const
{
    BSLS_ASSERT_SAFE(index < this->numBuckets());

    return static_cast<SizeType>(bucketAtIndex(index).countElements());
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::elementListRoot() const
{
    return d_anchor.listRootAddress();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::find(
                                                      const KeyType& key) const
{
    return bslalg::HashTableImpUtil::find<KEY_CONFIG>(
                                             d_anchor,
                                             key,
                                             d_parameters.comparator(),
                                             d_parameters.hashCodeForKey(key));
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bslalg::BidirectionalLink *
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::findEndOfRange(
                                        bslalg::BidirectionalLink *first) const
{
    BSLS_ASSERT_SAFE(first);

    typedef bslalg::HashTableImpUtil ImpUtil;

    // The reference to the Key passed to the functor is only optionally
    // const-qualified.  We must be sure to hold a reference with the correct
    // qualification.

    typedef
           typename bslalg::HashTableImpUtil_ExtractKeyResult<KEY_CONFIG>::Type
                                                                        KeyRef;
    KeyRef k = ImpUtil::extractKey<KEY_CONFIG>(first);

    while (0 != (first = first->nextLink()) &&
           d_parameters.comparator()(k,ImpUtil::extractKey<KEY_CONFIG>(first)))
    {
        // This loop body is intentionally left blank.
    }
    return first;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::findRange(
                                         bslalg::BidirectionalLink **first,
                                         bslalg::BidirectionalLink **last,
                                         const KeyType&              key) const
{
    BSLS_ASSERT_SAFE(first);
    BSLS_ASSERT_SAFE(last);

    *first = this->find(key);
    *last  = *first ? this->findEndOfRange(*first) : 0;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
bool
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::hasSameValue(
                                                  const HashTable& other) const
{
    // TBD: The template bloat of this function can be significantly reduced.
    //..
    // What matters is that the two hash tables:
    // i/   are the same size
    // ii/  have lists that are permutations of each other according to the
    //      element's 'operator=='
    // This means that the implementation should be independent of all four
    // template parameters, but will depend on VALUE_TYPE deduced from the
    // KEY_CONFIG.  Otherwise, after the initial size comparison, the rest
    // depends only on the anchors.
    //..

    typedef typename KEY_CONFIG::ValueType ValueType;
    typedef typename ::bsl::allocator_traits<ALLOCATOR>::size_type SizeType;
    typedef bslalg::HashTableImpUtil ImpUtil;


    // First test - are the containers the same size?

    if (this->size() != other.size()) {
        return false;                                                 // RETURN
    }
    bslalg::BidirectionalLink *cursor = this->elementListRoot();
    if (!cursor) {  // containers are the same size, and empty.
        return true;                                                  // RETURN
    }

    while (cursor) {
        bslalg::BidirectionalLink *rhsFirst =
             ImpUtil::find<KEY_CONFIG>(other.d_anchor,
                                       ImpUtil::extractKey<KEY_CONFIG>(cursor),
                                       other.d_parameters.comparator(),
                                       other.d_parameters.hashCodeForKey(
                                     ImpUtil::extractKey<KEY_CONFIG>(cursor)));
        if (!rhsFirst) {
            return false;  // no matching key                         // RETURN
        }

        bslalg::BidirectionalLink *endRange = this->findEndOfRange(cursor);
        bslalg::BidirectionalLink *rhsLast  = other.findEndOfRange(rhsFirst);

        // Check the key-groups have the same length - a quick-fail test.

        bslalg::BidirectionalLink *endWalker = cursor->nextLink();
        bslalg::BidirectionalLink *rhsWalker = rhsFirst->nextLink();

        while (endWalker != endRange) {


            if (rhsWalker == rhsLast) {
                return false;   // different length subsequences      // RETURN
            }
            endWalker = endWalker->nextLink();
            rhsWalker = rhsWalker->nextLink();
        }

        if (rhsWalker != rhsLast) {
            return false;   // different length subsequences          // RETURN
        }

        // Efficiently compare identical prefixes: O[N] if sequences have the
        // same elements in the same order.  Note that comparison of values in
        // nodes is tested using 'operator==' and not the key-equality
        // comparator stored in the hash table.

        while (cursor != endRange &&
                 (ImpUtil::extractValue<KEY_CONFIG>(cursor) ==
                  ImpUtil::extractValue<KEY_CONFIG>(rhsFirst)))
        {
            cursor   = cursor->nextLink();
            rhsFirst = rhsFirst->nextLink();
        }

        if (cursor == endRange) {
            continue;
        }


        // Now comes the harder part of validating that one subsequence is a
        // permutation of another, by counting elements that compare equal
        // using the equality operator, 'operator=='.  Note that this code
        // could be simplified for hash-tables with unique keys, as we can omit
        // the counting-scan, and merely test for any match within the 'other'
        // range.  Trade off the ease of a single well-tested code path, vs.
        // the importance of an efficient 'operator==' for hash containers.
        // This is currently the only place the hash-table would care about
        // uniqueness, and risk different hash-table types for unique- vs.
        // multi-containers.  Note again that comparison of values in nodes is
        // tested using 'operator==' and not the key-equality comparator stored
        // in the hash tables.

        for (bslalg::BidirectionalLink *marker = cursor;
             marker != endRange;
             marker = marker->nextLink())
        {
            const ValueType& valueAtMarker =
                                    ImpUtil::extractValue<KEY_CONFIG>(marker);

            if (cursor != marker) {  // skip on first pass only
                // Check if the value at 'marker' has already be seen.

                bslalg::BidirectionalLink *scanner = cursor;
                while (scanner != marker &&
                 ImpUtil::extractValue<KEY_CONFIG>(scanner) != valueAtMarker) {
                    scanner = scanner->nextLink();
                }
                if (scanner != marker) {  // We have seen 'lhs' one before.
                    continue;
                }
            }

            SizeType matches = 0;
            for (bslalg::BidirectionalLink *scanner = rhsFirst;
                 scanner != rhsLast;
                 scanner = scanner->nextLink()) {
                if (ImpUtil::extractValue<KEY_CONFIG>(scanner) ==
                                                               valueAtMarker) {
                    ++matches;
                }
            }
            if (!matches) {
                return false;                                         // RETURN
            }

            // Remember, *scanner is by definition a good match

            for (bslalg::BidirectionalLink *scanner = marker->nextLink();
                 scanner != endRange;
                 scanner = scanner->nextLink()) {

                if (ImpUtil::extractValue<KEY_CONFIG>(scanner) ==
                                                               valueAtMarker) {
                    if (!--matches) {  // equal matches, but excluding initial
                        return false;                                 // RETURN
                    }
                }
            }
            if (1 != matches) {
                return false;                                         // RETURN
            }
        }
        cursor = endRange;
    }
    return true;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
const HASHER&
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::hasher() const
{
    return d_parameters.originalHasher();
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
float HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::loadFactor() const
{
    return static_cast<float>(static_cast<double>(this->size())
                                    / static_cast<double>(this->numBuckets()));
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
float
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::maxLoadFactor() const
{
    return d_maxLoadFactor;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::maxNumBuckets() const
{
    // This estimate is still on the high side, we should actually pick the
    // preceding entry from our table of prime numbers used for valid bucket
    // array sizes.  There is no easy way to find that value at the moment
    // though.

    typedef typename AllocatorTraits::
                                template rebind_traits<bslalg::HashTableBucket>
                                                         BucketAllocatorTraits;
    typedef typename BucketAllocatorTraits::allocator_type BucketAllocator;

    return BucketAllocatorTraits::max_size(BucketAllocator(this->allocator()));
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::maxSize() const
{
    return AllocatorTraits::max_size(this->allocator()) / sizeof(NodeType);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::numBuckets() const
{
    return static_cast<SizeType>(d_anchor.bucketArraySize());
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::rehashThreshold() const
{
    return d_capacity;
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
typename HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::SizeType
HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>::size() const
{
    return d_size;
}

}  // close package namespace

//-----------------------------------------------------------------------------
//                      free functions and operators
//-----------------------------------------------------------------------------

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
void
bslstl::swap(bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& a,
             bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& b)
{
    typedef bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>
                                                                     TableType;

    if (::bsl::allocator_traits<ALLOCATOR>::propagate_on_container_swap::value
        || a.allocator() == b.allocator()) {
        a.swap(b);
    }
    else {
        // C++11 behavior: undefined for unequal allocators
        // BSLS_ASSERT(allocator() == other.allocator());

        TableType aCopy(a, b.allocator());
        TableType bCopy(b, a.allocator());

        b.swap(aCopy);
        a.swap(bCopy);
    }
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
bool bslstl::operator==(
       const bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& lhs,
       const bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& rhs)
{
    return lhs.hasSameValue(rhs);
}

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
inline
bool bslstl::operator!=(
         const bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& a,
         const bslstl::HashTable<KEY_CONFIG, HASHER, COMPARATOR, ALLOCATOR>& b)
{
    return !(a == b);
}

template <class FUNCTOR>
inline
void bslstl::swap(bslstl::HashTable_HashWrapper<FUNCTOR> &a,
                  bslstl::HashTable_HashWrapper<FUNCTOR> &b)
{
    a.swap(b);
}

template <class FUNCTOR>
inline
void bslstl::swap(bslstl::HashTable_ComparatorWrapper<FUNCTOR> &a,
                  bslstl::HashTable_ComparatorWrapper<FUNCTOR> &b)
{
    a.swap(b);
}

// ============================================================================
//                              TYPE TRAITS
// ============================================================================

// Type traits for HashTable:
//: o A HashTable is bitwise movable if the both functors and the allocator are
//:     bitwise movable.
//: o A HashTable uses 'bslma' allocators if the parameterized 'ALLOCATOR' is
//:     convertible from 'bslma::Allocator*'.

namespace bslma {

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
struct UsesBslmaAllocator<bslstl::HashTable<KEY_CONFIG,
                                            HASHER,
                                            COMPARATOR,
                                            ALLOCATOR> >
    : bsl::is_convertible<Allocator*, ALLOCATOR>::type {
};

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
struct UsesBslmaAllocator<bslstl::HashTable_ImplParameters<KEY_CONFIG,
                                                             HASHER,
                                                             COMPARATOR,
                                                             ALLOCATOR> >
    : bsl::is_convertible<Allocator*, ALLOCATOR>::type {
};

}  // close namespace bslma

namespace bslmf {

template <class KEY_CONFIG, class HASHER, class COMPARATOR, class ALLOCATOR>
struct IsBitwiseMoveable<bslstl::HashTable<KEY_CONFIG,
                                           HASHER,
                                           COMPARATOR,
                                           ALLOCATOR> >
: bsl::integral_constant< bool, bslmf::IsBitwiseMoveable<HASHER>::value
                             && bslmf::IsBitwiseMoveable<COMPARATOR>::value
                             && bslmf::IsBitwiseMoveable<ALLOCATOR>::value>
{};

}  // close namespace bslmf
}  // close enterprise namespace

#endif // End C++11 code

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------