#include "../include/lass.h"

/**
 *
 * 	@file ddss_tiled2flat.c
 *
 * 	@brief LASs-DDSs ddss_tiled2flat routines.
 *
 * 	LASs-DDSs is a software package provided by:
 * 	Barcelona Supercomputing Center - Centro Nacional de Supercomputacion
 *
 * 	@author Pedro Valero-Lara pedro.valero@bsc.es
 * 	@date 2017-05-10
 * 	@reviewer 
 * 	@modified 
 *
 **/

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dtiled2flat:
 *	Performs the change of the data layout from tile layout to flat layout 
 *  according to row-major order.
 *
**/

/**
 * 
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	MT		int.	
 *	        MT specifies the number of rows of the matrix TILE_A.
 *			
 * 	@param[in]
 *	NT		int.
 *  		NT specifies the number of columns of the matrix TILE_A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 **/ 

/**
 *
 *	@sa ddss_dscatter_tile
 *	@sa ddss_tile_size
 *
 **/ 

void ddss_dtiled2flat( int M, int N, double *A, int LDA, int MT, int NT, 
		double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE] )
{

	// Local variables
	int m, n;

  	for ( m = 0; m < MT; m++ )
	{
		for ( n = 0; n < NT; n++ )
		{
			#pragma oss task inout(TILE_A[m][n]) \
			label( dtiled2flat)
			{
				ddss_dscatter_tile( M, N, 
					&A[m * TILE_SIZE * N + n * TILE_SIZE], LDA, 
					TILE_A[m][n], m, n );
			}
		}
	}

	#pragma oss taskwait

}

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dtiled2flat_nb:
 *	Performs the change of the data layout from tile layout to flat layout 
 *  according to row-major order in a non-blocking execution mode.
 *
**/

/**
 * 
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	MT		int.	
 *	        MT specifies the number of rows of the matrix TILE_A.
 *			
 * 	@param[in]
 *	NT		int.
 *  		NT specifies the number of columns of the matrix TILE_A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 **/ 

/**
 *
 *	@sa ddss_dscatter_tile
 *	@sa ddss_tile_size
 *
 **/ 

void ddss_dtiled2flat_nb( int M, int N, double *A, int LDA, int MT, int NT, 
		double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE] )
{

	// Local variables
	int m, n;

  	for ( m = 0; m < MT; m++ )
	{
		for ( n = 0; n < NT; n++ )
		{
			#pragma oss task inout(TILE_A[m][n]) \
			label( dtiled2flat)
			{
				ddss_dscatter_tile( M, N, 
					&A[m * TILE_SIZE * N + n * TILE_SIZE], LDA, 
					TILE_A[m][n], m, n );
			}
		}
	}

}

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dtiled2flat_trailing:
 *	Performs the change of the data layout from tile layout to flat layout 
 *  according to row-major order. Converts tiled sub-matrix from positions
 *  TILE_A[MSI:MEI][NSI:NEI] to submatrix into flat layout
 *
**/

/**
 * 
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	MT		int.	
 *	        MT specifies the number of rows of the matrix TILE_A.
 *			
 * 	@param[in]
 *	NT		int.
 *  		NT specifies the number of columns of the matrix TILE_A.
 *
 * 	@param[in]
 *	MSI		int.
 *  		MSI specifies the row-index of the starting tile in the matrix 
 *			TILE_A.
 *
 * 	@param[in]
 *	NSI		int.
 *  		NSI specifies the column-index of the starting tile in the matrix 
 *			TILE_A.
 *
 * 	@param[in]
 *	MEI		int.
 *  		MEI specifies the row-index of the last tile in the matrix TILE_A.
 *
 * 	@param[in]
 *	NEI		int.
 *  		NEI specifies the column-index of the last tile in the matrix 
 *			TILE_A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 **/ 

/**
 *
 *	@sa ddss_dscatter_tile_trailing
 *	@sa ddss_tile_size
 *
 **/ 


void ddss_dtiled2flat_trailing( int M, int N, double *A,
		 int LDA, int MT, int NT, int MSI, int NSI, int MEI, int NEI,
		double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE] )
{

	// Local variables
	int m, n;

  	for ( m = MSI; m < MEI; m++ )
	{
		for ( n = NSI; n < NEI; n++ )
		{
			#pragma oss task inout(TILE_A[m][n]) \
			label( dtiled2flat_v2)
			{
				ddss_dscatter_tile_trailing( M, N, 
					&A[(m-MSI) * TILE_SIZE * LDA + (n-NSI) * TILE_SIZE], LDA, 
					TILE_A[m][n], m-MSI, n-NSI );
			}
		}
	}

	#pragma oss taskwait

}

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dtiled2flat_trailing:
 *	Performs the change of the data layout from tile layout to flat layout 
 *  according to row-major order in a non-blocking execution mode. 
 *  Converts tiled sub-matrix from positions TILE_A[MSI:MEI][NSI:NEI] 
 *  to submatrix into flat layout
 *
**/

/**
 * 
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	MT		int.	
 *	        MT specifies the number of rows of the matrix TILE_A.
 *			
 * 	@param[in]
 *	NT		int.
 *  		NT specifies the number of columns of the matrix TILE_A.
 *
 * 	@param[in]
 *	MSI		int.
 *  		MSI specifies the row-index of the starting tile in the matrix 
 *			TILE_A.
 *
 * 	@param[in]
 *	NSI		int.
 *  		NSI specifies the column-index of the starting tile in the matrix 
 *			TILE_A.
 *
 * 	@param[in]
 *	MEI		int.
 *  		MEI specifies the row-index of the last tile in the matrix TILE_A.
 *
 * 	@param[in]
 *	NEI		int.
 *  		NEI specifies the column-index of the last tile in the matrix 
* 			TILE_A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 **/ 

/**
 *
 *	@sa ddss_dscatter_tile_trailing
 *	@sa ddss_tile_size
 *
 **/ 

void ddss_dtiled2flat_trailing_nb( int M, int N, double *A,
		int LDA, int MT, int NT, int MSI, int NSI, int MEI, int NEI,
		double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE], int prio )
{

	// Local variables
	int m, n;

  	//for ( m = starti; m < MT; m++ )
  	for ( m = MSI; m < MEI; m++ )
	{
		//for ( n = startj; n < NT; n++ )
		for ( n = NSI; n < NEI; n++ )
		{
			#pragma oss task inout(TILE_A[m][n]) \
					priority( prio ) \
					label( dtiled2flat_v2)
			{
				ddss_dscatter_tile_trailing( M, N, 
					&A[(m-MSI) * TILE_SIZE * LDA + (n-NSI) * TILE_SIZE], LDA, 
					TILE_A[m][n], m-MSI, n-NSI );
			}
		}
	}
}


/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dsymtiled2flat:
 *	Performs the change of the data layout from tile layout to flat layout 
 *  for symmetric matrices according to row-major order.
 *
**/

/**
 * 
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	MT		int.	
 *	        MT specifies the number of rows of the matrix TILE_A.
 *			
 * 	@param[in]
 *	NT		int.
 *  		NT specifies the number of columns of the matrix TILE_A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 * 
 * 	@param[in]
 *	UPLO	enum DDSS_UPLO.
 *	        UPLO specifies the form of A is stored:
 *       	- Lower: Lower triangle of A is stored. The upper traingular part is
 *       	not referenced.
 *      	- Upper: Upper triangle of A is stored. The lower triangular part is
 *      	not referenced.
 *
 **/ 

/**
 *
 *	@sa ddss_dscatter_tile
 *	@sa ddss_tile_size
 *
 **/ 

void ddss_dsymtiled2flat( int M, int N, double *A, int LDA, int MT, int NT, 
		double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE], enum DDSS_UPLO UPLO )
{

	// Local variables
	int m, n;
	
	if ( UPLO == Upper )
	{
  		for ( m = 0; m < MT; m++ )
		{
			for ( n = NT-1; n >= m; n-- )
			{
				#pragma oss task inout(TILE_A[m][n]) \
				label( dsymmtiled2flat )
				{ 
					ddss_dscatter_tile( M, N, 
						&A[m * TILE_SIZE * N + n * TILE_SIZE], LDA, 
						TILE_A[m][n], m, n );
				}
			}
		}
	}	
	else if ( UPLO == Lower )
	{
  		for ( m = 0; m < MT; m++ )
		{
			for ( n = 0; n <= m; n++ )
			{
				#pragma oss task inout(TILE_A[m][n]) \
				label( dsymmtiled2flat )
				{ 
					ddss_dscatter_tile( M, N, 
						&A[m * TILE_SIZE * N + n * TILE_SIZE], LDA, 
						TILE_A[m][n], m, n );
				}
			}
		}
	}		

	#pragma oss taskwait	
}

/**
 *  
 *  @ingroup DDSs
 *   
 *  ddss_dsymtiled2flat_nb:
 *  Performs the change of the data layout from tile layout to flat layout 
 *  for symmetric matrices according to row-major order in a non-blocking 
 *  execution mode.
 *
**/

/**
 * 
 *  @param[in]
 *  M       int.    
 *          M specifies the number of rows of the flat matrix.
 * 
 *  @param[in]
 *  N       int.    
 *          N specifies the number of columns of the flat matrix.
 *  
 *  @param[in, out]
 *  A       double *.   
 *          A is a pointer to the flat matrix.
 *
 *  @param[in]
 *  LDA     int.
 *          LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 *  @param[in]
 *  MT      int.    
 *          MT specifies the number of rows of the matrix TILE_A.
 *          
 *  @param[in]
 *  NT      int.
 *          NT specifies the number of columns of the matrix TILE_A.
 *
 *  @param[in]
 *  TILE_A  double *.
 *          TILE_A is a pointer to the tile matrix.
 * 
 *  @param[in]
 *  UPLO    enum DDSS_UPLO.
 *          UPLO specifies the form of A is stored:
 *          - Lower: Lower triangle of A is stored. The upper traingular part is
 *          not referenced.
 *          - Upper: Upper triangle of A is stored. The lower triangular part is
 *          not referenced.
 *
 **/

/**
 *
 *  @sa ddss_dscatter_tile
 *  @sa ddss_tile_size
 *
 **/

void ddss_dsymtiled2flat_nb( int M, int N, double *A, int LDA, int MT, int NT,
        double (*TILE_A)[NT][TILE_SIZE * TILE_SIZE], enum DDSS_UPLO UPLO )
{

    // Local variables
    int m, n;

    if ( UPLO == Upper )
    {
        for ( m = 0; m < MT; m++ )
        {
            for ( n = NT-1; n >= m; n-- )
            {
                #pragma oss task inout(TILE_A[m][n]) \
                label( dsymmtiled2flat_nb )
                {
                    ddss_dscatter_tile( M, N,
                        &A[m * TILE_SIZE * N + n * TILE_SIZE], LDA,
                        TILE_A[m][n], m, n );
                }
            }
        }
    }
    else if ( UPLO == Lower )
    {
        for ( m = 0; m < MT; m++ )
        {
            for ( n = 0; n <= m; n++ )
            {
                #pragma oss task inout(TILE_A[m][n]) \
                label( dsymmtiled2flat_nb )
                {
                    ddss_dscatter_tile( M, N,
                        &A[m * TILE_SIZE * N + n * TILE_SIZE], LDA,
                        TILE_A[m][n], m, n );
                }
            }
        }
    }

}

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dscatter_tile:
 *	Performs the copy of a tile from the tile matrix TILE_A to the flat matrix A
 * 	for the MT, NT tile.
 *
**/

/**
 *
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *			
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 * 	@param[in]
 *	MID		int.
 *  		MID specifies the row id of the tile.
 *
 * 	@param[in]
 *	NID		int.
 *  		NID specifies the column id of the tile. 
 **/

/**
 *
 *	@sa ddss_tile_size
 *	@sa ddss_dtiled2flat
 *
 **/ 

void ddss_dscatter_tile( int M, int N, double *A, int LDA, 
		double *TILE_A, int MID, int NID )
{

	//Local variables
	int i, j;
	int tile_size_m, tile_size_n;
	 
	tile_size_m = ddss_tile_size( M, MID );
	tile_size_n = ddss_tile_size( N, NID );

	for ( i = 0; i < tile_size_m; i++ )
	{
		for ( j = 0; j < tile_size_n; j++ )
	 	{
	 		A[i * LDA + j] = TILE_A[i * tile_size_n + j];
	 	}
	}

}

/**
 *  
 *	@ingroup DDSs
 *   
 *  ddss_dscatter_tile_trailing:
 *	Performs the copy of a tile from the tile matrix TILE_A to the flat matrix A
 * 	for the MT, NT tile.
 * 	
 * 	The difference from ddss_dscatter_tile is only in the last line in 
 * 	which the elements on TILE_A are access using (improperly) tile_size_n 
 * 	instead of TILE_SIZE.
 * 	
 * 	The tile_size_n may vary from case to case and thus result in accesing wrong
 * 	element or even out-of-bound.
 *
**/

/**
 *
 * 	@param[in]
 *	M		int.	
 *	        M specifies the number of rows of the flat matrix.
 * 
 * 	@param[in]
 *	N		int.	
 *	        N specifies the number of columns of the flat matrix.
 *	
 * 	@param[in, out]
 *	A		double *.	
 *	        A is a pointer to the flat matrix.
 *			
 * 	@param[in]
 *	LDA		int.
 *  		LDA specifies the number of columns ( row-major order ) of matrix A.
 *
 * 	@param[in]
 *	TILE_A	double *.
 *  		TILE_A is a pointer to the tile matrix.
 *
 * 	@param[in]
 *	MID		int.
 *  		MID specifies the row id of the tile.
 *
 * 	@param[in]
 *	NID		int.
 *  		NID specifies the column id of the tile. 
 **/

/**
 *
 *	@sa ddss_tile_size
 *	@sa ddss_dtiled2flat
 *
 **/ 
void ddss_dscatter_tile_trailing( int M, int N, double *A, int LDA, 
		double *TILE_A, int MID, int NID )
{

	//Local variables
	int i, j;
	int tile_size_m, tile_size_n;
	 
	tile_size_m = ddss_tile_size( M, MID );
	tile_size_n = ddss_tile_size( N, NID );

	for ( i = 0; i < tile_size_m; i++ )
	{
		for ( j = 0; j < tile_size_n; j++ )
	 	{
	 		A[i * LDA + j] = TILE_A[i * TILE_SIZE + j];
	 	}
	}

}
