/* pipe.c - Computing the data rate in a pipe
   Compile with
      % gcc -o pipe pipe.c -lm
   And run with  
      % pipe
***************************************************************/

#include <sys/time.h>
#include <stdlib.h>
#include <math.h>

#define BUFSIZE 64*1024         // size of buffer to pipe 
#define MAX_SIZE 32*1024*1024	// Amount of data transferered in one iteration
#define N_ITERATIONS 10		// Number oif iterations

long int time_elapsed(struct timeval time1, struct timeval time2);

int
main(int argc, char *argv[])
{
    static char buf[BUFSIZE];
    int iter;
    int total;
    int fd[2];
    int pid;
    int n;

    if (pipe(fd) < 0) {
	   perror("Error creating a pipe");
	   exit(1);
    }
    if ((pid = fork()) < 0) {
	   perror("fork");
	   exit(1);
    }
    if (pid == 0) { // child = consumer
	    double avgs, stddevs;
	    struct timeval time_start, time_end;
	    long int t;
	    double sum = 0.0;
	    double bitRate [N_ITERATIONS]; 
	    int k;

	   close(fd[1]);
	   sleep(1); // making sure producer has started
	   for (iter = 0; iter<N_ITERATIONS; ++iter) {
	       //	printf("Starting iteration %d\n", iter);
	       total = 0;
	       if (gettimeofday(&time_start, NULL) != 0) {
	      	  perror("gettimeofday");
	      	  exit(1);
	       }
	       for (; (total < MAX_SIZE); total += n) {
		 if ((n = read(fd[0], buf, BUFSIZE)) <= 0) {
		    perror("read");
		    exit(1);
		 }
	       }
	       if (gettimeofday(&time_end, NULL) != 0) {
	      	  perror("gettimeofday");
	      	  exit(1);
	       }
	       t = time_elapsed(time_start, time_end);
	       bitRate[iter] = MAX_SIZE*8.0/(1.0*t);
	       //	printf("\tbandwidth is %f Mbps\n", bitRate[iter]);
	       sum += bitRate[iter];
	  }
         avgs = sum/N_ITERATIONS;
         stddevs = 0.0;
         for (k = 0; k<N_ITERATIONS; ++k)
	          stddevs += (bitRate[k] - avgs)*(bitRate[k] - avgs);
         stddevs = sqrt(stddevs/N_ITERATIONS);
         printf("Average: %f Mbps, Standard Deviation: %f\n", avgs,
	              stddevs);
	 exit(0);
    }
    // in the parent = producer
    close(fd[0]);
    for (iter = 0; iter<N_ITERATIONS; ++iter) {
		total = 0;
		for (; (total < MAX_SIZE); total += n) {
		   n = write(fd[1], buf, BUFSIZE);
		   if (n <= 0) {
			perror("write");
			exit(1);
		   }
		}
    }
    wait(0);
    return 0;
} 


long int time_elapsed(struct timeval time1, struct timeval time2)
{
  return (1000000*(time2.tv_sec-time1.tv_sec) + (time2.tv_usec-time1.tv_usec));
}

