#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <math.h>
#include <stdio.h>
#include <pthread.h>
//include "operations.h"
//include <string.h>
#include <stdlib.h>
//include <ctype.h>
#define NUM_THREADS 4

//time_t time ( time_t * timer );
struct timeval starttime,endtime;
        //struct tm *local;
        //time_t t;
int z1[12];
long z2[12];
float z3[12];
double z4[12];
int a, b, counter;
int a1, a2, a3, a4, t, s;
long double te0,te1,te2,te3;
long double te4,te5,te6,te7;
//long double calc0, calc1, calc2, calc3, calc4, calc5, calc6, calc7;


void *IntWork(void *null);
void *LongWork(void *null);
void *FloatWork(void *null);
void *DoubleWork(void *null);

#include "intops.h"
#include "longops.h"
#include "doubleops.h"
#include "floatops.h"

main()
{

   //pthread_t thread[NUM_THREADS];
   //pthread_attr_t attr;
   //pthread_attr_init(&attr);
   //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	//int a, b;
	//long c, d;
	//float e, f;
	//double g, h;
	//int a1, t;

//int b;
//	int z1[4096];
//	long z2[4096];
//	float z3[4096];
//	double z4[4096];
	//time_t seconds;
/*
	for(a=0; a<15; a++){
		srand ( time(NULL) );
		//seconds = time(NULL);	
                //srand(seconds);
                //b = a*9/7 + 2 ;
		b = rand(); 
		z1[a] = b;
		z2[a] = b;
		z3[a] = b;
		z4[a] = b;
	}
*/

	//struct timeval starttime,endtime;
	//double te0,te1,te2,te3;
	//double te4,te5,te6,te7;
	printf("Single Threaded Performance:\n");

	gettimeofday(&starttime, NULL);

/*	for(a=0; a<500000; a++){
		for(b=0; b<10 ; b++){
			z1[b] = z1[14] * z1[13];
			z1[b] = z1[12] / 4.2;
			z1[b] = z1[11] + z1[12];
			z1[b] = z1[14] - z1[13];
		}
	}
*/

	for(a=0; a<1000; a++){
		IntList();
		IntList();
		IntList();
		IntList();
	}
	gettimeofday(&endtime, NULL);
	te0=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	//te0=1000/te0/5;

	te0 = (1000000000 / te0) / 1000000000 ;
	

        //te0 = te0 /4;
	//calc0 = .25;
	//te0 = calc0 / te0;

	printf("\n\tTest 1:\t int:   \t%Lf/GFLOPS", te0);
	printf("\n\tTest 1:\t array size:\t%lu\n", sizeof(z1));

        gettimeofday(&starttime, NULL);

/*        for(a=0; a<500000; a++){
                for(b=0; b<10 ; b++){
                        z2[b] = z2[14] * z2[13];
                        z2[b] = z2[12] / 4.2;
                        z2[b] = z2[11] + z2[12];
			z2[b] = z2[14] - z2[13];
                }
        }
*/
        for(a=0; a<1000; a++){
		LongList();
        	LongList();
        	LongList();
        	LongList();
	}
	gettimeofday(&endtime, NULL);
	te1=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	//te1=1000/te1/5;
        //te1 = 1000000000/te1/1000000;  
        //te1 = te1 / 1000000;
	te1 = (1000000000 / te1) / 1000000000 ;
        //te1 = te1 /4;
        //calc1 = .250;
        //te1 = calc1 / te1; 

        printf("\n\tTest 2:\t long:  \t%Lf/GFLOPS", te1);
	printf("\n\tTest 2:\t array size:\t%lu\n", sizeof(z2));

        gettimeofday(&starttime, NULL);
        /*for(a=0; a<500000; a++){
                for(b=0; b<10 ; b++){
                        z3[b] = z3[14] * z3[13];
                        z3[b] = z3[12] / 4.2;
                        z3[b] = z3[11] + z3[12];
			z3[b] = z3[14] - z3[13];
                }
        }*/
	for(a=0; a<1000; a++){
        	FloatList();
        	FloatList();
        	FloatList();
        	FloatList();
	}
	gettimeofday(&endtime, NULL);
	te2=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	//te2 = 1000/te2/5;
	te2 = (1000000000 / te2) / 1000000000 ;
        //te2 = te2 /4;
        //calc2 = .250;
        //te2 = calc2 / te2; 

        printf("\n\tTest 3:\t float: \t%Lf/GFLOPS", te2);
	printf("\n\tTest 3:\t array size:\t%lu\n", sizeof(z3));


        gettimeofday(&starttime, NULL);

/*        for(a=0; a<500000; a++){
                for(b=0; b<10; b++){
                        z4[b] = z4[14] * z4[13];
                        z4[b] = z4[12] / 4.2;
                        z4[b] = z4[11] + z4[12];
			z4[b] = z4[14] - z4[13];
                }
        }
*/
        for(a=0; a<1000; a++){
        	DoubleList();
        	DoubleList();
        	DoubleList();
        	DoubleList();
	}
	gettimeofday(&endtime, NULL);
	te3=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	te3 = (1000000000 / te3) / 1000000000 ;

        //te3 = te3 /4;
        //calc3 = .250;
        //te3 = calc3 / te3; 

	printf("\n\tTest 4:\t double:\t%Lf/GFLOPS", te3);
	printf("\n\tTest 4:\t array size:\t%lu\n", sizeof(z4));
	//printf("te3 %lf\n", te3);


/*
        for(a=0; a<15; a++){
               srand ( time(NULL) );
                //coord8 = rand() ;

                //seconds = time(NULL);
                //srand(seconds);
                //b = a*9/7 + 2 ;
                b = rand();
                z1[a] = b;
                z2[a] = b;
                z3[a] = b;
                z4[a] = b;
        }

*/


	printf("Multi-Threaded Performance:\n");
	pthread_t thread[NUM_THREADS];
	pthread_attr_t attr1;
	pthread_attr_init(&attr1);
	pthread_attr_setdetachstate(&attr1, PTHREAD_CREATE_JOINABLE);
        gettimeofday(&starttime, NULL);
        //for(a=0; a<1000; a++){
        	for(t=0; t<NUM_THREADS; t++)
        	{
                	//printf("Creating thread %d\n", t);
                	a1 = pthread_create(&thread[t], &attr1, IntWork, NULL);
        	}
	//}
        pthread_attr_destroy(&attr1); 
        for(t=0; t<NUM_THREADS; t++)   
        {
                a1 = pthread_join(thread[t], NULL);
                //printf("Completed join with thread %d s= %d\n",t, s);
        }
        gettimeofday(&endtime, NULL);
        te4=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;

	te4 = (250000 * NUM_THREADS * 1000 / te4) / 1000000000;

        //calc4 = te4 * 1000000; 
        //te4 = 250000*1000*NUM_THREADS;
        //te4 = te4/calc4;
	//te4=(250*NUM_THREADS/te4);
        printf("\n\tTest 1:\t int:   \t%Lf/GFLOPS", te4);
        printf("\n\tTest 1:\t array size:\t%lu\n", sizeof(z1));

pthread_attr_t attr2;
   pthread_attr_init(&attr2);
   pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_JOINABLE);

        gettimeofday(&starttime, NULL);
        //for(a=0; a<1000; a++){
        	for(t=0; t<NUM_THREADS; t++)
        	{
                	//printf("Creating thread %d\n", t);
                	a2 = pthread_create(&thread[t], &attr2, LongWork, NULL);
        	}
	//}
        pthread_attr_destroy(&attr2);   
        for(t=0; t<NUM_THREADS; t++)
        {
                a2 = pthread_join(thread[t], NULL);
                //printf("Completed join with thread %d s= %d\n",t, s);
        }
        gettimeofday(&endtime, NULL);
        te5=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	//te5 = (((20000000*NUM_THREADS)/te5)/(20*NUM_THREADS));
        te5 = (250000 * NUM_THREADS * 1000 / te5) / 1000000000;
        //calc5 = te5 * 1000000; 
        //te5 = 250000*1000*NUM_THREADS;
        //te5 = te5/calc5;
	//te5 = (250*NUM_THREADS/te5);
        printf("\n\tTest 2:\t long:  \t%Lf/GFLOPS", te5);
        printf("\n\tTest 2:\t array size:\t%lu\n", sizeof(z2));
	//printf("te5 %lf\n", te5);

pthread_attr_t attr3;
   pthread_attr_init(&attr3);
   pthread_attr_setdetachstate(&attr3, PTHREAD_CREATE_JOINABLE);

        gettimeofday(&starttime, NULL);
	//for(a=0; a<1000; a++){
        	for(t=0; t<NUM_THREADS; t++)
        	{
                	//printf("Creating thread %d\n", t);
                	a3 = pthread_create(&thread[t], &attr3, FloatWork, NULL);
        	}
	//}
        pthread_attr_destroy(&attr3);
        for(t=0; t<NUM_THREADS; t++)
        {
                a3 = pthread_join(thread[t], NULL);
                //printf("Completed join with thread %d s= %d\n",t, s);
        }
//}       
        gettimeofday(&endtime, NULL);
        te6=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
        te6 = (250000 * NUM_THREADS * 1000 / te6) / 1000000000;
        //calc6 = te6 * 1000000; 
        //te6 = 250000*1000*NUM_THREADS;
        //te6 = te6/calc6;

	//te6 = (250*NUM_THREADS/te6);
	//te6 = (((20000000*NUM_THREADS)/te6)/(20*NUM_THREADS));
        printf("\n\tTest 3:\t float: \t%Lf/GFLOPS", te6);
        printf("\n\tTest 3:\t array size:\t%lu\n", sizeof(z3));
	//printf("te6 %lf\n", te6);



pthread_attr_t attr4;
   pthread_attr_init(&attr4);
   pthread_attr_setdetachstate(&attr4, PTHREAD_CREATE_JOINABLE);

        gettimeofday(&starttime, NULL);
	//for(a=0; a<1000 ; a++){
        	for(t=0; t<NUM_THREADS; t++)
        	{
                	//printf("Creating thread %d\n", t);
                	a4 = pthread_create(&thread[t], &attr4, DoubleWork, NULL);
        	}
	//}
        pthread_attr_destroy(&attr4);   
        for(t=0; t<NUM_THREADS; t++)
        {
                a4 = pthread_join(thread[t], NULL);
                //printf("Completed join with thread %d s= %d\n",t, s);
        }
//}
        gettimeofday(&endtime, NULL);
        te7=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
        te7 = (250000 * NUM_THREADS * 1000 / te7) / 1000000000;
	//calc7 = te7 * 1000000;
	//te7 = 250000*1000*NUM_THREADS;
	//te7 = te7/calc7;
	//te7 = ((250000*1000*NUM_THREADS/te7)/1000000);
	//te7 = (((20000000*NUM_THREADS)/te7)/(20*NUM_THREADS));
        printf("\n\tTest 4:\t double:\t%Lf/GFLOPS", te7);
        printf("\n\tTest 4:\t array size:\t%lu\n", sizeof(z4));


printf("Percentage Change:\n");
printf("\n\tTest 1:\t%Lf%%\n", (1 -(te0 / te4 )) * 100);
printf("\n\tTest 2:\t%Lf%%\n", (1 -(te1 / te5 )) * 100);
printf("\n\tTest 3:\t%Lf%%\n", (1 -(te2 / te6 )) * 100);
printf("\n\tTest 4:\t%Lf%%\n", (1 -(te3 / te7 )) * 100);




return 0;

}


void *IntWork(void *null)
{
	for(counter=0; counter<1000; counter++)
        {
		IntList();
	}
   pthread_exit((void *) 0);
}

void *LongWork(void *null)
{                       
        for(counter=0; counter<1000; counter++)
        {        
		LongList();
	}
   pthread_exit((void *) 0);
}                       


void *FloatWork(void *null)
{                       
        for(counter=0; counter<1000; counter++)
        {
		FloatList();
	}
   pthread_exit((void *) 0);
}                       


void *DoubleWork(void *null)
{
        for(counter=0; counter<1000; counter++)
        {
        	DoubleList();
	}
   pthread_exit((void *) 0);
}                       



