#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[16];
        long z2[16];
        float z3[16];
        double z4[16];

        int z5[NUM_THREADS];
        long z6[NUM_THREADS];
        float z7[NUM_THREADS];
        double z8[NUM_THREADS];  



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()
{



	for(b=0; b<16; b++){
		srand ( time(NULL) );
		z1[b]=rand();
                z2[b]=rand(); 
                z3[b]=rand();  
                z4[b]=rand();   

	}

	printf("Single Threaded Performance:\n");

	gettimeofday(&starttime, NULL);
	for(a=0; a<15625000; a++){

		for(b=0; b<16; b++){
			z1[b]=z1[b]+z1[b]*z1[b]/4-2;
			z1[b]=z1[b]-2/4*z1[b]+z1[b];
			//z1[b]=b*b+z1[b];
		}
	}
	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 = 1 / te0;
        //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<15625000; a++){

                for(b=0; b<16; b++){
                        z2[b]=z2[b]+z2[b]*z2[b]/4-2;
                        z2[b]=z2[b]-2/4*z2[b]+z2[b];
                        //z1[b]=b*b+z1[b];
                }
        }

        //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; 
	te1 = 1 / 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<15625000; a++){

                for(b=0; b<16; b++){
                        z3[b]=z3[b]+z3[b]*z3[b]/4-2;
                        z3[b]=z3[b]-2/4*z3[b]+z3[b];
                        //z1[b]=b*b+z1[b];
                }
        }


	//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; 
	te2 = 1/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<15625000; a++){

                for(b=0; b<16; b++){
                        z4[b]=z4[b]+z4[b]*z4[b]/4-2;
                        z4[b]=z4[b]-2/4*z4[b]+z4[b];
                        //z1[b]=b*b+z1[b];
                }
        }

        //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 = 1 /te3;
        //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<16; 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 = 1 / te4;
	//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 = 1 / te5;
	//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 = 1 / te6;
        //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 = 1 / te7;
        //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(a=0; a<(15625000/NUM_THREADS); a++){
        
                for(b=0; b<16; b++){
                        z5[t]=z1[b]+z1[b]*z1[b]/4-2;
                        z5[t]=z1[b]-4/2*z1[b]+z1[b];
                        //z1[b]=b*b+z1[b];
                }
        }



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

void *LongWork(void *null)
{
        for(a=0; a<(15625000/NUM_THREADS); a++){
        
                for(b=0; b<16; b++){
                        z6[t]=z2[b]+z2[b]*z2[b]/4-2;
                        z6[t]=z2[b]-4/2*z2[b]+z2[b];   
                        //z1[b]=b*b+z1[b];
                }
        }
                       
        //for(counter=0; counter<1000; counter++)
        //{        
	//	LongList();
	//}
   pthread_exit((void *) 0);
}                       


void *FloatWork(void *null)
{                      
        for(a=0; a<(15625000/NUM_THREADS); a++){
        
                for(b=0; b<16; b++){
                        z7[t]=z3[b]+z3[b]*z3[b]/4-2;
                        z7[t]=z3[b]-4/2*z3[b]+z3[b];   
                        //z1[b]=b*b+z1[b];
                }
        }
 
        //for(counter=0; counter<1000; counter++)
        //{
	//	FloatList();
	//}
   pthread_exit((void *) 0);
}                       


void *DoubleWork(void *null)
{
        for(a=0; a<(15625000/NUM_THREADS); a++){
        
                for(b=0; b<16; b++){
                        z8[t]=z4[b]+z4[b]*z4[b]/4-2;
                        z8[t]=z4[b]-4/2*z4[b]+z4[b];   
                        //z1[b]=b*b+z1[b];
                }
        }

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



