     cudaFree(data1_d); CUERR
     cudaFree(data2_d); CUERR
     cudaFree(hash_table_d); CUERR
+    */
 }

#include <iostream>

#include "../include/warpdrive.cuh"

DEVICEQUALIFIER INLINEQUALIFIER
void print_bits_32(uint32_t x)

    using namespace warpdrive;
    using namespace std;

    cout << "==========WARPDRIVE TEST/BENCHMARK==========" << endl;

    if (argc < 9)
    {
        cerr << "ERROR: Not enough parameters (read \"PARAMS\" section inside the main function)" << endl;
        return -1;
    }

    //the global index type to use
    using index_t = uint64_t;

    //PARAMS
    //the size of the thread groups (must be available at compile time)
    static constexpr index_t group_size = 16;
    //output verbosity (must be available at compile time)
    static constexpr index_t verbosity = 1;
    //filename for test data (dumped with binary_io.h)
    const string  filename = argv[1];
    //length of test data
    const index_t len_data = atoi(argv[2]);
    //load factor of the hash table
    const float   load     = atof(argv[3]);
    //capacity of the hash table
    const index_t capacity = len_data/load;
    //max chaotic probing attempts
    const index_t lvl1_max = atoi(argv[4]);
    //max linear probing attempts
    const index_t lvl2_max = atoi(argv[5]);
    //number of CUDA blocks per grid
    const index_t blocks_per_grid = (atoi(argv[6]) != 0) ? atoi(argv[6]): std::numeric_limits<index_t>::max();
    //number of threads per CUDA block (must be multiple of group_size)
    const index_t threads_per_block = atoi(argv[7]);
    //id of selected CUDA device
    const index_t device_id = atoi(argv[8]);

    if (verbosity > 0)
    {
        cout << "================== PARAMS =================="
             << "\n(static) group_size=" << group_size
             << "\n(static) verbosity=" << verbosity
             << "\nfilename=" << filename
             << "\nlen_data=" << len_data
             << "\nload=" << load
             << "\ncapacity=" << capacity
             << "\nlvl1_max=" << lvl1_max
             << "\nlvl2_max=" << lvl2_max
             << "\nblocks_per_grid=" << blocks_per_grid
             << "\nthreads_per_block=" << threads_per_block
             << "\ndevice_id=" << device_id
             << endl;
    }

    //DECLS
    //data policy
    using data_p    = warpdrive::policies::PackedPairDataPolicy<>;
    //failure policy
    using failure_p = std::conditional<(verbosity > 1),
                                       warpdrive::policies::PrintIdFailurePolicy,
                                       warpdrive::policies::IgnoreFailurePolicy>::type;

   //data types
   using data_t    = data_p::data_t;
   using key_t     = data_p::key_t;
   using value_t   = data_p::value_t;

    //plan (the meat)
    using plan_t = plans::BasicPlan<data_p,
                                    index_t,    //index type to use
                                    group_size, //size of parallel probing group
                                    failure_p>;

    //config struct (probing lengths and kernel launch config)
    plan_t::config_t config(lvl1_max,
                            lvl2_max,
                            blocks_per_grid,
                            threads_per_block);

    //set the selected CUDA device
    cudaSetDevice(device_id); CUERR

    //load random keys
    key_t * keys_h = (key_t*)malloc(sizeof(key_t)*len_data);
    load_binary(keys_h, len_data, filename);

    free(keys_h);
    /*
    // generate data
    data_t * data1_h = new data_t[len_data];
    data_t * data2_h = new data_t[len_data];
