// -*- C++ -*-

/*!
  \file PackedArrayOfArrays.h
  \brief A class for a packed %array of arrays.
*/

#if !defined(__array_PackedArrayOfArrays_h__)
#define __array_PackedArrayOfArrays_h__

#include "../ext/vector.h"

namespace array {

//! A packed %array of arrays.
/*!
  \param _T is the value type.

  This is an array of arrays that have varying sizes. One may think of it as 
  as two dimensional array where the rows have varying numbers of columns.
  This data structure is useful where one might think of using a container
  of containers like \c std::vector<std::vector<double> >. 

  This class packs all of the elements of all of the arrays into a 
  contigous storage. This gives it better cache utilization than a 
  container of containers. The downside is that it has limited ability 
  to modify the sizes of the component arrays. You can only manipulate 
  the last array. We will talk more about that later.
  In addition to packed elements, this class stores a vector of array 
  delimiters that define the begining and end of each array.

  There are a number of ways to construct this class. The default 
  constructor makes an empty data structure. There are no arrays and
  no elements. Below we construct the class and verify this.
  \code
  array::PackedArrayOfArrays<double> x;
  assert(x.numArrays() == 0);
  assert(x.empty());
  \endcode
  You can also construct it from a container of containers. You may use
  any container that satisfies the STL interface for a sequence.
  \code
  std::vector<std::vector<double> > a;
  ...
  array::PackedArrayOfArrays<double> x(a);
  \endcode
  \code
  std::list<std::list<double> > a;
  ...
  array::PackedArrayOfArrays<double> x(a);
  \endcode
  If you know the sizes of the arrays but do not yet know the element values,
  you can construct a PackedArrayOfArrays with the range of sizes.
  \code
  std::vector<std::size_t> sizes;
  ...
  array::PackedArrayOfArrays<double> x(sizes.begin(), sizes.end());
  \endcode
  You can also construct a PackedArrayOfArrays from a range of array sizes
  and the range for the packed elements.
  \code
  std::vector<std::size_t> sizes;
  std::vector<double> packedData;
  ...
  array::PackedArrayOfArrays<double> x(sizes.begin(), sizes.end(),
                                       packedData.begin(), packedData.end());
  \endcode

  This class inherits from std::vector to store the elements, and 
  it retains the accessors. Thus you can use \c operator[], \c size(),
  \c empty(), \c begin(), \c end(), \c rbegin(), and \c rend() to work 
  with the packed array of elements. For example, below we compute the
  sum of the elements in three different ways.
  \code
  array::PackedArrayOfArrays<double> x;
  ...
  // Indexing.
  double sum = 0;
  for (std::size_t i = 0; i != x.size(); ++i) {
     sum += x[i];
  }
  // Iterator.
  sum = 0;
  for (array::PackedArrayOfArrays<double>::const_iterator i = x.begin();
       i != x.end(); ++i) {
     sum += *i;
  }
  // Accumulate.
  sum = std::accumulate(x.begin(), x.end(), 0.)
  \endcode

  Of course, you can also work with the component arrays. There are 
  versions of \c size(), \c empty(), \c begin(), \c end(), \c rbegin(),
  and \c rend() that take the component array index as an argument and 
  return information about that array.
  \code
  array::PackedArrayOfArrays<double> x;
  ...
  // Make a vector of the array sizes.
  std::vector<std::size_t> sizes(x.numArrays());
  for (std::size_t i = 0; i != x.numArrays(); ++i) {
     sizes[i] = x.size(i);
  }
  // Calculate the sum of each array.
  std::vector<double> sums(x.numArrays());
  for (std::size_t i = 0; i != x.numArrays(); ++i) {
     sums[i] = std::accumulate(x.begin(i), x.end(i), 0.);
  }
  \endcode
  
  \c operator() with a single argument will give you the beginning of 
  the specified array. This \c x(i) is equivalent to \c x.begin(i).
  With two arguments it gives you the specified element of the
  specified array. That is, \c x(m,n) is the nth element of the mth array.

  Note that in inheriting from std::vector, the modifier functions
  \c assign(), \c insert(), and \c erase() have been disabled.
  This is because (for the sake of efficiency) PackedArrayOfArrays
  only supports changing the size of the last array.
  Use \c push_back() and pop_back() to append an element to the last array
  and remove the last element from the last array, respectively.
  \c pushArray() appends an empty array, while \c popArray() 
  erases the last array (which need not be empty).
*/
template<typename _T>
class PackedArrayOfArrays :
      public std::vector<_T> {
   //
   // Types.
   //
private:

   typedef std::vector<_T> Base;

   //
   // Data.
   //
private:

   //! Delimiters that determine the beginning and end of each %array.
   std::vector<typename Base::size_type> _delimiters;

   //
   // Disable the following functions from the base class:
   // assign(), insert(), and erase().
   //
private:
   template<typename _InputIterator>
   void
   assign(_InputIterator first, _InputIterator last);

   void
   assign(typename Base::size_type n, typename Base::const_reference x);

   typename Base::iterator
   insert(typename Base::iterator position, typename Base::const_reference x);

   void
   insert(typename Base::iterator position, typename Base::size_type n,
          typename Base::const_reference x);

   template<typename _InputIterator>
   void
   insert(typename Base::iterator position, _InputIterator first,
          _InputIterator last);

   typename Base::iterator
   erase(typename Base::iterator position);

   typename Base::iterator
   erase(typename Base::iterator first, typename Base::iterator last);

   //
   // Use from the base class.
   //
public:
   using Base::size;
   using Base::empty;
   using Base::begin;
   using Base::end;
   using Base::rbegin;
   using Base::rend;

public:

   //--------------------------------------------------------------------------
   /*! \name Constructors etc.
     Use the synthesized copy constructor, assignment operator, and destructor.
   */
   // @{

   //! Default constructor. Empty data structure.
   PackedArrayOfArrays() :
      Base(),
      _delimiters(1) {
      _delimiters[0] = 0;
   }

   //! Construct from a container of containers.
   template<typename _Container>
   PackedArrayOfArrays(const _Container& cOfC);

   //! Construct from the %array sizes and the values.
   template<typename SizeForwardIter, typename ValueForwardIter>
   PackedArrayOfArrays(SizeForwardIter sizesBeginning, SizeForwardIter sizesEnd,
                       ValueForwardIter valuesBeginning,
                       ValueForwardIter valuesEnd);

   //! Rebuild from the component %array sizes.
   template<typename SizeForwardIter>
   void
   rebuild(SizeForwardIter sizesBeginning, SizeForwardIter sizesEnd);

   //! Rebuild from the %array sizes and the values.
   template<typename SizeForwardIter, typename ValueForwardIter>
   void
   rebuild(SizeForwardIter sizesBeginning, SizeForwardIter sizesEnd,
           ValueForwardIter valuesBeginning, ValueForwardIter valuesEnd) {
      rebuild(sizesBeginning, sizesEnd);
      std::copy(valuesBeginning, valuesEnd, begin());
   }

   //! Swap with the argument.
   void
   swap(PackedArrayOfArrays& other) {
      Base::swap(other);
      _delimiters.swap(other._delimiters);
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name Accessors for the whole set of elements.
   // @{

   //! Return the number of arrays.
   typename Base::size_type
   numArrays() const {
      return _delimiters.size() - 1;
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name Accessors for individual arrays.
   // @{

   //! Return the number of elements in the n<sup>th</sup> %array.
   typename Base::size_type
   size(const typename Base::size_type n) const {
      return _delimiters[n + 1] - _delimiters[n];
   }

   //! Return true if the n<sup>th</sup> %array is empty.
   bool
   empty(const typename Base::size_type n) const {
      return size(n) == 0;
   }

   //! Return a const iterator to the first value in the n<sup>th</sup> %array.
   typename Base::const_iterator
   begin(const typename Base::size_type n) const {
      return Base::begin() + _delimiters[n];
   }

   //! Return a const iterator to one past the last value in the n<sup>th</sup> %array.
   typename Base::const_iterator
   end(const typename Base::size_type n) const {
      return Base::begin() + _delimiters[n + 1];
   }

   //! Return a const reverse iterator to the last value in the n<sup>th</sup> %array.
   typename Base::const_reverse_iterator
   rbegin(const typename Base::size_type n) const {
      return typename Base::const_reverse_iterator(end(n));
   }

   //! Return a const reverse iterator to one before the first value in the n<sup>th</sup> %array.
   typename Base::const_reverse_iterator
   rend(const typename Base::size_type n) const {
      return typename Base::const_reverse_iterator(begin(n));
   }

   //! Return a const iterator to the first element of the n<sup>th</sup> %array.
   typename Base::const_iterator
   operator()(const typename Base::size_type n) const {
      return begin(n);
   }

   //! Return the m<sup>th</sup> element of the n<sup>th</sup> %array.
   typename Base::const_reference
   operator()(const std::size_t n, const std::size_t m) const {
#ifdef DEBUG_stlib
      assert(m < size(n));
#endif
      return Base::operator[](_delimiters[n] + m);
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name Manipulators for the whole set of elements.
   // @{

   //! Clear the %array of arrays.
   void
   clear() {
      Base::clear();
      _delimiters.resize(1);
      _delimiters[0] = 0;
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name Manipulators for individual arrays.
   // @{

   //! Return an iterator to the first value in the n<sup>th</sup> %array.
   typename Base::iterator
   begin(const typename Base::size_type n) {
      return Base::begin() + _delimiters[n];
   }

   //! Return an iterator to one past the last value in the n<sup>th</sup> %array.
   typename Base::iterator
   end(const typename Base::size_type n) {
      return Base::begin() + _delimiters[n + 1];
   }

   //! Return an iterator to the first element of the n<sup>th</sup> %array.
   typename Base::iterator
   operator()(const typename Base::size_type n) {
      return begin(n);
   }

   //! Return the m<sup>th</sup> element of the n<sup>th</sup> %array.
   typename Base::reference
   operator()(const typename Base::size_type n, const typename Base::size_type m) {
#ifdef DEBUG_stlib
      assert(m < size(n));
#endif
      return Base::operator[](_delimiters[n] + m);
   }

   //! Append an empty array.
   void
   pushArray() {
      _delimiters.push_back(_delimiters.back());
   }

   //! Append an array with elements defined by the sequence.
   template<typename _InputIterator>
   void
   pushArray(_InputIterator begin, _InputIterator end) {
      pushArray();
      while (begin != end) {
         push_back(*begin++);
      }
   }

   //! Pop the last array.
   void
   popArray() {
      Base::erase(begin(numArrays() - 1), end(numArrays() - 1));
      _delimiters.pop_back();
   }

   //! Append an element to the last array.
   void
   push_back(typename Base::const_reference x) {
      Base::push_back(x);
      ++_delimiters.back();
   }

   //! Pop an element from the last array.
   void
   pop_back() {
      Base::pop_back();
      --_delimiters.back();
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name Equality.
   // @{

   //! Return true if the arrays are equal.
   bool
   operator==(const PackedArrayOfArrays& x) const {
      return static_cast<const Base&>(*this) == static_cast<const Base&>(x) &&
         _delimiters == x._delimiters;
   }

   //! Return true if the arrays are not equal.
   bool
   operator!=(const PackedArrayOfArrays& x) const {
      return ! operator==(x);
   }

   // @}
   //--------------------------------------------------------------------------
   //! \name File I/O.
   // @{

   //! Write to a file stream in ascii format.
   void
   put(std::ostream& out) const;

   //! Read from a file stream in ascii format.
   void
   get(std::istream& in);

   // @}
};

//
// File I/O.
//

//! Write a PackedArrayOfArrays in ascii format.
/*!
  \relates PackedArrayOfArrays

  Below is the file format.
  \verbatim
  number_of_arrays number_of_elements
  array_0_size
  array_0_value_0 array_0_value_1 ...
  array_1_size
  array_1_value_0 array_1_value_1 ...
  ... \endverbatim
*/
template<typename _T>
inline
std::ostream&
operator<<(std::ostream& out, const PackedArrayOfArrays<_T>& x) {
   x.put(out);
   return out;
}

//! Read a PackedArrayOfArrays in ascii format.
/*!
  \relates PackedArrayOfArrays

  Below is the file format.
  \verbatim
  number_of_arrays number_of_elements
  array_0_size
  array_0_value_0 array_0_value_1 ...
  array_1_size
  array_1_value_0 array_1_value_1 ...
  ... \endverbatim
*/
template<typename _T>
inline
std::istream&
operator>>(std::istream& in, PackedArrayOfArrays<_T>& x) {
   x.get(in);
   return in;
}

} // namespace array

#define __array_PackedArrayOfArrays_ipp__
#include "PackedArrayOfArrays.ipp"
#undef __array_PackedArrayOfArrays_ipp__

#endif
