#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <sys/mman.h>

#include "schedulers.h"

using namespace std;


int& CELL(int *B, int s, int i, int j, int n)
{
	return B[s*n*n + i*n +j];
}

int* createSharedTensor(size_t totalBytes)
{
	int *ptr = (int*) mmap(
		nullptr,
		totalBytes,
		PROT_READ | PROT_WRITE,
		MAP_SHARED | MAP_ANONYMOUS,
		-1,
		0
	);

	if(ptr == MAP_FAILED) {
		perror("mmap failed");
		exit(1);
	}

	return ptr;

}

void fillTensor(int *B, int n, int k)
{
	for(int s = 0; s<k; s++){
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				CELL(B, s, i, j, n) = rand() % 2001 - 1000;
			}
		}
	}
}

bool isSortedRows(int *B, int n, int k)
{
	for(int s=0; s<k; s++){
		for(int i=0; i<n; i++){
			for(int j=0; j<n-1; j++){
				if(CELL(B, s, i, j, n) > CELL(B, s, i, j+1, n))
					return false;
			}
		}
	}
	return true;
}


const char* getAlgoName(int algorithm)
{
	switch(algorithm)
	{
		case 1: return "Quick Sort";
		case 2: return "Merge Sort";
		case 3: return "Insertion Sort";
		case 4: return "Selection Sort";
		case 5: return "Bubble Sort";
		case 6: return "Enumeration Sort";
		default: return "Unknown";
	}
}


void runOneTest(int n, int k, int algorithm)
{
	size_t totalBytes = n*n*k*sizeof(int);

	int *B_original = createSharedTensor(totalBytes);
	int *B_prolific = createSharedTensor(totalBytes);
	int *B_collective = createSharedTensor(totalBytes);

	fillTensor(B_original, n, k);

	memcpy(B_prolific, B_original, totalBytes);
	memcpy(B_collective, B_original, totalBytes);

	cout<< "\n===============================\n";
	cout<< "Sorting Algorithm: "<<getAlgoName(algorithm)<<"\n";
	cout<< "n  :   "<<n<<"\n";
	cout<< "k  :   "<<k<<"\n";
	cout<< "=================================\n";

	//run prolific
	cout<<"\n[Running Prolific Scheduler]\n";
	ProlificScheduler prolific;
	prolific.run(B_prolific, n, k, algorithm);

	cout << "Sorted correctly: "
		<< (isSortedRows(B_prolific, n, k) ? "YES" : "NO") << "\n";

	//run collective
	cout<<"\n[Running Collective Scheduler]\n";
	CollectiveScheduler collective;
	collective.run(B_collective, n, k, algorithm);

	cout << "Sorted correctly: "
		<< (isSortedRows(B_prolific, n, k) ? "YES" : "NO") << "\n";

	//free shared mmry
	munmap(B_original, totalBytes);
	munmap(B_prolific, totalBytes);
	munmap(B_collective, totalBytes);
}

int main(){

	srand(time(0));

	int n;
	cout<<"Give n:";
	cin >> n;

	for( int algorithm = 1; algorithm<=6; algorithm++){
		runOneTest(n, 10, algorithm);
	}

	for(int algorithm = 1; algorithm<=6; algorithm++){
		runOneTest(n, 100, algorithm);
	}

	return 0;
}
