/*    DISTR2
TESTING distribute and redistribute CLAUSE*/

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

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

static void distr24();
static void distr32();
static void distr33();
static void distr41();
static void distr42();

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, n, m, ia, ib;

int main(int an, char **as)
{
    printf("=== START OF DISTR2 ===================\n");
    /* DISTRIBUTE arrA2[BLOCK][BLOCK]  REDISTRIBUTE arrA2[*][*]*/
    distr24();
    /* DISTRIBUTE  arrA3[BLOCK][*][BLOCK] REDISTRIBUTE arrA3[*][BLOCK][BLOCK] */
    distr32();
    /* DISTRIBUTE  arrA3[BLOCK][*][BLOCK] REDISTRIBUTE arrA3[*][BLOCK][*] */
    distr33();
    /* DISTRIBUTE arrA4[*][*][BLOCK][BLOCK] REDISTRIBUTE arrA4[*][*][*][*]*/
    distr41();
    /* DISTRIBUTE arrA4[BLOCK][*][BLOCK][*] REDISTRIBUTE arrA4[*][BLOCK][BLOCK][*]*/
    distr42();
    printf("=== END OF DISTR2 =====================\n");
    return 0;
}
/* ---------------------------------------------DISTR24*/
/*  DISTRIBUTE arrA2[BLOCK][BLOCK] REDISTRIBUTE arrA2[*][*]*/
void distr24()
{
    #define AN1 8
    #define AN2 8

    #pragma dvm array distribute[block][block]
    int A2[AN1][AN2];
    char tname[] = "distr24";

    erri = ER;
    #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[*][*])
    #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)
                    erri = Min(erri, i * NL + j);
    }

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    #undef AN1
    #undef AN2
}
/* ---------------------------------------------DISTR32*/
/* DISTRIBUTE arrA3[BLOCK][*][BLOCK] REDISTRIBUTE arrA3[*][BLOCK][BLOCK]*/
void distr32()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8

    #pragma dvm array distribute[block][*][block]
    float A3[AN1][AN2][AN3];
    char tname[] = "distr32";

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

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    #undef AN1
    #undef AN2
    #undef AN3
}
/* ---------------------------------------------DISTR33*/
/* DISTRIBUTE  arrA3[BLOCK][*][ BLOCK] REDISTRIBUTE arrA3[*][BLOCK][*] */
void distr33()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8

    #pragma dvm array distribute[block][*][block]
    double A3[AN1][AN2][AN3];
    char tname[] = "distr33";

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

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    #undef AN1
    #undef AN2
    #undef AN3
}
/* ---------------------------------------------DISTR41*/
/* DISTRIBUTE arrA4[*][*][BLOCK][BLOCK] REDISTRIBUTE arrA4[*][*][*][*]*/
void distr41()
{
    #define AN1 5
    #define AN2 5
    #define AN3 5
    #define AN4 5

    #pragma dvm array distribute[*][*][block][block]
    int A4[AN1][AN2][AN3][AN4];
    char tname[] = "distr41";

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

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    #undef AN1
    #undef AN2
    #undef AN3
    #undef AN4
}
/* ---------------------------------------------DISTR42*/
/* DISTRIBUTE arrA4[BLOCK][*][BLOCK][*] REDISTRIBUTE arrA4[*][BLOCK][BLOCK][*]*/
void distr42()
{
    #define AN1 5
    #define AN2 5
    #define AN3 5
    #define AN4 5

    #pragma dvm array distribute[block][*][block][*]
    float A4[AN1][AN2][AN3][AN4];
    char tname[] = "distr42";

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

    #pragma dvm get_actual(erri)
    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);
    #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);
}
