#ifndef _GROUPED_ITERATORS_H_
#define _GROUPED_ITERATORS_H_

#include <base/exceptions.h>

/**
 * This class basically keeps a group of iterators in sync.
 * Since there is no way to know on how many subproblems a given problem might depend on, then
 * we need to have a way to keep all the cell iterators in sync.
 * The <code>deal.II</code> library has a similar capability, namelty, the 
 * SynchronousIterators class template. However, it allows for a maximum of four (4)
 * grouped iteratos. A way around this is to parametrize the class with the number of
 * iterators.
 */
template<unsigned N, typename I> class IteratorGroup{
  public:
    /**
     * Does nothing.
     */
    IteratorGroup();
    /**
     * Copy the contents of each one of the iterators
     */
    IteratorGroup( const IteratorGroup<N,I> &g );
    // Assignment operator.
    /**
     * Copy operator
     */
    void operator=( const IteratorGroup<N,I> &g );
    /**
     * Does nothing
     */
    ~IteratorGroup();
    // Referencing
    /**
     * Access the content of the array of iterators
     */
    inline I & operator()( unsigned i );
    /**
     * Access the content of the array of iterators as const
     */
    inline const I & operator()( unsigned i ) const;
  protected:
    I tt[N]; ///< The actual iterators
};

template<unsigned N, typename I> IteratorGroup<N,I>::IteratorGroup(){}

template<unsigned N, typename I> IteratorGroup<N,I>::IteratorGroup( const IteratorGroup<N,I> &g ){
  for( unsigned i=0; i<N; ++i )
    tt[i] = g.tt[i];
}

template<unsigned N, typename I> IteratorGroup<N,I>::~IteratorGroup(){}

template<unsigned N, typename I> inline I& IteratorGroup<N,I>::operator()( unsigned i ){
  Assert( i<N, ExcIndexRange( 0, N, i ) );
  return tt[i];
}

template<unsigned N, typename I> inline const I& IteratorGroup<N,I>::operator()( unsigned i ) const{
  Assert( i<N, ExcIndexRange( 0, N, i ) );
  return tt[i];
}

template<unsigned N, typename I> inline void IteratorGroup<N,I>::operator=( const IteratorGroup<N,I> &g ){
  for( unsigned i=0; i<N; ++i )
    tt[i] = g.tt[i];
}

/** @relates IteratorGroup
 * Compares only the first iterators. After all they're supposed to be in sync all the time
 */
template<unsigned N, typename I> inline bool operator<( const IteratorGroup<N,I> &a,
                                                        const IteratorGroup<N,I> &b ){
  return ( a(0) < b(0) );
}

/** @relates IteratorGroup
 * Returns the distance between two iterators
 */
template<unsigned N, typename I> inline std::size_t operator-( const IteratorGroup<N,I> &a,
                                                               const IteratorGroup<N,I> &b ){
  std::size_t dist = std::distance( b(0), a(0) );
  Assert( dist >= 0, ExcInternalError() );
  return dist;
}

/** @relates IteratorGroup
  * advances all the iterators by n
  */
template<unsigned N, typename I> inline void advance( IteratorGroup<N,I> &t, const unsigned n ){
  for( unsigned i=0; i<N; ++i )
    std::advance ( t(i), n );
}

/** @relates IteratorGroup
 * advances all the iterators by one
 */
template<unsigned N, typename I> inline void advance_by_one( IteratorGroup<N,I> &t ){
  for( unsigned i=0; i<N; ++i )
    ++t(i);
}

/** @relates IteratorGroup
 * maps to advance(a,n)
 */
template<unsigned N, typename I> inline IteratorGroup<N,I> operator+( const IteratorGroup<N,I> &a,
                                                                      const std::size_t n ){
  IteratorGroup<N,I> x( a );
  advance( x, n );
  return x;
}

/** @relates IteratorGroup
 * advances by one the argument
 */
template<unsigned N, typename I> inline IteratorGroup<N,I> operator++( IteratorGroup<N,I> &a ){
  advance_by_one( a );
  return a;
}

/** @relates IteratorGroup
 * comparison operator. Again, we only compare the first component
 */
template<unsigned N, typename I> inline bool operator!=( const IteratorGroup<N,I> &a,
                                                         const IteratorGroup<N,I> &b ){
  return ( a(0) != b(0) );
}

#endif // GROUPED_ITERATORS_H_
