// ---------------------------------------------------------------------
//
// Copyright (C) 2008 - 2022 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE.md at
// the top level directory of deal.II.
//
// ---------------------------------------------------------------------

#ifndef dealii_chunk_sparsity_pattern_h
#define dealii_chunk_sparsity_pattern_h


#include <deal.II/base/config.h>

#include <deal.II/base/exceptions.h>
#include <deal.II/base/subscriptor.h>

#include <deal.II/lac/sparsity_pattern.h>

#include <iostream>
#include <vector>

DEAL_II_NAMESPACE_OPEN


// Forward declaration
#ifndef DOXYGEN
template <typename>
class ChunkSparseMatrix;
#endif

/*! @addtogroup Sparsity
 *@{
 */



/**
 * Iterators on sparsity patterns
 */
namespace ChunkSparsityPatternIterators
{
  // forward declaration
  class Iterator;

  /**
   * Accessor class for iterators into sparsity patterns. This class is also
   * the base class for both const and non-const accessor classes into sparse
   * matrices.
   *
   * Note that this class only allows read access to elements, providing their
   * row and column number. It does not allow modifying the sparsity pattern
   * itself.
   */
  class Accessor
  {
  public:
    /**
     * Declare the type for container size.
     */
    using size_type = types::global_dof_index;

    /**
     * Constructor.
     */
    Accessor(const ChunkSparsityPattern *matrix, const size_type row);

    /**
     * Constructor. Construct the end accessor for the given sparsity pattern.
     */
    Accessor(const ChunkSparsityPattern *matrix);

    /**
     * Row number of the element represented by this object. This function can
     * only be called for entries for which is_valid_entry() is true.
     */
    size_type
    row() const;

    /**
     * Return the global index from the reduced sparsity pattern.
     */
    std::size_t
    reduced_index() const;

    /**
     * Column number of the element represented by this object. This function
     * can only be called for entries for which is_valid_entry() is true.
     */
    size_type
    column() const;

    /**
     * Return whether the sparsity pattern entry pointed to by this iterator
     * is valid or not. Note that after compressing the sparsity pattern, all
     * entries are valid. However, before compression, the sparsity pattern
     * allocated some memory to be used while still adding new nonzero
     * entries; if you create iterators in this phase of the sparsity
     * pattern's lifetime, you will iterate over elements that are not valid.
     * If this is so, then this function will return false.
     */
    bool
    is_valid_entry() const;


    /**
     * Comparison. True, if both iterators point to the same matrix position.
     */
    bool
    operator==(const Accessor &) const;


    /**
     * Comparison operator. Result is true if either the first row number is
     * smaller or if the row numbers are equal and the first index is smaller.
     *
     * This function is only valid if both iterators point into the same
     * sparsity pattern.
     */
    bool
    operator<(const Accessor &) const;

  protected:
    /**
     * The sparsity pattern we operate on accessed.
     */
    const ChunkSparsityPattern *sparsity_pattern;

    /**
     * The accessor of the (reduced) sparsity pattern.
     */
    SparsityPatternIterators::Accessor reduced_accessor;

    /**
     * Current chunk row number.
     */
    size_type chunk_row;

    /**
     * Current chunk col number.
     */
    size_type chunk_col;

    /**
     * Move the accessor to the next nonzero entry in the matrix.
     */
    void
    advance();

    // Grant access to iterator class.
    friend class Iterator;
  };



  /**
   * Iterator that walks over the elements of a sparsity pattern.
   */
  class Iterator
  {
  public:
    /**
     * Declare the type for container size.
     */
    using size_type = types::global_dof_index;

    /**
     * Constructor. Create an iterator into the sparsity pattern @p sp for the
     * given row and the index within it.
     */
    Iterator(const ChunkSparsityPattern *sp, const size_type row);

    /**
     * Prefix increment.
     */
    Iterator &
    operator++();

    /**
     * Postfix increment.
     */
    Iterator
    operator++(int);

    /**
     * Dereferencing operator.
     */
    const Accessor &
    operator*() const;

    /**
     * Dereferencing operator.
     */
    const Accessor *
    operator->() const;

    /**
     * Comparison. True, if both iterators point to the same matrix position.
     */
    bool
    operator==(const Iterator &) const;

    /**
     * Inverse of <tt>==</tt>.
     */
    bool
    operator!=(const Iterator &) const;

    /**
     * Comparison operator. Result is true if either the first row number is
     * smaller or if the row numbers are equal and the first index is smaller.
     *
     * This function is only valid if both iterators point into the same
     * matrix.
     */
    bool
    operator<(const Iterator &) const;

  private:
    /**
     * Store an object of the accessor class.
     */
    Accessor accessor;
  };
} // namespace ChunkSparsityPatternIterators



/**
 * Structure representing the sparsity pattern of a sparse matrix. This class
 * is an example of the "static" type of
 * @ref Sparsity.
 * It uses the compressed row storage (CSR) format to store data.
 *
 * The use of this class is demonstrated in step-51.
 */
class ChunkSparsityPattern : public Subscriptor
{
public:
  /**
   * Declare the type for container size.
   */
  using size_type = types::global_dof_index;
  /**
   * Typedef an iterator class that allows to walk over all nonzero elements
   * of a sparsity pattern.
   */
  using const_iterator = ChunkSparsityPatternIterators::Iterator;

  /**
   * Typedef an iterator class that allows to walk over all nonzero elements
   * of a sparsity pattern.
   *
   * Since the iterator does not allow to modify the sparsity pattern, this
   * type is the same as that for @p const_iterator.
   */
  using iterator = ChunkSparsityPatternIterators::Iterator;

  /**
   * Define a value which is used to indicate that a certain value in the
   * colnums array is unused, i.e. does not represent a certain column number
   * index.
   *
   * Indices with this invalid value are used to insert new entries to the
   * sparsity pattern using the add() member function, and are removed when
   * calling compress().
   *
   * You should not assume that the variable declared here has a certain
   * value. The initialization is given here only to enable the compiler to
   * perform some optimizations, but the actual value of the variable may
   * change over time.
   */
  static const size_type invalid_entry = SparsityPattern::invalid_entry;

  /**
   * Initialize the matrix empty, that is with no memory allocated. This is
   * useful if you want such objects as member variables in other classes. You
   * can make the structure usable by calling the reinit() function.
   */
  ChunkSparsityPattern();

  /**
   * Copy constructor. This constructor is only allowed to be called if the
   * matrix structure to be copied is empty. This is so in order to prevent
   * involuntary copies of objects for temporaries, which can use large
   * amounts of computing time. However, copy constructors are needed if one
   * wants to place a ChunkSparsityPattern in a container, e.g., to write such
   * statements like <tt>v.push_back (ChunkSparsityPattern());</tt>, with
   * <tt>v</tt> a vector of ChunkSparsityPattern objects.
   *
   * Usually, it is sufficient to use the explicit keyword to disallow
   * unwanted temporaries, but this does not work for <tt>std::vector</tt>.
   * Since copying a structure like this is not useful anyway because multiple
   * matrices can use the same sparsity structure, copies are only allowed for
   * empty objects, as described above.
   */
  ChunkSparsityPattern(const ChunkSparsityPattern &);

  /**
   * Initialize a rectangular matrix.
   *
   * @arg m number of rows @arg n number of columns @arg max_per_row maximum
   * number of nonzero entries per row
   */
  ChunkSparsityPattern(const size_type m,
                       const size_type n,
                       const size_type max_chunks_per_row,
                       const size_type chunk_size);

  /**
   * Initialize a rectangular matrix.
   *
   * @arg m number of rows @arg n number of columns @arg row_lengths possible
   * number of nonzero entries for each row.  This vector must have one entry
   * for each row.
   */
  ChunkSparsityPattern(const size_type               m,
                       const size_type               n,
                       const std::vector<size_type> &row_lengths,
                       const size_type               chunk_size);

  /**
   * Initialize a quadratic matrix of dimension <tt>n</tt> with at most
   * <tt>max_per_row</tt> nonzero entries per row.
   *
   * This constructor automatically enables optimized storage of diagonal
   * elements. To avoid this, use the constructor taking row and column
   * numbers separately.
   */
  ChunkSparsityPattern(const size_type n,
                       const size_type max_per_row,
                       const size_type chunk_size);

  /**
   * Initialize a quadratic matrix.
   *
   * @arg m number of rows and columns @arg row_lengths possible number of
   * nonzero entries for each row.  This vector must have one entry for each
   * row.
   */
  ChunkSparsityPattern(const size_type               m,
                       const std::vector<size_type> &row_lengths,
                       const size_type               chunk_size);

  /**
   * Destructor.
   */
  ~ChunkSparsityPattern() override = default;

  /**
   * Copy operator. For this the same holds as for the copy constructor: it is
   * declared, defined and fine to be called, but the latter only for empty
   * objects.
   */
  ChunkSparsityPattern &
  operator=(const ChunkSparsityPattern &);

  /**
   * Reallocate memory and set up data structures for a new matrix with <tt>m
   * </tt>rows and <tt>n</tt> columns, with at most <tt>max_per_row</tt>
   * nonzero entries per row.
   *
   * This function simply maps its operations to the other <tt>reinit</tt>
   * function.
   */
  void
  reinit(const size_type m,
         const size_type n,
         const size_type max_per_row,
         const size_type chunk_size);

  /**
   * Reallocate memory for a matrix of size <tt>m x n</tt>. The number of
   * entries for each row is taken from the array <tt>row_lengths</tt> which
   * has to give this number of each row <tt>i=1...m</tt>.
   *
   * If <tt>m*n==0</tt> all memory is freed, resulting in a total
   * reinitialization of the object. If it is nonzero, new memory is only
   * allocated if the new size extends the old one. This is done to save time
   * and to avoid fragmentation of the heap.
   *
   * If the number of rows equals the number of columns then diagonal elements
   * are stored first in each row to allow optimized access in relaxation
   * methods of SparseMatrix.
   */
  void
  reinit(const size_type               m,
         const size_type               n,
         const std::vector<size_type> &row_lengths,
         const size_type               chunk_size);

  /**
   * Same as above, but with an ArrayView argument instead.
   */
  void
  reinit(const size_type                   m,
         const size_type                   n,
         const ArrayView<const size_type> &row_lengths,
         const size_type                   chunk_size);

  /**
   * This function compresses the sparsity structure that this object
   * represents.  It does so by eliminating unused entries and sorting the
   * remaining ones to allow faster access by usage of binary search
   * algorithms. A special sorting scheme is used for the diagonal entry of
   * quadratic matrices, which is always the first entry of each row.
   *
   * The memory which is no more needed is released.
   *
   * SparseMatrix objects require the ChunkSparsityPattern objects they are
   * initialized with to be compressed, to reduce memory requirements.
   */
  void
  compress();

  /**
   * This function can be used as a replacement for reinit(), subsequent calls
   * to add() and a final call to close() if you know exactly in advance the
   * entries that will form the matrix sparsity pattern.
   *
   * The first two parameters determine the size of the matrix. For the two
   * last ones, note that a sparse matrix can be described by a sequence of
   * rows, each of which is represented by a sequence of pairs of column
   * indices and values. In the present context, the begin() and end()
   * parameters designate iterators (of forward iterator type) into a
   * container, one representing one row. The distance between begin() and
   * end() should therefore be equal to n_rows(). These iterators may be
   * iterators of <tt>std::vector</tt>, <tt>std::list</tt>, pointers into a
   * C-style array, or any other iterator satisfying the requirements of a
   * forward iterator. The objects pointed to by these iterators (i.e. what we
   * get after applying <tt>operator*</tt> or <tt>operator-></tt> to one of
   * these iterators) must be a container itself that provides functions
   * <tt>begin</tt> and <tt>end</tt> designating a range of iterators that
   * describe the contents of one line. Dereferencing these inner iterators
   * must either yield a pair of an integer as column index and a
   * value of arbitrary type (such a type would be used if we wanted to
   * describe a sparse matrix with one such object), or simply an integer (if we
   * only wanted to describe a sparsity pattern). The function is able to
   * determine itself whether an integer or a pair is what we get after
   * dereferencing the inner iterators, through some template magic.
   *
   * While the order of the outer iterators denotes the different rows of the
   * matrix, the order of the inner iterator denoting the columns does not
   * matter, as they are sorted internal to this function anyway.
   *
   * Since that all sounds very complicated, consider the following example
   * code, which may be used to fill a sparsity pattern:
   * @code
   * std::vector<std::vector<size_type> > column_indices (n_rows);
   * for (size_type row=0; row<n_rows; ++row)
   *         // generate necessary columns in this row
   *   fill_row (column_indices[row]);
   *
   * sparsity.copy_from (n_rows, n_cols,
   *                     column_indices.begin(),
   *                     column_indices.end());
   * @endcode
   *
   * Note that this example works since the iterators dereferenced yield
   * containers with functions <tt>begin</tt> and <tt>end</tt> (namely
   * <tt>std::vector</tt>s), and the inner iterators dereferenced yield
   * integers as column indices. Note that we could have replaced
   * each of the two <tt>std::vector</tt> occurrences by <tt>std::list</tt>,
   * and the inner one by <tt>std::set</tt> as well.
   *
   * Another example would be as follows, where we initialize a whole matrix,
   * not only a sparsity pattern:
   * @code
   * std::vector<std::map<size_type,double> > entries (n_rows);
   * for (size_type row=0; row<n_rows; ++row)
   *         // generate necessary pairs of columns
   *         // and corresponding values in this row
   *   fill_row (entries[row]);
   *
   * sparsity.copy_from (n_rows, n_cols,
   *                     column_indices.begin(),
   *                     column_indices.end());
   * matrix.reinit (sparsity);
   * matrix.copy_from (column_indices.begin(),
   *                   column_indices.end());
   * @endcode
   *
   * This example works because dereferencing iterators of the inner type
   * yields a pair of integers and a value, the first of which we
   * take as column index. As previously, the outer <tt>std::vector</tt> could
   * be replaced by <tt>std::list</tt>, and the inner
   * <tt>std::map<size_type,double></tt> could be replaced by
   * <tt>std::vector<std::pair<size_type,double> ></tt>, or a list or set of
   * such pairs, as they all return iterators that point to such pairs.
   */
  template <typename ForwardIterator>
  void
  copy_from(const size_type       n_rows,
            const size_type       n_cols,
            const ForwardIterator begin,
            const ForwardIterator end,
            const size_type       chunk_size);

  /**
   * Copy data from an object of type DynamicSparsityPattern. Previous content
   * of this object is lost, and the sparsity pattern is in compressed mode
   * afterwards.
   */
  template <typename SparsityPatternType>
  void
  copy_from(const SparsityPatternType &dsp, const size_type chunk_size);

  /**
   * Take a full matrix and use its nonzero entries to generate a sparse
   * matrix entry pattern for this object.
   *
   * Previous content of this object is lost, and the sparsity pattern is in
   * compressed mode afterwards.
   */
  template <typename number>
  void
  copy_from(const FullMatrix<number> &matrix, const size_type chunk_size);

  /**
   * Set the sparsity pattern of the chunk sparsity pattern to be given by
   * <tt>chunk_size*chunksize</tt> blocks of the sparsity pattern for chunks
   * specified. Note that the final number of rows <tt>m</tt> of the sparsity
   * pattern will be approximately <tt>sparsity_pattern_for_chunks.n_rows() *
   * chunk_size</tt> (modulo padding elements in the last chunk) and similarly
   * for the number of columns <tt>n</tt>.
   *
   * This is a special initialization option in case you can tell the position
   * of the chunk already from the beginning without generating the sparsity
   * pattern using <tt>make_sparsity_pattern</tt> calls. This bypasses the
   * search for chunks but of course needs to be handled with care in order to
   * give a correct sparsity pattern.
   *
   * Previous content of this object is lost, and the sparsity pattern is in
   * compressed mode afterwards.
   */
  template <typename Sparsity>
  void
  create_from(const size_type m,
              const size_type n,
              const Sparsity &sparsity_pattern_for_chunks,
              const size_type chunk_size,
              const bool      optimize_diagonal = true);

  /**
   * Return whether the object is empty. It is empty if no memory is
   * allocated, which is the same as that both dimensions are zero.
   */
  bool
  empty() const;

  /**
   * Return the chunk size given as argument when constructing this object.
   */
  size_type
  get_chunk_size() const;

  /**
   * Return the maximum number of entries per row. Before compression, this
   * equals the number given to the constructor, while after compression, it
   * equals the maximum number of entries actually allocated by the user.
   */
  size_type
  max_entries_per_row() const;

  /**
   * Add a nonzero entry to the matrix. This function may only be called for
   * non-compressed sparsity patterns.
   *
   * If the entry already exists, nothing bad happens.
   */
  void
  add(const size_type i, const size_type j);

  /**
   * Make the sparsity pattern symmetric by adding the sparsity pattern of the
   * transpose object.
   *
   * This function throws an exception if the sparsity pattern does not
   * represent a quadratic matrix.
   */
  void
  symmetrize();

  /**
   * Return number of rows of this matrix, which equals the dimension of the
   * image space.
   */
  inline size_type
  n_rows() const;

  /**
   * Return number of columns of this matrix, which equals the dimension of
   * the range space.
   */
  inline size_type
  n_cols() const;

  /**
   * Check if a value at a certain position may be non-zero.
   */
  bool
  exists(const size_type i, const size_type j) const;

  /**
   * Number of entries in a specific row.
   */
  size_type
  row_length(const size_type row) const;

  /**
   * Compute the bandwidth of the matrix represented by this structure. The
   * bandwidth is the maximum of $|i-j|$ for which the index pair $(i,j)$
   * represents a nonzero entry of the matrix. Consequently, the maximum
   * bandwidth a $n\times m$ matrix can have is $\max\{n-1,m-1\}$.
   */
  size_type
  bandwidth() const;

  /**
   * Return the number of nonzero elements of this matrix. Actually, it
   * returns the number of entries in the sparsity pattern; if any of the
   * entries should happen to be zero, it is counted anyway.
   *
   * This function may only be called if the matrix struct is compressed. It
   * does not make too much sense otherwise anyway.
   */
  size_type
  n_nonzero_elements() const;

  /**
   * Return whether the structure is compressed or not.
   */
  bool
  is_compressed() const;

  /**
   * Return whether this object stores only those entries that have been added
   * explicitly, or if the sparsity pattern contains elements that have been
   * added through other means (implicitly) while building it. For the current
   * class, the result is true if and only if it is square because it then
   * unconditionally stores the diagonal entries whether they have been added
   * explicitly or not.
   *
   * This function mainly serves the purpose of describing the current class
   * in cases where several kinds of sparsity patterns can be passed as
   * template arguments.
   */
  bool
  stores_only_added_elements() const;

  /**
   * Iterator starting at the first entry of the matrix. The resulting
   * iterator can be used to walk over all nonzero entries of the sparsity
   * pattern.
   */
  iterator
  begin() const;

  /**
   * Final iterator.
   */
  iterator
  end() const;

  /**
   * Iterator starting at the first entry of row <tt>r</tt>.
   *
   * Note that if the given row is empty, i.e. does not contain any nonzero
   * entries, then the iterator returned by this function equals
   * <tt>end(r)</tt>. Note also that the iterator may not be dereferenceable in
   * that case.
   */
  iterator
  begin(const size_type r) const;

  /**
   * Final iterator of row <tt>r</tt>. It points to the first element past the
   * end of line @p r, or past the end of the entire sparsity pattern.
   *
   * Note that the end iterator is not necessarily dereferenceable. This is in
   * particular the case if it is the end iterator for the last row of a
   * matrix.
   */
  iterator
  end(const size_type r) const;

  /**
   * Write the data of this object en bloc to a file. This is done in a binary
   * mode, so the output is neither readable by humans nor (probably) by other
   * computers using a different operating system of number format.
   *
   * The purpose of this function is that you can swap out matrices and
   * sparsity pattern if you are short of memory, want to communicate between
   * different programs, or allow objects to be persistent across different
   * runs of the program.
   */
  void
  block_write(std::ostream &out) const;

  /**
   * Read data that has previously been written by block_write() from a file.
   * This is done using the inverse operations to the above function, so it is
   * reasonably fast because the bitstream is not interpreted except for a few
   * numbers up front.
   *
   * The object is resized on this operation, and all previous contents are
   * lost.
   *
   * A primitive form of error checking is performed which will recognize the
   * bluntest attempts to interpret some data as a vector stored bitwise to a
   * file, but not more.
   */
  void
  block_read(std::istream &in);

  /**
   * Print the sparsity of the matrix. The output consists of one line per row
   * of the format <tt>[i,j1,j2,j3,...]</tt>. <i>i</i> is the row number and
   * <i>jn</i> are the allocated columns in this row.
   */
  void
  print(std::ostream &out) const;

  /**
   * Print the sparsity of the matrix in a format that <tt>gnuplot</tt>
   * understands and which can be used to plot the sparsity pattern in a
   * graphical way. The format consists of pairs <tt>i j</tt> of nonzero
   * elements, each representing one entry of this matrix, one per line of the
   * output file. Indices are counted from zero on, as usual. Since sparsity
   * patterns are printed in the same way as matrices are displayed, we print
   * the negative of the column index, which means that the <tt>(0,0)</tt>
   * element is in the top left rather than in the bottom left corner.
   *
   * Print the sparsity pattern in gnuplot by setting the data style to dots
   * or points and use the <tt>plot</tt> command.
   */
  void
  print_gnuplot(std::ostream &out) const;

  /**
   * Determine an estimate for the memory consumption (in bytes) of this
   * object. See MemoryConsumption.
   */
  std::size_t
  memory_consumption() const;

  /**
   * @addtogroup Exceptions
   * @{
   */
  /**
   * Exception
   */
  DeclException1(ExcInvalidNumber,
                 size_type,
                 << "The provided number is invalid here: " << arg1);
  /**
   * Exception
   */
  DeclException2(ExcInvalidIndex,
                 size_type,
                 size_type,
                 << "The given index " << arg1 << " should be less than "
                 << arg2 << '.');
  /**
   * Exception
   */
  DeclException2(ExcNotEnoughSpace,
                 size_type,
                 size_type,
                 << "Upon entering a new entry to row " << arg1
                 << ": there was no free entry any more. " << std::endl
                 << "(Maximum number of entries for this row: " << arg2
                 << "; maybe the matrix is already compressed?)");
  /**
   * The operation is only allowed after the SparsityPattern has been set up
   * and compress() was called.
   */
  DeclExceptionMsg(
    ExcNotCompressed,
    "The operation you attempted is only allowed after the SparsityPattern "
    "has been set up and compress() was called.");
  /**
   * This operation changes the structure of the SparsityPattern and is not
   * possible after compress() has been called.
   */
  DeclExceptionMsg(
    ExcMatrixIsCompressed,
    "The operation you attempted changes the structure of the SparsityPattern "
    "and is not possible after compress() has been called.");
  /**
   * Exception
   */
  DeclException0(ExcEmptyObject);
  /**
   * Exception
   */
  DeclException2(ExcIteratorRange,
                 size_type,
                 size_type,
                 << "The iterators denote a range of " << arg1
                 << " elements, but the given number of rows was " << arg2);
  /**
   * Exception
   */
  DeclException0(ExcMETISNotInstalled);
  /**
   * Exception
   */
  DeclException1(ExcInvalidNumberOfPartitions,
                 size_type,
                 << "The number of partitions you gave is " << arg1
                 << ", but must be greater than zero.");
  /**
   * Exception
   */
  DeclException2(ExcInvalidArraySize,
                 size_type,
                 size_type,
                 << "The array has size " << arg1 << " but should have size "
                 << arg2);
  //@}
private:
  /**
   * Number of rows that this sparsity structure shall represent.
   */
  size_type rows;

  /**
   * Number of columns that this sparsity structure shall represent.
   */
  size_type cols;

  /**
   * The size of chunks.
   */
  size_type chunk_size;

  /**
   * The reduced sparsity pattern. We store only which chunks exist, with each
   * chunk a block in the matrix of size chunk_size by chunk_size.
   */
  SparsityPattern sparsity_pattern;

  // Make all the chunk sparse matrix kinds friends.
  template <typename>
  friend class ChunkSparseMatrix;

  // Make the accessor class a friend.
  friend class ChunkSparsityPatternIterators::Accessor;
};


/*@}*/
/*---------------------- Inline functions -----------------------------------*/

#ifndef DOXYGEN

namespace ChunkSparsityPatternIterators
{
  inline Accessor::Accessor(const ChunkSparsityPattern *sparsity_pattern,
                            const size_type             row)
    : sparsity_pattern(sparsity_pattern)
    , reduced_accessor(row == sparsity_pattern->n_rows() ?
                         *sparsity_pattern->sparsity_pattern.end() :
                         *sparsity_pattern->sparsity_pattern.begin(
                           row / sparsity_pattern->get_chunk_size()))
    , chunk_row(row == sparsity_pattern->n_rows() ?
                  0 :
                  row % sparsity_pattern->get_chunk_size())
    , chunk_col(0)
  {}



  inline Accessor::Accessor(const ChunkSparsityPattern *sparsity_pattern)
    : sparsity_pattern(sparsity_pattern)
    , reduced_accessor(*sparsity_pattern->sparsity_pattern.end())
    , chunk_row(0)
    , chunk_col(0)
  {}



  inline bool
  Accessor::is_valid_entry() const
  {
    return reduced_accessor.is_valid_entry() &&
           sparsity_pattern->get_chunk_size() * reduced_accessor.row() +
               chunk_row <
             sparsity_pattern->n_rows() &&
           sparsity_pattern->get_chunk_size() * reduced_accessor.column() +
               chunk_col <
             sparsity_pattern->n_cols();
  }



  inline Accessor::size_type
  Accessor::row() const
  {
    Assert(is_valid_entry() == true, ExcInvalidIterator());

    return sparsity_pattern->get_chunk_size() * reduced_accessor.row() +
           chunk_row;
  }



  inline Accessor::size_type
  Accessor::column() const
  {
    Assert(is_valid_entry() == true, ExcInvalidIterator());

    return sparsity_pattern->get_chunk_size() * reduced_accessor.column() +
           chunk_col;
  }



  inline std::size_t
  Accessor::reduced_index() const
  {
    Assert(is_valid_entry() == true, ExcInvalidIterator());

    return reduced_accessor.linear_index;
  }



  inline bool
  Accessor::operator==(const Accessor &other) const
  {
    // no need to check for equality of sparsity patterns as this is done in
    // the reduced case already and every ChunkSparsityPattern has its own
    // reduced sparsity pattern
    return (reduced_accessor == other.reduced_accessor &&
            chunk_row == other.chunk_row && chunk_col == other.chunk_col);
  }



  inline bool
  Accessor::operator<(const Accessor &other) const
  {
    Assert(sparsity_pattern == other.sparsity_pattern, ExcInternalError());

    if (chunk_row != other.chunk_row)
      {
        if (reduced_accessor.linear_index ==
            reduced_accessor.container->n_nonzero_elements())
          return false;
        if (other.reduced_accessor.linear_index ==
            reduced_accessor.container->n_nonzero_elements())
          return true;

        const auto global_row = sparsity_pattern->get_chunk_size() *
                                  reduced_accessor.row() +
                                chunk_row,
                   other_global_row = sparsity_pattern->get_chunk_size() *
                                        other.reduced_accessor.row() +
                                      other.chunk_row;
        if (global_row < other_global_row)
          return true;
        else if (global_row > other_global_row)
          return false;
      }

    return (
      reduced_accessor.linear_index < other.reduced_accessor.linear_index ||
      (reduced_accessor.linear_index == other.reduced_accessor.linear_index &&
       chunk_col < other.chunk_col));
  }


  inline void
  Accessor::advance()
  {
    const auto chunk_size = sparsity_pattern->get_chunk_size();
    Assert(chunk_row < chunk_size && chunk_col < chunk_size,
           ExcIteratorPastEnd());
    Assert(reduced_accessor.row() * chunk_size + chunk_row <
               sparsity_pattern->n_rows() &&
             reduced_accessor.column() * chunk_size + chunk_col <
               sparsity_pattern->n_cols(),
           ExcIteratorPastEnd());
    if (chunk_size == 1)
      {
        reduced_accessor.advance();
        return;
      }

    ++chunk_col;

    // end of chunk
    if (chunk_col == chunk_size ||
        reduced_accessor.column() * chunk_size + chunk_col ==
          sparsity_pattern->n_cols())
      {
        const auto reduced_row = reduced_accessor.row();
        // end of row
        if (reduced_accessor.linear_index + 1 ==
            reduced_accessor.container->rowstart[reduced_row + 1])
          {
            ++chunk_row;

            chunk_col = 0;

            // end of chunk rows or end of matrix
            if (chunk_row == chunk_size ||
                (reduced_row * chunk_size + chunk_row ==
                 sparsity_pattern->n_rows()))
              {
                chunk_row = 0;
                reduced_accessor.advance();
              }
            // go back to the beginning of the same reduced row but with
            // chunk_row increased by one
            else
              reduced_accessor.linear_index =
                reduced_accessor.container->rowstart[reduced_row];
          }
        // advance within chunk
        else
          {
            reduced_accessor.advance();
            chunk_col = 0;
          }
      }
  }



  inline Iterator::Iterator(const ChunkSparsityPattern *sparsity_pattern,
                            const size_type             row)
    : accessor(sparsity_pattern, row)
  {}



  inline Iterator &
  Iterator::operator++()
  {
    accessor.advance();
    return *this;
  }



  inline Iterator
  Iterator::operator++(int)
  {
    const Iterator iter = *this;
    accessor.advance();
    return iter;
  }



  inline const Accessor &
  Iterator::operator*() const
  {
    return accessor;
  }



  inline const Accessor *
  Iterator::operator->() const
  {
    return &accessor;
  }


  inline bool
  Iterator::operator==(const Iterator &other) const
  {
    return (accessor == other.accessor);
  }



  inline bool
  Iterator::operator!=(const Iterator &other) const
  {
    return !(accessor == other.accessor);
  }


  inline bool
  Iterator::operator<(const Iterator &other) const
  {
    return accessor < other.accessor;
  }

} // namespace ChunkSparsityPatternIterators



inline ChunkSparsityPattern::iterator
ChunkSparsityPattern::begin() const
{
  return {this, 0};
}


inline ChunkSparsityPattern::iterator
ChunkSparsityPattern::end() const
{
  return {this, n_rows()};
}



inline ChunkSparsityPattern::iterator
ChunkSparsityPattern::begin(const size_type r) const
{
  AssertIndexRange(r, n_rows());
  return {this, r};
}



inline ChunkSparsityPattern::iterator
ChunkSparsityPattern::end(const size_type r) const
{
  AssertIndexRange(r, n_rows());
  return {this, r + 1};
}



inline ChunkSparsityPattern::size_type
ChunkSparsityPattern::n_rows() const
{
  return rows;
}


inline ChunkSparsityPattern::size_type
ChunkSparsityPattern::n_cols() const
{
  return cols;
}



inline ChunkSparsityPattern::size_type
ChunkSparsityPattern::get_chunk_size() const
{
  return chunk_size;
}



inline bool
ChunkSparsityPattern::is_compressed() const
{
  return sparsity_pattern.compressed;
}



template <typename ForwardIterator>
void
ChunkSparsityPattern::copy_from(const size_type       n_rows,
                                const size_type       n_cols,
                                const ForwardIterator begin,
                                const ForwardIterator end,
                                const size_type       chunk_size)
{
  Assert(static_cast<size_type>(std::distance(begin, end)) == n_rows,
         ExcIteratorRange(std::distance(begin, end), n_rows));

  // first determine row lengths for each row. if the matrix is quadratic,
  // then we might have to add an additional entry for the diagonal, if that
  // is not yet present. as we have to call compress anyway later on, don't
  // bother to check whether that diagonal entry is in a certain row or not
  const bool             is_square = (n_rows == n_cols);
  std::vector<size_type> row_lengths;
  row_lengths.reserve(n_rows);
  for (ForwardIterator i = begin; i != end; ++i)
    row_lengths.push_back(std::distance(i->begin(), i->end()) +
                          (is_square ? 1 : 0));
  reinit(n_rows, n_cols, row_lengths, chunk_size);

  // now enter all the elements into the matrix
  size_type row = 0;
  using inner_iterator =
    typename std::iterator_traits<ForwardIterator>::value_type::const_iterator;
  for (ForwardIterator i = begin; i != end; ++i, ++row)
    {
      const inner_iterator end_of_row = i->end();
      for (inner_iterator j = i->begin(); j != end_of_row; ++j)
        {
          const size_type col =
            internal::SparsityPatternTools::get_column_index_from_iterator(*j);
          Assert(col < n_cols, ExcInvalidIndex(col, n_cols));

          add(row, col);
        }
    }

  // finally compress everything. this also sorts the entries within each row
  compress();
}


#endif // DOXYGEN

DEAL_II_NAMESPACE_CLOSE

#endif
