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

/**
 *
 * 	@file ddss_test_dtpgetrf.c
 *
 * 	@brief LASs-DDSs ddss_test_dtpgetrf routine.
 *
 * 	LASs-DDSs is a software package provided by:
 * 	Barcelona Supercomputing Center - Centro Nacional de Supercomputacion
 *
 * 	@author Pedro Valero-Lara pedro.valero@bsc.es
 * 	@author Boro Sofranac boro.sofranac@bsc.es
 * 	@date 2018-04-08
 * 	@reviewer 
 * 	@modified 2018-05-08
 *
 **/

/**
 *  
 *	@ingroup DDSs
 *   
 *  main:
 *	Performs the testing of the ddss_dtpgetrf routine. 
 *
**/

/**
 *
 *	@sa ddss_dtpgetrf
 *	@sa kdtpgetrf
 *
 **/

int main( int argc, char const *argv[] )
{

	if ( argc < 3 || argc > 3 )
	{ 		
		fprintf(stderr, "Usage: ./test_dtpgetrf M N\n" );
		return NoSuccess;
	}

	// Local variables
	int i, j;
	int m, n;
	int k;
	int ldA;
	double error;
	double max_error;
	double count_error;	
	double *A;
	double *A_ref;
	double *A_test;
	double *A_warmup;
	double *L;
	double *U;
	struct timeval start, end;
	double time;
	double flops;
	double flops_ref;
	double flops_ddss;
	enum LASS_RETURN ret;
	lapack_int retval;
	int *ipiv;
	int *ipiv_ref;
	int *ipiv_warmup;
	int seed[] = {0, 0, 0 , 1};

	m = atoi( argv[1] );
	n = atoi( argv[2] );
	
	// Row mayor order
	ldA = n; 
	
	max_error = 0.0;
	count_error = 0.0;

	// Checking inputs
	if ( m < 0 )
	{
		fprintf(stderr, "Illegal value of M, M must be >= 0\n");
		return NoSuccess;
	}
	if ( n < 0 )
	{
		fprintf(stderr, "Illegal value of N, N must be >= 0\n");
		return NoSuccess;
	}
	
    k = MIN(m,n);

	// Matrices allocation
	A = ( double * ) malloc( sizeof( double ) * m * n );
	A_ref = ( double * ) malloc( sizeof( double ) * m * n );
	A_test = ( double * ) malloc( sizeof( double ) * m * n );
	A_warmup = ( double * ) malloc( sizeof( double ) * m * n );
	L = ( double * ) calloc(  m * k, sizeof( double ) );
	U = ( double * ) calloc(  k * n, sizeof( double ) );
	ipiv = ( int* ) malloc( k * sizeof( int ) );
	ipiv_ref = ( int* ) malloc( k * sizeof( int ) );
	ipiv_warmup = ( int* ) malloc( k * sizeof( int ) );

	// Matrix A initialization
    retval = LAPACKE_dlarnv( 1, seed, m * n, A );
    assert( retval == 0 );
   
	// Copy matrices for testing purpose
	memcpy( A_ref, A, m * n * sizeof( double ) );
    memcpy( A_test, A, m * n * sizeof( double ) );
    memcpy( A_warmup, A, m * n * sizeof( double ) );
			
	// DGETRF FLOPS
	flops = FLOPS_DGETRF( m, n );
	
	gettimeofday( &start, NULL );
	
	LAPACKE_dgetrf( CblasRowMajor,
					m, n, 
					A_ref, ldA, 
					ipiv_ref );
	
	gettimeofday( &end, NULL );
	
	flops_ref = ( double ) flops / ( ( ( end.tv_sec * 1e6 + end.tv_usec )
			    - ( start.tv_sec * 1e6 + start.tv_usec ) ) / 1e6 );
	
	ret = ddss_dtpgetrf( m, n,
					     A_warmup, ldA,
					     ipiv_warmup );
	
	gettimeofday( &start, NULL );
	
	ret = ddss_dtpgetrf( m, n,
					     A, ldA,
					     ipiv );
	
	gettimeofday( &end, NULL );
	if ( ret == NoSuccess )
		return ret;
	
	// Execution time
	time = ( double ) ( ( ( end.tv_sec * 1e6 + end.tv_usec ) 
			- ( start.tv_sec * 1e6 + start.tv_usec ) ) / 1e6 );

	// FLOPS achieved by the ddss_dgetrf routine
	flops_ddss = flops / time;

	// Copy results from A to L and U
	for ( i = 0; i < m; i++ )
	{
		for( j = 0; j < n; j++ )
		{
			if ( i > j )
			{
				L[i * k + j] = A[i * n + j];
			}
			else if ( i < j )
			{
				U[i * n + j] = A[i * n + j];
			}
			else if( i == j )
			{
				L[i * k + j] = 1.0;
				U[i * n + j] = A[i * n + j];
			}
		}
	}
		
	// Multiply A = L * U
	ddss_dgemm( CblasNoTrans, CblasNoTrans,
				m, n, k,
				1.0, L, k,
					 U, n,
				0.0, A, n );
	
	// Swap rows
	LAPACKE_dlaswp( CblasRowMajor,
				    n, A, ldA,
					1, k,
					ipiv, -1 );
		
	// Error computation
	for ( int i = 0; i < m; i++ ) 
	{
		for ( int j = 0; j < n; j++ ) 
		{
			error = fabs( A[i * n + j] - A_test[i * n + j] );
			if ( error > max_error )
				max_error = error;
			count_error += error;
		}
	}
	
	fprintf( stdout, "Max. error = %1.2f\n", max_error );
	fprintf( stdout, "Av. error = %1.2f\n", count_error / ( m * n ) );
	
	fprintf( stdout, "DDSs_DTPGETRF TIME(s) = %1.2f\n", time );
	fprintf( stdout, "DDSs_DTPGETRF GFLOPS = %1.2f\n", flops_ddss / 1e9 );
	fprintf( stdout, "REF. DGETRF GFLOPS = %1.2f\n", flops_ref / 1e9 );
	
	free( A );
	free( A_ref );
	free( A_test );
	free( A_warmup );
	free( U );
	free( L );
	free( ipiv );
	free( ipiv_ref );
	free( ipiv_warmup );

	return Success;

}
