#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <stdlib.h>

/* CONSTANTES */
#define NLINS  8
#define NCOLS  8
#define SKEY 123
#define SSIZE  NLINS*NCOLS*sizeof(int)

/* MACROS */
#define CELL(s,r,c)  (*((s)+((r)*NCOLS)+(c)))

/* operações sobre semáforos */
#define UP(sid,n) {                  \
            struct sembuf up={n,1,0};\
            semop(sid, &up, 1);      \
}
#define DOWN(sid,n) {                   \
            struct sembuf down={n,-1,0};\
            semop(sid, &down, 1);       \
}
#define INIT(sid,n) UP(sid,n)

/* VARIAVEIS GLOBAIS */
int totalLin, totalCol; /* totais na linha e coluna */
int linSems, colSems;   /* id vectores de semaforos */

/* PROTOTIPOS DAS FUNCOES */
void gera_nova_entrada(int *);
void escreve_e_verifica(int *);
int  inicia_sems(key_t, int);

int main()
{
  int id, lin, col, *folha, i=0, j=0;

  setbuf(stdout,NULL);  /* evita buffering */
  totalLin= NLINS-1;
  totalCol= NCOLS-1;
  /* seg. mem. partilhada para a matriz */
  id= shmget(SKEY, SSIZE,IPC_CREAT|0600);
  folha= (int *) shmat(id,0,0);
  for (lin=0; lin < NLINS; lin++) /* celulas a zero */
    for (col=0; col<NCOLS; col++)
      CELL(folha, lin, col)=0;
                          /* cria e inicia vecs de sems */
  linSems= inicia_sems(SKEY, NLINS);
  colSems= inicia_sems(SKEY+1, NCOLS);
  if (fork()) {           /* pai escreve e verifica*/
    escreve_e_verifica(folha);
  }
  else {                  /* filho gera valores */
     gera_nova_entrada(folha);
     exit(0);
  }
  wait(0);
  semctl(linSems,0,IPC_RMID);/* liberta sems*/
  semctl(colSems,0,IPC_RMID);
  shmdt(folha);              /* liberta shm */
  shmctl(id,IPC_RMID, 0);
  exit(0);
}

int inicia_sems(key_t k, int n)
{
  int semid, i;

  if ((semid=semget(k,n,0))!=-1)  /* se ja existem */
    semctl(semid,0,IPC_RMID);     /* liberta-os    */
                                  /* cria novos    */
  if ((semid=semget(k,n,IPC_CREAT|0600))!=-1)
    for (i=0; i<n; i++)   /* inicia sems do vector */
      INIT(semid,i);
  return semid;
}

void gera_nova_entrada(int *s)
{
  int lin, col, old, new;

  /* escolhe aleatoriamente celula e novo valor */
  lin= rand() % (NLINS-1);
  col= rand() % (NCOLS-1);
  new= rand() % 1000;
                     /* tenta entrar na zona critica */
  DOWN(linSems, lin);  
  DOWN(colSems, col);
  old= CELL(s, lin, col); /* actualiza celula e totais */
  CELL(s, lin, col)= new;
  CELL(s, lin, totalCol) += (new-old);
  CELL(s, totalLin, col) += (new-old);
  UP(colSems, col); /* sai da zona critica */
  UP(linSems, lin);
  usleep(5000);
}

void escreve_e_verifica(int *s)
{
  int lin, col, soma, totalErrs;
  static int ctr= 0;

  totalErrs=0;
  ctr++;
  for (lin=0; lin<NLINS; lin++) {
    soma= 0;
    DOWN(linSems, lin);
    for (col=0; col<NCOLS; col++) {
      if (col != totalCol)
        soma += CELL(s, lin, col);
      printf("%5d", CELL(s,lin,col));
    }
    if (lin!= totalLin)
      totalErrs += (soma != CELL(s, lin, totalCol));
    UP(linSems, lin);
    printf("\n");
  }
  for (col=0; col<totalCol; col++) {
    soma=0;
    DOWN(colSems,col);
    for (lin=0; lin<totalLin; lin++)
      soma += CELL(s,lin, col);
    totalErrs += (soma != CELL(s, totalLin, col));
    UP(colSems, col);
  }
  printf("Total de Erros = %d\n", totalErrs);
  if (totalErrs)
    printf("\nFolhaCalculo n(%d) falhou\n",ctr);
  if ((ctr % 20) == 0)
    printf("\nFolhaCalculo n(%d) processada\n",ctr);
  printf("\n---------------------------------------\n");
  sleep(2);
}
