
/*
 *  ex1.c  --  пример 1       см.также  --  http://www.csa.ru/~il/mpi_tutor
 *
 *  Простейшая приемопередача:
 *     MPI_Send, MPI_Recv
 *  Завершение по ошибке:
 *     MPI_Abort
 */

#include <mpi.h>

#include <stdio.h>

  /* Идентификаторы сообщений */
#define tagFloatData  1
#define tagDoubleData 2

  /* Этот макрос введен для удобства, */
  /* он позволяет указывать длину массива в количестве ячеек */ 
#define ELEMS(x)  ( sizeof(x) / sizeof(x[0]) )

int main( int argc, char **argv )
{
    int size, rank, count;
    float floatData[10];
    double doubleData[20];
    MPI_Status status;

      /* Инициализируем библиотеку */
    MPI_Init( &argc, &argv );

      /* Узнаем количество задач в запущенном приложении */
    MPI_Comm_size( MPI_COMM_WORLD, &size );

      /* ... и свой собственный номер: от 0 до (size-1) */
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );

      /* пользователь должен запустить ровно две задачи, иначе ошибка */
    if( size != 2 ) {

          /* задача с номером 0 сообщает пользователю об ошибке */
        if( rank==0 )
            printf("Error: two processes required instead of %d, abort\n",
                size );

          /* Все задачи-абоненты области связи MPI_COMM_WORLD
           * будут стоять, пока задача 0 не выведет сообщение.
           */
        MPI_Barrier( MPI_COMM_WORLD );
          /* Без точки синхронизации может оказаться, что одна из задач
           * вызовет MPI_Abort раньше, чем успеет отработать printf()
           * в задаче 0, MPI_Abort немедленно принудительно завершит
           * все задачи и сообщение выведено не будет
           */
          /* все задачи аварийно завершают работу */
        MPI_Abort(
            MPI_COMM_WORLD,  /* Описатель области связи, на которую */
                             /*    распространяется действие ошибки */
            MPI_ERR_OTHER ); /* Целочисленный код ошибки */
        return -1;
    }

    if( rank==0 ) {
      /* Задача 0 что-то такое передает задаче 1 */

        MPI_Send(
            floatData,        /* 1) адрес передаваемого массива */
            5,       /* 2) сколько: 5 ячеек, т.е. floatData[0]..floatData[4] */
            MPI_FLOAT,        /* 3) тип ячеек */
            1,                /* 4) кому: задаче 1 */
            tagFloatData,     /* 5) идентификатор сообщения */
            MPI_COMM_WORLD ); /* 6) описатель области связи, через которую */
                              /*    происходит передача */

          /* и еще одна передача: данные другого типа */
        MPI_Send( doubleData, 6, MPI_DOUBLE, 1, tagDoubleData, MPI_COMM_WORLD );

    } else {
      /* Задача 1 что-то такое принимает от задачи 0 */

          /* дожидаемся сообщения и помещаем пришедшие данные в буфер */
        MPI_Recv(
            doubleData,     /* 1) адрес массива, куда складывать принятое */
            ELEMS( doubleData ), /* 2) фактическая длина приемного */
                                 /*    массива в числе ячеек */
            MPI_DOUBLE,     /* 3) сообщаем MPI, что пришедшее сообщение */
                            /*    состоит из чисел типа 'double' */
            0,              /* 4) от кого: от задачи 0 */
            tagDoubleData,  /* 5) ожидаем сообщение с таким идентификатором */
            MPI_COMM_WORLD, /* 6) описатель области связи, через которую */
                            /*    ожидается приход сообщения */
            &status );      /* 7) сюда будет записан статус завершения приема */

          /* Вычисляем фактически принятое количество данных */
        MPI_Get_count(
            &status,        /* статус завершения */
            MPI_DOUBLE,     /* сообщаем MPI, что пришедшее сообщение */
                            /*    состоит из чисел типа 'double' */
            &count );       /* сюда будет записан результат */

          /* Выводим фактическую длину принятого на экран */
        printf("Received %d elems\n", count );

          /* Аналогично принимаем сообщение с данными типа float
           * Обратите внимание: задача-приемник имеет возможность
           * принимать сообщения не в том порядке, в котором они
           * отправлялись, если эти сообщения имеют разные идентификаторы
           */
        MPI_Recv( floatData, ELEMS( floatData ), MPI_FLOAT,
            0, tagFloatData, MPI_COMM_WORLD, &status );
        MPI_Get_count( &status, MPI_FLOAT, &count );
    }

      /* Обе задачи завершают выполнение */
    MPI_Finalize();
    return 0;
}
