/**********************************************************************

                 Example 9  (pthreads_MatrixVector_CB.c)

Objective                 : To Compute Matrix Vector Mulyiplication using
                            pthreads and Checkerboard algorithm. Matrix and
                            vector are populated internally with 1 and 2.
			                   Demonstrates the use of:

			                   pthread_create()
			                   pthread_join()

Input                     : Number of Rows, Columns and threads

Output                    : The resultant vector.

 **********************************************************************/ 



#include <stdio.h>
#include <pthread.h>
#include <math.h>

int             i, j, **mat, *vec, **fvec, *rvec, noofrows, noofcols,
                rcs, ccs;

int             num_threads, perfect_square, rmajor, cmajor;


void 
mul(int id) 
{
	
	if (id > (perfect_square - 1))
		
		rcs = (id % perfect_square) * rmajor;
	
		else
		
			rcs = id * rmajor;
	
		ccs = (id / perfect_square) * cmajor;
	
		printf("\nI am thread no %d and calculated from %d to %d rows and %d to %d cols ", id, rcs, rcs + rmajor - 1, ccs, ccs + cmajor - 1);
	
		for (i = rcs; i < rcs + rmajor; i++)
		
			for (j = ccs; j < ccs + cmajor; j++)
			
		{
			
				fvec[i][j] = mat[i][j] * vec[j];
			
		} 
} 

main(int argc, char *argv[]) 
{
	
		pthread_t * threads;

		if (argc < 4 ){
				 printf(" Insufficient Arguments. \n");
				 printf(" Usage: exe rows cols threads. \n");
				 return;
				 }
	
		noofrows = abs(atoi(argv[1]));
	
		noofcols = abs(atoi(argv[2]));
	
		num_threads = abs(atoi(argv[3]));
	
		**mat, *vec;
	
		perfect_square = (sqrt((double) num_threads));
	
		
		if (perfect_square * perfect_square != num_threads)
		
	{
		
			printf("\nThreads should be  square numbers ");
		
			exit(0);
		
	} 
		
		if (noofrows % perfect_square != 0 || noofcols % perfect_square != 0)
		
	{
		
			printf("\nMatrix can't be evenly distributed evenly among threads");
		
			exit(0);
		
	} 
		threads = (pthread_t *) malloc(num_threads * sizeof(pthread_t));
	
		mat = (int **) malloc(noofrows * sizeof(int));
	
		vec = (int *) malloc(noofcols * sizeof(int));
	
		fvec = (int **) malloc(noofrows * sizeof(int));
	
		rvec = (int *) malloc(noofrows * sizeof(int));
	
		
		rmajor = noofrows / perfect_square;
	
		cmajor = noofcols / perfect_square;
	
		
		for (i = 0; i < noofrows; i++)
		
	{
		
			mat[i] = (int *) malloc(noofcols * sizeof(int));
		
			fvec[i] = (int *) malloc(noofcols * sizeof(int));
		
			for (j = 0; j < noofcols; j++)
			
		{
			
				mat[i][j] = 1;
			
				fvec[i][j] = 0;
			
		} 
	} 
		
		
		for (i = 0; i < noofcols; i++)
		
			vec[i] = 2;
	
		
		for (i = 0; i < num_threads; i++)
		
			pthread_create(&threads[i], NULL, (void *(*) (void *)) mul, (void *) i);
	
		
		for (i = 0; i < num_threads; i++)
		
			pthread_join(threads[i], NULL);
	
		printf("\nThe final resultant vector is ");
	
		for (i = 0; i < noofrows; i++)
		
	{
		
			for (j = 0; j < noofcols; j++)
			
				rvec[i] += fvec[i][j];
		
			printf("%4d ", rvec[i]);
		
	} 
} 
