/*
    -- MAGMA (version 2.0) --
       Univ. of Tennessee, Knoxville
       Univ. of California, Berkeley
       Univ. of Colorado, Denver
       @date

       @author Stan Tomov
       @author Mark Gates
       @author Azzam Haidar
*/

#include "magma_internal.h"

#ifdef __cplusplus
extern "C" {
#endif

// TODO: also add some for AMD cards
// Definition of blocking sizes for NVIDIA cards
//#ifdef MAGMA_HAVE_CUDA

// =============================================================================
/// @addtogroup magma_tuning
/// Optimal block sizes vary with GPU and, to a lesser extent, CPU.
/// Kepler tuning was on K20c   705 MHz with SandyBridge 2.6 GHz host (bunsen).
/// Fermi  tuning was on S2050 1147 MHz with AMD Opteron 2.4 GHz host (romulus).
/// @{


/******************************************************************************/
/// @return nb for spotrf based on n
magma_int_t magma_get_spotrf_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  1500) nb = 256;
        else                nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (n <  2048) nb = 256;
        else                nb = 512;
    }
    else {                     // 1.x
        if      (n <  3328) nb = 128;
        else if (n <  4256) nb = 224;
        else                nb = 288;
    }
    return nb;
}

/// @return nb for dpotrf based on n
magma_int_t magma_get_dpotrf_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  2048) nb = 128;
        else if (n <  3072) nb = 256;
        else                nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 256;
    }
    else {                     // 1.x
        if      (n <  3328) nb = 128;
        else if (n <  4256) nb = 128;
        else                nb = 256;
    }
    return nb;
}

/// @return nb for cpotrf based on n
magma_int_t magma_get_cpotrf_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        nb = 256;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (n <  1500) nb = 192;
        else                nb = 256;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for zpotrf based on n
magma_int_t magma_get_zpotrf_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        nb = 256;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (n <  1500) nb = 192;
        else                nb = 256;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}


/******************************************************************************/
/// @return nb for zpotrf_right based on n
magma_int_t magma_get_zpotrf_right_nb( magma_int_t n )
{
    return 128;
}

/// @return nb for cpotrf_right based on n
magma_int_t magma_get_cpotrf_right_nb( magma_int_t n )
{
    return 128;
}

/// @return nb for dpotrf_right based on n
magma_int_t magma_get_dpotrf_right_nb( magma_int_t n )
{
    return 320;
}

/// @return nb for spotrf_right based on n
magma_int_t magma_get_spotrf_right_nb( magma_int_t n )
{
    return 128;
}


/******************************************************************************/
/// @return nb for sgeqp3 based on m, n
magma_int_t magma_get_sgeqp3_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for dgeqp3 based on m, n
magma_int_t magma_get_dgeqp3_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for cgeqp3 based on m, n
magma_int_t magma_get_cgeqp3_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for zgeqp3 based on m, n
magma_int_t magma_get_zgeqp3_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}


/******************************************************************************/
/// @return nb for sgeqrf based on m, n
magma_int_t magma_get_sgeqrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        nb = 32;
    }
    else if ( arch >= 300 ) {
        if      (minmn <  4096) nb = 96;
        else if (minmn <  7168) nb = 128;
        else if (minmn < 18432) nb = 256;
        else                    nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  3072) nb = 64;
        else if (minmn <  8192) nb = 128;
        else                    nb = 256;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 32;
        else if (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for dgeqrf based on m, n
magma_int_t magma_get_dgeqrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
        if ( arch >= 800 ) {
        nb = 32;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <   256) nb =  64;
        else if (minmn <  3072) nb = 128;
        else if (minmn < 10240) nb = 128;
        else                    nb = 256;
    }
    else {                     // 1.x and 2.x Fermi
        if      (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for cgeqrf based on m, n
magma_int_t magma_get_cgeqrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
        if ( arch >= 800 ) {
        nb = 32;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    else {                     // 1.x and 2.x Fermi
        if      (minmn <  2048) nb = 32;
        else if (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for zgeqrf based on m, n
magma_int_t magma_get_zgeqrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
        if ( arch >= 800 ) {
        nb = 32;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  2048) nb = 32;
        else if (minmn <  4096) nb = 64;
        else                    nb = 128;
    }
    else {                     // 1.x
        if      (minmn <  1024) nb = 64;
        else                    nb = 128;
    }
    return nb;
}


/******************************************************************************/
/// @return nb for sgeqlf based on m, n
magma_int_t magma_get_sgeqlf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 200 ) {       // 2.x Fermi
        nb = magma_get_sgeqrf_nb( m, n );
    }
    else {                     // 1.x
        if      (minmn <  1024) nb = 32;
        else if (minmn <  4032) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for dgeqlf based on m, n
magma_int_t magma_get_dgeqlf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 200 ) {       // 2.x Fermi
        nb = magma_get_dgeqrf_nb( m, n );
    }
    else {                     // 1.x
        if      (minmn <  1024) nb = 32;
        else if (minmn <  4032) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for cgeqlf based on m, n
magma_int_t magma_get_cgeqlf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    if      (minmn <  2048) nb = 32;
    else if (minmn <  4032) nb = 64;
    else                    nb = 128;
    return nb;
}

/// @return nb for zgeqlf based on m, n
magma_int_t magma_get_zgeqlf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    if      (minmn <  1024) nb = 64;
    else                    nb = 128;
    return nb;
}


/******************************************************************************/
/// @return nb for sgelqf based on m, n
magma_int_t magma_get_sgelqf_nb( magma_int_t m, magma_int_t n )
{
    return magma_get_sgeqrf_nb( m, n );
}

/// @return nb for dgelqf based on m, n
magma_int_t magma_get_dgelqf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 200 ) {       // 2.x Fermi
        nb = magma_get_dgeqrf_nb( m, n );
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 32;
        else if (minmn <  4032) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for cgelqf based on m, n
magma_int_t magma_get_cgelqf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    if      (minmn <  2048) nb = 32;
    else if (minmn <  4032) nb = 64;
    else                    nb = 128;
    return nb;
}

/// @return nb for zgelqf based on m, n
magma_int_t magma_get_zgelqf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    if      (minmn <  1024) nb = 64;
    else                    nb = 128;
    return nb;
}

/******************************************************************************/
/// @return nb for hgetrf based on m, n
//-------------------------------------------------------------------------------
double magma_get_gemex_rankk_time( magma_int_t m, magma_int_t k, magma_mp_type_t gmtype)
{
    double perf=0;
    switch (gmtype)
    {
        case Magma_MP_GEMEX_I16_O32_C32:
            {
                // use square data for k>1024 for now
                if(k >= 1024)
                {
                    perf = 90e12;
                }else if( k >= 512)
                {
                    if(m <= 2048)
                        perf = 28e12;
                    else if(m <= 4096)
                        perf = 46e12;
                    else
                        perf = 54e12;
                }else if (k >= 384)
                {
                    if(m <= 2048)
                        perf = 21e12;
                    else if(m <= 4096)
                        perf = 40e12;
                    else
                        perf = 47e12;
                }else if (k >= 256)
                {
                    if(m <= 2048)
                        perf = 16e12;
                    else if(m <= 4096)
                        perf = 29e12;
                    else
                        perf = 36e12;
                }else if (k >= 128)
                {
                    if(m <= 2048)
                        perf = 9e12;
                    else if(m <= 4096)
                        perf = 17e12;
                    else
                        perf = 22e12;
                }else
                {
                    perf = 9e12;
                }
            }
            break;
        case Magma_MP_GEMEX_I16_O16_C32:
            {

            }
            break;
        case Magma_MP_GEMEX_I16_O16_C16:
            {

            }
            break;
        default:
            {

            }
    }
    return (2.0*double(m)*double(m)*double(k)/perf);
}
//-------------------------------------------------------------------------------
#define FMULS_GETRF(m_, n_) ( ((m_) < (n_)) \
    ? (0.5 * (m_) * ((m_) * ((n_) - (1./3.) * (m_) - 1. ) + (n_)) + (2. / 3.) * (m_)) \
    : (0.5 * (n_) * ((n_) * ((m_) - (1./3.) * (n_) - 1. ) + (m_)) + (2. / 3.) * (n_)) )
#define FADDS_GETRF(m_, n_) ( ((m_) < (n_)) \
    ? (0.5 * (m_) * ((m_) * ((n_) - (1./3.) * (m_)      ) - (n_)) + (1. / 6.) * (m_)) \
    : (0.5 * (n_) * ((n_) * ((m_) - (1./3.) * (n_)      ) - (m_)) + (1. / 6.) * (n_)) )
#define FLOPS_SGETRF(m_, n_) (     FMULS_GETRF((double)(m_), (double)(n_)) +       FADDS_GETRF((double)(m_), (double)(n_)) )

double magma_get_cpu_sgetrf_time( magma_int_t m, magma_int_t n)
{
    double sgetrf_perf=0;
    double pci_bandwidth = 12e9;
    double time_data_transfer = 2*(m*n*4)/pci_bandwidth;
    double time_sgetrf        = 0;

    if(n >= 1024)
    {
        sgetrf_perf = 587e9;
    }else if( n >= 512)
    {
        if(m <= 4096)
            sgetrf_perf = 180e9;
        else if(m <= 8192)
            sgetrf_perf = 280e9;
        else if(m <= 14000)
            sgetrf_perf = 380e9;
        else if(m <= 20000)
            sgetrf_perf = 480e9;
        else
            sgetrf_perf = 600e9;
    }else if( n >= 384)
    {
        if(m <= 4096)
            sgetrf_perf = 150e9;
        else if(m <= 8192)
            sgetrf_perf = 250e9;
        else if(m <= 16000)
            sgetrf_perf = 350e9;
        else if(m <= 20000)
            sgetrf_perf = 450e9;
        else
            sgetrf_perf = 570e9;
    }else if( n >= 256)
    {
        if(m <= 4096)
            sgetrf_perf = 100e9;
        else if(m <= 8192)
            sgetrf_perf = 160e9;
        else if(m <= 16000)
            sgetrf_perf = 260e9;
        else if(m <= 22000)
            sgetrf_perf = 300e9;
        else
            sgetrf_perf = 350e9;
    }else if( n >= 128)
    {
        if(m <= 4096)
            sgetrf_perf = 60e9;
        else if(m <= 8192)
            sgetrf_perf = 100e9;
        else if(m <= 16000)
            sgetrf_perf = 160e9;
        else if(m <= 22000)
            sgetrf_perf = 200e9;
        else
            sgetrf_perf = 250e9;
    }else
    {
        sgetrf_perf = 200e9;
    }

    time_sgetrf = FLOPS_SGETRF(m, n)/sgetrf_perf;

    return (time_sgetrf+time_data_transfer);


}
#undef FLOPS_SGETRF
#undef FMULS_GETRF
#undef FADDS_GETRF
//-------------------------------------------------------------------------------
magma_int_t magma_get_xgetrf_nb(
        magma_int_t m, magma_int_t n, magma_int_t prev_nb,
        magma_mp_type_t enable_tc, magma_mp_type_t mp_algo_type)
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    //magma_int_t arch = magma_getdevice_arch();
#if 0
    if (minmn <=  5248) nb = 128;
//    else if (minmn <= 10240) nb = 256;
    else if (minmn <= 8192) nb = 256;
    else if (minmn <= 10240) nb = 384;
    else if (minmn <= 20480) nb = 512;
    else                    nb = 512;
#else
    if (minmn <=  6000) nb = 128;
    else if (minmn <= 12000) nb = 256;
    else if (minmn <= 36000) nb = 384;
    else                    nb = 512;
#endif


    double mintime128=0.0, mintime1536=0.0;
    int proposed_nb128=128, proposed_nb1536=128;
    for (int ib=128; ib<=512; ib+=128)
    {
         double time_sgetrf = magma_get_cpu_sgetrf_time(minmn, ib);
         double time_gemm   = magma_get_gemex_rankk_time( minmn, ib, Magma_MP_GEMEX_I16_O32_C32);
         double maxtime     = max(time_sgetrf, time_gemm);
         double divto_128   = double(ib/128);
         double multo_1536  = double(1536/ib);
         double time_on_128 = maxtime/divto_128;
         double time_on_1536= maxtime*multo_1536;
         mintime128  = ib == 128 ? time_on_128  : min(mintime128,  time_on_128);
         mintime1536 = ib == 128 ? time_on_1536 : min(mintime1536, time_on_1536);
         if(mintime128  == time_on_128)
             proposed_nb128  = ib;
         if(mintime1536 == time_on_1536)
             proposed_nb1536 = ib;

     /*
         double scale;
         char unit[10];
         scale       = maxtime < 1e-3 ? 1e6     : maxtime < 1e0 ? 1e3  : 1e0;
         snprintf(unit, sizeof(unit),"%s",(maxtime < 1e-3 ? "micros": maxtime < 1e0 ? "ms" : "s"));
         printf("\t\tvoici minmn %5d ib %3d, time_sgetrf %8.5f %s, time_gemm %8.5f %s, max_time: %8.5f %s, 128: %8.5f %s 1536: %8.5f %s\n",
                 minmn, ib, time_sgetrf*scale, unit, time_gemm*scale, unit,
                 maxtime*scale, unit, maxtime*scale/divto_128, unit, maxtime*scale*multo_1536, unit);
               */
    }
    /*
    printf("\t\tvoici minmn %5d choosen nb %3d  proposed_nb128 %4d proposed_nb1536 %4d\n",
                 minmn, nb, proposed_nb128, proposed_nb1536, min(proposed_nb128));
                 */

    MAGMA_UNUSED( nb );
    MAGMA_UNUSED( proposed_nb128 );
    MAGMA_UNUSED( proposed_nb1536 );

    return proposed_nb128;
    //return nb;

}
//-------------------------------------------------------------------------------
magma_int_t magma_get_hgetrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    //magma_int_t arch = magma_getdevice_arch();
#if 0
    if (minmn <=  5248) nb = 128;
//    else if (minmn <= 10240) nb = 256;
    else if (minmn <= 8192) nb = 256;
    else if (minmn <= 10240) nb = 384;
    else if (minmn <= 20480) nb = 512;
    else                    nb = 512;
#else
    if (minmn <=  6000) nb = 128;
    else if (minmn <= 12000) nb = 256;
    else if (minmn <= 36000) nb = 384;
    else                    nb = 512;
#endif

    double mintime128=0.0, mintime1536=0.0;
    int proposed_nb128=128, proposed_nb1536=128;
    for (int ib=128; ib<=512; ib+=128)
    {
         double time_sgetrf = magma_get_cpu_sgetrf_time(minmn, ib);
         double time_gemm   = magma_get_gemex_rankk_time( minmn, ib, Magma_MP_GEMEX_I16_O32_C32);
         double maxtime     = max(time_sgetrf, time_gemm);
         double divto_128   = double(ib/128);
         double multo_1536  = double(1536/ib);
         double time_on_128 = maxtime/divto_128;
         double time_on_1536= maxtime*multo_1536;
         mintime128  = ib == 128 ? time_on_128  : min(mintime128,  time_on_128);
         mintime1536 = ib == 128 ? time_on_1536 : min(mintime1536, time_on_1536);
         if(mintime128  == time_on_128)
             proposed_nb128  = ib;
         if(mintime1536 == time_on_1536)
             proposed_nb1536 = ib;

     /*
         double scale;
         char unit[10];
         scale       = maxtime < 1e-3 ? 1e6     : maxtime < 1e0 ? 1e3  : 1e0;
         snprintf(unit, sizeof(unit),"%s",(maxtime < 1e-3 ? "micros": maxtime < 1e0 ? "ms" : "s"));
         printf("\t\tvoici minmn %5d ib %3d, time_sgetrf %8.5f %s, time_gemm %8.5f %s, max_time: %8.5f %s, 128: %8.5f %s 1536: %8.5f %s\n",
                 minmn, ib, time_sgetrf*scale, unit, time_gemm*scale, unit,
                 maxtime*scale, unit, maxtime*scale/divto_128, unit, maxtime*scale*multo_1536, unit);
               */
    }
    /*
    printf("\t\tvoici minmn %5d choosen nb %3d  proposed_nb128 %4d proposed_nb1536 %4d\n",
                 minmn, nb, proposed_nb128, proposed_nb1536, min(proposed_nb128));
                 */


    MAGMA_UNUSED( nb );
    MAGMA_UNUSED( proposed_nb128 );
    MAGMA_UNUSED( proposed_nb1536 );

    //return proposed_nb128;
    return nb;

}


/******************************************************************************/
/// @return nb for sgetrf based on m, n
magma_int_t magma_get_sgetrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        // high-end GPUs are much faster thanmost CPUs, so small nb's usually
        // gives a higher performance than bigger nb's
        nb = 32;
    }
    else if ( arch >= 300 ) {
        if      (minmn <  4096) nb = 256;
        else if (minmn < 18432) nb = 512;
        else                    nb = 1024;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  3072) nb = 128;
        else if (minmn < 10240) nb = 256;
        else                    nb = 512;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for dgetrf based on m, n
magma_int_t magma_get_dgetrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        if      (minmn <=  9000) nb = 32;
        else if (minmn <= 14000) nb = 64;
        else                     nb = 96;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <  2048) nb =  64;
        else if (minmn <  3072) nb = 128;
        else if (minmn <  8192) nb = 256;
        else                    nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  3072) nb = 128;
        else if (minmn < 10240) nb = 256;
        else                    nb = 512;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for cgetrf based on m, n
magma_int_t magma_get_cgetrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        nb = 32;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn < 4096) nb = 64;
        else if (minmn < 8192) nb = 256;
        else                   nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for zgetrf based on m, n
magma_int_t magma_get_zgetrf_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        nb = 32;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn < 4096) nb = 64;
        else if (minmn < 8192) nb = 256;
        else                   nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn < 4096) nb = 64;
        else                   nb = 128;
    }
    else {                     // 1.x
        nb = 128;
    }
    return nb;
}


/******************************************************************************/
/// @return nb for native sgetrf based on m, n
magma_int_t magma_get_sgetrf_native_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        nb = 512;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <=  4096) nb = 64;
        else if (minmn <= 10240) nb = 128;
        else if (minmn <= 20480) nb = 512;
        else                     nb = 1024;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  3072) nb = 128;
        else if (minmn < 10240) nb = 256;
        else                    nb = 512;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for native dgetrf based on m, n
magma_int_t magma_get_dgetrf_native_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        if ( minmn <= 7000 ) nb = 256;
        else                 nb = 512;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <=  4096)  nb = 64;
        else if (minmn <=  10240) nb = 128;
        else if (minmn <=  20480) nb = 512;
        else                      nb = 1024;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  3072) nb = 128;
        else if (minmn < 10240) nb = 256;
        else                    nb = 512;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for native cgetrf based on m, n
magma_int_t magma_get_cgetrf_native_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        if ( minmn <= 15000 ) nb = 64;
        else                  nb = 512;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <=  2048)  nb = 128;
        else                      nb = 256;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    else {                     // 1.x
        if      (minmn <  2048) nb = 64;
        else                    nb = 128;
    }
    return nb;
}

/// @return nb for native zgetrf based on m, n
magma_int_t magma_get_zgetrf_native_nb( magma_int_t m, magma_int_t n )
{
    magma_int_t nb;
    magma_int_t minmn = min( m, n );
    magma_int_t arch = magma_getdevice_arch();
    // TODO: try all nb's (128,256,512, ... etc)
    if ( arch >= 800 ) {
        // based on experiments using AMD CPU running MKL + A100-SXM4 GPU
        if ( minmn <= 8000 ) nb = 256;
        else                 nb = 512;
    }
    else if ( arch >= 300 ) {       // 3.x Kepler
        if      (minmn <=  4096)  nb = 32;
        else if (minmn <=  10240) nb = 64;
        else if (minmn <=  20480) nb = 256;
        else                      nb = 512;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (minmn < 4096) nb = 64;
        else                   nb = 128;
    }
    else {                     // 1.x
        nb = 128;
    }
    return nb;
}


/******************************************************************************/
/// @return nb for sgehrd based on n
magma_int_t magma_get_sgehrd_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 200 ) {       // 2.x Fermi
        if      (n <  1024) nb = 32;
        else                nb = 96;
    }
    else {                     // 1.x
        if      (n <  1024) nb = 32;
        else                nb = 64;
    }
    return nb;
}

/// @return nb for dgehrd based on n
magma_int_t magma_get_dgehrd_nb( magma_int_t n )
{
    magma_int_t nb;
    if      (n <  2048) nb = 32;
    else                nb = 64;
    return nb;
}

/// @return nb for cgehrd based on n
magma_int_t magma_get_cgehrd_nb( magma_int_t n )
{
    magma_int_t nb;
    if      (n <  1024) nb = 32;
    else                nb = 64;
    return nb;
}

/// @return nb for zgehrd based on n
magma_int_t magma_get_zgehrd_nb( magma_int_t n )
{
    magma_int_t nb;
    if      (n <  2048) nb = 32;
    else                nb = 64;
    return nb;
}


/******************************************************************************/
// Currently, must be 64 due to zhemv_mgpu restrictions.

/// @return nb for ssytrd based on n
magma_int_t magma_get_ssytrd_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for dsytrd based on n
magma_int_t magma_get_dsytrd_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for chetrd based on n
magma_int_t magma_get_chetrd_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for zhetrd based on n
magma_int_t magma_get_zhetrd_nb( magma_int_t n )
{
    return 64;
}


/******************************************************************************/
/// @return nb for zhetrf based on n
magma_int_t magma_get_zhetrf_nb( magma_int_t n )
{
    return 256;
}

/// @return nb for chetrf based on n
magma_int_t magma_get_chetrf_nb( magma_int_t n )
{
    return 256;
}

/// @return nb for dsytrf based on n
magma_int_t magma_get_dsytrf_nb( magma_int_t n )
{
    return 96;
}

/// @return nb for ssytrf based on n
magma_int_t magma_get_ssytrf_nb( magma_int_t n )
{
    return 256;
}


/******************************************************************************/
/// @return nb for zhetrf_aasen based on n
magma_int_t magma_get_zhetrf_aasen_nb( magma_int_t n )
{
    return 256;
}

/// @return nb for chetrf_aasen based on n
magma_int_t magma_get_chetrf_aasen_nb( magma_int_t n )
{
    return 256;
}

/// @return nb for dsytrf_aasen based on n
magma_int_t magma_get_dsytrf_aasen_nb( magma_int_t n )
{
    return 256;
}

/// @return nb for ssytrf_aasen based on n
magma_int_t magma_get_ssytrf_aasen_nb( magma_int_t n )
{
    return 256;
}


/******************************************************************************/
/// @return nb for zhetrf_nopiv based on n
magma_int_t magma_get_zhetrf_nopiv_nb( magma_int_t n )
{
    return 320;
}

/// @return nb for chetrf_nopiv based on n
magma_int_t magma_get_chetrf_nopiv_nb( magma_int_t n )
{
    return 320;
}

/// @return nb for dsytrf_nopiv based on n
magma_int_t magma_get_dsytrf_nopiv_nb( magma_int_t n )
{
    return 320;
}

/// @return nb for ssytrf_nopiv based on n
magma_int_t magma_get_ssytrf_nopiv_nb( magma_int_t n )
{
    return 320;
}


/******************************************************************************/
/// @return nb for sgebrd based on m, n
magma_int_t magma_get_sgebrd_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for dgebrd based on m, n
magma_int_t magma_get_dgebrd_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for cgebrd based on m, n
magma_int_t magma_get_cgebrd_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}

/// @return nb for zgebrd based on m, n
magma_int_t magma_get_zgebrd_nb( magma_int_t m, magma_int_t n )
{
    return 32;
}


/******************************************************************************/
/// @return nb for ssygst based on n
magma_int_t magma_get_ssygst_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  4096) nb = 768;
        else                nb = 1536;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (n <  2048) nb = 512;
        else                nb = 1024;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for dsygst based on n
magma_int_t magma_get_dsygst_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  2048) nb = 384;
        else                nb = 768;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 512;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for chegst based on n
magma_int_t magma_get_chegst_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  2048) nb = 384;
        else                nb = 768;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 512;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for zhegst based on n
magma_int_t magma_get_zhegst_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        nb = 384;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 256;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}


/******************************************************************************/
/// @return nb for sgetri based on n
magma_int_t magma_get_sgetri_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for dgetri based on n
magma_int_t magma_get_dgetri_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for cgetri based on n
magma_int_t magma_get_cgetri_nb( magma_int_t n )
{
    return 64;
}

/// @return nb for zgetri based on n
magma_int_t magma_get_zgetri_nb( magma_int_t n )
{
    return 64;
}


/******************************************************************************/
/// @return nb for sgesvd based on m, n
magma_int_t magma_get_sgesvd_nb( magma_int_t m, magma_int_t n )
{
    return magma_get_sgebrd_nb( m, n );
}

/// @return nb for dgesvd based on m, n
magma_int_t magma_get_dgesvd_nb( magma_int_t m, magma_int_t n )
{
    return magma_get_dgebrd_nb( m, n );
}

/// @return nb for cgesvd based on m, n
magma_int_t magma_get_cgesvd_nb( magma_int_t m, magma_int_t n )
{
    return magma_get_cgebrd_nb( m, n );
}

/// @return nb for zgesvd based on m, n
magma_int_t magma_get_zgesvd_nb( magma_int_t m, magma_int_t n )
{
    return magma_get_zgebrd_nb( m, n );
}

/***************************************************************************//**
 ** This is an internal routine for selecting one `nb` value out of an array
    of candidate values for the blocked gesvj routine.
 ** Performance benchmarks show that the `mid-range` of sizes has no clear winner
    for blocking sizes, and often alternates between a handful of values. The value
    that gives the best performance in this case is observed to be the one requiring
    the least amount of padding to the columns of the input matrix
*******************************************************************************/
/// @return nb with the least padding for blocked batch gesvj
static magma_int_t magma_get_gesvj_nb_min_padding(magma_int_t m, magma_int_t n, magma_int_t max_nb)
{
    float n_f = (float)n;

    magma_int_t nb            = max_nb;
    magma_int_t nbx2          = 2*nb;
    magma_int_t min_blk_pairs = magma_ceildiv(n, nbx2);
    float score               = (n_f / (float)nbx2) / (float)min_blk_pairs;
    for(auto inb = nb-1; inb > 1; inb--) {
        nbx2 = inb * 2;
        magma_int_t blk_pairs = magma_ceildiv(n, nbx2);

        if(blk_pairs != min_blk_pairs) break;

        float iscore = (n_f / (float)nbx2) / (float)blk_pairs;
        if(iscore > score) {
            nb = inb;
            min_blk_pairs = blk_pairs;
        }
    }
    return nb;
}

/***************************************************************************//**
 ** Batch gesvj uses batch heevj, with heevj_nb = 2 x gesvj_nb
    heev_nb should be small enough so that the matrix (and eigen vectors) fit
    in the GPU shared memory (that is: 2 x heev_nb^2, plus minimal extra workspace)
 ** Use the table below for a quick shared memory lookup (in KB):
   gesvj_nb  heev_nb      's'    'd'    'c'    'z'
    4            8        0.5    1.0    1.0    2.0
    8           16        2.0    4.0    4.0    8.0
   12           24        4.5    9.0    9.0   18.0
   16           32        8.0   16.0   16.0   32.0
   20           40       12.5   25.0   25.0   50.0
   24           48       18.0   36.0   36.0   72.0
   28           56       24.5   49.0   49.0   98.0
   32           64       32.0   64.0   64.0  128.0
   36           72       40.5   81.0   81.0  162.0
   40           80       50.0  100.0  100.0  200.0
   44           88       60.5  121.0  121.0  242.0
   48           96       72.0  144.0  144.0  288.0
   52          104       84.5  169.0  169.0  338.0
   56          112       98.0  196.0  196.0  392.0
   60          120      112.5  225.0  225.0  450.0
   64          128      128.0  256.0  256.0  512.0
*******************************************************************************/
/// @return nb for sgesvj_batched based on m, n
magma_int_t magma_get_sgesvj_batched_nb( magma_int_t m, magma_int_t n )
{
    #ifdef MAGMA_HAVE_CUDA
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  32;

    #else
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  32;
    #endif

    nb = magma_get_gesvj_nb_min_padding(m, n, nb_asymptotic);

    return nb;
}

/// @return nb for dgesvj_batched based on m, n
magma_int_t magma_get_dgesvj_batched_nb( magma_int_t m, magma_int_t n )
{
    #ifdef MAGMA_HAVE_CUDA
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  32;

    #else
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  31;
    #endif

    nb = magma_get_gesvj_nb_min_padding(m, n, nb_asymptotic);

    return nb;
}

/// @return nb for cgesvj_batched based on m, n
magma_int_t magma_get_cgesvj_batched_nb( magma_int_t m, magma_int_t n )
{
    #ifdef MAGMA_HAVE_CUDA
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  32;

    #else
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  31;
    #endif

    nb = magma_get_gesvj_nb_min_padding(m, n, nb_asymptotic);

    return nb;
}

/// @return nb for zgesvj_batched based on m, n
magma_int_t magma_get_zgesvj_batched_nb( magma_int_t m, magma_int_t n )
{
    #ifdef MAGMA_HAVE_CUDA
    magma_int_t arch = magma_getdevice_arch();
    magma_int_t nb            =   8; // default value
    magma_int_t nb_asymptotic =  ( arch >= 800 ) ? 32 : 24; // avoid exceeding shared memory size for pre-Ampere GPUs

    #else
    magma_int_t nb            =  8; // default value
    magma_int_t nb_asymptotic = 16;
    #endif

    nb = magma_get_gesvj_nb_min_padding(m, n, nb_asymptotic);

    return nb;
}


/******************************************************************************/
/// @return nb for ssygst_m based on n
magma_int_t magma_get_ssygst_m_nb( magma_int_t n )
{
    return 256; //to be updated

    /*
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  4096) nb = 768;
        else                nb = 1536;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        if      (n <  2048) nb = 512;
        else                nb = 1024;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
    */
}

/// @return nb for dsygst_m based on n
magma_int_t magma_get_dsygst_m_nb( magma_int_t n )
{
    return 256; //to be updated

    /*
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  2048) nb = 384;
        else                nb = 768;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 512;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
    */
}

/// @return nb for chegst_m based on n
magma_int_t magma_get_chegst_m_nb( magma_int_t n )
{
    return 256; //to be updated

    /*
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        if      (n <  2048) nb = 384;
        else                nb = 768;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 512;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
    */
}

/// @return nb for zhegst_m based on n
magma_int_t magma_get_zhegst_m_nb( magma_int_t n )
{
    return 256; //to be updated

    /*
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler
        nb = 384;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 256;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
    */
}


/******************************************************************************/
// TODO: these numbers seem very strange.
// Numbers in zbulge_back.cpp seem more realistic: 27 for Fermi + Westmere.

/// @return gpu over cpu performance for 2 stage TRD
magma_int_t magma_get_sbulge_gcperf( )
{
    magma_int_t perf;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        perf = 37;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        perf = 15000;
    }
    else {                     // 1.x
        perf = 10000;
    }
    return perf;
}

/// @return gpu over cpu performance for 2 stage TRD
magma_int_t magma_get_dbulge_gcperf( )
{
    magma_int_t perf;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        perf = 37;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        perf = 15000;
    }
    else {                     // 1.x
        perf = 10000;
    }
    return perf;
}

/// @return gpu over cpu performance for 2 stage TRD
magma_int_t magma_get_cbulge_gcperf( )
{
    magma_int_t perf;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        perf = 50;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        perf = 15000;
    }
    else {                     // 1.x
        perf = 10000;
    }
    return perf;
}

/// @return gpu over cpu performance for 2 stage TRD
magma_int_t magma_get_zbulge_gcperf( )
{
    magma_int_t perf;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        perf = 50;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        perf = 15000;
    }
    else {                     // 1.x
        perf = 10000;
    }
    return perf;
}


/******************************************************************************/
/// @return smlsiz for the divide and conquewr routine dlaex0 dstedx zstedx
magma_int_t magma_get_smlsize_divideconquer()
{
    return 128;
}



/******************************************************************************/
/// @return nb for 2 stage TRD
magma_int_t magma_get_sbulge_nb( magma_int_t n, magma_int_t nbthreads  )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 128;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD
magma_int_t magma_get_dbulge_nb( magma_int_t n, magma_int_t nbthreads  )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 128;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 128;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD
magma_int_t magma_get_cbulge_nb( magma_int_t n, magma_int_t nbthreads  )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        if ( nbthreads > 14 )
            nb = 128;
        else
            nb = 64;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD
magma_int_t magma_get_zbulge_nb( magma_int_t n, magma_int_t nbthreads )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        if ( nbthreads > 14 )
            nb = 128;
        else
            nb = 64;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}


/******************************************************************************/
/// @return Vblksiz for 2 stage TRD
magma_int_t magma_get_sbulge_vblksiz( magma_int_t n, magma_int_t nb, magma_int_t nbthreads  )
{
    magma_int_t size;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        size = min(nb, 128);
    }
    else {                     // 2.x Fermi or 1.x
        size = min(nb, 64);
    }
    return size;
}

/// @return Vblksiz for 2 stage TRD
magma_int_t magma_get_dbulge_vblksiz( magma_int_t n, magma_int_t nb, magma_int_t nbthreads  )
{
    magma_int_t size;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        size = min(nb, 64);
    }
    else {                     // 2.x Fermi or 1.x
        size = min(nb, 48);
    }
    return size;
}

/// @return Vblksiz for 2 stage TRD
magma_int_t magma_get_cbulge_vblksiz( magma_int_t n, magma_int_t nb, magma_int_t nbthreads )
{
    magma_int_t size;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        if ( nbthreads > 14 )
            size = min(nb, 48);
        else
            size = min(nb, 48);
    }
    else {                     // 2.x Fermi or 1.x
        size = min(nb, 48);
    }
    return size;
}

/// @return Vblksiz for 2 stage TRD
magma_int_t magma_get_zbulge_vblksiz( magma_int_t n, magma_int_t nb, magma_int_t nbthreads )
{
    magma_int_t size;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        if ( nbthreads > 14 )
            size = min(nb, 64);
        else
            size = min(nb, 32);
    }
    else {                     // 2.x Fermi or 1.x
        size = min(nb, 48);
    }
    return size;
}


/******************************************************************************/
/// @return nb for 2 stage TRD_MGPU
magma_int_t magma_get_sbulge_mgpu_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 128;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD_MGPU
magma_int_t magma_get_dbulge_mgpu_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 128;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD_MGPU
magma_int_t magma_get_cbulge_mgpu_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 64;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}

/// @return nb for 2 stage TRD_MGPU
magma_int_t magma_get_zbulge_mgpu_nb( magma_int_t n )
{
    magma_int_t nb;
    magma_int_t arch = magma_getdevice_arch();
    if ( arch >= 300 ) {       // 3.x Kepler + SB
        nb = 64;
    }
    else if ( arch >= 200 ) {  // 2.x Fermi
        nb = 64;
    }
    else {                     // 1.x
        nb = 64;
    }
    return nb;
}


// =============================================================================
/// @}
// end group magma_tuning

//#endif  // MAGMA_HAVE_CUDA

#ifdef __cplusplus
} // extern "C"
#endif
