#include <unordered_map>
#include <vector>
#include <benchmark/benchmark.h>
#include <experimental/random>
#include <experimental/algorithm>

struct item_t
{
    int key;
    int a, b, c, d, e, f, g, h; // een beetje veel data
};

static std::unordered_map<int, item_t> ConstructMap(int size)
{
    std::unordered_map<int, item_t> its;
    for (int x = 0; x < size; x++)
    {
        its[x] = item_t { .key = x };
    }
    return its;
}

static std::vector<item_t> ConstructVec(int size)
{
    std::vector<item_t> its;
    for (int x = 0; x < size; x++)
    {
        its.push_back (item_t { .key = x });
    }

    std::random_device rd;
    std::mt19937_64 g(rd());

    std::shuffle(its.begin(), its.end(), g);

    return its;
}

static void bench_map_construct(benchmark::State& state)
{
    while (state.KeepRunning())
    {
        auto v = ConstructMap(state.range(0));
        benchmark::ClobberMemory();
    }
}

static void bench_vec_construct(benchmark::State& state)
{
    while (state.KeepRunning())
    {
        auto v = ConstructVec(state.range(0));
        benchmark::ClobberMemory();
    }
}

static void bench_map_find(benchmark::State& state)
{
    auto m = ConstructMap(state.range(0));
    std::random_device rd;

    std::uniform_int_distribution<int> dist(0, state.range(0));

    while (state.KeepRunning())
    {
        int i = dist(rd);
        auto v = m[i];
        benchmark::DoNotOptimize(v);
    }
}

static void bench_vec_find(benchmark::State& state)
{
    auto v = ConstructVec(state.range(0));
    std::random_device rd;

    std::uniform_int_distribution<int> dist(0, state.range(0));

    while (state.KeepRunning())
    {
        int i = dist(rd);
        for (auto &item : v)
        {
            if (item.key == i) benchmark::DoNotOptimize(item);
        }
    }
}

BENCHMARK(bench_map_construct)->Range(2, 512000);
BENCHMARK(bench_vec_construct)->Range(2, 512000);
BENCHMARK(bench_map_find)->Range(2, 512000);
BENCHMARK(bench_vec_find)->Range(2, 512000);

BENCHMARK_MAIN();
