// testet private copyprivate firstprivate lastprivate

#include "stdafx.h"
#include <omp.h>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

// private
void test1() {
	int i,j, k;
	i=1;
	j=2;
	k=3;

	// private:	
	// Wert in der Schleife unbestimmt,                       Absturz	
	#pragma omp parallel private(i,j)
	{
		i=42;
		k++;
		j=0;  // auskommentieren
		int id = omp_get_thread_num();
		printf("Thread %d  lokales i: %d  lokales j: %d   k:%d\n",id, i, j, k );
		i+=id;
		j+=id;	
	}

	printf("Nach private Loop i: %d  j: %d\n", i, j);
	printf("\n\n");
}


// firstprivate:	
// Der Wert der Variablen in der Schleife wird aus der Variablen vor der Schleife "kopiert"
void test2() {
	int i,j, k;
	i=1;
	j=2;
	k=3;
	#pragma omp parallel private(i)  firstprivate(j) 
	{
		i=42;
		int id = omp_get_thread_num();
		printf("a) Thread %d  lokales i: %d  lokales j: %d\n",id, i, j );
		i+=id+1;
		j+=id+1;	
		printf("b) Thread %d  lokales i: %d  lokales j: %d\n",id, i, j );
	}

	printf("Nach firstprivate Loop i: %d  j: %d\n", i, j);
	printf("\n\n");
}


// lastprivate:	
// der letzte Wert in der Schleife wird der Variablen im Block zugewiesen
void test3() {
	int i,j;
	i=-1;
	j=-2;
	int n=10;
	// lastprivate(i)
/*
			srand ( time(NULL) );
			j=rand()  + 1;
			if (i==1) {
				for (int j=0; j<100000; j++) {
					double d=sin(34.0);
				}
			}

*/

  /* generate secret number: */
  

	#pragma omp parallel for private(i) lastprivate(j)
		for (i=0; i<n; i++) {
			j=i+1;
			srand ( time(NULL) );
			if (i==1) {
				for (int jj=0; jj<10000000; jj++) {
					double d=sin(34.0);
				}
			}
		}
	printf("Nach lastprivate Loop i: %d  j: %d\n", i, j);
	printf("\n\n");
}



// threadprivate
/*
Diese Variablen sind global, die im parallelen Programmabschnitt 
jedoch als privat behandelt werden und als Wert uebergeben
Der globale Wert wird aber über den parallelen Abschnitt hinweg bewahrt.

also firstprivate und lastprivate mit globaler Variable
*/
int G_ThreadPrivate_id, G_ThreadPrivate_sum;
#pragma omp threadprivate(G_ThreadPrivate_id, G_ThreadPrivate_sum)

void test4() {
	int k, tid;	
	int n=10;		
	G_ThreadPrivate_id=1000;
	G_ThreadPrivate_sum=0;
	printf("Vor threadprivate Loop G_ThreadPrivate_j: %d\n", G_ThreadPrivate_id);
	omp_set_num_threads(4);
	
	omp_set_dynamic(0);
	// keine Aufteilung der Indizes, jeder "Thread" wird nur einmal ausgeführt
	// keine doppelten i
	#pragma omp parallel private(k, tid)
	{
		tid = omp_get_thread_num();        
		k = G_ThreadPrivate_id;
		G_ThreadPrivate_id=tid;
		G_ThreadPrivate_sum++;
		printf("In Loop:  tid:%d   alt:%5d   G_ThreadPrivate_j: %d\n", tid, k, G_ThreadPrivate_id);
	}
	printf("Nach threadprivate Loop G_ThreadPrivate_j: %d\n", G_ThreadPrivate_id);
	printf("\n\n");
	#pragma omp parallel private(k, tid)
	{		
		tid = omp_get_thread_num();
		printf("In Loop:  tid:%d   G_ThreadPrivate_j: %5d G_ThreadPrivate_sum:%d\n", tid, G_ThreadPrivate_id, G_ThreadPrivate_sum);
	}

   //	static  dynamic  guided  runtime

   #pragma omp parallel for schedule(dynamic,1)  private(k, tid)
	  for (int i=0; i<1000000; i++) {
         if (G_ThreadPrivate_id==2) {
				for (int jj=0; jj<10000; jj++) {
					double d=sin(34.0);
				}
			}
		 else {
				for (int jj=0; jj<100; jj++) {
					double d=sin(34.0);
				}
		 }
		G_ThreadPrivate_sum++;
	  }	

	printf("Nach threadprivate Loop G_ThreadPrivate_j: %d\n", G_ThreadPrivate_id);
	printf("\n\n");
	#pragma omp parallel private(k, tid)
	{		
		tid = omp_get_thread_num();
		printf("In Loop:  tid:%d   G_ThreadPrivate_j: %5d G_ThreadPrivate_sum:%d\n", tid, G_ThreadPrivate_id, G_ThreadPrivate_sum);
	}

	printf("Nach threadprivate Loop G_ThreadPrivate_j: %d\n", G_ThreadPrivate_id);
	printf("\n\n");
}  // test4




// copyprivate:	der zugewiesene Wert wird allen zugestellt BCast
void test5() {	
	float a, b, x, y;
	a=-1;
	b=-2;
	x=-3;
	y=-4;
	#pragma omp parallel private(a,b, x,y) 
	{
		  // nur einer durchlaueft diesen Teil, egal welcher (z. B. Master)
		#pragma omp single copyprivate(a,b) 
		{
			puts("x");
			// lesen aus Datei etc.
			a=2;
			b=3;
			int id = omp_get_thread_num();
			printf("Init durch Thread  %d   a:%f  b:%f\n", id,a,b);
		}		
		int id = omp_get_thread_num();
		x=(float)id;
		y=a+ b*x;
		printf("Thread-Id %d  a: %f  b: %f x: %f  y: %f\n", id, a, b , x, y  );

	}
}


// copyin
/*
copyin ist analog zu firstprivate für private Daten, 
allerdings nicht für threadprivate Daten, welche nicht initialisiert werden. 
Mit copyin wird der globale Wert explizit an die privaten Daten übertragen. 
Ein copyout ist nicht notwendig, da der globale Wert bewahrt wird.
Allows threads to access the master thread's value, for a threadprivate variable.
*/

int G_ThreadPrivate_k;
#pragma omp threadprivate(G_ThreadPrivate_k)

void test6() {
	int i,k;
	i=-1;	
	int n=10;
	G_ThreadPrivate_k=1001;

    printf("Vor copyin Loop G_ThreadPrivate_k: %d\n", G_ThreadPrivate_k);

	// copyin(G_ThreadPrivate_k)
	#pragma omp parallel for copyin(G_ThreadPrivate_k) private(i,k)
		for (i=0; i<n-1; i++) {
			k=G_ThreadPrivate_k;
			G_ThreadPrivate_k=i;
			printf("In Loop:  i:%d   k:%d  G_ThreadPrivate_k: %d\n", i, k, G_ThreadPrivate_k);
		}
	printf("Nach threadprivate Loop G_ThreadPrivate_k: %d\n\n\n", G_ThreadPrivate_k);

	#pragma omp parallel for copyin(G_ThreadPrivate_k) private(i,k)
		for (i=0; i<n-1; i++) {
			k=G_ThreadPrivate_k;
			printf("In Loop2:  i:%d   k:%d  G_ThreadPrivate_k: %d\n", i, k, G_ThreadPrivate_k);
		}

	printf("Nach threadprivate Loop G_ThreadPrivate_k: %d\n", G_ThreadPrivate_k);
	printf("\n\n");
}



int _tmain(int argc, _TCHAR* argv[])
{
	omp_set_num_threads(4);
	puts("Vor den Methoden");
	printf("Max Threads: %d   Anzahl CPU: %d\n\n", omp_get_max_threads(), omp_get_num_procs() );

	//test1();  // private
	//test2();  // firstprivate
	//test3();  // lastprivate
	test4();  // threadprivate
	//test5();  // copyprivate


     // ----------------------------------------------------------------


	char ch[200];
	gets(ch);
	
	return 0;
}




