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

/**
 *
 * 	@file ddss_test_dgesv.c
 *
 * 	@brief LASs-DDSs ddss_test_dgesv routine.
 *
 * 	LASs-DDSs is a software package provided by:
 * 	Barcelona Supercomputing Center - Centro Nacional de Supercomputacion
 *
 * 	@author Pedro Valero-Lara pedro.valero@bsc.es
 * 	@date 2018-07-26
 * 	@reviewer 
 * 	@modified 
 *
 **/

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

/**
 *
 *	@sa ddss_dnpgesv
 *	@sa kdnpgesv
 *
 **/ 

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

	if( argc < 3 || argc > 3 )
	{ 		
		fprintf( stderr, "Usage: ./test_dnpgesv N NRHS\n" );
		return NoSuccess;
	}
	
	// Local variables
	int i, j;
	int n, nrhs;
	double error;
	double max_error;
	double count_error;	
	double *A;
	double *A_ref;
	double *A_test;
	double *B;
	double *B_ref;
	double *B_test;
	struct timeval start, end;
	double time;
	double flops;
	double flops_ddss; 
	double flops_ref; 
	enum LASS_RETURN ret;
	lapack_int retval;
	int seed[] = {0, 0, 0 , 1};

	n = atoi( argv[1] );
	nrhs = atoi( argv[2] );
	
	max_error = 0.0;
	count_error = 0.0;

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

	// Matrices allocation
	A = ( double * ) malloc( sizeof( double ) * n * n );
	A_ref = ( double * ) malloc( sizeof( double ) * n * n );
	A_test = ( double * ) malloc( sizeof( double ) * n * n );
	B = ( double * ) malloc( sizeof( double ) * n * nrhs );
	B_ref = ( double * ) malloc( sizeof( double ) * n * nrhs );
	B_test = ( double * ) malloc( sizeof( double ) * n * nrhs );

	// Matrix A initialization
    retval = LAPACKE_dlarnv( 1, seed, n * n, A );
    assert( retval == 0 );
	
	// Matrix B initialization
    retval = LAPACKE_dlarnv( 1, seed, n * nrhs, B );
    assert( retval == 0 );
	
	memcpy( A_ref, A, n * n * sizeof( double ) );
	memcpy( A_test, A, n * n * sizeof( double ) );
	memcpy( B_ref, B, n * nrhs * sizeof( double ) );
	memcpy( B_test, B, n * nrhs * sizeof( double ) );

	// DGESV FLOPS
	flops = FLOPS_DGETRF( n, n ); 
	flops += 2 * FLOPS_DTRSM( Left, n, nrhs ); 
		
	gettimeofday( &start, NULL );

	#if defined(LASs_WITH_MKL)
	
    LAPACKE_mkl_dgetrfnpi( CblasRowMajor,
                           n, n,
                           n,
                           A_ref, n );

	#else
	
	dnpgetrf( n, n, A_ref, n );

	#endif

	cblas_dtrsm( CblasRowMajor,
				 ( CBLAS_SIDE ) Left, ( CBLAS_UPLO ) Lower,
				 ( CBLAS_TRANSPOSE ) NoTrans,
				 ( CBLAS_DIAG ) Unit,
				 n,
				 nrhs,
				 1.0, A_ref, n,
					  B_ref, nrhs );

	cblas_dtrsm( CblasRowMajor,
				 ( CBLAS_SIDE ) Left, ( CBLAS_UPLO ) Upper,
				 ( CBLAS_TRANSPOSE ) NoTrans,
				 ( CBLAS_DIAG ) NonUnit,
				 n,
				 nrhs,
				 1.0, A_ref, n,
					  B_ref, nrhs );

	gettimeofday( &end, NULL );
	
	// FLOPS achieved by the (reference) routine
	flops_ref = (double) flops / (((end.tv_sec * 1e6 + end.tv_usec)
				 - (start.tv_sec * 1e6 + start.tv_usec)) / 1e6);

	ret = ddss_dnpgesv( n, nrhs, 
					    A_test,    n, 
					    B_test, nrhs );
	
	gettimeofday( &start, NULL );
	
	ret = ddss_dnpgesv( n, nrhs, 
					    A,    n, 
					    B, nrhs );
	
	gettimeofday( &end, NULL );
	if ( ret == NoSuccess )
		return ret;

	// TIME consumed by ddss_dnpgesv	
	time = ( double ) (((end.tv_sec * 1e6 + end.tv_usec)
				- (start.tv_sec * 1e6 + start.tv_usec)) / 1e6);
	
	// FLOPS achieved by the ddss_dnpgesv routine
	flops_ddss = flops / time;

	// Error computation
	for ( i = 0; i < n; i++ )
	{
		for ( j = 0; j < nrhs; j++ )
		{
			error = fabs( B[ i * nrhs + j ] - B_ref[ i * nrhs + j ] );
			if ( max_error < 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 / ( n * n ) );
	
	fprintf( stdout, "DDSs_DNPGESV TIME(s) = %1.2f\n", time );
	fprintf( stdout, "DDSs_DNPGESV GFLOPS = %1.2f\n", flops_ddss / 1e9 );
	fprintf( stdout, "REF. DNPGESV GFLOPS = %1.2f\n", flops_ref / 1e9 );

	free( A );
	free( A_ref );
	free( A_test );
	free( B );
	free( B_ref );
	free( B_test );
	
	return Success;

}
