// CUDA Runtime API helper functions

#include <cuda_runtime_api.h>
#include <stdio.h>

// CUDA Runtime API Version

// Return the ID of the device with MAX GFLOPs
//inline
extern "C" int cutGetMaxGflopsDeviceId()
{
    //#ifndef max
#if !defined max    
  #define max( a, b ) ( ((a) > (b)) ? (a) : (b) )
#endif
    //#ifnef min
#if !defined min    
   #define min( a, b ) ( ((a) < (b)) ? (a) : (b) )
#endif
    int current_device    = 0, sm_per_multiproc = 0;
    int max_compute_perf = 0, max_perf_device = 0;
    int device_count      = 0, best_SM_arch     = 0;
    int arch_cores_sm[3] = { 1, 8, 32 };
    cudaDeviceProp deviceProp;
    cudaGetDeviceCount( &device_count );
    // Find the best major SM Architecture GPU device -> best_SM_arch
    while ( current_device < device_count ) {
        cudaGetDeviceProperties( &deviceProp, current_device );
        if (deviceProp.major > 0 && deviceProp.major < 9999) {
            best_SM_arch = max(best_SM_arch, deviceProp.major);
        }
        current_device++;
    }
    // Find the best CUDA capable GPU device
    current_device = 0;
    while( current_device < device_count ) {
        cudaGetDeviceProperties( &deviceProp, current_device );
        if (deviceProp.major == 9999 && deviceProp.minor == 9999) {
            sm_per_multiproc = 1;
        } else if (deviceProp.major <= 2) {
            sm_per_multiproc = arch_cores_sm[deviceProp.major];
        } else { // Device has SM major > 2
            sm_per_multiproc = arch_cores_sm[2];
        }
        int compute_perf = deviceProp.multiProcessorCount *
                            sm_per_multiproc * deviceProp.clockRate;
        if( compute_perf > max_compute_perf ) {
            // If we find GPU of SM major > 2, search only these
            if ( best_SM_arch > 2 ) {
                // If device==best_SM_arch, choose this, or else pass
                if (deviceProp.major == best_SM_arch) {
                     max_compute_perf = compute_perf;
                     max_perf_device   = current_device;
                }
            } else {
                max_compute_perf = compute_perf;
                max_perf_device    = current_device;
            }
        }
        ++current_device;
    }
    cudaGetDeviceProperties(&deviceProp, max_perf_device);
    printf("\nDevice %d: \"%s\"\n", max_perf_device,
                                     deviceProp.name);
    printf("Compute Capability    : %d.%d\n",
            deviceProp.major, deviceProp.minor);
    return max_perf_device;
}
