// bdlc_queue.h                                                       -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLC_QUEUE
#define INCLUDED_BDLC_QUEUE

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

//@PURPOSE: Provide an in-place double-ended queue of 'T' values.
//
//@DEPRECATED: Use 'bsl::deque' instead.
//
//@CLASSES:
//  bdlc::Queue: memory manager for in-place queue of 'T' values
//
//@DESCRIPTION: This component implements an efficient, in-place, indexable,
// double-ended queue of 'T' values, where 'T' is a templatized, user-defined
// type.  The functionality of a 'bdlc::Queue' is relatively rich; it is almost
// a proper superset of a vector, with efficient implementations of 'front',
// 'back', 'pushFront', and 'popBack' methods added.  However, the queue does
// *not* provide a 'data' method (yielding direct access to the underlying
// memory), because its internal organization is not array-like.
//
// Typical usage involves pushing (appending) values to the back of the queue,
// popping (removing) values from the front of the queue, and retrieving
// (operator[]) values from a specified index; unlike the O[n] runtime cost for
// an 'insert(0, v)', however, a 'pushFront' has a constant average-case cost.
//
// Note that appending, inserting, removing or pushing (back and front)
// elements potentially alters the memory address of other element in the
// queue, and there is no guarantee of contiguous storage of consecutive queued
// elements.
//
///Abstract Representation
///-----------------------
// The logical organization of an indexable, in-place, double-ended
// 'bdlc::Queue' object 'q' is shown below, along with an illustration of some
// of its most common methods:
//..
//                                    QUEUE
//                 v = q.front()                          v = q.back()
//                  +------+------+------+------+--//--+------+
//   q.popFront() <-|      |      |      |      |      |      |<- pushBack(v)
// q.pushFront(v) ->|      |      |      |      |      |      |-> popBack()
//                  +------+------+------+------+--//--+------+
//                    q[0]   q[1]                       q[n-1]
//                  <------------ n = q.length() --//--------->
//..
//
///Performance
///-----------
// The following characterizes the performance of representative operations
// using big-oh notation, O[f(N,M)], where the names 'N' and 'M' also refer to
// the number of respective elements in each container (i.e., its 'length').
// Here the average case, A[f(N)], is the amortized cost, which is defined as
// the cost of 'N' successive invocations of the operation divided by 'N'.
//..
//     Operation           Worst Case          Average Case
//     ---------           ----------          ------------
//     DEFAULT CTOR        O[1]
//     COPY CTOR(N)        O[N]
//     N.DTOR()            O[1]
//     N.OP=(M)            O[M]
//     OP==(N,M)           O[min(N,M)]
//
//     N.pushFront(value)  O[N]                A[1]
//     N.pushBack(value)   O[N]                A[1]
//     N.popFront()        O[1]
//     N.popBack()         O[1]
//
//     N.append(value)     O[N]                A[1]
//     N.insert(value)     O[N]
//     N.replace(value)    O[1]
//     N.remove(index)     O[N]
//
//     N.OP[]()            O[1]
//     N.length()          O[1]
//..
//
///Usage
///-----
// The following snippets of code illustrate how to create and use a queue.
// First, create an empty 'bdlc::Queue<double>' 'q' and populate it with two
// elements 'E1' and 'E2'.
//..
//      const double E1 = 100.01;
//      const double E2 = 200.02;
//
//      bdlc::Queue<double> q;          assert( 0 == q.length());
//
//      q.append(E1);                   assert( 1 == q.length());
//                                      assert(E1 == q[0]);
//                                      assert(E1 == q.front());
//                                      assert(E1 == q.back());
//
//      q.append(E2);                   assert( 2 == q.length());
//                                      assert(E1 == q[0]);
//                                      assert(E2 == q[1]);
//                                      assert(E1 == q.front());
//                                      assert(E2 == q.back());
//..
// Now, pop the first element ('E1') from 'q' and push the same value to the
// front of the queue.
//..
//      q.popFront();                   assert( 1 == q.length());
//                                      assert(E2 == q[0]);
//                                      assert(E2 == q.front());
//                                      assert(E2 == q.back());
//
//      q.pushFront(E1);                assert( 2 == q.length());
//                                      assert(E1 == q[0]);
//                                      assert(E2 == q[1]);
//                                      assert(E1 == q.front());
//                                      assert(E2 == q.back());
//..
// Then, pop the last element ('E2') from the back of 'q' and push a new value
// 'E3' at the end of the queue.
//..
//      const double E3 = 300.03;
//
//      q.popBack();                    assert( 1 == q.length());
//                                      assert(E1 == q[0]);
//                                      assert(E1 == q.front());
//                                      assert(E1 == q.back());
//
//      q.pushBack(E3);                 assert( 2 == q.length());
//                                      assert(E1 == q[0]);
//                                      assert(E3 == q[1]);
//                                      assert(E1 == q.front());
//                                      assert(E3 == q.back());
//..
// Now, assign 'E2' to the first element (index position 0) of 'q'.
//..
//      q[0] = E2;                      assert( 2 == q.length());
//                                      assert(E2 == q[0]);
//                                      assert(E3 == q[1]);
//..
// Then, insert a new value in the middle (index position 1) of 'q'.
//..
//      const double E4 = 400.04;
//
//      q.insert(1, E4);                assert( 3 == q.length());
//                                      assert(E2 == q[0]);
//                                      assert(E4 == q[1]);
//                                      assert(E3 == q[2]);
//..
// Next, iterate over the elements in 'q', printing them in increasing order of
// their index positions, '[0 .. q.length() - 1]',
//..
//      bsl::cout << '[';
//      int len = q.length();
//      for (int i = 0; i < len; ++i) {
//          bsl::cout << ' ' << q[i];
//      }
//      bsl::cout << " ]" << bsl::endl;
//
//..
// which produces the following output on 'stdout':
//..
//      [ 200.02 400.04 300.03 ]
//..
// Finally, remove the elements from queue 'q'.
//..
//      q.remove(2);                    assert( 2 == q.length());
//                                      assert(E2 == q[0]);
//                                      assert(E4 == q[1]);
//
//      q.remove(0);                    assert( 1 == q.length());
//                                      assert(E4 == q[0]);
//
//      q.remove(0);                    assert( 0 == q.length());
//
//..
// Note that, in general, specifying index positions greater than or equal to
// length() will result in undefined behavior.

#include <bdlscm_version.h>

#include <bdlb_print.h>
#include <bdlb_printmethods.h>

#include <bslma_default.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslx_instreamfunctions.h>
#include <bslx_outstreamfunctions.h>

#include <bsl_cstring.h>        // memmove(), memcmp(), memcpy()
#include <bsl_ostream.h>
#include <bsl_new.h>

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

namespace BloombergLP {
namespace bdlc {

                                // ===========
                                // class Queue
                                // ===========

template <class T>
class Queue {
    // This class implements an efficient, in-place double-ended queue of
    // values of parameterized type 'T'.  The physical capacity of this queue
    // may grow, but never shrinks.  Capacity may be reserved initially via a
    // constructor, or at any time thereafter by using the 'reserveCapacity'
    // and 'reserveCapacityRaw' methods.  Note that there is no guarantee of
    // contiguous storage of consecutive elements.
    //
    // More generally, this container class supports a complete set of *value*
    // *semantics* operations, including copy construction, assignment,
    // equality comparison, 'ostream' printing, and 'bdex' serialization.  (A
    // precise operational definition of when two objects have the same value
    // can be found in the description of 'operator==' for the class.)  This
    // container is *exception* *neutral* with no guarantee of rollback: if an
    // exception is thrown during the invocation of a method on a pre-existing
    // object, the container is left in a valid state, but its value is
    // undefined.  In no event is memory leaked.  Finally, *aliasing* (e.g.,
    // using all or part of an object as both source and destination) is
    // supported in all cases.

    // PRIVATE TYPES
    enum {
        // The queue is full when 'd_front == d_back'.  Hence, 'k_INITIAL_SIZE'
        // must be at least two.

        k_INITIAL_SIZE   = 2,  // initial physical capacity (in elements)
        k_GROW_FACTOR    = 2,  // multiplicative factor for growing 'd_size'
        k_EXTRA_CAPACITY = 2   // extra capacity needed by implementation
    };

  public:
    // TYPES
    struct InitialCapacity {
        // Enable uniform use of an optional integral constructor argument to
        // specify the initial internal capacity (in elements).  For example,
        //..
        //   Queue<unsigned int> x(Queue::InitialCapacity(8));
        //..
        // instantiates an object 'x' with an initial capacity of 8 elements,
        // but with a logical length of 0 elements.

        unsigned int d_i;

        // CREATORS
        explicit InitialCapacity(unsigned int i) : d_i(i) { }
        ~InitialCapacity() { }
    };

  private:
    // DATA
    T                *d_array_p;      // dynamically allocated array ('d_size'
                                      // elements)

    int               d_size;         // physical capacity of this array (in
                                      // elements)

    int               d_front;        // index of element before first stored
                                      // element

    int               d_back;         // index of element past last stored
                                      // element

    bslma::Allocator *d_allocator_p;  // holds (but not own) memory allocator

  private:
    // PRIVATE MANIPULATORS
    int calculateSufficientSize(int minLength, int size);
        // Grow geometrically the specified current 'size' value while it is
        // less than the specified 'minLength' value plus any additional
        // capacity required by the implementation (i.e., 'k_EXTRA_CAPACITY'
        // elements).  Return the new size value.  The behavior is undefined
        // unless 'k_INITIAL_SIZE <= size' and '0 <= minLength'.  Note that if
        // 'minLength + k_EXTRA_CAPACITY <= size' then 'size' is returned.

    int memcpyCircular(T       *dstArray,
                       int      dstSize,
                       int      dstIndex,
                       const T *srcArray,
                       int      srcSize,
                       int      srcIndex,
                       int      numElements);
        // Copy efficiently the specified 'numElements' data values from the
        // specified 'srcArray' of the specified 'srcSize' starting at the
        // specified 'srcIndex' into the specified 'dstArray' of the specified
        // 'dstSize' starting at the specified 'dstIndex'.  Return the new
        // value for the back of the queue.  The 'srcArray' and the 'dstArray'
        // are assumed to be queues; they are circular which implies copy may
        // have to be broken into multiple parts since the underlying array is
        // linear.  The behavior is undefined unless '0 <= dstSize',
        // '0 <= dstIndex < dstSize', '0 <= srcIndex < srcSize',
        // '0 <= numElements', 'numElements <= dstSize - k_EXTRA_CAPACITY', and
        // 'numElements <= srcSize - k_EXTRA_CAPACITY' (the 'k_EXTRA_CAPACITY'
        // accounts for the locations of 'd_front' and 'd_back').  Note that
        // aliasing is not handled properly.

    void memShiftLeft(T   *array,
                      int  size,
                      int  dstIndex,
                      int  srcIndex,
                      int  numElements);
        // Copy efficiently the specified 'numElements' data values from the
        // specified 'array' of the specified 'size' starting at the specified
        // index 'srcIndex' to the specified 'dstIndex' assuming the elements
        // are to be moved to the left (towards the front of the queue).
        // 'array' is assumed to be a queue; it is circular.  The behavior is
        // undefined unless '0 <= size', '0 <= dstIndex < size',
        // '0 <= srcIndex < size', and
        // '0 <= numElements <= size - k_EXTRA_CAPACITY'.  Note that this
        // function is alias safe.

    void memShiftRight(T   *array,
                       int  size,
                       int  dstIndex,
                       int  srcIndex,
                       int  numElements);
        // Copy efficiently the specified 'numElements' data values from the
        // specified 'array' of the specified 'size' starting at the specified
        // index 'srcIndex' to the specified 'dstIndex' assuming the elements
        // are to be moved to the right (towards the back of the queue).
        // 'array' is assumed to be a queue; it is circular.  The behavior is
        // undefined unless '0 <= size', '0 <= dstIndex < size',
        // '0 <= srcIndex < size', and
        // '0 <= numElements <= size - k_EXTRA_CAPACITY'.  Note that this
        // function is alias safe.

    void copyData(T       *dstArray,
                  int     *dstBack,
                  int      dstSize,
                  int      dstFront,
                  const T *srcArray,
                  int      srcSize,
                  int      srcFront,
                  int      srcBack);
        // Copy efficiently the queue indicated by the specified 'srcArray' of
        // the specified 'srcSize' with the specified 'srcFront' and the
        // specified 'srcBack' into the queue indicated by the specified
        // 'dstArray' of the specified 'dstSize', with the specified
        // 'dstFront'.  The specified 'dstBack' is set to make the length of
        // the destination queue the same as the length of the source queue.
        // The behavior is undefined unless '0 <= dstSize',
        // '0 <= dstFront < dstSize', '0 <= srcSize',
        // '0 <= srcFront < srcSize', and '0 <= srcBack < srcSize'.  Note that
        // aliasing is not handled properly.

    int increaseSizeImp(T                **addrArray,
                        int               *front,
                        int               *back,
                        int                newSize,
                        int                size,
                        bslma::Allocator  *allocator);
        // Increase the physical capacity of the queue represented by the
        // specified 'addrArray' to specified 'newSize' from the specified
        // 'size'.  Return the new size of the queue.  This function copies the
        // data contained within the queue between the specified 'front' and
        // 'back' to the new queue and update the values of both 'front' and
        // 'back'.  Use the specified 'allocator' to supply and retrieve
        // memory.  The behavior is undefined unless
        // 'k_INITIAL_SIZE <= newSize', 'k_INITIAL_SIZE <= size',
        // 'size <= newSize', '0 <= *front < size', and '0 <= *back < size'.

    void increaseSize();
        // Increase the physical capacity of this array by at least one
        // element.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Queue, bdlb::HasPrintMethod);
    BSLMF_NESTED_TRAIT_DECLARATION(Queue, bslma::UsesBslmaAllocator);

    // CLASS METHODS
    static int maxSupportedBdexVersion(int versionSelector);
        // Return the maximum valid BDEX format version, as indicated by the
        // specified 'versionSelector', to be passed to the 'bdexStreamOut'
        // method.  Note that it is highly recommended that 'versionSelector'
        // be formatted as "YYYYMMDD", a date representation.  Also note that
        // 'versionSelector' should be a *compile*-time-chosen value that
        // selects a format version supported by both externalizer and
        // unexternalizer.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

    // CREATORS
    explicit
    Queue(bslma::Allocator *basicAllocator = 0);
    explicit
    Queue(unsigned int      initialLength,
          bslma::Allocator *basicAllocator = 0);
    Queue(int               initialLength,
          const T&          initialValue,
          bslma::Allocator *basicAllocator = 0);
        // Create an in-place queue.  By default, the queue is empty.
        // Optionally specify the 'initialLength' of the queue.  Queue elements
        // are initialized with the specified 'initialValue', or to 0.0 if
        // 'initialValue' is not specified.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless '0 <= initialLength'.

    explicit
    Queue(const InitialCapacity&  numElements,
          bslma::Allocator       *basicAllocator = 0);
        // Create an in-place queue with sufficient initial capacity to
        // accommodate up to the specified 'numElements' values without
        // subsequent reallocation.  A valid reference returned by the
        // 'operator[]' method is guaranteed to remain valid unless the value
        // returned by the 'length' method exceeds 'numElements' (which would
        // potentially cause a reallocation).  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless '0 <= numElements'.

    Queue(const T          *srcArray,
          int               numElements,
          bslma::Allocator *basicAllocator = 0);
        // Create an in-place queue initialized with the specified
        // 'numElements' leading values from the specified 'srcArray'.
        // Optionally specify the 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined unless '0 <= numElements'.  Note
        // that 'srcArray' must refer to sufficient memory to hold
        // 'numElements' values.

    Queue(const Queue& original, bslma::Allocator* basicAllocator = 0);
        // Create an in-place queue initialized to the value of the specified
        // 'original' queue.  Optionally specify the 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    ~Queue();
        // Destroy this object.

    // MANIPULATORS
    Queue& operator=(const Queue& rhs);
        // Assign to this queue the value of the specified 'rhs' queue and
        // return a reference to this modifiable queue.

    T& operator[](int index);
        // Return a reference to the modifiable element at the specified
        // 'index' position in this queue.  The reference will remain valid as
        // long as this queue is not destroyed or modified (e.g., via 'insert',
        // 'remove', or 'append').  The behavior is undefined unless
        // '0 <= index < length()'.

    void append(const T& item);
        // Append to the end of this queue the value of the specified 'item'.
        // Note that this function is a synonym for 'pushBack' and is logically
        // equivalent to (but generally more efficient than):
        //..
        //   insert(length(), item);
        //..

    void append(const Queue& srcQueue);
        // Append to the end of this queue the sequence of values in the
        // specified 'srcQueue'.  Note that this function is logically
        // equivalent to:
        //..
        //   insert(length(), srcQueue);
        //..

    void append(const Queue& srcQueue, int srcIndex, int numElements);
        // Append to the end of this queue the specified 'numElements' value in
        // the specified 'srcQueue' starting at the specified index position
        // 'srcIndex'.  Note that this function is logically equivalent to:
        //..
        //   insert(length(), srcQueue, srcIndex, numElements);
        //..
        // The behavior is undefined unless '0 <= srcIndex',
        // '0 <= numElements', and
        // 'srcIndex + numElements <= srcQueue.length()'.

    T& back();
        // Return a reference to the modifiable value at the back of this
        // queue.  The reference will remain valid as long as the queue is not
        // destroyed or modified (e.g., via 'insert', 'remove', or 'append').
        // The behavior is undefined if the queue is empty.  Note that this
        // function is logically equivalent to:
        //..
        //  operator[](length() - 1)
        //..

    T& front();
        // Return a reference to the modifiable value at the front of this
        // queue.  The reference will remain valid as long as the queue is not
        // destroyed or modified (e.g., via 'insert', 'remove', or 'append').
        // The behavior is undefined if the queue is empty.  Note that this
        // function is logically equivalent to:
        //..
        //  operator[](0)
        //..

    void insert(int dstIndex, const T& item);
        // Insert the specified 'item' into this queue at the specified
        // 'dstIndex'.  All current values with indices at or above 'dstIndex'
        // are shifted up by one index position.  The behavior is undefined
        // unless '0 <= dstIndex <= length()'.

    void insert(int dstIndex, const Queue& srcQueue);
        // Insert the specified 'srcQueue' into this queue at the specified
        // 'dstIndex'.  All current values with indices at or above 'dstIndex'
        // are shifted up by 'srcQueue.length()' index positions.  The behavior
        // is undefined unless '0 <= dstIndex <= length()'.

    void insert(int          dstIndex,
                const Queue& srcQueue,
                int          srcIndex,
                int          numElements);
        // Insert the specified 'numElements' values starting at the specified
        // 'srcIndex' position from the specified 'srcQueue' into this queue at
        // the specified 'dstIndex'.  All current values with indices at or
        // above 'dstIndex' are shifted up by 'numElements' index positions.
        // The behavior is undefined unless '0 <= dstIndex <= length()',
        // '0 <= srcIndex', '0 <= numElements', and
        // 'srcIndex + numElements <= srcQueue.length()'.

    void popBack();
        // Remove the value from the back of this queue efficiently (in O[1]
        // time).  The behavior is undefined if this queue is empty.  Note that
        // this function is logically equivalent to (but more efficient than):
        //..
        //    remove(length() - 1)
        //..

    void popFront();
        // Remove the value from the front of this queue efficiently (in O[1]
        // time).  The behavior is undefined if this queue is empty.  Note that
        // this function is logically equivalent to (but more efficient than):
        //..
        //    remove(0)
        //..

    void pushBack(const T& item);
        // Append the specified 'item' to the back of this queue efficiently
        // (in O[1] time when memory reallocation is not required).  Note that
        // this function is logically equivalent to (but generally more
        // efficient than):
        //..
        //    insert(length(), item);
        //..

    void pushFront(const T& item);
        // Insert the specified 'item' into the front of this queue efficiently
        // (in O[1] time when memory reallocation is not required).  Note that
        // this function is logically equivalent to (but generally more
        // efficient than):
        //..
        //    insert(0, item);
        //..

    void remove(int index);
        // Remove from this queue the value at the specified 'index'.  All
        // values with initial indices above 'index' are shifted down by one
        // index position.  The behavior is undefined unless
        // '0 <= index < length()'.

    void remove(int index, int numElements);
        // Remove from this queue, beginning at the specified 'index', the
        // specified 'numElements' values.  All values with initial indices at
        // or above 'index + numElements' are shifted down by 'numElements'
        // index positions.  The behavior is undefined unless '0 <= index',
        // '0 <= numElements', and 'index + numElements <= length()'.

    void removeAll(bsl::vector<T> *buffer = 0);
        // Remove all elements from this queue.  If the optionally specified
        // 'buffer' is not 0, append to 'buffer' a copy of each element removed
        // (in front-to-back order of the elements in the queue prior to the
        // invocation of this method).

    void replace(int dstIndex, const T& item);
        // Replace the element at the specified 'dstIndex' in this queue with
        // the specified 'item'.  The behavior is undefined unless
        // '0 <= dstIndex < length()'.  Note that this function is logically
        // equivalent to (but more efficient than):
        //..
        //    insert(dstIndex, item);
        //    remove(dstIndex + 1);
        //..

    void replace(int          dstIndex,
                 const Queue& srcQueue,
                 int          srcIndex,
                 int          numElements);
        // Replace the specified 'numElements' values beginning at the
        // specified 'dstIndex' in this queue with values from the specified
        // 'srcQueue' beginning at the specified 'srcIndex'.  The behavior is
        // undefined unless '0 <= dstIndex, 0 <= numElements',
        // 'dstIndex + numElements <= length()', '0 <= srcIndex', and
        // 'srcIndex + numElements <= srcQueue.length()'.  Note that this
        // function is logically equivalent to (but more efficient than):
        //..
        //    insert(dstIndex, srcQueue, srcIndex, numElements);
        //    remove(dstIndex + numElements, numElements);
        //..

    void reserveCapacity(int numElements);
        // Reserve sufficient internal capacity to accommodate up to the
        // specified 'numElements' values without subsequent reallocation.
        // Note that if 'numElements <= length()', this operation has no
        // effect.

    void reserveCapacityRaw(int numElements);
        // Reserve sufficient and minimal internal capacity to accommodate up
        // to the specified 'numElements' values without subsequent
        // reallocation.  Beware, however, that repeated calls to this function
        // may invalidate bounds on runtime complexity otherwise guaranteed by
        // this container.  Note that if 'numElements <= length()', this
        // operation has no effect.

    void setLength(int newLength);
    void setLength(int newLength, const T& initialValue);
        // Set the length of this queue to the specified 'newLength'.  If
        // 'newLength' is less than the current length, elements at index
        // positions at or above 'newLength' are removed.  Otherwise any new
        // elements (at or above the current length) are initialized to the
        // specified 'initialValue', or to 0.0 if 'initialValue' is not
        // specified.  The behavior is undefined unless '0 <= newLength'.

    void setLengthRaw(int newLength);
        // Set the length of this queue to the specified 'newLength'.  If
        // 'newLength' is less than the current length, elements at index
        // positions at or above 'newLength' are removed.  If 'newLength' is
        // equal to the current length, this function has no effect.  Otherwise
        // new elements at or above the current length are not initialized to
        // any value.

    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 swap(int index1, int index2);
        // Swap efficiently the values at the specified indices 'index1' and
        // 'index2'.  The behavior is undefined unless '0 <= index1 < length()'
        // and '0 <= index2 < length()'.

    // ACCESSORS
    const T& operator[](int index) const;
        // Return a reference to the non-modifiable element at the specified
        // 'index' position in this queue.  The reference will remain valid as
        // long as this queue is not destroyed or modified (e.g., via 'insert',
        // 'remove', or 'append').  The behavior is undefined unless
        // '0 <= index < length()'.

    const T& back() const;
        // Return a reference to the non-modifiable element at the back of this
        // queue.  The reference will remain valid as long as this queue is not
        // destroyed or modified (e.g., via 'insert', 'remove', or 'append').
        // The behavior is undefined if this queue is empty.  Note that this
        // function is logically equivalent to:
        //..
        //    operator[](length() - 1)
        //..

    const T& front() const;
        // Return a reference to the non-modifiable element at the front of
        // this queue.  The reference will remain valid as long as this queue
        // is not destroyed or modified (e.g., via 'insert', 'remove', or
        // 'append').  The behavior is undefined if this queue is empty.  Note
        // that this function is logically equivalent to:
        //..
        //    operator[](0)
        //..

    int length() const;
        // Return the number of elements in this queue.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level,
                        int           spacesPerLevel) const;
        // Format this object to the specified output 'stream' at the
        // optionally specified indentation 'level' and return a reference to
        // the modifiable 'stream'.  If 'level' is specified, optionally
        // specify 'spacesPerLevel', the number of spaces per indentation level
        // for this and all of its nested objects.  Each line is indented by
        // the absolute value of 'level * spacesPerLevel'.  If 'level' is
        // negative, suppress indentation of the first line.  If
        // 'spacesPerLevel' is negative, suppress line breaks and format the
        // entire output on one line.  If 'stream' is initially invalid, this
        // operation has no effect.  Note that a trailing newline is provided
        // in multi-line mode only.

    bsl::ostream& streamOut(bsl::ostream& stream) const
        // Write the elements of this queue out to the specified 'stream'.
        // Note that for this method to compile, 'operator<<' has to be defined
        // for arguments 'stream' and type 'T'.
    {
        stream << '[';
        for (int i = 0; i < length(); ++i) {
            stream << ' ' << (*this)[i];
        }
        return stream << " ]";
    }

    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write the value of this object, using the specified 'version'
        // format, to the specified output 'stream', 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.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // pending deprecation

    static int maxSupportedBdexVersion();
        // !DEPRECATED!: Use 'maxSupportedBdexVersion(int)' instead.
        //
        // Return the most current BDEX streaming version number supported by
        // this class.

    static int maxSupportedVersion();
        // Return the most current 'bdex' streaming version number supported by
        // this class.  (See the package-group-level documentation for more
        // information on 'bdex' streaming of container types.)
        //
        // DEPRECATED: Use 'maxSupportedBdexVersion' instead.

#endif // BDE_OMIT_INTERNAL_DEPRECATED -- pending deprecation
};

// FREE OPERATORS
template <class T>
inline
bool operator==(const Queue<T>& lhs, const Queue<T>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' queues have the same
    // value, and 'false' otherwise.  Two queues have the same value if they
    // have the same length and the same element value at each respective index
    // position.

template <class T>
inline
bool operator!=(const Queue<T>& lhs, const Queue<T>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' queues do not have the
    // same value, and 'false' otherwise.  Two queues do not have the same
    // value if they have different lengths or differ in at least one index
    // position.

template <class T>
inline
bsl::ostream& operator<<(bsl::ostream& stream, const Queue<T>& queue);
    // Write the specified 'queue' to the specified output 'stream' and return
    // a reference to the modifiable 'stream'.

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

// TBD pass through allocator
// TBD isBitwise, etc.

              // ---------------------------------------------
              // inlined methods used by other inlined methods
              // ---------------------------------------------

template <class T>
inline
int Queue<T>::length() const
{
    return d_back > d_front ? d_back - d_front - 1
                            : d_back + d_size - d_front - 1;
}

// PRIVATE MANIPULATORS
template <class T>
int Queue<T>::calculateSufficientSize(int minLength, int size)
{
    const int len = minLength + k_EXTRA_CAPACITY;
    while (size < len) {
        size *= k_GROW_FACTOR;
    }
    return size;
}

template <class T>
int Queue<T>::memcpyCircular(T       *dstArray,
                             int      dstSize,
                             int      dstIndex,
                             const T *srcArray,
                             int      srcSize,
                             int      srcIndex,
                             int      numElements)
{
    int dst;  // temporary value to store the current destination location

    // Break the source queue into one or two linear arrays to copy.

    int srcA = srcIndex;
    if (srcA + numElements <= srcSize) {  // one linear source array
        int lenSrcA = numElements;

        dst = dstIndex;

        // Compute the maximum number of elements that can be copied to the
        // destination array.

        int dstLen = dstSize - dst;

        if (dstLen >= lenSrcA) {  // can copy everything from srcA
            // TBD efficiency

            for (int i = 0; i < lenSrcA; ++i) {
                new (&dstArray[dst + i]) T(srcArray[srcA + i]);
            }
            dst += lenSrcA;
        }
        else {  // can copy only part of srcA without changing dst
            // TBD efficiency

            for (int i = 0; i < dstLen; ++i) {
                new (&dstArray[dst + i]) T(srcArray[srcA + i]);
            }
            srcA += dstLen;
            lenSrcA -= dstLen;

            // WARNING:  There seems to be an AIX compiler issue for the
            // following four lines.  Removing the 'assert' and moving the
            // 'memcpy' down two lines may cause the program to compile, but
            // not execute properly.

            // TBD efficiency

            for (int i = 0; i < lenSrcA; ++i) {
                new (&dstArray[i]) T(srcArray[srcA + i]);
            }
            dstLen = dst;  // max numElements that can be copied to index 0
            dst = lenSrcA;

            // TBD doc above assert(lenSrcA <= dstLen - k_EXTRA_CAPACITY);
        }
    }
    else {  // two linear source arrays
        int lenSrcA = srcSize - srcA;
        int lenSrcB = numElements - lenSrcA;

        dst = dstIndex;

        // Compute the maximum number of elements that can be copied to the
        // destination array.

        int dstLen = dstSize - dst;

        if (dstLen >= lenSrcA) {  // can copy everything from srcA
            // TBD efficiency

            for (int i = 0; i < lenSrcA; ++i) {
                new (&dstArray[dst + i]) T(srcArray[srcA + i]);
            }
            dst += lenSrcA;
        }
        else {  // can copy only part of srcA without changing dst
            // TBD efficiency

            for (int i = 0; i < dstLen; ++i) {
                new (&dstArray[dst + i]) T(srcArray[srcA + i]);
            }
            srcA += dstLen;
            lenSrcA -= dstLen;

            // WARNING:  There seems to be an AIX compiler issue for the
            // following four lines.  Removing the 'assert' and moving the
            // 'memcpy' down two lines may cause the program to compile, but
            // not execute properly.

            // TBD efficiency

            for (int i = 0; i < lenSrcA; ++i) {
                new (&dstArray[i]) T(srcArray[srcA + i]);
            }
            dstLen = dst;  // max numElements that can be copied to index 0
            dst = lenSrcA;

            // TBD
            // doc above assert(
            // lenSrcA + lenSrcB <= dstLen - k_EXTRA_CAPACITY);
        }
        dstLen -= lenSrcA;

        if (dstLen >= lenSrcB) {  // can copy everything from srcB
            // TBD efficiency

            for (int i = 0; i < lenSrcB; ++i) {
                new (&dstArray[dst + i]) T(srcArray[i]);
            }
            dst += lenSrcB;
        }
        else {  // can copy only part of srcB without changing dst
            // NOTE: could not have had insufficient room for srcA
            // TBD efficiency

            for (int i = 0; i < dstLen; ++i) {
                new (&dstArray[dst + i]) T(srcArray[i]);
            }
            lenSrcB -= dstLen;
            dst = lenSrcB;

            // TBD efficiency

            for (int i = 0; i < lenSrcB; ++i) {
                new (&dstArray[i]) T(srcArray[dstLen + i]);
            }
        }
    }

    return dst % dstSize;
}

template <class T>
void Queue<T>::memShiftLeft(T   *array,
                            int  size,
                            int  dstIndex,
                            int  srcIndex,
                            int  numElements)
{
    // Move the elements that do not wrap around the array end.

    if (srcIndex > dstIndex) {
        int numMove = size - srcIndex;
        if (numMove >= numElements) {
            // TBD efficiency

            for (int i = 0; i < numElements; ++i) {
                new (&array[dstIndex + i]) T(array[srcIndex + i]);
                array[srcIndex + i].~T();
            }
            return;                                                   // RETURN
        }

        // TBD efficiency

        for (int i = 0; i < numMove; ++i) {
            new (&array[dstIndex + i]) T(array[srcIndex + i]);
            array[srcIndex + i].~T();
        }
        numElements -= numMove;
        dstIndex += numMove;
        srcIndex = 0;
    }
    else if (srcIndex == dstIndex) {
        return;                                                       // RETURN
    }

    // Move the elements of the source that will just precede the array end.

    int numMove = size - dstIndex;
    if (numMove >= numElements) {
        // TBD efficiency

        for (int i = numElements - 1; i >= 0; --i) {
            new (&array[dstIndex + i]) T(array[srcIndex + i]);
            array[srcIndex + i].~T();
        }

        return;                                                       // RETURN
    }
    // TBD efficiency

    for (int i = numMove - 1; i >= 0; --i) {
        new (&array[dstIndex + i]) T(array[srcIndex + i]);
        array[srcIndex + i].~T();
    }
    numElements -= numMove;
    srcIndex += numMove;

    // Move the elements of the source that are around the array end.

    // TBD efficiency

    for (int i = 0; i < numElements; ++i) {
        new (&array[i]) T(array[srcIndex + i]);
        array[srcIndex + i].~T();
    }
}

template <class T>
void Queue<T>::memShiftRight(T   *array,
                             int  size,
                             int  dstIndex,
                             int  srcIndex,
                             int  numElements)
{
    if (dstIndex == srcIndex) {
        return;                                                       // RETURN
    }

    {

        // Move the elements of the source that wrap around the array end.

        int numMove = srcIndex + numElements;
        if (numMove > size) {
            numMove -= size;
            // TBD efficiency

            for (int i = numMove - 1; i >= 0; --i) {
                new (&array[(dstIndex + numElements - numMove) % size + i])
                                                                   T(array[i]);
                array[i].~T();
            }
            numElements -= numMove;
        }
    }

    {
        // Move the elements of the source that will wrap around the array end.

        int numMove = dstIndex + numElements;
        if (numMove > size) {
            numMove -= size;
            // TBD efficiency

            for (int i = 0; i < numMove; ++i) {
                new (&array[i])
                       T(array[(srcIndex + numElements - numMove) % size + i]);
                array[srcIndex + numElements - numMove + i].~T();
            }
            numElements -= numMove;
        }
    }

    // Move the elements of the source that do not and will not wrap around
    // the array end.

    if (dstIndex < srcIndex) {
        // TBD efficiency

        for (int i = 0; i < numElements; ++i) {
            new (&array[dstIndex + i]) T(array[srcIndex + i]);
            array[srcIndex + i].~T();
        }
    }
    else {
        // TBD efficiency

        for (int i = numElements - 1; i >= 0; --i) {
            new (&array[dstIndex + i]) T(array[srcIndex + i]);
            array[srcIndex + i].~T();
        }
    }
}

template <class T>
inline
void Queue<T>::copyData(T       *dstArray,
                        int     *dstBack,
                        int      dstSize,
                        int      dstFront,
                        const T *srcArray,
                        int      srcSize,
                        int      srcFront,
                        int      srcBack)
{
    const int dstIndex = (dstFront + 1) % dstSize;
    const int srcIndex = (srcFront + 1) % srcSize;
    const int numElements = (srcBack + srcSize - srcFront - 1) % srcSize;

    *dstBack = memcpyCircular(dstArray,
                              dstSize,
                              dstIndex,
                              srcArray,
                              srcSize,
                              srcIndex,
                              numElements);
}

template <class T>
int Queue<T>::increaseSizeImp(T                **addrArray,
                              int               *front,
                              int               *back,
                              int                newSize,
                              int                size,
                              bslma::Allocator  *allocator)
{
    T *array = (T *)allocator->allocate(newSize * sizeof **addrArray);

    // COMMIT

    const int oldFront = *front;
    const int oldBack = *back;
    *front = newSize - 1;
    copyData(array, back, newSize, *front, *addrArray, size, oldFront, *back);

    // TBD efficiency

    for (int i = (oldFront + 1) % size; i != oldBack; i = (i + 1) % size) {
        (*addrArray)[i].~T();
    }

    allocator->deallocate(*addrArray);
    *addrArray = array;
    return newSize;
}

template <class T>
inline
void Queue<T>::increaseSize()
{
    d_size = increaseSizeImp(&d_array_p,
                             &d_front,
                             &d_back,
                             d_size * k_GROW_FACTOR,
                             d_size,
                             d_allocator_p);
}

// CLASS METHODS
template <class T>
inline
int Queue<T>::maxSupportedBdexVersion(int /* versionSelector */)
{
    return 1;  // Required by BDE policy; versions start at 1.
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // pending deprecation

// DEPRECATED METHODS

template <class T>
inline
int Queue<T>::maxSupportedVersion()
{
    return maxSupportedBdexVersion();
}

template <class T>
inline
int Queue<T>::maxSupportedBdexVersion()
{
    return 1;  // Required by BDE policy; versions start at 1.
}

#endif // BDE_OMIT_INTERNAL_DEPRECATED -- pending deprecation

// CREATORS
template <class T>
Queue<T>::Queue(bslma::Allocator *basicAllocator)
: d_size(k_INITIAL_SIZE)
, d_front(k_INITIAL_SIZE - 1)
, d_back(0)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);
}

template <class T>
Queue<T>::Queue(unsigned int initialLength, bslma::Allocator *basicAllocator)
: d_back(initialLength)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_size = calculateSufficientSize(initialLength, k_INITIAL_SIZE);
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);
    d_front = d_size - 1;

    // initialize the array values
    // TBD efficiency
    // TBD exception neutrality

    for (int i = 0; i < d_back; ++i) {
        new (d_array_p + i) T();
    }
}

template <class T>
Queue<T>::Queue(int               initialLength,
                const T&          initialValue,
                bslma::Allocator *basicAllocator)
: d_back(initialLength)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_size = calculateSufficientSize(initialLength, k_INITIAL_SIZE);
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);
    d_front = d_size - 1;

    // TBD efficiency
    // TBD exception neutrality

    for (int i = 0; i < d_back; ++i) {
        new (d_array_p + i) T(initialValue);
    }
}

template <class T>
Queue<T>::Queue(const InitialCapacity&  numElements,
                bslma::Allocator       *basicAllocator)
: d_size(numElements.d_i + k_EXTRA_CAPACITY) // to hold the empty positions
, d_front(numElements.d_i + k_EXTRA_CAPACITY - 1)
, d_back(0)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);
}

template <class T>
Queue<T>::Queue(const T          *srcArray,
                int               numElements,
                bslma::Allocator *basicAllocator)
: d_back(numElements)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_size = calculateSufficientSize(numElements, k_INITIAL_SIZE);
    d_front = d_size - 1;
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);

    // TBD efficiency

    for (int i = 0; i < numElements; ++i) {
        new (&d_array_p[i]) T(srcArray[i]);
    }
}

template <class T>
Queue<T>::Queue(const Queue& original, bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    d_size = calculateSufficientSize(original.length(), k_INITIAL_SIZE);
    d_array_p = (T *)d_allocator_p->allocate(d_size * sizeof *d_array_p);
    d_front = d_size - 1;
    copyData(d_array_p,
             &d_back,
             d_size,
             d_front,
             original.d_array_p,
             original.d_size,
             original.d_front,
             original.d_back);
}

template <class T>
Queue<T>::~Queue()
{
    // TBD efficiency

    for (int i = (d_front + 1) % d_size; i != d_back; i = (i + 1) % d_size) {
        d_array_p[i].~T();
    }

    d_allocator_p->deallocate(d_array_p);
}

// MANIPULATORS
template <class T>
Queue<T>& Queue<T>::operator=(const Queue& rhs)
{
    if (this != &rhs) {
        const int newSize =
                         calculateSufficientSize(rhs.length(), k_INITIAL_SIZE);
        if (newSize > d_size) {
            T *array =
                     (T *)d_allocator_p->allocate(newSize * sizeof *d_array_p);

            // TBD efficiency

            for (int i = (d_front + 1) % d_size; i != d_back;
                                                        i = (i + 1) % d_size) {
                d_array_p[i].~T();
            }

            d_allocator_p->deallocate(d_array_p);
            d_array_p = array;
            d_size = newSize;
        }
        else {
            // TBD efficiency

            for (int i = (d_front + 1) % d_size; i != d_back;
                                                        i = (i + 1) % d_size) {
                d_array_p[i].~T();
            }
        }
        copyData(d_array_p,
                 &d_back,
                 d_size,
                 d_front,
                 rhs.d_array_p,
                 rhs.d_size,
                 rhs.d_front,
                 rhs.d_back);
    }
    return *this;
}

template <class T>
inline
T& Queue<T>::operator[](int index)
{
    return d_array_p[(index + d_front + 1) % d_size];
}

template <class T>
void Queue<T>::append(const Queue& srcQueue)
{
    const int numElements = srcQueue.length();
    const int newLength = length() + numElements;
    const int minSize = calculateSufficientSize(newLength, d_size);
    if (d_size < minSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 minSize,
                                 d_size,
                                 d_allocator_p);
    }
    d_back = memcpyCircular(d_array_p,
                            d_size,
                            d_back,
                            srcQueue.d_array_p,
                            srcQueue.d_size,
                            (srcQueue.d_front + 1) % srcQueue.d_size,
                            numElements);
}

template <class T>
void Queue<T>::append(const Queue& srcQueue,
                      int          srcIndex,
                      int          numElements)
{
    const int newLength = length() + numElements;
    const int minSize = calculateSufficientSize(newLength, d_size);
    if (d_size < minSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 minSize,
                                 d_size,
                                 d_allocator_p);
    }
    d_back = memcpyCircular(d_array_p,
                            d_size,
                            d_back,
                            srcQueue.d_array_p,
                            srcQueue.d_size,
                            (srcQueue.d_front + 1 + srcIndex) %
                                srcQueue.d_size,
                            numElements);
}

template <class T>
inline
T& Queue<T>::back()
{
    return d_array_p[(d_back - 1 + d_size) % d_size];
}

template <class T>
inline
T& Queue<T>::front()
{
    return d_array_p[(d_front + 1) % d_size];
}

template <class T>
void Queue<T>::insert(int dstIndex, const T& item)
{
    T itemCopy(item);  // TBD hack for aliased case

    // The capacity must always be greater than or equal to
    // 'length + k_EXTRA_CAPACITY'.

    const int originalLength = length();
    const int newLength = originalLength + 1;
    const int newSize = calculateSufficientSize(newLength, d_size);

    if (d_size < newSize) {
        // resize, makes move easy

        T *array = (T *)d_allocator_p->allocate(newSize * sizeof *d_array_p);

        // COMMIT

        const int start = d_front + 1;

        // NOTE: newSize >= size + 1 so '% newSize' is not needed in next line.

        memcpyCircular(array,
                       newSize,
                       start,           // no '% newSize'
                       d_array_p,
                       d_size,
                       start % d_size,
                       dstIndex);
        memcpyCircular(array,
                       newSize,
                       (start + dstIndex + 1) % newSize,
                       d_array_p,
                       d_size,
                       (start + dstIndex) % d_size,
                       originalLength - dstIndex);

        // TBD efficiency

        for (int i = (d_front + 1) % d_size; i != d_back;
                                                        i = (i + 1) % d_size) {
            d_array_p[i].~T();
        }

        d_allocator_p->deallocate(d_array_p);
        d_array_p = array;

        d_size = newSize;
        d_back = (start + newLength) % d_size;
        new (&d_array_p[(start + dstIndex) % d_size]) T(itemCopy);
    }
    else {  // sufficient capacity

        // No resize is required.  Copy as few elements as possible.

        // Compute number of elements that are past the insertion point: the
        // back length.

        const int backLen = originalLength - dstIndex;

        if (dstIndex < backLen) {

            // We will choose to shift 'dstIndex' elements to the left.

            const int src = (d_front + 1) % d_size;
            const int dst = d_front;

            memShiftLeft(d_array_p, d_size, dst, src, dstIndex);
            new (&d_array_p[(d_front + dstIndex) % d_size]) T(itemCopy);
            d_front = (d_front - 1 + d_size) % d_size;
        }
        else {

            // We will choose to shift 'backLen' elements to the right.

            const int src = (d_front + 1 + dstIndex) % d_size;
            const int dst = (src + 1) % d_size;

            memShiftRight(d_array_p,
                          d_size,
                          dst,
                          src,
                          backLen);
            new (&d_array_p[(d_front + 1 + dstIndex) % d_size]) T(itemCopy);
            d_back = (d_back + 1) % d_size;
        }
    }
}

template <class T>
void Queue<T>::insert(int          dstIndex,
                      const Queue& srcQueue,
                      int          srcIndex,
                      int          numElements)
{
    // The capacity must always be greater than or equal to
    // 'length + k_EXTRA_CAPACITY'.

    const int originalLength = length();
    const int newLength = originalLength + numElements;
    const int newSize = calculateSufficientSize(newLength, d_size);

    if (d_size < newSize) {
        // resize, makes move easy

        T *array = (T *)d_allocator_p->allocate(newSize * sizeof *d_array_p);

        // COMMIT

        const int start = d_front + 1;
        const int startIndex = start + dstIndex;

        // NOTE: newSize >= size + 1 so '% newSize' is not needed in next line.

        memcpyCircular(array,
                       newSize,
                       start,           // no '% newSize'
                       d_array_p,
                       d_size,
                       start % d_size,
                       dstIndex);
        memcpyCircular(array,
                       newSize,
                       (startIndex + numElements) % newSize,
                       d_array_p,
                       d_size,
                       (startIndex) % d_size,
                       originalLength - dstIndex);
        memcpyCircular(array,
                       newSize,
                       startIndex % newSize,
                       srcQueue.d_array_p,
                       srcQueue.d_size,
                       (srcQueue.d_front + 1 + srcIndex) % srcQueue.d_size,
                       numElements);

        // TBD efficiency

        for (int i = (d_front + 1) % d_size; i != d_back;
                                                        i = (i + 1) % d_size) {
            d_array_p[i].~T();
        }

        d_allocator_p->deallocate(d_array_p);
        d_array_p = array;
        d_size = newSize;
        d_back = (start + newLength) % d_size;
    }
    else { // sufficient capacity

        // No resize is required.  Copy as few elements as possible.

        // Compute number of elements that are past the insertion point: the
        // back length.

        const int backLen = originalLength - dstIndex;
        if (dstIndex < backLen) {

            // We will shift 'dstIndex' elements to the left.

            const int d = (d_front + 1 - numElements + d_size) % d_size;
            memShiftLeft(d_array_p,
                         d_size,
                         d,
                         (d_front + 1) % d_size,
                         dstIndex);

            if (this != &srcQueue || srcIndex >= dstIndex) {  // not aliased
                memcpyCircular(d_array_p,
                               d_size,
                               (d + dstIndex) % d_size,
                               srcQueue.d_array_p,
                               srcQueue.d_size,
                               (srcQueue.d_front + 1 + srcIndex) %
                                                               srcQueue.d_size,
                               numElements);
            }
            else { // aliased
                const int distance = dstIndex - srcIndex;
                if (distance >= numElements) {
                    memcpyCircular(d_array_p,
                                   d_size,
                                   (d + dstIndex) % d_size,
                                   d_array_p,
                                   d_size,
                                   (d + srcIndex) % d_size,
                                   numElements);
                }
                else {
                    memcpyCircular(d_array_p,
                                   d_size,
                                   (d + dstIndex) % d_size,
                                   d_array_p,
                                   d_size,
                                   (d + srcIndex) % d_size,
                                   distance);
                    memcpyCircular(d_array_p,
                                   d_size,
                                   (d + dstIndex + distance) % d_size,
                                   d_array_p,
                                   d_size,
                                   (d_front + 1 + dstIndex) % d_size,
                                   numElements - distance);
                }
            }
            d_front = (d_front - numElements + d_size) % d_size;
        }
        else {

            // We will shift 'backLen' elements to the right.

            // Destination index is as close or closer to the back as to the
            // front.

            const int s = (d_front + 1 + dstIndex) % d_size;
            memShiftRight(d_array_p,
                          d_size,
                          (s + numElements) % d_size,
                          s,
                          backLen);

            if (this != &srcQueue ||
                           srcIndex + numElements <= dstIndex) { // not aliased
                memcpyCircular(d_array_p,
                               d_size,
                               s,
                               srcQueue.d_array_p,
                               srcQueue.d_size,
                               (srcQueue.d_front + 1 + srcIndex) %
                                                               srcQueue.d_size,
                               numElements);
            }
            else { // aliased
                if (dstIndex <= srcIndex) {
                    memcpyCircular(d_array_p,
                                   d_size,
                                   s,
                                   d_array_p,
                                   d_size,
                                   (d_front + 1 + srcIndex + numElements) %
                                                                        d_size,
                                   numElements);
                }
                else {
                    const int distance = dstIndex - srcIndex;
                    memcpyCircular(d_array_p,
                                   d_size,
                                   s,
                                   d_array_p,
                                   d_size,
                                   (d_front + 1 + srcIndex) % d_size,
                                   distance);
                    memcpyCircular(d_array_p,
                                   d_size,
                                   (s + distance) % d_size,
                                   d_array_p,
                                   d_size,
                                   (d_front + 1 + srcIndex + distance +
                                                         numElements) % d_size,
                                   numElements - distance);
                }
            }
            d_back = (d_back + numElements) % d_size;
        }
    }
}

template <class T>
inline
void Queue<T>::insert(int dstIndex, const Queue& srcQueue)
{
    insert(dstIndex, srcQueue, 0, srcQueue.length());
}

template <class T>
inline
void Queue<T>::popBack()
{
    d_back = (d_back - 1 + d_size) % d_size;
    d_array_p[d_back].~T();
}

template <class T>
inline
void Queue<T>::popFront()
{
    d_front = (d_front + 1) % d_size;
    d_array_p[d_front].~T();
}

template <class T>
void Queue<T>::pushBack(const T& item)
{
    T itemCopy(item);  // TBD aliasing hack

    int newBack = (d_back + 1) % d_size;
    if (d_front == newBack) {
        increaseSize();  // NOTE: this can change the value of d_back
        newBack = (d_back + 1) % d_size;
    }
    new (&d_array_p[d_back]) T(itemCopy);
    d_back = newBack;
}

template <class T>
void Queue<T>::pushFront(const T& item)
{
    T itemCopy(item);  // TBD aliasing hack

    int newFront = (d_front - 1 + d_size) % d_size;
    if (newFront == d_back) {
        increaseSize();  // NOTE: this can change the value of d_front
        newFront = (d_front - 1 + d_size) % d_size;
    }
    new (&d_array_p[d_front]) T(itemCopy);
    d_front = newFront;
}

template <class T>
inline
void Queue<T>::append(const T& item)
{
    pushBack(item);
}

template <class T>
void Queue<T>::remove(int index)
{
    d_array_p[(index + d_front + 1) % d_size].~T();

    // Compute number of elements that are past the insertion point: the back
    // length.

    const int backLen =
               (d_back - d_front - k_EXTRA_CAPACITY - index + d_size) % d_size;

    if (index < backLen) {
        d_front = (d_front + 1) % d_size;
        memShiftRight(d_array_p,
                      d_size,
                      (d_front + 1) % d_size,
                      d_front,
                      index);
    }
    else {
        const int d = (d_front + 1 + index) % d_size;
        memShiftLeft(d_array_p,
                     d_size,
                     d,
                     (d + 1) % d_size,
                     (d_back + d_size - d_front - 1) % d_size - 1 - index);
        d_back = (d_back - 1 + d_size) % d_size;
    }
}

template <class T>
void Queue<T>::remove(int index, int numElements)
{
    // TBD efficiency

    for (int i = 0; i < numElements; ++i) {
        d_array_p[(index + d_front + 1 + i) % d_size].~T();
    }

    // Compute number of elements that are past the insertion point: the back
    // length.

    const int backLen = (d_back - d_front - 1
                                      - index - numElements + d_size) % d_size;
    if (index < backLen) {
        const int dst = (d_front + 1 + numElements) % d_size;
        const int src = (d_front + 1) % d_size;

        memShiftRight(d_array_p, d_size, dst, src, index);
        d_front = (d_front + numElements) % d_size;
    }
    else {
        const int dst = (d_front + 1 + index) % d_size;
        const int src = (dst + numElements) % d_size;

        memShiftLeft(d_array_p,
                     d_size,
                     dst,
                     src,
                     (d_back + d_size - d_front - 1) % d_size -
                                                          numElements - index);
        d_back = (d_back - numElements + d_size) % d_size;
    }
}

template <class T>
void Queue<T>::removeAll(bsl::vector<T> *buffer)
{
    d_front = (d_front + 1) % d_size;

    // TBD efficiency

    if (buffer) {
        while (d_back != d_front) {
            buffer->push_back(d_array_p[d_front]);
            d_array_p[d_front].~T();
            d_front = (d_front + 1) % d_size;
        }
    } else {
        while (d_back != d_front) {
            d_array_p[d_front].~T();
            d_front = (d_front + 1) % d_size;
        }
    }
    d_front = (d_back - 1 + d_size) % d_size;
}

template <class T>
void Queue<T>::replace(int dstIndex, const T& item)
{
    T itemCopy(item);  // TBD hack for aliased case

    // TBD efficiency

    d_array_p[(d_front + 1 + dstIndex) % d_size].~T();
    new (&d_array_p[(d_front + 1 + dstIndex) % d_size]) T(itemCopy);
}

template <class T>
void Queue<T>::replace(int          dstIndex,
                       const Queue& srcQueue,
                       int          srcIndex,
                       int          numElements)
{
    // TBD need placement new

    if (this != &srcQueue || srcIndex + numElements <= dstIndex ||
                          dstIndex + numElements <= srcIndex) {  // not aliased
        memcpyCircular(d_array_p,
                       d_size,
                       (d_front + 1 + dstIndex) % d_size,
                       srcQueue.d_array_p,
                       srcQueue.d_size,
                       (srcQueue.d_front + 1 + srcIndex) % srcQueue.d_size,
                       numElements);
    }
    else {  // aliased; do nothing if srcIndex == dstIndex
        if (srcIndex < dstIndex) {
            memShiftRight(d_array_p,
                          d_size,
                          (d_front + 1 + dstIndex) % d_size,
                          (d_front + 1 + srcIndex) % d_size,
                          numElements);
        }
        else if (srcIndex > dstIndex) {
            memShiftLeft(d_array_p,
                         d_size,
                         (d_front + 1 + dstIndex) % d_size,
                         (d_front + 1 + srcIndex) % d_size,
                         numElements);
        }
    }
}

template <class T>
void Queue<T>::reserveCapacity(int numElements)
{
    const int newSize = calculateSufficientSize(numElements, d_size);
    if (d_size < newSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 newSize,
                                 d_size,
                                 d_allocator_p);

        // To improve testability, all empty queues have canonical front and
        // back values.

        if (0 == length()) {
            d_front = d_size - 1;
            d_back = 0;
        }
    }
}

template <class T>
void Queue<T>::reserveCapacityRaw(int numElements)
{
    const int newSize = numElements + k_EXTRA_CAPACITY;
                                            // to hold the front/back positions

    if (d_size < newSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 newSize,
                                 d_size,
                                 d_allocator_p);
    }
}

template <class T>
void Queue<T>::setLength(int newLength)
{
    const int newSize = newLength + k_EXTRA_CAPACITY;
                                            // to hold the front/back positions

    if (d_size < newSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 newSize,
                                 d_size,
                                 d_allocator_p);
    }
    const int oldBack = d_back;
    const int oldLength = length();
    d_back = (d_front + 1 + newLength) % d_size;
    if (newLength > oldLength) {
        if (oldBack < d_back) {
            // TBD efficiency

            for (int i = 0; i < d_back - oldBack; ++i) {
                new (d_array_p + oldBack + i) T();
            }
        }
        else {
            // TBD efficiency

            for (int i = 0; i < d_size - oldBack; ++i) {
                new (d_array_p + oldBack + i) T();
            }

            // TBD efficiency

            for (int i = 0; i < d_back; ++i) {
                new (d_array_p + i) T();
            }
        }
    }
}

template <class T>
void Queue<T>::setLength(int newLength, const T& initialValue)
{
    const int newSize = newLength + k_EXTRA_CAPACITY;
                                                 // to hold the empty positions

    if (d_size < newSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 newSize,
                                 d_size,
                                 d_allocator_p);
    }
    const int oldBack = d_back;
    const int oldLength = length();
    d_back = (d_front + 1 + newLength) % d_size;
    if (newLength > oldLength) {
        if (oldBack < d_back) {
            // TBD efficiency

            for (int i = 0; i < d_back - oldBack; ++i) {
                new (d_array_p + oldBack + i) T(initialValue);
            }
        }
        else {
            // TBD efficiency

            for (int i = 0; i < d_size - oldBack; ++i) {
                new (d_array_p + oldBack + i) T(initialValue);
            }
            // TBD efficiency

            for (int i = 0; i < d_back; ++i) {
                new (d_array_p + i) T(initialValue);
            }
        }
    }
}

template <class T>
void Queue<T>::setLengthRaw(int newLength)
{
    const int newSize = newLength + k_EXTRA_CAPACITY;
                                                 // to hold the empty positions

    if (d_size < newSize) {
        d_size = increaseSizeImp(&d_array_p,
                                 &d_front,
                                 &d_back,
                                 newSize,
                                 d_size,
                                 d_allocator_p);
    }
    d_back = (d_front + 1 + newLength) % d_size;
}

template <class T>
template <class STREAM>
STREAM& Queue<T>::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            int newLength;
            stream.getLength(newLength);

            if (stream) {
                int newSize = calculateSufficientSize(newLength, d_size);
                if (d_size < newSize) {
                    d_size = increaseSizeImp(&d_array_p,
                                             &d_front,
                                             &d_back,
                                             newSize,
                                             d_size,
                                             d_allocator_p);
                }
                d_front = d_size - 1;
                d_back = newLength;
                for (int i = 0; i < newLength && stream; ++i) {
                    bslx::InStreamFunctions::bdexStreamIn(
                                                  stream, (*this)[i], version);
                }
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

template <class T>
void Queue<T>::swap(int index1, int index2)
{
    if (index1 != index2) {
        const int tmp = d_front + 1;
        const int i1 = (tmp + index1) % d_size;
        const int i2 = (tmp + index2) % d_size;

        T temp(d_array_p[i1]);
        d_array_p[i1].~T();
        new (d_array_p + i1) T(d_array_p[i2]);
        d_array_p[i2].~T();
        new (d_array_p + i2) T(temp);
    }
}

// ACCESSORS
template <class T>
inline
const T& Queue<T>::operator[](int index) const
{
    return d_array_p[(index + d_front + 1) % d_size];
}

template <class T>
inline
const T& Queue<T>::back() const
{
    return d_array_p[(d_back - 1 + d_size) % d_size];
}

template <class T>
inline
const T& Queue<T>::front() const
{
    return d_array_p[(d_front + 1) % d_size];
}

template <class T>
bsl::ostream& Queue<T>::print(bsl::ostream& stream,
                                    int           level,
                                    int           spacesPerLevel) const
{
    if (level < 0) {
        level = -level;
    }
    else {
        bdlb::Print::indent(stream, level, spacesPerLevel);
    }

    int levelPlus1 = level + 1;
    if (0 <= spacesPerLevel) {

        stream << "[\n";

        const int len = length();
        for (int i = 0; i < len; ++i) {
            bdlb::Print::indent(stream, levelPlus1, spacesPerLevel);
            stream << d_array_p[(i + d_front + 1) % d_size] << '\n';
        }

        bdlb::Print::indent(stream, level, spacesPerLevel);
        stream << "]\n";
    }
    else {
        stream << "[ ";

        const int len = length();
        for (int i = 0; i < len; ++i) {
            stream << ' ';
            stream << d_array_p[(i + d_front + 1) % d_size];
        }

        stream << " ] ";
    }
    return stream << bsl::flush;
}

template <class T>
template <class STREAM>
STREAM& Queue<T>::bdexStreamOut(STREAM& stream, int version) const
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            const int len = length();
            stream.putLength(len);
            for (int i = 0; i < len && stream; ++i) {
                bslx::OutStreamFunctions::bdexStreamOut(
                                                  stream, (*this)[i], version);
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

}  // close package namespace

// FREE OPERATORS
template <class T>
bool bdlc::operator==(const Queue<T>& lhs, const Queue<T>& rhs)
{
    const int len = lhs.length();
    if (rhs.length() != len) {
        return 0;                                                     // RETURN
    }

    // Lengths are equal.

    for (int i = 0; i < len; ++i) {
        if (!(lhs[i] == rhs[i])) {
            return 0;                                                 // RETURN
        }
    }
    return 1;
}

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

template <class T>
inline
bsl::ostream& bdlc::operator<<(bsl::ostream& stream, const Queue<T>& queue)
{
    return queue.streamOut(stream);
}

}  // 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 ----------------------------------