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

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

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

/**
 *
 *  @sa ddss_dgtsv
 *  @sa kdgtsv
 *
 **/

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

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

	// Local variables
	int i;
	int n;
	double *DL;
	double *D;
	double *DU;
	double *RHS;
	double *DL_ref;
	double *D_ref;
	double *DU_ref;
	double *RHS_ref;
	double *DL_test;
	double *D_test;
	double *DU_test;
	double *RHS_test;
	double error;
	double max_error;
	double count_error;	
	struct timeval start, end;
	double time_ref;
	double time;
	double flops;
	double flops_dsss; 
	double flops_ref; 
	enum LASS_RETURN ret;

	n = atoi( argv[1] );

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

	max_error = 0.0;
	count_error = 0.0;
	
	// Vector allocation
	DL  = ( double * ) malloc( sizeof( double ) * n );
	D   = ( double * ) malloc( sizeof( double ) * n );
	DU  = ( double * ) malloc( sizeof( double ) * n );
	RHS = ( double * ) malloc( sizeof( double ) * n );
	
	DL_ref  = ( double * ) malloc( sizeof( double ) * n );
	D_ref   = ( double * ) malloc( sizeof( double ) * n );
	DU_ref  = ( double * ) malloc( sizeof( double ) * n );
	RHS_ref = ( double * ) malloc( sizeof( double ) * n );

	DL_test  = ( double * ) malloc( sizeof( double ) * n );
	D_test   = ( double * ) malloc( sizeof( double ) * n );
	DU_test  = ( double * ) malloc( sizeof( double ) * n );
	RHS_test = ( double * ) malloc( sizeof( double ) * n );
	
	// Make the matrix diagonal dominant 
	for ( i = 0; i < n; i++ )
	{
		 DL[i] = (double) ( rand() + DBL_MIN );
		 DU[i] = (double) ( rand() + DBL_MIN );
		  D[i] = 2 * fabs( DL[i] ) + fabs( DU[i] );
		RHS[i] = (double) ( rand() + DBL_MIN );
	}

	memcpy( DL_ref, DL, n * sizeof( double ) );
	memcpy( D_ref, D, n * sizeof( double ) );
	memcpy( DU_ref, DU, n * sizeof( double ) );
	memcpy( RHS_ref, RHS, n * sizeof( double ) );
	
	memcpy( DL_test, DL, n * sizeof( double ) );
	memcpy( D_test, D, n * sizeof( double ) );
	memcpy( DU_test, DU, n * sizeof( double ) );
	memcpy( RHS_test, RHS, n * sizeof( double ) );

	// DGEMM FLOPS
	flops = FLOPS_DGTSV( n ); 
	
	gettimeofday( &start, NULL );
	
	dgtsvseq( n, 
		DL_ref, D_ref, DU_ref,
        RHS_ref );
	
	gettimeofday( &end, NULL );
	
	// TIME consumed by dsss_dgtsv	
	time_ref = ( double ) (((end.tv_sec * 1e6 + end.tv_usec)
				 - (start.tv_sec * 1e6 + start.tv_usec)) / 1e6);
	
	// FLOPS achieved by the (reference) dgtsv routine
	flops_ref = (double) flops / time_ref;

	ret = dsss_dgtsv(n,
                DL_test, D_test, DU_test,
                RHS_test );
	
	if ( ret == NoSuccess )
        return ret;	
	
	gettimeofday( &start, NULL );
	
	ret = dsss_dgtsv(n,
                DL, D, DU,
                RHS );
	
	gettimeofday( &end, NULL );
	if ( ret == NoSuccess )
        return ret;	
	
	// TIME consumed by dsss_dgtsv	
	time = ( double ) (((end.tv_sec * 1e6 + end.tv_usec)
				 - (start.tv_sec * 1e6 + start.tv_usec)) / 1e6);
	
	// FLOPS achieved by the ddss_dgemm routine
	flops_dsss = flops / time;
	
	// Error computation
	for ( i = 0; i < n; i++ )
	{
		error = fabs( RHS_ref[i] - RHS[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 / n  );

	fprintf( stdout, "DDSs_DGTSV TIME(s) = %1.2f\n", time );
	fprintf( stdout, "REF. DGTSV TIME(s) = %1.2f\n", time_ref );
	fprintf( stdout, "DDSs_DGTSV GFLOPS = %1.2f\n", flops_dsss / 1e9 );
	fprintf( stdout, "REF. DGTSV GFLOPS = %1.2f\n", flops_ref / 1e9 );

	free( DL );
	free( D );
	free( DU );
	free( RHS );
	free( DL_test );
	free( D_test );
	free( DU_test );
	free( RHS_test );
	free( DL_ref );
	free( D_ref );
	free( DU_ref );
	free( RHS_ref );

	return Success;

}
