/***************************************************************************
                          MatrixWrapping.h -  description
                             -------------------
    begin                : May 3, 2021
    copyright            : (C) 2021 by Tomas Oberhuber
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

#pragma once

#include <TNL/Algorithms/Segments/CSR.h>
#include <TNL/Algorithms/Segments/Ellpack.h>
#include <TNL/Matrices/SparseMatrixView.h>
#include <TNL/Matrices/DenseMatrixView.h>

namespace TNL {
namespace Matrices {

/**
 * \brief Function for wrapping an array of values into a dense matrix view.
 *
 * \tparam Device is a device on which the array is allocated.
 * \tparam Real is a type of array elements.
 * \tparam Index is a type for indexing of matrix elements.
 * \tparam Organization is matrix elements organization - see \ref TNL::Algorithms::Segments::ElementsOrganization.
 * \param rows is a number of matrix rows.
 * \param columns is a number of matrix columns.
 * \param values is the array with matrix elements values.
 * \return instance of DenseMatrixView wrapping the array.
 *
 * The array size must be equal to product of `rows` and `columns`. The dense matrix view does not deallocate the input
 * array at the end of its lifespan.
 *
 * \par Example
 * \include Matrices/DenseMatrix/DenseMatrixViewExample_wrap.cpp
 * \par Output
 * \include DenseMatrixViewExample_wrap.out
 */
template< typename Device,
          typename Real,
          typename Index,
          ElementsOrganization Organization = Algorithms::Segments::DefaultElementsOrganization< Device >::getOrganization() >
DenseMatrixView< Real, Device, Index, Organization >
wrapDenseMatrix( const Index& rows, const Index& columns, Real* values )
{
   using MatrixView = DenseMatrixView< Real, Device, Index, Organization >;
   using ValuesViewType = typename MatrixView::ValuesViewType;
   return MatrixView( rows, columns, ValuesViewType( values, rows * columns ) );
}

/**
 * \brief Function for wrapping of arrays defining [CSR format](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)) into a sparse matrix view.
 *
 * \tparam Device  is a device on which the arrays are allocated.
 * \tparam Real is a type of matrix elements values.
 * \tparam Index is a type for matrix elements indexing.
 * \param rows is a number of matrix rows.
 * \param columns is a number of matrix columns.
 * \param rowPointers is an array holding row pointers of the CSR format ( `ROW_INDEX` [here](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)))
 * \param values is an array with values of matrix elements ( `V` [here](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)))
 * \param columnIndexes is an array with column indexes of matrix elements  ( `COL_INDEX` [here](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)))
 * \return instance of SparseMatrixView with CSR format.
 *
 * The size of array \e rowPointers must be equal to number of `rows + 1`. The last element of the array equals to the number of all nonzero matrix elements. The sizes of arrays `values` and
 * `columnIndexes` must be equal to this number.
 *
 * \par Example
 * \include Matrices/SparseMatrix/SparseMatrixViewExample_wrapCSR.cpp
 * \par Output
 * \include SparseMatrixViewExample_wrapCSR.out
 */
template< typename Device,
          typename Real,
          typename Index >
SparseMatrixView< Real, Device, Index, GeneralMatrix, Algorithms::Segments::CSRViewDefault >
wrapCSRMatrix( const Index& rows, const Index& columns, Index* rowPointers, Real* values, Index* columnIndexes )
{
   using MatrixView = SparseMatrixView< Real, Device, Index, GeneralMatrix, Algorithms::Segments::CSRViewDefault >;
   using ValuesViewType = typename MatrixView::ValuesViewType;
   using ColumnIndexesView = typename MatrixView::ColumnsIndexesViewType;
   using SegmentsView = typename MatrixView::SegmentsViewType;
   using KernelView = typename SegmentsView::KernelView;
   using RowPointersView = typename SegmentsView::OffsetsView;
   RowPointersView rowPointersView( rowPointers, rows + 1 );
   Index elementsCount = rowPointersView.getElement( rows );
   SegmentsView segments( rowPointersView, KernelView() );
   ValuesViewType valuesView( values, elementsCount );
   ColumnIndexesView columnIndexesView( columnIndexes, elementsCount );
   return MatrixView( rows, columns, valuesView, columnIndexesView, segments );
}

/// This is to prevent from appearing in Doxygen documentation.
/// \cond HIDDEN_CLASS
template< typename Device,
          ElementsOrganization Organization,
          typename Real,
          typename Index,
          int Alignment = 1 >
struct EllpackMatrixWrapper
{
   template< typename Device_, typename Index_ >
   using EllpackSegments = Algorithms::Segments::EllpackView< Device_, Index_, Organization, Alignment >;
   using MatrixView = SparseMatrixView< Real, Device, Index, GeneralMatrix, EllpackSegments >;

   static MatrixView wrap( const Index& rows, const Index& columns, const Index& nonzerosPerRow, Real* values, Index* columnIndexes )
   {
      using ValuesViewType = typename MatrixView::ValuesViewType;
      using ColumnIndexesView = typename MatrixView::ColumnsIndexesViewType;
      using SegmentsView = Algorithms::Segments::EllpackView< Device, Index, Organization, Alignment >;
      SegmentsView segments( rows, nonzerosPerRow );
      Index elementsCount = segments.getStorageSize();
      ValuesViewType valuesView( values, elementsCount );
      ColumnIndexesView columnIndexesView( columnIndexes, elementsCount );
      return MatrixView( rows, columns, valuesView, columnIndexesView, segments );
   }
};
/// \endcond

/**
 * \brief Function for wrapping of arrays defining [Ellpack format](https://people.math.sc.edu/Burkardt/data/sparse_ellpack/sparse_ellpack.html) into a sparse matrix view.
 *
 * \tparam Device  is a device on which the arrays are allocated.
 * \tparam Real is a type of matrix elements values.
 * \tparam Index is a type for matrix elements indexing.
 * \tparam Alignment defines alignment of data. The number of matrix rows is rounded to a multiple of this number. It it usefull mainly for GPUs.
 * \param rows is a number of matrix rows.
 * \param columns is a number of matrix columns.
 * \param nonzerosPerRow is number of nonzero matrix elements in each row.
 * \param values is an array with values of matrix elements.
 * \param columnIndexes is an array with column indexes of matrix elements.
 * \return instance of SparseMatrixView with CSR format.
 *
 *  The sizes of arrays `values` and `columnIndexes` must be equal to `rows * nonzerosPerRow`. Use `-1` as a column index for padding zeros.
 *
 * \par Example
 * \include Matrices/SparseMatrix/SparseMatrixViewExample_wrapEllpack.cpp
 * \par Output
 * \include SparseMatrixViewExample_wrapEllpack.out
 */
template< typename Device,
          ElementsOrganization Organization,
          typename Real,
          typename Index,
          int Alignment = 1 >
auto
wrapEllpackMatrix( const Index rows, const Index columns, const Index nonzerosPerRow, Real* values, Index* columnIndexes )
-> decltype( EllpackMatrixWrapper< Device, Organization, Real, Index, Alignment >::wrap( rows, columns, nonzerosPerRow, values, columnIndexes ) )
{
   return EllpackMatrixWrapper< Device, Organization, Real, Index, Alignment >::wrap( rows, columns, nonzerosPerRow, values, columnIndexes );
}

   } //namespace Matrices
} //namepsace TNL
