// bdlc_compactedarray.h                                              -*-C++-*-
#ifndef INCLUDED_BDLC_COMPACTEDARRAY
#define INCLUDED_BDLC_COMPACTEDARRAY

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

//@PURPOSE: Provide a compacted array of 'const' user-defined objects.
//
//@CLASSES:
//  bdlc::CompactedArray: compacted array of user-defined objects
//
//@DESCRIPTION: This component provides a space-efficient value-semantic array,
// 'bdlc::CompactedArray', and an associated iterator,
// 'bdlc::CompactedArray::const_iterator', that provides non-modifiable access
// to its elements.  The interface of this class provides the user with
// functionality similar to a 'bsl::vector<T>'.  The implementation is designed
// to reduce dynamic memory usage by (1) removing data duplication at the
// expense of an additional indirection to obtain the stored objects (using the
// flyweight design pattern) and (2) requiring 'operator<' to be defined for
// the type of the stored objects.  The array supports primitive operations
// (e.g., insertion, look-up, removal), as well as a complete set of
// value-semantic operations; however, modifiable reference to individual
// elements is not available.  Users can access the (non-modifiable) value of
// individual elements by calling the indexing operator or via iterators.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: 'Storing Daily Schedules'
/// - - - - - - - - - - - - - - - - - -
// Suppose we are creating a sequence of daily schedules for an employee.  Most
// Mondays (and Tuesdays, Wednesdays, etc.) will have the same schedule,
// although some may differ.  Instead of storing this data in a
// 'bsl::vector<my_DailySchedule>', we can use
// 'bdlc::CompactedArray<my_DailySchedule>' to efficiently store this data.
//
// First, we declare and define a 'my_DailySchedule' class.  This class is not
// overly relevant to the example and is elided for the sake of brevity:
//..
//                          // ================
//                          // my_DailySchedule
//                          // ================
//
//  class my_DailySchedule {
//      // A value-semantic class that provides a daily schedule and consumes a
//      // significant amount of memory.
//
//      int d_initialLocationId;
//
//      // ...
//
//      // FRIENDS
//      friend bool operator<(const my_DailySchedule&,
//                            const my_DailySchedule&);
//
//    public:
//      // CREATORS
//      my_DailySchedule(int               initialLocationId,
//                       bslma::Allocator *basicAllocator = 0);
//          // Create a 'my_DailySchedule' object having the specified
//          // 'initialLocationId'.  Optionally specify a 'basicAllocator' used
//          // to supply memory.  If 'basicAllocator' is 0, the currently
//          // installed default allocator is used.
//
//      // ...
//
//  };
//
//  bool operator<(const my_DailySchedule& lhs, const my_DailySchedule& rhs);
//      // Return 'true' if the specified 'lhs' is lexicographically less than
//      // the specified 'rhs' object, and 'false' otherwise.
//
//                           // ----------------
//                           // my_DailySchedule
//                           // ----------------
//
//  // CREATORS
//  inline
//  my_DailySchedule::my_DailySchedule(int               initialLocationId,
//                                     bslma::Allocator *basicAllocator)
//  : d_initialLocationId(initialLocationId)
//  {
//      (void)basicAllocator;  // suppress unused variable compiler warning
//
//      // ...
//  }
//
//  bool operator<(const my_DailySchedule& lhs, const my_DailySchedule& rhs)
//  {
//      if (lhs.d_initialLocationId < rhs.d_initialLocationId) {
//          return true;                                              // RETURN
//      }
//
//      // ...
//
//      return false;
//  }
//..
// Then, we create our schedule, which is an array of 'my_DailySchedule' where
// the index of each element is the date offset (from an arbitrary epoch
// measured in days).
//..
//  bdlc::CompactedArray<my_DailySchedule> schedule;
//..
// Now, we create some daily schedules and append them to the 'schedule':
//..
//  my_DailySchedule evenDays(0);
//  my_DailySchedule oddDays(1);
//
//  // Population of the 'my_DailySchedule' objects is elided.
//
//  schedule.push_back(evenDays);
//  schedule.push_back(oddDays);
//  schedule.push_back(evenDays);
//  schedule.push_back(oddDays);
//  schedule.push_back(evenDays);
//..
// Finally, we verify that the storage is compacted:
//..
//  assert(5 == schedule.length());
//  assert(2 == schedule.uniqueLength());
//..

#include <bdlscm_version.h>

#include <bdlc_packedintarray.h>

#include <bdlb_algorithmworkaroundutil.h>
#include <bslalg_swaputil.h>

#include <bslh_hash.h>

#include <bslim_printer.h>

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

#include <bsls_assert.h>
#include <bsls_objectbuffer.h>
#include <bsls_review.h>

#include <bsl_algorithm.h>
#include <bsl_cstddef.h>
#include <bsl_iosfwd.h>
#include <bsl_iterator.h>
#include <bsl_limits.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace bdlc {

// FORWARD DECLARATIONS
template <class TYPE> class CompactedArray;

template <class TYPE> class CompactedArray_ConstIterator;

template <class TYPE> CompactedArray_ConstIterator<TYPE>
                          operator++(CompactedArray_ConstIterator<TYPE>&, int);

template <class TYPE> CompactedArray_ConstIterator<TYPE>
                          operator--(CompactedArray_ConstIterator<TYPE>&, int);

template <class TYPE>
CompactedArray_ConstIterator<TYPE> operator+(
                                     const CompactedArray_ConstIterator<TYPE>&,
                                     bsl::ptrdiff_t);

template <class TYPE>
CompactedArray_ConstIterator<TYPE> operator-(
                                     const CompactedArray_ConstIterator<TYPE>&,
                                     bsl::ptrdiff_t);

template <class TYPE>
bsl::ptrdiff_t operator-(const CompactedArray_ConstIterator<TYPE>&,
                         const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator==(const CompactedArray_ConstIterator<TYPE>&,
                const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator!=(const CompactedArray_ConstIterator<TYPE>&,
                const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator<(const CompactedArray_ConstIterator<TYPE>&,
               const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator<=(const CompactedArray_ConstIterator<TYPE>&,
                const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator>(const CompactedArray_ConstIterator<TYPE>&,
               const CompactedArray_ConstIterator<TYPE>&);

template <class TYPE>
bool operator>=(const CompactedArray_ConstIterator<TYPE>&,
                const CompactedArray_ConstIterator<TYPE>&);

                  // =====================================
                  // class CompactedArray_RemoveAllProctor
                  // =====================================

template <class TYPE>
class CompactedArray_RemoveAllProctor {
    // This class implements a proctor that, unless its 'release' method has
    // previously been invoked, automatically invokes 'removeAll' on a
    // 'CompactedArray' upon destruction.

    // DATA
    CompactedArray<TYPE> *d_array_p;  // managed array

    // NOT IMPLEMENTED
    CompactedArray_RemoveAllProctor();
    CompactedArray_RemoveAllProctor(const CompactedArray_RemoveAllProctor&);
    CompactedArray_RemoveAllProctor& operator=(
                                       const CompactedArray_RemoveAllProctor&);

  public:
    // CREATORS
    CompactedArray_RemoveAllProctor(CompactedArray<TYPE> *array);
        // Create a 'removeAll' proctor that conditionally manages the
        // specified 'array' (if non-zero).

    ~CompactedArray_RemoveAllProctor();
        // Destroy this object and, if 'release' has not been invoked, invoke
        // the managed array's 'removeAll' method.

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

                    // ==================================
                    // struct CompactedArray_CountedValue
                    // ==================================

template <class TYPE>
struct CompactedArray_CountedValue {
    // This 'struct' represents a reference-counted value.  Note that
    // comparison of 'd_count' is intentionally omitted from the free
    // equality-comparison operators of this class.

    // PUBLIC DATA
    bsls::ObjectBuffer<TYPE> d_value;  // footprint of stored object
    bsl::size_t              d_count;  // reference count of the stored object

    // CREATORS
    CompactedArray_CountedValue(const TYPE&       value,
                                bsl::size_t       count,
                                bslma::Allocator *basicAllocator);
        // Create a 'CompactedArray_CountedValue' having the specified 'value'
        // and reference 'count'.  The specified 'basicAllocator' is used to
        // supply memory.  The behavior is undefined unless
        // '0 != basicAllocator'.

    CompactedArray_CountedValue(
                     const CompactedArray_CountedValue<TYPE>&  original,
                     bslma::Allocator                         *basicAllocator);
        // Create a 'CompactedArray_CountedValue' having the same underlying
        // object value and reference count as the specified 'original' object.
        // The specified 'basicAllocator' is used to supply memory.  The
        // behavior is undefined unless '0 != basicAllocator'.

    ~CompactedArray_CountedValue();
        // Destroy this object.

    // MANIPULATORS
    CompactedArray_CountedValue& operator=(
                                 const CompactedArray_CountedValue<TYPE>& rhs);
        // Assign to this object the underlying object value and reference
        // count of the specified 'rhs' object, and return a reference
        // providing modifiable access to this object.
};

// FREE OPERATORS
template <class TYPE>
bool operator==(const CompactedArray_CountedValue<TYPE>& lhs,
                const CompactedArray_CountedValue<TYPE>& rhs);
    // Return 'true' if the underlying object value of the specified 'lhs' is
    // the same as the underlying object value of the specified 'rhs', and
    // 'false' otherwise.  Note that the reference counts are intentionally
    // ignored.

template <class TYPE>
bool operator!=(const CompactedArray_CountedValue<TYPE>& lhs,
                const CompactedArray_CountedValue<TYPE>& rhs);
    // Return 'true' if the underlying object value of the specified 'lhs' is
    // not the same as the underlying object value of the specified 'rhs', and
    // 'false' otherwise.  Note that the reference counts are intentionally
    // ignored.

template <class TYPE>
bool operator<(const CompactedArray_CountedValue<TYPE>& lhs, const TYPE& rhs);
    // Return 'true' if the underlying object value of the specified 'lhs' is
    // less than the value of the specified 'rhs', and 'false' otherwise.  Note
    // that the reference count is intentionally ignored.

template <class TYPE>
bool operator<(const TYPE& lhs, const CompactedArray_CountedValue<TYPE>& rhs);
    // Return 'true' if the value of the specified 'lhs' is less than the
    // underlying object value of the specified 'rhs', and 'false' otherwise.
    // Note that the reference count is intentionally ignored.

                    // ==================================
                    // class CompactedArray_ConstIterator
                    // ==================================

template <class TYPE>
class CompactedArray_ConstIterator {
    // This value-semantic class represents a random access iterator providing
    // non-modifiable access to the elements of a 'CompactedArray'.  This class
    // provides all functionality of a random access iterator, as defined by
    // the standard, but is *not* compatible with most standard methods
    // requiring a bidirectional 'const_iterator'.
    //
    // This class does not perform any bounds checking.  Any iterator, 'it',
    // referencing a 'CompactedArray' 'array', remains valid while
    // '0 <= it - array.begin() < array.length()'.

    // DATA
    const CompactedArray<TYPE> *d_array_p;  // 'CompactedArray' referenced by
                                            // this iterator, or 0 if default
                                            // value

    bsl::size_t                 d_index;    // index of the referenced element,
                                            // or one past the end of
                                            // 'd_array_p'

    // FRIENDS
    friend class CompactedArray<TYPE>;

    friend CompactedArray_ConstIterator
                              operator++<>(CompactedArray_ConstIterator&, int);

    friend CompactedArray_ConstIterator
                              operator--<>(CompactedArray_ConstIterator&, int);

    friend bool operator==<>(const CompactedArray_ConstIterator&,
                             const CompactedArray_ConstIterator&);

    friend bool operator!=<>(const CompactedArray_ConstIterator&,
                             const CompactedArray_ConstIterator&);

    friend CompactedArray_ConstIterator<TYPE> operator+<>(
                                     const CompactedArray_ConstIterator<TYPE>&,
                                     bsl::ptrdiff_t);

    friend CompactedArray_ConstIterator<TYPE> operator-<>(
                                     const CompactedArray_ConstIterator<TYPE>&,
                                     bsl::ptrdiff_t);

    friend bsl::ptrdiff_t operator-<>(const CompactedArray_ConstIterator&,
                                      const CompactedArray_ConstIterator&);

    friend bool operator< <>(const CompactedArray_ConstIterator&,
                             const CompactedArray_ConstIterator&);

    friend bool operator<=<>(const CompactedArray_ConstIterator&,
                             const CompactedArray_ConstIterator&);

    friend bool operator><>(const CompactedArray_ConstIterator&,
                            const CompactedArray_ConstIterator&);

    friend bool operator>=<>(const CompactedArray_ConstIterator&,
                             const CompactedArray_ConstIterator&);

  public:
    // PUBLIC TYPES

    // The following 'typedef's define the traits for this iterator to make it
    // compatible with standard functions.

    typedef bsl::ptrdiff_t  difference_type;  // The type used for the distance
                                              // between two iterators.

    typedef bsl::size_t     size_type;        // The type used for any function
                                              // requiring a length (i.e,
                                              // index).

    typedef TYPE            value_type;       // The type for elements.

    typedef TYPE           *pointer;          // The type of an arbitrary
                                              // pointer into the array.

    typedef TYPE&           reference;        // The type for element
                                              // references.

    typedef std::random_access_iterator_tag iterator_category;
                                              // This is a random access
                                              // iterator.

  private:
    // PRIVATE CREATORS
    CompactedArray_ConstIterator(const CompactedArray<TYPE> *array,
                                 bsl::size_t                 index);
        // Create a 'CompactedArray_ConstIterator' object that refers to the
        // element at the specified 'index' in the specified 'array', or the
        // past-the-end iterator for 'array' if 'index == array->length()'.
        // The behavior is undefined unless 'index <= array->length()'.

  public:
    // CREATORS
    CompactedArray_ConstIterator();
        // Create a default 'CompactedArray_ConstIterator'.  Note that the
        // behavior of most methods is undefined when used on a
        // default-constructed iterator.

    CompactedArray_ConstIterator(const CompactedArray_ConstIterator& original);
        // Create a 'CompactedArray_ConstIterator' having the same value as the
        // specified 'original' object.

    //! ~CompactedArray_ConstIterator() = default;
        // Destroy this object.

    // MANIPULATORS
    CompactedArray_ConstIterator&
                             operator=(
                                      const CompactedArray_ConstIterator& rhs);
        // Assign to this iterator the value of the specified 'rhs' iterator,
        // and return a reference providing modifiable access to this iterator.

    CompactedArray_ConstIterator& operator+=(bsl::ptrdiff_t offset);
        // Advance this iterator by the specified 'offset' from the location
        // referenced by this iterator, and return a reference providing
        // modifiable access to this iterator.  The returned iterator, 'it',
        // referencing a 'CompactedArray' 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless 'CompactedArray_ConstIterator() != *this' and
        // '0 <= *this - array.begin() + offset <= array.length()'.

    CompactedArray_ConstIterator& operator-=(bsl::ptrdiff_t offset);
        // Decrement this iterator by the specified 'offset' from the location
        // referenced by this iterator, and return a reference providing
        // modifiable access to this iterator.  The returned iterator, 'it',
        // referencing a 'CompactedArray' 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless 'CompactedArray_ConstIterator() != *this' and
        // '0 <= *this - array.begin() - offset <= array.length()'.

    CompactedArray_ConstIterator& operator++();
        // Advance this iterator to refer to the next location in the
        // referenced array, and return a reference to this iterator *after*
        // the advancement.  The returned iterator, 'it', referencing a
        // 'CompactedArray' 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless, on entry,
        // 'CompactedArray_ConstIterator() != *this' and
        // '*this - array.begin() < array.length()'.

    CompactedArray_ConstIterator& operator--();
        // Decrement this iterator to refer to the previous location in the
        // referenced array, and return a reference to this iterator *after*
        // the decrementation.  The returned iterator, 'it', referencing a
        // 'CompactedArray' 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless, on entry,
        // 'CompactedArray_ConstIterator() != *this' and
        // '0 < *this - array.begin()'.

    // ACCESSORS
    const TYPE& operator*() const;
        // Return a 'const' reference to the element referenced by this
        // iterator.  The behavior is undefined unless for this iterator,
        // referencing a 'CompactedArray' 'array',
        // 'CompactedArray_ConstIterator() != *this' and
        // '*this - array.begin() < array.length()'.

    const TYPE& operator->() const;
        // Return a 'const' reference to the element referenced by this
        // iterator.  The behavior is undefined unless for this iterator,
        // referencing a 'CompactedArray' 'array',
        // 'CompactedArray_ConstIterator() != *this' and
        // '*this - array.begin() < array.length()'.

    const TYPE& operator[](bsl::ptrdiff_t offset) const;
        // Return a 'const' reference to the element at the specified 'offset'
        // from the location referenced by this iterator.  The behavior is
        // undefined unless for this iterator, referencing a 'CompactedArray'
        // 'array', 'CompactedArray_ConstIterator() != *this' and
        // '0 <= *this - array.begin() + offset < array.length()'.
};

// FREE OPERATORS
template <class TYPE>
CompactedArray_ConstIterator<TYPE>
                       operator++(CompactedArray_ConstIterator<TYPE>& iterator,
                                  int);
    // Advance the specified 'iterator' to refer to the next location in the
    // referenced array, and return an iterator referring to the original
    // location (*before* the advancement).  The returned iterator, 'it',
    // referencing a 'CompactedArray' 'array', remains valid as long as
    // '0 <= it - array.begin() <= array.length()'.  The behavior is undefined
    // unless, on entry, 'CompactedArray_ConstIterator() != iterator' and
    // 'iterator - array.begin() < array.length()'.

template <class TYPE>
CompactedArray_ConstIterator<TYPE>
                       operator--(CompactedArray_ConstIterator<TYPE>& iterator,
                                  int);
    // Decrement the specified 'iterator' to refer to the previous location in
    // the referenced array, and return an iterator referring to the original
    // location (*before* the decrementation).  The returned iterator, 'it',
    // referencing a 'CompactedArray' 'array', remains valid as long as
    // '0 <= it - array.begin() <= array.length()'.  The behavior is undefined
    // unless, on entry, 'CompactedArray_ConstIterator() != iterator' and
    // '0 < iterator - array.begin()'.

template <class TYPE>
CompactedArray_ConstIterator<TYPE> operator+(
                           const CompactedArray_ConstIterator<TYPE>& iterator,
                           bsl::ptrdiff_t                            offset);
template <class TYPE>
CompactedArray_ConstIterator<TYPE> operator+(
                           bsl::ptrdiff_t                            offset,
                           const CompactedArray_ConstIterator<TYPE>& iterator);
    // Return an iterator referencing the location at the specified 'offset'
    // from the location referenced by the specified 'iterator'.  The returned
    // iterator, 'it', referencing a 'CompactedArray' 'array', remains valid as
    // long as '0 <= it - array.begin() <= array.length()'.  The behavior is
    // undefined unless 'CompactedArray_ConstIterator() != iterator' and
    // '0 <= iterator - array.begin() + offset <= array.length()'.

template <class TYPE>
CompactedArray_ConstIterator<TYPE> operator-(
                            const CompactedArray_ConstIterator<TYPE>& iterator,
                            bsl::ptrdiff_t                            offset);
    // Return an iterator referencing the location at the specified 'offset'
    // from the location referenced by the specified 'iterator'.  The returned
    // iterator, 'it', referencing a 'CompactedArray' 'array', remains valid as
    // long as '0 <= it - array.begin() <= array.length()'.  The behavior is
    // undefined unless 'CompactedArray_ConstIterator() != iterator' and
    // '0 <= iterator - array.begin() - offset <= array.length()'.

template <class TYPE>
bsl::ptrdiff_t operator-(const CompactedArray_ConstIterator<TYPE>& lhs,
                         const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return the number of elements between the specified 'lhs' and 'rhs' as a
    // signed value.  The behavior is undefined unless 'lhs' and 'rhs'
    // reference the same array.  Note that the return value is positive when a
    // positive number of 'rhs++' invocations would result in 'lhs == rhs', and
    // negative when a positive number of 'rhs--' invocations would result in
    // 'lhs == rhs'.

template <class TYPE>
bool operator==(const CompactedArray_ConstIterator<TYPE>& lhs,
                const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators have the same
    // value, and 'false' otherwise.  Two 'CompactedArray_ConstIterator'
    // iterators have the same value if they both have the default value, or
    // neither has the default value and they reference the same location in
    // the same array.

template <class TYPE>
bool operator!=(const CompactedArray_ConstIterator<TYPE>& lhs,
                const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not have the
    // same value, and 'false' otherwise.  Two 'CompactedArray_ConstIterator'
    // iterators do not have the same value if one has the default value and
    // the other does not, or neither has the default value and they do not
    // reference the same location in the same array.

template <class TYPE>
bool operator<(const CompactedArray_ConstIterator<TYPE>& lhs,
               const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Iterator 'lhs' has a value less than
    // iterator 'rhs' if '0 < rhs - lhs' (see 'operator-').  The behavior is
    // undefined unless 'lhs' and 'rhs' refer to the same array.

template <class TYPE>
bool operator<=(const CompactedArray_ConstIterator<TYPE>& lhs,
                const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value less than or equal to
    // the specified 'rhs, and 'false' otherwise.  Iterator 'lhs' has a value
    // less than or equal to iterator 'rhs' if '0 <= rhs - lhs' (see
    // 'operator-').  The behavior is undefined unless 'lhs' and 'rhs' refer to
    // the same array.

template <class TYPE>
bool operator>(const CompactedArray_ConstIterator<TYPE>& lhs,
               const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value greater than the
    // specified 'rhs', and 'false' otherwise.  Iterator 'lhs' has a value
    // greater than iterator 'rhs' if '0 > rhs - lhs' (see 'operator-').  The
    // behavior is undefined unless 'lhs' and 'rhs' refer to the same array.

template <class TYPE>
bool operator>=(const CompactedArray_ConstIterator<TYPE>& lhs,
                const CompactedArray_ConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value greater or equal than
    // the specified 'rhs', and 'false' otherwise.  Iterator 'lhs' has a value
    // greater than or equal to iterator 'rhs' if '0 >= rhs - lhs' (see
    // 'operator-').  The behavior is undefined unless 'lhs' and 'rhs' refer to
    // the same array.

                           // ====================
                           // class CompactedArray
                           // ====================

template <class TYPE>
class CompactedArray {
    // This space-efficient, value-semantic array class represents a sequence
    // of 'TYPE' elements.  The interface provides functionality similar to a
    // 'vector<TYPE>', however, modifiable references to individual elements
    // are not provided.  This class provides accessors that return iterators
    // that provide non-modifiable access to its elements.  The returned
    // iterators, unlike those returned by a 'vector<TYPE>', are *not*
    // invalidated upon reallocation.

    // PRIVATE TYPES
    typedef bsl::vector<CompactedArray_CountedValue<TYPE> > Data;

    // DATA
    Data                        d_data;   // sorted vector of reference-counted
                                          // unique objects

    PackedIntArray<bsl::size_t> d_index;  // array of indices into 'd_data'

  private:
    // PRIVATE MANIPULATORS
    void erase(bsl::size_t index);
        // Remove the element in 'd_data' at the specified 'index'.  Update the
        // 'd_index' values to account for this removal.  The behavior is
        // undefined unless 'index < uniqueLength()'.

    bsl::size_t increment(const TYPE& value, bsl::size_t count = 1);
        // Find the element in 'd_data' equal to the specified 'value',
        // increment the element's reference count by the specified 'count',
        // and return the element's index.  If the 'value' is not in 'd_data',
        // insert the element so as to retain sorted order in 'd_data', assign
        // 'count' as the new element's reference count, and return the
        // inserted element's index.  The behavior is undefined unless
        // '0 < count'.

  public:
    // PUBLIC TYPES
    typedef TYPE value_type;  // The type for elements.

    typedef CompactedArray_ConstIterator<TYPE> const_iterator;

    // CREATORS
    explicit CompactedArray(bslma::Allocator *basicAllocator = 0);
        // Create an empty 'CompactedArray'.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    explicit CompactedArray(bsl::size_t       numElements,
                            const TYPE&       value = TYPE(),
                            bslma::Allocator *basicAllocator = 0);
        // Create a 'CompactedArray' having the specified 'numElements'.
        // Optionally specify a 'value' to which each element will be set.  If
        // 'value' is not specified, 'TYPE()' is used.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

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

    ~CompactedArray();
        // Destroy this object

    // MANIPULATORS
    CompactedArray& operator=(const CompactedArray& rhs);
        // Assign to this array the value of the specified 'rhs' array, and
        // return a reference providing modifiable access to this array.

    void append(const TYPE& value);
        // Append to this array an element having the specified 'value'.  Note
        // that this method is logically equivalent to:
        //..
        //  push_back(value);
        //..

    void append(const CompactedArray& srcArray);
        // Append to this array the elements from the specified 'srcArray'.
        // Note that if this array and 'srcArray' are the same, the behavior is
        // as if a copy of 'srcArray' were passed.

    void append(const CompactedArray& srcArray,
                bsl::size_t           srcIndex,
                bsl::size_t           numElements);
        // Append to this array the specified 'numElements' starting at the
        // specified 'srcIndex' in the specified 'srcArray'.  The behavior is
        // undefined unless 'srcIndex + numElements <= srcArray.length()'.
        // Note that if this array and 'srcArray' are the same, the behavior is
        // as if a copy of 'srcArray' were passed.

    void insert(bsl::size_t dstIndex, const TYPE& value);
        // Insert into this array, at the specified 'dstIndex', an element
        // having the specified 'value', shifting any elements originally at or
        // above 'dstIndex' up by one index position.  The behavior is
        // undefined unless 'dstIndex <= length()'.

    const_iterator insert(const_iterator dst, const TYPE& value);
        // Insert into this array, at the specified 'dst', an element having
        // the specified 'value', shifting any elements originally at or above
        // 'dst' up by one index position.  Return an iterator to the newly
        // inserted element.  The behavior is undefined unless 'dst' is an
        // iterator over this array.

    void insert(bsl::size_t dstIndex, const CompactedArray& srcArray);
        // Insert into this array, at the specified 'dstIndex', the elements
        // from the specified 'srcArray', shifting any elements originally at
        // or above 'dstIndex' up by 'srcArray.length()' index positions.  The
        // behavior is undefined unless 'dstIndex <= length()'.  Note that if
        // this array and 'srcArray' are the same, the behavior is as if a copy
        // of 'srcArray' were passed.

    void insert(bsl::size_t           dstIndex,
                const CompactedArray& srcArray,
                bsl::size_t           srcIndex,
                bsl::size_t           numElements);
        // Insert into this array, at the specified 'dstIndex', the specified
        // 'numElements' starting at the specified 'srcIndex' in the specified
        // 'srcArray'.  Elements having an index greater than or equal to
        // 'dstIndex' before the insertion are shifted up by 'numElements'
        // index positions.  The behavior is undefined unless
        // 'dstIndex <= length()' and
        // 'srcIndex + numElements <= srcArray.length()'.  Note that if this
        // array and 'srcArray' are the same, the behavior is as if a copy of
        // 'srcArray' were passed.

    void pop_back();
        // Remove the last element from this array.  The behavior is undefined
        // unless '0 < length()'.

    void push_back(const TYPE& value);
        // Append to this array an element having the specified 'value'.

    void remove(bsl::size_t dstIndex);
        // Remove from this array the element at the specified 'dstIndex'.
        // Each element having an index greater than 'dstIndex' before the
        // removal is shifted down by one index position.  The behavior is
        // undefined unless 'dstIndex < length()'.

    void remove(bsl::size_t dstIndex, bsl::size_t numElements);
        // Remove from this array the specified 'numElements' starting at the
        // specified 'dstIndex'.  Each element having an index greater than or
        // equal to 'dstIndex + numElements' before the removal is shifted down
        // by 'numElements' index positions.  The behavior is undefined unless
        // 'dstIndex + numElements <= length()'.

    const_iterator remove(const_iterator dstFirst, const_iterator dstLast);
        // Remove from this array the elements starting at the specified
        // 'dstFirst' iterator up to, but not including, the specified
        // 'dstLast' iterator.  Each element at or above 'dstLast' before the
        // removal is shifted down by 'dstLast - dstFirst' index positions.
        // Return an iterator to the new position of the element that was
        // referred to by 'dstLast', or 'end()' if 'dstLast == end()'.  The
        // behavior is undefined unless 'dstFirst' and 'dstLast' are iterators
        // over this array, and 'dstFirst <= dstLast'.

    void removeAll();
        // Remove all the elements from this array.

    void replace(bsl::size_t dstIndex, const TYPE& value);
        // Change the value of the element at the specified 'dstIndex' in this
        // array to the specified 'value'.  The behavior is undefined unless
        // 'dstIndex < length()'.

    void replace(bsl::size_t           dstIndex,
                 const CompactedArray& srcArray,
                 bsl::size_t           srcIndex,
                 bsl::size_t           numElements);
        // Change the values of the specified 'numElements' starting at the
        // specified 'dstIndex' in this array to those of the 'numElements'
        // starting at the specified 'srcIndex' in the specified 'srcArray'.
        // The behavior is undefined unless
        // 'srcIndex + numElements <= srcArray.length()' and
        // 'dstIndex + numElements <= length()'.  Note that if this array and
        // 'srcArray' are the same, the behavior is as if a copy of 'srcArray'
        // were passed.

    void reserveCapacity(bsl::size_t numElements);
        // Make the capacity of this array at least the specified
        // 'numElements', assuming the number of unique elements within this
        // array does not increase.  This method has no effect if the current
        // capacity meets or exceeds the required capacity.  The behavior is
        // undefined unless 'false == isEmpty() || 0 == numElements'.  Note
        // that the assumption of not increasing the number of unique elements
        // implies the need for the narrow contract.

    void reserveCapacity(bsl::size_t numElements,
                         bsl::size_t numUniqueElements);
        // Make the capacity of this array at least the specified
        // 'numElements', assuming the number of unique elements in this array
        // does not exceed the greater of the specified 'numUniqueElements' and
        // 'uniqueLength()'.  This method has no effect if the current capacity
        // meets or exceeds the required capacity.  The behavior is undefined
        // unless 'numUniqueElements <= numElements' and
        // '0 < numUniqueElements || 0 == numElements'.

    void resize(bsl::size_t numElements);
        // Set the length of this array to the specified 'numElements'.  If
        // 'numElements > length()', the added elements are initialized to
        // 'TYPE()'.

    void swap(CompactedArray& other);
        // Efficiently exchange the value of this array with the value of the
        // specified 'other' array.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // array was created with the same allocator as 'other'.

    // ACCESSORS
    const TYPE& operator[](bsl::size_t index) const;
        // Return a 'const' reference to the element at the specified 'index'
        // in this array.  The behavior is undefined unless 'index < length()'.

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

    const TYPE& back() const;
        // Return a 'const' reference to the element at the back of this array.
        // The behavior is undefined unless '0 < length()'.  Note that this
        // method is logically equivalent to:
        //..
        //    operator[](length() - 1)
        //..

    const_iterator begin() const;
        // Return an iterator referring to the first element in this array, or
        // the past-the-end iterator if this array is empty.  The iterator
        // remains valid as long as this array exists.

    bsl::size_t capacity() const;
        // Return the number of elements this array can hold, without
        // reallocation, assuming the number of unique elements within this
        // array does not increase.

    const_iterator end() const;
        // Return the past-the-end iterator for this array.  The iterator
        // remains valid as long as this array exists, and its length does not
        // decrease.

    const TYPE& front() const;
        // Return a 'const' reference to the element at the front of this
        // array.  The behavior is undefined unless '0 < length()'.  Note that
        // this method is logically equivalent to:
        //..
        //    operator[](0)
        //..

    bool isEmpty() const;
        // Return 'true' if there are no elements in this array, and 'false'
        // otherwise.

    bool isEqual(const CompactedArray& other) const;
        // Return 'true' if this and the specified 'other' array have the same
        // value, and 'false' otherwise.  Two 'CompactedArray' arrays have the
        // same value if they have the same length, and all corresponding
        // elements (those at the same indices) have the same value.

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

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this array to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested arrays.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested arrays.  If 'level' is negative, format the entire
        // output on one line, suppressing all but the initial indentation (as
        // governed by 'level').  If 'stream' is not valid on entry, this
        // operation has no effect.  Note that the format is not fully
        // specified, and can change without notice.

    const TYPE& uniqueElement(bsl::size_t index) const;
        // Return a 'const' reference to the element at the specified 'index'
        // within the sorted sequence of unique element values in this object.
        // The behavior is undefined unless 'index < uniqueLength()'.  Note
        // that 'uniqueElement(index)' and 'operator[](index)' can return
        // different objects.

    bsl::size_t uniqueLength() const;
        // Return the number of unique elements in this array.
};

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

template <class TYPE>
bool operator==(const CompactedArray<TYPE>& lhs,
                const CompactedArray<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' arrays have the same
    // value, and 'false' otherwise.  Two 'CompactedArray' arrays have the same
    // value if they have the same length, and all corresponding elements
    // (those at the same indices) have the same value.

template <class TYPE>
bool operator!=(const CompactedArray<TYPE>& lhs,
                const CompactedArray<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' arrays do not have the
    // same value, and 'false' otherwise.  Two 'CompactedArray' arrays do not
    // have the same value if they do not have the same length, or if any
    // corresponding elements (those at the same indices) do not have the same
    // value.

// FREE FUNCTIONS
template <class TYPE>
void swap(CompactedArray<TYPE>& a, CompactedArray<TYPE>& b);
    // Exchange the values of the specified 'a' and 'b' objects.  This function
    // provides the no-throw exception-safety guarantee if the two objects were
    // created with the same allocator and the basic guarantee otherwise.

// HASH SPECIALIZATIONS
template <class HASHALG, class TYPE>
void hashAppend(HASHALG& hashAlg, const CompactedArray<TYPE>& input);
    // Pass the specified 'input' to the specified 'hashAlg'.

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                  // -------------------------------------
                  // class CompactedArray_RemoveAllProctor
                  // -------------------------------------

// CREATORS
template <class TYPE>
inline
CompactedArray_RemoveAllProctor<TYPE>::CompactedArray_RemoveAllProctor(
                                                   CompactedArray<TYPE> *array)
: d_array_p(array)
{
}

template <class TYPE>
inline
CompactedArray_RemoveAllProctor<TYPE>::~CompactedArray_RemoveAllProctor()
{
    if (d_array_p) {
        d_array_p->removeAll();
    }
}

// MANIPULATORS
template <class TYPE>
inline
void CompactedArray_RemoveAllProctor<TYPE>::release()
{
    d_array_p = 0;
}

                    // ----------------------------------
                    // struct CompactedArray_CountedValue
                    // ----------------------------------

// CREATORS
template <class TYPE>
inline
CompactedArray_CountedValue<TYPE>::CompactedArray_CountedValue(
                                              const TYPE&       value,
                                              bsl::size_t       count,
                                              bslma::Allocator *basicAllocator)
: d_count(count)
{
    BSLS_ASSERT(basicAllocator);

    bslma::ConstructionUtil::construct(d_value.address(),
                                       basicAllocator,
                                       value);
}

template <class TYPE>
inline
CompactedArray_CountedValue<TYPE>::CompactedArray_CountedValue(
                      const CompactedArray_CountedValue<TYPE>&  original,
                      bslma::Allocator                         *basicAllocator)
: d_count(original.d_count)
{
    BSLS_ASSERT(basicAllocator);

    bslma::ConstructionUtil::construct(d_value.address(),
                                       basicAllocator,
                                       original.d_value.object());
}

template <class TYPE>
inline
CompactedArray_CountedValue<TYPE>::~CompactedArray_CountedValue()
{
    d_value.object().~TYPE();
}

// MANIPULATORS
template <class TYPE>
inline
CompactedArray_CountedValue<TYPE>&
        CompactedArray_CountedValue<TYPE>::operator=(
                                  const CompactedArray_CountedValue<TYPE>& rhs)
{
    d_value.object() = rhs.d_value.object();
    d_count          = rhs.d_count;

    return *this;
}

}  // close package namespace

// FREE OPERATORS
template <class TYPE>
inline
bool bdlc::operator==(const CompactedArray_CountedValue<TYPE>& lhs,
                      const CompactedArray_CountedValue<TYPE>& rhs)
{
    return lhs.d_value.object() == rhs.d_value.object();
}

template <class TYPE>
inline
bool bdlc::operator!=(const CompactedArray_CountedValue<TYPE>& lhs,
                      const CompactedArray_CountedValue<TYPE>& rhs)
{
    return lhs.d_value.object() != rhs.d_value.object();
}

template <class TYPE>
inline
bool bdlc::operator<(const CompactedArray_CountedValue<TYPE>& lhs,
                     const TYPE&                              rhs)
{
    return lhs.d_value.object() < rhs;
}

template <class TYPE>
inline
bool bdlc::operator<(const TYPE&                              lhs,
                     const CompactedArray_CountedValue<TYPE>& rhs)
{
    return lhs < rhs.d_value.object();
}

namespace bdlc {

                    // ----------------------------------
                    // class CompactedArray_ConstIterator
                    // ----------------------------------

// PRIVATE CREATORS
template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>::CompactedArray_ConstIterator(
                                             const CompactedArray<TYPE> *array,
                                             bsl::size_t                 index)
: d_array_p(array)
, d_index(index)
{
    BSLS_ASSERT(d_array_p);
    BSLS_ASSERT(d_index <= d_array_p->length());
}

// CREATORS
template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>::CompactedArray_ConstIterator()
: d_array_p(0)
, d_index(0)
{
}

template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>::CompactedArray_ConstIterator(
                                  const CompactedArray_ConstIterator& original)
: d_array_p(original.d_array_p)
, d_index(original.d_index)
{
}

// MANIPULATORS
template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>& CompactedArray_ConstIterator<TYPE>::
                             operator=(const CompactedArray_ConstIterator& rhs)
{
    d_array_p = rhs.d_array_p;
    d_index   = rhs.d_index;
    return *this;
}

template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>&
          CompactedArray_ConstIterator<TYPE>::operator+=(bsl::ptrdiff_t offset)
{
    BSLS_ASSERT(d_array_p);

    // Assert '0 <= d_index + offset <= d_array_p->length()' without risk of
    // overflow.
    BSLS_ASSERT(   0 <= offset || d_index >= bsl::size_t(-offset));
    BSLS_ASSERT(   0 >= offset
                     || d_array_p->length() - d_index >= bsl::size_t(offset));

    d_index += offset;
    return *this;
}

template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>&
          CompactedArray_ConstIterator<TYPE>::operator-=(bsl::ptrdiff_t offset)
{
    BSLS_ASSERT(d_array_p);

    // Assert '0 <= d_index - offset <= d_array_p->length()' without risk of
    // overflow.
    BSLS_ASSERT(   0 >= offset || d_index >= bsl::size_t(offset));
    BSLS_ASSERT(   0 <= offset
                     || d_array_p->length() - d_index >= bsl::size_t(-offset));

    d_index -= offset;
    return *this;
}

template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>&
                               CompactedArray_ConstIterator<TYPE>::operator++()
{
    BSLS_ASSERT(d_array_p);
    BSLS_ASSERT(d_index < d_array_p->length());

    ++d_index;
    return *this;
}

template <class TYPE>
inline
CompactedArray_ConstIterator<TYPE>&
                               CompactedArray_ConstIterator<TYPE>::operator--()
{
    BSLS_ASSERT(d_array_p);
    BSLS_ASSERT(0 < d_index);

    --d_index;
    return *this;
}

// ACCESSORS
template <class TYPE>
inline
const TYPE& CompactedArray_ConstIterator<TYPE>::operator*() const
{
    BSLS_ASSERT(d_array_p);
    BSLS_ASSERT(d_index < d_array_p->length());

    return (*d_array_p)[d_index];
}

template <class TYPE>
inline
const TYPE& CompactedArray_ConstIterator<TYPE>::operator->() const
{
    BSLS_ASSERT(d_array_p);
    BSLS_ASSERT(d_index < d_array_p->length());

    return (*d_array_p)[d_index];
}

template <class TYPE>
inline
const TYPE& CompactedArray_ConstIterator<TYPE>::
                                        operator[](bsl::ptrdiff_t offset) const
{
    BSLS_ASSERT(d_array_p);

    // Assert '0 <= d_index + offset < d_array_p->length()' without risk of
    // overflow.
    BSLS_ASSERT(   0 <= offset || d_index >= bsl::size_t(-offset));
    BSLS_ASSERT(   0 >= offset
                     || d_array_p->length() - d_index > bsl::size_t(offset));

    return (*d_array_p)[d_index + offset];
}

}  // close package namespace

// FREE OPERATORS
template <class TYPE>
inline
bdlc::CompactedArray_ConstIterator<TYPE> bdlc::operator++(
                                  CompactedArray_ConstIterator<TYPE>& iterator,
                                  int)
{
    BSLS_ASSERT(iterator.d_array_p);
    BSLS_ASSERT(iterator.d_index < iterator.d_array_p->length());

    const CompactedArray_ConstIterator<TYPE> curr = iterator;
    ++iterator;
    return curr;
}

template <class TYPE>
inline
bdlc::CompactedArray_ConstIterator<TYPE> bdlc::operator--(
                                  CompactedArray_ConstIterator<TYPE>& iterator,
                                  int)
{
    BSLS_ASSERT(iterator.d_array_p);
    BSLS_ASSERT(iterator.d_index > 0);

    const CompactedArray_ConstIterator<TYPE> curr = iterator;
    --iterator;
    return curr;
}

template <class TYPE>
inline
bdlc::CompactedArray_ConstIterator<TYPE> bdlc::operator+(
                            const CompactedArray_ConstIterator<TYPE>& iterator,
                            bsl::ptrdiff_t                            offset)
{
    BSLS_ASSERT(iterator.d_array_p);

    // Assert '0 <= iterator.d_index + offset <= iterator.d_array_p->length()'
    // without risk of overflow.
    BSLS_ASSERT(   0 <= offset
                     || iterator.d_index              >= bsl::size_t(-offset));
    BSLS_ASSERT(   0 >= offset
                     || iterator.d_array_p->length() - iterator.d_index
                                                      >= bsl::size_t( offset));

    return CompactedArray_ConstIterator<TYPE>(iterator.d_array_p,
                                              iterator.d_index + offset);
}

template <class TYPE>
inline
bdlc::CompactedArray_ConstIterator<TYPE> bdlc::operator+(
                            bsl::ptrdiff_t                            offset,
                            const CompactedArray_ConstIterator<TYPE>& iterator)
{
    return iterator + offset;
}

template <class TYPE>
inline
bdlc::CompactedArray_ConstIterator<TYPE> bdlc::operator-(
                            const CompactedArray_ConstIterator<TYPE>& iterator,
                            bsl::ptrdiff_t                            offset)
{
    BSLS_ASSERT(iterator.d_array_p);

    // Assert '0 <= iterator.d_index - offset <= iterator.d_array_p->length()'
    // without risk of overflow.
    BSLS_ASSERT(   0 >= offset
                     || iterator.d_index              >= bsl::size_t( offset));
    BSLS_ASSERT(   0 <= offset
                     || iterator.d_array_p->length() - iterator.d_index
                                                      >= bsl::size_t(-offset));

    return CompactedArray_ConstIterator<TYPE>(iterator.d_array_p,
                                              iterator.d_index - offset);
}

template <class TYPE>
inline
bsl::ptrdiff_t bdlc::operator-(const CompactedArray_ConstIterator<TYPE>& lhs,
                               const CompactedArray_ConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p);
    BSLS_ASSERT(rhs.d_array_p);
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    BSLS_ASSERT(
          lhs.d_index >= rhs.d_index
        ? lhs.d_index - rhs.d_index <=
                        bsl::size_t(bsl::numeric_limits<bsl::ptrdiff_t>::max())
        : rhs.d_index - lhs.d_index <=
                      bsl::size_t(bsl::numeric_limits<bsl::ptrdiff_t>::min()));

    return static_cast<bsl::ptrdiff_t>(lhs.d_index - rhs.d_index);
}

template <class TYPE>
inline
bool bdlc::operator==(const CompactedArray_ConstIterator<TYPE>& lhs,
                      const CompactedArray_ConstIterator<TYPE>& rhs)
{
    return lhs.d_array_p == rhs.d_array_p && lhs.d_index == rhs.d_index;
}

template <class TYPE>
inline
bool bdlc::operator!=(const CompactedArray_ConstIterator<TYPE>& lhs,
                      const CompactedArray_ConstIterator<TYPE>& rhs)
{
    return lhs.d_array_p != rhs.d_array_p || lhs.d_index != rhs.d_index;
}

template <class TYPE>
inline
bool bdlc::operator<(const CompactedArray_ConstIterator<TYPE>& lhs,
                     const CompactedArray_ConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p);
    BSLS_ASSERT(rhs.d_array_p);
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    return lhs.d_index < rhs.d_index;
}

template <class TYPE>
inline
bool bdlc::operator<=(const CompactedArray_ConstIterator<TYPE>& lhs,
                      const CompactedArray_ConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p);
    BSLS_ASSERT(rhs.d_array_p);
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    return lhs.d_index <= rhs.d_index;
}

template <class TYPE>
inline
bool bdlc::operator>(const CompactedArray_ConstIterator<TYPE>& lhs,
                     const CompactedArray_ConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p);
    BSLS_ASSERT(rhs.d_array_p);
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    return lhs.d_index > rhs.d_index;
}

template <class TYPE>
inline
bool bdlc::operator>=(const CompactedArray_ConstIterator<TYPE>& lhs,
                      const CompactedArray_ConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p);
    BSLS_ASSERT(rhs.d_array_p);
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    return lhs.d_index >= rhs.d_index;
}

namespace bdlc {

                           // --------------------
                           // class CompactedArray
                           // --------------------

// PRIVATE MANIPULATORS
template <class TYPE>
void CompactedArray<TYPE>::erase(bsl::size_t index)
{
    BSLS_ASSERT(index < uniqueLength());

    for (bsl::size_t i = 0; i < d_index.length(); ++i) {
        if (d_index[i] > index) {
            d_index.replace(i, d_index[i] - 1);
        }
    }

    d_data.erase(d_data.begin() + index);
}

template <class TYPE>
bsl::size_t CompactedArray<TYPE>::increment(const TYPE& value,
                                            bsl::size_t count)
{
    BSLS_ASSERT(0 < count);

    bsl::size_t index;

    typename Data::iterator iter =
                      bdlb::AlgorithmWorkaroundUtil::lowerBound(d_data.begin(),
                                                                d_data.end(),
                                                                value);

    if (iter == d_data.end()) {
        index = d_data.size();
        d_data.emplace_back(value, count);
    }
    else if (value < iter->d_value.object()) {
        index = iter - d_data.begin();

        d_data.insert(iter,
                      CompactedArray_CountedValue<TYPE>(
                                          value,
                                          count,
                                          d_data.get_allocator().mechanism()));

        for (bsl::size_t i = 0; i < d_index.length(); ++i) {
            if (d_index[i] >= index) {
                d_index.replace(i, d_index[i] + 1);
            }
        }
    }
    else {
        index = iter - d_data.begin();

        iter->d_count += count;
    }

    return index;
}

// CREATORS
template <class TYPE>
CompactedArray<TYPE>::CompactedArray(bslma::Allocator *basicAllocator)
: d_data(basicAllocator)
, d_index(basicAllocator)
{
}

template <class TYPE>
CompactedArray<TYPE>::CompactedArray(bsl::size_t       numElements,
                                     const TYPE&       value,
                                     bslma::Allocator *basicAllocator)
: d_data(basicAllocator)
, d_index(basicAllocator)
{
    if (numElements) {
        d_index.reserveCapacity(numElements, 1);

        d_data.emplace_back(value, numElements);
        d_index.resize(numElements);
    }
}

template <class TYPE>
CompactedArray<TYPE>::CompactedArray(
                                   const CompactedArray<TYPE>&  original,
                                   bslma::Allocator            *basicAllocator)
: d_data(original.d_data, basicAllocator)
, d_index(original.d_index, basicAllocator)
{
}

template <class TYPE>
CompactedArray<TYPE>::~CompactedArray()
{
}

// MANIPULATORS
template <class TYPE>
CompactedArray<TYPE>& CompactedArray<TYPE>::operator=(
                                               const CompactedArray<TYPE>& rhs)
{
    if (this != &rhs) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(rhs.length(), rhs.uniqueLength());
        d_data  = rhs.d_data;
        d_index = rhs.d_index;

        proctor.release();
    }

    return *this;
}

template <class TYPE>
void CompactedArray<TYPE>::append(const TYPE& value)
{
    CompactedArray_RemoveAllProctor<TYPE> proctor(this);

    d_index.reserveCapacity(d_index.length() + 1, d_data.size() + 1);

    d_index.push_back(increment(value));

    proctor.release();
}

template <class TYPE>
void CompactedArray<TYPE>::append(const CompactedArray& srcArray)
{
    if (&srcArray != this) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(d_index.length() + srcArray.d_index.length(),
                                d_data.size()    + srcArray.d_data.size());

        for (bsl::size_t i = 0; i < srcArray.length(); ++i) {
            d_index.push_back(increment(srcArray[i]));
        }

        proctor.release();
    }
    else {
        d_index.reserveCapacity(d_index.length() * 2);

        for (bsl::size_t i = 0; i < d_data.size(); ++i) {
            d_data[i].d_count *= 2;
        }

        d_index.append(d_index);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::append(const CompactedArray& srcArray,
                                  bsl::size_t           srcIndex,
                                  bsl::size_t           numElements)
{
    // Assert 'srcIndex + numElements <= srcArray.length()' without risk of
    // overflow.
    BSLS_ASSERT(numElements <= srcArray.length());
    BSLS_ASSERT(srcIndex    <= srcArray.length() - numElements);

    if (&srcArray != this) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(d_index.length() + numElements,
                                d_data.size()    + numElements);

        for (bsl::size_t i = 0; i < numElements; ++i) {
            d_index.push_back(increment(srcArray[srcIndex + i]));
        }

        proctor.release();
    }
    else {
        d_index.reserveCapacity(d_index.length() + numElements);

        for (bsl::size_t i = 0; i < numElements; ++i) {
            d_data[d_index[srcIndex + i]].d_count += 1;
        }

        d_index.append(d_index, srcIndex, numElements);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::insert(bsl::size_t dstIndex, const TYPE& value)
{
    BSLS_ASSERT(dstIndex <= d_index.length());

    CompactedArray_RemoveAllProctor<TYPE> proctor(this);

    d_index.reserveCapacity(d_index.length() + 1, d_data.size() + 1);

    d_index.insert(dstIndex, increment(value));

    proctor.release();
}

template <class TYPE>
inline
typename CompactedArray<TYPE>::const_iterator
            CompactedArray<TYPE>::insert(const_iterator dst, const TYPE& value)
{
    BSLS_ASSERT(this == dst.d_array_p);

    insert(dst.d_index, value);
    return dst;
}

template <class TYPE>
void CompactedArray<TYPE>::insert(bsl::size_t           dstIndex,
                                  const CompactedArray& srcArray)
{
    BSLS_ASSERT(dstIndex <= d_index.length());

    if (&srcArray != this) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(d_index.length() + srcArray.d_index.length(),
                                d_data.size()    + srcArray.d_data.size());

        for (bsl::size_t i = 0; i < srcArray.length(); ++i) {
            d_index.insert(dstIndex + i, increment(srcArray[i]));
        }

        proctor.release();
    }
    else {
        d_index.reserveCapacity(d_index.length() * 2);

        for (bsl::size_t i = 0; i < d_data.size(); ++i) {
            d_data[i].d_count *= 2;
        }

        d_index.insert(dstIndex, d_index);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::insert(bsl::size_t           dstIndex,
                                  const CompactedArray& srcArray,
                                  bsl::size_t           srcIndex,
                                  bsl::size_t           numElements)
{
    BSLS_ASSERT(dstIndex <= d_index.length());

    // Assert 'srcIndex + numElements <= srcArray.length()' without risk of
    // overflow.
    BSLS_ASSERT(numElements <= srcArray.length());
    BSLS_ASSERT(srcIndex    <= srcArray.length() - numElements);

    if (&srcArray != this) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(d_index.length() + numElements,
                                d_data.size()    + numElements);

        for (bsl::size_t i = 0; i < numElements; ++i) {
            d_index.insert(dstIndex + i, increment(srcArray[srcIndex + i]));
        }

        proctor.release();
    }
    else {
        d_index.reserveCapacity(d_index.length() + numElements);

        for (bsl::size_t i = 0; i < numElements; ++i) {
            d_data[d_index[srcIndex + i]].d_count += 1;
        }

        d_index.insert(dstIndex, d_index, srcIndex, numElements);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::pop_back()
{
    BSLS_ASSERT(!isEmpty());

    bsl::size_t                        dataIndex = d_index.back();
    CompactedArray_CountedValue<TYPE>& dataValue = d_data[dataIndex];

    d_index.pop_back();

    if (0 == --dataValue.d_count) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        erase(dataIndex);

        proctor.release();
    }
}

template <class TYPE>
inline
void CompactedArray<TYPE>::push_back(const TYPE& value)
{
    append(value);
}

template <class TYPE>
inline
void CompactedArray<TYPE>::remove(bsl::size_t dstIndex)
{
    BSLS_ASSERT(dstIndex < d_index.length());

    remove(dstIndex, 1);
}

template <class TYPE>
void CompactedArray<TYPE>::remove(bsl::size_t dstIndex,
                                  bsl::size_t numElements)
{
    // Assert 'dstIndex + numElements <= d_index.length()' without risk of
    // overflow.
    BSLS_ASSERT(numElements <= d_index.length());
    BSLS_ASSERT(dstIndex    <= d_index.length() - numElements);

    CompactedArray_RemoveAllProctor<TYPE> proctor(this);

    bsl::size_t endIndex = dstIndex + numElements;
    for (bsl::size_t i = dstIndex; i < endIndex; ++i) {
        bsl::size_t                        dataIndex = d_index[i];
        CompactedArray_CountedValue<TYPE>& dataValue = d_data[dataIndex];

        if (0 == --dataValue.d_count) {
            erase(dataIndex);
        }
    }

    d_index.remove(dstIndex, numElements);

    proctor.release();
}

template <class TYPE>
inline
typename CompactedArray<TYPE>::const_iterator
                          CompactedArray<TYPE>::remove(const_iterator dstFirst,
                                                       const_iterator dstLast)
{
    BSLS_ASSERT(this     == dstFirst.d_array_p);
    BSLS_ASSERT(this     == dstLast.d_array_p);
    BSLS_ASSERT(dstFirst <= dstLast);

    remove(dstFirst.d_index, dstLast.d_index - dstFirst.d_index);
    return dstFirst;
}

template <class TYPE>
void CompactedArray<TYPE>::removeAll()
{
    d_data.clear();
    d_index.removeAll();
}

template <class TYPE>
void CompactedArray<TYPE>::replace(bsl::size_t dstIndex, const TYPE& value)
{
    BSLS_ASSERT(dstIndex < length());

    CompactedArray_RemoveAllProctor<TYPE> proctor(this);

    d_index.reserveCapacity(d_index.length(), d_data.size() + 1);

    bsl::size_t                        newDataIndex = increment(value);
    bsl::size_t                        dataIndex    = d_index[dstIndex];
    CompactedArray_CountedValue<TYPE>& dataValue    = d_data[dataIndex];

    if (0 == --dataValue.d_count) {
        erase(dataIndex);
        if (dataIndex <= newDataIndex) {
            --newDataIndex;
        }
    }

    d_index.replace(dstIndex, newDataIndex);

    proctor.release();
}

template <class TYPE>
void CompactedArray<TYPE>::replace(bsl::size_t           dstIndex,
                                   const CompactedArray& srcArray,
                                   bsl::size_t           srcIndex,
                                   bsl::size_t           numElements)
{
    // Assert 'dstIndex + numElements <= length()' without risk of overflow.
    BSLS_ASSERT(numElements <= length());
    BSLS_ASSERT(dstIndex    <= length() - numElements);

    // Assert 'srcIndex + numElements <= srcArray.length()' without risk of
    // overflow.
    BSLS_ASSERT(numElements <= srcArray.length());
    BSLS_ASSERT(srcIndex    <= srcArray.length() - numElements);

    CompactedArray_RemoveAllProctor<TYPE> proctor(this);

    if (&srcArray != this) {
        d_index.reserveCapacity(d_index.length(), d_data.size() + numElements);

        for (bsl::size_t i = 0; i < numElements; ++i) {
            bsl::size_t newDataIndex = increment(srcArray[srcIndex + i]);
            bsl::size_t dataIndex    = d_index[dstIndex + i];

            CompactedArray_CountedValue<TYPE>& dataValue = d_data[dataIndex];

            if (0 == --dataValue.d_count) {
                erase(dataIndex);
                if (dataIndex <= newDataIndex) {
                    --newDataIndex;
                }
            }

            d_index.replace(dstIndex + i, newDataIndex);
        }
    }
    else {
        bsl::size_t endIndex;

        endIndex = srcIndex + numElements;
        for (bsl::size_t i = srcIndex; i < endIndex; ++i) {
            ++d_data[d_index[i]].d_count;
        }

        endIndex = dstIndex + numElements;
        for (bsl::size_t i = dstIndex; i < endIndex; ++i) {
            bsl::size_t                        dataIndex = d_index[i];
            CompactedArray_CountedValue<TYPE>& dataValue = d_data[dataIndex];

            if (0 == --dataValue.d_count) {
                erase(dataIndex);
            }
        }

        d_index.replace(dstIndex, d_index, srcIndex, numElements);
    }

    proctor.release();
}

template <class TYPE>
void CompactedArray<TYPE>::reserveCapacity(bsl::size_t numElements)
{
    BSLS_ASSERT(false == isEmpty() || 0 == numElements);

    if (0 < numElements) {
        d_index.reserveCapacity(numElements, d_data.size() - 1);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::reserveCapacity(bsl::size_t numElements,
                                           bsl::size_t numUniqueElements)
{
    BSLS_ASSERT(numUniqueElements <= numElements);
    BSLS_ASSERT(0 < numUniqueElements || 0 == numElements);

    if (0 < numElements) {
        d_data.reserve(numUniqueElements);
        if (d_data.size() > numUniqueElements) {
            numUniqueElements = d_data.size();
        }
        d_index.reserveCapacity(numElements, numUniqueElements - 1);
    }
}

template <class TYPE>
void CompactedArray<TYPE>::resize(bsl::size_t numElements)
{
    if (d_index.length() < numElements) {
        CompactedArray_RemoveAllProctor<TYPE> proctor(this);

        d_index.reserveCapacity(numElements, d_data.size() + 1);

        bsl::size_t count = numElements - d_index.length();
        bsl::size_t index = increment(TYPE(), count);

        for (bsl::size_t i = 0; i < count; ++i) {
            d_index.push_back(index);
        }

        proctor.release();
    }
    else {
        bsl::size_t count = d_index.length() - numElements;

        for (bsl::size_t i = 0; i < count; ++i) {
            pop_back();
        }
    }
}

template <class TYPE>
void CompactedArray<TYPE>::swap(CompactedArray<TYPE>& other)
{
    BSLS_ASSERT(allocator() == other.allocator());

    bslalg::SwapUtil::swap(&d_data,  &other.d_data);
    bslalg::SwapUtil::swap(&d_index, &other.d_index);
}

// ACCESSORS
template <class TYPE>
inline
const TYPE& CompactedArray<TYPE>::operator[](bsl::size_t index) const
{
    BSLS_ASSERT(index < length());

    return d_data[d_index[index]].d_value.object();
}

template <class TYPE>
inline
bslma::Allocator *CompactedArray<TYPE>::allocator() const
{
    return d_index.allocator();
}

template <class TYPE>
inline
const TYPE& CompactedArray<TYPE>::back() const
{
    BSLS_ASSERT(0 < length());

    return operator[](length() - 1);
}

template <class TYPE>
inline
typename CompactedArray<TYPE>::const_iterator
                                            CompactedArray<TYPE>::begin() const
{
    return const_iterator(this, 0);
}

template <class TYPE>
inline
bsl::size_t CompactedArray<TYPE>::capacity() const
{
    return d_index.isEmpty() ? 0 : d_index.capacity();
}

template <class TYPE>
inline
typename CompactedArray<TYPE>::const_iterator CompactedArray<TYPE>::end() const
{
    return const_iterator(this, d_index.length());
}

template <class TYPE>
inline
const TYPE& CompactedArray<TYPE>::front() const
{
    BSLS_ASSERT(0 < length());

    return operator[](0);
}

template <class TYPE>
inline
bool CompactedArray<TYPE>::isEmpty() const
{
    return 0 == length();
}

template <class TYPE>
inline
bool CompactedArray<TYPE>::isEqual(const CompactedArray& other) const
{
    return d_index == other.d_index && d_data == other.d_data;
}

template <class TYPE>
inline
bsl::size_t CompactedArray<TYPE>::length() const
{
    return d_index.length();
}

template <class TYPE>
bsl::ostream& CompactedArray<TYPE>::print(bsl::ostream& stream,
                                          int           level,
                                          int           spacesPerLevel) const
{
    if (stream.bad()) {
        return stream;                                                // RETURN
    }

    bslim::Printer printer(&stream, level, spacesPerLevel);
    printer.start();
    for (bsl::size_t i = 0; i < d_index.length(); ++i) {
        printer.printValue(d_data[d_index[i]].d_value.object());
    }
    printer.end();

    return stream;
}

template <class TYPE>
inline
const TYPE& CompactedArray<TYPE>::uniqueElement(bsl::size_t index) const
{
    BSLS_ASSERT(index < uniqueLength());

    return d_data[index].d_value.object();
}

template <class TYPE>
inline
bsl::size_t CompactedArray<TYPE>::uniqueLength() const
{
    return d_data.size();
}

}  // close package namespace

// FREE OPERATORS
template <class TYPE>
inline
bsl::ostream& bdlc::operator<<(bsl::ostream&               stream,
                               const CompactedArray<TYPE>& array)
{
    return array.print(stream, 0, -1);
}

template <class TYPE>
inline
bool bdlc::operator==(const CompactedArray<TYPE>& lhs,
                      const CompactedArray<TYPE>& rhs)
{
    return lhs.isEqual(rhs);
}

template <class TYPE>
inline
bool bdlc::operator!=(const CompactedArray<TYPE>& lhs,
                      const CompactedArray<TYPE>& rhs)
{
    return !lhs.isEqual(rhs);
}

// FREE FUNCTIONS
template <class TYPE>
void bdlc::swap(CompactedArray<TYPE>& a, CompactedArray<TYPE>& b)
{
    if (a.allocator() == b.allocator()) {
        a.swap(b);

        return;                                                       // RETURN
    }

    CompactedArray<TYPE> futureA(b, a.allocator());
    CompactedArray<TYPE> futureB(a, b.allocator());

    futureA.swap(a);
    futureB.swap(b);
}

// HASH SPECIALIZATIONS
template <class HASHALG, class TYPE>
void bdlc::hashAppend(HASHALG& hashAlg, const CompactedArray<TYPE>& input)
{
    using ::BloombergLP::bslh::hashAppend;
    typedef typename CompactedArray<TYPE>::const_iterator citer;
    hashAppend(hashAlg, input.length());
    for (citer b = input.begin(), e = input.end(); b != e; ++b) {
        hashAppend(hashAlg, *b);
    }
}

}  // close enterprise namespace

// TRAITS

namespace BloombergLP {
namespace bslma {

template <class TYPE>
struct UsesBslmaAllocator<bdlc::CompactedArray_CountedValue<TYPE> >
                                                           : bsl::true_type {};

template <class TYPE>
struct UsesBslmaAllocator<bdlc::CompactedArray<TYPE> > : bsl::true_type {};

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

#endif

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