//#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
//#include <strings.h>
#include <string.h>
//include <stdlib.h>

#include <pthread.h>
//#include "cores.h"
//#include "meminfo.h"
//double memtotal;
#include "bandwidth.h"

int cores;


//cores = coresdetected();

struct timeval starttime,endtime;
double te0;
int i, b, a, a3, t, s;
int counter1, counter2, counter3;
//int mutex;
//int  pthread_mutex_init (pthread_mutex_t * mutex , pthread_mutexattr_t * attr );

pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t    mutex1;
pthread_mutex_t    mutex2;
pthread_mutex_t    mutex3;


void *FloatWork(void *null);

//int z1[16];
//long z2[16];
//float z3[128];
//double z4[16];

void usage(void){
        printf("Usage: bench [threads] [max mem MB]\n");
        //printf("Options:\n");
        //printf("        -t: threads to run for multi-threaded test\n");
        printf("        -h: this help menu\n");
	printf("\n");
}


int main(int argc, char **argv) {
	int c;
	//char *type;
	//while ((c = getopt (argc, argv, "htm:")) != -1)
	//	switch (c)   
        //   	{
			

	if(argc != 3){
		usage();
		//printf("%d\n", argc);
		exit(1);
	}else{
		for(c=0; c<argc; c++){
			//strcpy(type, argv[c]);
			//strcpy(type[1], argv[c+1]);
			if( !strcmp(argv[c], "-h") || !strcmp(argv[c], "-H") ){
				usage();
				exit(0);
			}
		}

/*
		if(argv[1] != '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'){
			usage();
			exit(1);
		}	
                if(argv[2] != '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'){
                        usage();
                        exit(1);
                }
*/		
		cores = atoi(argv[1]);
		memtotal = atof(argv[2]);
		if(memtotal == 0 || cores == 0){
			usage();
			exit(1);
		}

		printf("\nRunning %d threads\n", cores);
		printf("Using %.02lfMB of mem\n\n", memtotal);

	}
	



  int                   rc=0;
  pthread_mutexattr_t   mta;
//cores = coresdetected();
float z3[128];
//cores = 1;
rc = pthread_mutexattr_init(&mta);
rc = pthread_mutex_init(&mutex1, NULL);
rc = pthread_mutex_init(&mutex3, NULL);
rc = pthread_mutex_init(&mutex2, &mta);

        pthread_t thread[cores];

/*        pthread_attr_t attr2;
        pthread_attr_init(&attr2);
        pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_JOINABLE);
//      pthread_mutex_t  mutex1 = PTHREAD_MUTEX_INITIALIZER;


                for(t=0; t<cores; t++)
                {       
                        //printf("Creating thread %d\n", t);
        
                        a3 = pthread_create(&thread[t], &attr2, membandwidth, NULL);
                
                }
                pthread_attr_destroy(&attr2);
                for(t=0; t<cores; t++)
                {
                        a3 = pthread_join(thread[t], NULL);
                        //printf("Completed join with thread %d s= %d\n",t, s);
                }       


*/
	membandwidth();

	printf("Running Floating Point Benchmarks\n\n");

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

        for(b=0; b<128; b++){
                srand ( b );
                //z1[b]=rand();
                //z2[b]=rand();
                z3[b]=rand();
		//printf("%d: %f\n", b, z3[b]);
                //z4[b]=rand();
        }

        gettimeofday(&starttime, NULL);
        for(a=0; a<610351; a++)
	{
        	for(b=0; b<128; b++)
		{
			
z3[b]=z3[b]+z3[b]-z3[b]+z3[b]*z3[b]+z3[b]-z3[b]+z3[b]*z3[b]*z3[b]-z3[b]*z3[b]*z3[b]*z3[b]-z3[b]*z3[b]*z3[b]*z3[b]-z3[b]+z3[b]+z3[b]*z3[b]-z3[b]+z3[b]+z3[b]+z3[b]-z3[b]*z3[b]+z3[b]+z3[b]-z3[b]*z3[b]+z3[b]*z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b];;
			
//z3[b]=z3[b]*z3[b]-z3[b]+z3[b]+z3[b]*z3[b]-z3[b]+z3[b]+z3[b]+z3[b]-z3[b]*z3[b]+z3[b]+z3[b]-z3[b]*z3[b]+z3[b];
			
//z3[b]=z3[b]*z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b];
			
//z3[b]=z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b];
/*
			z3[b]=z3[b]*z3[b]-z3[b]*z3[b]*z3[b]*z3[b]-z3[b]*z3[b]*z3[b];
			z3[b]=z3[b]+z3[b]-z3[b]*z3[b]+z3[b]+z3[b]-z3[b]*z3[b]+z3[b];
			z3[b]=z3[b]+z3[b]-z3[b]*z3[b]-z3[b]+z3[b]-z3[b]*z3[b]-z3[b];
			z3[b]=z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b]*z3[b]-z3[b];

                        z3[b]=z3[b]+z3[b]-z3[b]+z3[b]*z3[b];
                        z3[b]=z3[b]*z3[b]-z3[b]+z3[b]+z3[b];
                        z3[b]=z3[b]*z3[b]-z3[b]+z3[b]-z3[b];
                        z3[b]=z3[b]+z3[b]-z3[b]+z3[b]-z3[b];
                        z3[b]=z3[b]*z3[b]-z3[b]*z3[b]*z3[b];
                        z3[b]=z3[b]+z3[b]-z3[b]*z3[b]+z3[b];
                        z3[b]=z3[b]+z3[b]-z3[b]*z3[b]-z3[b];
                        z3[b]=z3[b]*z3[b]-z3[b]*z3[b]-z3[b];
*/
                        //z3[b]=z3[b]+z3[b]*z3[b];
                        //z3[b]=z3[b]*z3[b]+z3[b];
                }
        }
        gettimeofday(&endtime, NULL);
        te0=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
	te0 = 50 /te0;
	printf("\t\tGFLOPS: %lf\n\n", te0);

        printf("\tMulti-Threaded Performance:\n");
//        pthread_t thread[cores];
        pthread_attr_t attr3;
        pthread_attr_init(&attr3);
        pthread_attr_setdetachstate(&attr3, PTHREAD_CREATE_JOINABLE);
//      pthread_mutex_t  mutex1 = PTHREAD_MUTEX_INITIALIZER;



/*
        for(b=0; b<128; b++){
                srand ( b );
                //z1[b]=rand();
                //z2[b]=rand();
                z3[b]=rand();
                //printf("%d: %f\n", b, z3[b]);
                //z4[b]=rand();
        }
*/

	gettimeofday(&starttime, NULL);
//1953132
//        for(a=0; a<244141; a++)
//        {
                for(t=0; t<cores; t++)
                {
                        //printf("Creating thread %d\n", t);
			
                        a3 = pthread_create(&thread[t], &attr3, FloatWork, NULL);

                }
		pthread_attr_destroy(&attr3);
        	for(t=0; t<cores; t++)
        	{
                	a3 = pthread_join(thread[t], NULL);
                	//printf("Completed join with thread %d s= %d\n",t, s);
        	}

//	}
        gettimeofday(&endtime, NULL);
        te0=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
        te0 = 50*cores /te0;
        printf("\t\tGFLOPS: %lf\n\n", te0);
	//printf("c1: %d c2: %d c3: %d\n", counter1, counter2, counter3);
	printf("\tdone.\n\n");
  pthread_mutex_destroy(&mutex);
  pthread_mutex_destroy(&mutex1);
  pthread_mutex_destroy(&mutex2);
  pthread_mutex_destroy(&mutex3);

	printf("Done\n");
return 0;
}

void *FloatWork(void *null)
{

	int w, y;
	float z2[128];
        for(b=0; b<128; b++){
                srand ( b );
                //z1[b]=rand();
                //z2[b]=rand();
                z2[b]=rand();
                //printf("%d: %f\n", b, z3[b]);
                //z4[b]=rand();
        }

//976562.5/NUM_THREADS

        for(w=0; w<610351; w++)
        {
                for(y=0; y<128; y++)
                {

                        
z2[y]=z2[y]+z2[y]-z2[y]+z2[y]*z2[y]+z2[y]-z2[y]+z2[y]*z2[y]*z2[y]-z2[y]*z2[y]*z2[y]*z2[y]-z2[y]*z2[y]*z2[y]*z2[y]-z2[y]+z2[y]+z2[y]*z2[y]-z2[y]+z2[y]+z2[y]+z2[y]-z2[y]*z2[y]+z2[y]+z2[y]-z2[y]*z2[y]+z2[y]*z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y];
			
//z2[y]=z2[y]*z2[y]-z2[y]+z2[y]+z2[y]*z2[y]-z2[y]+z2[y]+z2[y]+z2[y]-z2[y]*z2[y]+z2[y]+z2[y]-z2[y]*z2[y]+z2[y];
			
//z2[y]=z2[y]*z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y];
			
//z2[y]=z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y];

/*
                        z2[y]=z2[y]*z2[y]-z2[y]*z2[y]*z2[y]*z2[y]-z2[y]*z2[y]*z2[y];
			z2[y]=z2[y]+z2[y]-z2[y]*z2[y]+z2[y]+z2[y]-z2[y]*z2[y]+z2[y];
			z2[y]=z2[y]+z2[y]-z2[y]*z2[y]-z2[y]+z2[y]-z2[y]*z2[y]-z2[y];
			z2[y]=z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y]*z2[y]-z2[y];

                        z2[y]=z2[y]+z2[y]-z2[y]+z2[y]*z2[y];
                        z2[y]=z2[y]*z2[y]-z2[y]+z2[y]+z2[y];
                        z2[y]=z2[y]*z2[y]-z2[y]+z2[y]-z2[y];
                        z2[y]=z2[y]+z2[y]-z2[y]+z2[y]-z2[y];
                        z2[y]=z2[y]*z2[y]-z2[y]*z2[y]*z2[y];
                        z2[y]=z2[y]+z2[y]-z2[y]*z2[y]+z2[y];
                        z2[y]=z2[y]+z2[y]-z2[y]*z2[y]-z2[y];
                        z2[y]=z2[y]*z2[y]-z2[y]*z2[y]-z2[y];
*/
			//pthread_mutex_lock (&mutex1);
                        //z2[y]=z2[y]/z2[y]-z2[y];
			//pthread_mutex_unlock (&mutex1);
			//pthread_mutex_lock (&mutex1);
                        //z2[y]=z2[y]*z2[y]/z2[y];
			//pthread_mutex_unlock (&mutex1);
			//pthread_mutex_lock (&mutex1);
                        //z2[y]=z2[y]+z2[y]*z2[y];
			//pthread_mutex_unlock (&mutex1);
			//pthread_mutex_lock (&mutex1);
                        //z2[y]=z2[y]-z2[y]+z2[y];
			//pthread_mutex_unlock (&mutex1);
                }
        }







	//pthread_mutex_t  mutex1 = PTHREAD_MUTEX_INITIALIZER;
/*
pthread_mutex_t       mutex2;
pthread_mutex_t       mutex3;
pthread_mutexattr_t   mta;
pthread_mutexattr_init(&mta);
pthread_mutex_t       mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&mutex2, NULL);
pthread_mutex_init(&mutex3, &mta);

16204149
4212394
3906250
	pthread_mutex_init (&mutex1, NULL);
*/
//pthread_mutex_lock (&mutex1);
//        for(a=0; a<(15625000/NUM_THREADS); a++)
//        {


/*   
                //for(b=0; b<16; b++)
                //{
			pthread_mutex_lock (&mutex1);
			counter1++;
                        z3[0]=z3[0]/z3[0]-z3[0];
                        z3[0]=z3[0]*z3[0]/z3[0];
                        z3[0]=z3[0]+z3[0]*z3[0];
                        z3[0]=z3[0]-z3[0]+z3[0];
                //}
        //}
//pthread_mutex_unlock (&mutex1);
//pthread_mutex_destroy(&mutex1);

z3[1]=z3[1]/z3[1]-z3[1];
z3[1]=z3[1]*z3[1]/z3[1];
z3[1]=z3[1]+z3[1]*z3[1];
z3[1]=z3[1]-z3[1]+z3[1];

z3[2]=z3[2]/z3[2]-z3[2];
z3[2]=z3[2]*z3[2]/z3[2];
z3[2]=z3[2]+z3[2]*z3[2];
z3[2]=z3[2]-z3[2]+z3[2];

z3[3]=z3[3]/z3[3]-z3[3];
z3[3]=z3[3]*z3[3]/z3[3];
z3[3]=z3[3]+z3[3]*z3[3];
z3[3]=z3[3]-z3[3]+z3[3];

z3[4]=z3[4]/z3[4]-z3[4];
z3[4]=z3[4]*z3[4]/z3[4];
z3[4]=z3[4]+z3[4]*z3[4];
z3[4]=z3[4]-z3[4]+z3[4];

z3[5]=z3[5]/z3[5]-z3[5];
z3[5]=z3[5]*z3[5]/z3[5];
z3[5]=z3[5]+z3[5]*z3[5];
z3[5]=z3[5]-z3[5]+z3[5];
pthread_mutex_unlock (&mutex1);

pthread_mutex_lock (&mutex2);
counter2++;
z3[6]=z3[6]/z3[6]-z3[6];
z3[6]=z3[6]*z3[6]/z3[6];
z3[6]=z3[6]+z3[6]*z3[6];
z3[6]=z3[6]-z3[6]+z3[6];

z3[7]=z3[7]/z3[7]-z3[7];
z3[7]=z3[7]*z3[7]/z3[7];
z3[7]=z3[7]+z3[7]*z3[7];
z3[7]=z3[7]-z3[7]+z3[7];

z3[8]=z3[8]/z3[8]-z3[8];
z3[8]=z3[8]*z3[8]/z3[8];
z3[8]=z3[8]+z3[8]*z3[8];
z3[8]=z3[8]-z3[8]+z3[8];

z3[9]=z3[9]/z3[9]-z3[9];
z3[9]=z3[9]*z3[9]/z3[9];
z3[9]=z3[9]+z3[9]*z3[9];
z3[9]=z3[9]-z3[9]+z3[9];


z3[10]=z3[10]/z3[10]-z3[10];
z3[10]=z3[10]*z3[10]/z3[10];
z3[10]=z3[10]+z3[10]*z3[10];
z3[10]=z3[10]-z3[10]+z3[10];


z3[11]=z3[11]/z3[11]-z3[11];
z3[11]=z3[11]*z3[11]/z3[11];
z3[11]=z3[11]+z3[11]*z3[11];
z3[11]=z3[11]-z3[11]+z3[11];
pthread_mutex_unlock (&mutex2);


pthread_mutex_lock (&mutex3);
counter3++;
z3[12]=z3[12]/z3[12]-z3[12];
z3[12]=z3[12]*z3[12]/z3[12];
z3[12]=z3[12]+z3[12]*z3[12];
z3[12]=z3[12]-z3[12]+z3[12];

z3[13]=z3[13]/z3[13]-z3[13];
z3[13]=z3[13]*z3[13]/z3[13];
z3[13]=z3[13]+z3[13]*z3[13];
z3[13]=z3[13]-z3[13]+z3[13];

z3[14]=z3[14]/z3[14]-z3[14];
z3[14]=z3[14]*z3[14]/z3[14];
z3[14]=z3[14]+z3[14]*z3[14];
z3[14]=z3[14]-z3[14]+z3[14];

z3[15]=z3[15]/z3[15]-z3[15];
z3[15]=z3[15]*z3[15]/z3[15];
z3[15]=z3[15]+z3[15]*z3[15];
z3[15]=z3[15]-z3[15]+z3[15];
pthread_mutex_unlock (&mutex3);

//}
//pthread_mutex_destroy(&mutex1);


pthread_mutex_lock (&mutex1);
SetOne();
pthread_mutex_unlock (&mutex1);

pthread_mutex_lock (&mutex2);
SetTwo();
pthread_mutex_unlock (&mutex2);

pthread_mutex_lock (&mutex3);
SetThree();
pthread_mutex_unlock (&mutex3);

pthread_mutex_lock (&mutex1);
SetFour();
pthread_mutex_unlock (&mutex1);

pthread_mutex_lock (&mutex2);
SetFive();
pthread_mutex_unlock (&mutex2);

pthread_mutex_lock (&mutex3);
SetSix();
pthread_mutex_unlock (&mutex3);

pthread_mutex_lock (&mutex1);
SetSeven();
pthread_mutex_unlock (&mutex1);

pthread_mutex_lock (&mutex2);
SetEight();
pthread_mutex_unlock (&mutex2);

*/

   pthread_exit((void *) 0);
}

/*
int SetOne()
{
//printf("join with thread %d s= %d in setone\n",t, s);
	int w;
	for(w=0; w<16; w++)
	{
		z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;
}

int SetTwo()
{
        int w;
        for(w=16; w<32; w++) 
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;
}

int SetThree()
{
        int w;
        for(w=32; w<48; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}

int SetFour()
{
        int w;
        for(w=48; w<64; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}

int SetFive()
{
        int w;
        for(w=64; w<80; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}

int SetSix()
{
        int w;
        for(w=80; w<96; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}

int SetSeven()
{
        int w;
        for(w=96; w<112; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}

int SetEight()
{
        int w;
        for(w=112; w<128; w++)
        {
                z3[w]=z3[w]/z3[w]-z3[w];
                z3[w]=z3[w]*z3[w]/z3[w];
                z3[w]=z3[w]+z3[w]*z3[w];
                z3[w]=z3[w]-z3[w]+z3[w];
        }
return 0;   
}
*/
