/* Compilation: gcc -o exam3 exam3.c -lpthread */

// Параллельное умножение квадратной матрицы А размерностью NxN
// на вектор B, результат - вектор C.
// Создается nt потоков, каждый из которых умножает ленту матрицы
// A размерностью (N/nt)xN на вектор B.
// Умножение повторяется nc раз при неизменной матрице A и
// различных векторах B.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#define _REENTRANT
#include <pthread.h>
#include <semaphore.h>

typedef struct {
  pthread_t tid;
  sem_t sem1;
  sem_t sem2;
  int first;
  int last;
  } ThreadRecord;

int N;	// Размер матрицы A и векторов B, C
double *A, *B, *C;

int done=0;
ThreadRecord *threads; // Массив индексов границ лент матрицы

void *
mysolver (void *arg_p) {
  ThreadRecord *thr;
  char numbs[] = {'1','2','3','4','5','6','7','8','9','0'};
  int i, j;

  thr = (ThreadRecord *)arg_p;

  while ( !done ) {
    sem_wait(&(thr->sem1));
write (1, numbs, thr->tid);
write (1, "\n", 1);
    for (i=thr->first; i<=thr->last; i++) {
      C[i] = 0;
      for (j=0; j<=N; j++)
        C[i] += A[N*i+j]*B[j];
      };
    sem_post(&(thr->sem2));
    };
  }

int
main (int argc, char* argv[]) {
  int nt;	// Кол-во расчетных потоков
  int nc;	// Кол-во циклов
  int i, j, k;

  pthread_attr_t pattr;

  if (argc != 4) {
    fprintf(stderr, "Исп: %s размерность #потоков #циклов\n", argv[0]);
    exit (1);
    };

  N = atoi(argv[1]);
  nt = atoi(argv[2]);
  nc = atoi(argv[3]);

  if ( N%nt ) {
    fprintf(stderr, "Размерность д.б. кратна кол-ву потоков\n", argv[0]);
    exit (2);
    };

  // Выделение памяти
  A = (double *) malloc( sizeof(double)*N*N );
  B = (double *) malloc( sizeof(double)*N );
  C = (double *) malloc( sizeof(double)*N );

  // Инициализация A и B случ. значениями
  srand(time(NULL));
  for (i=0; i<N; i++) {
    B[i] = (double) rand();
    for (j=0; j<N; j++)
      A[i*N+j] = (double) rand();
    };

  pthread_attr_init (&pattr);
  pthread_attr_setscope (&pattr, PTHREAD_SCOPE_SYSTEM);
  pthread_attr_setdetachstate (&pattr,PTHREAD_CREATE_JOINABLE);

  threads = (ThreadRecord *) calloc (nt, sizeof(ThreadRecord));

  j = N/nt;
  for (i=0; i<nt; i++) {
    threads[i].first = j*i; // Индекс нач. строки А для потока
    threads[i].last = j*(i+1)-1; // Индекс конечн. строки А для потока
    sem_init(&(threads[i].sem1), 0, 0);
    sem_init(&(threads[i].sem2), 0, 0);
    if ( pthread_create (&(threads[i].tid), &pattr, mysolver, (void *) &(threads[i])) )
      perror("pthread_create");
    };

  for (i=0; i<nt; i++) // Старт расчетов
    sem_post(&(threads[i].sem1));

  for (k=1; k<nc; k++) {
    for (i=0; i<nt; i++)
      sem_wait(&(threads[i].sem2));
write(1, "---------\n", 10);

    for (j=0; j<N; j++)
        B[j] = (double) rand();

    for (i=0; i<nt; i++)
      sem_post(&(threads[i].sem1));
    };
  done = 1;

  exit (0);
  }