#ifndef STOPWATCH
#define STOPWATCH

#include <algorithm> //Sortierung
#include <cstdlib> //rand()
#include <vector> //Vector
#include <thread> //Threads

#include "Stopwatch.hpp"

/**
 * Hier wird gemessen wie schnell Sortieren mit verschiedener Threadzahl läuft.
 *
 * Achtung: Auch nachdem mehr Threads als hardwareseitig möglich sind verwendet
 * werden sinkt noch die Dauer. Die ist nur der Fall, da die merge Phase weggelassen
 * wurde und somit das Sortieren billiger wird je mehr einzelne Teile sortiert werden
 * (da die sortierten Teile nicht mehr zusammengefügt werden).
 *
 * Unter Umständen muss die pthread library mit -lpthread gelinkt werden, sonst
 * kann es beim Ausführen zu einer Fehlermeldung kommen
 *
 */


/// Füllt den Vector mit Testwerten
void fill(std::vector<int>& v, int size){
	std::cout << "Fülle Vector mit " << size << " Werten...";
	for(int i = 0; i < size ; i++ ){
		v.push_back(rand());
	}
	std::cout << "FERTIG" << std::endl;
}

//Sortiert die Positionen von start bis end
void doSort(std::vector<int>* v, int start, int end){
	std::sort(v->begin()+start,v->begin()+end);
}

//Sortiert den Eingabevector multithreaded
void sortThreaded(std::vector<int>& v,int numThreads){
	std::vector<std::thread*> threads;
	int offset = 0;

	//Erstelle und starte die Threads
	int chunkSize = v.size() / numThreads;
	for(int i = 0; i < numThreads ; i++ ){
		//Der Konstruktor von thread nimmt eine Funktion (oder einen Funktoren, also eine Klasse die den
		//function call Operator definiert
		//die weiteren Parameter werden an diese Funktion übergeben
		std::thread* t = new std::thread(doSort,&v,offset,offset+chunkSize );
		threads.push_back(t);
		offset += chunkSize;
	}

	//Warte darauf, dass jeder thread fertig ist
	for(std::thread* t : threads){
		t->join();
		delete t;
	}
}

//sortiert den eingabevector single threaded
void sortSingleThreaded(std::vector<int>& v){
	doSort(&v,0,v.size());
}

//Misst die Sortierdauer für 16 Millionen Einträge
void measureSort(int numThreads){
	std::vector<int> v;
	fill(v,1 << 24);
	std::cout << "Sortiere mit " << numThreads << " threads" << std::endl;
	Stopwatch s;
	s.start();
	if(numThreads == 1){
		sortSingleThreaded(v);
	} else {
		sortThreaded(v,numThreads);
	}
	s.stop();
	std::cout << "Sortierdauer mit " << numThreads << " threads: " << s << std::endl;
}

int main(){
	//Ausgabe der maximal möglichen Hardware threads.
	std::cout << "Concurrency support: " <<  std::thread::hardware_concurrency() << std::endl;

	//Teste Sortierung mit unterschiedlicher Threadzahl
	measureSort(1);
	measureSort(2);
	measureSort(4);
	measureSort(8);
	measureSort(16);
	measureSort(32);

	return 0;
}


#endif
