/*    DISTRWGT3
    Testing DISTRIBUTE and REDISTRIBUTE directives
            WGT_BLOCK distribution*/

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

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

static void distrwgt39();
static void distrwgt310();
static void distrwgt311();
static void distrwgt312();
static void distrwgt313();
static void distrwgt314();
static void distrwgt315();
static void distrwgt41();

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 DISTRWGT3 ===================\n");
    /* 39 DISTRIBUTE arrA3[BLOCK][BLOCK][BLOCK]
                REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]*/
    distrwgt39();
    /* 310 DISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE [BLOCK][BLOCK][BLOCK]*/
    distrwgt310();
    /* 311  DISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK][WGT_BLOCK] other weigths*/
    distrwgt311();
    /* 312 DISTRIBUTE arrA2[WGT_BLOCK][BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA3[BLOCK][WGT_BLOCK][BLOCK]*/
    distrwgt312();
    /* 313 DISTRIBUTE arrA3[BLOCK][WGT_BLOCK][BLOCK]
                REDISTRIBUTE arrA3[WGT_BLOCK][BLOCK][WGT_BLOCK]*/
    distrwgt313();
    /* 314 DISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA2[*][*][*]*/
    distrwgt314();
    /* 315  DISTRIBUTE arrA3[*][*][*]
                REDISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]*/
    distrwgt315();
    /* 41  DISTRIBUTE arrA4[WGT_BLOCK][*][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA4[BLOCK][WGT_BLOCK][BLOCK][*]*/
    distrwgt41();

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

/* ---------------------------------------------DISTR39*/
/* DISTRIBUTE arrA3[BLOCK][BLOCK][BLOCK]
                REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]*/
void distrwgt39()
{
    #define AN1 16
    #define AN2 16
    #define AN3 16
    double wb1[6] = {3.0, 1., 2., 2.0, 2.5, 1.2};
    double wb2[7] = {1., 3., 4.0, 1., 2., 2., 4.};
    double wb3[6] = {5.0, 1., 3., 6.0, 2., 4.};
    #pragma dvm array distribute[block][block][block]

    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt39";

    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[wgtblock(wb1, 6)][wgtblock(wb2, 7)][wgtblock(wb3, 6)])
    #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
}

/* ---------------------------------------------DISTR310*/
/* DISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE [BLOCK][BLOCK][BLOCK]*/
void distrwgt310()
{
    #define AN1 12
    #define AN2 12
    #define AN3 24
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt310";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[wgtblock(wb1, 6)][wgtblock(wb2, 8)][wgtblock(wb3, 6)])

    #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
}

/* ---------------------------------------------DISTR311*/
/* DISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE [WGT_BLOCK][WGT_BLOCK][WGT_BLOCK] other weigths*/
void distrwgt311()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt311";

    erri = ER;

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

    #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[wgtblock(wb3, 6)][wgtblock(wb1, 6)][wgtblock(wb2, 6)])
    #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[WGT_BLOCK][BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA3[BLOCK][WGT_BLOCK][BLOCK]*/
void distrwgt312()
{
    #define AN1 10
    #define AN2 10
    #define AN3 30
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt312";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[wgtblock(wb1, 6)][block][wgtblock(wb3, 6)])

    #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][wgtblock(wb2, 8)][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
}

/* ---------------------------------------------DISTR313*/
/* DISTRIBUTE arrA3[BLOCK][WGT_BLOCK][BLOCK]
                REDISTRIBUTE arrA3[WGT_BLOCK][BLOCK][WGT_BLOCK]*/
void distrwgt313()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt313";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[block][wgtblock(wb2, 8)][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[wgtblock(wb1, 6)][block][wgtblock(wb3, 6)])
    #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[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA3[*][*][*]*/
void distrwgt314()
{
    #define AN1 8
    #define AN2 12
    #define AN3 24
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt314";

    erri = ER;

    A3 = malloc(AN1 * AN2 * AN3 * sizeof(int));
    #pragma dvm redistribute(A3[wgtblock(wb1, 6)][wgtblock(wb2, 8)][wgtblock(wb3, 6)])

    #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
}

/* ---------------------------------------------DISTR315*/
/*  DISTRIBUTE arrA3[*][*][*]
                REDISTRIBUTE arrA3[WGT_BLOCK][WGT_BLOCK][WGT_BLOCK]*/
void distrwgt315()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array distribute[*][*][*]
    int (*A3)[AN2][AN3];
    char tname[] = "distrwgt315";

    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[wgtblock(wb1, 6)][wgtblock(wb2, 8)][wgtblock(wb3, 6)])
    #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
}

/* ---------------------------------------------DISTR41*/
/*DISTRIBUTE arrA4[WGT_BLOCK][*][WGT_BLOCK][WGT_BLOCK]
                REDISTRIBUTE arrA4[BLOCK][WGT_BLOCK][BLOCK][*]*/
void distrwgt41()
{
    #define AN1 8
    #define AN2 8
    #define AN3 8
    #define AN4 8
    double wb1[6] = {2.0, 1., 1., 3.0, 2., 1.};
    double wb2[8] = {1.0, 1., 2., 2.0, 1., 1., 2., 2.};
    double wb3[6] = {2.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A4)[AN2][AN3][AN4];
    char tname[] = "distrwgt41";

    erri = ER;

    A4 = malloc(AN1 * AN2 * AN3 * AN4 * sizeof(int));
    #pragma dvm redistribute(A4[wgtblock(wb1, 6)][*][wgtblock(wb2, 8)][wgtblock(wb3, 6)])

    #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[block][wgtblock(wb3, 6)][block][*])
    #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);
}
