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

/**
 *
 * 	@file ddss_test_dsymm.c
 *
 * 	@brief LASs-DDSs ddss_test_dsymm 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 2017-08-11
 * 	@reviewer 
 * 	@modified 
 *
 **/

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

/**
 *
 *	@sa ddss_dsymm
 *	@sa kdsymm
 *
 **/ 

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

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

	
	// Local variables
	int i;
	int m, n;
	int ldA;
	const char *side_input = NULL;
	const char *uplo_input = NULL;
	enum DDSS_SIDE side;
	enum DDSS_UPLO uplo;
	double alpha; 
	double beta;
	double error;
	double max_error;
	double count_error;	
	double *A;
	double *B;
	double *C;
	double *A_test;
	double *B_test;
	double *C_test;
	double *A_ref;
	double *B_ref;
	double *C_ref;
	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};

	m = atoi( argv[1] );
	n = atoi( argv[2] );
	
	if ( strlen( argv[3] ) != 1 ) 
	{
		fprintf( stderr,"Illegal value of SIDE, SIDE can be L or R\n" );
		return NoSuccess;
	}
	side_input = argv[3];	
	
	if ( strlen( argv[4] ) != 1 ) 
	{
		fprintf( stderr,"Illegal value of UPLO, UPLO can be L or U\n" );
		return NoSuccess;
	}
	uplo_input = argv[4];	

	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;
	}

	if ( side_input[0] == 'L' )
	{
		side = Left;
		ldA = m; 
	}
	else if ( side_input[0] == 'R' )
	{
		side = Right;
		ldA = n; 
	}
	else
	{
		fprintf( stderr, "Illegal value of SIDE, SIDE can be L or R\n" );
		return NoSuccess;
	}
	
	if ( uplo_input[0] == 'L' )
	{
		uplo = Lower;
	}
	else if ( uplo_input[0] == 'U' )
	{
		uplo = Upper;
	}
	else
	{
		fprintf( stderr, "Illegal value of UPLO, UPLO can be L or U\n" );
		return NoSuccess;
	}

	// Matrices allocation
	A = ( double * ) malloc( sizeof( double ) * ldA * ldA );
	B = ( double * ) malloc( sizeof( double ) * m * n );
	C = ( double * ) malloc( sizeof( double ) * m * n );
	A_test = ( double * ) malloc( sizeof( double ) * ldA * ldA );
	B_test = ( double * ) malloc( sizeof( double ) * m * n );
	C_test = ( double * ) malloc( sizeof( double ) * m * n );
	A_ref = ( double * ) malloc( sizeof( double ) * ldA * ldA );
	B_ref = ( double * ) malloc( sizeof( double ) * m * n );
	C_ref = ( double * ) malloc( sizeof( double ) * m * n );

	// Alpha and beta initialization
	alpha = ( double ) rand() / (double) rand() + DBL_MIN;
	beta  = ( double ) rand() / (double) rand() + DBL_MIN;
 
	// Matrix A initialization
    retval = LAPACKE_dlarnv( 1, seed, ldA * ldA, A );
    assert( retval == 0 );
	
	// Matrix B initialization
    retval = LAPACKE_dlarnv( 1, seed, m * n, B );
    assert( retval == 0 );

	// Matrix C initialization
    retval = LAPACKE_dlarnv( 1, seed, m * n, C );
    assert( retval == 0 );

	// Matrix A_test & A_ref initialization
	memcpy( A_test, A, ldA * ldA * sizeof( double ) );
	memcpy( A_ref, A, ldA * ldA * sizeof( double ) );
	
	// Matrix B_test & B_ref initialization
	memcpy( B_test, B, m * n * sizeof( double ) );
	memcpy( B_ref, B, m * n * sizeof( double ) );
	
	// Matrix C_test & C_ref initialization
	memcpy( C_test, C, m * n * sizeof( double ) );
	memcpy( C_ref, C, m * n * sizeof( double ) );
	
	// DSYMM FLOPS
	flops = FLOPS_DSYMM( side, m, n ); 
	
	gettimeofday( &start, NULL );

	cblas_dsymm( CblasRowMajor, 
				 ( CBLAS_SIDE ) side,
				 ( CBLAS_UPLO ) uplo,
								m, n,
								alpha,  A_ref, ldA,
							 			B_ref, 	 n,
							 	beta,   C_ref,   n );

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

	ret = ddss_dsymm( side, uplo, 
				m, n,
                alpha, A_test, ldA,
                       B_test,   n,
                 beta, C_test,   n );
	
	gettimeofday( &start, NULL );
	
	ret = ddss_dsymm( side, uplo, 
				m, n,
                alpha, A, ldA,
                       B,   n,
                 beta, C,   n );
	
	gettimeofday( &end, NULL );
	if ( ret == NoSuccess )
		return ret;

	// TIME consumed by ddss_dsymm	
	time = ( double ) (((end.tv_sec * 1e6 + end.tv_usec)
				- (start.tv_sec * 1e6 + start.tv_usec)) / 1e6);
	
	// FLOPS achieved by the ddss_dsymm routine
	flops_ddss = flops / time;
		
	// Error computation
	for ( i = 0; i < m * n; i++ )
	{
		error = fabs( C[ i ] - C_ref[ i ] );
		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 / ( m * n ) );
	
	fprintf( stdout, "DDSs_DSYMM TIME(s) = %1.2f\n", time );
	fprintf( stdout, "DDSs_DSYMM GFLOPS = %1.2f\n", flops_ddss / 1e9 );
	fprintf( stdout, "REF. DSYMM GFLOPS = %1.2f\n", flops_ref / 1e9 );

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

}
