// bdlc_flathashmap.h                                                 -*-C++-*-
#ifndef INCLUDED_BDLC_FLATHASHMAP
#define INCLUDED_BDLC_FLATHASHMAP

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

//@PURPOSE: Provide an open-addressed unordered map container.
//
//@CLASSES:
//   bdlc::FlatHashMap: open-addressed unordered map container
//
//@SEE_ALSO: bdlc_flathashtable, bdlc_flathashset
//
//@DESCRIPTION: This component defines a single class template,
// 'bdlc::FlatHashMap', that implements an open-addressed unordered map of
// items with unique keys.
//
// Unordered maps are useful in situations when there is no meaningful way to
// order key values, when the order of the keys is irrelevant to the problem
// domain, or (even if there is a meaningful ordering) the value of ordering
// the keys is outweighed by the higher performance provided by unordered maps
// (compared to ordered maps).  On platforms that support relevant SIMD
// instructions (e.g., SSE2), 'bdlc::FlatHashMap' generally exhibits better
// performance than 'bsl::unordered_map'.
//
// An instantiation of 'bdlc::FlatHashMap' is an allocator-aware,
// value-semantic type whose salient attributes are the collection of
// 'KEY-VALUE' pairs contained, without regard to order.  An instantiation may
// be provided with custom hash and key-equality functors, but those are not
// salient attributes.  In particular, when comparing element values for
// equality between two different 'bdlc::FlatHashMap' objects, the elements are
// compared using 'operator=='.
//
// The implemented data structure is inspired by Google's 'flat_hash_map'
// CppCon presentations (available on YouTube).  The implementation draws from
// Google's open source 'raw_hash_set.h' file at:
// https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal.
//
///Performance Caveats
///-------------------
// 'bdlc::FlatHashMap' is recommended for Intel platforms *only* (i.e., Linux
// and Windows, and pre-ARM Macs); on platforms using other processors (i.e.,
// Sun and AIX), 'bdlc::FlatHashMap' may have slower performance than
// 'bsl::unordered_map'.  However, note that 'bdlc::FlatHashMap' will use
// significantly less memory than 'bsl::unordered_map' on *all* platforms.
// Given the Intel-only performance caveat, it is recommended to benchmark
// before using 'bdlc::FlatHashMap' -- particularly on non-Intel production
// environments.
//
///Interface Differences with 'unordered_map'
///------------------------------------------
// A 'bdlc::FlatHashMap' meets most of the requirements of an unordered
// associative container with forward iterators in the C++11 Standard [23.2.5].
// It does not have the bucket interface, and locations of elements may change
// when the container is modified (and therefore iterators become invalid too).
// Allocator use follows BDE style, and the various allocator propagation
// attributes are not present (e.g., the allocator trait
// 'propagate_on_container_copy_assignment').  The maximum load factor of the
// container (the ratio of size to capacity) is maintained by the container
// itself and is not settable (the maximum load factor is implementation
// defined and fixed).
//
///Load Factor and Resizing
///------------------------
// An invariant of 'bdlc::FlatHashMap' is that
// '0 <= load_factor() <= max_load_factor() <= 1.0'.  Any operation that would
// result in 'load_factor() > max_load_factor()' for a 'bdlc::FlatHashMap'
// causes the capacity to increase.  This resizing allocates new memory, copies
// or moves all elements to the new memory, and reclaims the original memory.
// The transfer of elements involves rehashing each element to determine its
// new location.  As such, all iterators, pointers, and references to elements
// of the 'bdlc::FlatHashMap' are invalidated on a resize.
//
///Requirements on 'KEY', 'HASH', and 'EQUAL'
///------------------------------------------
// The template parameter type 'KEY' must be copy or move constructible.  The
// template parameter types 'HASH' and 'EQUAL' must be default and copy
// constructible function objects.
//
// 'HASH' must support a function-call operator compatible with the following
// statements for an object 'key' of type 'KEY':
//..
//  HASH        hash;
//  bsl::size_t result = hash(key);
//..
//
// 'EQUAL' must support a function-call operator compatible with the
//  following statements for objects 'key1' and 'key2' of type 'KEY':
//..
//  EQUAL equal;
//  bool  result = equal(key1, key2);
//..
// where the definition of the called function defines an equivalence
// relationship on keys that is both reflexive and transitive.
//
// 'HASH' and 'EQUAL' function objects are further constrained: if the
// comparator determines that two values are equal, the hasher must produce the
// same hash value for each.
//
///Iterator, Pointer, and Reference Invalidation
///---------------------------------------------
// Any change in capacity of a 'bdlc::FlatHashMap' invalidates all pointers,
// references, and iterators.  A 'bdlc::FlatHashMap' manipulator that erases an
// element invalidates all pointers, references, and iterators to the erased
// element.
//
///Exception Safety
///----------------
// A 'bdlc::FlatHashMap' is exception neutral, and all of the methods of
// 'bdlc::FlatHashMap' provide the basic exception safety guarantee (see
// {'bsldoc_glossary'|Basic Guarantee}).
//
///Move Semantics in C++03
///-----------------------
// Move-only types are supported by 'bdlc::FlatHashMap' on C++11, and later,
// platforms only (where 'BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES' is defined),
// and are not supported on C++03 platforms.  Unfortunately, in C++03, there
// are user-defined types where a 'bslmf::MovableRef' will not safely degrade
// to an lvalue reference when a move constructor is not available (types
// providing a constructor template taking any type), so
// 'bslmf::MovableRefUtil::move' cannot be used directly on a user-supplied
// template parameter type.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Example 1: Gathering Document Statistics
/// - - - - - - - - - - - - - - - - - - - -
// Suppose one wished to gather statistics on the words appearing in a large
// set of documents on disk or in a database.  Gathering those statistics is
// intrusive (as one is competing for access to the documents with the regular
// users) and must be done as quickly as possible.  Moreover, the set of unique
// words appearing in those documents may be high.  The English language has in
// excess of a million words (albeit many appear infrequently), and, if the
// documents contain serial numbers, or Social Security numbers, or chemical
// formulas, etc., then the 'O[log(n)]' insertion time of ordered maps may well
// be inadequate.  An unordered map, having an 'O[1]' typical insertion cost,
// is a viable alternative.
//
// This example illustrates the use of 'bdlc::FlatHashMap' to gather one simple
// statistic (counts of unique words) on a portion of a single document.  To
// avoid irrelevant details of acquiring the data, the data is stored in static
// arrays:
//..
//  static char document[] =
//  " IN CONGRESS, July 4, 1776.\n"
//  "\n"
//  " The unanimous Declaration of the thirteen united States of America,\n"
//  "\n"
//  " When in the Course of human events, it becomes necessary for one\n"
//  " people to dissolve the political bands which have connected them with\n"
//  " another, and to assume among the powers of the earth, the separate\n"
//  " and equal station to which the Laws of Nature and of Nature's G-d\n"
//  " entitle them, a decent respect to the opinions of mankind requires\n"
//  " that they should declare the causes which impel them to the\n"
//  " separation.  We hold these truths to be self-evident, that all men\n"
//  " are created equal, that they are endowed by their Creator with\n"
//  " certain unalienable Rights, that among these are Life, Liberty and\n"
//  " the pursuit of Happiness.  That to secure these rights, Governments\n"
//  " are instituted among Men, deriving their just powers from the consent\n"
//  " of the governed, That whenever any Form of Government becomes\n";
//..
// First, we define an alias to make our code more comprehensible:
//..
//  typedef bdlc::FlatHashMap<bsl::string, int> WordTally;
//..
// Next, we create an (empty) flat hash map to hold our word tallies:
//..
//  WordTally wordTally;
//..
// Then, we define the set of characters that define word boundaries:
//..
//  const char *delimiters = " \n\t,:;.()[]?!/";
//..
// Next, we extract the words from our document.  Note that 'strtok' modifies
// the document array (which was not made 'const').
//
// For each iteration of the loop, a map entry matching the key value parsed by
// 'strtok' is obtained.  On the first occurrence of a word, the map has no
// such entry, so one is created with a default value of the mapped value (0,
// just what we want in this case) and inserted into the map where it is found
// on any subsequent occurrences of the word.  The 'operator[]' method returns
// a reference providing modifiable access to the mapped value.  Here, we apply
// the '++' operator to that reference to maintain a tally for the word:
//..
//  for (char *cur = strtok(document, delimiters);
//             cur;
//             cur = strtok(NULL,     delimiters)) {
//      ++wordTally[bsl::string(cur)];
//  }
//..
// Now that the data has been (quickly) gathered, we can indulge in analysis
// that is more time consuming.  For example, we can define a comparison
// function, copy the data to another container (e.g., 'bsl::vector'), sort the
// entries, and determine the 10 most commonly used words in the given
// documents:
//..
//  typedef bsl::pair<bsl::string, int> WordTallyEntry;
//      // Assignable equivalent to 'WordTally::value_type'.  Note that
//      // 'bsl::vector' requires assignable types.
//
//  struct WordTallyEntryCompare {
//      static bool lessThan(const WordTallyEntry& a,
//                           const WordTallyEntry& b) {
//          return a.second < b.second;
//      }
//      static bool greaterThan(const WordTallyEntry& a,
//                              const WordTallyEntry& b) {
//          return a.second > b.second;
//      }
//  };
//
//  bsl::vector<WordTallyEntry> array(wordTally.cbegin(), wordTally.cend());
//
//  assert(10 <= array.size());
//
//  bsl::partial_sort(array.begin(),
//                    array.begin() + 10,
//                    array.end(),
//                    WordTallyEntryCompare::greaterThan);
//..
// Notice that 'partial_sort' suffices here since we seek only the 10 most used
// words, not a complete distribution of word counts.
//
// Finally, we print the sorted portion of 'array':
//..
//  for (bsl::vector<WordTallyEntry>::const_iterator cur  = array.begin(),
//                                                   end  = cur + 10;
//                                                   end != cur; ++cur) {
//      printf("%-10s %4d\n", cur->first.c_str(), cur->second);
//  }
//..
// and standard output shows:
//..
//  the          13
//  of           10
//  to            7
//  that          4
//  are           4
//  and           4
//  which         3
//  these         3
//  them          3
//  among         3
//..

#include <bdlscm_version.h>

#include <bdlc_flathashtable.h>

#include <bslalg_hasstliterators.h>
#include <bslalg_swaputil.h>

#include <bslh_fibonaccibadhashwrapper.h>

#include <bslim_printer.h>

#include <bslma_allocator.h>
#include <bslma_constructionutil.h>
#include <bslma_destructorguard.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_addconst.h>
#include <bslmf_enableif.h>
#include <bslmf_isconvertible.h>
#include <bslmf_movableref.h>
#include <bslmf_util.h>    // 'forward(V)'

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

#include <bslstl_equalto.h>
#include <bslstl_hash.h>
#include <bslstl_stdexceptutil.h>

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
#include <bsl_initializer_list.h>
#endif
#include <bsl_cstddef.h>
#include <bsl_ostream.h>
#include <bsl_utility.h>

#if defined(BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER)
#include <bsl_type_traits.h>
    #ifndef BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES
    #error Rvalue references curiously absent despite native 'type_traits'.
    #endif
#endif

namespace BloombergLP {
namespace bdlc {

// FORWARD DECLARATIONS
template <class KEY,
          class VALUE,
          class HASH  = bslh::FibonacciBadHashWrapper<bsl::hash<KEY> >,
          class EQUAL = bsl::equal_to<KEY> >
class FlatHashMap;

template <class KEY, class VALUE, class HASH, class EQUAL>
bool operator==(const FlatHashMap<KEY, VALUE, HASH, EQUAL> &lhs,
                const FlatHashMap<KEY, VALUE, HASH, EQUAL> &rhs);

template <class KEY, class VALUE, class HASH, class EQUAL>
bool operator!=(const FlatHashMap<KEY, VALUE, HASH, EQUAL> &lhs,
                const FlatHashMap<KEY, VALUE, HASH, EQUAL> &rhs);

template <class KEY, class VALUE, class HASH, class EQUAL>
void swap(FlatHashMap<KEY, VALUE, HASH, EQUAL>& a,
          FlatHashMap<KEY, VALUE, HASH, EQUAL>& b);

                       // ============================
                       // struct FlatHashMap_EntryUtil
                       // ============================

template <class KEY, class VALUE, class ENTRY>
struct FlatHashMap_EntryUtil
    // This templated utility provides methods to construct an 'ENTRY' and a
    // method to extract the key from an 'ENTRY'.
{
    // CLASS METHODS
    template <class KEY_TYPE>
    static void construct(
                        ENTRY                                       *entry,
                        bslma::Allocator                            *allocator,
                        BSLS_COMPILERFEATURES_FORWARD_REF(KEY_TYPE)  key);
        // Load into the specified 'entry' the 'ENTRY' value comprised of the
        // specified 'key' and a default constructed 'VALUE', using the
        // specified 'allocator' to supply memory.  'allocator' is ignored if
        // the (template parameter) type 'ENTRY' is not allocator aware.

    static const KEY& key(const ENTRY& entry);
        // Return the key of the specified 'entry'.
};

                            // =================
                            // class FlatHashMap
                            // =================

template <class KEY, class VALUE, class HASH, class EQUAL>
class FlatHashMap {
    // This class template implements a value-semantic container type holding
    // an unordered map of 'KEY-VALUE' pairs having unique keys that provides a
    // mapping from keys of (template parameter) type 'KEY' to their associated
    // mapped values of (template parameter) type 'VALUE'.  The (template
    // parameter) type 'HASH' is a functor providing the hash value for 'KEY'.
    // The (template parameter) type 'EQUAL' is a functor providing the
    // equality function for two 'KEY' values.  See {Requirements on 'KEY',
    // 'HASH', and 'EQUAL'} for more information.

  private:
    // PRIVATE TYPES
    typedef FlatHashTable<KEY,
                          bsl::pair<KEY, VALUE>,
                          FlatHashMap_EntryUtil<KEY,
                                                VALUE,
                                                bsl::pair<KEY, VALUE> >,
                          HASH,
                          EQUAL> ImplType;
        // This is the underlying implementation class.

    // FRIENDS
    friend bool operator==<>(const FlatHashMap&, const FlatHashMap&);
    friend bool operator!=<>(const FlatHashMap&, const FlatHashMap&);

    // The following verbose declaration is required by the xlC 12.1 compiler.
    template <class K, class V, class H, class E>
    friend void swap(FlatHashMap<K, V, H, E>&, FlatHashMap<K, V, H, E>&);

  public:
    // PUBLIC TYPES
    typedef bsl::pair<typename bsl::add_const<KEY>::type, VALUE> value_type;

    typedef KEY                                key_type;
    typedef VALUE                              mapped_type;
    typedef bsl::size_t                        size_type;
    typedef bsl::ptrdiff_t                     difference_type;
    typedef EQUAL                              key_compare;
    typedef HASH                               hasher;
    typedef value_type&                        reference;
    typedef const value_type&                  const_reference;
    typedef value_type*                        pointer;
    typedef const value_type*                  const_pointer;
    typedef typename ImplType::iterator        iterator;
    typedef typename ImplType::const_iterator  const_iterator;

  private:
    // DATA
    ImplType d_impl;  // underlying flat hash table used by this flat hash map

  public:
    // CREATORS
    FlatHashMap();
    explicit FlatHashMap(bslma::Allocator *basicAllocator);
    explicit FlatHashMap(bsl::size_t capacity);
    FlatHashMap(bsl::size_t capacity, bslma::Allocator *basicAllocator);
    FlatHashMap(bsl::size_t       capacity,
                const HASH&       hash,
                bslma::Allocator *basicAllocator = 0);
    FlatHashMap(bsl::size_t       capacity,
                const HASH&       hash,
                const EQUAL&      equal,
                bslma::Allocator *basicAllocator = 0);
        // Create an empty 'FlatHashMap' object.  Optionally specify a
        // 'capacity' indicating the minimum initial size of the underlying
        // array of entries of this container.  If 'capacity' is not supplied
        // or is 0, no memory is allocated.  Optionally specify a 'hash'
        // functor used to generate the hash values associated with the keys of
        // elements in this container.  If 'hash' is not supplied, a
        // default-constructed object of the (template parameter) type 'HASH'
        // is used.  Optionally specify an equality functor 'equal' used to
        // determine whether the keys of two elements are equivalent.  If
        // 'equal' is not supplied, a default-constructed object of the
        // (template parameter) type 'EQUAL' is used.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // supplied or is 0, the currently installed default allocator is used.

    template <class INPUT_ITERATOR>
    FlatHashMap(INPUT_ITERATOR    first,
                INPUT_ITERATOR    last,
                bslma::Allocator *basicAllocator = 0);
    template <class INPUT_ITERATOR>
    FlatHashMap(INPUT_ITERATOR    first,
                INPUT_ITERATOR    last,
                bsl::size_t       capacity,
                bslma::Allocator *basicAllocator = 0);
    template <class INPUT_ITERATOR>
    FlatHashMap(INPUT_ITERATOR    first,
                INPUT_ITERATOR    last,
                bsl::size_t       capacity,
                const HASH&       hash,
                bslma::Allocator *basicAllocator = 0);
    template <class INPUT_ITERATOR>
    FlatHashMap(INPUT_ITERATOR    first,
                INPUT_ITERATOR    last,
                bsl::size_t       capacity,
                const HASH&       hash,
                const EQUAL&      equal,
                bslma::Allocator *basicAllocator = 0);
        // Create a 'FlatHashMap' object initialized by insertion of the values
        // from the input iterator range specified by 'first' through 'last'
        // (including 'first', excluding 'last').  Optionally specify a
        // 'capacity' indicating the minimum initial size of the underlying
        // array of entries of this container.  If 'capacity' is not supplied
        // or is 0, no memory is allocated.  Optionally specify a 'hash'
        // functor used to generate hash values associated with the keys of the
        // elements in this container.  If 'hash' is not supplied, a
        // default-constructed object of the (template parameter) type 'HASH'
        // is used.  Optionally specify an equality functor 'equal' used to
        // determine whether the keys of two elements are equivalent.  If
        // 'equal' is not supplied, a default-constructed object of the
        // (template parameter) type 'EQUAL' is used.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // supplied or is 0, the currently installed default allocator is used.
        // The behavior is undefined unless 'first' and 'last' refer to a
        // sequence of valid values where 'first' is at a position at or before
        // 'last'.  Note that if a member of the input sequence has an
        // equivalent key to an earlier member, the later member will not be
        // inserted.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    FlatHashMap(bsl::initializer_list<value_type>  values,
                bslma::Allocator                  *basicAllocator = 0);
    FlatHashMap(bsl::initializer_list<value_type>  values,
                bsl::size_t                        capacity,
                bslma::Allocator                  *basicAllocator = 0);
    FlatHashMap(bsl::initializer_list<value_type>  values,
                bsl::size_t                        capacity,
                const HASH&                        hash,
                bslma::Allocator                  *basicAllocator = 0);
    FlatHashMap(bsl::initializer_list<value_type>  values,
                bsl::size_t                        capacity,
                const HASH&                        hash,
                const EQUAL&                       equal,
                bslma::Allocator                  *basicAllocator = 0);
        // Create a 'FlatHashMap' object initialized by insertion of the
        // specified 'values'.  Optionally specify a 'capacity' indicating the
        // minimum initial size of the underlying array of entries of this
        // container.  If 'capacity' is not supplied or is 0, no memory is
        // allocated.  Optionally specify a 'hash' functor used to generate
        // hash values associated with the keys of elements in this container.
        // If 'hash' is not supplied, a default-constructed object of the
        // (template parameter) type 'HASH' is used.  Optionally specify an
        // equality functor 'equal' used to determine whether the keys of two
        // elements are equivalent.  If 'equal' is not supplied, a
        // default-constructed object of the (template parameter) type 'EQUAL'
        // is used.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is not supplied or is 0, the currently
        // installed default allocator is used.  Note that if a member of
        // 'values' has an equivalent key to an earlier member, the later
        // member will not be inserted.
#endif

    FlatHashMap(const FlatHashMap&  original,
                bslma::Allocator   *basicAllocator = 0);
        // Create a 'FlatHashMap' object having the same value, hasher, and
        // equality comparator as the specified 'original' object.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is not specified or is 0, the currently installed
        // default allocator is used.

    FlatHashMap(bslmf::MovableRef<FlatHashMap> original);
        // Create a 'FlatHashMap' object having the same value, hasher,
        // equality comparator, and allocator as the specified 'original'
        // object.  The contents of 'original' are moved (in constant time) to
        // this object, 'original' is left in a (valid) unspecified state, and
        // no exceptions will be thrown.

    FlatHashMap(bslmf::MovableRef<FlatHashMap>  original,
                bslma::Allocator               *basicAllocator);
        // Create a 'FlatHashMap' object having the same value, hasher, and
        // equality comparator as the specified 'original' object, using the
        // specified 'basicAllocator' to supply memory.  If 'basicAllocator' is
        // 0, the currently installed default allocator is used.  The allocator
        // of 'original' remains unchanged.  If 'original' and the newly
        // created object have the same allocator then the contents of
        // 'original' are moved (in constant time) to this object, 'original'
        // is left in a (valid) unspecified state, and no exceptions will be
        // thrown; otherwise, 'original' is unchanged (and an exception may be
        // thrown).

    ~FlatHashMap();
        // Destroy this object and each of its elements.

    // MANIPULATORS
    FlatHashMap& operator=(const FlatHashMap& rhs);
        // Assign to this object the value, hasher, and equality functor of the
        // specified 'rhs' object, and return a reference providing modifiable
        // access to this object.

    FlatHashMap& operator=(bslmf::MovableRef<FlatHashMap> rhs);
        // Assign to this object the value, hasher, and equality comparator of
        // the specified 'rhs' object, and return a reference providing
        // modifiable access to this object.  If this object and 'rhs' use the
        // same allocator the contents of 'rhs' are moved (in constant time) to
        // this object.  'rhs' is left in a (valid) unspecified state.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    FlatHashMap& operator=(bsl::initializer_list<value_type> values);
        // Assign to this object the value resulting from first clearing this
        // map and then inserting each object in the specified 'values'
        // initializer list, ignoring those objects having a value whose key is
        // equivalent to that which appears earlier in the list; return a
        // reference providing modifiable access to this object.  This method
        // requires that the (template parameter) type 'KEY' be
        // 'copy-insertable' into this map (see {Requirements on 'KEY', 'HASH',
        // and 'EQUAL'}).
#endif

    template <class KEY_TYPE>
    VALUE& operator[](BSLS_COMPILERFEATURES_FORWARD_REF(KEY_TYPE) key);
        // Return a reference providing modifiable access to the mapped value
        // associated with the specified 'key' in this map.  If this map does
        // not already contain an element having 'key', insert an element with
        // the 'key' and a default-constructed 'VALUE', and return a reference
        // to the newly mapped value.  If 'key' is movable, 'key' is left in a
        // (valid) unspecified state.

    VALUE& at(const KEY& key);
        // Return a reference providing modifiable access to the mapped value
        // associated with the specified 'key' in this map, if such an entry
        // exists; otherwise throw a 'std::out_of_range' exception.  Note that
        // this method is not exception-neutral.

    void clear();
        // Remove all elements from this map.  Note that this map will be empty
        // after calling this method, but allocated memory may be retained for
        // future use.  See the 'capacity' method.

    bsl::pair<iterator, iterator> equal_range(const KEY& key);
        // Return a pair of iterators defining the sequence of modifiable
        // elements in this map having 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
        // map contains no elements having a key equivalent to 'key', then the
        // two returned iterators will have the same value.  Note that since a
        // map maintains unique keys, the range will contain at most one
        // element.

    bsl::size_t erase(const KEY& key);
        // Remove from this map the element whose key is equal to the specified
        // 'key', if it exists, and return 1; otherwise (there is no element
        // having 'key' in this map), return 0 with no other effect.  This
        // method invalidates all iterators and references to the removed
        // element.

    iterator erase(const_iterator position);
    iterator erase(iterator position);
        // Remove from this map the element at the specified 'position', and
        // return an iterator referring to the modifiable element immediately
        // following the removed element, or to the past-the-end position if
        // the removed element was the last element in the sequence of elements
        // maintained by this map.  This method invalidates all iterators and
        // references to the removed element.  The behavior is undefined unless
        // 'position' refers to an element in this map.

    iterator erase(const_iterator first, const_iterator last);
        // Remove from this map the elements starting at the specified 'first'
        // position up to, but not including, the specified 'last' position,
        // and return an iterator referencing the same element as 'last'.  This
        // method invalidates all iterators and references to the removed
        // elements.  The behavior is undefined unless 'first' and 'last' are
        // valid iterators on this map, and the 'first' position is at or
        // before the 'last' position in the iteration sequence provided by
        // this container.

    iterator find(const KEY& key);
        // Return an iterator referring to the modifiable element in this map
        // having the specified 'key', or 'end()' if no such entry exists in
        // this map.

#if defined(BSLS_PLATFORM_CMP_SUN) && BSLS_PLATFORM_CMP_VERSION < 0x5130
    template <class VALUE_TYPE>
    bsl::pair<iterator, bool>
#elif !defined(BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER)
    template <class VALUE_TYPE>
    typename bsl::enable_if<bsl::is_convertible<VALUE_TYPE, value_type>::value,
                            bsl::pair<iterator, bool> >::type
#else
    template <class VALUE_TYPE>
    typename bsl::enable_if<bsl::is_constructible<value_type,
                                                  VALUE_TYPE&&>::value,
                            bsl::pair<iterator, bool> >::type
#endif
    insert(BSLS_COMPILERFEATURES_FORWARD_REF(VALUE_TYPE) value)
        // Insert the specified 'value' into this map if the key of 'value'
        // does not already exist in this map; otherwise, this method has no
        // effect.  Return a 'pair' whose 'first' member is an iterator
        // referring to the (possibly newly inserted) modifiable element in
        // this map whose key is equivalent to that of the element to be
        // inserted, and whose 'second' member is 'true' if a new element was
        // inserted, and 'false' if an element with an equivalent key was
        // already present.
    {
        // Note that some compilers require functions declared with 'enable_if'
        // to be defined inline.

        return d_impl.insert(BSLS_COMPILERFEATURES_FORWARD(VALUE_TYPE, value));
    }

#if defined(BSLS_PLATFORM_CMP_SUN) && BSLS_PLATFORM_CMP_VERSION < 0x5130
    template <class VALUE_TYPE>
    iterator
#elif !defined(BSLS_COMPILERFEATURES_SUPPORT_TRAITS_HEADER)
    template <class VALUE_TYPE>
    typename bsl::enable_if<bsl::is_convertible<VALUE_TYPE, value_type>::value,
                            iterator>::type
#else
    template <class VALUE_TYPE>
    typename bsl::enable_if<bsl::is_constructible<value_type,
                                                  VALUE_TYPE&&>::value,
                            iterator>::type
#endif
    insert(const_iterator                                ,
           BSLS_COMPILERFEATURES_FORWARD_REF(VALUE_TYPE) value)
        // Insert the specified 'value' into this map if the key of 'value'
        // does not already exist in this map; otherwise, this method has no
        // effect.  Return an iterator referring to the (possibly newly
        // inserted) modifiable element in this map whose key is equivalent to
        // that of the element to be inserted.  The supplied 'const_iterator'
        // is ignored.
    {
        // Note that some compilers require functions declared with 'enable_if'
        // to be defined inline.

        return d_impl.insert(BSLS_COMPILERFEATURES_FORWARD(VALUE_TYPE,
                                                           value)).first;
    }

    template <class INPUT_ITERATOR>
    void insert(INPUT_ITERATOR first, INPUT_ITERATOR last);
        // Insert into this map the value of each element in the input iterator
        // range specified by 'first' through 'last' (including 'first',
        // excluding 'last').  The behavior is undefined unless 'first' and
        // 'last' refer to a sequence of valid values where 'first' is at a
        // position at or before 'last'.  Note that if the key of a member of
        // the input sequence is equivalent to the key of an earlier member,
        // the later member will not be inserted.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    void insert(bsl::initializer_list<value_type> values);
        // Insert into this map an element having the value of each object in
        // the specified 'values' initializer list if a value with an
        // equivalent key is not already contained in this map.  This method
        // requires that the (template parameter) type 'KEY' be copy-insertable
        // (see {Requirements on 'KEY', 'HASH', and 'EQUAL'}).
#endif

    void rehash(bsl::size_t minimumCapacity);
        // Change the capacity of this map to at least the specified
        // 'minimumCapacity', and redistribute all the contained elements into
        // a new sequence of entries according to their hash values.  If
        // '0 == minimumCapacity' and '0 == size()', the map is returned to the
        // default constructed state.  After this call, 'load_factor()' will be
        // less than or equal to 'max_load_factor()' and all iterators,
        // pointers, and references to elements of this map are invalidated.

    void reserve(bsl::size_t numEntries);
        // Change the capacity of this map to at least a capacity that can
        // accommodate the specified 'numEntries' (accounting for the load
        // factor invariant), and redistribute all the contained elements into
        // a new sequence of entries according to their hash values.  If
        // '0 == numEntries' and '0 == size()', the map is returned to the
        // default constructed state.  After this call, 'load_factor()' will be
        // less than or equal to 'max_load_factor()' and all iterators,
        // pointers, and references to elements of this map are invalidated.
        // Note that this method is effectively equivalent to:
        //..
        //     rehash(bsl::ceil(numEntries / max_load_factor()))
        //..

    void reset();
        // Remove all elements from this map and release all memory from this
        // map, returning the map to the default constructed state.

                          // Iterators

    iterator begin();
        // Return an iterator to the first element in the sequence of
        // modifiable elements maintained by this map, or the 'end' iterator if
        // this map is empty.

    iterator end();
        // Return an iterator to the past-the-end element in the sequence of
        // modifiable elements maintained by this map.

                             // Aspects

    void swap(FlatHashMap& other);
        // Exchange the value of this object as well as its hasher and equality
        // functors with those of the specified 'other' object.  The behavior
        // is undefined unless this object was created with the same allocator
        // as 'other'.

    // ACCESSORS
    const VALUE& at(const KEY& key) const;
        // Return a reference providing non-modifiable access to the mapped
        // value associated with the specified 'key' in this map, if such an
        // entry exists; otherwise throw a 'std::out_of_range' exception.  Note
        // that this method is not exception-neutral.

    bsl::size_t capacity() const;
        // Return the number of elements this map could hold if the load factor
        // were 1.

    bool contains(const KEY& key) const;
        // Return 'true' if this map contains an element having the specified
        // 'key', and 'false' otherwise.

    bsl::size_t count(const KEY& key) const;
        // Return the number of elements in this map having the specified
        // 'key'.  Note that since a flat hash map maintains unique keys, the
        // returned value will be either 0 or 1.

    bool empty() const;
        // Return 'true' if this map contains no elements, and 'false'
        // otherwise.

    bsl::pair<const_iterator, const_iterator> equal_range(
                                                         const KEY& key) const;
        // Return a pair of 'const_iterator's defining the sequence of elements
        // in this map having 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 map contains
        // no elements having a key equivalent to 'key', then the two returned
        // iterators will have the same value.  Note that since a map maintains
        // unique keys, the range will contain at most one element.

    const_iterator find(const KEY& key) const;
        // Return a 'const_iterator' referring to the element in this map
        // having the specified 'key', or 'end()' if no such entry exists in
        // this map.

    HASH hash_function() const;
        // Return (a copy of) the unary hash functor used by this map to
        // generate a hash value (of type 'bsl::size_t') for a 'KEY' object.

    EQUAL key_eq() const;
        // Return (a copy of) the binary key-equality functor that returns
        // 'true' if the value of two 'KEY' objects are equivalent, and 'false'
        // otherwise.

    float load_factor() const;
        // Return the current ratio between the number of elements in this
        // container and its capacity.

    float max_load_factor() const;
        // Return the maximum load factor allowed for this map.  Note that if
        // an insert operation would cause the load factor to exceed
        // 'max_load_factor()', that same insert operation will increase the
        // capacity and rehash the entries of the container (see {Load Factor
        // and Resizing}).  Also note that the value returned by
        // 'max_load_factor' is implementation defined and cannot be changed by
        // the user.

    bsl::size_t size() const;
        // Return the number of elements in this map.

                          // Iterators

    const_iterator begin() const;
        // Return a 'const_iterator' to the first element in the sequence of
        // elements maintained by this map, or the 'end' iterator if this map
        // is empty.

    const_iterator cbegin() const;
        // Return a 'const_iterator' to the first element in the sequence of
        // elements maintained by this map, or the 'end' iterator if this map
        // is empty.

    const_iterator cend() const;
        // Return a 'const_iterator' to the past-the-end element in the
        // sequence of elements maintained by this map.

    const_iterator end() const;
        // Return a 'const_iterator' to the past-the-end element in the
        // sequence of elements maintained by this map.

                           // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this flat hash map to supply memory.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level', and return a
        // reference to the modifiable 'stream'.  If 'level' is specified,
        // optionally specify 'spacesPerLevel', the number of spaces per
        // indentation level for this and all of its nested objects.  If
        // 'level' is negative, suppress indentation of the first line.  If
        // 'spacesPerLevel' is negative, format the entire output on one line,
        // suppressing all but the initial indentation (as governed by
        // 'level').  If 'stream' is not valid on entry, this operation has no
        // effect.
};

// FREE OPERATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
bool operator==(const FlatHashMap<KEY, VALUE, HASH, EQUAL> &lhs,
                const FlatHashMap<KEY, VALUE, HASH, EQUAL> &rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'FlatHashMap' objects have the same
    // value if their sizes are the same and each element contained in one is
    // equal to an element of the other.  The hash and equality functors are
    // not involved in the comparison.

template <class KEY, class VALUE, class HASH, class EQUAL>
bool operator!=(const FlatHashMap<KEY, VALUE, HASH, EQUAL> &lhs,
                const FlatHashMap<KEY, VALUE, HASH, EQUAL> &rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'FlatHashMap' objects do not
    // have the same value if their sizes are different or one contains an
    // element equal to no element of the other.  The hash and equality
    // functors are not involved in the comparison.

template <class KEY, class VALUE, class HASH, class EQUAL>
bsl::ostream& operator<<(bsl::ostream&                               stream,
                         const FlatHashMap<KEY, VALUE, HASH, EQUAL>& map);
    // Write the value of the specified 'map' to the specified output 'stream'
    // in a single-line format, and return a reference providing modifiable
    // access to 'stream'.  If 'stream' is not valid on entry, this operation
    // has no effect.  Note that this human-readable format is not fully
    // specified and can change without notice.

// FREE FUNCTIONS
template <class KEY, class VALUE, class HASH, class EQUAL>
void swap(FlatHashMap<KEY, VALUE, HASH, EQUAL>& a,
          FlatHashMap<KEY, VALUE, HASH, EQUAL>& b);
    // Exchange the value, the hasher, and the key-equality functor of the
    // specified 'a' and 'b' objects.  This function provides the no-throw
    // exception-safety guarantee if the two objects were created with the same
    // allocator and the basic guarantee otherwise.

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

                       // ----------------------------
                       // struct FlatHashMap_EntryUtil
                       // ----------------------------

// CLASS METHODS
template <class KEY, class VALUE, class ENTRY>
template <class KEY_TYPE>
inline
void FlatHashMap_EntryUtil<KEY, VALUE, ENTRY>::construct(
                        ENTRY                                       *entry,
                        bslma::Allocator                            *allocator,
                        BSLS_COMPILERFEATURES_FORWARD_REF(KEY_TYPE)  key)
{
    BSLS_ASSERT_SAFE(entry);

    bsls::ObjectBuffer<VALUE> value;

    bslma::ConstructionUtil::construct(value.address(), allocator);
    bslma::DestructorGuard<VALUE> guard(value.address());

    bslma::ConstructionUtil::construct(
                                  entry,
                                  allocator,
                                  BSLS_COMPILERFEATURES_FORWARD(KEY_TYPE, key),
                                  bslmf::MovableRefUtil::move(value.object()));
}

template <class KEY, class VALUE, class ENTRY>
inline
const KEY& FlatHashMap_EntryUtil<KEY, VALUE, ENTRY>::key(const ENTRY& entry)
{
    return entry.first;
}

                            // -----------------
                            // class FlatHashMap
                            // -----------------

// CREATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap()
: d_impl(0, HASH(), EQUAL())
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              bslma::Allocator *basicAllocator)
: d_impl(0, HASH(), EQUAL(), basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(bsl::size_t capacity)
: d_impl(capacity, HASH(), EQUAL())
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              bsl::size_t       capacity,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, HASH(), EQUAL(), basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              bsl::size_t       capacity,
                                              const HASH&       hash,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, hash, EQUAL(), basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              bsl::size_t       capacity,
                                              const HASH&       hash,
                                              const EQUAL&      equal,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, hash, equal, basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class INPUT_ITERATOR>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              bslma::Allocator *basicAllocator)
: d_impl(0, HASH(), EQUAL(), basicAllocator)
{
    insert(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class INPUT_ITERATOR>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              bsl::size_t       capacity,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, HASH(), EQUAL(), basicAllocator)
{
    insert(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class INPUT_ITERATOR>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              bsl::size_t       capacity,
                                              const HASH&       hash,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, hash, EQUAL(), basicAllocator)
{
    insert(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class INPUT_ITERATOR>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              bsl::size_t       capacity,
                                              const HASH&       hash,
                                              const EQUAL&      equal,
                                              bslma::Allocator *basicAllocator)
: d_impl(capacity, hash, equal, basicAllocator)
{
    insert(first, last);
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                             bsl::initializer_list<value_type>  values,
                             bslma::Allocator                  *basicAllocator)
: FlatHashMap(values.begin(),
              values.end(),
              0,
              HASH(),
              EQUAL(),
              basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                             bsl::initializer_list<value_type>  values,
                             bsl::size_t                        capacity,
                             bslma::Allocator                  *basicAllocator)
: FlatHashMap(values.begin(),
              values.end(),
              capacity,
              HASH(),
              EQUAL(),
              basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                             bsl::initializer_list<value_type>  values,
                             bsl::size_t                        capacity,
                             const HASH&                        hash,
                             bslma::Allocator                  *basicAllocator)
: FlatHashMap(values.begin(),
              values.end(),
              capacity,
              hash,
              EQUAL(),
              basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                             bsl::initializer_list<value_type>  values,
                             bsl::size_t                        capacity,
                             const HASH&                        hash,
                             const EQUAL&                       equal,
                             bslma::Allocator                  *basicAllocator)
: FlatHashMap(values.begin(),
              values.end(),
              capacity,
              hash,
              equal,
              basicAllocator)
{
}
#endif

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                            const FlatHashMap&  original,
                                            bslma::Allocator   *basicAllocator)
: d_impl(original.d_impl, basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                       bslmf::MovableRef<FlatHashMap> original)
: d_impl(bslmf::MovableRefUtil::move(
                               bslmf::MovableRefUtil::access(original).d_impl))
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::FlatHashMap(
                                bslmf::MovableRef<FlatHashMap>  original,
                                bslma::Allocator               *basicAllocator)
: d_impl(bslmf::MovableRefUtil::move(
                               bslmf::MovableRefUtil::access(original).d_impl),
         basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>::~FlatHashMap()
{
}

// MANIPULATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>&
FlatHashMap<KEY, VALUE, HASH, EQUAL>::operator=(const FlatHashMap& rhs)
{
    d_impl = rhs.d_impl;

    return *this;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>&
FlatHashMap<KEY, VALUE, HASH, EQUAL>::operator=(
                                            bslmf::MovableRef<FlatHashMap> rhs)
{
    FlatHashMap& lvalue = rhs;

    d_impl = bslmf::MovableRefUtil::move(lvalue.d_impl);

    return *this;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
FlatHashMap<KEY, VALUE, HASH, EQUAL>&
FlatHashMap<KEY, VALUE, HASH, EQUAL>::operator=(
                                      bsl::initializer_list<value_type> values)
{
    FlatHashMap tmp(values.begin(),
                    values.end(),
                    0,
                    d_impl.hash_function(),
                    d_impl.key_eq(),
                    d_impl.allocator());

    this->swap(tmp);

    return *this;
}
#endif

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class KEY_TYPE>
inline
VALUE& FlatHashMap<KEY, VALUE, HASH, EQUAL>::operator[](
                               BSLS_COMPILERFEATURES_FORWARD_REF(KEY_TYPE) key)
{
    return d_impl[BSLS_COMPILERFEATURES_FORWARD(KEY_TYPE, key)].second;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
VALUE& FlatHashMap<KEY, VALUE, HASH, EQUAL>::at(const KEY& key)
{
    iterator node = d_impl.find(key);

    if (node == d_impl.end()) {
        BloombergLP::bslstl::StdExceptUtil::throwOutOfRange(
                          "FlatHashMap<...>::at(key_type): invalid key value");
    }

    return node->second;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void
FlatHashMap<KEY, VALUE, HASH, EQUAL>::clear()
{
    d_impl.clear();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
bsl::pair<typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator,
          typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator>
FlatHashMap<KEY, VALUE, HASH, EQUAL>::equal_range(const KEY& key)
{
    return d_impl.equal_range(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
bsl::size_t FlatHashMap<KEY, VALUE, HASH, EQUAL>::erase(const KEY& key)
{
    return d_impl.erase(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
FlatHashMap<KEY, VALUE, HASH, EQUAL>::erase(const_iterator position)
{
    BSLS_ASSERT_SAFE(position != end());

    return d_impl.erase(position);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
FlatHashMap<KEY, VALUE, HASH, EQUAL>::erase(iterator position)
{
    // Note that this overload is necessary to avoid ambiguity when the key is
    // an iterator.

    BSLS_ASSERT_SAFE(position != end());

    return d_impl.erase(position);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
FlatHashMap<KEY, VALUE, HASH, EQUAL>::erase(const_iterator first,
                                            const_iterator last)
{
    return d_impl.erase(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
FlatHashMap<KEY, VALUE, HASH, EQUAL>::find(const KEY& key)
{
    return d_impl.find(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class INPUT_ITERATOR>
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::insert(INPUT_ITERATOR first,
                                                  INPUT_ITERATOR last)
{
    d_impl.insert(first, last);
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class VALUE, class HASH, class EQUAL>
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::insert(
                                      bsl::initializer_list<value_type> values)
{
    insert(values.begin(), values.end());
}
#endif

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::rehash(bsl::size_t minimumCapacity)
{
    d_impl.rehash(minimumCapacity);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::reserve(bsl::size_t numEntries)
{
    d_impl.reserve(numEntries);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::reset()
{
    d_impl.reset();
}

                          // Iterators

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
                                  FlatHashMap<KEY, VALUE, HASH, EQUAL>::begin()
{
    return d_impl.begin();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::iterator
                                    FlatHashMap<KEY, VALUE, HASH, EQUAL>::end()
{
    return d_impl.end();
}

                             // Aspects

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void FlatHashMap<KEY, VALUE, HASH, EQUAL>::swap(FlatHashMap& other)
{
    BSLS_ASSERT_SAFE(allocator() == other.allocator());

    d_impl.swap(other.d_impl);
}

// ACCESSORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
const VALUE& FlatHashMap<KEY, VALUE, HASH, EQUAL>::at(const KEY& key) const
{
    const_iterator node = d_impl.find(key);

    if (node == d_impl.end()) {
        BloombergLP::bslstl::StdExceptUtil::throwOutOfRange(
                    "FlatHashMap<...>::at(key_type) const: invalid key value");
    }

    return node->second;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t FlatHashMap<KEY, VALUE, HASH, EQUAL>::capacity() const
{
    return d_impl.capacity();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool FlatHashMap<KEY, VALUE, HASH, EQUAL>::contains(const KEY& key) const
{
    return d_impl.contains(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t FlatHashMap<KEY, VALUE, HASH, EQUAL>::count(const KEY& key) const
{
    return d_impl.count(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool FlatHashMap<KEY, VALUE, HASH, EQUAL>::empty() const
{
    return d_impl.empty();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
bsl::pair<typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator,
          typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator>
FlatHashMap<KEY, VALUE, HASH, EQUAL>::equal_range(const KEY& key) const
{
    return d_impl.equal_range(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator
FlatHashMap<KEY, VALUE, HASH, EQUAL>::find(const KEY& key) const
{
    return d_impl.find(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
HASH FlatHashMap<KEY, VALUE, HASH, EQUAL>::hash_function() const
{
    return d_impl.hash_function();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
EQUAL FlatHashMap<KEY, VALUE, HASH, EQUAL>::key_eq() const
{
    return d_impl.key_eq();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
float FlatHashMap<KEY, VALUE, HASH, EQUAL>::load_factor() const
{
    return d_impl.load_factor();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
float FlatHashMap<KEY, VALUE, HASH, EQUAL>::max_load_factor() const
{
    return d_impl.max_load_factor();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t FlatHashMap<KEY, VALUE, HASH, EQUAL>::size() const
{
    return d_impl.size();
}

                          // Iterators

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator
                      FlatHashMap<KEY, VALUE, HASH, EQUAL>::begin() const
{
    return d_impl.begin();
}
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator
                     FlatHashMap<KEY, VALUE, HASH, EQUAL>::cbegin() const
{
    return d_impl.cbegin();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator
                       FlatHashMap<KEY, VALUE, HASH, EQUAL>::cend() const
{
    return d_impl.cend();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
typename FlatHashMap<KEY, VALUE, HASH, EQUAL>::const_iterator
                        FlatHashMap<KEY, VALUE, HASH, EQUAL>::end() const
{
    return d_impl.end();
}

                             // Aspects

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bslma::Allocator *FlatHashMap<KEY, VALUE, HASH, EQUAL>::allocator() const
{
    return d_impl.allocator();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
bsl::ostream& FlatHashMap<KEY, VALUE, HASH, EQUAL>::print(
                                            bsl::ostream& stream,
                                            int           level,
                                            int           spacesPerLevel) const
{
    if (stream.bad()) {
        return stream;                                                // RETURN
    }

    bslim::Printer printer(&stream, level, spacesPerLevel);

    printer.start();

    const_iterator iter = begin();
    while (iter != end()) {
        printer.printValue(*iter);
        ++iter;
    }

    printer.end();

    return stream;
}

}  // close package namespace

// FREE OPERATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool bdlc::operator==(const FlatHashMap<KEY, VALUE, HASH, EQUAL>& lhs,
                      const FlatHashMap<KEY, VALUE, HASH, EQUAL>& rhs)
{
    return lhs.d_impl == rhs.d_impl;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool bdlc::operator!=(const FlatHashMap<KEY, VALUE, HASH, EQUAL>& lhs,
                      const FlatHashMap<KEY, VALUE, HASH, EQUAL>& rhs)
{
    return lhs.d_impl != rhs.d_impl;
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::ostream& bdlc::operator<<(
                            bsl::ostream&                               stream,
                            const FlatHashMap<KEY, VALUE, HASH, EQUAL>& map)
{
    return map.print(stream, 0, -1);
}

// FREE FUNCTIONS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void bdlc::swap(FlatHashMap<KEY, VALUE, HASH, EQUAL>& a,
                FlatHashMap<KEY, VALUE, HASH, EQUAL>& b)
{
    bslalg::SwapUtil::swap(&a.d_impl, &b.d_impl);
}

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

namespace bslalg {

template <class KEY, class VALUE, class HASH, class EQUAL>
struct HasStlIterators<bdlc::FlatHashMap<KEY, VALUE, HASH, EQUAL> >
: bsl::true_type {
};

}  // close namespace bslalg

namespace bslma {

template <class KEY, class VALUE, class HASH, class EQUAL>
struct UsesBslmaAllocator<bdlc::FlatHashMap<KEY, VALUE, HASH, EQUAL> >
: bsl::true_type {
};

}  // close namespace bslma
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2020 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 ----------------------------------