/*
    -- MAGMA (version 1.1) --
       Univ. of Tennessee, Knoxville
       Univ. of California, Berkeley
       Univ. of Colorado, Denver
       April 2012

       @precisions normal z -> s d c

*/

// 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 "magma_types.h"
#include "testings.h"

#define PRECISION_z

/* ////////////////////////////////////////////////////////////////////////////
   -- Testing zgebrd
*/
int main( int argc, char** argv)
{
    /* Initialize */
    magma_queue_t  queue;
    magma_device_t device[ MagmaMaxDevices ];
    int num = 0;
    magma_err_t err;

    magma_init();

    err = magma_get_devices( device, MagmaMaxDevices, &num );
    if ( err != 0 || num < 1 ) {
      fprintf( stderr, "magma_get_devices failed: %d\n", err );
      exit(-1);
    }
    err = magma_queue_create( device[0], &queue );
    if ( err != 0 ) {
      fprintf( stderr, "magma_queue_create failed: %d\n", err );
      exit(-1);
    }

    real_Double_t    gflops, mic_perf, mic_time, cpu_perf, cpu_time;
    magmaDoubleComplex *h_A, *h_Q, *h_PT, *h_work, *chkwork;
    magmaDoubleComplex *taup, *tauq;
    #if defined(PRECISION_z) || defined(PRECISION_c)
    double      *rwork;
    #endif
    double      *diag, *offdiag;
    double      eps, result[3] = {0., 0., 0.};
    magma_int_t M, N, n2, lda, lhwork, lchkwork, info, minmn, nb;
    magma_int_t ione     = 1;
    magma_int_t ISEED[4] = {0,0,0,1};

    eps = lapackf77_dlamch( "E" );

    magma_opts opts;
    parse_opts( argc, argv, &opts );
    
    printf("    M     N   CPU GFlop/s (sec)   MAGMA GFlop/s (sec)   |A-QBP'|/N|A|  |I-QQ'|/N  |I-PP'|/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];
            minmn  = min(M, N);
            nb     = magma_get_zgebrd_nb(N);
            lda    = M;
            n2     = lda*N;
            lhwork = (M + N)*nb;
            gflops = FLOPS_ZGEBRD( M, N ) / 1e9;

            TESTING_MALLOC_HOST( h_A,     magmaDoubleComplex, lda*N );
            TESTING_MALLOC_HOST( tauq,    magmaDoubleComplex, minmn  );
            TESTING_MALLOC_HOST( taup,    magmaDoubleComplex, minmn  );
            TESTING_MALLOC_HOST( diag,    double, minmn   );
            TESTING_MALLOC_HOST( offdiag, double, (minmn-1) );
            TESTING_MALLOC_HOST( h_Q, magmaDoubleComplex, lda*N );
            TESTING_MALLOC_HOST( h_work, magmaDoubleComplex, lhwork );
            
            /* Initialize the matrices */
            lapackf77_zlarnv( &ione, ISEED, &n2, h_A );
            lapackf77_zlacpy( MagmaUpperLowerStr, &M, &N, h_A, &lda, h_Q, &lda );
            
            /* ====================================================================
               Performs operation using MAGMA
               =================================================================== */
            mic_time = magma_wtime();
            magma_zgebrd( M, N, h_Q, lda,
                          diag, offdiag, tauq, taup,
                          h_work, lhwork, &info, queue);
            mic_time = magma_wtime() - mic_time;
            mic_perf = gflops / mic_time;
            if (info != 0)
                printf("magma_zgebrd returned error %d.\n", (int) info);
            
            /* =====================================================================
               Check the factorization
               =================================================================== */
            if ( opts.check ) {
                lchkwork = max( minmn * nb, M+N );
                /* For optimal performance in zunt01 */
                lchkwork = max( lchkwork, minmn*minmn );
                TESTING_MALLOC_HOST( h_PT,    magmaDoubleComplex, lda*N   );
                TESTING_MALLOC_HOST( chkwork, magmaDoubleComplex, lchkwork );
                #if defined(PRECISION_z) || defined(PRECISION_c)
                TESTING_MALLOC_HOST( rwork, double, 5*minmn );
                #endif

                lapackf77_zlacpy(MagmaUpperLowerStr, &M, &N, h_Q, &lda, h_PT, &lda);
                
                // generate Q & P'
                lapackf77_zungbr("Q", &M, &minmn, &N, h_Q,  &lda, tauq, chkwork, &lchkwork, &info);
                if (info != 0)
                    printf("lapackf77_zungbr returned error %d.\n", (int) info);
                lapackf77_zungbr("P", &minmn, &N, &M, h_PT, &lda, taup, chkwork, &lchkwork, &info);
                if (info != 0)
                    printf("lapackf77_zungbr (2) returned error %d.\n", (int) info);
                
                // Test 1:  Check the decomposition A := Q * B * PT
                //      2:  Check the orthogonality of Q
                //      3:  Check the orthogonality of PT
                #if defined(PRECISION_z) || defined(PRECISION_c)
                lapackf77_zbdt01(&M, &N, &ione,
                                 h_A, &lda, h_Q, &lda,
                                 diag, offdiag, h_PT, &lda,
                                 chkwork, rwork, &result[0]);
		//checking failed for these two functions -- kk->031513
                //lapackf77_zunt01("Columns", &M, &minmn, h_Q,  &lda, chkwork, &lchkwork, rwork, &result[1]);
                //lapackf77_zunt01("Rows",    &minmn, &N, h_PT, &lda, chkwork, &lchkwork, rwork, &result[2]);
                #else
                lapackf77_zbdt01(&M, &N, &ione,
                                 h_A, &lda, h_Q, &lda,
                                 diag, offdiag, h_PT, &lda,
                                 chkwork, &result[0]);
                lapackf77_zunt01("Columns", &M, &minmn, h_Q,  &lda, chkwork, &lchkwork, &result[1]);
                lapackf77_zunt01("Rows",    &minmn, &N, h_PT, &lda, chkwork, &lchkwork, &result[2]);
                #endif
                
                TESTING_FREE_HOST( h_PT );
                TESTING_FREE_HOST( chkwork );
                #if defined(PRECISION_z) || defined(PRECISION_c)
                TESTING_FREE_HOST( rwork );
                #endif
            }
            
            /* =====================================================================
               Performs operation using LAPACK
               =================================================================== */
            if ( opts.lapack ) {
                cpu_time = magma_wtime();
                lapackf77_zgebrd(&M, &N, h_A, &lda,
                                 diag, offdiag, tauq, taup,
                                 h_work, &lhwork, &info);
                cpu_time = magma_wtime() - cpu_time;
                cpu_perf = gflops / cpu_time;
                if (info != 0)
                    printf("lapackf77_zgebrd returned error %d.\n", (int) info);
            }
            
            /* =====================================================================
               Print performance and error.
               =================================================================== */
            if ( opts.lapack ) {
                printf("%5d %5d   %7.2f (%7.2f)   %7.2f (%7.2f)",
                       (int) M, (int) N, cpu_perf, cpu_time, mic_perf, mic_time );
            }
            else {
                printf("%5d %5d     ---   (  ---  )   %7.2f (%7.2f)",
                       (int) M, (int) N, mic_perf, mic_time );
            }
            if ( opts.check ) {
                printf("   %8.2e       %8.2e   %8.2e\n",
                       result[0]*eps, result[1]*eps, result[2]*eps );
            } else {
                printf("     ---            ---      ---\n");
            }
       
            fflush(0);	    
            TESTING_FREE_HOST( h_A );
            TESTING_FREE_HOST( tauq );
            TESTING_FREE_HOST( taup );
            TESTING_FREE_HOST( diag );
            TESTING_FREE_HOST( offdiag );
            TESTING_FREE_HOST( h_Q );
            TESTING_FREE_HOST( h_work );
        }
        if ( opts.niter > 1 ) {
            printf( "\n" );
        }
    }

    magma_queue_destroy( queue );
    magma_finalize();
    return 0;
}
