#include <stdio.h>  /* Include the stdio library */
#include <stdlib.h> /* Include the stdlib library */
#include <math.h>   /* Include the math library */
#include "../include/pvm3.h"  /* Include the pvm3 library */

#define SLAVE "scalc"  /* The program which the slave processes run */
#define PROC 10  /* The number of slave processes */

int main(int argc, char *argv[]) {
    int mytid, tids[PROC];  /* Creates storage for the master process' tid
                               as well as the slave process' tids */
    int no, i=0, j, who, msgtype; /* no stores the number of processes
                                actually spawned, i+j are counters, who is
                                the slave process which has sent the
                                message, msgtype is the key used to
                                confirm that information passed between
                                master and slaves is valid */
    int partition, remainder; /* partition holds the size of the
                                 partitions to be sent to the slave
                                 processes, while remainder is the amount
                                 of numbers left over */
    int tpartition; /* The true partition for a particular slave */
    float data[1000], lsums[10]; /* data[] holds the numbers read from the
                                    file while lsums holds the local sums
                                    received from the processes */
    float mean, variance, std_dev; /* mean holds the mean, variance holds
                                    the variance, and std_dev holds the
                                    standard deviation */
    int num_of_vals; /* Holds the number of values read from the file */
    int so_far = 0; /* Holds the amount of data partitioned so far */
    char filename[100]; /* Holds the name of the file */
    FILE *infile; /* The file to be read from */

    mytid = pvm_mytid(); /* Enroll in PVM and set mytid to the tid of the
                            master process */

    /* Check to see if the user has input a filename in the
       command line */
    if(argc < 2) {
        printf("Usage: mcalc <filename>\n");
        pvm_exit();
        exit(0);
    }

    /* Open the file to be read from, giving an error message if the
       file is unable to be read */
    strcpy(filename, argv[1]);
    if((infile = fopen(filename, "r")) == NULL) {
        printf("Error opening '%s'.\n", filename);
        pvm_exit();
        exit(0);
    }

    /* The following attempts to create 10 slave processes, which will run
       the scalc program. The various tids of the slave processes are
       stored in the tids[] array, and the function returns the number of
       slaves actually spawned. */

    no = pvm_spawn(SLAVE, (char**)0, 0, "", PROC, tids);
    if(no < PROC) { /* If the number of processes created is less than
                       the number which should have been created... */
        printf("Error spawning slaves.\n"); /* Print error message */
        for(i = 0; i < no; i++) pvm_kill(tids[i]); /* close slave
                                                      processes */
        pvm_exit(); /* Close master process */
        exit(0); /* Exit the program */
    }

    /* Read in the data from the file, and record the number of values */
    i = 0;
    while(fscanf(infile, "%f", &data[i]) != EOF) i++;
    num_of_vals = i;

    /* Find the partition size, and any remaining values */
    partition = num_of_vals/PROC;
    remainder = num_of_vals % PROC;

    /* Send out the data. Note that any remaining values are divided
       up as best they can be between the slave processes */
    msgtype = 0;
    for(i = 0; i < PROC; i++) {
        pvm_initsend(PvmDataDefault);
        pvm_pkint(tids, PROC, 1);
        if(remainder > 0) {
            tpartition = partition + 1;
            remainder--;
        }
        else tpartition = partition;
        pvm_pkint(&tpartition, 1, 1);
        for(j = so_far; j < so_far+tpartition; j++) {
            pvm_pkfloat(&data[j], 1, 1);
        }
        so_far = so_far + tpartition;
        pvm_send(tids[i], msgtype);
    }

    /* Receive the local sums from the slave processes */
    msgtype = 1;
    for(i = 0; i < PROC; i++) {
        pvm_recv(-1, msgtype);
        pvm_upkint(&who, 1, 1);
        pvm_upkfloat(&lsums[who], 1, 1);
    }

    /* Calculate the mean (the sum of the local sums over the
       number of values) */
    mean = 0;
    for(i = 0; i < PROC; i++) {
        mean += lsums[i];
    }
    mean = mean/num_of_vals;

    /* Broadcast the mean to the slave processes */
    msgtype = 2;
    pvm_initsend(PvmDataDefault);
    pvm_pkfloat(&mean, 1, 1);
    pvm_mcast(tids, PROC, msgtype);

    /* Receive the local sums of the squared differences */
    msgtype = 3;
    for(i = 0; i < PROC; i++) {
        pvm_recv(-1, msgtype);   
        pvm_upkint(&who, 1, 1);
        pvm_upkfloat(&lsums[who], 1, 1);
    }

    /* Find the variance (the sum of the local sums of the squared
       differences over the number of values */
    variance = 0;
    for(i = 0; i < PROC; i++) {
        variance += lsums[i];
    }
    variance = variance/num_of_vals;
    
    /* Find the standard deviation (the square root of the variance) */
    std_dev = sqrt(variance);

    /* Print the mean, variance, and standard deviation */
    printf("Mean: %f\n", mean);
    printf("Variance: %f\n", variance);
    printf("Standard Deviation: %f\n", std_dev);

    /* Exit the program */
    pvm_exit();
    exit(0);
}
