/*
 *  -- MAGMA (version 1.1) --
 *     Univ. of Tennessee, Knoxville
 *     Univ. of California, Berkeley
 *     Univ. of Colorado, Denver
 *     November 2011
 *
 * @generated d Fri Nov 22 14:21:31 2013
 * @author Chongxiao Cao
 *
 **/
// includes, system
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

// includes, project
#include "flops.h"
#include "magma.h"
#include "magma_lapack.h"
#include "testings.h"


/* ////////////////////////////////////////////////////////////////////////////
   -- Testing dsyrk
*/
int main( int argc, char** argv)
{
    /* Initialize */
    magma_queue_t  queue;
    magma_device_t device;
    int num = 0;
    magma_err_t err;
    magma_init();
    err = magma_get_devices( &device, 1, &num );
    if ( err != MAGMA_SUCCESS or num < 1 ) {
      fprintf( stderr, "magma_get_devices failed: %d\n", err );
      exit(-1);
    }
    err = magma_queue_create( device, &queue );
    if ( err != MAGMA_SUCCESS ) {
      fprintf( stderr, "magma_queue_create failed: %d\n", err );
      exit(-1);
    }

    real_Double_t   gflops, mic_perf, mic_time, cpu_perf, cpu_time;
    double          mic_error, Cnorm, work[1];
    magma_int_t N, K;
    magma_int_t Ak, An;
    magma_int_t sizeA, sizeC;
    magma_int_t lda, ldc, ldda, lddc;
    magma_int_t ione     = 1;
    magma_int_t ISEED[4] = {0,0,0,1};
    
    double *h_A, *h_C, *h_Cmic;
    double *d_A, *d_C;
    double c_neg_one = MAGMA_D_NEG_ONE;
    double alpha = MAGMA_D_MAKE(  0.29, -0.86 );
    double beta  = MAGMA_D_MAKE( -0.48,  0.38 );
    
    magma_opts opts;
    parse_opts( argc, argv, &opts );
    
    printf("    N     K   MIC Gflop/s (ms)   CPU Gflop/s (ms)  MIC error\n");
    printf("==================================================================\n");
    for( int i = 0; i < opts.ntest; ++i ) {
        for( int iter = 0; iter < opts.niter; ++iter ) {
            N = opts.nsize[i];
            K = opts.ksize[i];
            gflops = FLOPS_DSYRK(K, N) / 1e9;

            if ( opts.transA == MagmaNoTrans ) {
                lda = An = N;
                Ak = K;
            } else {
                lda = An = K;
                Ak = N;
            }
            
            ldc = N;
            
            ldda = ((lda+31)/32)*32;
            lddc = ((ldc+31)/32)*32;
            
            sizeA = lda*Ak;
            sizeC = ldc*N;
            
            TESTING_MALLOC_HOST( h_A,  double, lda*Ak );
            TESTING_MALLOC_HOST( h_C,  double, ldc*N  );
            TESTING_MALLOC_HOST( h_Cmic, double, ldc*N  );
            
            TESTING_MALLOC_DEV( d_A, double, ldda*Ak );
            TESTING_MALLOC_DEV( d_C, double, lddc*N  );
            
            /* Initialize the matrices */
            lapackf77_dlarnv( &ione, ISEED, &sizeA, h_A );
            lapackf77_dlarnv( &ione, ISEED, &sizeC, h_C );
            
            /* =====================================================================
               Performs operation using CUDA-BLAS
               =================================================================== */
            magma_dsetmatrix( An, Ak, h_A, 0, lda, d_A, 0, ldda, queue );
            magma_dsetmatrix( N, N, h_C, 0, ldc, d_C, 0, lddc, queue );
            
            magma_dsyrk( opts.uplo, opts.transA, N, K,
                         alpha, d_A, 0, ldda,
                         beta,  d_C, 0, lddc, queue );

            magma_dsetmatrix( N, N, h_C, 0, ldc, d_C, 0, lddc, queue );

            mic_time = magma_sync_wtime( NULL );
            magma_dsyrk( opts.uplo, opts.transA, N, K,
                         alpha, d_A, 0, ldda,
                         beta,  d_C, 0, lddc, queue );
            mic_time = magma_sync_wtime( NULL ) - mic_time;
            mic_perf = gflops / mic_time;
            
            magma_dgetmatrix( N, N, d_C, 0, lddc, h_Cmic, 0, ldc, queue );
            
            /* =====================================================================
               Performs operation using CPU BLAS
               =================================================================== */
	    if ( opts.check ) {
		    cpu_time = magma_wtime();
		    blasf77_dsyrk( lapack_const(opts.uplo), lapack_const(opts.transA), &N, &K,
				    &alpha, h_A, &lda,
				    &beta,  h_C, &ldc );
		    cpu_time = magma_wtime() - cpu_time;
		    cpu_perf = gflops / cpu_time;

		    /* =====================================================================
		       Check the result
		       =================================================================== */
		    // compute relative error for both magma & mic, relative to lapack,
		    // |C_magma - C_lapack| / |C_lapack|
		    //Cnorm = lapackf77_dlange( "M", &N, &N, h_C, &ldc, work );
		    Cnorm = lapackf77_dlansy( "M", lapack_const(opts.uplo), &N, h_C, &ldc, work );

		    blasf77_daxpy( &sizeC, &c_neg_one, h_C, &ione, h_Cmic, &ione );
		    //mic_error = lapackf77_dlange( "M", &N, &N, h_Cmic, &ldc, work ) / Cnorm;
		    mic_error = lapackf77_dlansy( "M", lapack_const(opts.uplo), &N, h_Cmic, &ldc, work ) / Cnorm;

		    printf("%5d %5d   %7.2f (%7.2f)   %7.2f (%7.2f)    %8.2e\n",
				    (int) N, (int) K,
				    mic_perf, 1000.*mic_time,
				    cpu_perf,    1000.*cpu_time,
				    mic_error );
	    }
	    else {
		    printf("%5d %5d   %7.2f (%7.2f)    ---   (  ---  )    ---\n",
				    (int) N, (int) K,
				    mic_perf, 1000.*mic_time);
	    }

	    TESTING_FREE_HOST( h_A  );
	    TESTING_FREE_HOST( h_C  );
	    TESTING_FREE_HOST( h_Cmic );

	    TESTING_FREE_DEV( d_A );
	    TESTING_FREE_DEV( d_C );
	}
	if ( opts.niter > 1 ) {
		printf( "\n" );
	}
    }

    magma_queue_destroy( queue );
    magma_finalize();

    return 0;
}
