/*    DISTRMULT3
    TESTING distribute and redistribute directive
            MULT_BLOCK distribution*/

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

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

static void distrm311();
static void distrm312();
static void distrm313();
static void distrm314();
static void distrm315();
static void distrm316();
static void distrm317();
static void distrm318();
static void distrm319();
static void distrm43();

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

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

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

    /* 311 DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
                REDISTRIBUTE [BLOCK][BLOCK][BLOCK]*/
    distrm311();
    /* 312 DISTRIBUTE arrA3DISTRIBUTE [BLOCK][BLOCK][BLOCK]
               REDISTRIBUTE  [MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]*/
    distrm312();
    /* 313 DISTRIBUTE arrA2[_BLOCK][BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA3[BLOCK][MULT_BLOCK][BLOCK]*/
    distrm313();
    /* 314 DISTRIBUTE arrA3[BLOCK][MULT_BLOCK][BLOCK]
                REDISTRIBUTE arrA3[MULT_BLOCK][BLOCK][MULT_BLOCK]*/
    distrm314();
    /* 315  DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
              REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK][MULT_BLOCK] other m1,m2,m3*/
    distrm315();
    /* 316 DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
                REDISTRIBUTE arrA2[*][*][*]*/
    distrm316();
    /* 317  DISTRIBUTE arrA3[*][*][*]
                REDISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]*/
    distrm317();
    /* 318 DISTRIBUTE arrA3[MULT_BLOCK][*][MULT_BLOCK]
                REDISTRIBUTE arrA3[*][MULT_BLOCK][*]*/
    distrm318();
    /* 319 DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][*]
                REDISTRIBUTE arrA3[*][MULT_BLOCK][MULT_BLOCK]*/
    distrm319();
    /* 43 DISTRIBUTE arrA4[MULT_BLOCK][*][MULT_BLOCK][*]
                REDISTRIBUTE arrA4[[*][MULT_BLOCK][*][MULT_BLOCK]*/
    distrm43();

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

/* ---------------------------------------------DISTR311*/
/*DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
                REDISTRIBUTE [BLOCK][BLOCK][BLOCK]*/
void distrm311()
{
    #define AN1 14
    #define AN2 12
    #define AN3 10
    int m1 = 7, m2 = 3, m3 = 5;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm311";

    erri = ER;

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

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[block][block][block])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR312*/
/*DISTRIBUTE arrA3[BLOCK][BLOCK][BLOCK]
                REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]*/
void distrm312()
{
    #define AN1 15
    #define AN2 15
    #define AN3 25
    int m1 = 5, m2 = 5, m3 = 5;
    #pragma dvm array distribute[block][block][block]
    int (*A3)[AN2][AN3];
    char tname[] = "distrm312";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[multblock(m1)][multblock(m2)][multblock(m3)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR313*/
/*DISTRIBUTE arrA2[MULT_BLOCK][BLOCK][MULT_BLOCK]
                REDISTRIBUTE arrA3[BLOCK][MULT_BLOCK][BLOCK]*/
void distrm313()
{
    #define AN1 24
    #define AN2 24
    #define AN3 24
    int m1 = 2, m2 = 3, m3 = 4;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm313";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[multblock(m1)][block][multblock(m3)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[block][multblock(m2)][block])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR314*/
/*DISTRIBUTE arrA3[BLOCK][MULT_BLOCK][BLOCK]
                REDISTRIBUTE arrA3[MULT_BLOCK][BLOCK][MULT_BLOCK]*/
void distrm314()
{
    #define AN1 20
    #define AN2 30
    #define AN3 30
    int m1 = 5, m2 = 3, m3 = 3;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm314";

    erri = ER;

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

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[multblock(m1)][block][multblock(m3)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR315*/
/*DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
            REDISTRIBUTE [MULT_BLOCK][MULT_BLOCK][MULT_BLOCK] other m1,m2,m3*/
void distrm315()
{
    #define AN1 16
    #define AN2 16
    #define AN3 16
    int m1 = 2, m2 = 4, m3 = 8;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm315";

    erri = ER;

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

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[multblock(m3)][multblock(m1)][multblock(m2)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR316*/
/*DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]
                REDISTRIBUTE arrA2[*][*][*]*/
void distrm316()
{
    #define AN1 12
    #define AN2 12
    #define AN3 48
    int m1 = 3, m2 = 2, m3 = 6;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm316";

    erri = ER;

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

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[*][*][*])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR317*/
/*DISTRIBUTE arrA3[*][*][*]
                REDISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][MULT_BLOCK]*/
void distrm317()
{
    #define AN1 10
    #define AN2 35
    #define AN3 10
    int m1 = 2, m2 = 5, m3 = 2;
    #pragma dvm array distribute[*][*][*]
    int (*A3)[AN2][AN3];
    char tname[] = "distrm317";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[multblock(m1)][multblock(m2)][multblock(m3)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR318*/
/*DISTRIBUTE arrA3[MULT_BLOCK][*][MULT_BLOCK]
                REDISTRIBUTE arrA3[*][MULT_BLOCK][*]*/
void distrm318()
{
    #define AN1 11
    #define AN2 14
    #define AN3 24
    int m1 = 1, m2 = 2, m3 = 6;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm318";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[multblock(m1)][*][multblock(m3)])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[*][multblock(m2)][*])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR319*/
/*DISTRIBUTE arrA3[MULT_BLOCK][MULT_BLOCK][*]
               REDISTRIBUTE arrA3[*][MULT_BLOCK][MULT_BLOCK]*/
void distrm319()
{
    #define AN1 30
    #define AN2 12
    #define AN3 30
    int m11 = 2, m12 = 2, m13 = 2;
    int m21 = 5, m22 = 4, m23 = 10;
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrm319";

    erri = ER;

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

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    A3[i][j][k] = i*NL/10 + j*NL/100 + k;
    }

    #pragma dvm redistribute(A3[*][multblock(m22)][multblock(m23)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k] on A3[i][j][k]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    if (A3[i][j][k] != i*NL/10 + j*NL/100 + k)
                        erri = Min(erri, i*NL/10 + j*NL/100 + k);
    }

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

/* ---------------------------------------------DISTR43*/
/*DISTRIBUTE arrA4[MULT_BLOCK][*][MULT_BLOCK][*]
                REDISTRIBUTE arrA4[*][MULT_BLOCK][*][MULT_BLOCK]*/
void distrm43()
{
    #define AN1 16
    #define AN2 16
    #define AN3 16
    #define AN4 16
    int m1 = 2, m2 = 4, m3 = 2, m4 = 4;
    #pragma dvm array
    int (*A4)[AN2][AN3][AN4];
    char tname[] = "distrm43";

    erri = ER;

    A4 = malloc(AN1 * AN2 * AN3 * AN4 * sizeof(int));
    #pragma dvm redistribute(A4[multblock(m1)][*][multblock(m3)][*])

    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k][n] on A4[i][j][k][n]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    for (n = 0; n < AN4; n++)
                        A4[i][j][k][n] = i*NL/10+j*NL/100+k*NL/1000+n;
    }

    #pragma dvm redistribute(A4[*][multblock(m2)][*][multblock(m4)])
    #pragma dvm region
    {
        #pragma dvm parallel([i][j][k][n] on A4[i][j][k][n]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            for (j = 0; j < AN2; j++)
                for (k = 0; k < AN3; k++)
                    for (n = 0; n < AN4; n++)
                        if (A4[i][j][k][n] != i*NL/10+j*NL/100+k*NL/1000+n)
                            erri = Min(erri, i*NL/10+j*NL/100+k*NL/1000+n);
    }

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

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

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