#include "core/Clock.hpp"
#include "core/HashMap.hpp"
#include "core/Logger.hpp"
#include "core/Random.hpp"

using HashMapInt = Core::HashMap<int, int>;
using Core::Clock;

static i64 testSearch(const HashMapInt& m) {
    i64 nanos = Clock::getNanos();
    volatile int sum = 0;
    for(int i = 0; i < 10'000; i++) {
        for(int k = -5000; k < 5000; k++) {
            const int* s = m.search(i + k);
            if(s != nullptr) {
                sum = sum + *s;
            }
        }
    }
    return Clock::getNanos() - nanos;
}

static i64 testEmptySearch(const HashMapInt& m) {
    i64 nanos = Clock::getNanos();
    volatile int sum = 0;
    for(int i = 0; i < 100'000'000; i++) {
        const int* s = m.search(-i);
        if(s != nullptr) {
            sum = sum + *s;
        }
    }
    return Clock::getNanos() - nanos;
}

static void fillOrder(HashMapInt& m) {
    i64 nanos = Clock::getNanos();
    for(int i = 0; i < 10'000; i++) {
        m.put(i, i * i);
    }
    LOG_INFO("Fill Order: #ns", Clock::getNanos() - nanos);
}

static void fillChaos(HashMapInt& m) {
    i64 nanos = Clock::getNanos();
    Core::Random random(0);
    for(int i = 0; i < 10'000; i++) {
        int r = random.nextI32(0, 10'000);
        m.put(r, r * r);
    }
    LOG_INFO("Fill Chaos: #ns", Clock::getNanos() - nanos);
}

static i64 average(HashMapInt& m, i64 (*f)(const HashMapInt& m), int n) {
    i64 sum = 0;
    for(int i = 0; i < n; i++) {
        sum += f(m);
    }
    return static_cast<i64>(sum / (n * 1'000'000));
}

static void order(int n) {
    HashMapInt m;
    fillOrder(m);
    LOG_INFO("Order Probing");
    LOG_INFO("Search | #ms", average(m, testSearch, n));
    LOG_INFO("EmptySearch | #ms", average(m, testEmptySearch, n));
}

static void chaos(int n) {
    HashMapInt m;
    fillChaos(m);
    LOG_INFO("Chaos Probing");
    LOG_INFO("Search | #ms", average(m, testSearch, n));
    LOG_INFO("EmptySearch | #ms", average(m, testEmptySearch, n));
}

int main() {
    order(3);
    chaos(3);
    return 0;
}