#include <algorithm>
#include <array>
#include <chrono>
#include <iostream>
#include <locale>
#include <memory>
#include <string>
#include <vector>

#include <cassert>

struct __attribute__((aligned(64))) element {
	size_t value;
};

using block = std::array<element, 100>;

block * create() {
	block * b = new block();
	for(auto & e : *b) {
		e.value = rand();
	}
	b->back().value = b->size();

	return b;
}

static inline size_t find(const block & b) {
	size_t r = 0;
	for(; r < b.size(); r++) {
		if(__builtin_expect(b[r].value == b.size(), false)) break;
	}

	return r;
}

void usage(char * argv[]) {
	std::cerr << argv[0] << ": [DURATION (FLOAT:SEC)] [NBLOCKS]" << std::endl;;
	std::exit(1);
}

int main(int argc, char * argv[]) {
	size_t nblocks = 1000;
	double duration = 5;

	std::cout.imbue(std::locale(""));

	switch (argc)
	{
	case 3:
		nblocks = std::stoul(argv[2]);
		[[fallthrough]];
	case 2:
		duration = std::stod(argv[1]);
		if( duration <= 0.0 ) {
			std::cerr << "Duration must be positive, was " << argv[1] << "(" << duration << ")" << std::endl;
			usage(argv);
		}
		[[fallthrough]];
	case 1:
		break;
	default:
		usage(argv);
		break;
	}

	std::vector<std::unique_ptr<block>> blocks;
	for(size_t i = 0; i < nblocks; i++) {
		blocks.emplace_back( create() );
	}
	std::random_shuffle(blocks.begin(), blocks.end());

	size_t CRC = 0;
	size_t count = 0;

	using clock = std::chrono::high_resolution_clock;
	auto before = clock::now();

	while(true) {
		for(const auto & b : blocks) {
			CRC += find(*b);
			count++;
		}
		auto now = clock::now();
		std::chrono::duration<double> durr = now - before;
		if( durr.count() > duration ) {
			break;
		}
	}

	auto after = clock::now();
	std::chrono::duration<double> durr = after - before;
	duration = durr.count();

	using std::chrono::duration_cast;
	using std::chrono::nanoseconds;

	size_t ops_sec = size_t(double(count) / duration);
	auto dur_nano = duration_cast<nanoseconds>(std::chrono::duration<double>(1.0)).count();

	std::cout << "CRC           : " << CRC << "\n";
	std::cout << "Duration      : " << duration << "s\n";
	std::cout << "Total ops     : " << count << "\n";
	std::cout << "Ops/sec       : " << ops_sec << "\n";
	std::cout << "ns/Op         : " << ( dur_nano / ops_sec )<< "\n";
}