/*
 *  -- MAGMA (version 1.1) --
 *     Univ. of Tennessee, Knoxville
 *     Univ. of California, Berkeley
 *     Univ. of Colorado, Denver
 *     April 2012
 *
 * @generated c Fri Nov 22 14:21:34 2013
 *
 **/
// 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"


float get_LU_error(magma_int_t M, magma_int_t N,
                    magmaFloatComplex *A,  magma_int_t lda,
                    magmaFloatComplex *LU, magma_int_t *IPIV)
{
    magma_int_t min_mn = min(M,N);
    magma_int_t ione   = 1;
    magma_int_t i, j;
    magmaFloatComplex alpha = MAGMA_C_ONE;
    magmaFloatComplex beta  = MAGMA_C_ZERO;
    magmaFloatComplex *L, *U;
    float work[1], matnorm, residual;
    
    TESTING_MALLOC( L, magmaFloatComplex, M*min_mn);
    TESTING_MALLOC( U, magmaFloatComplex, min_mn*N);
    memset( L, 0, M*min_mn*sizeof(magmaFloatComplex) );
    memset( U, 0, min_mn*N*sizeof(magmaFloatComplex) );

    lapackf77_claswp( &N, A, &lda, &ione, &min_mn, IPIV, &ione);
    lapackf77_clacpy( MagmaLowerStr, &M, &min_mn, LU, &lda, L, &M      );
    lapackf77_clacpy( MagmaUpperStr, &min_mn, &N, LU, &lda, U, &min_mn );

    for(j=0; j<min_mn; j++)
        L[j+j*M] = MAGMA_C_MAKE( 1., 0. );
    
    matnorm = lapackf77_clange("f", &M, &N, A, &lda, work);

    blasf77_cgemm("N", "N", &M, &N, &min_mn,
                  &alpha, L, &M, U, &min_mn, &beta, LU, &lda);

    for( j = 0; j < N; j++ ) {
        for( i = 0; i < M; i++ ) {
            LU[i+j*lda] = MAGMA_C_SUB( LU[i+j*lda], A[i+j*lda] );
        }
    }
    residual = lapackf77_clange("f", &M, &N, LU, &lda, work);

    TESTING_FREE(L);
    TESTING_FREE(U);

    return residual / (matnorm * N);
}

/* ////////////////////////////////////////////////////////////////////////////
   -- Testing cgetrf
*/
int main( int argc, char** argv)
{
    /* Initialize */
    magma_queue_t  queue;
    magma_device_t device;
    magma_int_t 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;
    float          error;
    magmaFloatComplex *h_A, *h_R;
    magmaFloatComplex *d_A;
    magma_int_t     *ipiv;
    magma_int_t M, N, n2, lda, ldda, info, min_mn;
    magma_int_t ione     = 1;
    magma_int_t ISEED[4] = {0,0,0,1};

    magma_opts opts;
    parse_opts( argc, argv, &opts );
    
    printf("  M     N     CPU GFlop/s (sec)   MAGMA GFlop/s (sec)   ||PA-LU||/(||A||*N)\n");
    printf("=========================================================================\n");
    for( int i = 0; i < opts.ntest; ++i ) {
        for( int iter = 0; iter < opts.niter; ++iter ) {
            M = opts.msize[i];
            N = opts.nsize[i];
            min_mn = min(M, N);
            lda    = M;
            n2     = lda*N;
            ldda   = ((M+31)/32)*32;
            gflops = FLOPS_CGETRF( M, N ) / 1e9;
            
            TESTING_MALLOC_HOST(    ipiv, magma_int_t,     min_mn );
            TESTING_MALLOC_HOST(    h_A,  magmaFloatComplex, n2     );
            TESTING_MALLOC_HOST( h_R,  magmaFloatComplex, n2     );
            TESTING_MALLOC_DEV (  d_A,  magmaFloatComplex, ldda*N );
            
            /* Initialize the matrix */
            if ( opts.check ) {
                lapackf77_clarnv( &ione, ISEED, &n2, h_A );
                lapackf77_clacpy( MagmaUpperLowerStr, &M, &N, h_A, &lda, h_R, &lda );
            }
            else
                lapackf77_clarnv( &ione, ISEED, &n2, h_R );
            
            /* =====================================================================
               Performs operation using LAPACK
               =================================================================== */
            if ( opts.check ) {
                cpu_time = magma_wtime();
                lapackf77_cgetrf(&M, &N, h_A, &lda, ipiv, &info);
                cpu_time = magma_wtime() - cpu_time;
                cpu_perf = gflops / cpu_time;
                if (info != 0)
                    printf("lapackf77_cgetrf returned error %d.\n", (int) info);
            }
            
            /* ====================================================================
               Performs operation using MAGMA
               =================================================================== */
            magma_csetmatrix( M, N, h_R, 0, lda, d_A, 0, ldda, queue );
            magma_cgetrf_mic( M, N, d_A, 0, ldda, ipiv, &info, queue);
            
            magma_csetmatrix( M, N, h_R, 0, lda, d_A, 0, ldda, queue );
	    mic_time = magma_wtime();
            magma_cgetrf_mic( M, N, d_A, 0, ldda, ipiv, &info, queue);
            mic_time = magma_wtime() - mic_time;
            mic_perf = gflops / mic_time;
            if (info != 0)
                printf("magma_cgetrf_mic returned error %d.\n", (int) info);
            
            /* =====================================================================
               Check the factorization
               =================================================================== */
            if ( opts.check ) {
                magma_cgetmatrix( M, N, d_A, 0, ldda, h_A, 0, lda, queue );
                error = get_LU_error(M, N, h_R, lda, h_A, ipiv);
                
                printf("%5d %5d   %7.2f (%7.2f)   %7.2f (%7.2f)   %8.2e\n",
                       (int) M, (int) N, cpu_perf, cpu_time, mic_perf, mic_time, error);
            }
            else {
                printf("%5d %5d     ---   (  ---  )   %7.2f (%7.2f)     ---  \n",
                       (int) M, (int) N, mic_perf, mic_time);
            }

	    fflush(0);
            
            TESTING_FREE_HOST( ipiv );
            TESTING_FREE_HOST( h_A );
            TESTING_FREE_HOST( h_R );
            TESTING_FREE_DEV ( d_A );
        }
        if ( opts.niter > 1 ) {
            printf( "\n" );
        }
    }
    
    /* Shutdown */
    magma_queue_destroy( queue );
    magma_finalize();

    return 0;
}
