/****************************************************************************** * MC514 - Laboratório 1 * ****************************************************************************** * Grupo: * * Gustavo Sverzut Barbieri ra008849 * * Ivens Prates Telles Alves ra008908 * ****************************************************************************** * Para compilar usando Threads, usar -DUSE_PTHREAD na linha de comando do * compilador. Exemplo: * * * Compilando usando threads: * gcc mult_matriz.c -o mult_matriz -lpthread -DUSE_PTHREAD * * * Compilando sem threads: * gcc mult_matriz.c -o mult_matriz * * OBS: Este arquivo está comentado de tal forma que se pode gerar * documentação usando o Doxygen. */ #ifdef USE_PTHREAD # include #endif #include #include #define matpos(row, col, col_size) ( (row) * (col_size) + (col) ) typedef struct matrix_s { int rows; /* number of rows */ int cols; /* number of cols */ int *matrix; /* matrix data (linear/1D). use matpos() (macro) to access data using 2D pairs (row, cool) */ } matrix_t; /** * matrix_create: create a new matrix with size: rows x cols * * @param rows number of rows * @param cols number of cols * @return the new *(malloc)ated* matrix (use matrix_destroy() after use) */ matrix_t *matrix_create(int rows, int cols) { matrix_t *mat = NULL; int i; if ( (rows < 1) && (cols < 1) ) return NULL; if ( (mat = (matrix_t*)calloc(1, sizeof(matrix_t))) == NULL) return NULL; if ( (mat->matrix = (int*)calloc(rows * cols, sizeof(int))) == NULL) { free(mat); return NULL; } mat->rows = rows; mat->cols = cols; return mat; } /** * matrix_destroy: destroy the given matrix * * @param mat the matrix to be destroyed (and freed) */ void matrix_destroy(matrix_t *mat) { if (mat) { if (mat->matrix) free(mat->matrix); free(mat); mat = NULL; } } /** * matrix_print: print the given matrix * * @param mat the matrix to be printed */ void matrix_print(matrix_t *mat) { int i, j; if (! mat) return; printf("%d %d\n", mat->rows, mat->cols); for (i=0; i < mat->rows; i++) { for (j=0; j < mat->cols; j++) { printf("%d ", mat->matrix[matpos(i, j, mat->cols)]); } printf("\n"); } printf("\n"); } /** * matrix_multiply_row: multiply the the src1_mat's row against the src2_mat's col, storing the result at dst_mat, position (row,col) * * @param src1_mat the source matrix #1 from which we use the 'row'. * @param src2_mat the source matrix #2 from which we use the 'col'. * @param dst_mat the destination matrix, in which we store the src1_mat(row) * src2_mat(col). * @param row the src1_mat's row number to use. * @param col the src2_mat's col number to use. */ void matrix_multiply_row(matrix_t *src1_mat, matrix_t *src2_mat, matrix_t *dst_mat, int row, int col) { int i, pos, val; pos = matpos(row, col, dst_mat->cols); val = 0; for (i=0; i < src1_mat->cols; i++) { val += src1_mat->matrix[matpos(row, i, src1_mat->cols)] * src2_mat->matrix[matpos(i, col, src2_mat->cols)]; } dst_mat->matrix[pos] = val; } #ifdef USE_PTHREAD /* structure to use as parameter to pthread matrix_multiply_row() version */ typedef struct pthread_matrix_multiply_row_s { matrix_t *src1_mat; matrix_t *src2_mat; matrix_t *dst_mat; int row; int col; } pthread_matrix_multiply_row_t; /** * matrix_multiply_row_pthread: a wrapper to matrix_multiply_row to be used with pthread * * @param v will be converted to pthread_matrix_multiply_row_t, which has all the parameters to be passed to matrix_multiply_row() * @return always NULL */ void *matrix_multiply_row_pthread(void *v) { pthread_matrix_multiply_row_t *mat_mult = (pthread_matrix_multiply_row_t*)v; if (! mat_mult) return NULL; matrix_multiply_row(mat_mult->src1_mat, mat_mult->src2_mat, mat_mult->dst_mat, mat_mult->row, mat_mult->col); return NULL; } #endif /** * matrix_multiply: multiply src1_mat * src2_mat * * @param src1_mat the left side matrix * @param src2_mat the right side matrix * @return the resultant matrix or NULL, if errors occurred. */ matrix_t *matrix_multiply(matrix_t *src1_mat, matrix_t *src2_mat) { int i, j; matrix_t *dst_mat = NULL; #ifdef USE_PTHREAD int pthread_pos; pthread_t *pthread; pthread_matrix_multiply_row_t *pthread_param; #endif /* check if matrices are valid */ if ( (src1_mat == NULL) || (src2_mat == NULL) || (src1_mat->cols != src2_mat->rows) ) return; dst_mat = matrix_create(src1_mat->rows, src2_mat->cols); #ifdef USE_PTHREAD if ( (pthread = (pthread_t*)calloc( src1_mat->rows * src2_mat->cols, sizeof(pthread_t))) == NULL ) return; if ( (pthread_param = (pthread_matrix_multiply_row_t*)calloc( src1_mat->rows * src2_mat->cols, sizeof(pthread_matrix_multiply_row_t))) == NULL) return; #endif if (! dst_mat) return NULL; /* multiply src1_mat(rows) x src2_mat(cols) */ for (i=0; i < src1_mat->rows; i++) for (j=0; j < src2_mat->cols; j++) #ifdef USE_PTHREAD { /* cache this value to avoid various recalculations */ pthread_pos = j + i * src2_mat->cols; pthread_param[ pthread_pos ].src1_mat = src1_mat; pthread_param[ pthread_pos ].src2_mat = src2_mat; pthread_param[ pthread_pos ].dst_mat = dst_mat; pthread_param[ pthread_pos ].row = i; pthread_param[ pthread_pos ].col = j; /* create a pthread, passing values as the pthread_matrix_multiply_row_t */ pthread_create(&pthread[ pthread_pos ], NULL, matrix_multiply_row_pthread, &pthread_param[ pthread_pos ]); } #else /* just multiply normally, one by one */ matrix_multiply_row(src1_mat, src2_mat, dst_mat, i, j); #endif #ifdef USE_PTHREAD /* wait for threads */ for (i=0; i < src1_mat->rows * src2_mat->cols; i++) pthread_join(pthread[i], NULL); /* free everything */ if (pthread_param) free(pthread_param); if (pthread) free(pthread); #endif return dst_mat; } /** * The main loop. * We read user input and output the results of commands * @return 0 = ok. -1 = failed */ int main() { int rows, cols, value, stop, retval = 0; matrix_t *a = NULL, *b = NULL, *c = NULL, *tmp = NULL; char opt, name; stop=0; while ( ((opt=getchar()) != EOF) && (!stop) ) { switch (opt) { case 'q': case 'x': stop=1; break; case 'h': printf("Help:\n" " q quit\n" " i [a|b] rows cols matrix insert matrix 'a' or 'b' with rows x cols.\n" " Then, follows matrix, which are matrix elements.\n" " m multiply a*b\n" " p [a|b|c] print matrix a, b or c\n" "\n" ); break; case 'i': { while ( ( (name = getchar()) == ' ' ) || (name == '\n') ); if ( name == EOF ) { retval = -1; opt = EOF; break; } if (scanf("%d %d", &rows, &cols) == EOF) { retval = -1; opt = EOF; break; } if ( (rows < 1) || (cols < 1) ) break; if (name == 'a') { if (a) matrix_destroy(a); a = matrix_create(rows, cols); if (!a) { retval = -1; stop = 1; break; } tmp = a; } else if (name == 'b') { if (b) matrix_destroy(b); b = matrix_create(rows, cols); if (!b) { retval = -1; stop = 1; break; } tmp = b; } else { printf("Invalid matrix name '%c'. Choose between 'a' and 'b'.\n", name); break; } for (rows = 0; rows < tmp->rows; rows++) for (cols = 0; cols < tmp->cols; cols++) { if (scanf("%d",&value) == EOF) { cols = tmp->cols; rows = tmp->rows; retval = -1; break; } tmp->matrix[matpos(rows, cols, tmp->cols)] = value; } matrix_print(tmp); } break; case 'm': { c = matrix_multiply(a, b); if (!c) { retval = -1; stop = 1; break; } matrix_print(c); } break; case 'p': { while ( ( (name = getchar()) == ' ' ) || (name == '\n') ); if ( name == EOF ) { opt = EOF; retval = -1; break; } tmp = NULL; switch (name) { case 'a': tmp = a; break; case 'b': tmp = b; break; case 'c': tmp = c; break; default: printf("Invalid matrix name '%c'. Choose between 'a', 'b' and 'c'.\n",name); break; } if (tmp) matrix_print(tmp); } break; default: break; } if (opt == EOF) break; } if (a) matrix_destroy(a); if (b) matrix_destroy(b); if (c) matrix_destroy(c); printf("Goodbye!\n"); return retval; } /* author: Gustavo Sverzut Barbieri (http://www.gustavobarbieri.com.br) */