
/*
 *  ex3.c  --  пример 3       см.также  --  http://www.csa.ru/~il/mpi_tutor
 *
 *  Функции общего назначения для работы с типами:
 *     MPI_Type_commit, MPI_Type_free, MPI_Type_extent
 *  Создание пользовательских типов данных:
 *     MPI_Type_vector, MPI_Type_hvector
 *
 *  1) Быстрая передача решетки:
 *     передаются только те ячейки, у которых обе координаты нечетные
 *          x.x.x
 *          .....       x - выделятся и передаются
 *          x.x.x
 *          .....       . - игнорируются
 *          x.x.x
 *
 *  2) Передача с транспонированием: столбцы становятся строками
 *
 *  Совет: перед тем, как запускать этот пример,
 *  переключите терминал/сессию в режим с максимальным количеством строк.
 */

#include <mpi.h>
#include <stdio.h>

 /* количество строк и столбцов в исходной матрице
  */
#define  SIZE  7

 /* только для первого примера:
  * количество строк и столбцов в итоговой матрице
  */
#define  halfSize  (SIZE+1)/2

int main( int argc, char **argv )
{
    int a[ SIZE ][ SIZE ],         /* исходная матрица - для обоих примеров */
        b[ halfSize ][ halfSize ], /* итоговая матрица для первого примера */
        c[ SIZE ][ SIZE ];         /* итоговая матрица для второго примера */
    int rank, i, j;
    MPI_Aint typeExtent;

    MPI_Datatype               /* описатели пользовательских типов */
        oneSlice, twoSlice,        /* для первого примера */
        oneRow, rowByRow;          /* для второго примера */

    MPI_Status status;         /* нужна только для приема, не используется */

/*-----------------*
 *  Инициализация  *
 *-----------------*/

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );

     /* Нам нужен только один процесс: он будет и передатчиком, и приемником.
      * Остальные процессы завершаются немедленно.
      */
    if( rank != 0 )
        goto done;

     /* Генерируем исходную матрицу и печатаем ее
      */
    for( i=0; i<SIZE; i++ )
        for( j=0; j<SIZE; j++ )
            a[i][j] = 10 * (i+1) + (j+1);

    puts("Sended:");
    for( i=0; i<SIZE; i++ ) {
        for( j=0; j<SIZE; j++ )
            printf("%4d", a[i][j] );
        puts("");
    }
    puts("");

/*------------------------------------------------*
 *  Первый пример: выделение и пересылка решетки  *
 *------------------------------------------------*/

     /* Создаем тип из элементов вида 'x.'
      * #1. Количество элементов в новом типе = половине числа столбцов
      * #2. Каждый элемент содержит одно целое число
      * #3. Начала элементов отстоят друг от друга через две ячейки типа int
      * => Новый тип накрывает РОВНО ОДНУ строку матрицы шаблоном 'x.x.x.x.'
      */
    MPI_Type_vector( halfSize, 1, 2, MPI_INT, &oneSlice );

     /* Создаем тип из элементов вида 'x.x.x.x.........'
      * #1. Количество элементов в новом типе = половине числа строк
      * #2. Каждый элемент содержит одну ячейку типа 'oneSlice'
      * #3. Начала элементов отстоят друг от друга через две строки
      * => Новый тип накрывает матрицу решеткой с нечетными координатами
      */
    MPI_Type_hvector(halfSize, 1, SIZE*2*sizeof(int), oneSlice, &twoSlice );

     /* Регистрируем тип 'twoSlice' для использования.
      * Поскольку тип 'oneSlice' используется только
      * для порождения других типов, его регистрировать не надо.
      */
    MPI_Type_commit( &twoSlice );

     /* На этом подготовительная работа, выполняемая
      * РОВНО ОДИН РАЗ, завершена.
      */

     /* Посылаем сами себе.
      * Отправляем одну переменную типа 'twoSlice',
      * расположенную по адресу матрицы 'a'.
      * Принимаем поток целых чисел и размещаем его по адресу матрицы 'b'
      */
    MPI_Sendrecv( a, 1, twoSlice, rank, 0,
                  b, halfSize*halfSize, MPI_INT, rank, 0,
        MPI_COMM_WORLD, &status );

     /* Печать принятой подматрицы
      */
    puts("Received sub-matrix:");
    for( i=0; i<halfSize; i++ ) {
        for( j=0; j<halfSize; j++ )
            printf("%4d", b[i][j] );
        puts("");
    }
    puts("");

     /* Печатаем размер типа при хранении, и размер типа при передаче
      */
    MPI_Type_extent( oneSlice, &typeExtent );
 /* MPI_Type_size  ( oneSlice, &typeSize ); */
    printf("\'oneSlice\' has extent = %3d\n", typeExtent );
    MPI_Type_extent( twoSlice, &typeExtent );
 /* MPI_Type_size  ( twoSlice, &typeSize ); */
    printf("\'twoSlice\' has extent = %3d\n", typeExtent );

     /* Очистка описателей. MPI удалит соотвествующие данные
      * из внутренних таблиц и сбросит описатели в MPI_TYPE_NULL
      */
    MPI_Type_free( &oneSlice );
    MPI_Type_free( &twoSlice );

/*------------------------------------------------*
 *  Второй пример: пересылка с транспонированием  *
 *------------------------------------------------*/

     /* В оперативной памяти матрица хранится построчно: строка1,строка2,...
      * Создаем тип-шаблон для быстрого выделения из матрицы одного столбца:
      * #1. Число элементов = количеству строк, т.е. высоте столбца
      * #2. В каждой строке нас интересует одна ячейка
      * #3. Элементы расположены в памяти с шагом, равным длине строки
      */
    MPI_Type_vector( SIZE, 1, SIZE, MPI_INT, &oneRow );

     /* Получаем тип для постолбцового представления матрицы:
      * #1. Число элементов = количеству столбцов
      * #2. Каждый элемент содержит один столбец, т.е. ячейку типа 'oneRow'
      * #3. Начала элементов (т.е. столбцов) отстоят друг от друга
      *     на расстоянии в одну ячейку типа int.
      */
    MPI_Type_hvector( SIZE, 1, sizeof(int), oneRow, &rowByRow );

    MPI_Type_commit( &rowByRow );
     /* Тип зарегистрирован,
      * подготовительная работа завершена.
      */

     /* Передаем с одновременным транспонированием.
      * Передается переменная типа 'rowByRow', находящаяся
      * по адресу матрицы 'a'. Данные уходят столбец за столбцом.
      * Принимается поток целых чисел, размещаемый по адресу матрицы 'b'.
      * Данные размещаются непрерывно, то есть построчно!
      */
    MPI_Sendrecv( a, 1, rowByRow, rank, 0,
                  c, SIZE*SIZE, MPI_INT, rank, 0,
        MPI_COMM_WORLD, &status );

     /* Печать принятой матрицы
      */
    puts("Received transposed matrix:");
    for( i=0; i<SIZE; i++ ) {
        for( j=0; j<SIZE; j++ )
            printf("%4d", c[i][j] );
        puts("");
    }
    puts("");

     /* Печатаем размер типа при хранении и размер типа при передаче
      */
    MPI_Type_extent( oneRow, &typeExtent );
 /* MPI_Type_size  ( oneRow, &typeSize ); */
    printf("\'oneRow\'   has extent = %3d\n", typeExtent );
    MPI_Type_extent( rowByRow, &typeExtent );
 /* MPI_Type_size  ( rowByRow, &typeSize ); */
    printf("\'rowByRow\' has extent = %3d\n", typeExtent );

     /* Очистка описателей.
      */
    MPI_Type_free( &oneRow );
    MPI_Type_free( &rowByRow );

/*---------------------*
 *  Завершение работы  *
 *---------------------*/

done:
    MPI_Finalize();
    return 0;
}
