// bslstl_multiset_cpp03.h                                            -*-C++-*-

// Automatically generated file.  **DO NOT EDIT**

#ifndef INCLUDED_BSLSTL_MULTISET_CPP03
#define INCLUDED_BSLSTL_MULTISET_CPP03

//@PURPOSE: Provide C++03 implementation for bslstl_multiset.h
//
//@CLASSES: See bslstl_multiset.h for list of classes
//
//@SEE_ALSO: bslstl_multiset
//
//@DESCRIPTION:  This component is the C++03 translation of a C++11 component,
// generated by the 'sim_cpp11_features.pl' program.  If the original header
// contains any specially delimited regions of C++11 code, then this generated
// file contains the C++03 equivalent, i.e., with variadic templates expanded
// and rvalue-references replaced by 'bslmf::MovableRef' objects.  The header
// code in this file is designed to be '#include'd into the original header
// when compiling with a C++03 compiler.  If there are no specially delimited
// regions of C++11 code, then this header contains no code and is not
// '#include'd in the original header.
//
// Generated on Thu Mar 19 20:53:55 2026
// Command line: sim_cpp11_features.pl bslstl_multiset.h

#ifdef COMPILING_BSLSTL_MULTISET_H

namespace bsl {

                             // ==============
                             // class multiset
                             // ==============

/// This class template implements a value-semantic container type holding
/// an ordered sequence of possibly duplicate keys (of the template
/// parameter type, `KEY`).
///
/// This class:
/// * supports a complete set of *value-semantic* operations
///   - except for BDEX serialization
/// * is *exception-neutral* (agnostic except for the `at` method)
/// * is *alias-safe*
/// * is `const` *thread-safe*
/// For terminology see {`bsldoc_glossary`}.
template <class KEY,
          class COMPARATOR = std::less<KEY>,
          class ALLOCATOR  = bsl::allocator<KEY> >
class multiset {

    // PRIVATE TYPES

    /// This typedef is an alias for the type of key objects maintained by
    /// this multiset.
    typedef const KEY                                           ValueType;

    /// This typedef is an alias for the comparator used internally by this
    /// multiset.
    typedef BloombergLP::bslstl::SetComparator<KEY, COMPARATOR> Comparator;

    /// This typedef is an alias for the type of nodes held by the tree (of
    /// nodes) used to implement this multiset.
    typedef BloombergLP::bslstl::TreeNode<KEY>                 Node;

    /// This typedef is an alias for the factory type used to create and
    /// destroy `Node` objects.
    typedef BloombergLP::bslstl::TreeNodePool<KEY, ALLOCATOR>  NodeFactory;

    /// This typedef is an alias for the allocator traits type associated
    /// with this container.
    typedef bsl::allocator_traits<ALLOCATOR>                   AllocatorTraits;

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

    /// This class is a wrapper around the comparator and allocator data
    /// members.  It takes advantage of the empty-base optimization (EBO) so
    /// that if the comparator is stateless, it takes up no space.
    ///
    /// TBD: This class should eventually be replaced by the use of a
    /// general EBO-enabled component that provides a `pair`-like interface
    /// or a `tuple`.
    class DataWrapper : public Comparator {

        // DATA
        NodeFactory d_pool;  // pool of 'Node' objects

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

      public:
        // CREATORS

        /// Create a data wrapper using a copy of the specified `comparator`
        /// to order keys and a copy of the specified `basicAllocator` to
        /// supply memory.
        explicit DataWrapper(const COMPARATOR& comparator,
                             const ALLOCATOR&  basicAllocator);

        /// Create a data wrapper initialized to the contents of the `pool`
        /// associated with the specified `original` data wrapper.  The
        /// comparator and allocator associated with `original` are
        /// propagated to the new data wrapper.  `original` is left in a
        /// valid but unspecified state.
        DataWrapper(BloombergLP::bslmf::MovableRef<DataWrapper> original);

        // MANIPULATORS

        /// Return a reference providing modifiable access to the node
        /// factory associated with this data wrapper.
        NodeFactory& nodeFactory();

        // ACCESSORS

        /// Return a reference providing non-modifiable access to the node
        /// factory associated with this data wrapper.
        const NodeFactory& nodeFactory() const;
    };

    // DATA
    DataWrapper                       d_compAndAlloc;
                                               // comparator and pool of 'Node'
                                               // objects

    BloombergLP::bslalg::RbTreeAnchor d_tree;  // balanced tree of 'Node'
                                               // objects

  public:
    // PUBLIC TYPES
    typedef KEY                                        key_type;
    typedef KEY                                        value_type;
    typedef COMPARATOR                                 key_compare;
    typedef COMPARATOR                                 value_compare;
    typedef ALLOCATOR                                  allocator_type;
    typedef value_type&                                reference;
    typedef const value_type&                          const_reference;

    typedef typename AllocatorTraits::size_type        size_type;
    typedef typename AllocatorTraits::difference_type  difference_type;
    typedef typename AllocatorTraits::pointer          pointer;
    typedef typename AllocatorTraits::const_pointer    const_pointer;

    typedef BloombergLP::bslstl::TreeIterator<const value_type,
                                              Node,
                                              difference_type> iterator;
    typedef BloombergLP::bslstl::TreeIterator<const value_type,
                                              Node,
                                              difference_type> const_iterator;
    typedef bsl::reverse_iterator<iterator>            reverse_iterator;
    typedef bsl::reverse_iterator<const_iterator>      const_reverse_iterator;

  private:
    // PRIVATE MANIPULATORS

    /// Return a reference providing modifiable access to the comparator for
    /// this multiset.
    Comparator& comparator();

    /// Return a reference providing modifiable access to the node-allocator
    /// for this multiset.
    NodeFactory& nodeFactory();

    /// Efficiently exchange the value, comparator, and allocator of this
    /// object with the value, comparator, and allocator of the specified
    /// `other` object.  This method provides the no-throw exception-safety
    /// guarantee, *unless* swapping the (user-supplied) comparator or
    /// allocator objects can throw.
    void quickSwapExchangeAllocators(multiset& other);

    /// Efficiently exchange the value and comparator of this object with
    /// the value and comparator of the specified `other` object.  This
    /// method provides the no-throw exception-safety guarantee, *unless*
    /// swapping the (user-supplied) comparator objects can throw.  The
    /// behavior is undefined unless this object was created with the same
    /// allocator as `other`.
    void quickSwapRetainAllocators(multiset& other);

    /// Insert the values between the specified `first` and `last` into an
    /// initially empty set.  If sorted, directly place each value in its
    /// proper position. If an out of order value is detected, revert to
    /// normal insertion.
    template <class INPUT_ITERATOR, class SENTINEL>
    void constructFromRange(INPUT_ITERATOR first, SENTINEL last);

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)

    /// Insert the values between the specified `first` and `last` into an
    /// initially empty set.  The specified 'numElements` is used to improve
    /// performance.  If sorted, directly place each value in its proper
    /// position. If an out of order value is detected, revert to
    /// normal insertion.  The behavior is undefined if the iterators support
    /// the calculation of distance and `numElements` is not the distance
    /// from `first` to `last`.
    template <class INPUT_ITERATOR, class SENTINEL>
    void constructFromRange(INPUT_ITERATOR first,
                            SENTINEL       last,
                            size_t         numElements);
#endif

    // Insert the values between `first` and `last` into this map.
    template <class INPUT_ITERATOR, class SENTINEL>
    void insertFromRange(INPUT_ITERATOR first,
                         SENTINEL       last);

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)

    /// Insert the values between the specified `first` and `last` into this
    /// map. The specified `numElements` is used to improve performance.
    /// The behavior is undefined if the iterators support the calculation
    /// of distance and `numElements` is not the distance from `first` to
    /// `last`.
    template <class INPUT_ITERATOR, class SENTINEL>
    void insertFromRange(INPUT_ITERATOR first,
                         SENTINEL       last,
                         size_t         numElements);
#endif

    // PRIVATE ACCESSORS

    /// Return a reference providing non-modifiable access to the comparator
    /// for this multiset.
    const Comparator& comparator() const;

    /// Return a reference providing non-modifiable access to the
    /// node-allocator for this multiset.
    const NodeFactory& nodeFactory() const;

  public:
    // CREATORS

    /// Create an empty multiset.  Optionally specify a `comparator` used to
    /// order keys contained in this object.  If `comparator` is not
    /// supplied, a default-constructed object of the (template parameter)
    /// type `COMPARATOR` is used.  Optionally specify the `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`
    /// (the default), then `basicAllocator`, if supplied, shall be
    /// convertible to `bslma::Allocator *`.  If the type `ALLOCATOR` is
    /// `bsl::allocator` and `basicAllocator` is not supplied, the currently
    /// installed default allocator is used.
    multiset();
    explicit multiset(const COMPARATOR& comparator,
                      const ALLOCATOR&  basicAllocator = ALLOCATOR())
    : d_compAndAlloc(comparator, basicAllocator)
    , d_tree()
    {
        // The implementation is placed here in the class definition to work
        // around an AIX compiler bug, where the constructor can fail to
        // compile because it is unable to find the definition of the default
        // argument.  This occurs when a templatized class wraps around the
        // container and the comparator is defined after the new class.
    }

    /// Create an empty multiset that uses the specified `basicAllocator` to
    /// supply memory.  Use a default-constructed object of the (template
    /// parameter) type `COMPARATOR` to order the keys contained in this
    /// multiset.  Note that a `bslma::Allocator *` can be supplied for
    /// `basicAllocator` if the (template parameter) `ALLOCATOR` is
    /// `bsl::allocator` (the default).
    explicit multiset(const ALLOCATOR& basicAllocator);

    /// Create a multiset having the same value as the specified `original`
    /// object.  Use a copy of `original.key_comp()` to order the keys
    /// contained in this multiset.  Use the allocator returned by
    /// 'bsl::allocator_traits<ALLOCATOR>::
    /// select_on_container_copy_construction(original.get_allocator())' to
    /// allocate memory.  This method requires that the (template parameter)
    /// type `KEY` be `copy-insertable` into this multiset (see
    /// {Requirements on `KEY`}).
    multiset(const multiset& original);

    /// Create a multiset having the same value as that of the specified
    /// `original` object by moving (in constant time) the contents of
    /// `original` to the new multiset.  Use a copy of `original.key_comp()`
    /// to order the keys contained in this multiset.  The allocator
    /// associated with `original` is propagated for use in the
    /// newly-created multiset.  `original` is left in a valid but
    /// unspecified state.
    multiset(BloombergLP::bslmf::MovableRef<multiset> original);    // IMPLICIT

    /// Create a multiset having the same value as the specified `original`
    /// object that uses the specified `basicAllocator` to supply memory.
    /// Use a copy of `original.key_comp()` to order the keys contained in
    /// this multiset.  This method requires that the (template parameter)
    /// type `KEY` be `copy-insertable` into this multiset (see
    /// {Requirements on `KEY`}).  Note that a `bslma::Allocator *` can be
    /// supplied for `basicAllocator` if the (template parameter) type
    /// `ALLOCATOR` is `bsl::allocator` (the default).
    multiset(const multiset&                                original,
             const typename type_identity<ALLOCATOR>::type& basicAllocator);

    /// Create a multiset having the same value 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
    /// multiset 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.key_comp()` to order the keys contained in this multiset.
    /// This method requires that the (template parameter) type `KEY` be
    /// `move-insertable` into this multiset (see {Requirements on `KEY`}).
    /// Note that a `bslma::Allocator *` can be supplied for
    /// `basicAllocator` if the (template parameter) type `ALLOCATOR` is
    /// `bsl::allocator` (the default).
    multiset(BloombergLP::bslmf::MovableRef<multiset>       original,
             const typename type_identity<ALLOCATOR>::type& basicAllocator);

    /// Create a multiset, and insert each `value_type` object in the
    /// sequence starting at the specified `first` element, and ending
    /// immediately before the specified `last` element.  Optionally specify
    /// a `comparator` used to order keys contained in this object.  If
    /// `comparator` is not supplied, a default-constructed object of the
    /// (template parameter) type `COMPARATOR` is used.  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.  If the sequence `first` to
    /// `last` is ordered according to `comparator`, then this operation has
    /// `O[N]` complexity, where `N` is the number of elements between
    /// `first` and `last`, otherwise this operation has `O[N * log(N)]`
    /// complexity.  The (template parameter) type `INPUT_ITERATOR` shall
    /// meet the requirements of an input iterator defined in the C++11
    /// standard [24.2.3] providing access to values of a type convertible
    /// to `value_type`, and `value_type` must be `emplace-constructible`
    /// from `*i` into this multiset, where `i` is a dereferenceable
    /// iterator in the range `[first .. last)` (see {Requirements on
    /// `KEY`}).  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 a `bslma::Allocator *` can be supplied for
    /// `basicAllocator` if the type `ALLOCATOR` is `bsl::allocator` (the
    /// default).
    template <class INPUT_ITERATOR>
    multiset(INPUT_ITERATOR    first,
             INPUT_ITERATOR    last,
             const COMPARATOR& comparator     = COMPARATOR(),
             const ALLOCATOR&  basicAllocator = ALLOCATOR());
    template <class INPUT_ITERATOR>
    multiset(INPUT_ITERATOR    first,
             INPUT_ITERATOR    last,
             const ALLOCATOR&  basicAllocator);

    /// Create a multiset having the (`value_type`) values obtained from the
    /// specified `range`.  Ignore those those objects having a key equivalent
    /// to that which appears earlier in the sequence.  Optionally specify a
    /// `comparator` used to order key-value pairs contained in this object.
    /// If `comparator` is not supplied, a default-constructed object of the
    /// (template parameter) type `COMPARATOR` is used.  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`
    /// (the default), then `basicAllocator`, if supplied, shall be
    /// convertible to `bslma::Allocator *`.  If the type `ALLOCATOR` is
    /// `bsl::allocator` and `basicAllocator` is not supplied, the currently
    /// installed default allocator is used.  If values obtained from `range
    /// are ordered according to `comparator`, then this operation has `O[N]`
    /// complexity, where `N` is the number of values in the `range`;
    /// otherwise, this operation has `O[N * log(N)]` complexity.  Note that
    /// `RANGE` must meet the requirements of an input range and the values
    /// from `range` must have a type matching or convertible to `value_type`.
    template <class RANGE>
    BSLSTL_MULTISET_REQUIRES_CONTAINER_COMPATIBLE_RANGE(RANGE, value_type)
    multiset(
        bsl::from_range_t                        ,
        BSLS_COMPILERFEATURES_FORWARD_REF(RANGE) range,
        const COMPARATOR&                        comparator     = COMPARATOR(),
        const ALLOCATOR&                         basicAllocator = ALLOCATOR())
    : d_compAndAlloc(comparator, basicAllocator)
    , d_tree()
    {
        // Defined inline to avoid Windows errors.

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)
        if constexpr (ranges::sized_range<RANGE>) {
            constructFromRange(bsl::ranges::begin(range),
                               bsl::ranges::end  (range),
                               bsl::ranges::size (range));
        } else // ...
#endif
        {
            constructFromRange(bsl::ranges::begin(range),
                               bsl::ranges::end  (range));
        }
    }

    template <class RANGE>
    BSLSTL_MULTISET_REQUIRES_CONTAINER_COMPATIBLE_RANGE(RANGE, value_type)
    multiset(from_range_t                             ,
             BSLS_COMPILERFEATURES_FORWARD_REF(RANGE) range,
             const ALLOCATOR&                         basicAllocator)
    : d_compAndAlloc(COMPARATOR(), basicAllocator)
    , d_tree()
    {
        // Defined inline to avoid Windows errors.

        multiset other(bsl::from_range,
                       range,
                       COMPARATOR(),
                       nodeFactory().allocator());
        quickSwapRetainAllocators(other);
    }

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    /// Create a multiset and insert each `value_type` object in the
    /// specified `values` initializer list.  Optionally specify a
    /// `comparator` used to order keys contained in this object.  If
    /// `comparator` is not supplied, a default-constructed object of the
    /// (template parameter) type `COMPARATOR` is used.  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.  If `values` is ordered
    /// according to `comparator`, then this operation has `O[N]`
    /// complexity, where `N` is the number of elements in `values`;
    /// otherwise this operation has `O[N * log(N)]` complexity.  This
    /// method requires that the (template parameter) type `KEY` be
    /// `copy-insertable` into this multiset (see {Requirements on `KEY`}).
    /// Note that a `bslma::Allocator *` can be supplied for
    /// `basicAllocator` if the type `ALLOCATOR` is `bsl::allocator` (the
    /// default).
    multiset(std::initializer_list<KEY> values,
             const COMPARATOR&          comparator     = COMPARATOR(),
             const ALLOCATOR&           basicAllocator = ALLOCATOR());
    multiset(std::initializer_list<KEY> values,
             const ALLOCATOR&           basicAllocator);
#endif

    /// Destroy this object.
    ~multiset();

    // MANIPULATORS

    /// Assign to this object the value and comparator 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.
    /// If an exception is thrown, `*this` is left in a valid but
    /// unspecified state.  This method requires that the (template
    /// parameter) type `KEY` be `copy-assignable` and `copy-insertable`
    /// into this multiset (see {Requirements on `KEY`}).
    multiset& operator=(const multiset& rhs);

    multiset& operator=(BloombergLP::bslmf::MovableRef<multiset> rhs)
                       BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                           AllocatorTraits::is_always_equal::value
                        && std::is_nothrow_move_assignable<COMPARATOR>::value);
        // Assign to this object the value and comparator 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.
        // The contents of 'rhs' are moved (in constant time) to this multiset
        // if 'get_allocator() == rhs.get_allocator()' (after accounting for
        // the aforementioned trait); otherwise, all elements in this multiset
        // are either destroyed or move-assigned to and each additional element
        // in 'rhs' is move-inserted into this multiset.  'rhs' is left in a
        // valid but unspecified state, and if an exception is thrown, '*this'
        // is left in a valid but unspecified state.  This method requires that
        // the (template parameter) type 'KEY' be 'move-assignable' and
        // 'move-insertable' into this multiset (see {Requirements on 'KEY'}).

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    /// Assign to this object the value resulting from first clearing this
    /// multiset and then inserting each `value_type` object in the
    /// specified `values` initializer list and return a reference providing
    /// modifiable access to this object.  This method requires that the
    /// (template parameter) type `KEY` be `copy-insertable` into this
    /// multiset (see {Requirements on `KEY`}).
    multiset& operator=(std::initializer_list<KEY> values);
#endif

    /// Return an iterator providing modifiable access to the first
    /// `value_type` object in the ordered sequence of `value_type` objects
    /// maintained by this multiset, or the `end` iterator if this multiset
    /// is empty.
    iterator begin() BSLS_KEYWORD_NOEXCEPT;

    /// Return an iterator providing modifiable access to the past-the-end
    /// element in the ordered sequence of `value_type` objects maintained
    /// by this multiset.
    iterator end() BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing modifiable access to the last
    /// `value_type` object in the ordered sequence of `value_type` objects
    /// maintained by this multiset, or `rend` if this multiset is empty.
    reverse_iterator rbegin() BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing modifiable access to the
    /// prior-to-the-beginning element in the ordered sequence of
    /// `value_type` objects maintained by this multiset.
    reverse_iterator rend() BSLS_KEYWORD_NOEXCEPT;

    /// Insert the specified `value` into this multiset.  If a range
    /// containing elements equivalent to `value` already exists, insert the
    /// `value` at the end of that range.  Return an iterator referring to
    /// the newly inserted `value_type` object.  This method requires that
    /// the (template parameter) type `KEY` be `copy-insertable` into this
    /// multiset (see {Requirements on `KEY`}).
    iterator insert(const value_type& value);

    /// Insert the specified `value` into this multiset.  If a range
    /// containing elements equivalent to `value` already exists in this
    /// multiset, insert `value` at the end of that range.  `value` is left
    /// in a valid but unspecified state.  Return an iterator referring to
    /// the newly inserted `value_type` object in this multiset that is
    /// equivalent to `value`.  This method requires that the (template
    /// parameter) type `KEY` be `move-insertable` into this multiset (see
    /// {Requirements on `KEY`}).
    iterator insert(BloombergLP::bslmf::MovableRef<value_type> value);

    /// Insert the specified `value` into this multiset (in amortized
    /// constant time if the specified `hint` is a valid immediate successor
    /// to `value`).  Return an iterator referring to the newly inserted
    /// `value_type` object in this multiset that is equivalent to `value`.
    /// If `hint` is not a valid immediate successor to `value`, this
    /// operation has `O[log(N)]` complexity, where `N` is the size of this
    /// multiset.  This method requires that the (template parameter) type
    /// `KEY` be `copy-insertable` into this multiset (see {Requirements on
    /// `KEY`}).  The behavior is undefined unless `hint` is an iterator in
    /// the range `[begin() .. end()]` (both endpoints included).
    iterator insert(const_iterator hint, const value_type& value);

    /// Insert the specified `value` into this multiset (in amortized
    /// constant time if the specified `hint` is a valid immediate successor
    /// to `value`).  `value` is left in a valid but unspecified state.
    /// Return an iterator referring to the newly inserted `value_type`
    /// object in this multiset that is equivalent to `value`.  If `hint` is
    /// not a valid immediate successor to `value`, this operation has
    /// `O[log(N)]` complexity, where `N` is the size of this multiset.
    /// This method requires that the (template parameter) type `KEY` be
    /// `move-insertable` into this multiset (see {Requirements on `KEY`}).
    /// The behavior is undefined unless `hint` is an iterator in the range
    /// `[begin() .. end()]` (both endpoints included).
    iterator insert(const_iterator                             hint,
                    BloombergLP::bslmf::MovableRef<value_type> value);

    /// Insert into this multiset the value of each `value_type` object in
    /// the range starting at the specified `first` iterator and ending
    /// immediately before the specified `last` iterator.  The (template
    /// parameter) type `INPUT_ITERATOR` shall meet the requirements of an
    /// input iterator defined in the C++11 standard [24.2.3] providing
    /// access to values of a type convertible to `value_type`, and
    /// `value_type` must be `emplace-constructible` from `*i` into this
    /// multiset, where `i` is a dereferenceable iterator in the range
    /// `[first .. last)` (see {Requirements on `KEY`}).  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`.
    template <class INPUT_ITERATOR>
    void insert(INPUT_ITERATOR first, INPUT_ITERATOR last);

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    /// Insert into this multiset the value of each `value_type` object in
    /// the specified `values` initializer list.  This method requires that
    /// the (template parameter) type `KEY` be `copy-insertable` into this
    /// multiset (see {Requirements on `KEY`}).
    void insert(std::initializer_list<KEY> values);
#endif

    /// Insert into this multiset the value of each `value_type` object in the
    /// specified `range` if the key equivalent of that object is not
    /// already contained in this map.  The (template parameter) type `RANGE`
    /// must meet the requirements the C++20 standard [ranges] providing access
    /// to values of a type convertible to `value_type`, and `value_type` must
    /// be `emplace-constructible` from `*i` into this map, where `i` is a
    /// dereferenceable iterator obtained from `range` (see {Requirements on
    /// `KEY`}).  The behavior is undefined if `range` overlaps this multiset.
    template <class RANGE>
    BSLSTL_MULTISET_REQUIRES_CONTAINER_COMPATIBLE_RANGE(RANGE, value_type)
    void insert_range(BSLS_COMPILERFEATURES_FORWARD_REF(RANGE) range)
    {
        // Defined inline to avoid Windows errors.

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)
        if constexpr (ranges::sized_range<RANGE>) {
            insertFromRange(bsl::ranges::begin(range),
                            bsl::ranges::end  (range),
                            bsl::ranges::size (range));
        } else // ...
#endif
        {
            insertFromRange(bsl::ranges::begin(range),
                            bsl::ranges::end  (range));
        }
    }

#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl bslstl_multiset.h
#ifndef BSLSTL_MULTISET_VARIADIC_LIMIT
#define BSLSTL_MULTISET_VARIADIC_LIMIT 10
#endif
#ifndef BSLSTL_MULTISET_VARIADIC_LIMIT_A
#define BSLSTL_MULTISET_VARIADIC_LIMIT_A BSLSTL_MULTISET_VARIADIC_LIMIT
#endif
#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 0
    iterator emplace();
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 0

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 1
    template <class Args_01>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 1

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 2
    template <class Args_01,
              class Args_02>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 2

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 3
    template <class Args_01,
              class Args_02,
              class Args_03>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 3

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 4
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 4

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 5
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 5

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 6
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 6

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 7
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 7

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 8
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 8

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 9
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08,
              class Args_09>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 9

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 10
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08,
              class Args_09,
              class Args_10>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09,
                     BSLS_COMPILERFEATURES_FORWARD_REF(Args_10) args_10);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 10


#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 0
    iterator emplace_hint(const_iterator hint);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 0

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 1
    template <class Args_01>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 1

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 2
    template <class Args_01,
              class Args_02>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 2

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 3
    template <class Args_01,
              class Args_02,
              class Args_03>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 3

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 4
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 4

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 5
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 5

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 6
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 6

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 7
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 7

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 8
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 8

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 9
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08,
              class Args_09>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 9

#if BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 10
    template <class Args_01,
              class Args_02,
              class Args_03,
              class Args_04,
              class Args_05,
              class Args_06,
              class Args_07,
              class Args_08,
              class Args_09,
              class Args_10>
    iterator emplace_hint(const_iterator hint,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09,
                           BSLS_COMPILERFEATURES_FORWARD_REF(Args_10) args_10);
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_A >= 10

#else
// The generated code below is a workaround for the absence of perfect
// forwarding in some compilers.
    template <class... Args>
    iterator emplace(BSLS_COMPILERFEATURES_FORWARD_REF(Args)... args);

    template <class... Args>
    iterator emplace_hint(const_iterator hint,
                              BSLS_COMPILERFEATURES_FORWARD_REF(Args)... args);

// }}} END GENERATED CODE
#endif

    /// Remove from this multiset the `value_type` object at the specified
    /// `position`, and return an iterator referring to the 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 multiset.   This method invalidates
    /// only iterators and references to the removed element and previously
    /// saved values of the `end()` iterator.  The behavior is undefined
    /// unless `position` refers to a `value_type` object in this multiset.
    iterator erase(const_iterator position);

    /// Remove from this multiset all `value_type` objects equivalent to the
    /// specified `key`, if they exist, and return the number of erased
    /// objects; otherwise, if there are no `value_type` objects equivalent
    /// to `key`, return 0 with no other effect.   This method invalidates
    /// only iterators and references to the removed element and previously
    /// saved values of the `end()` iterator.
    size_type erase(const key_type& key);
    template <class t_KEY>
    typename enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   t_KEY>::value &&
        !is_convertible<BSLS_COMPILERFEATURES_FORWARD_REF(t_KEY),
                        iterator>::value &&
        !is_convertible<BSLS_COMPILERFEATURES_FORWARD_REF(t_KEY),
                        const_iterator>::value,
    size_type>::type erase(BSLS_COMPILERFEATURES_FORWARD_REF(t_KEY) key)
    {
        // Implemented inline due to Sun CC compilation error.
        size_type count = 0;
        iterator  it    = this->lower_bound(key);
        while (it != end() && !key_comp()(key, *it)) {
                           // !(*it > key)
            it = erase(it);
            count++;
        }
        return count;
    }

    /// Remove from this multiset the `value_type` objects starting at the
    /// specified `first` position up to, but not including the specified
    /// `last` position, and return `last`.   This method invalidates only
    /// iterators and references to the removed element and previously saved
    /// values of the `end()` iterator.  The behavior is undefined unless
    /// `first` and `last` either refer to elements in this multiset or are
    /// the `end` iterator, and the `first` position is at or before the
    /// `last` position in the ordered sequence provided by this container.
    iterator erase(const_iterator first, const_iterator last);

    void swap(multiset& other) BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                 AllocatorTraits::is_always_equal::value
                              && bsl::is_nothrow_swappable<COMPARATOR>::value);
        // Exchange the value and comparator of this object with those of the
        // specified 'other' object; also exchange the allocator of this object
        // with that of 'other' if the (template parameter) type 'ALLOCATOR'
        // has the 'propagate_on_container_swap' trait, and do not modify
        // either allocator otherwise.  This method provides the no-throw
        // exception-safety guarantee if and only if the (template parameter)
        // type 'COMPARATOR' provides a no-throw swap operation, and provides
        // the basic exception-safety guarantee otherwise; if an exception is
        // thrown, both objects are left in valid but unspecified states.  This
        // operation has 'O[1]' complexity if either this object was created
        // with the same allocator as 'other' or 'ALLOCATOR' has the
        // 'propagate_on_container_swap' trait; otherwise, it has 'O[n + m]'
        // complexity, where 'n' and 'm' are the number of elements in this
        // object and 'other', respectively.  Note that this method's support
        // for swapping objects created with different allocators when
        // 'ALLOCATOR' does not have the 'propagate_on_container_swap' trait is
        // a departure from the C++ Standard.

    /// Remove all entries from this multiset.  Note that the multiset is
    /// empty after this call, but allocated memory may be retained for
    /// future use.
    void clear() BSLS_KEYWORD_NOEXCEPT;

    // Turn off complaints about necessarily class-defined methods.
    // BDE_VERIFY pragma: push
    // BDE_VERIFY pragma: -CD01

    /// Return an iterator providing modifiable access to the first
    /// `value_type` object in this multiset equivalent to the specified
    /// `key`, if such an object exists, and the past-the-end (`end`)
    /// iterator otherwise.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    iterator find(const key_type& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::find(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing modifiable access to the first
    /// `value_type` object in this multiset equivalent to the specified
    /// `key`, if such an object exists, and the past-the-end (`end`)
    /// iterator otherwise.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        iterator>::type
    find(const LOOKUP_KEY& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::find(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing modifiable access to the first (i.e.,
    /// ordered least) `value_type` object in this multiset greater-than or
    /// equal-to the specified `key`, and the past-the-end iterator if this
    /// multiset does not contain a `value_type` object greater-than or
    /// equal-to `key`.  Note that this function returns the *first*
    /// position before which a `value_type` object equivalent to `key`
    /// could be inserted into the ordered sequence maintained by this
    /// multiset, while preserving its ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    iterator lower_bound(const key_type& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::lowerBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing modifiable access to the first (i.e.,
    /// ordered least) `value_type` object in this multiset greater-than or
    /// equal-to the specified `key`, and the past-the-end iterator if this
    /// multiset does not contain a `value_type` object greater-than or
    /// equal-to `key`.  Note that this function returns the *first*
    /// position before which a `value_type` object equivalent to `key`
    /// could be inserted into the ordered sequence maintained by this
    /// multiset, while preserving its ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        iterator>::type
    lower_bound(const LOOKUP_KEY& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::lowerBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing modifiable access to the first (i.e.,
    /// ordered least) `value_type` object in this multiset greater than the
    /// specified `key`, and the past-the-end iterator if this multiset does
    /// not contain a `value_type` object greater-than `key`.  Note that
    /// this function returns the *last* position before which a
    /// `value_type` object equivalent to `key` could be inserted into the
    /// ordered sequence maintained by this multiset, while preserving its
    /// ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    iterator upper_bound(const key_type& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::upperBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing modifiable access to the first (i.e.,
    /// ordered least) `value_type` object in this multiset greater than the
    /// specified `key`, and the past-the-end iterator if this multiset does
    /// not contain a `value_type` object greater-than `key`.  Note that
    /// this function returns the *last* position before which a
    /// `value_type` object equivalent to `key` could be inserted into the
    /// ordered sequence maintained by this multiset, while preserving its
    /// ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        iterator>::type
    upper_bound(const LOOKUP_KEY& key)
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::upperBound(
            d_tree, this->comparator(), key));
    }

    /// Return a pair of iterators providing modifiable access to the
    /// sequence of `value_type` objects in this multiset equivalent to the
    /// specified `key`, where the first iterator is positioned at the start
    /// of the sequence and the second is positioned one past the end of the
    /// sequence.  The first returned iterator will be `lower_bound(key)`,
    /// the second returned iterator will be `upper_bound(key)`, and, if
    /// this multiset contains no `value_type` objects with an equivalent
    /// key, then the two returned iterators will have the same value.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    pair<iterator, iterator> equal_range(const key_type& key)
    {
        iterator startIt = lower_bound(key);
        iterator endIt   = startIt;

        if (endIt != end() && !comparator()(key, *endIt.node())) {
            endIt = upper_bound(key);
        }
        return pair<iterator, iterator>(startIt, endIt);
    }

    /// Return a pair of iterators providing modifiable access to the
    /// sequence of `value_type` objects in this multiset equivalent to the
    /// specified `key`, where the first iterator is positioned at the start
    /// of the sequence and the second is positioned one past the end of the
    /// sequence.  The first returned iterator will be `lower_bound(key)`,
    /// the second returned iterator will be `upper_bound(key)`, and, if
    /// this multiset contains no `value_type` objects with an equivalent
    /// key, then the two returned iterators will have the same value.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        pair<iterator, iterator> >::type
    equal_range(const LOOKUP_KEY& key)
    {
        iterator startIt = lower_bound(key);
        iterator endIt   = startIt;
        if (endIt != end() && !comparator()(key, *endIt.node())) {
            endIt = upper_bound(key);
        }
        return pair<iterator, iterator>(startIt, endIt);
    }

    // BDE_VERIFY pragma: pop

    // ACCESSORS

    /// Return (a copy of) the allocator used for memory allocation by this
    /// multiset.
    allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT;

    /// Return an iterator providing non-modifiable access to the first
    /// `value_type` object in the ordered sequence of `value_type` objects
    /// maintained by this multiset, or the `end` iterator if this multiset
    /// is empty.
    const_iterator begin() const BSLS_KEYWORD_NOEXCEPT;

    /// Return an iterator providing non-modifiable access to the
    /// past-the-end element in the ordered sequence of `value_type` objects
    /// maintained by this multiset.
    const_iterator end() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing non-modifiable access to the
    /// last `value_type` object in the ordered sequence of `value_type`
    /// objects maintained by this multiset, or `rend` if this multiset is
    /// empty.
    const_reverse_iterator rbegin() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing non-modifiable access to the
    /// prior-to-the-beginning element in the ordered sequence of
    /// `value_type` objects maintained by this multiset.
    const_reverse_iterator rend() const BSLS_KEYWORD_NOEXCEPT;

    /// Return an iterator providing non-modifiable access to the first
    /// `value_type` object in the ordered sequence of `value_type` objects
    /// maintained by this multiset, or the `end` iterator if this multiset
    /// is empty.
    const_iterator cbegin() const BSLS_KEYWORD_NOEXCEPT;

    /// Return an iterator providing non-modifiable access to the
    /// past-the-end element in the ordered sequence of `value_type` objects
    /// maintained by this multiset.
    const_iterator cend() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing non-modifiable access to the
    /// last `value_type` object in the ordered sequence of `value_type`
    /// objects maintained by this multiset, or `rend` if this multiset is
    /// empty.
    const_reverse_iterator crbegin() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a reverse iterator providing non-modifiable access to the
    /// prior-to-the-beginning element in the ordered sequence of
    /// `value_type` objects maintained by this multiset.
    const_reverse_iterator crend() const BSLS_KEYWORD_NOEXCEPT;

    /// Return `true` if this map contains an element whose key is
    /// equivalent to the specified `key`.
    bool contains(const key_type &key) const;

    /// Return `true` if this map contains an element whose key is
    /// equivalent to the specified `key`.
    ///
    /// Note: implemented inline due to Sun CC compilation error
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        bool>::type
    contains(const LOOKUP_KEY& key) const
    {
        return find(key) != end();
    }

    /// Return `true` if this multiset contains no elements, and `false`
    /// otherwise.
    bool empty() const BSLS_KEYWORD_NOEXCEPT;

    /// Return the number of elements in this multiset.
    size_type size() const BSLS_KEYWORD_NOEXCEPT;

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

    /// Return the key-comparison functor (or function pointer) used by this
    /// multiset; if a comparator was supplied at construction, return its
    /// value, otherwise return a default constructed `key_compare` object.
    /// Note that this comparator compares objects of type `KEY`, which is
    /// the type of the `value_type` objects contained in this multiset.
    key_compare key_comp() const;

    /// Return a functor for comparing two `value_type` objects using
    /// `key_comp()`.  Note that since `value_type` is an alias to `KEY` for
    /// `multiset`, this method returns the same functor as `key_comp()`.
    value_compare value_comp() const;

    // Turn off complaints about necessarily class-defined methods.
    // BDE_VERIFY pragma: push
    // BDE_VERIFY pragma: -CD01

    /// Return an iterator providing non-modifiable access to the first
    /// `value_type` object that is equivalent to the specified `key` in
    /// ordered sequence maintained by this multiset, if such an object
    /// exists, and the past-the-end (`end`) iterator otherwise.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    const_iterator find(const key_type& key) const
    {
        return const_iterator(BloombergLP::bslalg::RbTreeUtil::find(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing non-modifiable access to the first
    /// `value_type` object that is equivalent to the specified `key` in
    /// ordered sequence maintained by this multiset, if such an object
    /// exists, and the past-the-end (`end`) iterator otherwise.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        const_iterator>::type
    find(const LOOKUP_KEY& key) const
    {
        return const_iterator(BloombergLP::bslalg::RbTreeUtil::find(
            d_tree, this->comparator(), key));
    }

    /// Return the number of `value_type` objects within this multiset that
    /// are equivalent to the specified `key`.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    size_type count(const key_type& key) const
    {
        int            count = 0;
        const_iterator it    = lower_bound(key);

        while (it != end() && !comparator()(key, *it.node())) {
            ++it;
            ++count;
        }
        return count;
    }

    /// Return the number of `value_type` objects within this multiset that
    /// are equivalent to the specified `key`.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        size_type>::type
    count(const LOOKUP_KEY& key) const
    {
        int            count = 0;
        const_iterator it    = lower_bound(key);

        while (it != end() && !comparator()(key, *it.node())) {
            ++it;
            ++count;
        }
        return count;
    }

    /// Return an iterator providing non-modifiable access to the first
    /// (i.e., ordered least) `value_type` object in this multiset
    /// greater-than or equal-to the specified `key`, and the past-the-end
    /// iterator if this multiset does not contain a `value_type`
    /// greater-than or equal-to `key`.  Note that this function returns the
    /// *first* position before which a `value_type` object equivalent to
    /// `key` could be inserted into the ordered sequence maintained by this
    /// multiset, while preserving its ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    const_iterator lower_bound(const key_type& key) const
    {
        return iterator(BloombergLP::bslalg::RbTreeUtil::lowerBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing non-modifiable access to the first
    /// (i.e., ordered least) `value_type` object in this multiset
    /// greater-than or equal-to the specified `key`, and the past-the-end
    /// iterator if this multiset does not contain a `value_type`
    /// greater-than or equal-to `key`.  Note that this function returns the
    /// *first* position before which a `value_type` object equivalent to
    /// `key` could be inserted into the ordered sequence maintained by this
    /// multiset, while preserving its ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        const_iterator>::type
    lower_bound(const LOOKUP_KEY& key) const
    {
        return const_iterator(BloombergLP::bslalg::RbTreeUtil::lowerBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing non-modifiable access to the first
    /// (i.e., ordered least) `value_type` object in this multiset greater
    /// than the specified `key`, and the past-the-end iterator if this
    /// multiset does not contain a `value_type` object greater-than `key`.
    /// Note that this function returns the *last* position before which a
    /// `value_type` object equivalent to `key` could be inserted into the
    /// ordered sequence maintained by this multiset, while preserving its
    /// ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    const_iterator upper_bound(const key_type& key) const
    {
        return const_iterator(BloombergLP::bslalg::RbTreeUtil::upperBound(
            d_tree, this->comparator(), key));
    }

    /// Return an iterator providing non-modifiable access to the first
    /// (i.e., ordered least) `value_type` object in this multiset greater
    /// than the specified `key`, and the past-the-end iterator if this
    /// multiset does not contain a `value_type` object greater-than `key`.
    /// Note that this function returns the *last* position before which a
    /// `value_type` object equivalent to `key` could be inserted into the
    /// ordered sequence maintained by this multiset, while preserving its
    /// ordering.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        const_iterator>::type
    upper_bound(const LOOKUP_KEY& key) const
    {
        return const_iterator(BloombergLP::bslalg::RbTreeUtil::upperBound(
            d_tree, this->comparator(), key));
    }

    /// Return a pair of iterators providing non-modifiable access to the
    /// sequence of `value_type` objects in this multiset that are
    /// equivalent to the specified `key`, where the first iterator is
    /// positioned at the start of the sequence, and the second is
    /// positioned one past the end of the sequence.  The first returned
    /// iterator will be `lower_bound(key)`; the second returned iterator
    /// will be `upper_bound(key)`; and, if this multiset contains no
    /// `value_type` objects equivalent to `key`, then the two returned
    /// iterators will have the same value.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    pair<const_iterator, const_iterator> equal_range(const key_type& key) const
    {
        const_iterator startIt = lower_bound(key);
        const_iterator endIt   = startIt;

        if (endIt != end() && !comparator()(key, *endIt.node())) {
            endIt = upper_bound(key);
        }
        return pair<const_iterator, const_iterator>(startIt, endIt);
    }

    /// Return a pair of iterators providing non-modifiable access to the
    /// sequence of `value_type` objects in this multiset that are
    /// equivalent to the specified `key`, where the first iterator is
    /// positioned at the start of the sequence, and the second is
    /// positioned one past the end of the sequence.  The first returned
    /// iterator will be `lower_bound(key)`; the second returned iterator
    /// will be `upper_bound(key)`; and, if this multiset contains no
    /// `value_type` objects equivalent to `key`, then the two returned
    /// iterators will have the same value.
    ///
    /// Note: implemented inline due to Sun CC compilation error.
    template <class LOOKUP_KEY>
    typename bsl::enable_if<
        BloombergLP::bslmf::IsTransparentPredicate<COMPARATOR,
                                                   LOOKUP_KEY>::value,
        pair<const_iterator, const_iterator> >::type
    equal_range(const LOOKUP_KEY& key) const
    {
        const_iterator startIt = lower_bound(key);
        const_iterator endIt   = startIt;
        if (endIt != end() && !comparator()(key, *endIt.node())) {
            endIt = upper_bound(key);
        }
        return pair<const_iterator, const_iterator>(startIt, endIt);
    }

    // BDE_VERIFY pragma: pop
};

#ifdef BSLS_COMPILERFEATURES_SUPPORT_CTAD
// CLASS TEMPLATE DEDUCTION GUIDES

/// Deduce the template parameter `KEY` from the `value_type` of the
/// iterators supplied to the constructor of `multiset`.  Deduce the
/// template parameters `COMPARATOR` and `ALLOCATOR` from the other
/// parameters passed to the constructor.  This guide does not participate
/// unless the supplied (or defaulted) `ALLOCATOR` meets the requirements of
/// a standard allocator.
template <
    class INPUT_ITERATOR,
    class KEY =
         typename BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class COMPARATOR = std::less<KEY>,
    class ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<!bsl::IsStdAllocator_v<COMPARATOR>>,
    class = bsl::enable_if_t<bsl::IsStdAllocator_v<ALLOCATOR>>
    >
multiset(INPUT_ITERATOR,
         INPUT_ITERATOR,
         COMPARATOR = COMPARATOR(),
         ALLOCATOR = ALLOCATOR())
-> multiset<KEY, COMPARATOR, ALLOCATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// iterators supplied to the constructor of `multiset`.  Deduce the
/// template parameter `COMPARATOR` from the other parameter passed to the
/// constructor.  This deduction guide does not participate unless the
/// specified `ALLOC` is convertible to `bsl::allocator<KEY>`.
template <
    class INPUT_ITERATOR,
    class COMPARATOR,
    class ALLOC,
    class KEY =
         typename BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class DEFAULT_ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
multiset(INPUT_ITERATOR, INPUT_ITERATOR, COMPARATOR, ALLOC *)
-> multiset<KEY, COMPARATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// iterators supplied to the constructor of `multiset`.  Deduce the
/// template parameter `ALLOCATOR` from the other parameter passed to the
/// constructor.  This deduction guide does not participate unless the
/// supplied allocator meets the requirements of a standard allocator.
template <
    class INPUT_ITERATOR,
    class ALLOCATOR,
    class KEY =
         typename BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class = bsl::enable_if_t<bsl::IsStdAllocator_v<ALLOCATOR>>
    >
multiset(INPUT_ITERATOR, INPUT_ITERATOR, ALLOCATOR)
-> multiset<KEY, std::less<KEY>, ALLOCATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// iterators supplied to the constructor of `multiset`.  This deduction
/// guide does not participate unless the specified `ALLOC` is convertible
/// to `bsl::allocator<KEY>`.
template <
    class INPUT_ITERATOR,
    class ALLOC,
    class KEY =
         typename BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class DEFAULT_ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
multiset(INPUT_ITERATOR, INPUT_ITERATOR, ALLOC *)
-> multiset<KEY>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// initializer_list supplied to the constructor of `multiset`.  Deduce the
/// template parameters `COMPARATOR` and `ALLOCATOR` from the other
/// parameters passed to the constructor.
template <
    class KEY,
    class COMPARATOR = std::less<KEY>,
    class ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<!bsl::IsStdAllocator_v<COMPARATOR>>,
    class = bsl::enable_if_t<bsl::IsStdAllocator_v<ALLOCATOR>>
    >
multiset(std::initializer_list<KEY>,
         COMPARATOR = COMPARATOR(),
         ALLOCATOR = ALLOCATOR())
-> multiset<KEY, COMPARATOR, ALLOCATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// initializer_list supplied to the constructor of `multiset`.  Deduce the
/// template parameter `COMPARATOR` from the other parameter passed to the
/// constructor.  This deduction guide does not participate unless the
/// specified `ALLOC` is convertible to `bsl::allocator<KEY>`.
template <
    class KEY,
    class COMPARATOR,
    class ALLOC,
    class DEFAULT_ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
multiset(std::initializer_list<KEY>, COMPARATOR, ALLOC *)
-> multiset<KEY, COMPARATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// initializer_list supplied to the constructor of `multiset`.  Deduce the
/// template parameter `ALLOCATOR` from the other parameter passed to the
/// constructor.
template <
    class KEY,
    class ALLOCATOR,
    class = bsl::enable_if_t<bsl::IsStdAllocator_v<ALLOCATOR>>
    >
multiset(std::initializer_list<KEY>, ALLOCATOR)
-> multiset<KEY, std::less<KEY>, ALLOCATOR>;

/// Deduce the template parameter `KEY` from the `value_type` of the
/// initializer_list supplied to the constructor of `multiset`.  This
/// deduction guide does not participate unless the specified `ALLOC` is
/// convertible to `bsl::allocator<KEY>`.
template <
    class KEY,
    class ALLOC,
    class DEFAULT_ALLOCATOR = bsl::allocator<KEY>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
multiset(std::initializer_list<KEY>, ALLOC *)
-> multiset<KEY>;

#endif

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` objects have the same
/// value, and `false` otherwise.  Two `multiset` objects `lhs` and `rhs`
/// have the same value if they have the same number of keys, and each
/// element in the ordered sequence of keys of `lhs` has the same value as
/// the corresponding element in the ordered sequence of keys of `rhs`.
/// This method requires that the (template parameter) type `KEY` be
/// `equality-comparable` (see {Requirements on `KEY`}).
template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator==(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);

#ifndef BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON
template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator!=(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'multiset' objects 'lhs' and
    // 'rhs' do not have the same value if they do not have the same number of
    // keys, or some element in the ordered sequence of keys of 'lhs' does not
    // have the same value as the corresponding element in the ordered sequence
    // of keys of 'rhs'.  This method requires that the (template parameter)
    // type 'KEY' be 'equality-comparable' (see {Requirements on 'KEY'}).
#endif

#ifdef BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

/// Perform a lexicographic three-way comparison of the specified `lhs` and
/// the specified `rhs` multisets by using the comparison operators of `KEY`
/// on each element; return the result of that comparison.
template <class KEY, class COMPARATOR, class ALLOCATOR>
BloombergLP::bslalg::SynthThreeWayUtil::Result<KEY>
operator<=>(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
            const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);

#else

template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator< (const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' multiset is
    // lexicographically less than that of the specified 'rhs' multiset, and
    // 'false' otherwise.  Given iterators 'i' and 'j' over the respective
    // sequences '[lhs.begin() .. lhs.end())' and '[rhs.begin() .. rhs.end())',
    // the value of multiset 'lhs' is lexicographically less than that of
    // multiset 'rhs' if 'true == *i < *j' for the first pair of corresponding
    // iterator positions where '*i < *j' and '*j < *i' are not both 'false'.
    // If no such corresponding iterator position exists, the value of 'lhs' is
    // lexicographically less than that of 'rhs' if 'lhs.size() < rhs.size()'.
    // This method requires that 'operator<', inducing a total order, be
    // defined for 'value_type'.

template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator> (const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' multiset is
    // lexicographically greater than that of the specified 'rhs' multiset, and
    // 'false' otherwise.  The value of multiset 'lhs' is lexicographically
    // greater than that of multiset 'rhs' if 'rhs' is lexicographically less
    // than 'lhs' (see 'operator<').  This method requires that 'operator<',
    // inducing a total order, be defined for 'value_type'.  Note that this
    // operator returns 'rhs < lhs'.

template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator<=(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' multiset is
    // lexicographically less than or equal to that of the specified 'rhs'
    // multiset, and 'false' otherwise.  The value of multiset 'lhs' is
    // lexicographically less than or equal to that of multiset 'rhs' if 'rhs'
    // is not lexicographically less than 'lhs' (see 'operator<').  This method
    // requires that 'operator<', inducing a total order, be defined for
    // 'value_type'.  Note that this operator returns '!(rhs < lhs)'.

template <class KEY, class COMPARATOR, class ALLOCATOR>
bool operator>=(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' multiset is
    // lexicographically greater than or equal to that of the specified 'rhs'
    // multiset, and 'false' otherwise.  The value of multiset 'lhs' is
    // lexicographically greater than or equal to that of multiset 'rhs' if
    // 'lhs' is not lexicographically less than 'rhs' (see 'operator<').  This
    // method requires that 'operator<', inducing a total order, be defined for
    // 'value_type'.  Note that this operator returns '!(lhs < rhs)'.

#endif  // BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

// FREE FUNCTIONS

/// Erase all the elements in the specified multiset `ms` that satisfy the
/// specified predicate `predicate`.  Return the number of elements erased.
template <class KEY, class COMPARATOR, class ALLOCATOR, class PREDICATE>
typename multiset<KEY, COMPARATOR, ALLOCATOR>::size_type
erase_if(multiset<KEY, COMPARATOR, ALLOCATOR>& ms, PREDICATE predicate);

template <class KEY, class COMPARATOR, class ALLOCATOR>
void swap(multiset<KEY, COMPARATOR, ALLOCATOR>& a,
          multiset<KEY, COMPARATOR, ALLOCATOR>& b)
                                    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                    BSLS_KEYWORD_NOEXCEPT_OPERATOR(a.swap(b)));
    // Exchange the value and comparator of the specified 'a' object with those
    // of the specified 'b' object; also exchange the allocator of 'a' with
    // that of 'b' if the (template parameter) type 'ALLOCATOR' has the
    // 'propagate_on_container_swap' trait, and do not modify either allocator
    // otherwise.  This function provides the no-throw exception-safety
    // guarantee if and only if the (template parameter) type 'COMPARATOR'
    // provides a no-throw swap operation, and provides the basic
    // exception-safety guarantee otherwise; if an exception is thrown, both
    // objects are left in valid but unspecified states.  This operation has
    // 'O[1]' complexity if either 'a' was created with the same allocator as
    // 'b' or 'ALLOCATOR' has the 'propagate_on_container_swap' trait;
    // otherwise, it has 'O[n + m]' complexity, where 'n' and 'm' are the
    // number of elements in 'a' and 'b', respectively.  Note that this
    // function's support for swapping objects created with different
    // allocators when 'ALLOCATOR' does not have the
    // 'propagate_on_container_swap' trait is a departure from the C++
    // Standard.

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

                             // -----------------
                             // class DataWrapper
                             // -----------------

// CREATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::DataWrapper::DataWrapper(
                                              const COMPARATOR& comparator,
                                              const ALLOCATOR&  basicAllocator)
: ::bsl::multiset<KEY, COMPARATOR, ALLOCATOR>::Comparator(comparator)
, d_pool(basicAllocator)
{
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::DataWrapper::DataWrapper(
                          BloombergLP::bslmf::MovableRef<DataWrapper> original)
: ::bsl::multiset<KEY, COMPARATOR, ALLOCATOR>::Comparator(
                                    MoveUtil::access(original).keyComparator())
, d_pool(MoveUtil::move(MoveUtil::access(original).d_pool))
{
}

// MANIPULATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::NodeFactory&
multiset<KEY, COMPARATOR, ALLOCATOR>::DataWrapper::nodeFactory()
{
    return d_pool;
}

// ACCESSORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
const typename multiset<KEY, COMPARATOR, ALLOCATOR>::NodeFactory&
multiset<KEY, COMPARATOR, ALLOCATOR>::DataWrapper::nodeFactory() const
{
    return d_pool;
}
                             // --------------
                             // class multiset
                             // --------------

// PRIVATE MANIPULATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::Comparator&
multiset<KEY, COMPARATOR, ALLOCATOR>::comparator()
{
    return d_compAndAlloc;
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::NodeFactory&
multiset<KEY, COMPARATOR, ALLOCATOR>::nodeFactory()
{
    return d_compAndAlloc.nodeFactory();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::quickSwapExchangeAllocators(
                                                               multiset& other)
{
    BloombergLP::bslalg::RbTreeUtil::swap(&d_tree, &other.d_tree);
    nodeFactory().swapExchangeAllocators(other.nodeFactory());

    // 'DataWrapper' contains a 'NodeFactory' object and inherits from
    // 'Comparator'.  If the empty-base-class optimization has been applied to
    // 'Comparator', then we must not call 'swap' on it because
    // 'sizeof(Comparator) > 0' and, therefore, we will incorrectly swap bytes
    // of the 'NodeFactory' members!

    if (sizeof(NodeFactory) != sizeof(DataWrapper)) {
        comparator().swap(other.comparator());
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::quickSwapRetainAllocators(
                                                               multiset& other)
{
    BloombergLP::bslalg::RbTreeUtil::swap(&d_tree, &other.d_tree);
    nodeFactory().swapRetainAllocators(other.nodeFactory());

    // See 'quickSwapExchangeAllocators' (above).

    if (sizeof(NodeFactory) != sizeof(DataWrapper)) {
        comparator().swap(other.comparator());
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR, class SENTINEL>
inline
void
multiset<KEY, COMPARATOR, ALLOCATOR>::constructFromRange(INPUT_ITERATOR first,
                                                         SENTINEL       last)
{
    if (first == last) {
        return;                                                       // RETURN
    }

    if BSLS_KEYWORD_CONSTEXPR_CPP17 (
        BloombergLP::bslstl::IteratorUtil::
                      canCalculateInsertDistance<INPUT_ITERATOR, SENTINEL>()) {
        const size_type numElements = static_cast<size_type>(
            BloombergLP::bslstl::IteratorUtil::insertDistance(first, last));
        nodeFactory().reserveNodes(numElements);
    }

    BloombergLP::bslalg::RbTreeUtilTreeProctor<NodeFactory> proctor(
                                                               &d_tree,
                                                               &nodeFactory());

    // The following loop guarantees amortized linear time to insert an ordered
    // sequence of values (as required by the standard).   If the values are
    // in sorted order, we are guaranteed the next node can be inserted as the
    // right child of the previous node, and can call 'insertAt' without
    // 'findUniqueInsertLocation'.

    insert(*first);
    BloombergLP::bslalg::RbTreeNode *prevNode = d_tree.rootNode();

    while (++first != last) {

        const value_type& value = *first;
        if (this->comparator()(value, *prevNode)) {
            // The values are not in order, so insert them normally.
            insert(value);
            insertFromRange(++first, last);
            break;
        }

        if (this->comparator()(*prevNode, value)) {
            BloombergLP::bslalg::RbTreeNode *node =
                                   nodeFactory().emplaceIntoNewNode(value);
            BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                                      prevNode,
                                                      false,
                                                      node);
            prevNode = node;
        }
    }

    proctor.release();
}

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR, class SENTINEL>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::constructFromRange(
                                                    INPUT_ITERATOR first,
                                                    SENTINEL       last,
                                                    size_t         numElements)

{
    BSLS_ASSERT_SAFE((
        !BloombergLP::bslstl::IteratorUtil
                       ::canCalculateInsertDistance<INPUT_ITERATOR, SENTINEL>()
        || numElements == static_cast<size_type>(
             BloombergLP::bslstl::IteratorUtil::insertDistance(first, last))));

    if (first == last) {
        return;                                                       // RETURN
    }

    if (0 < numElements) {
        nodeFactory().reserveNodes(numElements);
    }

    BloombergLP::bslalg::RbTreeUtilTreeProctor<NodeFactory> proctor(
                                                               &d_tree,
                                                               &nodeFactory());

    // The following loop guarantees amortized linear time to insert an ordered
    // sequence of values (as required by the standard).   If the values are
    // in sorted order, we are guaranteed the next node can be inserted as the
    // right child of the previous node, and can call 'insertAt' without
    // 'findUniqueInsertLocation'.

    insert(*first); --numElements;
    BloombergLP::bslalg::RbTreeNode *prevNode = d_tree.rootNode();

    while (++first != last) {

        const value_type& value = *first;
        if (this->comparator()(value, *prevNode)) {
            // The values are not in order, so insert them normally.
            insert(value); --numElements;
            insertFromRange(++first, last, numElements);
            break;
        }

        if (this->comparator()(*prevNode, value)) {
            BloombergLP::bslalg::RbTreeNode *node =
                                   nodeFactory().emplaceIntoNewNode(value);
            BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                                      prevNode,
                                                      false,
                                                      node);
            --numElements;
            prevNode = node;
        }
    }

    proctor.release();
}

#endif

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR, class SENTINEL>
inline
void
multiset<KEY, COMPARATOR, ALLOCATOR>::insertFromRange(INPUT_ITERATOR first,
                                                      SENTINEL       last)
{
    ///Implementation Notes
    ///--------------------
    // First, consume currently held free nodes.  Free nodes may be available
    // from previous insertions that where skipped due to collisions with
    // keys already in the map or from nodes reserved in `constructFromRange`.
    //
    // If those nodes are insufficient *and* one can calculate the remaining
    // number of elements, then reserve exactly that many free nodes.  There is
    // no more than one call to 'reserveNodes' per invocation of this method,
    // hence the use of 'BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY'.
    //
    // When reserving nodes, we assume the elements remaining to be inserted
    // have unique keys that do not duplicate any keys already in the container
    // If there are any duplicates, this container will have free nodes on
    // return from this method.

    while (first != last) {

        if (BloombergLP::bslstl::IteratorUtil
                       ::canCalculateInsertDistance<INPUT_ITERATOR, SENTINEL>()
        && BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
                                              !nodeFactory().hasFreeNodes())) {
            nodeFactory().reserveNodes(
               BloombergLP::bslstl::IteratorUtil::insertDistance(first, last));
        }

        insert(*first);
        ++first;
    }
}

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS) \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP20_RANGES)

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR, class SENTINEL>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::insertFromRange(
                                                    INPUT_ITERATOR first,
                                                    SENTINEL       last,
                                                    size_t         numElements)
{
    BSLS_ASSERT_SAFE((
        !BloombergLP::bslstl::IteratorUtil
                       ::canCalculateInsertDistance<INPUT_ITERATOR, SENTINEL>()
        || numElements == static_cast<size_type>(
             BloombergLP::bslstl::IteratorUtil::insertDistance(first, last))));

    while (first != last) {

        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
                                              !nodeFactory().hasFreeNodes())) {
            nodeFactory().reserveNodes(numElements);
        }

        insert(*first);
        --numElements;
        ++first;
    }
}

#endif

// PRIVATE ACCESSORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
const typename multiset<KEY, COMPARATOR, ALLOCATOR>::Comparator&
multiset<KEY, COMPARATOR, ALLOCATOR>::comparator() const
{
    return d_compAndAlloc;
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
const typename multiset<KEY, COMPARATOR, ALLOCATOR>::NodeFactory&
multiset<KEY, COMPARATOR, ALLOCATOR>::nodeFactory() const
{
    return d_compAndAlloc.nodeFactory();
}

// CREATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset()
: d_compAndAlloc(COMPARATOR(), ALLOCATOR())
, d_tree()
{
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(const ALLOCATOR& basicAllocator)
: d_compAndAlloc(COMPARATOR(), basicAllocator)
, d_tree()
{
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(const multiset& original)
: d_compAndAlloc(original.comparator().keyComparator(),
                 AllocatorTraits::select_on_container_copy_construction(
                                           original.nodeFactory().allocator()))
, d_tree()
{
    if (0 < original.size()) {
        nodeFactory().reserveNodes(original.size());
        BloombergLP::bslalg::RbTreeUtil::copyTree(&d_tree,
                                                  original.d_tree,
                                                  &nodeFactory());
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                             BloombergLP::bslmf::MovableRef<multiset> original)
: d_compAndAlloc(MoveUtil::move(MoveUtil::access(original).d_compAndAlloc))
, d_tree()
{
    multiset& lvalue = original;
    BloombergLP::bslalg::RbTreeUtil::swap(&d_tree, &lvalue.d_tree);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(const multiset&  original,
                 const typename type_identity<ALLOCATOR>::type& basicAllocator)
: d_compAndAlloc(original.comparator().keyComparator(), basicAllocator)
, d_tree()
{
    if (0 < original.size()) {
        nodeFactory().reserveNodes(original.size());
        BloombergLP::bslalg::RbTreeUtil::copyTree(&d_tree,
                                                  original.d_tree,
                                                  &nodeFactory());
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                 BloombergLP::bslmf::MovableRef<multiset>       original,
                 const typename type_identity<ALLOCATOR>::type& basicAllocator)
: d_compAndAlloc(MoveUtil::access(original).comparator().keyComparator(),
                 basicAllocator)
, d_tree()
{
    multiset& lvalue = original;

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(
              nodeFactory().allocator() == lvalue.nodeFactory().allocator())) {
        d_compAndAlloc.nodeFactory().adopt(
                          MoveUtil::move(lvalue.d_compAndAlloc.nodeFactory()));
        BloombergLP::bslalg::RbTreeUtil::swap(&d_tree, &lvalue.d_tree);
    }
    else {
        if (0 < lvalue.size()) {
            nodeFactory().reserveNodes(lvalue.size());
            BloombergLP::bslalg::RbTreeUtil::moveTree(&d_tree,
                                                      &lvalue.d_tree,
                                                      &nodeFactory(),
                                                      &lvalue.nodeFactory());
        }
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              const COMPARATOR& comparator,
                                              const ALLOCATOR&  basicAllocator)
: d_compAndAlloc(comparator, basicAllocator)
, d_tree()
{
    if (first != last) {

        const size_type numElements = static_cast<size_type>(
            BloombergLP::bslstl::IteratorUtil::insertDistance(first, last));

        if (0 < numElements) {
            nodeFactory().reserveNodes(numElements);
        }

        BloombergLP::bslalg::RbTreeUtilTreeProctor<NodeFactory> proctor(
                                                               &d_tree,
                                                               &nodeFactory());

        // The following loop guarantees amortized linear time to insert an
        // ordered sequence of values (as required by the standard).   If the
        // values are in sorted order, we are guaranteed the next node can be
        // inserted as the right child of the previous node, and can call
        // 'insertAt' without 'findUniqueInsertLocation'.

        insert(*first);
        BloombergLP::bslalg::RbTreeNode *prevNode = d_tree.rootNode();
        while (++first != last) {
            // The values are not in order, so insert them normally.

            const value_type& value = *first;
            if (this->comparator()(value, *prevNode)) {
                insert(value);
                insert(++first, last);
                break;
            }
            BloombergLP::bslalg::RbTreeNode *node =
                nodeFactory().emplaceIntoNewNode(value);
            BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                                      prevNode,
                                                      false,
                                                      node);
            prevNode = node;
        }

        proctor.release();
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                                              INPUT_ITERATOR    first,
                                              INPUT_ITERATOR    last,
                                              const ALLOCATOR&  basicAllocator)
: d_compAndAlloc(COMPARATOR(), basicAllocator)
, d_tree()
{
    if (first != last) {

        const size_type numElements = static_cast<size_type>(
            BloombergLP::bslstl::IteratorUtil::insertDistance(first, last));

        if (0 < numElements) {
            nodeFactory().reserveNodes(numElements);
        }

        BloombergLP::bslalg::RbTreeUtilTreeProctor<NodeFactory> proctor(
                                                               &d_tree,
                                                               &nodeFactory());

        // The following loop guarantees amortized linear time to insert an
        // ordered sequence of values (as required by the standard).   If the
        // values are in sorted order, we are guaranteed the next node can be
        // inserted as the right child of the previous node, and can call
        // 'insertAt' without 'findUniqueInsertLocation'.

        insert(*first);
        BloombergLP::bslalg::RbTreeNode *prevNode = d_tree.rootNode();
        while (++first != last) {
            // The values are not in order, so insert them normally.

            const value_type& value = *first;
            if (this->comparator()(value, *prevNode)) {
                insert(value);
                insert(++first, last);
                break;
            }
            BloombergLP::bslalg::RbTreeNode *node =
                nodeFactory().emplaceIntoNewNode(value);
            BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                                      prevNode,
                                                      false,
                                                      node);
            prevNode = node;
        }

        proctor.release();
    }
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                                     std::initializer_list<KEY> values,
                                     const COMPARATOR&          comparator,
                                     const ALLOCATOR&           basicAllocator)
: multiset(values.begin(), values.end(), comparator, basicAllocator)
{
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::multiset(
                                     std::initializer_list<KEY> values,
                                     const ALLOCATOR&           basicAllocator)
: multiset(values.begin(), values.end(), COMPARATOR(), basicAllocator)
{
}
#endif

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>::~multiset()
{
    clear();
}

// MANIPULATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>&
multiset<KEY, COMPARATOR, ALLOCATOR>::operator=(const multiset& rhs)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(this != &rhs)) {
        if (AllocatorTraits::propagate_on_container_copy_assignment::value) {
            multiset other(rhs, rhs.nodeFactory().allocator());
            quickSwapExchangeAllocators(other);
        }
        else {
            multiset other(rhs, nodeFactory().allocator());
            quickSwapRetainAllocators(other);
        }
    }
    return *this;
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>&
multiset<KEY, COMPARATOR, ALLOCATOR>::operator=(
                                  BloombergLP::bslmf::MovableRef<multiset> rhs)
                        BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                            AllocatorTraits::is_always_equal::value
                         && std::is_nothrow_move_assignable<COMPARATOR>::value)
{
    multiset& lvalue = rhs;

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(this != &lvalue)) {
        if (nodeFactory().allocator() == lvalue.nodeFactory().allocator()) {
            multiset other(MoveUtil::move(lvalue));
            quickSwapRetainAllocators(other);
        }
        else if (
              AllocatorTraits::propagate_on_container_move_assignment::value) {
            multiset other(MoveUtil::move(lvalue));
            quickSwapExchangeAllocators(other);
        }
        else {
            multiset other(MoveUtil::move(lvalue), nodeFactory().allocator());
            quickSwapRetainAllocators(other);
        }
    }
    return *this;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
multiset<KEY, COMPARATOR, ALLOCATOR>&
multiset<KEY, COMPARATOR, ALLOCATOR>::operator=(
                                             std::initializer_list<KEY> values)
{
    clear();
    insert(values.begin(), values.end());
    return *this;
}
#endif

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::begin() BSLS_KEYWORD_NOEXCEPT
{
    return iterator(d_tree.firstNode());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::end() BSLS_KEYWORD_NOEXCEPT
{
    return iterator(d_tree.sentinel());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::rbegin() BSLS_KEYWORD_NOEXCEPT
{
    return reverse_iterator(end());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::rend() BSLS_KEYWORD_NOEXCEPT
{
    return reverse_iterator(begin());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::insert(const value_type& value)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                                            value);

    BloombergLP::bslalg::RbTreeNode *node =
        nodeFactory().emplaceIntoNewNode(value);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::insert(
                              BloombergLP::bslmf::MovableRef<value_type> value)
{
    value_type& lvalue = value;
    bool        leftChild;

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                                            lvalue);

    BloombergLP::bslalg::RbTreeNode *node =
        nodeFactory().emplaceIntoNewNode(MoveUtil::move(lvalue));

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::insert(const_iterator    hint,
                                             const value_type& value)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                                            value,
                                                            hintNode);

    BloombergLP::bslalg::RbTreeNode *node =
        nodeFactory().emplaceIntoNewNode(value);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::insert(
                              const_iterator                             hint,
                              BloombergLP::bslmf::MovableRef<value_type> value)
{
    value_type& lvalue = value;
    bool        leftChild;

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                                            lvalue,
                                                            hintNode);

    BloombergLP::bslalg::RbTreeNode *node =
        nodeFactory().emplaceIntoNewNode(MoveUtil::move(lvalue));

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class INPUT_ITERATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::insert(INPUT_ITERATOR first,
                                                  INPUT_ITERATOR last)
{
    ///Implementation Notes
    ///--------------------
    // First, consume currently held free nodes.  If those nodes are
    // insufficient *and* one can calculate the remaining number of elements,
    // then reserve exactly that many free nodes.  There is no more than one
    // call to 'reserveNodes' per invocation of this method, hence the use of
    // 'BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY'.

    while (first != last) {
        if (BloombergLP::bslstl::IteratorUtil::
                    canCalculateInsertDistance<INPUT_ITERATOR,INPUT_ITERATOR>()
        && BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
                                              !nodeFactory().hasFreeNodes())) {
            const size_type numElements = static_cast<size_type>(
               BloombergLP::bslstl::IteratorUtil::insertDistance(first, last));

            nodeFactory().reserveNodes(numElements);
        }
        insert(*first);
        ++first;
    }
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::insert(
                                             std::initializer_list<KEY> values)
{
    insert(values.begin(), values.end());
}
#endif

#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl bslstl_multiset.h
#ifndef BSLSTL_MULTISET_VARIADIC_LIMIT
#define BSLSTL_MULTISET_VARIADIC_LIMIT 10
#endif
#ifndef BSLSTL_MULTISET_VARIADIC_LIMIT_B
#define BSLSTL_MULTISET_VARIADIC_LIMIT_B BSLSTL_MULTISET_VARIADIC_LIMIT
#endif
#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 0
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                               )
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                                  );

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 0

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 1
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 1

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 2
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 2

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 3
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 3

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 4
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 4

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 5
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 5

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 6
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 6

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 7
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 7

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 8
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 8

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 9
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08,
          class Args_09>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08),
                               BSLS_COMPILERFEATURES_FORWARD(Args_09,args_09));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 9

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 10
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08,
          class Args_09,
          class Args_10>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_10) args_10)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08),
                               BSLS_COMPILERFEATURES_FORWARD(Args_09,args_09),
                               BSLS_COMPILERFEATURES_FORWARD(Args_10,args_10));

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 10


#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 0
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                                  );

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 0

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 1
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 1

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 2
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 2

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 3
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 3

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 4
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 4

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 5
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 5

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 6
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 6

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 7
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 7

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 8
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 8

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 9
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08,
          class Args_09>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08),
                               BSLS_COMPILERFEATURES_FORWARD(Args_09,args_09));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 9

#if BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 10
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class Args_01,
          class Args_02,
          class Args_03,
          class Args_04,
          class Args_05,
          class Args_06,
          class Args_07,
          class Args_08,
          class Args_09,
          class Args_10>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_01) args_01,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_02) args_02,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_03) args_03,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_04) args_04,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_05) args_05,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_06) args_06,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_07) args_07,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_08) args_08,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_09) args_09,
                            BSLS_COMPILERFEATURES_FORWARD_REF(Args_10) args_10)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                               BSLS_COMPILERFEATURES_FORWARD(Args_01,args_01),
                               BSLS_COMPILERFEATURES_FORWARD(Args_02,args_02),
                               BSLS_COMPILERFEATURES_FORWARD(Args_03,args_03),
                               BSLS_COMPILERFEATURES_FORWARD(Args_04,args_04),
                               BSLS_COMPILERFEATURES_FORWARD(Args_05,args_05),
                               BSLS_COMPILERFEATURES_FORWARD(Args_06,args_06),
                               BSLS_COMPILERFEATURES_FORWARD(Args_07,args_07),
                               BSLS_COMPILERFEATURES_FORWARD(Args_08,args_08),
                               BSLS_COMPILERFEATURES_FORWARD(Args_09,args_09),
                               BSLS_COMPILERFEATURES_FORWARD(Args_10,args_10));

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
#endif  // BSLSTL_MULTISET_VARIADIC_LIMIT_B >= 10

#else
// The generated code below is a workaround for the absence of perfect
// forwarding in some compilers.
template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class... Args>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace(
                               BSLS_COMPILERFEATURES_FORWARD_REF(Args)... args)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                                  BSLS_COMPILERFEATURES_FORWARD(Args,args)...);

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                     static_cast<const Node *>(node)->value());

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
template <class... Args>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::emplace_hint(const_iterator hint,
                               BSLS_COMPILERFEATURES_FORWARD_REF(Args)... args)
{
    bool leftChild;

    BloombergLP::bslalg::RbTreeNode *node = nodeFactory().emplaceIntoNewNode(
                                  BSLS_COMPILERFEATURES_FORWARD(Args,args)...);

    BloombergLP::bslalg::RbTreeNode *hintNode =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(hint.node());

    BloombergLP::bslalg::RbTreeNode *insertLocation =
        BloombergLP::bslalg::RbTreeUtil::findInsertLocation(&leftChild,
                                                            &d_tree,
                                                            this->comparator(),
                                      static_cast<const Node *>(node)->value(),
                                                            hintNode);

    BloombergLP::bslalg::RbTreeUtil::insertAt(&d_tree,
                                              insertLocation,
                                              leftChild,
                                              node);
    return iterator(node);
}
// }}} END GENERATED CODE
#endif

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::erase(const_iterator position)
{
    BSLS_ASSERT_SAFE(position != end());

    BloombergLP::bslalg::RbTreeNode *node =
                const_cast<BloombergLP::bslalg::RbTreeNode *>(position.node());
    BloombergLP::bslalg::RbTreeNode *result =
                                   BloombergLP::bslalg::RbTreeUtil::next(node);
    BloombergLP::bslalg::RbTreeUtil::remove(&d_tree, node);
    nodeFactory().deleteNode(node);
    return iterator(result);
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::size_type
multiset<KEY, COMPARATOR, ALLOCATOR>::erase(const key_type& key)
{
    size_type      count = 0;
    const_iterator first = find(key);
    if (first != end()) {
        const_iterator last = upper_bound(key);
        while (first != last) {
            first = erase(first);
            ++count;
        }
    }
    return count;
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::erase(const_iterator first,
                                            const_iterator last)
{
    while (first != last) {
        first = erase(first);
    }
    return iterator(last.node());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::swap(multiset& other)
                              BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                  AllocatorTraits::is_always_equal::value
                               && bsl::is_nothrow_swappable<COMPARATOR>::value)
{
    if (AllocatorTraits::propagate_on_container_swap::value) {
        quickSwapExchangeAllocators(other);
    }
    else {
        // C++11 behavior for member 'swap': undefined for unequal allocators.
        // BSLS_ASSERT(allocator() == other.allocator());

        if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(
               nodeFactory().allocator() == other.nodeFactory().allocator())) {
            quickSwapRetainAllocators(other);
        }
        else {
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

            multiset toOtherCopy(MoveUtil::move(*this),
                                 other.nodeFactory().allocator());
            multiset toThisCopy(MoveUtil::move(other),
                                nodeFactory().allocator());

            other.quickSwapRetainAllocators(toOtherCopy);
            this->quickSwapRetainAllocators(toThisCopy);
        }
    }
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
void multiset<KEY, COMPARATOR, ALLOCATOR>::clear() BSLS_KEYWORD_NOEXCEPT
{
    BSLS_ASSERT_SAFE(d_tree.firstNode());

    if (d_tree.rootNode()) {
        BSLS_ASSERT_SAFE(0 < d_tree.numNodes());
        BSLS_ASSERT_SAFE(d_tree.firstNode() != d_tree.sentinel());

        BloombergLP::bslalg::RbTreeUtil::deleteTree(&d_tree, &nodeFactory());
    }
#if defined(BSLS_ASSERT_SAFE_IS_USED)
    else {
        BSLS_ASSERT_SAFE(0 == d_tree.numNodes());
        BSLS_ASSERT_SAFE(d_tree.firstNode() == d_tree.sentinel());
    }
#endif
}

// ACCESSORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::allocator_type
multiset<KEY, COMPARATOR, ALLOCATOR>::get_allocator() const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return nodeFactory().allocator();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::begin() const BSLS_KEYWORD_NOEXCEPT
{
    return cbegin();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::end() const BSLS_KEYWORD_NOEXCEPT
{
    return cend();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::rbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return crbegin();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::rend() const BSLS_KEYWORD_NOEXCEPT
{
    return crend();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::cbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return const_iterator(d_tree.firstNode());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::cend() const BSLS_KEYWORD_NOEXCEPT
{
    return const_iterator(d_tree.sentinel());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::crbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return const_reverse_iterator(end());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::const_reverse_iterator
multiset<KEY, COMPARATOR, ALLOCATOR>::crend() const BSLS_KEYWORD_NOEXCEPT
{
    return const_reverse_iterator(begin());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
bool multiset<KEY, COMPARATOR, ALLOCATOR>::contains(const key_type& key) const
{
    return find(key) != end();
}

// capacity:
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
bool multiset<KEY, COMPARATOR, ALLOCATOR>::empty() const BSLS_KEYWORD_NOEXCEPT
{
    return 0 == d_tree.numNodes();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::size_type
multiset<KEY, COMPARATOR, ALLOCATOR>::size() const BSLS_KEYWORD_NOEXCEPT
{
    return d_tree.numNodes();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::size_type
multiset<KEY, COMPARATOR, ALLOCATOR>::max_size() const BSLS_KEYWORD_NOEXCEPT
{
    return AllocatorTraits::max_size(get_allocator());
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::key_compare
multiset<KEY, COMPARATOR, ALLOCATOR>::key_comp() const
{
    return comparator().keyComparator();
}

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
typename multiset<KEY, COMPARATOR, ALLOCATOR>::value_compare
multiset<KEY, COMPARATOR, ALLOCATOR>::value_comp() const
{
    return value_compare(key_comp());
}

}  // close namespace bsl

// FREE OPERATORS
template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
bool bsl::operator==(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                     const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return BloombergLP::bslalg::RangeCompare::equal(lhs.begin(),
                                                    lhs.end(),
                                                    lhs.size(),
                                                    rhs.begin(),
                                                    rhs.end(),
                                                    rhs.size());
}

#ifndef BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON
template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
bool bsl::operator!=(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                     const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return !(lhs == rhs);
}
#endif

#ifdef BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

template <class KEY, class COMPARATOR, class ALLOCATOR>
inline
BloombergLP::bslalg::SynthThreeWayUtil::Result<KEY>
bsl::operator<=>(const multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                 const multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return bsl::lexicographical_compare_three_way(
                              lhs.begin(),
                              lhs.end(),
                              rhs.begin(),
                              rhs.end(),
                              BloombergLP::bslalg::SynthThreeWayUtil::compare);
}

#else

template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
bool bsl::operator<(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                    const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return 0 > BloombergLP::bslalg::RangeCompare::lexicographical(lhs.begin(),
                                                                  lhs.end(),
                                                                  lhs.size(),
                                                                  rhs.begin(),
                                                                  rhs.end(),
                                                                  rhs.size());
}

template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
bool bsl::operator>(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                    const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return rhs < lhs;
}

template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
bool bsl::operator<=(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                     const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return !(rhs < lhs);
}

template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
bool bsl::operator>=(const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& lhs,
                     const bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& rhs)
{
    return !(lhs < rhs);
}

#endif  // BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

// FREE FUNCTIONS
template <class KEY,  class COMPARATOR,  class ALLOCATOR, class PREDICATE>
inline
typename bsl::multiset<KEY, COMPARATOR, ALLOCATOR>::size_type
bsl::erase_if(multiset<KEY, COMPARATOR, ALLOCATOR>& ms, PREDICATE predicate)
{
    return BloombergLP::bslstl::AlgorithmUtil::containerEraseIf(ms, predicate);
}

template <class KEY,  class COMPARATOR,  class ALLOCATOR>
inline
void bsl::swap(bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& a,
               bsl::multiset<KEY, COMPARATOR, ALLOCATOR>& b)
                                 BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                     BSLS_KEYWORD_NOEXCEPT_OPERATOR(a.swap(b)))
{
    a.swap(b);
}

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

// Type traits for STL *ordered* containers:
//: o An ordered container defines STL iterators.
//: o An ordered container uses 'bslma' allocators if the (template parameter)
//:   type 'ALLOCATOR' is convertible from 'bslma::Allocator *'.

namespace BloombergLP {

namespace bslalg {

template <class KEY, class COMPARATOR, class ALLOCATOR>
struct HasStlIterators<bsl::multiset<KEY, COMPARATOR, ALLOCATOR> >
    : bsl::true_type
{};

}  // close namespace bslalg

namespace bslma {

template <class KEY, class COMPARATOR, class ALLOCATOR>
struct UsesBslmaAllocator<bsl::multiset<KEY, COMPARATOR, ALLOCATOR> >
    : bsl::is_convertible<Allocator*, ALLOCATOR>
{};

}  // close namespace bslma

}  // close enterprise namespace

#else // if ! defined(DEFINED_BSLSTL_MULTISET_H)
# error Not valid except when included from bslstl_multiset.h
#endif // ! defined(COMPILING_BSLSTL_MULTISET_H)

#endif // ! defined(INCLUDED_BSLSTL_MULTISET_CPP03)

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