// bdlt_packedcalendar.h                                              -*-C++-*-
#ifndef INCLUDED_BDLT_PACKEDCALENDAR
#define INCLUDED_BDLT_PACKEDCALENDAR

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

//@PURPOSE: Provide a compact repository for weekend/holiday information.
//
//@CLASSES:
//  bdlt::PackedCalendar: compact repository of weekend/holiday information
//
//@SEE_ALSO: bdlt_calendar
//
//@DESCRIPTION: This component provides a value-semantic class,
// 'bdlt::PackedCalendar', that represents weekend and holiday information over
// a *valid* *range* of dates.  A 'bdlt::PackedCalendar' is an approximation to
// the same *mathematical* type, and is capable of representing the same subset
// of *mathematical* values, as a 'bdlt::Calendar'.
//
// But unlike 'bdlt::Calendar', which is optimized for runtime efficiency,
// 'bdlt::PackedCalendar' is designed to minimize the amount of in-process
// memory required to represent that information.  For example, a packed
// calendar storing 250 holidays and holiday codes can consume as little as
// approximately 0.75K bytes (e.g., 2 bytes per holiday plus 1 byte per
// holiday code) to as much as approximately 3K bytes (e.g., 8 bytes per
// holiday plus 4 bytes per holiday code) depending upon the data of the
// calendar.  For typical calendars having a range of 40 years and 10 holidays
// per year, the expected size of the packed calendar is about half that of a
// similar implementation using a non-packed structure.
//
// Default-constructed calendars are empty, and have an empty valid range.
// Calendars can also be constructed with an initial (non-empty) valid range,
// implying that all dates within that range are business days.  The
// 'setValidRange' and 'addDay' methods modify the valid range of a calendar,
// and a suite of "add" methods can be used to populate a calendar with
// weekend days and holidays.
//
// The 'addHolidayCode' method associates an integer "holiday code" with a
// specific date, and can be called repeatedly with different integers and the
// same date to build up a set of holiday codes for that date.  Note that
// holiday codes are unique integers that, from the perspective of the
// calendar, have no particular meaning.  Typically, the user will choose
// holiday code values that are indices into an auxiliary collection (such as a
// 'bsl::vector<bsl::string>') to identify holiday names for end-user display.
//
// Once a calendar is populated, a rich set of accessor methods can be used to
// determine, e.g., if a given date is a business day, or the number of
// non-business days within some subrange of the calendar.  The holidays
// within a calendar can be obtained in increasing (chronological) order using
// an iterator identified by the nested 'HolidayConstIterator' 'typedef'.  The
// set of holiday codes associated with an arbitrary date in a
// 'bdlt::PackedCalendar' (or the current holiday referred to by a
// 'HolidayConstIterator') can be obtained in increasing (numerical) order
// using an iterator identified by the nested 'HolidayCodeConstIterator'
// 'typedef' (see below).
//
// Calendars are value-semantic objects, and, as such, necessarily support all
// of the standard value-semantic operations, such as default construction,
// copy construction and copy assignment, equality comparison, and
// externalization (BDEX streaming, in particular).  Calendars also support
// the notions of both union and intersection merging operations, whereby a
// calendar can change its value to contain the union or intersection of its
// own contained weekend days, holidays, and holiday codes with those of
// another calendar object.  Such merging operations will, in general, also
// alter the valid range of the resulting calendar.  Note that merged
// calendars can be significantly more efficient for certain repeated
// "is-common-business-day" determinations among two or more calendars.
//
///Weekend Days and Weekend-Days Transitions
///-----------------------------------------
// A calendar maintains a set of dates considered to be weekend days.
// Typically, a calendar's weekend days fall on the same days of the week for
// the entire range of a calendar.  For example, the weekend for United States
// has consisted of Saturday and Sunday since the year 1940.  The
// 'addWeekendDay' and 'addWeekendDays' methods can be used to specify the
// weekend days for these calendars.
//
// However, sometimes a calendar's weekend days changes over time.  For
// example, Bangladesh's weekend consisted of Friday until June 1, 1997 when
// Bangladesh changed its weekends to contain both Friday and Saturday.  Later,
// on October 1, 2001, Bangladesh reverted to a weekend of only Friday, until
// on September 9, 2009, Bangladesh again changed its weekends to include both
// Friday and Saturday.
//
// To optimize for space allocation while supporting both consistent and
// changing weekend days, a calendar represents weekend information using a
// sequence of "weekend-days transitions", each of which comprises a date and a
// set of days of the week considered to be the weekend on and after that
// date.  To represent the weekend days of Bangladesh, a calendar can use a
// sequence of four weekend-days transitions: (1) a transition on January 1,
// 0001 having a weekend day set containing only Friday, (2) a transition at
// June 1, 1997 having a weekend day set containing Friday and Saturday, (3) a
// transition at October 1, 2001 having a weekend day set containing only
// Friday, and (4) a transition at September 9, 2009 having a weekend day set
// containing Friday and Saturday.  To represent the weekend days of the United
// States, a calendar having a range after 1940 can use a single weekend-days
// transition on January 1, 0001 containing Saturday and Sunday.
//
// On construction, a calendar does not contain any weekend-days transitions.
// The 'addWeekendDaysTransition' method adds a new weekend-days transition or
// replaces an existing weekend-days transition.  The 'addWeekendDay' and
// 'addWeekendDays' methods create a weekend-days transition at January 1,
// 0001, if one doesn't already exist, and update the set of weekend days for
// that transition.  'addWeekendDay' and 'addWeekendDays' should only be used
// for calendars having a consistent set of weekend days throughout their
// entire range.  The use of 'addWeekendDay' and 'addWeekendDays' is intended
// to be *mutually* *exclusive* to the use of 'addWeekendDaysTransition'.  As
// such, the behavior of using these two methods together with
// 'addWeekendDaysTransition' is undefined.
//
///Nested Iterators
///----------------
// Also provided are several STL-style 'const' bidirectional iterators
// accessible as nested 'typedef's.  'HolidayConstIterator',
// 'HolidayCodeConstIterator', 'WeekendDaysTransitionConstIterator', and
// 'BusinessDayConstIterator', respectively, iterate over a chronologically
// ordered sequence of holidays, a numerically ordered sequence of holiday
// codes, a sequence of chronologically ordered weekend-days transitions, and a
// sequence of chronologically ordered business days.  Reverse iterators are
// also provided for each of these (forward) iterators.  As a general rule,
// calling a 'const' method will not invalidate any iterators, and calling a
// non-'const' method might invalidate all of them; it is, however, guaranteed
// that attempting to add *duplicate* holidays or holiday codes will have no
// effect, and therefore will not invalidate any iterators.  It is also
// guaranteed that adding a new code for an existing holiday will not
// invalidate any 'HolidayConstIterator' objects.
//
// Note that these iterators do *not* meet the requirements for a
// 'bsl::forward_iterator' and should not be used in standard algorithms (e.g.,
// 'bsl::lower_bound').
//
///Iterator Invalidation
///---------------------
// The modification of a 'bdlt::PackedCalendar' will invalidate iterators
// referring to the calendar.  The following table shows the relationship
// between a calendar manipulator and the types of iterators it will
// invalidate if the invocation of the manipulator modified the calendar (e.g.,
// using 'addHoliday' with a date that is not currently a holiday in the
// calendar):
//..
//          Manipulator                         Invalidates
//    --------------------------            --------------------
//    'operator='                           H    HC    WDT    BD
//    'addHoliday'                          H    HC           BD
//    'addHolidayCode'                           HC
//    'addHolidayCodeIfInRange'                  HC
//    'addHolidayIfInRange'                 H    HC           BD
//    'addWeekendDay'                                  WDT    BD
//    'addWeekendDays'                                 WDT    BD
//    'addWeekendDaysTransition'                       WDT    BD
//    'intersectBusinessDays'               H    HC    WDT    BD
//    'intersectNonBusinessDays'            H    HC    WDT    BD
//    'removeAll'                           H    HC    WDT    BD
//    'removeHoliday'                       H    HC           BD
//    'removeHolidayCode'                        HC
//    'setValidRange'                       H    HC           BD
//    'unionBusinessDays'                   H    HC    WDT    BD
//    'unionNonBusinessDays'                H    HC    WDT    BD
//
// where "H" represents the holiday iterators ('HolidayConstIterator' and
// 'HolidayConstReverseIterator'), "HC" represents the holiday code iterators
// ('HolidayCodeConstIterator' and 'HolidayCodeConstReverseIterator'), "WDT"
// represents the weekend-days transition iterators
// ('WeekendDaysTransitionConstIterator' and
// 'WeekendDaysTransitionConstReverseIterator'), and "BD" represents the
// business day iterators ('BusinessDayConstIterator' and
// 'BusinessDayConstReverseIterator').
//..
//
///Performance and Exception-Safety Guarantees
///-------------------------------------------
// The asymptotic worst-case performance of representative operations is
// characterized using big-O notation, 'O[f(N,M,W,V)]'.  'N' and 'M' each refer
// to the combined number ('H + C') of holidays 'H' (i.e., method
// 'numHolidays') and holiday codes 'C' (i.e., 'numHolidayCodesTotal') in the
// respective packed calendars.  'W' and 'V' each refer to the (likely small)
// number of weekend-days transitions in the respective packed calendars.  For
// clarity, the methods have abbreviated arguments: 'b', 'e', and 'd' are
// dates, 'c' is a holiday code, 'u' is a weekday, and 'w' is a set of
// weekdays.  Here, *Best* *Case* complexity, denoted by 'B[f(N)]', is loosely
// defined (for manipulators) as the worst-case cost, provided that (1) no
// additional internal capacity is required, (2) the start of the valid range
// does not change, and (3) that if a holiday (or holiday code) is being added,
// it is being appended *to* *the* *end* of the current sequence (of the latest
// holiday).
//..
//                                    Worst       Best    Exception-Safety
//  Operation                          Case       Case      Guarantee
//  ---------                         -----       ----    ----------------
//  DEFAULT CTOR                      O[1]                No-Throw
//  COPY CTOR(N)                      O[N]                Exception-Safe
//  N.DTOR()                          O[1]                No-Throw
//
//  N.OP=(M)                          O[M]                Basic <*>
//
//  N.reserveCapacity(H, C)           O[N]                Strong <*>
//
//  N.setValidRange(b, e)             O[N]        O[1]    Basic <*>
//  N.addDay(d)                       O[N]        O[1]    Basic <*>
//  N.addHoliday(d)                   O[N]        O[1]    Basic <*>
//  N.addHolidayCode(d,c)             O[N]        O[1]    Basic <*>
//
//  N.addWeekendDay(u)                O[1]                No-Throw
//  N.addWeekendDaysTransition(d,w)   O[W]                Basic <*>
//
//  N.intersectBusinessDays(M)        O[N+M+W+V]          Basic <*>
//  N.intersectNonBusinessDays(M)     O[N+M+W+V]          Basic <*>
//  N.unionBusinessDays(M)            O[N+M+W+V]          Basic <*>
//  N.unionNonBusinessDays(M)         O[N+M+W+V]          Basic <*>
//
//  N.removeHoliday(d)                O[N]                No-Throw
//  N.removeHolidayCode(d, c)         O[N]                No-Throw
//  N.removeAll();                    O[1]                No-Throw
//
//  N.swap(M)                         O[1]                No-Throw
//
//  N.firstDate()                     O[1]                No-Throw
//  N.lastDate()                      O[1]                No-Throw
//  N.length()                        O[1]                No-Throw
//
//  N.numHolidays()                   O[1]                No-Throw
//
//  N.numHolidayCodesTotal()          O[1]                No-Throw
//  N.numWeekendDaysInRange()         O[1]                No-Throw
//
//  N.isInRange(d);                   O[1]                No-Throw
//  N.isWeekendDay(w);                O[1]                No-Throw
//  N.isWeekendDay(d)                 O[log(W)]           No-Throw
//
//  N.isHoliday(d);                   O[log(N)]           No_Throw
//  N.isBusinessDay(d);               O[log(N)]           No_Throw
//  N.isNonBusinessDay(d);            O[log(N)]           No_Throw
//
//  N.numHolidayCodes(d)              O[log(N)]           No-Throw
//
//  N.numBusinessDays()               O[N]                No-Throw
//  N.numNonBusinessDays()            O[N]                No-Throw
//
//  other 'const' methods             O[1] .. O[N]        No-Throw
//
//
//  OP==(N,M)                         O[min(N,M)+min(W+V) No-Throw
//  OP!=(N,M)                         O[min(N,M)+min(W+V) No-Throw
//
//                          <*> No-Throw guarantee when capacity is sufficient.
//..
// Note that *all* of the non-creator methods of 'bdlt::PackedCalendar' provide
// the *No-Throw* guarantee whenever sufficient capacity is already available.
// Also note that these are largely the same as 'bdlt::Calendar' *except* that
// the accessors 'isBusinessDay' and 'isNonBusinessDay' are logarithmic in the
// number of holidays in 'bdlt::PackedCalendar'.
//
///Usage
///-----
// The two subsections below illustrate various aspects of populating and using
// packed calendars.
//
///Example 1: Populating Packed Calendars
/// - - - - - - - - - - - - - - - - - - -
// Packed calendars will typically be populated from a database or flat file.
// The user should employ an appropriate population mechanism that provides
// the desired holiday dates and associated holiday codes within some desired
// range.  For example, suppose we have created the following flat-file format
// that encodes calendar information, including holidays and holiday codes (we
// assume, for the simplicity of this example, that "Weekend Days" (i.e.,
// recurring non-business days) are always just Saturdays and Sundays):
//..
//  // HOLIDAY DATE   HOLIDAY CODES
//  // ------------   -------------
//  // Year Mon Day    #    Codes     Comments, separated by Semicolons (;)
//  // ---- --- ---   --- ---------   -------------------------------------
//     2010  1  18     1   57         ;Martin Luther King, Jr. Day
//     2010  2  15     1   51         ;Presidents Day
//     2010  4   2     2   9 105      ;Easter Sunday (Observed); Good Friday
//     2010  5  31     1   16         ;Memorial Day
//     2010  7   5     1   28         ;Independence Day (Observed)
//     2010  9   6     1   44         ;Labor Day
//     2010 10  11     1   19         ;Columbus Day
//     2010 11   2     0              ;Election Day
//     2010 11  25     1   14         ;Thanksgiving Day
//     2010 12  25     1    4         ;Christmas Day (Observed)
//     2010 12  31     1   22         ;New Year's Day (Observed)
//..
// Let's now create a couple of primitive helper functions to extract holiday
// and holiday-code counts from a given input stream.
//
// First, we'll create a helper function to get a holiday record:
//..
//  int getNextHoliday(bsl::istream& input, bdlt::Date *holiday, int *numCodes)
//      // Load into the specified 'holiday' the date of the next holiday, and
//      // into the specified 'numCodes' the associated number of holiday codes
//      // for the holiday read from the specified 'input' stream.  Return 0 on
//      // success, and a non-zero value (with no effect on '*holiday' and
//      // '*numCodes') otherwise.
//  {
//      enum { SUCCESS = 0, FAILURE = 1 };
//
//      int year, month, day, codes;
//
//      if (input.good()) {
//          input >> year;
//      }
//      if (input.good()) {
//          input >> month;
//      }
//      if (input.good()) {
//          input >> day;
//      }
//      if (input.good()) {
//          input >> codes;
//      }
//
//      if (input.good()
//       && bdlt::Date::isValidYearMonthDay(year, month, day)) {
//          *holiday  = bdlt::Date(year, month, day);
//          *numCodes = codes;
//          return SUCCESS;                                           // RETURN
//      }
//
//      return FAILURE;                                               // RETURN
//  }
//..
// Then, we'll write a function that gets us an integer holiday code, or
// invalidates the stream if it cannot (note that negative holiday codes are
// not supported by this function, but negative holiday codes *are* supported
// by 'bdlt::PackedCalendar'):
//..
//  void getNextHolidayCode(bsl::istream& input, int *result)
//      // Load, into the specified 'result', the value read from the specified
//      // 'input' stream.  If the next token is not an integer, invalidate the
//      // stream with no effect on 'result'.
//  {
//      int holidayCode;
//
//      if (input.good()) {
//          input >> holidayCode;
//      }
//
//      if (input.good()) {
//          *result = holidayCode;
//      }
//  }
//..
// Now, with these helper functions, it is a simple matter to write a calendar
// loader function, 'load', that populates a given calendar with data in this
// "proprietary" format:
//..
//  void load(bsl::istream& input, bdlt::PackedCalendar *calendar)
//      // Populate the specified 'calendar' with holidays and corresponding
//      // codes read from the specified 'input' stream in our "proprietary"
//      // format (see above).  On success, 'input' will be empty, but valid;
//      // otherwise 'input' will be invalid.
//  {
//      bdlt::Date holiday;
//      int        numCodes;
//
//      while (0 == getNextHoliday(input, &holiday, &numCodes)) {
//          calendar->addHoliday(holiday);                       // add date
//          for (int i = 0; i < numCodes; ++i) {
//              int holidayCode;
//              getNextHolidayCode(input, &holidayCode);
//              if (input.good()) {
//                  // add codes
//
//                  calendar->addHolidayCode(holiday, holidayCode);
//              }
//          }
//          input.ignore(256, '\n');  // skip comments
//      }
//  }
//..
// Finally, we load a 'bdlt::PackedCalendar' and verify some values from the
// calendar.
//..
//  bsl::stringstream stream;
//  {
//      stream << "2010  9   6     1   44         ;Labor Day\n"
//             << "2010 10  11     1   19         ;Columbus Day\n"
//             << "2010 11   2     0              ;Election Day\n"
//             << "2010 11  25     1   14         ;Thanksgiving Day\n";
//  }
//
//  bdlt::PackedCalendar calendar;
//  load(stream, &calendar);
//
//  assert(bdlt::Date(2010,  9,  6) == calendar.firstDate());
//  assert(bdlt::Date(2010, 11, 25) == calendar.lastDate());
//  assert(true  == calendar.isBusinessDay(bdlt::Date(2010, 10, 12)));
//  assert(false == calendar.isBusinessDay(bdlt::Date(2010, 11,  2)));
//..
// Note that different formats can easily be accommodated, while still using
// the same basic population strategy.  Also note that it may be substantially
// more efficient to populate calendars in increasing date order, compared to
// either reverse or random order.
//
///Example 2: Using Packed Calendars
///- - - - - - - - - - - - - - - - -
// Higher-level clients (e.g., a GUI) may need to extract the holiday codes
// for a particular date, use them to look up their corresponding string names
// in a separate repository (e.g., a vector of strings), and to display these
// names to end users.
//
// First, let's create a function that prints the names of holidays for a
// given date:
//..
//  void
//  printHolidayNamesForGivenDate(bsl::ostream&                   output,
//                                const bdlt::PackedCalendar&     calendar,
//                                const bdlt::Date&               date,
//                                const bsl::vector<bsl::string>& holidayNames)
//      // Write, to the specified 'output' stream, the elements in the
//      // specified 'holidayNames' associated, via holiday codes in the
//      // specified 'calendar', to the specified 'date'.  Each holiday name
//      // emitted is followed by a newline ('\n').  The behavior is undefined
//      // unless 'date' is within the valid range of 'calendar'.
//  {
//      for (bdlt::PackedCalendar::HolidayCodeConstIterator
//                                       it = calendar.beginHolidayCodes(date);
//                                       it != calendar.endHolidayCodes(date);
//                                     ++it) {
//          output << holidayNames[*it] << bsl::endl;
//      }
//  }
//..
// Then, since we can write the names of holidays for a given date, let's
// write a function that can write out all of the names associated with each
// holiday in the calendar:
//..
//  void
//  printHolidayDatesAndNames(bsl::ostream&                   output,
//                            const bdlt::PackedCalendar&     calendar,
//                            const bsl::vector<bsl::string>& holidayNames)
//      // Write, to the specified 'output' stream, each date associated with
//      // a holiday in the specified 'calendar' followed by any elements in
//      // the specified 'holidayNames' (associated via holiday codes in
//      // 'calendar') corresponding to that date.  Each date emitted is
//      // preceded and followed by a newline ('\n').  Each holiday name
//      // emitted is followed by a newline ('\n').
//  {
//      for (bdlt::PackedCalendar::HolidayConstIterator
//                        it = calendar.beginHolidays();
//                                      it != calendar.endHolidays(); ++it) {
//          output << '\n' << *it << '\n';
//          printHolidayNamesForGivenDate(output,
//                                        calendar,
//                                        *it,
//                                        holidayNames);
//      }
//  }
//..
// Next, we populate the 'holidayNames' vector:
//..
//  bsl::vector<bsl::string> holidayNames;
//  {
//      holidayNames.resize(45);
//
//      holidayNames[44] = "Labor Day";         // holiday code 44 is for
//                                              // Labor Day
//
//      holidayNames[14] = "Thanksgiving Day";  // holiday code 14 is for
//                                              // Thanksgiving Day
//  }
//..
// Now, using the 'calendar' populated in the previous example, we print the
// holiday information to a new 'bsl::stringstream':
//..
//  bsl::stringstream printStream;
//
//  printHolidayDatesAndNames(printStream, calendar, holidayNames);
//..
// Finally, we verify the output:
//..
//  assert(printStream.str() == "\n06SEP2010\nLabor Day\n\n11OCT2010\n\n\n"
//                               "02NOV2010\n\n25NOV2010\nThanksgiving Day\n");
//..

#include <bdlscm_version.h>

#include <bdlt_calendarreverseiteratoradapter.h>
#include <bdlt_date.h>
#include <bdlt_dayofweek.h>
#include <bdlt_dayofweekset.h>

#include <bdlc_packedintarray.h>
#include <bdlc_packedintarrayutil.h>

#include <bslalg_swaputil.h>

#include <bslh_hash.h>

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

#include <bslmf_integralconstant.h>

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

#include <bsl_cstddef.h>
#include <bsl_iosfwd.h>
#include <bsl_iterator.h>
#include <bsl_utility.h>      // 'bsl::pair'
#include <bsl_vector.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>

#include <bsl_algorithm.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bdlt {

class PackedCalendar_BusinessDayConstIterator;
class PackedCalendar_HolidayCodeConstIterator;
class PackedCalendar_HolidayConstIterator;

                          // ====================
                          // class PackedCalendar
                          // ====================

class PackedCalendar {
    // This class implements a space-efficient, value-semantic repository of
    // weekend and holiday information over a *valid* *range* of dates.  This
    // valid range, '[firstDate() .. lastDate()]', spans the first and last
    // dates of a calendar's accessible contents.  A calendar can be
    // "populated" with weekend and holiday information via a suite of "add"
    // methods.  Any subset of days of the week may be specified as weekend
    // (i.e., recurring non-business) days starting from a specified date by
    // adding a weekend-days transition; holidays within the valid range are
    // specified individually.  When adding a holiday, an arbitrary integer
    // "holiday code" may be associated with that date.  Additional holiday
    // codes for that date may subsequently be added.  Both the holidays and
    // the set of unique holiday codes associated with each holiday date are
    // maintained (internally) in order of increasing value.  Note that the
    // behavior of requesting *any* calendar information for a supplied date
    // whose value is outside the current *valid* *range* for that calendar
    // (unless otherwise noted, e.g., 'isWeekendDay') is undefined.

  private:
    // PRIVATE TYPES
    typedef bsl::pair<Date, DayOfWeekSet> WeekendDaysTransitionPrivate;

    typedef bdlc::PackedIntArray<int>::const_iterator OffsetsConstIterator;
    typedef bdlc::PackedIntArray<int>::const_iterator CodesIndexConstIterator;
    typedef bdlc::PackedIntArray<int>::const_iterator CodesConstIterator;

    typedef bsl::vector<WeekendDaysTransitionPrivate>
                                                 WeekendDaysTransitionSequence;

    struct WeekendDaysTransitionLess {
        // This 'struct' provides a comparator predicate for the type
        // 'WeekendDaysTransition' to enable the use of standard algorithms
        // (such as 'bsl::lower_bound') on ranges of objects of that type.

        // ACCESSORS
        bool operator()(const WeekendDaysTransitionPrivate& lhs,
                        const WeekendDaysTransitionPrivate& rhs) const
            // Return 'true' if the value of the specified 'lhs' is less than
            // (ordered before) the value of the specified 'rhs', and 'false'
            // otherwise.  The value of 'lhs' is less than the value of 'rhs'
            // if the date represented by the data member 'first' of 'lhs' is
            // earlier than the date represented by the data member 'first' of
            // 'rhs'.
        {
            return lhs.first < rhs.first;
        }
    };

  public:
    // TYPES
    typedef WeekendDaysTransitionPrivate WeekendDaysTransition;

    typedef PackedCalendar_BusinessDayConstIterator   BusinessDayConstIterator;

    typedef PackedCalendar_HolidayConstIterator       HolidayConstIterator;

    typedef PackedCalendar_HolidayCodeConstIterator   HolidayCodeConstIterator;

    typedef bdlt::CalendarReverseIteratorAdapter<BusinessDayConstIterator>
                                               BusinessDayConstReverseIterator;

    typedef bdlt::CalendarReverseIteratorAdapter<HolidayConstIterator>
                                                   HolidayConstReverseIterator;

    typedef bdlt::CalendarReverseIteratorAdapter<HolidayCodeConstIterator>
                                               HolidayCodeConstReverseIterator;

    typedef WeekendDaysTransitionSequence::const_iterator
                                            WeekendDaysTransitionConstIterator;

    typedef
    bsl::reverse_iterator<WeekendDaysTransitionSequence::const_iterator>
                                     WeekendDaysTransitionConstReverseIterator;

  private:
    // DATA
    Date                       d_firstDate;  // first valid date of calendar or
                                             // 9999/12/31 if this calendar is
                                             // empty

    Date                       d_lastDate;   // last valid date of calendar or
                                             // 0001/01/01 if this calendar is
                                             // empty

    bsl::vector<WeekendDaysTransition>
                               d_weekendDaysTransitions;
                                             // chronological list of weekend-
                                             // days transitions

    bdlc::PackedIntArray<int>  d_holidayOffsets;
                                             // ordered list of all holidays in
                                             // this calendar stored as offsets
                                             // from 'd_firstDate'

    bdlc::PackedIntArray<int>  d_holidayCodesIndex;
                                             // parallel to 'd_holidayOffsets',
                                             // this is a list of indices into
                                             // 'd_holidayCodes'; note that the
                                             // end of each sequence can be
                                             // determined using the value of
                                             // the next entry in this array if
                                             // it exists, or else the length
                                             // of 'd_holidayCodes' itself

    bdlc::PackedIntArray<int>  d_holidayCodes;
                                             // sequences of holiday codes,
                                             // each partitioned into an
                                             // ordered "chunk" of codes per
                                             // holiday in 'd_holidayOffsets';
                                             // chunks are stored in the same
                                             // order as in 'd_holidayOffsets'

    bslma::Allocator          *d_allocator_p;
                                             // memory allocator (held, not
                                             // owned)

    // FRIENDS
    friend class PackedCalendar_BusinessDayConstIterator;

    friend bool operator==(const PackedCalendar&, const PackedCalendar&);
    friend bool operator!=(const PackedCalendar&, const PackedCalendar&);
    template <class HASHALG>
    friend void hashAppend(HASHALG& hashAlg, const PackedCalendar&);

  private:
    // PRIVATE CLASS METHODS
    static void intersectHolidays(
                               bdlc::PackedIntArray<int> *resHolidayOffsets,
                               bdlc::PackedIntArray<int> *resHolidayCodesIndex,
                               bdlc::PackedIntArray<int> *resHolidayCodes,
                               const PackedCalendar&      lhs,
                               const PackedCalendar&      rhs,
                               const bdlt::Date           firstDate,
                               const bdlt::Date           lastDate);
        // Append, onto the specified 'resHolidayOffsets',
        // 'resHolidayCodesIndex', and 'resHolidayCodes', the intersection of
        // the holidays from the specified 'lhs' and 'rhs' calendars
        // restricted to the date range of the specified 'firstDate' and
        // 'lastDate'.  The behavior is undefined unless
        // 'firstDate <= lastDate'.

    static void unionHolidays(bdlc::PackedIntArray<int> *resHolidayOffsets,
                              bdlc::PackedIntArray<int> *resHolidayCodesIndex,
                              bdlc::PackedIntArray<int> *resHolidayCodes,
                              const PackedCalendar&      lhs,
                              const PackedCalendar&      rhs,
                              const bdlt::Date           firstDate,
                              const bdlt::Date           lastDate);
        // Append, onto the specified 'resHolidayOffsets',
        // 'resHolidayCodesIndex', and 'resHolidayCodes', the union of the
        // holidays from the specified 'lhs' and 'rhs' calendars restricted to
        // the date range of the specified 'firstDate' and 'lastDate'.  The
        // behavior is undefined unless 'firstDate <= lastDate'.

    // PRIVATE MANIPULATORS
    int addHolidayImp(int offset);
        // Add the specified 'offset' as a holiday offset in this calendar.  If
        // the date represented by 'offset' is already a holiday, this method
        // has no effect.  Return the index of 'd_holidayOffsets' where
        // 'offset' is stored.  The behavior is undefined unless '0 <= offset'
        // and the 'offset' represents a date within the valid range of the
        // calendar.

    // PRIVATE ACCESSORS
    CodesConstIterator beginHolidayCodes(
                                       const OffsetsConstIterator& iter) const;
        // Return an iterator that refers to the first non-modifiable holiday
        // code for the holiday referenced by the specified 'iter'.  If there
        // are no holiday codes associated with the date referenced by 'iter',
        // the returned iterator has the same value as that returned by
        // 'endHolidayCodes(iter)'.  The behavior is undefined unless 'iter'
        // refers to a valid holiday of this calendar.

    CodesConstIterator endHolidayCodes(const OffsetsConstIterator& iter) const;
        // Return an iterator that indicates the element one past the last
        // holiday code associated with the date referenced by the specified
        // 'iter'.  The behavior is undefined unless 'iter' references a valid
        // holiday in this calendar.

  public:
    // CLASS METHODS

                                  // Aspects

    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 PackedCalendar(bslma::Allocator *basicAllocator = 0);
        // Create an empty calendar, i.e., a calendar having an empty valid
        // range.  Optionally specify a 'basicAllocator' used to supply memory.
        // If 'basicAllocator' is 0, the currently installed default allocator
        // is used.

    PackedCalendar(const Date&       firstDate,
                   const Date&       lastDate,
                   bslma::Allocator *basicAllocator = 0);
        // Create a calendar having a valid range from the specified
        // 'firstDate' through the specified 'lastDate'.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless 'firstDate <= lastDate'.

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

    ~PackedCalendar();
        // Destroy this object.

    // MANIPULATORS
    PackedCalendar& operator=(const PackedCalendar& rhs);
        // Assign to this calendar the value of the specified 'rhs' calendar,
        // and return a reference providing modifiable access to this calendar.
        // See {Iterator Invalidation} for information regarding which
        // iterators are affected by the use of this method.

    void addDay(const Date& date);
        // Extend the valid range (if necessary) of this calendar to include
        // the specified 'date' value.

    void addHoliday(const Date& date);
        // Mark the specified 'date' as a holiday (i.e., a non-business day)
        // in this calendar.  Extend the valid range of this calendar if
        // necessary.  If 'date' is already marked as a holiday, this method
        // has no effect.  See {Iterator Invalidation} for information
        // regarding which iterators are affected by the use of this method.

    void addHolidayCode(const Date& date, int holidayCode);
        // Mark the specified 'date' as a holiday (i.e., a non-business day) in
        // this calendar and add the specified 'holidayCode' (if not already
        // present) to the ordered set of codes associated with 'date'.
        // Extend the valid range of this calendar if necessary.  If
        // 'holidayCode' is already a code for 'date', this method has no
        // effect.  See {Iterator Invalidation} for information regarding
        // which iterators are affected by the use of this method.

    int addHolidayCodeIfInRange(const Date& date, int holidayCode);
        // Mark the specified 'date' as a holiday (i.e., a non-business day)
        // in this calendar and add the specified 'holidayCode' (if not
        // already present) to the set of codes associated with 'date', if
        // 'date' is within the valid range of this calendar.  Return 0 if
        // 'date' is in range, and a non-zero value otherwise.  If
        // 'holidayCode' is already a code for 'date', or if 'date' is not in
        // the valid range, this method has no effect.  See
        // {Iterator Invalidation} for information regarding which iterators
        // are affected by the use of this method.  Note that this method may
        // be called repeatedly with the same value for 'date' to build up a
        // set of holiday codes for that date.

    int addHolidayIfInRange(const Date& date);
        // Mark the specified 'date' as a holiday (i.e., a non-business day) in
        // this calendar if 'date' is within the valid range.  Return 0 if
        // 'date' is in range, and a non-zero value otherwise.  This method has
        // no effect if 'date' is already marked as a holiday or is not in the
        // valid range.  See {Iterator Invalidation} for information regarding
        // which iterators are affected by the use of this method.

    void addWeekendDay(DayOfWeek::Enum weekendDay);
        // Add the specified 'weekendDay' to the set of weekend days associated
        // with the weekend-days transition at January 1, 0001 maintained by
        // this calendar.  Create a transition at January 1, 0001 if one does
        // not exist.  See {Iterator Invalidation} for information regarding
        // which iterators are affected by the use of this method.  The
        // behavior is undefined unless no weekend-days transitions were added
        // to this calendar via the 'addWeekendDaysTransition' method.

    void addWeekendDays(const DayOfWeekSet& weekendDays);
        // Add the specified 'weekendDays' to the set of weekend days
        // associated with the weekend-days transition at January 1, 0001
        // maintained by this calendar.  Create a transition at January 1, 0001
        // if one does not exist.  See {Iterator Invalidation} for information
        // regarding which iterators are affected by the use of this method.
        // The behavior is undefined unless no weekend-days transitions were
        // added to this calendar via the 'addWeekendDaysTransition' method.

    void addWeekendDaysTransition(const Date&         startDate,
                                  const DayOfWeekSet& weekendDays);
        // Add to this calendar a weekend-days transition on the specified
        // 'startDate' having the specified 'weekendDays' set.  If a
        // weekend-days transition already exists on 'startDate', replace the
        // set of weekend days of that transition with 'weekendDays'.  See
        // {Iterator Invalidation} for information regarding which iterators
        // are affected by the use of this method.  Note that this method does
        // not extend the valid range of the calendar.

    void intersectBusinessDays(const PackedCalendar& other);
        // Merge the specified 'other' calendar into this calendar such that
        // the valid range of this calendar becomes the *intersection* of the
        // two calendars' ranges, and the weekend days and holidays for this
        // calendar become the union of those (non-business) days from the two
        // calendars -- i.e., the valid business days of this calendar become
        // the intersection of those of the two original calendar values over
        // the *intersection* of their ranges.  For each holiday that remains,
        // the resulting holiday codes in this calendar will be the union of
        // the corresponding original holiday codes.  See
        // {Iterator Invalidation} for information regarding which iterators
        // are affected by the use of this method.

    void intersectNonBusinessDays(const PackedCalendar& other);
        // Merge the specified 'other' calendar into this calendar such that
        // the valid range of this calendar becomes the *intersection* of the
        // two calendars' ranges, the weekend days for this calendar become the
        // intersection of those days from the two calendars, and the holidays
        // for this calendar become the set of days that are a holiday in one
        // of the calendars and a non-business day in the other calendar --
        // i.e., the valid business days of this calendar become the union of
        // those of the two original calendars over the *intersection* of their
        // ranges.  For each holiday that remains, the resulting holiday codes
        // in this calendar will be the union of the corresponding original
        // holiday codes.  See {Iterator Invalidation} for information
        // regarding which iterators are affected by the use of this method.

    void removeAll();
        // Remove all information from this calendar, leaving it with its
        // default constructed "empty" value.  See {Iterator Invalidation} for
        // information regarding which iterators are affected by the use of
        // this method.

    void removeHoliday(const Date& date);
        // Remove from this calendar the holiday having the specified 'date' if
        // such a holiday exists.  This operation has no effect if 'date' is
        // not a holiday in this calendar.  See {Iterator Invalidation} for
        // information regarding which iterators are affected by the use of
        // this method.  Note that this method is defined for all 'date'
        // values, not just those that fall within the valid range, and may be
        // invoked even on an empty calendar (i.e., having '0 == length()').

    void removeHolidayCode(const Date& date, int holidayCode);
        // Remove from this calendar the specified 'holidayCode' for the
        // holiday having the specified 'date' if such a holiday having
        // 'holidayCode' exists.  This operation has no effect if 'date' is not
        // a holiday in this calendar, or if the holiday at 'date' does not
        // have 'holidayCode' associated with it.  See {Iterator Invalidation}
        // for information regarding which iterators are affected by the use of
        // this method.  Note that this method is defined for all 'date'
        // values, not just those that fall within the valid range, and may be
        // invoked even on an empty calendar (i.e., having '0 == length()').

    void reserveHolidayCapacity(int numHolidays);
        // Reserve enough space to store at least the specified 'numHolidays'
        // within this calendar.  This method has no effect if
        // 'numHolidays <= numHolidays()'.

    void reserveHolidayCodeCapacity(int numHolidayCodes);
        // Reserve enough space to store at least the specified
        // 'numHolidayCodes' within this calendar assuming no additional
        // holidays are added to this calendar.  This method has no effect if
        // 'numHolidayCodes <= numHolidayCodesTotal()'.

    void setValidRange(const Date& firstDate, const Date& lastDate);
        // Set the range of this calendar using the specified 'firstDate' and
        // 'lastDate' as, respectively, the first date and the last date of the
        // calendar.  Any holiday that is outside the new range and its holiday
        // codes are removed.  See {Iterator Invalidation} for information
        // regarding which iterators are affected by the use of this method.
        // The behavior is undefined unless 'firstDate <= lastDate'.

    void unionBusinessDays(const PackedCalendar& other);
        // Merge the specified 'other' calendar into this calendar such that
        // the valid range of this calendar becomes the *union* of the two
        // calendars' ranges (or the minimal continuous range spanning the two
        // ranges, if the ranges are non-overlapping), the weekend days for
        // this calendar become the intersection of those days from the two
        // calendars, and the holidays for this calendar become the set of days
        // that are a holiday in one of the calendars and a non-business day in
        // the other calendar -- i.e., the valid business days of this calendar
        // become the union of those of the two original calendar values.  For
        // each holiday that remains, the resulting holiday codes in this
        // calendar will be the union of the corresponding original holiday
        // codes.  See {Iterator Invalidation} for information regarding which
        // iterators are affected by the use of this method.

    void unionNonBusinessDays(const PackedCalendar& other);
        // Merge the specified 'other' calendar into this calendar such that
        // the valid range of this calendar becomes the *union* of the two
        // calendars' ranges (or the minimal continuous range spanning the two
        // ranges, if the ranges are non-overlapping), and the weekend days
        // and holidays for this calendar become the union of those
        // (non-business) days from the two calendars -- i.e., the valid
        // business days of this calendar become the intersection of those of
        // the two calendars after each range is extended to cover the
        // resulting one.  For each holiday in either calendar, the resulting
        // holiday codes in this calendar will be the union of the
        // corresponding original holiday codes.  See {Iterator Invalidation}
        // for information regarding which iterators are affected by the use of
        // this method.

                                  // Aspects

    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 the modifiable 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'stream' becomes invalid during this
        // operation or if 'version' is not supported, this object is
        // unaltered.  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(PackedCalendar& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    BusinessDayConstIterator beginBusinessDays() const;
        // Return an iterator providing non-modifiable access to the first
        // business day in this calendar.  If this calendar has no valid
        // business days, the returned iterator has the same value as that
        // returned by 'endBusinessDays()'.

    BusinessDayConstIterator beginBusinessDays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // business day that occurs on or after the specified 'date' in this
        // calendar.  If this calendar has no such business day, the returned
        // iterator has the same value as that returned by
        // 'endBusinessDays(date)'.  The behavior is undefined unless 'date' is
        // within the valid range of this calendar.

    HolidayCodeConstIterator beginHolidayCodes(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday code for the specified 'date' in this calendar.  If there is
        // no holiday code associated with 'date', the returned iterator has
        // the same value as that returned by 'endHolidayCodes(date)'.  The
        // behavior is undefined unless 'date' is marked as a holiday in this
        // calendar.

    HolidayCodeConstIterator beginHolidayCodes(
                                       const HolidayConstIterator& iter) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday code for the holiday referenced by the specified 'iter'.  If
        // there is no holiday code associated with the date referenced by
        // 'iter', the returned iterator has the same value as that returned by
        // 'endHolidayCodes(iter)'.  The behavior is undefined unless 'iter'
        // refers to a valid holiday of this calendar.

    HolidayConstIterator beginHolidays() const;
        // Return an iterator providing non-modifiable access to the first
        // holiday in this calendar.  If this calendar has no holidays, the
        // returned iterator has the same value as that returned by
        // 'endHolidays()'.

    HolidayConstIterator beginHolidays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday that occurs on or after the specified 'date' in this
        // calendar.  If this calendar has no such holiday, the returned
        // iterator has the same value as that returned by 'endHolidays(date)'.
        // The behavior is undefined unless 'date' is within the valid range
        // of this calendar.

    WeekendDaysTransitionConstIterator beginWeekendDaysTransitions() const;
        // Return an iterator providing non-modifiable access to the first
        // weekend-days transition in the chronological sequence of
        // weekend-days transitions maintained by this calendar.  If this
        // calendar has no weekend-days transitions, the returned iterator has
        // the same value as that returned by 'endWeekendDaysTransitions()'.

    BusinessDayConstIterator endBusinessDays() const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end business day in this calendar.

    BusinessDayConstIterator endBusinessDays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // business day that occurs after the specified 'date' in this
        // calendar.  If this calendar has no such business day, the returned
        // iterator has the same value as that returned by
        // 'endBusinessDays()'.  The behavior is undefined unless 'date' is
        // within the valid range of this calendar.

    HolidayCodeConstIterator endHolidayCodes(const Date& date) const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end holiday code associated with the specified 'date'.  The
        // behavior is undefined unless 'date' is marked as a holiday in this
        // calendar.

    HolidayCodeConstIterator
                       endHolidayCodes(const HolidayConstIterator& iter) const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end holiday code associated with the date referenced by
        // the specified 'iter'.  The behavior is undefined unless 'iter'
        // references a valid holiday in this calendar.

    HolidayConstIterator endHolidays() const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end holiday in the chronological sequence of holidays
        // maintained by this calendar.

    HolidayConstIterator endHolidays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday that occurs after the specified 'date' in this calendar.
        // If this calendar has no such holiday, the returned iterator has the
        // same value as that returned by 'endHolidays()'.  The behavior is
        // undefined unless 'date' is within the valid range of this calendar.

    WeekendDaysTransitionConstIterator endWeekendDaysTransitions() const;
        // Return an iterator providing non-modifiable access to the
        // past-the-end weekend-days transition in the chronological sequence
        // of weekend-days transitions maintained by this calendar.

    const Date& firstDate() const;
        // Return a reference providing non-modifiable access to the earliest
        // date in the valid range of this calendar.  The behavior is
        // undefined unless this calendar is non-empty -- i.e., unless
        // '1 <= length()'.

    int getNextBusinessDay(Date *nextBusinessDay, const Date& date) const;
        // Load, into the specified 'nextBusinessDay', the date of the first
        // business day in this calendar following the specified 'date'.
        // Return 0 on success -- i.e., if such a business day exists, and a
        // non-zero value (with no effect on 'nextBusinessDay') otherwise.  The
        // behavior is undefined unless 'date + 1' is both a valid 'bdlt::Date'
        // and within the valid range of this calendar.

    int getNextBusinessDay(Date        *nextBusinessDay,
                           const Date&  date,
                           int          nth) const;
        // Load, into the specified 'nextBusinessDay', the date of the
        // specified 'nth' business day in this calendar following the
        // specified 'date'.  Return 0 on success -- i.e., if such a business
        // day exists, and a non-zero value (with no effect on
        // 'nextBusinessDay') otherwise.  The behavior is undefined unless
        // 'date + 1' is both a valid 'bdlt::Date' and within the valid range
        // of this calendar, and '0 < nth'.

    bdlt::Date holiday(int index) const;
        // Return the holiday at the specified 'index' in this calendar.  For
        // all 'index' values from 0 to 'numHolidays() - 1' (inclusive), a
        // unique holiday is returned.  The mapping of 'index' to holiday is
        // invalidated when the set of holidays is modified by an invocation of
        // 'addHoliday', 'addHolidayIfInRange', 'intersectBusinessDays',
        // 'intersectNonBusinessDays', 'removeAll', 'removeHoliday',
        // 'setValidRange', 'unionBusinessDays', or 'unionNonBusinessDays'.
        // The behavior is undefined unless '0 <= index < numHolidays()'.

    int holidayCode(const Date& date, int index) const;
        // Return, for the holiday at the specified 'date' in this calendar,
        // the holiday code at the specified 'index'.  For all 'index' values
        // from 0 to 'numHolidayCodes(date) - 1' (inclusive), a unique holiday
        // code is returned.  The mapping of 'index' to holiday code is
        // invalidated when the set of holidays or holiday codes is modified by
        // an invocation of 'addHoliday', 'addHolidayCode',
        // 'addHolidayCodeIfInRange', 'addHolidayIfInRange',
        // 'intersectBusinessDays', 'intersectNonBusinessDays', 'removeAll',
        // 'removeHoliday', 'removeHolidayCode', 'setValidRange',
        // 'unionBusinessDays', or 'unionNonBusinessDays'.  The behavior is
        // undefined unless 'date' is a holiday in this calendar and
        // '0 <= index < numHolidayCodes(date)'.  Note that this method
        // facilitates testing and generally should not be used by clients; in
        // particular, using this method to iterate over the holiday codes for
        // 'date' is less efficient than using a 'HolidayCodeConstIterator'.

    bool isBusinessDay(const Date& date) const;
        // Return 'true' if the specified 'date' is a business day (i.e., not
        // a holiday or weekend day) in this calendar, and 'false' otherwise.
        // The behavior is undefined unless 'date' is within the valid range of
        // this calendar.

    bool isHoliday(const Date& date) const;
        // Return 'true' if the specified 'date' is a holiday in this calendar,
        // and 'false' otherwise.  The behavior is undefined unless 'date' is
        // within the valid range of this calendar.

    bool isInRange(const Date& date) const;
        // Return 'true' if the specified 'date' is within the valid range of
        // this calendar (i.e., 'firstDate() <= date <= lastDate()'), and
        // 'false' otherwise.  Note that the valid range for a
        // 'PackedCalendar' is empty if its length is 0.

    bool isNonBusinessDay(const Date& date) const;
        // Return 'true' if the specified 'date' is not a business day (i.e.,
        // is either a holiday or weekend day) in this calendar, and 'false'
        // otherwise.  The behavior is undefined unless 'date' is within the
        // valid range of this calendar.  Note that:
        //..
        //  !isBusinessDay(date)
        //..
        // returns the same result.

    bool isWeekendDay(const Date& date) const;
        // Return 'true' if the specified 'date' falls on a day of the week
        // that is considered a weekend day in this calendar, and 'false'
        // otherwise.  Note that this method is defined for all 'date' values,
        // not just those that fall within the valid range, and may be invoked
        // even on an empty calendar (i.e., having '0 == length()').

    bool isWeekendDay(DayOfWeek::Enum dayOfWeek) const;
        // Return 'true' if the specified 'dayOfWeek' is a weekend day in this
        // calendar, and 'false' otherwise.  The behavior is undefined unless
        // no weekend-days transitions were added to this calendar via the
        // 'addWeekendDaysTransition' method.

    const Date& lastDate() const;
        // Return a reference providing non-modifiable access to the latest
        // date in the valid range of this calendar.  The behavior is
        // undefined unless this calendar is non-empty -- i.e., unless
        // '1 <= length()'.

    int length() const;
        // Return the number of days in the valid range of this calendar,
        // which is defined to be 0 if this calendar is empty, and
        // 'lastDate() - firstDate() + 1' otherwise.

    int numBusinessDays() const;
        // Return the number of days in the valid range of this calendar that
        // are considered business days -- i.e., are neither holidays nor
        // weekend days.  Note that
        // 'numBusinessDays() == length() - numNonBusinessDays()'.

    int numBusinessDays(const Date& beginDate, const Date& endDate) const;
        // Return the number of days in the specified range
        // '[beginDate .. endDate]' of this calendar that are considered
        // business days -- i.e., are neither holidays nor weekend days.  The
        // behavior is undefined unless 'beginDate' and 'endDate' are within
        // the valid range of this calendar, and 'beginDate <= endDate'.

    int numHolidayCodes(const Date& date) const;
        // Return the number of (unique) holiday codes associated with the
        // specified 'date' in this calendar if 'date' is a holiday in this
        // calendar, and 0 otherwise.  The behavior is undefined unless 'date'
        // is within the valid range of this calendar.

    int numHolidayCodesTotal() const;
        // Return the total number of holiday codes for all holidays in this
        // calendar.  Note that this function is used primarily in conjunction
        // with 'reserveHolidayCodeCapacity'.

    int numHolidays() const;
        // Return the number of days in the valid range of this calendar that
        // are individually marked as holidays, irrespective of whether or not
        // the date is also considered a weekend day.

    int numNonBusinessDays() const;
        // Return the number of days in the valid range of this calendar that
        // are *not* considered business days -- i.e., are either holidays,
        // weekend days, or both.  Note that
        // 'numNonBusinessDays() == length() - numBusinessDays()'.

    int numWeekendDaysInRange() const;
        // Return the number of days in the valid range of this calendar that
        // are considered weekend days, irrespective of any designated
        // holidays.

    int numWeekendDaysTransitions() const;
        // Return the number of weekend-days transitions maintained by this
        // calendar.

    BusinessDayConstReverseIterator rbeginBusinessDays() const;
        // Return an iterator providing non-modifiable access to the last
        // business day in this calendar.  If this calendar has no valid
        // business days, the returned iterator has the same value as that
        // returned by 'rendBusinessDays()'.

    BusinessDayConstReverseIterator rbeginBusinessDays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // business day that occurs on or before the specified 'date' in this
        // calendar.  If this calendar has no such business day, the returned
        // iterator has the same value as that returned by
        // 'rendBusinessDays(date)'.  The behavior is undefined unless 'date'
        // is within the valid range of this calendar.

    HolidayCodeConstReverseIterator rbeginHolidayCodes(const Date& date) const;
        // Return an iterator providing non-modifiable access to the last
        // holiday code associated with the specified 'date' in this calendar.
        // If there are no holiday codes associated with 'date', the returned
        // iterator has the same value as that returned by
        // 'rendHolidayCodes(date)'.  The behavior is undefined unless 'date'
        // is marked as a holiday in this calendar.

    HolidayCodeConstReverseIterator
                    rbeginHolidayCodes(const HolidayConstIterator& iter) const;
        // Return an iterator providing non-modifiable access to the last
        // holiday code associated with the holiday referenced by the specified
        // 'iter'.  If there are no holiday codes associated with the date
        // referenced by 'iter', the returned iterator has the same value as
        // that returned by 'rendHolidayCodes(iter)'.  The behavior is
        // undefined unless 'iter' refers to a valid holiday of this calendar.

    HolidayConstReverseIterator rbeginHolidays() const;
        // Return an iterator providing non-modifiable access to the last
        // holiday in this calendar.  If this calendar has no holidays, the
        // returned iterator has the same value as that returned by
        // 'rendHolidays()'.

    HolidayConstReverseIterator rbeginHolidays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday that occurs on or before the specified 'date' in this
        // calendar.  If this calendar has no such holiday, the returned
        // iterator has the same value as that returned by
        // 'rendHolidays(date)'.  The behavior is undefined unless 'date' is
        // within the valid range of this calendar.

    WeekendDaysTransitionConstReverseIterator
                                          rbeginWeekendDaysTransitions() const;
        // Return an iterator providing non-modifiable access to the last
        // weekend-days transition in the chronological sequence of
        // weekend-days transitions maintained by this calendar.  If this
        // calendar has no weekend-days transitions, the returned iterator has
        // the same value as that returned by 'rendWeekendDaysTransitions()'.

    BusinessDayConstReverseIterator rendBusinessDays() const;
        // Return an iterator providing non-modifiable access to the element
        // one before the first business day in this calendar.

    BusinessDayConstReverseIterator rendBusinessDays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // business day that occurs before the specified 'date' in this
        // calendar.  If this calendar has no such business day, the returned
        // iterator has the same value as that returned by
        // 'rendBusinessDays()'.  The behavior is undefined unless 'date' is
        // within the valid range of this calendar.

    HolidayCodeConstReverseIterator rendHolidayCodes(const Date& date) const;
        // Return an iterator providing non-modifiable access to the element
        // one before the first holiday code associated with the specified
        // 'date'.  The behavior is undefined unless 'date' is marked as a
        // holiday in this calendar.

    HolidayCodeConstReverseIterator
                      rendHolidayCodes(const HolidayConstIterator& iter) const;
        // Return an iterator providing non-modifiable access to the element
        // one before the first holiday code associated with the holiday
        // referenced by the specified 'iter'.  The behavior is undefined
        // unless 'iter' references a valid holiday in this calendar.

    HolidayConstReverseIterator rendHolidays() const;
        // Return an iterator providing non-modifiable access to the element
        // one before the first holiday in this calendar.

    HolidayConstReverseIterator rendHolidays(const Date& date) const;
        // Return an iterator providing non-modifiable access to the first
        // holiday that occurs before the specified 'date' in this calendar.
        // If this calendar has no such holiday, the returned iterator has the
        // same value as that returned by 'rendHolidays()'.  The behavior is
        // undefined unless 'date' is within the valid range of this calendar.

    WeekendDaysTransitionConstReverseIterator
                                            rendWeekendDaysTransitions() const;
        // Return an iterator providing non-modifiable access to the element
        // one before the first weekend-days transition in the chronological
        // sequence of weekend-days transitions maintained by this calendar.

    WeekendDaysTransition weekendDaysTransition(int index) const;
        // Return the weekend-days transition at the specified 'index' in this
        // calendar.  For all 'index' values from 0 to
        // 'numWeekendDaysTransitions() - 1' (inclusive), a unique weekend-days
        // transition is returned.  The mapping of 'index' to weekend-days
        // transition is invalidated when the set of weekend-days transitions
        // is modified by an invocation of 'addWeekendDay', 'addWeekendDays',
        // 'addWeekendDaysTransition', 'intersectBusinessDays',
        // 'intersectNonBusinessDays', 'removeAll', 'unionBusinessDays', or
        // 'unionNonBusinessDays'.  The behavior is undefined unless
        // '0 <= index < numWeekendDaysTransitions()'.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object 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 the modifiable
        // 'stream'.  If 'version' is not supported, 'stream' is 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.

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

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE2.22

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

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22
};

// FREE OPERATORS
bool operator==(const PackedCalendar& lhs, const PackedCalendar& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' calendars have the same
    // value, and 'false' otherwise.  Two calendars have the same value if they
    // have the same valid range (or are both empty), the same weekend days,
    // the same holidays, and each corresponding pair of holidays has the same
    // (ordered) set of associated holiday codes.

bool operator!=(const PackedCalendar& lhs, const PackedCalendar& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' calendars do not have the
    // same value, and 'false' otherwise.  Two calendars do not have the same
    // value if they do not have the same valid range (and are not both empty),
    // do not have the same weekend days, do not have the same holidays, or,
    // for at least one corresponding pair of holidays, do not have the same
    // (ordered) set of associated holiday codes.

bsl::ostream& operator<<(bsl::ostream&         stream,
                         const PackedCalendar& calendar);
    // Write the value of the specified 'calendar' to the specified output
    // 'stream', and return a reference to the modifiable 'stream'.

// FREE FUNCTIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const PackedCalendar& object);
    // Pass the specified 'object' to the specified 'hashAlg'.  This function
    // integrates with the 'bslh' modular hashing system and effectively
    // provides a 'bsl::hash' specialization for 'PackedCalendar'.

void swap(PackedCalendar& a, PackedCalendar& 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.

                       // ==============================
                       // class PackedCalendar_DateProxy
                       // ==============================

class PackedCalendar_DateProxy {
    // This class serves as a proxy for 'Date' for use by the arrow operator of
    // calendar iterators (e.g., 'PackedCalendar_HolidayConstIterator').  An
    // object of this class behaves as the 'Date' object with which it was
    // constructed.

    // DATA
    Date d_date;  // proxied date

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

  public:
    // CREATORS
    PackedCalendar_DateProxy(const Date& date);                     // IMPLICIT
        // Create a proxy object for the specified 'date'.

    ~PackedCalendar_DateProxy();
        // Destroy this object.

    PackedCalendar_DateProxy(const PackedCalendar_DateProxy& original);
        // Create a proxy object referencing the same 'Date' value as the
        // specified 'original' proxy.

    // ACCESSORS
    const Date *operator->() const;
        // Return the address providing non-modifiable access to the proxied
        // date object.
};

                      // ============================
                      // class PackedCalendar_DateRef
                      // ============================

class PackedCalendar_DateRef : public Date {
    // This private class is used by the arrow operator of the holiday
    // iterator and business day iterator classes.  The objects instantiated
    // from this class serve as references to 'Date' objects.

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

  public:
    // CREATORS
    explicit PackedCalendar_DateRef(const Date& date);
        // Create a date reference object using the specified 'date'.

    PackedCalendar_DateRef(const PackedCalendar_DateRef& original);
        // Create a date reference object having the value of the specified
        // 'original' object.

    ~PackedCalendar_DateRef();
        // Destroy this object.

    // ACCESSORS
    PackedCalendar_DateProxy operator&() const;
        // Return a proxy object to this object's referenced date.
};

                  // =========================================
                  // class PackedCalendar_HolidayConstIterator
                  // =========================================

class PackedCalendar_HolidayConstIterator {
    // Provide read-only, sequential access in increasing (chronological) order
    // to the holidays in a 'PackedCalendar' object.  This class owns an
    // iterator into the 'bdlc::PackedIntArray' storing the holiday offsets of
    // the referenced 'bdlt::PackedCalendar' and a date, 'd_firstDate', to
    // convert the offsets into dates corresponding to holidays.

    // DATA
    bdlc::PackedIntArray<int>::const_iterator d_iterator;   // array's iterator

    Date                                      d_firstDate;  // offset date

    // FRIENDS
    friend class PackedCalendar;
    friend bool operator==(const PackedCalendar_HolidayConstIterator&,
                           const PackedCalendar_HolidayConstIterator&);
    friend bool operator!=(const PackedCalendar_HolidayConstIterator&,
                           const PackedCalendar_HolidayConstIterator&);

  private:
    // PRIVATE TYPES
    typedef bdlc::PackedIntArray<int>::const_iterator OffsetsConstIterator;

    // PRIVATE CREATORS
    PackedCalendar_HolidayConstIterator(const OffsetsConstIterator& iter,
                                        const Date                  firstDate);
        // Create a holiday iterator using the specified 'iter' and
        // 'firstDate'.

  public:
    // TYPES
    typedef Date                     value_type;
    typedef int                      difference_type;
    typedef PackedCalendar_DateProxy pointer;
    typedef PackedCalendar_DateRef   reference;
        // The star operator returns a 'PackedCalendar_DateRef' *by* *value*.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE3.0

    typedef bsl::bidirectional_iterator_tag iterator_category;

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE3.0


    // CREATORS
    PackedCalendar_HolidayConstIterator(
                          const PackedCalendar_HolidayConstIterator& original);
        // Create an iterator having the value of the specified 'original'
        // iterator.

    ~PackedCalendar_HolidayConstIterator();
        // Destroy this object.

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

    PackedCalendar_HolidayConstIterator& operator++();
        // Advance this iterator to refer to the next holiday in the calendar,
        // and return a reference providing modifiable access to this object.
        // The behavior is undefined unless, on entry, this iterator
        // references a valid holiday.

    PackedCalendar_HolidayConstIterator& operator--();
        // Regress this iterator to refer to the previous holiday in the
        // calendar, and return a reference providing modifiable access to this
        // object.  The behavior is undefined unless, on entry, this iterator
        // references a valid holiday that is not the first holiday in the
        // associated calendar.

    // ACCESSORS
    PackedCalendar_DateRef operator*() const;
        // Return a 'PackedCalendar_DateRef' object that contains the date
        // value of the holiday referenced by this iterator.

    PackedCalendar_DateProxy operator->() const;
        // Return a date proxy for the holiday referenced by this iterator.
};

// FREE OPERATORS
bool operator==(const PackedCalendar_HolidayConstIterator& lhs,
                const PackedCalendar_HolidayConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators refer to the
    // same element in the same calendar, and 'false' otherwise.  The behavior
    // is undefined unless 'lhs' and 'rhs' both iterate over the same calendar.

bool operator!=(const PackedCalendar_HolidayConstIterator& lhs,
                const PackedCalendar_HolidayConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not refer to
    // the same element in the same calendar, and 'false' otherwise.  The
    // behavior is undefined unless 'lhs' and 'rhs' both iterate over the same
    // calendar.

PackedCalendar_HolidayConstIterator
                operator++(PackedCalendar_HolidayConstIterator& iterator, int);
    // Advance the specified 'iterator' to refer to the next holiday in the
    // associated calendar, and return the previous value of 'iterator'.  The
    // behavior is undefined unless, on entry, 'iterator' references a valid
    // holiday.

PackedCalendar_HolidayConstIterator
                operator--(PackedCalendar_HolidayConstIterator& iterator, int);
    // Regress the specified 'iterator' to refer to the previous holiday in the
    // associated calendar, and return the previous value of 'iterator'.  The
    // behavior is undefined unless, on entry, 'iterator' references a valid
    // holiday that is not the first holiday in the associated calendar.

               // =============================================
               // class PackedCalendar_HolidayCodeConstIterator
               // =============================================

class PackedCalendar_HolidayCodeConstIterator {
    // Provide read-only, sequential access in increasing (numerical) order to
    // the holiday codes in a 'PackedCalendar' object.

    // DATA
    bdlc::PackedIntArray<int>::const_iterator d_iterator;  // array's iterator

    // FRIENDS
    friend class PackedCalendar;
    friend bool operator==(const PackedCalendar_HolidayCodeConstIterator&,
                           const PackedCalendar_HolidayCodeConstIterator&);
    friend bool operator!=(const PackedCalendar_HolidayCodeConstIterator&,
                           const PackedCalendar_HolidayCodeConstIterator&);
    friend bsl::ptrdiff_t operator-(
                               const PackedCalendar_HolidayCodeConstIterator&,
                               const PackedCalendar_HolidayCodeConstIterator&);

  private:
    // PRIVATE TYPES
    typedef bdlc::PackedIntArray<int>::const_iterator CodesConstIterator;

    // PRIVATE CREATORS
    PackedCalendar_HolidayCodeConstIterator(const CodesConstIterator& iter);
                                                                    // IMPLICIT
        // Create a holiday-code iterator referencing the holiday code
        // referred to by the specified 'iter'.

  public:
    // TYPES
    typedef int  value_type;
    typedef int  difference_type;
    typedef int *pointer;
    typedef int  reference;
        // The star operator returns an 'int' *by* *value*.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE3.0

    typedef bsl::bidirectional_iterator_tag iterator_category;

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE3.0

    // CREATORS
    PackedCalendar_HolidayCodeConstIterator(
                      const PackedCalendar_HolidayCodeConstIterator& original);
        // Create an object having the value of the specified 'original'
        // iterator.

    ~PackedCalendar_HolidayCodeConstIterator();
        // Destroy this object.

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

    PackedCalendar_HolidayCodeConstIterator& operator++();
        // Advance this iterator to refer to the next holiday code for the
        // associated date in the associated calendar, and return a reference
        // providing modifiable access to this object.  The behavior is
        // undefined unless, on entry, this iterator references a valid
        // holiday code.

    PackedCalendar_HolidayCodeConstIterator& operator--();
        // Regress this iterator to refer to the previous holiday code for the
        // associated date in the associated calendar, and return a reference
        // providing modifiable access to this object.  The behavior is
        // undefined unless, on entry, this iterator references a valid
        // holiday code that is not the first holiday code for the associated
        // date in the calendar.

    // ACCESSORS
    int operator*() const;
        // Return the holiday code referenced by this iterator.
};

// FREE OPERATORS
bool operator==(const PackedCalendar_HolidayCodeConstIterator& lhs,
                const PackedCalendar_HolidayCodeConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators refers to the
    // same element, and 'false' otherwise.  The behavior is undefined unless
    // 'lhs' and 'rhs' both reference the same holiday in the same calendar.

bool operator!=(const PackedCalendar_HolidayCodeConstIterator& lhs,
                const PackedCalendar_HolidayCodeConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not refer to
    // the same element, and 'false' otherwise.  The behavior is undefined
    // unless 'lhs' and 'rhs' both reference the same holiday in the same
    // calendar.

PackedCalendar_HolidayCodeConstIterator
            operator++(PackedCalendar_HolidayCodeConstIterator& iterator, int);
    // Advance the specified 'iterator' to refer to the next holiday code for
    // the associated date in the associated calendar, and return the previous
    // value of 'iterator'.  The behavior is undefined unless, on entry,
    // 'iterator' references a valid holiday code.

PackedCalendar_HolidayCodeConstIterator
            operator--(PackedCalendar_HolidayCodeConstIterator& iterator, int);
    // Regress the specified 'iterator' to refer to the previous holiday code
    // for the associated date in the associated calendar, and return the
    // previous value of 'iterator'.  The behavior is undefined unless, on
    // entry, 'iterator' references a valid holiday code that is not the first
    // holiday code for the associated date in the calendar.

bsl::ptrdiff_t operator-(const PackedCalendar_HolidayCodeConstIterator& lhs,
                         const PackedCalendar_HolidayCodeConstIterator& rhs);
    // Return the number of elements between specified 'lhs' and 'rhs'.  The
    // behavior is undefined unless 'lhs' and 'rhs' refer to codes associated
    // with the same holiday in the same calendar.

              // =============================================
              // class PackedCalendar_BusinessDayConstIterator
              // =============================================

class PackedCalendar_BusinessDayConstIterator {
    // Provide read-only, sequential access in increasing (chronological) order
    // to the business days in a 'PackedCalendar' object.

    // DATA
    bdlc::PackedIntArray<int>::const_iterator  d_offsetIter;
                     // iterator for the holiday offsets

    const PackedCalendar                      *d_calendar_p;
                     // pointer to the calendar

    int                                        d_currentOffset;
                     // offset of the date referenced by this iterator from
                     // 'd_calendar_p->firstDate()'; if
                     // 'd_endFlag && 0 < d_calendar_p->length()', must equal
                     // 'd_calendar_p->lastDate() - d_calendar_p->firstDate()';
                     // if 'd_endFlag && 0 == d_calendar_p->length()', must
                     // equal 0

    bool                                       d_endFlag;
                     // indicates an 'end' iterator if set to 'true'

    // FRIENDS
    friend class PackedCalendar;
    friend bool operator==(const PackedCalendar_BusinessDayConstIterator&,
                           const PackedCalendar_BusinessDayConstIterator&);
    friend bool operator!=(const PackedCalendar_BusinessDayConstIterator&,
                           const PackedCalendar_BusinessDayConstIterator&);

  private:
    // PRIVATE TYPES
    typedef bdlc::PackedIntArray<int>::const_iterator OffsetsConstIterator;

    // PRIVATE CREATORS
    PackedCalendar_BusinessDayConstIterator(const PackedCalendar& calendar,
                                            const Date&           startDate,
                                            bool                  endIterFlag);
        // Create a business day iterator for the specified 'calendar'.  If the
        // specified 'endIterFlag' is 'false', then this iterator references
        // the first business day on or after the specified 'startDate';
        // otherwise, this iterator references one business day *past* the
        // first business day on or after 'startDate'.  If no business day
        // matching the above specification exists, then this iterator will
        // reference one day past the end of its range.

    // PRIVATE MANIPULATORS
    void nextBusinessDay();
        // Advance this iterator to the next business day.  The behavior is
        // undefined unless 'false == d_endFlag'.

    void previousBusinessDay();
        // Regress this iterator to the previous business day.  The behavior is
        // undefined unless '*this != d_calendar_p->beginBusinessDays()'.

  public:
    // TYPES
    typedef Date                     value_type;
    typedef int                      difference_type;
    typedef PackedCalendar_DateProxy pointer;
    typedef PackedCalendar_DateRef   reference;
        // The star operator returns a 'PackedCalendar_DateRef' *by* *value*.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE3.0

    typedef bsl::bidirectional_iterator_tag iterator_category;

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE3.0

    // CREATORS
    PackedCalendar_BusinessDayConstIterator(
                      const PackedCalendar_BusinessDayConstIterator& original);
        // Create an iterator having the value of the specified 'original'
        // iterator.

    ~PackedCalendar_BusinessDayConstIterator();
        // Destroy this object.

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

    PackedCalendar_BusinessDayConstIterator& operator++();
        // Advance this iterator to refer to the next business day in the
        // associated calendar, and return a reference providing modifiable
        // access to this object.  The behavior is undefined unless, on entry,
        // this iterator references a valid business day.

    PackedCalendar_BusinessDayConstIterator& operator--();
        // Regress this iterator to refer to the previous business day in the
        // associated calendar, and return a reference providing modifiable
        // access to this object.  The behavior is undefined unless, on entry,
        // this iterator references a valid business day that is not the first
        // business day for the associated calendar.

    // ACCESSORS
    PackedCalendar_DateRef operator*() const;
        // Return a 'PackedCalendar_DateRef' object that contains the date
        // value of the business day referenced by this iterator.

    PackedCalendar_DateProxy operator->() const;
        // Return a date proxy for the business day referenced by this
        // iterator.
};

// FREE OPERATORS
bool operator==(const PackedCalendar_BusinessDayConstIterator& lhs,
                const PackedCalendar_BusinessDayConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators refer to the
    // same element in the same calendar, and 'false' otherwise.  The behavior
    // is undefined unless 'lhs' and 'rhs' both iterate over the same calendar.

bool operator!=(const PackedCalendar_BusinessDayConstIterator& lhs,
                const PackedCalendar_BusinessDayConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not refer to
    // the same element in the same calendar, and 'false' otherwise.  The
    // behavior is undefined unless 'lhs' and 'rhs' both iterate over the same
    // calendar.

PackedCalendar_BusinessDayConstIterator operator++(
                       PackedCalendar_BusinessDayConstIterator& iterator, int);
    // Advance the specified 'iterator' to refer to the next business day in
    // the associated calendar, and return the previous value of 'iterator'.
    // The behavior is undefined unless, on entry, 'iterator' references a
    // valid business day.

PackedCalendar_BusinessDayConstIterator operator--(
                       PackedCalendar_BusinessDayConstIterator& iterator, int);
    // Regress the specified 'iterator' to refer to the previous business day
    // in the associated calendar, and return the previous value of 'iterator'.
    // The behavior is undefined unless, on entry, 'iterator' references a
    // valid business day that is not the first business day for the associated
    // calendar.

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

                     // ------------------------------
                     // class PackedCalendar_DateProxy
                     // ------------------------------

// CREATORS
inline
PackedCalendar_DateProxy::PackedCalendar_DateProxy(const Date& date)
: d_date(date)
{
}

inline
PackedCalendar_DateProxy::~PackedCalendar_DateProxy()
{
}

inline
PackedCalendar_DateProxy::PackedCalendar_DateProxy(
                                      const PackedCalendar_DateProxy& original)
: d_date(original.d_date)
{
}

// ACCESSORS
inline
const Date *PackedCalendar_DateProxy::operator->() const
{
    return &d_date;
}

                        // ----------------------------
                        // class PackedCalendar_DateRef
                        // ----------------------------

// CREATORS
inline
PackedCalendar_DateRef::PackedCalendar_DateRef(const Date& date)
: Date(date)
{
}

inline
PackedCalendar_DateRef::PackedCalendar_DateRef(
                                        const PackedCalendar_DateRef& original)
: Date(original)
{
}

inline
PackedCalendar_DateRef::~PackedCalendar_DateRef()
{
}

// ACCESSORS
inline
PackedCalendar_DateProxy PackedCalendar_DateRef::operator&() const
{
    return *this;
}

                    // -----------------------------------------
                    // class PackedCalendar_HolidayConstIterator
                    // -----------------------------------------

// PRIVATE CREATORS
inline
PackedCalendar_HolidayConstIterator::
     PackedCalendar_HolidayConstIterator(const OffsetsConstIterator& iter,
                                         const Date                  firstDate)
: d_iterator(iter)
, d_firstDate(firstDate)
{
}

// CREATORS
inline
PackedCalendar_HolidayConstIterator::PackedCalendar_HolidayConstIterator(
                           const PackedCalendar_HolidayConstIterator& original)
: d_iterator(original.d_iterator)
, d_firstDate(original.d_firstDate)
{
}

inline
PackedCalendar_HolidayConstIterator::~PackedCalendar_HolidayConstIterator()
{
}

// MANIPULATORS
inline
PackedCalendar_HolidayConstIterator& PackedCalendar_HolidayConstIterator::
                      operator=(const PackedCalendar_HolidayConstIterator& rhs)
{
    d_iterator  = rhs.d_iterator;
    d_firstDate = rhs.d_firstDate;
    return *this;
}

inline
PackedCalendar_HolidayConstIterator&
                              PackedCalendar_HolidayConstIterator::operator++()
{
    ++d_iterator;
    return *this;
}

inline
PackedCalendar_HolidayConstIterator&
                              PackedCalendar_HolidayConstIterator::operator--()
{
    --d_iterator;
    return *this;
}

// ACCESSORS
inline
PackedCalendar_DateRef PackedCalendar_HolidayConstIterator::operator*() const
{
    return PackedCalendar_DateRef(d_firstDate + *d_iterator);
}

inline
PackedCalendar_DateProxy
                        PackedCalendar_HolidayConstIterator::operator->() const
{
    return PackedCalendar_DateProxy(this->operator*());
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const PackedCalendar_HolidayConstIterator& lhs,
                      const PackedCalendar_HolidayConstIterator& rhs)
{
    return lhs.d_iterator == rhs.d_iterator;
}

inline
bool bdlt::operator!=(const PackedCalendar_HolidayConstIterator& lhs,
                      const PackedCalendar_HolidayConstIterator& rhs)
{
    return lhs.d_iterator != rhs.d_iterator;
}

inline
bdlt::PackedCalendar_HolidayConstIterator
           bdlt::operator++(PackedCalendar_HolidayConstIterator& iterator, int)
{
    PackedCalendar_HolidayConstIterator tmp(iterator);
    ++iterator;
    return tmp;
}

inline
bdlt::PackedCalendar_HolidayConstIterator
           bdlt::operator--(PackedCalendar_HolidayConstIterator& iterator, int)
{
    PackedCalendar_HolidayConstIterator tmp(iterator);
    --iterator;
    return tmp;
}

namespace bdlt {

                // ---------------------------------------------
                // class PackedCalendar_HolidayCodeConstIterator
                // ---------------------------------------------

// PRIVATE CREATORS
inline
PackedCalendar_HolidayCodeConstIterator::
        PackedCalendar_HolidayCodeConstIterator(const CodesConstIterator& iter)
: d_iterator(iter)
{
}

// CREATORS
inline
PackedCalendar_HolidayCodeConstIterator::
PackedCalendar_HolidayCodeConstIterator(
                       const PackedCalendar_HolidayCodeConstIterator& original)
: d_iterator(original.d_iterator)
{
}

inline
PackedCalendar_HolidayCodeConstIterator::
                                     ~PackedCalendar_HolidayCodeConstIterator()
{
}

// MANIPULATORS
inline
PackedCalendar_HolidayCodeConstIterator&
PackedCalendar_HolidayCodeConstIterator::
                  operator=(const PackedCalendar_HolidayCodeConstIterator& rhs)
{
    d_iterator = rhs.d_iterator;
    return *this;
}

inline
PackedCalendar_HolidayCodeConstIterator&
                          PackedCalendar_HolidayCodeConstIterator::operator++()
{
    ++d_iterator;
    return *this;
}

inline
PackedCalendar_HolidayCodeConstIterator&
                          PackedCalendar_HolidayCodeConstIterator::operator--()
{
    --d_iterator;
    return *this;
}

// ACCESSORS
inline
int PackedCalendar_HolidayCodeConstIterator::operator*() const
{
    return *d_iterator;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const PackedCalendar_HolidayCodeConstIterator& lhs,
                      const PackedCalendar_HolidayCodeConstIterator& rhs)
{
    return lhs.d_iterator == rhs.d_iterator;
}

inline
bool bdlt::operator!=(const PackedCalendar_HolidayCodeConstIterator& lhs,
                      const PackedCalendar_HolidayCodeConstIterator& rhs)
{
    return lhs.d_iterator != rhs.d_iterator;
}

inline
bdlt::PackedCalendar_HolidayCodeConstIterator
       bdlt::operator++(PackedCalendar_HolidayCodeConstIterator& iterator, int)
{
    bdlt::PackedCalendar_HolidayCodeConstIterator tmp(iterator);
    ++iterator;
    return tmp;
}

inline
bdlt::PackedCalendar_HolidayCodeConstIterator
       bdlt::operator--(PackedCalendar_HolidayCodeConstIterator& iterator, int)
{
    PackedCalendar_HolidayCodeConstIterator tmp(iterator);
    --iterator;
    return tmp;
}

inline
bsl::ptrdiff_t
            bdlt::operator-(const PackedCalendar_HolidayCodeConstIterator& lhs,
                            const PackedCalendar_HolidayCodeConstIterator& rhs)
{
    return lhs.d_iterator - rhs.d_iterator;
}

namespace bdlt {

               // ---------------------------------------------
               // class PackedCalendar_BusinessDayConstIterator
               // ---------------------------------------------

// CREATORS
inline
PackedCalendar_BusinessDayConstIterator::
PackedCalendar_BusinessDayConstIterator(
                       const PackedCalendar_BusinessDayConstIterator& original)
: d_offsetIter(original.d_offsetIter)
, d_calendar_p(original.d_calendar_p)
, d_currentOffset(original.d_currentOffset)
, d_endFlag(original.d_endFlag)
{
}

inline
PackedCalendar_BusinessDayConstIterator::
                                     ~PackedCalendar_BusinessDayConstIterator()
{
}

// MANIPULATORS
inline
PackedCalendar_BusinessDayConstIterator&
                          PackedCalendar_BusinessDayConstIterator::operator++()
{
    BSLS_ASSERT_SAFE(false == d_endFlag);

    nextBusinessDay();
    return *this;
}

inline
PackedCalendar_BusinessDayConstIterator&
                          PackedCalendar_BusinessDayConstIterator::operator--()
{
    BSLS_ASSERT_SAFE(*this != d_calendar_p->beginBusinessDays());

    previousBusinessDay();
    return *this;
}

// ACCESSORS
inline
PackedCalendar_DateRef
                     PackedCalendar_BusinessDayConstIterator::operator*() const
{
    return PackedCalendar_DateRef(d_calendar_p->firstDate() + d_currentOffset);
}

inline
PackedCalendar_DateProxy
                    PackedCalendar_BusinessDayConstIterator::operator->() const
{
    return PackedCalendar_DateProxy(this->operator*());
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const PackedCalendar_BusinessDayConstIterator& lhs,
                      const PackedCalendar_BusinessDayConstIterator& rhs)
{
    BSLS_ASSERT_SAFE(lhs.d_calendar_p == rhs.d_calendar_p);

    return lhs.d_endFlag       == rhs.d_endFlag
        && lhs.d_currentOffset == rhs.d_currentOffset;
}

inline
bool bdlt::operator!=(const PackedCalendar_BusinessDayConstIterator& lhs,
                      const PackedCalendar_BusinessDayConstIterator& rhs)
{
    BSLS_ASSERT_SAFE(lhs.d_calendar_p == rhs.d_calendar_p);

    return lhs.d_endFlag       != rhs.d_endFlag
        || lhs.d_currentOffset != rhs.d_currentOffset;
}

inline
bdlt::PackedCalendar_BusinessDayConstIterator bdlt::operator++(
                        PackedCalendar_BusinessDayConstIterator& iterator, int)
{
    PackedCalendar_BusinessDayConstIterator tmp(iterator);
    ++iterator;
    return tmp;
}

inline
bdlt::PackedCalendar_BusinessDayConstIterator bdlt::operator--(
                        PackedCalendar_BusinessDayConstIterator& iterator, int)
{
    PackedCalendar_BusinessDayConstIterator tmp(iterator);
    --iterator;
    return tmp;
}

namespace bdlt {

                         // --------------------
                         // class PackedCalendar
                         // --------------------

// PRIVATE ACCESSORS
inline
PackedCalendar::CodesConstIterator
      PackedCalendar::beginHolidayCodes(const OffsetsConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(d_firstDate + *iter));

    const int indexOffset = static_cast<int>(iter - d_holidayOffsets.begin());

    const bsl::size_t codeOffset = d_holidayCodesIndex[indexOffset];

    return d_holidayCodes.begin() + codeOffset;
}

inline
PackedCalendar::CodesConstIterator
        PackedCalendar::endHolidayCodes(const OffsetsConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(d_firstDate + *iter));

    const bsl::size_t endIndexOffset = iter - d_holidayOffsets.begin() + 1;

    const bsl::size_t iterIndex = endIndexOffset ==
                                                   d_holidayCodesIndex.length()
                                  ? d_holidayCodes.length()
                                  : d_holidayCodesIndex[endIndexOffset];
    return d_holidayCodes.begin() + iterIndex;
}

// CLASS METHODS

                                  // Aspects

inline
int PackedCalendar::maxSupportedBdexVersion(int versionSelector)
{
    if (versionSelector >= 20150612) {
        return 3;                                                     // RETURN
    }
    return 2;
}

// MANIPULATORS
inline
int PackedCalendar::addHolidayCodeIfInRange(const Date& date, int holidayCode)
{
    if (isInRange(date)) {
        addHolidayCode(date, holidayCode);
        return 0;                                                     // RETURN
    }
    return -1;
}

inline
int PackedCalendar::addHolidayIfInRange(const Date& date)
{
    if (isInRange(date)) {
        addHoliday(date);
        return 0;                                                     // RETURN
    }
    return -1;
}

inline
void PackedCalendar::reserveHolidayCapacity(int numHolidays)
{
    BSLS_ASSERT(0 <= numHolidays);

    d_holidayOffsets.reserveCapacity(numHolidays);
    d_holidayCodesIndex.reserveCapacity(numHolidays);
}

inline
void PackedCalendar::reserveHolidayCodeCapacity(int numHolidayCodes)
{
    BSLS_ASSERT(0 <= numHolidayCodes);

    d_holidayCodes.reserveCapacity(numHolidayCodes);
}

                                  // Aspects

template <class STREAM>
STREAM& PackedCalendar::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) {  // Switch on the schema version (starting with 1).
          case 3: {
            bdlt::Date firstDate;
            firstDate.bdexStreamIn(stream, 1);

            if (!stream) {
                return stream;                                        // RETURN
            }

            bdlt::Date lastDate;
            lastDate.bdexStreamIn(stream, 1);

            if (!stream
             || (firstDate > lastDate
              && (firstDate != Date(9999,12,31)
                  || lastDate  != Date(1,1,1)))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            int length = lastDate - firstDate + 1;

            int transitionsLength = 0;
            stream.getLength(transitionsLength);

            if (!stream || transitionsLength < 0) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            bsl::vector<WeekendDaysTransition>
                      weekendDaysTransitions(transitionsLength, d_allocator_p);
            for (int i = 0; i < transitionsLength; ++i) {
                WeekendDaysTransition& wdt = weekendDaysTransitions[i];

                wdt.first.bdexStreamIn(stream, 1);

                if (!stream) {
                    return stream;                                    // RETURN
                }

                // The data must be monotonically increasing.

                if (i && wdt.first <= weekendDaysTransitions[i - 1].first) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }

                wdt.second.bdexStreamIn(stream, 1);

                if (!stream) {
                    return stream;                                    // RETURN
                }
            }

            bdlc::PackedIntArray<int> holidayOffsets(d_allocator_p);
            holidayOffsets.bdexStreamIn(stream, 1);

            if (!stream
             || (firstDate >  lastDate && !holidayOffsets.isEmpty())
             || (firstDate <= lastDate
              && (static_cast<int>(holidayOffsets.length()) > length
               || (!holidayOffsets.isEmpty()
                && (holidayOffsets[0] < 0
                 || holidayOffsets[0] > length))))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            // The values in 'holidayOffsets' must be monotonically increasing
            // and represent dates within the valid range.

            for (bsl::size_t i = 1; i < holidayOffsets.length(); ++i) {
                if (holidayOffsets[i - 1] >= holidayOffsets[i]
                 || holidayOffsets[i] > length) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
            }

            bdlc::PackedIntArray<int> holidayCodesIndex(d_allocator_p);
            holidayCodesIndex.bdexStreamIn(stream, 1);

            if (!stream
             || holidayCodesIndex.length() != holidayOffsets.length()
             || (!holidayCodesIndex.isEmpty() && holidayCodesIndex[0] != 0)) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            // The values in 'holidayCodesIndex' must be monotonically
            // non-decreasing.

            for (bsl::size_t i = 1; i < holidayCodesIndex.length(); ++i) {
                if (holidayCodesIndex[i - 1] > holidayCodesIndex[i]) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
            }

            bdlc::PackedIntArray<int> holidayCodes(d_allocator_p);
            holidayCodes.bdexStreamIn(stream, 1);

            if (!stream
             || (holidayOffsets.isEmpty() && !holidayCodes.isEmpty())) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            // The values in the segments of 'holidayOffsets' must be
            // monotonically increasing.

            bsl::size_t index = 0;
            for (int i = 0; i < static_cast<int>(holidayCodes.length()); ++i) {
                if (index < holidayCodesIndex.length()
                 && i == holidayCodesIndex[index]) {
                    ++index;
                    while (index < holidayCodesIndex.length()
                        && i == holidayCodesIndex[index]) {
                        ++index;
                    }
                }
                else if (holidayCodes[i - 1] >= holidayCodes[i]) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
            }

            bslalg::SwapUtil::swap(&d_firstDate,         &firstDate);
            bslalg::SwapUtil::swap(&d_lastDate,          &lastDate);
            bslalg::SwapUtil::swap(&d_weekendDaysTransitions,
                                   &weekendDaysTransitions);
            bslalg::SwapUtil::swap(&d_holidayOffsets,    &holidayOffsets);
            bslalg::SwapUtil::swap(&d_holidayCodesIndex, &holidayCodesIndex);
            bslalg::SwapUtil::swap(&d_holidayCodes,      &holidayCodes);
          } break;
          case 2: {
            PackedCalendar inCal(d_allocator_p);
            inCal.d_firstDate.bdexStreamIn(stream, 1);
            if (!stream) {
                return stream;                                        // RETURN
            }

            inCal.d_lastDate.bdexStreamIn(stream, 1);
            if (!stream
             || (inCal.d_firstDate > inCal.d_lastDate
              && (inCal.d_firstDate != Date(9999,12,31)
               || inCal.d_lastDate  != Date(1,1,1)))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }
            int length = inCal.d_lastDate - inCal.d_firstDate + 1;

            int transitionsLength = 0;
            stream.getLength(transitionsLength);
            if (!stream || transitionsLength < 0)
            {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            int offsetsLength = 0;
            stream.getLength(offsetsLength);
            if (!stream
             || (inCal.d_firstDate >  inCal.d_lastDate
              && offsetsLength != 0)
             || (inCal.d_firstDate <= inCal.d_lastDate
              && (offsetsLength < 0 || offsetsLength > length))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }
            BSLS_ASSERT(offsetsLength >= 0);

            int codesLength = 0;
            stream.getLength(codesLength);
            if (!stream || (0 == offsetsLength && codesLength != 0)) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            inCal.d_weekendDaysTransitions.resize(transitionsLength);

            // To ensure 'inCal' cannot be in an invalid state, we must first
            // reserve capacity for the three 'PackedIntArray' and then resize
            // them to guarantee the operations can complete without
            // re-allocation *and* the object invariants (see the destructor
            // implementation) are maintained.

            inCal.d_holidayOffsets.reserveCapacity(offsetsLength);
            inCal.d_holidayCodesIndex.reserveCapacity(offsetsLength);
            inCal.d_holidayCodes.reserveCapacity(codesLength);

            inCal.d_holidayOffsets.resize(offsetsLength);
            inCal.d_holidayCodesIndex.resize(offsetsLength);
            inCal.d_holidayCodes.resize(codesLength);

            for (WeekendDaysTransitionSequence::iterator it =
                     inCal.d_weekendDaysTransitions.begin();
                 it != inCal.d_weekendDaysTransitions.end();
                 ++it) {
                it->first.bdexStreamIn(stream, 1);
                if (!stream) {
                    return stream;                                    // RETURN
                }

                if (it        != inCal.d_weekendDaysTransitions.begin()
                 && it->first <= (it - 1)->first) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }

                it->second.bdexStreamIn(stream, 1);
                if (!stream) {
                    return stream;                                    // RETURN
                }
            }

            int previousValue = -1;
            for (int i = 0; i < offsetsLength; ++i) {
                int tmp;
                stream.getInt32(tmp);
                if (!stream
                 || tmp < 0
                 || tmp >= length
                 || tmp <= previousValue) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayOffsets.replace(i, tmp);
                previousValue = tmp;
            }

            previousValue = -1;
            for (int i = 0; i < offsetsLength; ++i) {
                int tmp;
                stream.getInt32(tmp);

                // This vector is ordered but duplicates are allowed.  The
                // first element must be 0.

                if (!stream
                 || tmp < 0
                 || tmp < previousValue
                 || tmp > codesLength
                 || (0 == i && 0 != tmp)) {

                    // If we get here, some of the code indices could
                    // potentially be greater than 'codesLength'.  That would
                    // trigger an assertion in the destructor.  So call
                    // 'removeAll' to clean up.

                    inCal.removeAll();
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayCodesIndex.replace(i, tmp);
                previousValue = tmp;
            }

            CodesIndexConstIterator it = inCal.d_holidayCodesIndex.begin();
            CodesIndexConstIterator end = inCal.d_holidayCodesIndex.end();

            // Skip the holidays that have no codes.

            while (it != end && 0 == *it) {
                ++it;
            }

            // 'it' is now positioned at the first holiday with one or more
            // codes or at the end.

            bool previousValueFlag = false;  // This flag is used to determine
                                             // if we are inside an ordered
                                             // sequence of codes (i.e.,
                                             // 'previousValue' refers to a
                                             // code for the same holiday as
                                             // 'value').

            for (int i = 0; i < codesLength; ++i) {
                int tmp;
                stream.getInt32(tmp);
                if (!stream
                 || (previousValueFlag && tmp <= previousValue)) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayCodes.replace(i, tmp);

                // Regardless of whether or not there is more data, advance the
                // index iterator as needed and update 'previousValueFlag' if
                // 'it' moves.

                if (it != end && i == (*it - 1)) {
                    previousValueFlag = false;

                    while (it != end && i == (*it - 1)) {
                        ++it;  // Skip the holidays that have no codes.
                    }
                }
                else {
                    previousValueFlag = true;
                }
                previousValue = tmp;
            }
            BSLS_ASSERT(it == end);

            swap(inCal);  // This cannot throw.
          } break;
          case 1: {
            PackedCalendar inCal(d_allocator_p);
            inCal.d_firstDate.bdexStreamIn(stream, 1);
            if (!stream) {
                return stream;                                        // RETURN
            }

            inCal.d_lastDate.bdexStreamIn(stream, 1);
            if (!stream
             || (inCal.d_firstDate > inCal.d_lastDate
              && (inCal.d_firstDate != Date(9999, 12, 31)
               || inCal.d_lastDate  != Date(1, 1, 1)))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }
            int length = inCal.d_lastDate - inCal.d_firstDate + 1;

            DayOfWeekSet weekendDays;
            weekendDays.bdexStreamIn(stream, 1);
            if (!stream) {
                return stream;                                        // RETURN
            }

            if (weekendDays.length() > 0) {
                inCal.addWeekendDays(weekendDays);
            }

            int offsetsLength = 0;
            stream.getLength(offsetsLength);
            if (!stream
             || (inCal.d_firstDate >  inCal.d_lastDate
              && offsetsLength != 0)
             || (inCal.d_firstDate <= inCal.d_lastDate
              && (offsetsLength < 0 || offsetsLength > length))) {
                stream.invalidate();
                return stream;                                        // RETURN
            }
            BSLS_ASSERT(offsetsLength >= 0);

            int codesLength = 0;
            stream.getLength(codesLength);
            if (!stream || (0 == offsetsLength && codesLength != 0)) {
                stream.invalidate();
                return stream;                                        // RETURN
            }

            // To ensure 'inCal' cannot be in an invalid state, we must first
            // reserve capacity for the three 'PackedIntArray' and then resize
            // them to guarantee the operations can complete without
            // re-allocation *and* the object invariants (see the destructor
            // implementation) are maintained.

            inCal.d_holidayOffsets.reserveCapacity(offsetsLength);
            inCal.d_holidayCodesIndex.reserveCapacity(offsetsLength);
            inCal.d_holidayCodes.reserveCapacity(codesLength);

            inCal.d_holidayOffsets.resize(offsetsLength);
            inCal.d_holidayCodesIndex.resize(offsetsLength);
            inCal.d_holidayCodes.resize(codesLength);

            int previousValue = -1;
            for (int i = 0; i < offsetsLength; ++i) {
                int tmp;
                stream.getInt32(tmp);
                if (!stream
                 || tmp < 0
                 || tmp >= length
                 || tmp <= previousValue) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayOffsets.replace(i, tmp);
                previousValue = tmp;
            }

            previousValue = -1;
            for (int i = 0; i < offsetsLength; ++i) {
                int tmp;
                stream.getInt32(tmp);

                // This vector is ordered but duplicates are allowed.  The
                // first element must be 0.

                if (!stream
                 || tmp < 0
                 || tmp < previousValue
                 || tmp > codesLength
                 || (0 == i && 0 != tmp)) {

                    // If we get here, some of the code indices could
                    // potentially be greater than 'codesLength'.  That would
                    // trigger an assertion in the destructor.  So call
                    // 'removeAll' to clean up.

                    inCal.removeAll();
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayCodesIndex.replace(i, tmp);
                previousValue = tmp;
            }

            CodesIndexConstIterator it = inCal.d_holidayCodesIndex.begin();
            CodesIndexConstIterator end = inCal.d_holidayCodesIndex.end();

            // Skip the holidays that have no codes.

            while (it != end && 0 == *it) {
                ++it;
            }

            // 'it' is now positioned at the first holiday with one or more
            // codes or at the end.

            bool previousValueFlag = false;  // This flag is used to determine
                                             // if we are inside an ordered
                                             // sequence of codes (i.e.,
                                             // 'previousValue' refers to a
                                             // code for the same holiday as
                                             // 'value').

            for (int i = 0; i < codesLength; ++i) {
                int tmp;
                stream.getInt32(tmp);
                if (!stream
                 || (previousValueFlag && tmp <= previousValue)) {
                    stream.invalidate();
                    return stream;                                    // RETURN
                }
                inCal.d_holidayCodes.replace(i, tmp);

                // Regardless of whether or not there is more data, advance the
                // index iterator as needed and update 'previousValueFlag' if
                // 'it' moves.

                if (it != end && i == (*it - 1)) {
                    previousValueFlag = false;

                    while (it != end && i == (*it - 1)) {
                        ++it;  // Skip the holidays that have no codes.
                    }
                }
                else {
                    previousValueFlag = true;
                }
                previousValue = tmp;
            }
            BSLS_ASSERT(it == end);

            swap(inCal);  // This cannot throw.
          } break;
          default: {
            stream.invalidate();
          }
        }
    }
    return stream;
}

// ACCESSORS
inline
PackedCalendar::BusinessDayConstIterator
                                      PackedCalendar::beginBusinessDays() const
{
    return BusinessDayConstIterator(*this, d_firstDate, false);
}

inline
PackedCalendar::BusinessDayConstIterator
                      PackedCalendar::beginBusinessDays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return BusinessDayConstIterator(*this, date, false);
}

inline
PackedCalendar::HolidayCodeConstIterator
      PackedCalendar::beginHolidayCodes(const HolidayConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(*iter));

    return HolidayCodeConstIterator(beginHolidayCodes(iter.d_iterator));
}

inline
PackedCalendar::HolidayConstIterator PackedCalendar::beginHolidays() const
{
    return HolidayConstIterator(d_holidayOffsets.begin(), d_firstDate);
}

inline
PackedCalendar::HolidayConstIterator
                          PackedCalendar::beginHolidays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    OffsetsConstIterator i = bdlc::PackedIntArrayUtil::lowerBound(
                                                      d_holidayOffsets.begin(),
                                                      d_holidayOffsets.end(),
                                                      date - d_firstDate);
    return HolidayConstIterator(i, d_firstDate);
}

inline
PackedCalendar::WeekendDaysTransitionConstIterator
                            PackedCalendar::beginWeekendDaysTransitions() const
{
    return d_weekendDaysTransitions.begin();
}

inline
PackedCalendar::BusinessDayConstIterator
                                        PackedCalendar::endBusinessDays() const
{
    return BusinessDayConstIterator(*this, d_lastDate, true);
}

inline
PackedCalendar::BusinessDayConstIterator
                        PackedCalendar::endBusinessDays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return BusinessDayConstIterator(*this, date, true);
}

inline
PackedCalendar::HolidayCodeConstIterator
        PackedCalendar::endHolidayCodes(const HolidayConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(*iter));

    return endHolidayCodes(iter.d_iterator);
}

inline
PackedCalendar::HolidayConstIterator PackedCalendar::endHolidays() const
{
    return HolidayConstIterator(d_holidayOffsets.end(), d_firstDate);
}

inline
PackedCalendar::HolidayConstIterator
                            PackedCalendar::endHolidays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    OffsetsConstIterator i = bdlc::PackedIntArrayUtil::upperBound(
                                                      d_holidayOffsets.begin(),
                                                      d_holidayOffsets.end(),
                                                      date - d_firstDate);
    return HolidayConstIterator(i, d_firstDate);
}

inline
PackedCalendar::WeekendDaysTransitionConstIterator
                              PackedCalendar::endWeekendDaysTransitions() const
{
    return d_weekendDaysTransitions.end();
}

inline
const Date& PackedCalendar::firstDate() const
{
    BSLS_ASSERT_SAFE(d_firstDate <= d_lastDate);

    return d_firstDate;
}

inline
int PackedCalendar::getNextBusinessDay(Date        *nextBusinessDay,
                                       const Date&  date) const
{
    BSLS_ASSERT_SAFE(nextBusinessDay);
    BSLS_ASSERT_SAFE(Date(9999, 12, 31) > date);
    BSLS_ASSERT_SAFE(isInRange(date + 1));

    enum { e_SUCCESS = 0, e_FAILURE = 1 };

    BusinessDayConstIterator iter = beginBusinessDays(date + 1);
    if (iter == endBusinessDays()) {
        return e_FAILURE;                                             // RETURN
    }
    *nextBusinessDay = *iter;
    return e_SUCCESS;
}

inline
bdlt::Date PackedCalendar::holiday(int index) const
{
    BSLS_ASSERT_SAFE(0 <= index);
    BSLS_ASSERT_SAFE(     index < numHolidays());

    return d_firstDate + d_holidayOffsets[index];
}

inline
int PackedCalendar::holidayCode(const Date& date, int index) const
{
    BSLS_ASSERT_SAFE(isInRange(date));
    BSLS_ASSERT_SAFE(isHoliday(date));
    BSLS_ASSERT_SAFE(0 <= index);
    BSLS_ASSERT_SAFE(     index < numHolidayCodes(date));

    const int                  offset = date - d_firstDate;
    const OffsetsConstIterator offsetBegin = d_holidayOffsets.begin();
    const OffsetsConstIterator offsetEnd   = d_holidayOffsets.end();
    const OffsetsConstIterator i = bdlc::PackedIntArrayUtil::lowerBound(
                                                                   offsetBegin,
                                                                   offsetEnd,
                                                                   offset);
    return d_holidayCodes[d_holidayCodesIndex[i - offsetBegin] + index];
}

inline
bool PackedCalendar::isBusinessDay(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return !isNonBusinessDay(date);
}

inline
bool PackedCalendar::isHoliday(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    const int                  offset = date - d_firstDate;
    const OffsetsConstIterator offsetEnd = d_holidayOffsets.end();
    const OffsetsConstIterator i = bdlc::PackedIntArrayUtil::lowerBound(
                                                      d_holidayOffsets.begin(),
                                                      offsetEnd,
                                                      offset);

    return i != offsetEnd && *i == offset;
}

inline
bool PackedCalendar::isInRange(const Date& date) const
{
    return d_firstDate <= date && date <= d_lastDate;
}

inline
bool PackedCalendar::isNonBusinessDay(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return isWeekendDay(date) || isHoliday(date);
}

inline
bool PackedCalendar::isWeekendDay(DayOfWeek::Enum dayOfWeek) const
{
    BSLS_PRECONDITIONS_BEGIN();
    BSLS_ASSERT_SAFE(d_weekendDaysTransitions.size() <= 1);
    BSLS_PRECONDITIONS_END();

    if (d_weekendDaysTransitions.empty()) {
        return false;                                                 // RETURN
    }

    BSLS_ASSERT_SAFE(d_weekendDaysTransitions[0].first == Date(1,1,1));

    return d_weekendDaysTransitions[0].second.isMember(dayOfWeek);
}

inline
const Date& PackedCalendar::lastDate() const
{
    BSLS_ASSERT_SAFE(d_firstDate <= d_lastDate);

    return d_lastDate;
}

inline
int PackedCalendar::length() const
{
    return d_firstDate <= d_lastDate ? d_lastDate - d_firstDate + 1 : 0;
}

inline
int PackedCalendar::numBusinessDays() const
{
    return length() - numNonBusinessDays();
}

inline
int PackedCalendar::numHolidayCodesTotal() const
{
    return static_cast<int>(d_holidayCodes.length());
}

inline
int PackedCalendar::numHolidays() const
{
    return static_cast<int>(d_holidayOffsets.length());
}

inline
int PackedCalendar::numWeekendDaysTransitions() const
{
    return static_cast<int>(d_weekendDaysTransitions.size());
}

inline
PackedCalendar::BusinessDayConstReverseIterator
                                     PackedCalendar::rbeginBusinessDays() const
{
    return BusinessDayConstReverseIterator(endBusinessDays());
}

inline
PackedCalendar::BusinessDayConstReverseIterator
                     PackedCalendar::rbeginBusinessDays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return BusinessDayConstReverseIterator(endBusinessDays(date));
}

inline
PackedCalendar::HolidayCodeConstReverseIterator
                     PackedCalendar::rbeginHolidayCodes(const Date& date) const
{
    BSLS_ASSERT_SAFE(isHoliday(date));

    return HolidayCodeConstReverseIterator(endHolidayCodes(date));
}

inline
PackedCalendar::HolidayCodeConstReverseIterator
     PackedCalendar::rbeginHolidayCodes(const HolidayConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(*iter));

    return HolidayCodeConstReverseIterator(endHolidayCodes(iter));
}

inline
PackedCalendar::HolidayConstReverseIterator
                                         PackedCalendar::rbeginHolidays() const
{
    return HolidayConstReverseIterator(endHolidays());
}

inline
PackedCalendar::HolidayConstReverseIterator
                         PackedCalendar::rbeginHolidays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return HolidayConstReverseIterator(endHolidays(date));
}

inline
PackedCalendar::WeekendDaysTransitionConstReverseIterator
                           PackedCalendar::rbeginWeekendDaysTransitions() const
{
    return WeekendDaysTransitionConstReverseIterator(
                                               d_weekendDaysTransitions.end());
}

inline
PackedCalendar::BusinessDayConstReverseIterator
                                       PackedCalendar::rendBusinessDays() const
{
    return BusinessDayConstReverseIterator(beginBusinessDays());
}

inline
PackedCalendar::BusinessDayConstReverseIterator
                       PackedCalendar::rendBusinessDays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return BusinessDayConstReverseIterator(beginBusinessDays(date));
}

inline
PackedCalendar::HolidayCodeConstReverseIterator
                       PackedCalendar::rendHolidayCodes(const Date& date) const
{
    BSLS_ASSERT_SAFE(isHoliday(date));

    return HolidayCodeConstReverseIterator(beginHolidayCodes(date));
}

inline
PackedCalendar::HolidayCodeConstReverseIterator
       PackedCalendar::rendHolidayCodes(const HolidayConstIterator& iter) const
{
    BSLS_ASSERT_SAFE(isHoliday(*iter));

    return HolidayCodeConstReverseIterator(beginHolidayCodes(iter));
}

inline
PackedCalendar::HolidayConstReverseIterator
                                           PackedCalendar::rendHolidays() const
{
    return HolidayConstReverseIterator(beginHolidays());
}

inline
PackedCalendar::HolidayConstReverseIterator
                           PackedCalendar::rendHolidays(const Date& date) const
{
    BSLS_ASSERT_SAFE(isInRange(date));

    return HolidayConstReverseIterator(beginHolidays(date));
}

inline
PackedCalendar::WeekendDaysTransitionConstReverseIterator
                             PackedCalendar::rendWeekendDaysTransitions() const
{
    return WeekendDaysTransitionConstReverseIterator(
                                             d_weekendDaysTransitions.begin());
}

inline
PackedCalendar::WeekendDaysTransition
                         PackedCalendar::weekendDaysTransition(int index) const
{
    BSLS_ASSERT_SAFE(0 <= index);
    BSLS_ASSERT_SAFE(     index < numWeekendDaysTransitions());

    return d_weekendDaysTransitions[index];
}

                                  // Aspects

inline
bslma::Allocator *PackedCalendar::allocator() const
{
    return d_allocator_p;
}

template <class STREAM>
STREAM& PackedCalendar::bdexStreamOut(STREAM& stream, int version) const
{
    switch (version) {  // Switch on the schema version (starting with 1).
      case 3: {
        d_firstDate.bdexStreamOut(stream, 1);
        d_lastDate.bdexStreamOut(stream, 1);

        stream.putLength(static_cast<int>(d_weekendDaysTransitions.size()));
        for (WeekendDaysTransitionSequence::size_type i = 0;
             i < d_weekendDaysTransitions.size();
             ++i) {
            d_weekendDaysTransitions[i].first.bdexStreamOut(stream, 1);
            d_weekendDaysTransitions[i].second.bdexStreamOut(stream, 1);
        }

        d_holidayOffsets.bdexStreamOut(stream, 1);
        d_holidayCodesIndex.bdexStreamOut(stream, 1);
        d_holidayCodes.bdexStreamOut(stream, 1);
      } break;
      case 2: {
        d_firstDate.bdexStreamOut(stream, 1);
        d_lastDate.bdexStreamOut(stream, 1);

        stream.putLength(static_cast<int>(d_weekendDaysTransitions.size()));
        stream.putLength(static_cast<int>(d_holidayOffsets.length()));
        stream.putLength(static_cast<int>(d_holidayCodes.length()));

        for (WeekendDaysTransitionSequence::size_type i = 0;
             i < d_weekendDaysTransitions.size();
             ++i) {
            d_weekendDaysTransitions[i].first.bdexStreamOut(stream, 1);
            d_weekendDaysTransitions[i].second.bdexStreamOut(stream, 1);
        }

        for (bsl::size_t i = 0; i < d_holidayOffsets.length(); ++i) {
            stream.putInt32(d_holidayOffsets[i]);
        }
        for (bsl::size_t i = 0; i < d_holidayCodesIndex.length(); ++i) {
            stream.putInt32(d_holidayCodesIndex[i]);
        }
        for (bsl::size_t i = 0; i < d_holidayCodes.length(); ++i) {
            stream.putInt32(d_holidayCodes[i]);
        }
      } break;
      case 1: {
        d_firstDate.bdexStreamOut(stream, 1);
        d_lastDate.bdexStreamOut(stream, 1);

        if (!d_weekendDaysTransitions.empty() &&
            d_weekendDaysTransitions[0].first == Date(1, 1, 1)) {
            d_weekendDaysTransitions[0].second.bdexStreamOut(stream, 1);
        }
        else {
            DayOfWeekSet tempSet;
            tempSet.bdexStreamOut(stream, 1);
        }

        stream.putLength(static_cast<int>(d_holidayOffsets.length()));
        stream.putLength(static_cast<int>(d_holidayCodes.length()));

        for (bsl::size_t i = 0; i < d_holidayOffsets.length(); ++i) {
            stream.putInt32(d_holidayOffsets[i]);
        }
        for (bsl::size_t i = 0; i < d_holidayCodesIndex.length(); ++i) {
            stream.putInt32(d_holidayCodesIndex[i]);
        }
        for (bsl::size_t i = 0; i < d_holidayCodes.length(); ++i) {
            stream.putInt32(d_holidayCodes[i]);
        }
      } break;
      default: {
        stream.invalidate();
      }

    }
    return stream;
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE2.22

// DEPRECATED METHODS
inline
int PackedCalendar::maxSupportedBdexVersion()
{
    return 1;
}

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator!=(const PackedCalendar& lhs, const PackedCalendar& rhs)
{
    return !(lhs == rhs);
}

// FREE FUNCTIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG& hashAlg, const PackedCalendar& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlg, object.d_firstDate);
    hashAppend(hashAlg, object.d_lastDate);
    hashAppend(hashAlg, object.d_weekendDaysTransitions);
    hashAppend(hashAlg, object.d_holidayOffsets);
    hashAppend(hashAlg, object.d_holidayCodesIndex);
    hashAppend(hashAlg, object.d_holidayCodes);
}

}  // close enterprise namespace

// TRAITS
namespace BloombergLP {
namespace bslma {

template <>
struct UsesBslmaAllocator<bdlt::PackedCalendar> : 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 ----------------------------------