// FirstOMPApp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "timerSystem.h"
#define PI_OUTPUT_FILENAME "out_pi.csv"
#define VECTOR_OUTPUT_FILENAME "out_vector.csv"

void test1();
void test2();
void test3();
void test4();
void test5();
void test6();
void test7();
void test8();
void test9();
void calculPi();
void CreateVectorData(char *fn);
void CreatePIData(char *fn);
void WriteVectorData(char *fn, int nbThreads, int size, float timeExec, float speedUp, float efficacity, float realEfficacity);
void WritePIData(char *fn, int nbThreads, int nbPas, float timeExec, float speedUp, float efficacity, float realEfficacity,double pi);
int _tmain(int argc, _TCHAR* argv[])
{

	CreateVectorData(VECTOR_OUTPUT_FILENAME);
	test9();
	CreatePIData(PI_OUTPUT_FILENAME);
	calculPi();
	system("pause");
}

void test1(){
	#pragma omp parallel
	printf("hello from %d of %d\n", omp_get_thread_num(), omp_get_num_threads());
}
void test2(){
	printf("Hello\n"); 
	double time0 = omp_get_wtime();
	#pragma omp parallel for num_threads(2)
	for (int n = 0; n<100000; ++n)
		printf(" %d ", n);
	printf("CPUtime=%f\n", omp_get_wtime() - time0);
}
void test3(){
	#pragma omp parallel
	#pragma omp sections 
		{
	#pragma omp section
			{
				for (int n = 0; n < 10; ++n)
					printf("To do A: %d\n", omp_get_thread_num());
			}
	#pragma omp section
			{
			for (int n = 0; n < 10; ++n)
				printf("To do B: %d\n", omp_get_thread_num());
		}
	#pragma omp section
			{
				for (int n = 0; n < 10; ++n)
					printf("To do C: %d\n", omp_get_thread_num());
			}
		}
}
void test4(){
	omp_set_num_threads(4);
	printf_s("%d\n", omp_get_max_threads());
#pragma omp parallel num_threads(10)
#pragma omp master
	{
		printf_s("%d\n", omp_get_num_threads());
	}
	printf_s("%d\n", omp_get_max_threads());
#pragma omp parallel num_threads(3)
#pragma omp master
	{
		printf_s("%d\n", omp_get_max_threads());
	}
	printf_s("%d\n", omp_get_max_threads());
}
void test5(){
	omp_set_num_threads(4);
	printf_s(" the first %d\n", omp_get_num_threads());
#pragma omp parallel
#pragma omp master
	{
		printf_s("the thread1 %d\n", omp_get_num_threads());
	}
	printf_s("the main %d\n", omp_get_num_threads());
#pragma omp parallel num_threads(3)
#pragma omp master
	{
		printf_s("the thread2 %d\n", omp_get_num_threads());
	}
	printf_s("the main %d\n", omp_get_num_threads());
}
void test6(){
	int t, j, i;
	j = 0;
	#pragma omp parallel private(t, i) shared(j) 
	{
		t = omp_get_thread_num();
		printf("running %d\n", t);
		for (i = 0; i < 1000000; i++)
			j++;
		printf("run %d\n", t);
	}
	printf("%d\n",j);
}
void test7(){
	int t=0;
#pragma omp parallel reduction(+:t)
	{
		t = omp_get_thread_num() + 2;
		printf("Partial %d\n", t);
	}
	printf("reduction %d\n", t);
}
void test8(){
#pragma omp parallel // #pragma omp parallel for
#pragma omp for //
	for (int i = 0; i < 5; i++)
		printf("hello from %d at %d\n", omp_get_thread_num(), i);
}
void test9(){
	TimerSystem timer;
	int max_threads = 10;
	int num_iter = 5;
	double average;
	int size = 1000;
	float speedUp;
	float efficacity;
	float realEfficacity;
	double seqTime, executionTime;

	for (size = 100000; size <= 1000000; size += 100000){
		float *A = new float[size];
		float *B = new float[size];
		float *C = new float[size];
		for (int i = 0; i < size; i++)
		{
			A[i] = static_cast<float> (rand()) / static_cast<float> (RAND_MAX);
			B[i] = static_cast<float> (rand()) / static_cast<float> (RAND_MAX);
			C[i] = 0.0f;
		}
		timer.startClock();
		for (int i = 0; i < size; i++)
		{
			C[i] = B[i] + A[i];
		}
		seqTime = timer.getTime();
		for (int n = 2; n < max_threads; n++){
			average = 0.0;
			for (int it = 0; it < num_iter; it++)
			{
				timer.startClock();
#pragma omp parallel for num_threads(n)
				for (int i = 0; i < size; i++)
				{
					C[i] = B[i] + A[i];
					/*printf("hello from %d at %d\n", omp_get_thread_num(), i);
					printf_s("total : %d\n", omp_get_num_threads());*/
				}
				executionTime = timer.getTime();
				average += executionTime;
			}
			average /= num_iter;
			speedUp = seqTime / average;
			efficacity = speedUp / n;
			realEfficacity = speedUp / 4;
			WriteVectorData(VECTOR_OUTPUT_FILENAME, n, size, average, speedUp, efficacity, realEfficacity);
		}
	}
}
void calculPi(){
	TimerSystem timer;
	int max_threads = 10;
	int num_iter = 5;
	double average;
	float speedUp;
	float efficacity;
	float realEfficacity;
	//long nb_pas = 100000;
	double pas; int i; double x, pi, som = 0.0;
	double executionTime,seqTime;
	for (long nb_pas = 100000; nb_pas <= 1000000; nb_pas += 100000){
		pas = 1.0 / (double)nb_pas;
		pi = 0.0;
		x = 0.0;
		som = 0.0;
		timer.startClock();
		for (i = 1; i <= nb_pas; i++){
			x = (i - 0.5)*pas;
			som = som + 4.0 / (1.0 + x*x);
		}
		pi += pas * som;
		seqTime = timer.getTime();

		for (int n = 2; n < max_threads; n++){	
				average = 0.0;
				pi = 0.0;
				for (int it = 0; it < num_iter; it++)
				{
					x = 0.0;	
					som = 0.0;
					timer.startClock();
	#pragma omp parallel for reduction(+:som) private (x) num_threads(n)
					for (i = 1; i <= nb_pas; i++){
						x = (i - 0.5)*pas;
						som = som + 4.0 / (1.0 + x*x);
					}
					pi += pas * som;
					executionTime = timer.getTime();
					average += executionTime;
				}
				pi /= num_iter;
				average /= num_iter;
				speedUp = seqTime / average;
				efficacity = speedUp / n;
				realEfficacity = speedUp / 4;
				WritePIData(PI_OUTPUT_FILENAME,n, nb_pas, average, speedUp, efficacity, realEfficacity, pi);
				printf("Pas=%d", nb_pas);
				printf(" CPUtime=%lf", average);
				printf(" PI=%Lf\n", pi);
			}
	}
}


void CreateVectorData(char *fn)
{
	FILE *fp;
	fopen_s(&fp, fn, "w");
	fprintf(fp, "%s,%s,%s,%s,%s,%s\n", "nbThreads", "size", "executionTime", "speedUp", "efficacity", "realEfficacity");
	fclose(fp);
}
void CreatePIData(char *fn)
{
	FILE *fp;
	fopen_s(&fp, fn, "w");
	fprintf(fp, "%s,%s,%s,%s,%s,%s,%s\n", "nbThreads", "nbPas", "executionTime", "speedUp", "efficacity", "realEfficacity", "pi");
	fclose(fp);
}
void WritePIData(char *fn, int nbThreads, int nbPas, float timeExec, float speedUp, float efficacity, float realEfficacity,double pi)
{
	FILE *fp;
	fopen_s(&fp, fn, "a");
	fprintf(fp, "%d,%d,%lf,%lf,%lf,%lf,%Lf\n", nbThreads, nbPas, timeExec, speedUp, efficacity, realEfficacity,pi);
	fclose(fp);
}
void WriteVectorData(char *fn, int nbThreads, int size, float timeExec, float speedUp, float efficacity, float realEfficacity)
{
	FILE *fp;
	fopen_s(&fp, fn, "a");
	fprintf(fp, "%d,%d,%lf,%lf,%lf,%lf\n", nbThreads, size, timeExec, speedUp, efficacity, realEfficacity);
	fclose(fp);
}