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

/**
 *
 * 	@file ddss_test_dsyr2k.c
 *
 * 	@brief LASs-DDSs ddss_test_dsyr2k 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-02-21
 * 	@reviewer 
 * 	@modified 
 *
 **/

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

/**
 *
 *	@sa ddss_dsyr2k
 *	@sa kdsyr2k
 *
 **/

int main( int argc, char const *argv[] )
{
	
	if( argc < 5 || argc > 5 )
	{ 		
		fprintf( stderr, "Usage: ./test_dsyr2k N K TRANS UPLO\n" );
		return NoSuccess;
	}

	int i, j;
	int n, k;
	int ldA, kA, ldB, kB, ldC;
	enum DDSS_TRANS trans;
	enum DDSS_UPLO uplo;
	const char *trans_input = NULL;
	const char *uplo_input = NULL;
	double alpha;
	double beta;
	double *A;
	double *A_ref;
	double *A_test;
	double *B;
	double *B_ref;
	double *B_test;
	double *C;
	double *C_ref;
	double *C_test;
	double error;
	double max_error;
	double count_error;	
	struct timeval start, end;
	double time;
	double flops;
	double flops_ddss;
	double flops_ref;
	lapack_int retval;
	int seed[] = {0, 0, 0 , 1};
	enum LASS_RETURN ret;
	
	n = atoi( argv[1] );
	k = atoi( argv[2] );
	
	if ( strlen( argv[3] ) != 1 ) 
		{
			fprintf( stderr,"Illegal value of TRANS, TRANS can be T or N\n" );
			return NoSuccess;
		}
	trans_input = argv[3];
	
	
	if ( strlen( argv[4] ) != 1 ) 
			{
				fprintf( stderr,"Illegal value of UPLO, UPLO can be U or L\n" );
				return NoSuccess;
			}
	uplo_input = argv[4];
	
	// Checking input
	if ( n < 0 )
	{
		fprintf( stderr, "Illegal value of N, N must be >= 0\n" );
		return NoSuccess;
	}
	if ( k < 0 )
	{
		fprintf( stderr, "Illegal value of K, K must be >= 0\n" );
		return NoSuccess;
	}
	
	if ( trans_input[0] == 'T' )
	{
		trans = Trans;
		ldA = ldB = n;
		kA = kB = k;
	}
	else if ( trans_input[0] == 'N' )
	{
		trans = NoTrans;
		ldA = ldB = k;
		kA = kB = n;
	}
	else
	{
		fprintf( stderr, "Illegal value of TRANS, TRANS can be T or N\n" );
		return NoSuccess;
	}
	
	if ( uplo_input[0] == 'U' ) 
	{
		uplo = Upper;
	} 
	else if ( uplo_input[0] == 'L' ) 
	{
		uplo = Lower;
	} 
	else 
	{
		fprintf( stderr, "Illegal value of UPLO, UPLO can be U or L\n" );
		return NoSuccess;
	}
	
	ldC = n;
	max_error = 0.0;
	count_error = 0.0;
	
	// Allocating matrices
	A = ( double * ) malloc( sizeof( double ) * kA * ldA );
	A_ref = ( double * ) malloc( sizeof( double ) * kA * ldA );
	A_test = ( double * ) malloc( sizeof( double ) * kA * ldA );
	B = ( double * ) malloc( sizeof( double ) * kB * ldB );
	B_ref = ( double * ) malloc( sizeof( double ) * kB * ldB );
	B_test = ( double * ) malloc( sizeof( double ) * kB * ldB );
	C = ( double * ) malloc( sizeof( double ) * n * n );
	C_ref = ( double * ) malloc( sizeof( double ) * n * n );
	C_test = ( double * ) malloc( sizeof( double ) * n * n );

	
	// Initializing alpha and beta
    alpha = ( double ) rand() / ( double ) rand() + DBL_MIN;
	beta = ( double ) rand() / ( double ) rand() + DBL_MIN;
		
	// Matrices initialization
	retval = LAPACKE_dlarnv( 1, seed, kA * ldA, A );
	assert( retval == 0 );
	
	retval = LAPACKE_dlarnv( 1, seed, kB * ldB, B );
	assert( retval == 0 );
	
	retval = LAPACKE_dlarnv( 1, seed, n * n, C );
	assert(retval == 0);
		
	memcpy( A_ref, A, kA * ldA * sizeof( double ) );
	memcpy( A_test, A, kA * ldA * sizeof( double ) );
	
	memcpy( B_ref, B, kB * ldB * sizeof( double ) );
	memcpy( B_test, B, kB * ldB * sizeof( double ) );
	
	memcpy( C_ref, C, n * n * sizeof( double ) );
	memcpy( C_test, C, n * n * sizeof( double ) );
	
    // DSYR2K FLOPS
	flops = FLOPS_DSYR2K( k, n );
	
	gettimeofday( &start, NULL );
	
	cblas_dsyr2k( CblasRowMajor,
			     ( CBLAS_UPLO ) uplo, ( CBLAS_TRANSPOSE ) trans,
				 n,k,
				 alpha, A_ref, ldA,
				 	    B_ref, ldB,
				 beta,  C_ref, ldC );
	
	gettimeofday( &end, NULL );
	
	// FLOPS achieved by the (reference) cblas_dsyr2k routine
	flops_ref = ( double ) flops / ( ( ( end.tv_sec * 1e6 + end.tv_usec )
			    - ( start.tv_sec * 1e6 + start.tv_usec ) ) / 1e6 );
	
	ret = ddss_dsyr2k( uplo, trans,
					  n, k,
					  alpha, A_test, ldA,
					  	     B_test, ldB,
					  beta,  C_test, ldC);
		
	gettimeofday( &start, NULL );
	
	ret = ddss_dsyr2k( uplo, trans,
					  n, k,
					  alpha, A, ldA,
					  	     B, ldB,
					  beta,  C, ldC);
	
	gettimeofday( &end, NULL );
	if ( ret == NoSuccess )
		return ret;
	
	// TIME consimed by ddss_dsyr2k routine
	time = ( double ) ( ( ( end.tv_sec * 1e6 + end.tv_usec )
				 - ( start.tv_sec * 1e6 + start.tv_usec ) ) / 1e6 );
	
	// FLOPS achieved by the ddss_dsyr2k routine
	flops_ddss = flops / time;
	

	// Error computation
	for ( i = 0; i<n; i++ )
		{
			for ( j=0; j<n; j++)
			{
				error = fabs( C[i*n + j] - C_ref[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 / (n * n ) );

	fprintf( stdout, "DDSs_DSYR2K TIME(s) = %1.2f\n", time );
	fprintf( stdout, "DDSs_DSYR2K GFLOPS = %1.2f\n", flops_ddss / 1e9 );
	fprintf( stdout, "REF. DSYR2K GFLOPS = %1.2f\n", flops_ref / 1e9 );
	
	// --Matrices free--
	free( A );
	free( A_ref );
	free( A_test );
	free( C );
	free( C_ref );
	free( C_test );
	free( B );
	free( B_ref );
	free( B_test );
	
	return Success;

}
