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


/*
 Version 3
 Aufteilen von 1 bis n auf     m Workers 
 Die Aufteilung wird über ein zusätzliches Feld realisiert
    siehe Feld    offset  und sendcounts
 
   MPI_Scatterv

 Nach der Übermittlung wird eine Funktion ausgeführt, add um 2
 und dann werden mittels der Gather-Anweisung die Daten wieder gesammelt
 MPI_Gatherv
*/

const int C_MAX = 20;

const int ROOT = 0;

// Scatter 3

int main(int argc, char *argv[]) {
	int     root = 0;
    int     numprocs, Pid, i;
    int     x_root[C_MAX+1];  // quelle
    int     y_root[C_MAX+1];
	int     client_size;
    

	int sendcounts[C_MAX+1]; // speichert die Längen pro Client
	int offset[C_MAX+1]; // speichert die Offset por Client
	int rest;
	int summe;


	int retcode;
    
	int     x[C_MAX+1];
    int     y[C_MAX+1];

    if (MPI_Init(&argc,&argv)!=MPI_SUCCESS) {
		printf("MPI_Init failed.\n");
		return -1;
    }

    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &Pid);

    printf("Proc %d: Ich bin am Leben. Anz:%d\n", Pid, numprocs);

	if (numprocs>C_MAX) {
		puts("Fehler, zu viele Prozesse");
	}


    if (Pid==ROOT) {
		  // Daten im Master erzeugen
		for (i=0; i<C_MAX; i++) {
			x_root[i] = i+1;
		}
		puts("  ");
    }

	
    
	  // zur Kontrolle auf Null gesetzt, pro Client
	for (i=0; i<C_MAX; i++) {
		x[i] = 0;  // Client array
	}

	   // Aufteilen der Felder
	client_size = C_MAX / numprocs;  // ganzzahlig !!!

	for (i=0; i<C_MAX; i++) {
		sendcounts[ i ] = client_size;
	}

	  // gib es ein Rest ?
	rest = C_MAX  - numprocs*client_size;
	if (rest>0) {
		// es fehlen noch welche,  verteilen vom Anfang
		for (i=0; i<rest; i++) {
			sendcounts[ i ] += 1;
		}
	}

	 // Berechne den StartOffset pro Client
	summe=0;
	for (i=0; i<C_MAX; i++) {
		offset[ i ] = summe;
		summe += sendcounts[ i ];
	}
	

	if (Pid==0) {
		// Ausgabe der Bereiche, nur Master
		for (i=0; i<numprocs; i++) {
			printf("Ausgabe Root: i:%d  offset %d  sendcounts:  %d  \n", i, offset[i], sendcounts[i]);		
		}
		puts("  ");
		puts("  ");
		
		for (i=0; i<C_MAX; i++) {
			printf("Ausgabe Root:   i:  %d  x_root[i]: %d\n",i, x_root[i]);
		}
		puts("  ");
		puts("  ");
	}



	// Verteilen des Feldes
    retcode = MPI_Scatterv(x_root, sendcounts, offset, MPI_INT,     
		  
		x, sendcounts[Pid],  MPI_INT, ROOT, MPI_COMM_WORLD);
	
	
	if ( retcode!= MPI_SUCCESS) {
		puts("Fehler beim Aufruf von Scatter");
        MPI_Finalize();
		return 1;
	}
  
	puts("  ");
	puts("  ");
	puts("Ausgabe der Clientdaten:");


	  // zur Kontrolle ausgeben
	for (i=0; i<sendcounts[Pid]; i++) {
		printf(" Pid %d  i:  %d  x[i]: %d\n",Pid, i, x[i]);		
	}



	 // Berechnen der Funktionen     y=x+2, gilt auch für Root
	for (i=0; i<sendcounts[Pid]; i++) {
		//printf("Pid %d  i:  %d  x[i]: %d\n",Pid, i, x[i]);		
		y[i] = x[i] + 2;
	}

    
	//MPI_Barrier(MPI_COMM_WORLD);

   
      // Sammeln der Daten an den Root
    retcode = MPI_Gatherv(y, sendcounts[Pid], MPI_INT,  
		y_root, sendcounts,  offset, MPI_INT, ROOT, MPI_COMM_WORLD);
	
	if ( retcode!= MPI_SUCCESS) {
		puts("Fehler beim Aufruf von Gather");
	}


      // Ausgabe der Ergebnisse
	if (Pid == ROOT ) {
		puts("");
		puts("");
		puts("Root:  Ausgabe der Ergebnisse:");
		for (i=0; i<C_MAX; i++) {
			printf("Root:  i:  %d  y_root[i]: %d\n", i, y_root[i]);
		}
		puts("");
		puts("");
	}


    MPI_Finalize();
	return 0;
}  // main






/*



Proc 0: Ich bin am Leben.
  
Ausgabe Root:   offset 0  sendcounts:  8  
Ausgabe Root:   offset 8  sendcounts:  7  
Ausgabe Root:   offset 15  sendcounts:  7    
  
  
Ausgabe der Clientdaten:
 Pid 0  i:  0  x[i]: 1
 Pid 0  i:  1  x[i]: 2
 Pid 0  i:  2  x[i]: 3
 Pid 0  i:  3  x[i]: 4
 Pid 0  i:  4  x[i]: 5
 Pid 0  i:  5  x[i]: 6
 Pid 0  i:  6  x[i]: 7
 Pid 0  i:  7  x[i]: 8



Proc 2: Ich bin am Leben.
Ausgabe der Clientdaten:
 Pid 2  i:  0  x[i]: 16
 Pid 2  i:  1  x[i]: 17
 Pid 2  i:  2  x[i]: 18
 Pid 2  i:  3  x[i]: 19
 Pid 2  i:  4  x[i]: 20
 Pid 2  i:  5  x[i]: 21
 Pid 2  i:  6  x[i]: 22


Proc 1: Ich bin am Leben.
  
Ausgabe der Clientdaten:
 Pid 1  i:  0  x[i]: 9
 Pid 1  i:  1  x[i]: 10
 Pid 1  i:  2  x[i]: 11
 Pid 1  i:  3  x[i]: 12
 Pid 1  i:  4  x[i]: 13
 Pid 1  i:  5  x[i]: 14
 Pid 1  i:  6  x[i]: 15




Ausgabe Root:   i:  0  x_root[i]: 1
Ausgabe Root:   i:  1  x_root[i]: 2
Ausgabe Root:   i:  2  x_root[i]: 3
Ausgabe Root:   i:  3  x_root[i]: 4
Ausgabe Root:   i:  4  x_root[i]: 5
Ausgabe Root:   i:  5  x_root[i]: 6
Ausgabe Root:   i:  6  x_root[i]: 7
Ausgabe Root:   i:  7  x_root[i]: 8
Ausgabe Root:   i:  8  x_root[i]: 9
Ausgabe Root:   i:  9  x_root[i]: 10
Ausgabe Root:   i:  10  x_root[i]: 11
Ausgabe Root:   i:  11  x_root[i]: 12
Ausgabe Root:   i:  12  x_root[i]: 13
Ausgabe Root:   i:  13  x_root[i]: 14
Ausgabe Root:   i:  14  x_root[i]: 15
Ausgabe Root:   i:  15  x_root[i]: 16
Ausgabe Root:   i:  16  x_root[i]: 17
Ausgabe Root:   i:  17  x_root[i]: 18
Ausgabe Root:   i:  18  x_root[i]: 19
Ausgabe Root:   i:  19  x_root[i]: 20
Ausgabe Root:   i:  20  x_root[i]: 21
Ausgabe Root:   i:  21  x_root[i]: 22
  



  */