// bdlc_packedintarray.h                                              -*-C++-*-
#ifndef INCLUDED_BDLC_PACKEDINTARRAY
#define INCLUDED_BDLC_PACKEDINTARRAY

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

//@PURPOSE: Provide an extensible, packed array of integral values.
//
//@CLASSES:
//  bdlc::PackedIntArray: packed array of integral values
//  bdlc::PackedIntArrayConstIterator: bidirectional 'const_iterator'
//
//@DESCRIPTION: This component provides a space-efficient value-semantic array
// class template, 'bdlc::PackedIntArray', and an associated iterator,
// 'bdlc::PackedIntArrayConstIterator', that provides non-modifiable access to
// its elements.  The interface of this class provides the user with
// functionality similar to a 'bsl::vector<int>'.  The implementation is
// designed to reduce dynamic memory usage by storing its contents differently
// according to the magnitude of values placed within it.  The user need not be
// concerned with the internal representation of the data.  The array supports
// primitive operations (e.g., insertion, look-up, removal) as well as a
// complete set of value-semantic operations; however, direct reference to
// individual elements is not available.  Users can access the value of
// individual elements by calling the indexing operator or via iterators.  Note
// that iterators are *not* invalidated if an array object reallocates memory.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: 'Temperature Map'
/// - - - - - - - - - - - - - -
// There exist many applications in which the range of 'int' data that a
// container will hold is not known at design time.  This means in order to
// build a robust component one must default to 'bsl::vector<int>', which for
// many applications is excessive in its usage of space.
//
// Suppose we are creating a map of temperatures for every city in the United
// States for every day.  This represents a large body of data, most of which
// is easily representable in a 'signed char', and in only rare situations is a
// 'short' required.
//
// To be able to represent all possible values for all areas and times,
// including extremes like Death Valley, a traditional implementation would
// require use of a 'vector<short>' for each day for each area.  This is
// excessive for all but the most extreme values, and therefore wasteful for
// this map as a whole.
//
// We can use 'bdlc::PackedIntArray' to efficiently store this data.
//
// First, we declare and define a 'my_Date' class.  This class is very similar
// to 'bdlt::Date', and therefore is elided for the sake of compactness.
//..
//                              // =======
//                              // my_Date
//                              // =======
//  class my_Date {
//      // A (value-semantic) attribute class that provides a very simple date.
//      signed char d_day;    // the day
//      signed char d_month;  // the month
//      int         d_year;   // the year
//
//      // FRIENDS
//      friend bool operator<(const my_Date&, const my_Date&);
//
//    public:
//      // CREATORS
//      explicit my_Date(int         year  = 1,
//                       signed char month = 1,
//                       signed char day   = 1);
//          // Create a 'my_Date' object having the optionally specified 'day',
//          // 'month', and 'year'.  Each, if unspecified, will default to 1.
//  };
//
//  bool operator<(const my_Date& lhs, const my_Date& rhs);
//      // Return 'true' if the specified 'lhs' represents an earlier date than
//      // the specified 'rhs' object, and 'false' otherwise.
//
//                          // -------
//                          // my_Date
//                          // -------
//  // CREATORS
//  inline
//  my_Date::my_Date(int year, signed char month , signed char day)
//  : d_day(day)
//  , d_month(month)
//  , d_year(year)
//  {
//  }
//
//  bool operator<(const my_Date& lhs, const my_Date& rhs)
//  {
//      return 10000 * lhs.d_year + 100 * lhs.d_month + lhs.d_day <
//             10000 * rhs.d_year + 100 * rhs.d_month + rhs.d_day;
//  }
//..
// Then, we create our 'temperatureMap', which is a map of dates to a map of
// zip codes to a 'PackedIntArray' of temperatures.  Each 'PackedIntArray' has
// entries for each temperature from 12 A.M, to 11 P.M for each city in each
// zip code.  Notice that we use a 'PackedIntArray' to hold the data compactly.
//..
//  bsl::map<my_Date, bsl::map<bsl::string, bdlc::PackedIntArray<int> > >
//                                                              temperatureMap;
//..
// Next, we add data to the map (provided by the National Weather Service) for
// a normal case, and the extreme.
//..
//  bdlc::PackedIntArray<int>& nyc
//                         = temperatureMap[my_Date(2013, 9,  6)]["10023"];
//  bdlc::PackedIntArray<int>& dValley
//                         = temperatureMap[my_Date(1913, 7, 10)]["92328"];
//  bdlc::PackedIntArray<int>& boston
//                         = temperatureMap[my_Date(2013, 9,  6)]["02202"];
//
//  int nycTemperatures[24]  = { 60,  58, 57,  56,  55,  54,  54,  55,
//                               56,  59, 61,  64,  66,  67,  69,  69,
//                               70,  70, 68,  67,  65,  63,  61,  60};
//
//  int deathValleyTemps[24] = { 65,  55, 50,  47,  62,  75,  77,  89,
//                               91,  92, 95, 110, 113, 121, 134, 126,
//                              113,  99, 96,  84,  79,  81,  73,  69};
//
//  int bostonTemps[24]      = { 55,  53, 52,  51,  50,  49,  49,  50,
//                               51,  54, 56,  59,  61,  62,  64,  64,
//                               65,  65, 63,  62,  60,  58,  56,  55};
//..
// Then, since the size of the data set is known at design time, as well as
// extreme values for the areas, we can use the 'reserveCapacity()' method to
// give the container hints about the data to come.
//..
//  nyc.reserveCapacity    (24, 54,  70);
//  dValley.reserveCapacity(24, 47, 134);
//  boston.reserveCapacity (24, 49,  65);
//..
// Now we add the data to the respective containers.
//..
//  for (bsl::size_t i= 0; i < 24; ++i) {
//      nyc.append(nycTemperatures[i]);
//      dValley.append(deathValleyTemps[i]);
//      boston.append(bostonTemps[i]);
//  }
//..
// Finally, notice that in order to represent these values in a
// 'PackedIntArray', it required '24 * sizeof(signed char)' bytes (24 on most
// systems) of dynamic memory for 'nyc', which represents the normal case for
// this data.  A 'vector<short>' would require '24 * sizeof(short)' bytes (48
// on most systems) of dynamic memory to represent the same data.
//..
//  assert(static_cast<int>(sizeof(signed char)) == nyc.bytesPerElement());
//  assert(                                   24 == nyc.length());
//..

#include <bdlscm_version.h>

#include <bslalg_swaputil.h>

#include <bslh_hash.h>

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

#include <bslmf_conditional.h>
#include <bslmf_issame.h>

#include <bsls_assert.h>
#include <bsls_performancehint.h>
#include <bsls_review.h>
#include <bsls_types.h>

#include <bsl_cstddef.h>
#include <bsl_cstdint.h>
#include <bsl_cstring.h>
#include <bsl_limits.h>
#include <bsl_iosfwd.h>

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

namespace BloombergLP {
namespace bdlc {

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

template <class TYPE> class PackedIntArrayConstIterator;

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

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

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

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

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

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

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

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

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

                      // ===============================
                      // struct PackedIntArrayImp_Signed
                      // ===============================

struct PackedIntArrayImp_Signed {
    // This 'struct' provides a namespace for types and methods used to
    // implement a space-efficient value-semantic array class representing a
    // sequence of 'TYPE' elements; 'TYPE' must be convertible to either a
    // 'bsl::int64_t'.  Specifically, it defines the types used to store the
    // array's data, methods needed to externalize and unexternalize the array,
    // and a method to determine the storage size to use for a given value.

    // PUBLIC TYPES
    typedef bsl::int8_t  OneByteStorageType;
    typedef bsl::int16_t TwoByteStorageType;
    typedef bsl::int32_t FourByteStorageType;
    typedef bsl::int64_t EightByteStorageType;

    // CLASS METHODS
    template <class STREAM>
    static void bdexGet8(STREAM& stream, bsl::int8_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet16(STREAM& stream, bsl::int16_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet32(STREAM& stream, bsl::int32_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet64(STREAM& stream, bsl::int64_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut8(STREAM& stream, bsl::int8_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut16(STREAM& stream, bsl::int16_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut32(STREAM& stream, bsl::int32_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut64(STREAM& stream, bsl::int64_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    static int requiredBytesPerElement(EightByteStorageType value);
        // Return the required number of bytes to store the specified 'value'.
};

                     // =================================
                     // struct PackedIntArrayImp_Unsigned
                     // =================================

struct PackedIntArrayImp_Unsigned {
    // This 'struct' provides a namespace for types and methods used to
    // implement a space-efficient value-semantic array class representing a
    // sequence of 'TYPE' elements; 'TYPE' must be convertible to either a
    // 'bsl::uint64_t'.  Specifically, it defines the types used to store the
    // array's data, methods needed to externalize and unexternalize the array,
    // and a method to determine the storage size to use for a given value.

    // PUBLIC TYPES
    typedef bsl::uint8_t  OneByteStorageType;
    typedef bsl::uint16_t TwoByteStorageType;
    typedef bsl::uint32_t FourByteStorageType;
    typedef bsl::uint64_t EightByteStorageType;

    // CLASS METHODS
    template <class STREAM>
    static void bdexGet8(STREAM& stream, bsl::uint8_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet16(STREAM& stream, bsl::uint16_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet32(STREAM& stream, bsl::uint32_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexGet64(STREAM& stream, bsl::uint64_t& variable);
        // Read from the specified 'stream' the specified 'variable' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut8(STREAM& stream, bsl::uint8_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut16(STREAM& stream, bsl::uint16_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut32(STREAM& stream, bsl::uint32_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    template <class STREAM>
    static void bdexPut64(STREAM& stream, bsl::uint64_t value);
        // Write to the specified 'stream' the specified 'value' as per the
        // requirements of the BDEX protocol.

    static int requiredBytesPerElement(EightByteStorageType value);
        // Return the required number of bytes to store the specified 'value'.
};

                          // =======================
                          // class PackedIntArrayImp
                          // =======================

template <class STORAGE>
class PackedIntArrayImp {
    // This space-efficient value-semantic array class represents a sequence of
    // 'STORAGE::EightByteStorageType' elements;
    // 'STORAGE::EightByteStorageType' must be convertible to either a signed
    // or unsigned 64-bit integer using 'static_cast'.  The interface provides
    // functionality similar to a 'vector<int>' however references to
    // individual elements are not provided.

  public:
    // PUBLIC TYPES
    typedef typename STORAGE::EightByteStorageType ElementType;

    // CLASS DATA
    static const bsl::size_t k_MAX_CAPACITY = 0x7fffffff;  // maximum capacity
                                                           // in bytes

  private:
    // DATA
    void             *d_storage_p;        // allocated memory

    bsl::size_t       d_length;           // length of the array

    int               d_bytesPerElement;  // number of bytes used to store each
                                          // element

    bsl::size_t       d_capacityInBytes;  // capacity of the array

    bslma::Allocator *d_allocator_p;      // allocator used for all memory

  private:
    // PRIVATE CLASS METHODS
    static bsl::size_t nextCapacityGE(bsl::size_t minValue, bsl::size_t value);
        // Return the next valid number of bytes of capacity that is at least
        // the specified 'minValue', starting from the specified 'value'.

    // PRIVATE MANIPULATORS
    void expandImp(int         requiredBytesPerElement,
                   bsl::size_t requiredCapacityInBytes);
        // Make the capacity of this array at least the specified
        // 'requiredCapacityInBytes' and increase the bytes used to store an
        // element to the specified 'requiredBytesPerElement'.  The behavior is
        // undefined unless 'requiredBytesPerElement > bytesPerElement()'.

    void replaceImp(bsl::size_t dstIndex, ElementType 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()' and the required bytes to store the 'value' is
        // less than or equal to 'bytesPerElement()'.

    void replaceImp(void        *dst,
                    bsl::size_t  dstIndex,
                    int          dstBytesPerElement,
                    void        *src,
                    bsl::size_t  srcIndex,
                    int          srcBytesPerElement,
                    bsl::size_t  numElements);
        // Change the values of the specified 'numElements' elements in the
        // specified 'dst' array beginning at the specified 'dstIndex' with the
        // specified 'dstBytesPerElement' to those of the 'numElements' values
        // in the specified 'src' array beginning at the specified 'srcIndex'
        // with the specified 'srcBytesPerElement'.  The behavior is undefined
        // unless the source array has sufficient values,
        // 'dstIndex + numElements <= length()',
        // 'srcBytesPerElement != dstBytesPerElement', and either the memory
        // ranges do not overlap or: 'dst == src' and 'dstIndex >= srcIndex'
        // and 'dstBytesPerElement > srcBytesPerElement'.

    // PRIVATE ACCESSORS
    char *address() const;
        // Return the address of the storage as a 'char *'.

    bool isEqualImp(const PackedIntArrayImp& other) const;
        // Return 'true' if this and the specified 'other' array have the same
        // value, and 'false' otherwise.  Two 'PackedIntArrayImp' arrays have
        // the same value if they have the same length, and all corresponding
        // elements (those at the same indices) have the same value.  The
        // behavior is undefined unless 'length() == other.length()' and
        // 'bytesPerElement() != other.bytesPerElement()'.

    int requiredBytesPerElement(bsl::size_t index,
                                bsl::size_t numElements) const;
        // Return the required number of bytes to store the specified
        // 'numValues' values of this array starting at the specified 'index'.
        // The behavior is undefined unless 'index + numElements <= length()'.

  public:
    // CLASS METHODS
    static int maxSupportedBdexVersion(int serializationVersion);
        // Return the 'version' to be used with the 'bdexStreamOut' method
        // corresponding to the specified 'serializationVersion'.  See the
        // 'bslx' package-level documentation for more information on BDEX
        // streaming of value-semantic types and containers.

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

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

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

    ~PackedIntArrayImp();
        // Destroy this object

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

    void append(ElementType value);
        // Append an element having the specified 'value' to the end of this
        // array.

    void append(const PackedIntArrayImp& srcArray);
        // Append the sequence of values represented by the specified
        // 'srcArray' to the end of this array.  Note that if this array and
        // 'srcArray' are the same, the behavior is as if a copy of 'srcArray'
        // were passed.

    void append(const PackedIntArrayImp& srcArray,
                bsl::size_t              srcIndex,
                bsl::size_t              numElements);
        // Append the sequence of values of the specified 'numElements'
        // starting at the specified 'srcIndex' in the specified 'srcArray' to
        // the end of this array.  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.

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // Assign to this object the value read from the specified input
        // 'stream' using the specified 'version' format, and return a
        // reference to 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'version' is not supported, this object
        // is unaltered and 'stream' is invalidated but otherwise unmodified.
        // If 'version' is supported but 'stream' becomes invalid during this
        // operation, this object has an undefined, but valid, state.  Note
        // that no version is read from 'stream'.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

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

    void insert(bsl::size_t dstIndex, const PackedIntArrayImp& srcArray);
        // Insert into this array, at the specified 'dstIndex', the sequence of
        // values represented by the specified 'srcArray', shifting any
        // elements originally at or above 'dstIndex' up by 'srcArray.length()'
        // indices higher.  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 PackedIntArrayImp& srcArray,
                bsl::size_t              srcIndex,
                bsl::size_t              numElements);
        // Insert into this array, at the specified 'dstIndex', the specified
        // 'numElements' values in the specified 'srcArray' starting at the
        // specified 'srcIndex'.  Elements greater than or equal to 'dstIndex'
        // are shifted up 'numElements' 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(ElementType value);
        // Append an element having the specified 'value' to the end of this
        // array.

    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, starting at the specified 'dstIndex', the
        // specified 'numElements'.  Shift the elements of this array that are
        // at 'dstIndex + numElements' or above to 'numElements' indices lower.
        // The behavior is undefined unless
        // 'dstIndex + numElements <= length()'.

    void removeAll();
        // Remove all the elements from this array and set the storage required
        // per element to one byte.

    void replace(bsl::size_t dstIndex, ElementType 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 PackedIntArrayImp& srcArray,
                 bsl::size_t              srcIndex,
                 bsl::size_t              numElements);
        // Change the values of the specified 'numElements' elements in this
        // array beginning at the specified 'dstIndex' to those of the
        // 'numElements' values in the specified 'srcArray' beginning at the
        // specified 'srcIndex'.  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 reserveCapacityImp(bsl::size_t requiredCapacityInBytes);
        // Make the capacity of this array at least the specified
        // 'requiredCapacityInBytes'.  This method has no effect if the
        // current capacity meets or exceeds the required capacity.

    void reserveCapacity(bsl::size_t numElements);
        // Make the capacity of this array at least the specified
        // 'numElements' assuming the current 'bytesPerElement()'.  This
        // method has no effect if the current capacity meets or exceeds the
        // required capacity.

    void reserveCapacity(bsl::size_t numElements, ElementType maxValue);
        // Make the capacity of this array at least the specified
        // 'numElements'.  The specified 'maxValue' denotes the maximum element
        // value that will be subsequently added to this array.  After this
        // call 'numElements' having values in the range '[0, maxValue]' are
        // guaranteed to not cause a reallocation.  This method has no effect
        // if the current capacity meets or exceeds the required capacity.
        // The behavior is undefined unless '0 <= maxValue'.

    void reserveCapacity(bsl::size_t numElements,
                         ElementType minValue,
                         ElementType maxValue);
        // Make the capacity of this array at least the specified
        // 'numElements'.  The specified 'minValue' and 'maxValue' denote,
        // respectively, the minimum and maximum elements values that will be
        // subsequently added to this array.  After this call 'numElements'
        // having values in the range '[minValue, maxValue]' are guaranteed to
        // not cause a reallocation.  This method has no effect if the current
        // capacity meets or exceeds the required capacity.  The behavior is
        // undefined unless 'minValue <= maxValue'.

    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 0.

    void swap(PackedIntArrayImp& 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
    ElementType operator[](bsl::size_t index) const;
        // Return the value of the element at the specified 'index'.  The
        // behavior is undefined unless 'index < length()'.

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

    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write this value to the specified output 'stream' using the
        // specified 'version' format, and return a reference to 'stream'.  If
        // 'stream' is initially invalid, this operation has no effect.  If
        // 'version' is not supported, 'stream' is invalidated but otherwise
        // unmodified.  Note that 'version' is not written to 'stream'.  See
        // the 'bslx' package-level documentation for more information on BDEX
        // streaming of value-semantic types and containers.

    int bytesPerElement() const;
        // Return the number of bytes currently used to store each element in
        // this array.

    bsl::size_t capacity() const;
        // Return the number of elements this array can hold in terms of the
        // current data type used to store its elements.

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

    bool isEqual(const PackedIntArrayImp& other) const;
        // Return 'true' if this and the specified 'other' array have the same
        // value, and 'false' otherwise.  Two 'PackedIntArrayImp' 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 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.
};

                        // ============================
                        // struct PackedIntArrayImpType
                        // ============================

template <class TYPE>
struct PackedIntArrayImpType {
    // This meta-function selects
    // 'PackedIntArrayImp<PackedIntArrayImp_Unsigned>' if 'TYPE' should be
    // stored as an unsigned integer, and
    // 'PackedIntArrayImp<PackedIntArrayImp_Signed>' otherwise.

    // TYPES
    typedef typename bsl::conditional<
                      bsl::is_same<TYPE, unsigned char>::value
                          || bsl::is_same<TYPE, unsigned short>::value
                          || bsl::is_same<TYPE, unsigned int>::value
                          || bsl::is_same<TYPE, unsigned long int>::value
                          || bsl::is_same<TYPE, bsls::Types::Uint64>::value
                          || bsl::is_same<TYPE, bsl::uint8_t>::value
                          || bsl::is_same<TYPE, bsl::uint16_t>::value
                          || bsl::is_same<TYPE, bsl::uint32_t>::value
                          || bsl::is_same<TYPE, bsl::uint64_t>::value,
                      PackedIntArrayImp<PackedIntArrayImp_Unsigned>,
                      PackedIntArrayImp<PackedIntArrayImp_Signed> >::type Type;
};

                     // =================================
                     // class PackedIntArrayConstIterator
                     // =================================

template <class TYPE>
class PackedIntArrayConstIterator {
    // This unconstrained (value-semantic) class represents a random access
    // iterator providing non-modifiable access to the elements of a
    // 'PackedIntArray'.  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.  The returned iterator,
    // 'it', referencing an element within a 'PackedIntArray', 'array', remains
    // valid while '0 <= it - array.begin() < array.length()'.

    // PRIVATE TYPES
    typedef typename PackedIntArrayImpType<TYPE>::Type ImpType;

    // DATA
    const ImpType *d_array_p; // A pointer to the 'PackedIntArrayImp' into
                              // which this iterator references.

    bsl::size_t    d_index;   // The index of the referenced value within the
                              // array.

    // FRIENDS
    friend class PackedIntArray<TYPE>;

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

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

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

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

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

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

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

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

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

  public:
    // PUBLIC TYPES

    // The following typedefs 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
                                                                // all returns
                                                                // of element
                                                                // values.

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

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

  private:
    // PRIVATE CREATORS
    PackedIntArrayConstIterator(const ImpType *array, bsl::size_t index);
        // Create a 'PackedIntArrayConstIterator' object with a pointer to the
        // specified 'array' and the specified 'index'.  The behavior is
        // undefined unless 'index <= array->length()'.

  public:
    // CREATORS
    PackedIntArrayConstIterator();
        // Create a default 'PackedIntArrayConstIterator'.  Note that the use
        // of most methods - as indicated in their documentation - upon this
        // iterator will result in undefined behavior.

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

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

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

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

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

    PackedIntArrayConstIterator& operator+=(bsl::ptrdiff_t offset);
        // Advance this iterator by the specified 'offset' from the element
        // referenced to this iterator.  The returned iterator, 'it',
        // referencing an element within a 'PackedIntArray', 'array', remains
        // valid as long as '0 <= it - array.begin() <= array.length()'.  The
        // behavior is undefined unless
        // 'PackedIntArrayConstInterator() != *this' and
        // '0 <= *this - array.begin() + offset <= array.length()'.

    PackedIntArrayConstIterator& operator-=(bsl::ptrdiff_t offset);
        // Decrement this iterator by the specified 'offset' from the element
        // referenced to this iterator.  The returned iterator, 'it',
        // referencing an element within a 'PackedIntArray', 'array', remains
        // valid as long as '0 <= it - array.begin() <= array.length()'.  The
        // behavior is undefined unless
        // 'PackedIntArrayConstInterator() != *this' and
        // '0 <= *this - array.begin() - offset <= array.length()'.

    // ACCESSORS
    TYPE operator*() const;
        // Return the element value referenced by this iterator.  The behavior
        // is undefined unless for this iterator, referencing an element within
        // a 'PackedIntArray' 'array',
        // 'PackedIntArrayConstInterator() != *this' and
        // '*this - array.begin() < array.length()'.

    TYPE operator->() const;
        // Return the element value referenced by this iterator.  The behavior
        // is undefined unless for this iterator, referencing an element within
        // a 'PackedIntArray' 'array',
        // 'PackedIntArrayConstInterator() != *this' and
        // '*this - array.begin() < array.length()'.

    TYPE operator[](bsl::ptrdiff_t offset) const;
        // Return the element that is the specified 'offset' from the element
        // reference by this array.  The behavior is undefined unless for this
        // iterator, referencing an element within a 'PackedIntArray' 'array',
        // 'PackedIntArrayConstInterator() != *this' and
        // '0 <= *this - array.begin() + offset < array.length()'.

    PackedIntArrayConstIterator operator+(bsl::ptrdiff_t offset) const;
        // Return an iterator referencing the location at the specified
        // 'offset' from the element referenced by this iterator.  The returned
        // iterator, 'it', referencing an element within a 'PackedIntArray',
        // 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless
        // '0 <= *this - array.begin() + offset <= array.length()'.

    PackedIntArrayConstIterator operator-(bsl::ptrdiff_t offset) const;
        // Return an iterator referencing the location at the specified
        // 'offset' from the element referenced by this iterator.  The returned
        // iterator, 'it', referencing an element within a 'PackedIntArray',
        // 'array', remains valid as long as
        // '0 <= it - array.begin() <= array.length()'.  The behavior is
        // undefined unless 'PackedIntArrayConstInterator() != *this' and
        // '0 <= *this - array.begin() - offset <= array.length()'.
};

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

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

template <class TYPE>
bool operator==(const PackedIntArrayConstIterator<TYPE>& lhs,
                const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators have the same
    // value, and 'false' otherwise.  Two 'PackedIntArrayConstIterator'
    // iterators have the same value if they refer to the same array, and have
    // the same index.

template <class TYPE>
bool operator!=(const PackedIntArrayConstIterator<TYPE>& lhs,
                const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not have the
    // same value and 'false' otherwise.  Two 'PackedIntArrayConstIterator'
    // iterators do not have the same value if they do not refer to the same
    // array, or do not have the same index.

template <class TYPE>
bsl::ptrdiff_t operator-(const PackedIntArrayConstIterator<TYPE>& lhs,
                         const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return the number of elements between specified 'lhs' and 'rhs'.  The
    // behavior is undefined unless 'lhs' and 'rhs' reference the same array.

template <class TYPE>
bool operator<(const PackedIntArrayConstIterator<TYPE>& lhs,
               const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', 'false' otherwise.  An iterator has a value less than another if
    // its index is less the other's index.  The behavior is undefined unless
    // 'lhs' and 'rhs' refer to the same array.

template <class TYPE>
bool operator<=(const PackedIntArrayConstIterator<TYPE>& lhs,
                const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value less than or equal to
    // the specified 'rhs, 'false' otherwise.  An iterator has a value less
    // than or equal to another if its index is less or equal the other's
    // index.  The behavior is undefined unless 'lhs' and 'rhs' refer to the
    // same array.

template <class TYPE>
bool operator>(const PackedIntArrayConstIterator<TYPE>& lhs,
               const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value greater than the
    // specified 'rhs', 'false' otherwise.  An iterator has a value greater
    // than another if its index is greater the other's index.  The behavior is
    // undefined unless 'lhs' and 'rhs' refer to the same array.

template <class TYPE>
bool operator>=(const PackedIntArrayConstIterator<TYPE>& lhs,
                const PackedIntArrayConstIterator<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' has a value greater or equal than
    // the specified 'rhs', 'false' otherwise.  An iterator has a value greater
    // than or equal to another if its index is greater the other's index.  The
    // behavior is undefined unless 'lhs' and 'rhs' refer to the same array.

                            // ====================
                            // class PackedIntArray
                            // ====================

template <class TYPE>
class PackedIntArray {
    // This space-efficient value-semantic array class represents a sequence of
    // 'TYPE' elements; 'TYPE' must be convertible to either a signed or
    // unsigned 64-bit integer using 'static_cast'.  The interface provides
    // functionality similar to a 'vector<int>' however 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<int>' are *not* invalidated upon reallocation.

    // PRIVATE TYPES
    typedef typename PackedIntArrayImpType<TYPE>::Type ImpType;

    // PRIVATE CLASS DATA
    static const bsl::size_t k_MAX_BYTES_PER_ELEMENT = 8;

    // DATA
    ImpType d_imp;  // Implementation of either a signed or unsigned 64-bit
                    // integer packed array.

  public:
    // PUBLIC TYPES
    typedef TYPE value_type;  // The type for all returns of element values.

    typedef PackedIntArrayConstIterator<TYPE> const_iterator;

    // CLASS METHODS
    static int maxSupportedBdexVersion(int serializationVersion);
        // Return the 'version' to be used with the 'bdexStreamOut' method
        // corresponding to the specified 'serializationVersion'.  See the
        // 'bslx' package-level documentation for more information on BDEX
        // streaming of value-semantic types and containers.

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

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

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

    ~PackedIntArray();
        // Destroy this object

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

    void append(TYPE value);
        // Append an element having the specified 'value' to the end of this
        // array.

    void append(const PackedIntArray& srcArray);
        // Append the sequence of values represented by the specified
        // 'srcArray' to the end of this array.  Note that if this array and
        // 'srcArray' are the same, the behavior is as if a copy of 'srcArray'
        // were passed.

    void append(const PackedIntArray& srcArray,
                bsl::size_t           srcIndex,
                bsl::size_t           numElements);
        // Append the sequence of values of the specified 'numElements'
        // starting at the specified 'srcIndex' in the specified 'srcArray' to
        // the end of this array.  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.

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // Assign to this object the value read from the specified input
        // 'stream' using the specified 'version' format, and return a
        // reference to 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'version' is not supported, this object
        // is unaltered and 'stream' is invalidated but otherwise unmodified.
        // If 'version' is supported but 'stream' becomes invalid during this
        // operation, this object has an undefined, but valid, state.  Note
        // that no version is read from 'stream'.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

    void insert(bsl::size_t dstIndex, 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.  The behavior is undefined unless
        // 'dstIndex <= length()'.

    const_iterator insert(const_iterator dst, 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.  Return an iterator to the newly inserted element.

    void insert(bsl::size_t dstIndex, const PackedIntArray& srcArray);
        // Insert into this array, at the specified 'dstIndex', the sequence of
        // values represented by the specified 'srcArray', shifting any
        // elements originally at or above 'dstIndex' up by 'srcArray.length()'
        // indices higher.  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 PackedIntArray& srcArray,
                bsl::size_t           srcIndex,
                bsl::size_t           numElements);
        // Insert into this array, at the specified 'dstIndex', the specified
        // 'numElements' values in the specified 'srcArray' starting at the
        // specified 'srcIndex'.  Elements greater than or equal to 'dstIndex'
        // are shifted up 'numElements' 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(TYPE value);
        // Append an element having the specified 'value' to the end of this
        // array.

    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, starting at the specified 'dstIndex', the
        // specified 'numElements', shifting the elements of this array that
        // are at 'dstIndex + numElements' or above to 'numElements' indices
        // lower.  The behavior is undefined unless
        // 'dstIndex + numElements <= length()'.

    const_iterator remove(const_iterator dstFirst, const_iterator dstLast);
        // Remove from this array the elements starting from the specified
        // 'dstFirst' up to, but not including, the specified 'dstLast',
        // shifting the elements of this array that are at or above 'dstLast'
        // to 'dstLast - dstFirst' indices lower.  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 <= dstLast'.

    void removeAll();
        // Remove all the elements from this array and set the storage required
        // per element to one byte.

    void replace(bsl::size_t dstIndex, 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 PackedIntArray& srcArray,
                 bsl::size_t           srcIndex,
                 bsl::size_t           numElements);
        // Change the values of the specified 'numElements' elements in this
        // array beginning at the specified 'dstIndex' to those of the
        // 'numElements' values in the specified 'srcArray' beginning at the
        // specified 'srcIndex'.  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'.  This method has no effect if the current capacity
        // meets or exceeds the required capacity.

    void reserveCapacity(bsl::size_t numElements, TYPE maxValue);
        // Make the capacity of this array at least the specified
        // 'numElements'.  The specified 'maxValue' denotes the maximum element
        // value that will be subsequently added to this array.  After this
        // call 'numElements' having values in the range '[0, maxValue]' are
        // guaranteed to not cause a reallocation.  This method has no effect
        // if the current capacity meets or exceeds the required capacity.
        // The behavior is undefined unless '0 <= maxValue'.

    void reserveCapacity(bsl::size_t numElements,
                         TYPE        minValue,
                         TYPE        maxValue);
        // Make the capacity of this array at least the specified
        // 'numElements'.  The specified 'minValue' and 'maxValue' denote,
        // respectively, the minimum and maximum elements values that will be
        // subsequently added to this array.  After this call 'numElements'
        // having values in the range '[minValue, maxValue]' are guaranteed to
        // not cause a reallocation.  This method has no effect if the current
        // capacity meets or exceeds the required capacity.  The behavior is
        // undefined unless 'minValue <= maxValue'.

    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 0.

    void swap(PackedIntArray& 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
    TYPE operator[](bsl::size_t index) const;
        // Return the value of the element at the specified 'index'.  The
        // behavior is undefined unless 'index < length()'.

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

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

    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write this value to the specified output 'stream' using the
        // specified 'version' format, and return a reference to 'stream'.  If
        // 'stream' is initially invalid, this operation has no effect.  If
        // 'version' is not supported, 'stream' is invalidated but otherwise
        // unmodified.  Note that 'version' is not written to 'stream'.  See
        // the 'bslx' package-level documentation for more information on BDEX
        // streaming of value-semantic types and containers.

    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).  This reference
        // remains valid as long as this array exists.

    int bytesPerElement() const;
        // Return the number of bytes currently used to store each element in
        // this array.

    bsl::size_t capacity() const;
        // Return the number of elements this array can hold in terms of the
        // current data type used to store its elements.

    const_iterator end() const;
        // Return an iterator referring to one element beyond the last element
        // in this array.  This reference remains valid as long as this array
        // exists, and length does not decrease.

    TYPE front() const;
        // Return the value of the element at the front of this array.  The
        // behavior is undefined unless '0 < length()'.  Note that this
        // function 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 PackedIntArray& other) const;
        // Return 'true' if this and the specified 'other' array have the same
        // value, and 'false' otherwise.  Two 'PackedIntArray' 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 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.
};

// FREE OPERATORS
template <class TYPE>
bsl::ostream& operator<<(bsl::ostream&               stream,
                         const PackedIntArray<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 PackedIntArray<TYPE>& lhs,
                const PackedIntArray<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' arrays have the same
    // value, and 'false' otherwise.  Two 'PackedIntArray' 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 PackedIntArray<TYPE>& lhs,
                const PackedIntArray<TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' arrays do not have the
    // same value, and 'false' otherwise.  Two 'PackedIntArray' 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(PackedIntArray<TYPE>& a, PackedIntArray<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 PackedIntArray<TYPE>& input);
    // Pass the specified 'input' to the specified 'hashAlg'

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

                      // -------------------------------
                      // struct PackedIntArrayImp_Signed
                      // -------------------------------

template <class STREAM>
void PackedIntArrayImp_Signed::bdexGet8(STREAM& stream, bsl::int8_t& variable)
{
    char v = 0;
    stream.getInt8(v);
    variable = static_cast<bsl::int8_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexGet16(STREAM&       stream,
                                         bsl::int16_t& variable)
{
    short v = 0;
    stream.getInt16(v);
    variable = static_cast<bsl::int16_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexGet32(STREAM&       stream,
                                         bsl::int32_t& variable)
{
    int v = 0;
    stream.getInt32(v);
    variable = static_cast<bsl::int32_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexGet64(STREAM&       stream,
                                         bsl::int64_t& variable)
{
    bsls::Types::Int64 v = 0;
    stream.getInt64(v);
    variable = static_cast<bsl::int64_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexPut8(STREAM& stream, bsl::int8_t value)
{
    stream.putInt8(static_cast<int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexPut16(STREAM& stream, bsl::int16_t value)
{
    stream.putInt16(static_cast<int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexPut32(STREAM& stream, bsl::int32_t value)
{
    stream.putInt32(static_cast<int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Signed::bdexPut64(STREAM& stream, bsl::int64_t value)
{
    stream.putInt64(static_cast<bsls::Types::Int64>(value));
}

                     // ---------------------------------
                     // struct PackedIntArrayImp_Unsigned
                     // ---------------------------------

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexGet8(STREAM&       stream,
                                          bsl::uint8_t& variable)
{
    unsigned char v;
    stream.getUint8(v);
    variable = static_cast<bsl::uint8_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexGet16(STREAM&        stream,
                                           bsl::uint16_t& variable)
{
    unsigned short v;
    stream.getUint16(v);
    variable = static_cast<bsl::uint16_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexGet32(STREAM&        stream,
                                           bsl::uint32_t& variable)
{
    unsigned int v;
    stream.getUint32(v);
    variable = static_cast<bsl::uint32_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexGet64(STREAM&        stream,
                                           bsl::uint64_t& variable)
{
    bsls::Types::Uint64 v;
    stream.getUint64(v);
    variable = static_cast<bsl::uint64_t>(v);
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexPut8(STREAM& stream, bsl::uint8_t value)
{
    stream.putUint8(static_cast<unsigned int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexPut16(STREAM& stream, bsl::uint16_t value)
{
    stream.putUint16(static_cast<unsigned int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexPut32(STREAM& stream, bsl::uint32_t value)
{
    stream.putUint32(static_cast<unsigned int>(value));
}

template <class STREAM>
void PackedIntArrayImp_Unsigned::bdexPut64(STREAM& stream, bsl::uint64_t value)
{
    stream.putUint64(static_cast<bsls::Types::Uint64>(value));
}

                          // ------------------------
                          // struct PackedIntArrayImp
                          // ------------------------

// PRIVATE CLASS METHODS
template <class STORAGE>
inline
bsl::size_t PackedIntArrayImp<STORAGE>::nextCapacityGE(bsl::size_t minValue,
                                                       bsl::size_t value)
{
    BSLS_ASSERT(minValue <= k_MAX_CAPACITY);

    static const bsl::size_t k_TOP_CAPACITY = k_MAX_CAPACITY / 3 * 2 - 3;

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(minValue >= k_TOP_CAPACITY)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return minValue;                                              // RETURN
    }

    while (value < minValue) {
        value += (value + 3) / 2;
    }

    return value;
}

// PRIVATE ACCESSORS
template <class STORAGE>
inline
char *PackedIntArrayImp<STORAGE>::address() const
{
    return static_cast<char *>(d_storage_p);
}

// CLASS METHODS
template <class STORAGE>
inline
int PackedIntArrayImp<STORAGE>::maxSupportedBdexVersion(int)
{
    return 1;
}

// MANIPULATORS
template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::
                             append(const PackedIntArrayImp<STORAGE>& srcArray)
{
    append(srcArray, 0, srcArray.d_length);
}

template <class STORAGE>
template <class STREAM>
inline
STREAM& PackedIntArrayImp<STORAGE>::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            int tmpBytesPerElement;
            {
                char v = 0;
                stream.getInt8(v);
                tmpBytesPerElement = static_cast<int>(v);
            }
            if (   1 != tmpBytesPerElement
                && 2 != tmpBytesPerElement
                && 4 != tmpBytesPerElement
                && 8 != tmpBytesPerElement) {
                stream.invalidate();
            }
            else {
                bsl::size_t tmpLength;
                {
                    int v;
                    stream.getLength(v);
                    tmpLength = static_cast<bsl::size_t>(v);
                }
                if (stream) {
                    bsl::size_t numBytes = tmpBytesPerElement * tmpLength;
                    if (numBytes > d_capacityInBytes) {
                        // Compute next capacity level.

                        bsl::size_t requiredCapacityInBytes =
                                   nextCapacityGE(numBytes, d_capacityInBytes);

                        // Allocate new memory.
                        void *dst =
                              d_allocator_p->allocate(requiredCapacityInBytes);

                        // Deallocate original memory.

                        d_allocator_p->deallocate(d_storage_p);

                        // Update storage and capacity.

                        d_storage_p = dst;
                        d_capacityInBytes = requiredCapacityInBytes;
                    }

                    // Update bytes per element and length.

                    d_bytesPerElement = tmpBytesPerElement;
                    d_length = tmpLength;

                    // Populate the data from the stream.

                    switch (d_bytesPerElement) {
                      case 1: {
                        typename STORAGE::OneByteStorageType *s =
                            static_cast<typename STORAGE::OneByteStorageType *>
                                                                 (d_storage_p);
                        for (bsl::size_t i = 0; i < d_length; ++i) {
                            STORAGE::bdexGet8(stream, s[i]);
                        }
                      } break;
                      case 2: {
                        typename STORAGE::TwoByteStorageType *s =
                            static_cast<typename STORAGE::TwoByteStorageType *>
                                                                 (d_storage_p);
                        for (bsl::size_t i = 0; i < d_length; ++i) {
                            STORAGE::bdexGet16(stream, s[i]);
                        }
                      } break;
                      case 4: {
                        typename STORAGE::FourByteStorageType *s =
                           static_cast<typename STORAGE::FourByteStorageType *>
                                                                 (d_storage_p);
                        for (bsl::size_t i = 0; i < d_length; ++i) {
                            STORAGE::bdexGet32(stream, s[i]);
                        }
                      } break;
                      case 8: {
                        typename STORAGE::EightByteStorageType *s =
                          static_cast<typename STORAGE::EightByteStorageType *>
                                                                 (d_storage_p);
                        for (bsl::size_t i = 0; i < d_length; ++i) {
                            STORAGE::bdexGet64(stream, s[i]);
                        }
                      } break;
                    }
                }
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::insert(
                                    bsl::size_t                       dstIndex,
                                    const PackedIntArrayImp<STORAGE>& srcArray)
{
    BSLS_ASSERT(dstIndex <= d_length);

    insert(dstIndex, srcArray, 0, srcArray.length());
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::pop_back()
{
    BSLS_ASSERT_SAFE(0 < d_length);

    --d_length;
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::push_back(ElementType value)
{
    append(value);
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::remove(bsl::size_t dstIndex)
{
    BSLS_ASSERT(dstIndex < d_length);

    remove(dstIndex, 1);
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::remove(bsl::size_t dstIndex,
                                        bsl::size_t numElements)
{
    // Assert 'dstIndex + numElements <= d_length' without risk of overflow.
    BSLS_ASSERT(numElements <= d_length);
    BSLS_ASSERT(dstIndex    <= d_length - numElements);

    d_length -= numElements;

    bsl::memmove(address() + dstIndex * d_bytesPerElement,
                 address() + (dstIndex + numElements) * d_bytesPerElement,
                 (d_length - dstIndex) * d_bytesPerElement);
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::removeAll()
{
    d_length = 0;
    d_bytesPerElement = 1;
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::reserveCapacity(bsl::size_t numElements)
{
    // Test for potential overflow.
    BSLS_ASSERT(k_MAX_CAPACITY / d_bytesPerElement >= numElements);

    size_t requiredCapacityInBytes = d_bytesPerElement * numElements;
    if (requiredCapacityInBytes > d_capacityInBytes) {
        reserveCapacityImp(requiredCapacityInBytes);
    }
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::reserveCapacity(bsl::size_t numElements,
                                                 ElementType maxValue)
{
    BSLS_ASSERT(0 <= maxValue);

    int requiredBytesPerElement = d_bytesPerElement;

    int rbpe = STORAGE::requiredBytesPerElement(maxValue);
    if (rbpe > requiredBytesPerElement) {
        requiredBytesPerElement = rbpe;
    }

    // Test for potential overflow.
    BSLS_ASSERT(k_MAX_CAPACITY / requiredBytesPerElement >= numElements);

    size_t requiredCapacityInBytes = requiredBytesPerElement * numElements;

    if (requiredCapacityInBytes > d_capacityInBytes) {
        reserveCapacityImp(requiredCapacityInBytes);
    }
}

template <>
inline
void PackedIntArrayImp<PackedIntArrayImp_Unsigned>::
                                       reserveCapacity(bsl::size_t numElements,
                                                       ElementType maxValue)
{
    int requiredBytesPerElement = d_bytesPerElement;

    int rbpe = PackedIntArrayImp_Unsigned::requiredBytesPerElement(maxValue);
    if (rbpe > requiredBytesPerElement) {
        requiredBytesPerElement = rbpe;
    }

    // Test for potential overflow.
    BSLS_ASSERT(k_MAX_CAPACITY / requiredBytesPerElement >= numElements);

    size_t requiredCapacityInBytes = requiredBytesPerElement * numElements;

    if (requiredCapacityInBytes > d_capacityInBytes) {
        reserveCapacityImp(requiredCapacityInBytes);
    }
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::reserveCapacity(bsl::size_t numElements,
                                                 ElementType minValue,
                                                 ElementType maxValue)
{
    BSLS_ASSERT(minValue <= maxValue);

    int requiredBytesPerElement = d_bytesPerElement;

    int rbpe = STORAGE::requiredBytesPerElement(maxValue);
    if (rbpe > requiredBytesPerElement) {
        requiredBytesPerElement = rbpe;
    }

    rbpe = STORAGE::requiredBytesPerElement(minValue);
    if (rbpe > requiredBytesPerElement) {
        requiredBytesPerElement = rbpe;
    }

    // Test for potential overflow.
    BSLS_ASSERT(k_MAX_CAPACITY / requiredBytesPerElement >= numElements);

    size_t requiredCapacityInBytes = requiredBytesPerElement * numElements;

    if (requiredCapacityInBytes > d_capacityInBytes) {
        reserveCapacityImp(requiredCapacityInBytes);
    }
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::resize(bsl::size_t numElements)
{
    if (numElements > d_length) {
        reserveCapacity(numElements);
        bsl::memset(address() + d_length * d_bytesPerElement,
                    0,
                    (numElements - d_length) * d_bytesPerElement);
    }
    d_length = numElements;
}

template <class STORAGE>
inline
void PackedIntArrayImp<STORAGE>::swap(PackedIntArrayImp<STORAGE>& other)
{
    BSLS_ASSERT(d_allocator_p == other.d_allocator_p);

    bslalg::SwapUtil::swap(&d_storage_p,        &other.d_storage_p);
    bslalg::SwapUtil::swap(&d_length,           &other.d_length);
    bslalg::SwapUtil::swap(&d_bytesPerElement,  &other.d_bytesPerElement);
    bslalg::SwapUtil::swap(&d_capacityInBytes,  &other.d_capacityInBytes);
}

// ACCESSORS
template <class STORAGE>
inline
bslma::Allocator *PackedIntArrayImp<STORAGE>::allocator() const
{
    return d_allocator_p;
}

template <class STORAGE>
template <class STREAM>
inline
STREAM& PackedIntArrayImp<STORAGE>::bdexStreamOut(STREAM& stream,
                                                  int     version) const
{
    if (stream) {
        switch (version) {
          case 1: {
            stream.putInt8(d_bytesPerElement);
            stream.putLength(static_cast<int>(d_length));
            switch (d_bytesPerElement) {
              case 1: {
                typename STORAGE::OneByteStorageType *s =
                            static_cast<typename STORAGE::OneByteStorageType *>
                                                                 (d_storage_p);
                for (bsl::size_t i = 0; i < d_length; ++i) {
                    STORAGE::bdexPut8(stream, s[i]);
                }
              } break;
              case 2: {
                typename STORAGE::TwoByteStorageType *s =
                            static_cast<typename STORAGE::TwoByteStorageType *>
                                                                 (d_storage_p);
                for (bsl::size_t i = 0; i < d_length; ++i) {
                    STORAGE::bdexPut16(stream, s[i]);
                }
              } break;
              case 4: {
                typename STORAGE::FourByteStorageType *s =
                           static_cast<typename STORAGE::FourByteStorageType *>
                                                                 (d_storage_p);
                for (bsl::size_t i = 0; i < d_length; ++i) {
                    STORAGE::bdexPut32(stream, s[i]);
                }
              } break;
              case 8: {
                typename STORAGE::EightByteStorageType *s =
                          static_cast<typename STORAGE::EightByteStorageType *>
                                                                 (d_storage_p);
                for (bsl::size_t i = 0; i < d_length; ++i) {
                    STORAGE::bdexPut64(stream, s[i]);
                }
              } break;
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

template <class STORAGE>
inline
int PackedIntArrayImp<STORAGE>::bytesPerElement() const {
    return d_bytesPerElement;
}

template <class STORAGE>
inline
bsl::size_t PackedIntArrayImp<STORAGE>::capacity() const {
    return d_capacityInBytes / d_bytesPerElement;
}

template <class STORAGE>
inline
bool PackedIntArrayImp<STORAGE>::isEmpty() const {
    return 0 == d_length;
}

template <class STORAGE>
inline
bool PackedIntArrayImp<STORAGE>::isEqual(
                                 const PackedIntArrayImp<STORAGE>& other) const
{
    if (d_length == other.d_length) {
        if (d_bytesPerElement == other.d_bytesPerElement) {
            return 0 == bsl::memcmp(d_storage_p,
                                    other.d_storage_p,
                                    d_length * d_bytesPerElement);    // RETURN
        }
        else {
            return isEqualImp(other);                                 // RETURN
        }
    }
    return false;
}

template <class STORAGE>
inline
bsl::size_t PackedIntArrayImp<STORAGE>::length() const
{
    return d_length;
}

                     // ---------------------------------
                     // class PackedIntArrayConstIterator
                     // ---------------------------------

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

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

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

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

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

    ++d_index;
    return *this;
}

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

    --d_index;
    return *this;
}

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

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

    d_index += offset;
    return *this;
}

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

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

    d_index -= offset;
    return *this;
}

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

    return static_cast<TYPE>((*d_array_p)[d_index]);
}

template <class TYPE>
inline
TYPE PackedIntArrayConstIterator<TYPE>::operator->() const
{
    BSLS_ASSERT_SAFE(d_array_p);
    BSLS_ASSERT_SAFE(d_index < d_array_p->length());

    return *(*this);
}

template <class TYPE>
inline
TYPE PackedIntArrayConstIterator<TYPE>::operator[](bsl::ptrdiff_t offset) const
{
    BSLS_ASSERT_SAFE(d_array_p);

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

    return static_cast<TYPE>((*d_array_p)[d_index + offset]);
}

template <class TYPE>
inline
PackedIntArrayConstIterator<TYPE>
      PackedIntArrayConstIterator<TYPE>::operator+(bsl::ptrdiff_t offset) const
{
    BSLS_ASSERT_SAFE(d_array_p);

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

    return PackedIntArrayConstIterator<TYPE>(d_array_p, d_index + offset);
}


template <class TYPE>
inline
PackedIntArrayConstIterator<TYPE>
      PackedIntArrayConstIterator<TYPE>::operator-(bsl::ptrdiff_t offset) const
{
    BSLS_ASSERT_SAFE(d_array_p);

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

    return PackedIntArrayConstIterator<TYPE>(d_array_p, d_index - offset);
}

}  // close package namespace

// FREE FUNCTIONS
template <class TYPE>
inline
bdlc::PackedIntArrayConstIterator<TYPE> bdlc::operator++(
                                       PackedIntArrayConstIterator<TYPE>& iter,
                                       int)
{
    BSLS_ASSERT_SAFE(iter.d_array_p);
    BSLS_ASSERT_SAFE(iter.d_index < iter.d_array_p->length());

    const PackedIntArrayConstIterator<TYPE> curr = iter;
    ++iter;
    return curr;
}

template <class TYPE>
inline
bdlc::PackedIntArrayConstIterator<TYPE> bdlc::operator--(
                                       PackedIntArrayConstIterator<TYPE>& iter,
                                       int)
{
    BSLS_ASSERT_SAFE(iter.d_array_p);
    BSLS_ASSERT_SAFE(iter.d_index > 0);

    const PackedIntArrayConstIterator<TYPE> curr = iter;
    --iter;
    return curr;
}

template <class TYPE>
inline
bool bdlc::operator==(const PackedIntArrayConstIterator<TYPE>& lhs,
                      const PackedIntArrayConstIterator<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 PackedIntArrayConstIterator<TYPE>& lhs,
                      const PackedIntArrayConstIterator<TYPE>& rhs)
{
    return lhs.d_array_p != rhs.d_array_p || lhs.d_index != rhs.d_index;
}

template <class TYPE>
inline
bsl::ptrdiff_t bdlc::operator-(const PackedIntArrayConstIterator<TYPE>& lhs,
                               const PackedIntArrayConstIterator<TYPE>& rhs)
{
    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 PackedIntArrayConstIterator<TYPE>& lhs,
                     const PackedIntArrayConstIterator<TYPE>& rhs)
{
    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 PackedIntArrayConstIterator<TYPE>& lhs,
                      const PackedIntArrayConstIterator<TYPE>& rhs)
{
    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 PackedIntArrayConstIterator<TYPE>& lhs,
                     const PackedIntArrayConstIterator<TYPE>& rhs)
{
    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 PackedIntArrayConstIterator<TYPE>& lhs,
                      const PackedIntArrayConstIterator<TYPE>& rhs)
{
    BSLS_ASSERT(lhs.d_array_p == rhs.d_array_p);

    return lhs.d_index >= rhs.d_index;
}

namespace bdlc {

                    // --------------------
                    // class PackedIntArray
                    // --------------------

// CLASS METHODS
template <class TYPE>
inline
int PackedIntArray<TYPE>::maxSupportedBdexVersion(int serializationVersion)
{
    return ImpType::maxSupportedBdexVersion(serializationVersion);
}

// CREATORS
template <class TYPE>
inline
PackedIntArray<TYPE>::PackedIntArray(bslma::Allocator *basicAllocator)
: d_imp(basicAllocator)
{
}

template <class TYPE>
inline
PackedIntArray<TYPE>::PackedIntArray(bsl::size_t       numElements,
                                     TYPE              value,
                                     bslma::Allocator *basicAllocator)
: d_imp(numElements,
        static_cast<typename ImpType::ElementType>(value),
        basicAllocator)
{
}

template <class TYPE>
inline
PackedIntArray<TYPE>::PackedIntArray(
                                   const PackedIntArray<TYPE>&  original,
                                   bslma::Allocator            *basicAllocator)
: d_imp(original.d_imp, basicAllocator)
{
}

template <class TYPE>
inline
PackedIntArray<TYPE>::~PackedIntArray()
{
}

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

template <class TYPE>
inline
void PackedIntArray<TYPE>::append(TYPE value)
{
    d_imp.append(static_cast<typename ImpType::ElementType>(value));
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::append(const PackedIntArray<TYPE>& srcArray)
{
    d_imp.append(srcArray.d_imp);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::append(const PackedIntArray<TYPE>& 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);

    d_imp.append(srcArray.d_imp, srcIndex, numElements);
}

template <class TYPE>
template <class STREAM>
inline
STREAM& PackedIntArray<TYPE>::bdexStreamIn(STREAM& stream, int version)
{
    return d_imp.bdexStreamIn(stream, version);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::insert(bsl::size_t dstIndex, TYPE value)
{
    BSLS_ASSERT_SAFE(dstIndex <= length());

    d_imp.insert(dstIndex, static_cast<typename ImpType::ElementType>(value));
}

template <class TYPE>
inline
typename PackedIntArray<TYPE>::const_iterator
                   PackedIntArray<TYPE>::insert(const_iterator dst, TYPE value)
{
    insert(dst.d_index, value);
    return dst;
}

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

    d_imp.insert(dstIndex, srcArray.d_imp);
}

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

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

    d_imp.insert(dstIndex, srcArray.d_imp, srcIndex, numElements);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::pop_back()
{
    BSLS_ASSERT_SAFE(0 < length());

    d_imp.pop_back();
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::push_back(TYPE value)
{
    d_imp.push_back(static_cast<typename ImpType::ElementType>(value));
}

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

    d_imp.remove(dstIndex);
}

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

    d_imp.remove(dstIndex, numElements);
}

template <class TYPE>
inline
typename PackedIntArray<TYPE>::const_iterator
  PackedIntArray<TYPE>::remove(const_iterator dstFirst, const_iterator dstLast)
{
    BSLS_ASSERT(dstFirst <= dstLast);

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

template <class TYPE>
inline
void PackedIntArray<TYPE>::removeAll()
{
    d_imp.removeAll();
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::replace(bsl::size_t dstIndex, TYPE value)
{
    BSLS_ASSERT_SAFE(dstIndex < length());

    d_imp.replace(dstIndex, static_cast<typename ImpType::ElementType>(value));
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::replace(bsl::size_t                 dstIndex,
                                   const PackedIntArray<TYPE>& 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);

    d_imp.replace(dstIndex, srcArray.d_imp, srcIndex, numElements);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::reserveCapacity(bsl::size_t numElements)
{
    // Test for potential overflow.
    BSLS_ASSERT(
             ImpType::k_MAX_CAPACITY / k_MAX_BYTES_PER_ELEMENT >= numElements);

    d_imp.reserveCapacityImp(numElements * k_MAX_BYTES_PER_ELEMENT);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::reserveCapacity(bsl::size_t numElements,
                                           TYPE        maxValue)
{
    // To avoid a compiler warning, asserting '0 <= maxValue' is omitted; the
    // test is performed in 'd_imp.reserveCapacity'.

    d_imp.reserveCapacity(numElements, maxValue);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::reserveCapacity(bsl::size_t numElements,
                                           TYPE        minValue,
                                           TYPE        maxValue)
{
    BSLS_ASSERT(minValue <= maxValue);

    d_imp.reserveCapacity(numElements, minValue, maxValue);
}

template <class TYPE>
inline
void PackedIntArray<TYPE>::resize(bsl::size_t numElements)
{
    d_imp.resize(numElements);
}

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

    d_imp.swap(other.d_imp);
}

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

    return static_cast<TYPE>(d_imp[index]);
}

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

template <class TYPE>
inline
TYPE PackedIntArray<TYPE>::back() const
{
    BSLS_ASSERT_SAFE(0 < length());

    return static_cast<TYPE>(d_imp[length() - 1]);
}

template <class TYPE>
template <class STREAM>
inline
STREAM& PackedIntArray<TYPE>::bdexStreamOut(STREAM& stream, int version) const
{
    return d_imp.bdexStreamOut(stream, version);
}

template <class TYPE>
inline
typename PackedIntArray<TYPE>::const_iterator
                                            PackedIntArray<TYPE>::begin() const
{
    return const_iterator(&d_imp, 0);
}

template <class TYPE>
inline
int PackedIntArray<TYPE>::bytesPerElement() const
{
    return d_imp.bytesPerElement();
}

template <class TYPE>
inline
bsl::size_t PackedIntArray<TYPE>::capacity() const
{
    return d_imp.capacity();
}

template <class TYPE>
inline
typename PackedIntArray<TYPE>::const_iterator PackedIntArray<TYPE>::end() const
{
    return const_iterator(&d_imp, d_imp.length());
}

template <class TYPE>
inline
TYPE PackedIntArray<TYPE>::front() const
{
    BSLS_ASSERT_SAFE(0 < length());

    return static_cast<TYPE>(d_imp[0]);
}

template <class TYPE>
inline
bool PackedIntArray<TYPE>::isEmpty() const
{
    return d_imp.isEmpty();
}

template <class TYPE>
inline
bool PackedIntArray<TYPE>::isEqual(const PackedIntArray<TYPE>& other) const
{
    return d_imp.isEqual(other.d_imp);
}

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

template <class TYPE>
bsl::ostream& PackedIntArray<TYPE>::print(bsl::ostream& stream,
                                          int           level,
                                          int           spacesPerLevel) const
{
    return d_imp.print(stream, level, spacesPerLevel);
}

}  // close package namespace

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

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

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

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

        return;                                                       // RETURN
    }

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

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

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

}  // close enterprise namespace

// TRAITS
namespace BloombergLP {
namespace bslma {

template <class STORAGE>
struct UsesBslmaAllocator<bdlc::PackedIntArrayImp<STORAGE> >
                                                           : bsl::true_type {};

template <class TYPE>
struct UsesBslmaAllocator<bdlc::PackedIntArray<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 ----------------------------------