/**
 * Programm für Aufgabe 2.4 unter Linux
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>

// some modifications made
int is_prime(int n)
{
    int i;
    if (n < 2)
	return 0;
// much faster so
    int square = (int)sqrt(n);
    for (i = 2; i <= square; ++i) {
	if ((n % i) == 0)
	    return 0;
    }

    return 1;
}

/*grobe Abschätzung der Rechendauer für die Bestimmung
der Primzahlen von 2 bis n mit oben angegeben
Algorithmus */


double f(double n)
{
    return exp(log(n) * (3.0 / 2.0));
}

double f_reverse(double n)
{
    return exp(log(n) * (2.0 / 3.0));
}

void usage()
{
    fprintf(stderr,
	    "Usage: aufg24 <highest number to test> <number of threads>\n");
}

int main(int argc, char *argv[])
{
// Überprüfen, ob korrekte Anzahl an Parametern übergeben
    if (argc != 3) {
	usage();
	exit(EXIT_FAILURE);
    }

/*
gleichmäßige Aufteilung:
*/

    int anzahl_procs = atoi(argv[2]);

    if (anzahl_procs < 1 || anzahl_procs>10000) {
	fprintf(stderr, "number of processes less than 1 or greater than 10000!\n");
	exit(EXIT_FAILURE);
    }

    int bis = 2; // we begin with three and add 2 to the total sum
    int von = 0;

    int max_primzahl = atoi(argv[1]);

    if (max_primzahl < 3 || max_primzahl == INT_MAX) {
	fprintf(stderr,
		"highest number to test less than 3 or bigger than %i!\n",
		INT_MAX);
	exit(EXIT_FAILURE);
    }
    //printf("We add the primes from 0 to %d with the help of %d worker processes\n",max_primzahl,anzahl_procs);

    int i;

    pid_t pidContainer[anzahl_procs];
    if (sizeof(pidContainer) != sizeof(pid_t) * anzahl_procs) {
	fprintf(stderr, "Could not allocate memory for pid array!\n");
	exit(EXIT_FAILURE);
    }

    double cycles = f(max_primzahl);

    for (i = 0; i < anzahl_procs; ++i) {


	von = bis + 1; // we begin with von = 3 and add 2 (first prime) to the total sum

	bis = (int)f_reverse((i + 1) * cycles / anzahl_procs);

	if (i == (anzahl_procs - 1)) {	/* letzter Prozess */
	    bis = max_primzahl;
	}

	if (bis < von) {
	    fprintf(stderr, "To many processes for too less numbers\n");
	    exit(EXIT_FAILURE);
	}
	//Prozess mit Intervall<von> <bis>starten 
	if ((pidContainer[i] = fork()) < 0) {
	    perror("fork did not work for us 8-(");
	    exit(EXIT_FAILURE);
	}
	if (!pidContainer[i]) {
	    // we are a child, so von and bis are our limit
	    // last two digits of the sum of primes in our interval
	    int sum = 0;
	    int j;
	    // printf("Von: %d bis: %d \n",von,bis);

	    // adjust values of von and bis to save time
	    if (!(von % 2))	// would be an odd number
		++von;
	    if (!(bis % 2))	// dito
		--bis;
	    for (j = von; j <= bis; j+=2) // only compute odd numbers
		if (is_prime(j)) {
		    // we only need the last two digits
		    sum += j % 100;
		    sum %= 100;
		}
	    // report our work as client
	    exit(sum);
	}
	// we are the dispatcher, continue
    }
// we are the dispatcher, now collect the results of our process 
    pid_t j;
    int totalsum = 2; // first prime that is not calculated by the processes
    int status;
    for (j = 0; j < anzahl_procs; ++j) {
	if (waitpid(pidContainer[j], &status, 0) < 0) {
	    perror("waitpid did not work for us!");
	    exit(EXIT_FAILURE);
	}

	if (WIFEXITED(status)) {
	    totalsum += WEXITSTATUS(status) % 100;
	    totalsum %= 100;
	} else {
	    fprintf(stderr,
		    "At least one our our processes did not exit properly\n");
	    exit(EXIT_FAILURE);
	}
    }
    printf("%s%d\n",totalsum<10?"0":"", totalsum);
    exit(EXIT_SUCCESS);
}

