/* $Id: readtest_ng.c,v 1.7 1999/10/21 16:36:55 garner Exp $ */

/* 
 * $Log: readtest_ng.c,v $
 * Revision 1.7  1999/10/21 16:36:55  garner
 * readtest with IBP additions
 *
 * Revision 1.6  1999/10/15 15:10:39  garner
 * Working version without IBP yet.  Multi-MPI apps work.
 *
 * Revision 1.5  1999/10/06 21:50:50  garner
 * Working with new server
 *
 * Revision 1.4  1999/10/04 18:02:33  garner
 * *** empty log message ***
 *
 */
#include "mpi.h"
#include "mpio.h"  /* not necessary with MPICH 1.1.1 or HPMPI 1.4 */
#include "client_lib.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>



/* Each process writes to separate files and reads them back. 
   The file name is taken as a command-line argument, and the process rank 
   is appended to it. */ 
char errbuf[MPI_MAX_ERROR_STRING];

/* 
* MPIO_Type_set_bounds  surround a type with holes (increasing the extent) 
*/ 
int MPIO_Type_set_bounds( 
			 int displacement, /* Displacement from the lower bound */ 
			 int ub, /* Set the upper bound */ 
			 MPI_Datatype oldtype, /* Old datatype */ 
			 MPI_Datatype *newtype) /* New datatype */ 
{
    int blocklength[3]; 
    MPI_Datatype type[3];
    MPI_Aint disp[3]; 
    blocklength[0] = 1; 
    disp[0] = 0; 
    type[0] = MPI_LB; 
    blocklength[1] = 1; 
    disp[1] = displacement; 
    type[1] = oldtype; 
    blocklength[2] = 1; 
    disp[2] = ub; 
    type[2] = MPI_UB; 
    /* 
     * newtype = 
     * -- (LB, 0), (oldtype, displacement), (UB, ub)  
     */ 
    return MPI_Type_struct(3, blocklength, disp, type, newtype); 
}

/* 
* MPIO_Type_scatter_gather  generate scatter/gather datatype to access data 
* block in rank order. Blocks are identical in size 
* and datatype. 
*/ 
int MPIO_Type_scatter_gather( 
			     MPI_Comm comm, /* Communicator group */ 
			     MPI_Datatype oldtype, /* Block datatype */ 
			     MPI_Datatype *newtype) /* New datatype */ 
{ 
    int size, rank; 
    int extent; 
    MPI_Type_extent(oldtype,&extent); 
    MPI_Comm_size(comm, &size);
    MPI_Comm_rank(comm, &rank);
    return MPIO_Type_set_bounds(rank*extent, size*extent, oldtype, newtype); 
}


int main(int argc, char **argv)
{
    int i, rank, ndoubles, len, locallen, flag, numelements;
    int myappnum, totalapps, localTMPlen, groupsize;
    double min, max, sum, avg, avgsum;
    double *minAll, *maxAll, *sumAll, *avgAll;
    int *numelementsAll,  totalElements;
    int errlen, ierr;
    MPI_Offset numbytes;
    char *globalfn, *localfn, *localfnTMP, *tmp;
    double *buf;
    MPI_File fh;
    MPI_Status status;
    MPI_Datatype filetype;

    struct stat sbuf;
    int cc, fsize;
    MPI_Offset size;
    char fname[256];

    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &groupsize);

    min = max = sum = avg = 0.0;
    buf = (double *) malloc(sizeof(double));

    
/* process 0 takes the file name as a command-line argument and 
   broadcasts it to other processes */
    if (argc < 5) {
	printf("\n*#  Usage: readtest_ng globalfn localfn myAppNum totalApps\n\n");
	MPI_Abort(MPI_COMM_WORLD, 1);
    } else {
	myappnum = atoi(argv[3]);
	totalapps = atoi(argv[4]);
	
	/* Get filename from argv */
	len = strlen(argv[1]);
	globalfn = (char *) malloc(len+10);
	strcpy(globalfn, argv[1]);
	
	locallen = strlen(argv[2]);
	localfn = (char *) malloc(locallen+10);
	strcpy(localfn, argv[2]);
	
	tmp = (char *) malloc(len+10);
	strcpy(tmp, localfn);
	sprintf(localfn, "%s.%d", tmp, myappnum);
	locallen = strlen(localfn);
	
	localTMPlen = locallen;
	localfnTMP = (char *) malloc(localTMPlen+10);
	/* strcpy(localfnTMP, localfn); */
	sprintf(localfnTMP, "/tmp/%s", localfn);
	localTMPlen = strlen(localfnTMP);
	
    }
    
    printf("\nmyrank: %d gfn: %s lfn: %s  myapp: %d  apps: %d\n", rank, globalfn, localfnTMP, myappnum, totalapps);
    
    MPI_Conn_getfile_view(globalfn, localfn, myappnum, totalapps,  MPI_DOUBLE, &fsize, MPI_COMM_WORLD);
    
    /* open the file and read the data in */
    
    printf("Opening file = %s with size %d \n", localfnTMP,  fsize);
    
    ierr = MPI_File_open(MPI_COMM_WORLD, localfnTMP, MPI_MODE_RDWR, 
			 MPI_INFO_NULL, &fh);

    if (ierr != MPI_SUCCESS){
	printf("Error in MPI_File_open = %d rank = %d\n", ierr, rank);
	MPI_Error_string(ierr, errbuf, &errlen); 
	printf("%s\n", errbuf);
	MPI_Abort(MPI_COMM_WORLD, 1);
    } else {
	printf("File %s successfully opened by %d\n", localfnTMP, rank);
    }
    
    MPI_File_get_size(fh, &size);
    printf("file size = %lld bytes\n", size);

    MPIO_Type_scatter_gather(MPI_COMM_WORLD, MPI_DOUBLE, &filetype);
    
    ierr = MPI_File_set_view(fh, 0, MPI_DOUBLE, filetype, "native", MPI_INFO_NULL);
    if (ierr != MPI_SUCCESS){
	printf("Error in MPI_File_set_view = %d  rank = %d\n", ierr, rank);
	MPI_Error_string(ierr, errbuf, &errlen); 
	printf("%s\n", errbuf);
    }

    MPI_Barrier(MPI_COMM_WORLD);
    buf = (double *)malloc(fsize);
    
    numelements = (fsize / sizeof(double))/groupsize;
    MPI_File_read_all(fh, buf, numelements, MPI_DOUBLE, &status);
    
    min = buf[0];
    max = buf[0];

    for (i = 0; i < numelements; i++) {
	if ( min > buf[i]) min = buf[i];
	if ( max < buf[i]) max = buf[i];
	sum += buf[i];
    }
    avg = sum / numelements;
    printf("--- Sub-rank: %d Min: %f Max: %f Sum: %f Avg: %f\n", rank, min, max, sum, avg);

    if (rank == 0) {
	minAll = (double *)malloc(groupsize * sizeof(double));
	maxAll = (double *)malloc(groupsize * sizeof(double));    
	sumAll = (double *)malloc(groupsize * sizeof(double));
	avgAll = (double *)malloc(groupsize * sizeof(double));
	numelementsAll = (int *)malloc(groupsize * sizeof(int));
    }

    MPI_Gather(&min, 1, MPI_DOUBLE, minAll, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    MPI_Gather(&max, 1, MPI_DOUBLE, maxAll, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    MPI_Gather(&sum, 1, MPI_DOUBLE, sumAll, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    MPI_Gather(&avg, 1, MPI_DOUBLE, avgAll, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    MPI_Gather(&numelements, 1, MPI_INT, numelementsAll, 1, MPI_INT, 0, MPI_COMM_WORLD);

    sum = 0;
    avg = 0;
    avgsum = 0;
    totalElements = 0;
    if (rank == 0) {
	min = minAll[0];
	max = maxAll[0];
	for (i = 0; i < groupsize; i++) {
	    if ( min > minAll[i]) min = minAll[i];
	    if ( max < maxAll[i]) max = maxAll[i];
	    sum += sumAll[i];
	    avgsum += avgAll[i] * numelementsAll[i];
	    totalElements += numelementsAll[i];
	}
    avg = avgsum / totalElements;
    printf("\n--- Rank: %d Min: %f Max: %f Sum: %f Avg: %f\n", rank, min, max, sum, avg);
    }

    MPI_File_close(&fh);

    if (!rank) { /* Root do cleanup here */
	printf("Done\n");
    }

    MPI_Finalize();
    return 0; 
}
