#include<stdlib.h>
#include <stdio.h>
#include<time.h>
#include <omp.h>

#define Nu 1500 // Lato della matrice quadrata
int main() {
	// clock_t ticks1, ticks2;
	
	int i,j,k;
	double *A, *B, *C, *D, tmp, start_time, end_time, ok_time, bad_time;
	A = (double *)malloc(Nu*Nu*sizeof(double)); // Riserva la memoria per
	B = (double *)malloc(Nu*Nu*sizeof(double)); 
	C = (double *)malloc(Nu*Nu*sizeof(double));
	D = (double *)malloc(Nu*Nu*sizeof(double));	
	printf(" Matrix line lenght (bytes): %d \n",Nu*sizeof(double));
	
// *********** Indirizzamento Corretto 
	
	start_time=omp_get_wtime(); // fa partire il timer
	
// Inizializza le matrici A e B
	
	for(j=0;j<Nu;j++)
	{
		for(i=0;i<Nu;i++)
		{			
			*(A+(j*Nu+i))=i+j; // Nota come l'indice che varia pių velocemente sia i Numero di accessi 2*Nu^2
			*(B+(j*Nu+i))=i-j;
		}
	}
	
	
	
// Prepara la matrice trasposta  D ( Trasposta di B )
	for (k=0; k<Nu; k++)
	{
		for (j=0; j<Nu; j++)
		{
			*(D+(k*Nu+j))= *(B+(j*Nu+k));   // Solo questo punto potrebbe dare dei problemi 
											// Ma il numero di accessi č di ordine 2*Nu^2 di cui solo Nu^2 non locale
		}
	}
	

	for (i=0; i<Nu; i++)
	{
		for (j=0; j<Nu; j++)
		{
			*(C+(i*Nu+j))=0.0;
			for(k=0;k<Nu ;k++)
			{
				*(C+(i*Nu+j))  += *(A+(i*Nu+k)) *  *(D+(j*Nu+k)); // C(i,j) = sum(over k) A(i,k) * B(k,j) Numero di accessi Nu^3
			}
		}
	}
	end_time=omp_get_wtime();
	ok_time=end_time-start_time;
	printf("Tempo ok : %f  \n",ok_time);
	start_time=omp_get_wtime();
	
	
	for(j=0;j<Nu;j++){
		for(i=0;i<Nu;i++){			
			*(A+(i*Nu+j))=i+j;
			*(B+(i*Nu+j))=i-j;
		}
	}
	
	
	for (i=0; i<Nu; i++)
	{
		for (j=0; j<Nu; j++)
		{
			*(C+(i*Nu+j))=0.0;
			for(k=0;k<Nu ;k++)
			{
				*(C+(i*Nu+j))  += *(A+(i*Nu+k)) *  *(B+(k*Nu+j)); /* C(i,j) = sum(over k) A(i,k) * B(k,j) */
			}
			
		}	
	}
		end_time=omp_get_wtime();
		bad_time=end_time-start_time;
		printf("Tempo bad : %f  \n",bad_time);
		printf("Rapporto : %f , Per una matrice quadrata di lato %d\n",bad_time/ok_time, Nu );
	free(A);
	free(B);
	free(C);
	free(D);
	
	
}
