/*    DISTRMIX2
Testing DISTRIBUTE and REDISTRIBUTE directive
            GEN_BLOCK, WGT_BLOCK, MULT_BLOCK distributions
*/

#include <math.h>
#include <stdlib.h>
#include <stdio.h>

#define Min(a, b) ((a) < (b) ? (a) : (b))

#ifndef _DVMH
#define dvmh_get_num_procs(X) 1
#endif

static void distrmix21();
static void distrmix22();
static void distrmix23();
static void distrmix24();
static void distrmix25();
static void distrmix26();
static void distrmix27();
static void distrmix28();
static void distrmix29();
static void distrmix210();
static void distrmix211();
static void distrmix212();
static void distrmix213();

static void ansyes(const char tname[]);
static void ansno(const char tname[]);

static int NL = 1000;
static int ER = 10000;
static int erri, errib, i, j, k, ia, ib;

int main(int an, char **as)
{
    printf("=== START OF DISTRMIX2 ===================\n");

    /* 21  DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]*/
    distrmix21();
    /* 22  DISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK]*/
    distrmix22();
    /* 23  DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]*/
    distrmix23();
    /* 24  DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]*/
    distrmix24();
    /* 25  DISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]*/
    distrmix25();
    /* 26  DISTRIBUTE arrA2[WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]*/
    distrmix26();
    /* 27  DISTRIBUTE arrA2[BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][BLOCK]*/
    distrmix27();
    /* 28  DISTRIBUTE arrA2[BLOCK][GEN_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [BLOCK][WGT_BLOCK]*/
    distrmix28();
    /* 29  DISTRIBUTE arrA2[WGT_BLOCK][BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [BLOCK][MULT_BLOCK]*/
    distrmix29();
    /* 210  DISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]*/
    distrmix210();
    /* 211  DISTRIBUTE arrA2[WGT_BLOCK][BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE arrA2[BLOCK][WGT_BLOCK]*/
    distrmix211();
    /* 212  DISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]*/
    distrmix212();
    /* 213  DISTRIBUTE arrA2[BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][MULT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][BLOCK]*/
    distrmix213();

    printf("=== END OF DISTRMIX2 =====================\n");
    return 0;
}

static int myRand() {
    const unsigned a = 1103515245U;
    const unsigned c = 12345U;
    const unsigned m = ((unsigned)RAND_MAX) + 1U;
    static unsigned prev = 5;
    prev = (a * prev + c) % m;
    return prev;
}

static void genBlocksAxis(const int procCount, const int weight, const int withoutZero, int **blocks) {
    *blocks = (int *)malloc(procCount * sizeof(int));
    int restWeight = weight, i, zeroind = -1;
    if (!withoutZero && procCount > 1)
        zeroind = myRand() % (procCount - 1);
    for (i = 0; i < (procCount - 1); i++) {
        if (i == zeroind)
            (*blocks)[i] = 0;
        else
            (*blocks)[i] = 1 * withoutZero + myRand() % (restWeight - (procCount - (i + 1)));
        restWeight -= (*blocks)[i];
    }
    (*blocks)[i] = restWeight;
}

/* ---------------------------------------------DISTR21*/
/*DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
             REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK]
             REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]*/
void distrmix21()
{
    #define AN1 10
    #define AN2 56
    int m11 = 2, m12 = 7;
    int m21 = 5, m22 = 8;
    double wb1[8] = {1.0, 2., 1., 3.2, 1.0, 1.5, 2.3, 2.};
    double wb2[7] = {1.3, 1.5, 2.2, 1.6, 2.6, 0.5, 1.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix21";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[multblock(m11)][multblock(m12)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[wgtblock(wb1, 8)][wgtblock(wb2, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[multblock(m21)][multblock(m22)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR22*/
/*DISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK]*/
void distrmix22()
{
    #define AN1 16
    #define AN2 32
    int m1 = 2, m2 = 4;
    double wb1[7] = {2.4, 1.2, 3.0, 0.2, 1.5, 2.8, 2.1};
    double wb2[6] = {2.0, 1.2, 2.6, 1.6, 3.5, 0.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix22";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb1, 7)][wgtblock(wb2, 6)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }
    #pragma dvm redistribute(A2[multblock(m1)][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                    A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[wgtblock(wb2, 6)][wgtblock(wb1, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR23*/
/*DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]*/
void distrmix23()
{
    #define AN1 18
    #define AN2 12
    int m11 = 2, m12 = 2;
    int m21 = 3, m22 = 3;
    double wb1[10] = {2., 1.2, 2., 2.5, 0.2, 1.5, 1., 2.8, 2.1, 3.};
    double wb2[8] = {3.0, 3.5, 2.0, 1.2, 2.6, 1.6, 3.5, 0.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix23";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[multblock(m11)][multblock(m12)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }
    #pragma dvm redistribute(A2[wgtblock(wb1, 10)][multblock(m22)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[multblock(m21)][wgtblock(wb2, 8)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR24*/
/*DISTRIBUTE arrA2[MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]*/
void distrmix24()
{
    #define AN1 30
    #define AN2 30
    int m1 = 3, m2 = 5;
    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix24";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[multblock(m1)][multblock(m2)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }
    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[multblock(m2)][multblock(m1)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR25*/
/*DISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]*/
void distrmix25()
{
    #define AN1 16
    #define AN2 12
    int m1 = 2, m2 = 3;
    int *BS1i, *BS1j, *BS2i, *BS2j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS2i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS2j);
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix25";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }
    #pragma dvm redistribute(A2[multblock(m1)][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[genblock(BS2i)][genblock(BS2j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(BS2i);
    free(BS2j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR26*/
/*DISTRIBUTE arrA2[WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]*/
void distrmix26()
{
    #define AN1 52
    #define AN2 50
    int m1 = 13, m2 = 5;
    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    double wb1[6] = {2.4, 2.2, 0.2, 3.5, 1.2, 1.};
    double wb2[8] = {1.0, 2.5, 3.0, 2.8, 1.6, 1., 0.5, 1.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix26";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb1, 6)][multblock(m2)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }
    #pragma dvm redistribute(A2[multblock(m1)][wgtblock(wb2, 8)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR27*/
/*DISTRIBUTE arrA2[BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][WGT_BLOCK]
              REDISTRIBUTE [GEN_BLOCK][BLOCK]*/
void distrmix27()
{
    #define AN1 8
    #define AN2 64
    int m1 = 2, m2 = 8;
    int *BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    double wb[7] = {2., 3.2, 2., 3.5, 1.2, 1., 4.};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix27";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[block][multblock(m2)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[multblock(m1)][wgtblock(wb, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[genblock(BS)][block])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR28*/
/*DISTRIBUTE arrA2[BLOCK][GEN_BLOCK]
              REDISTRIBUTE [WGT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [BLOCK][WGT_BLOCK]*/
void distrmix28()
{
    #define AN1 42
    #define AN2 16
    int m1 = 3, m2 = 2;
    int *BS;
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS);
    double wb1[6] = {2., 3., 1.2, 1.5, 1., 1.5};
    double wb2[7] = {2.2, 1.5, 3.0, 2.8, 2.6, 1.4, 0.5};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix28";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[block][genblock(BS)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[wgtblock(wb1, 6)][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[block][wgtblock(wb2, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR29*/
/*DISTRIBUTE arrA2[WGT_BLOCK][BLOCK]
              REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK]
              REDISTRIBUTE [BLOCK][MULT_BLOCK]*/
void distrmix29()
{
    #define AN1 21
    #define AN2 48
    int m1 = 3, m2 = 2;
    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    double wb[9] = {2.2, 1.5, 3.0, 2.8, 2.6, 1.4, 0.5, 1., 2.};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix29";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb, 9)][block])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[block][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR210*/
/*DISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]*/
void distrmix210()
{
    #define AN1 9
    #define AN2 11

    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    double wb1[6] = {1.0, 1.2, 2.5, 1.4, 2.5, 1.3};
    double wb2[4] = {1.0, 2., 1.5, 1.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix210";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb1, 6)][wgtblock(wb2, 4)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[wgtblock(wb2, 4)][wgtblock(wb1, 6)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR211*/
/*DISTRIBUTE arrA2[WGT_BLOCK][BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[BLOCK][WGT_BLOCK]*/
void distrmix211()
{
    #define AN1 16
    #define AN2 16

    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    double wb[7] = {1.0, 1.2, 2.5, 1.4, 2.5, 1.3, 2};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix211";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb, 7)][block])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[block][wgtblock(wb, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR212*/
/*DISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][WGT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][GEN_BLOCK]*/
void distrmix212()
{
    #define AN1 6
    #define AN2 28

    int *BS1i, *BS1j, *BS2i, *BS2j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS2i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS2j);
    double wb1[8] = {1.0, 1.2, 2.5, 1.4, 2.5, 1.3, 1., 2.};
    double wb2[5] = {2., 1.3, 2., 1.0, 1.7};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix212";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[genblock(BS1i)][genblock(BS1j)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[wgtblock(wb1, 6)][wgtblock(wb2, 4)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[genblock(BS2i)][genblock(BS2j)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(BS2i);
    free(BS2j);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR213*/
/*DISTRIBUTE arrA2[BLOCK][GEN_BLOCK]
               REDISTRIBUTE arrA2[WGT_BLOCK][MULT_BLOCK]
               REDISTRIBUTE arrA2[GEN_BLOCK][BLOCK]*/
void distrmix213()
{
    #define AN1 27
    #define AN2 14
    int m1 = 3, m2 = 2;
    int *BS1i, *BS1j;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1i);
    genBlocksAxis(dvmh_get_num_procs(2), AN2, 1, &BS1j);
    double wb[4] = {1.2, 1.6, 2.0, 1.8};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrmix213";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[block][genblock(BS1j)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] = i * NL + j;
    }

    #pragma dvm redistribute(A2[wgtblock(wb, 4)][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                A2[i][j] += 1;
    }
    #pragma dvm redistribute(A2[genblock(BS1i)][block])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j] on A2[i][j]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                if (A2[i][j] != i * NL + j + 1)
                    erri = Min(erri, i * NL + j + 1);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    free(BS1i);
    free(BS1j);
    free(A2);
    #undef AN1
    #undef AN2
}

void ansyes(const char name[])
{
    printf("%s  -  complete\n", name);
}

void ansno(const char name[])
{
    printf("%s  -  ***error\n", name);
}
