/*    DISTRWGT1
    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 distrwgt11();
static void distrwgt12();
static void distrwgt13();
static void distrwgt14();
static void distrwgt15();
static void distrwgt16();
static void distrwgt17();
static void distrwgt21();
static void distrwgt22();
static void distrwgt23();

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;

int main(int an, char **as)
{
    printf("=== START OF DISTRWGT1 ===================\n");
    /* 11  DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[WGT_BLOCK]*/
    distrwgt11();

    /* DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[BLOCK] */
    distrwgt12();

    /* DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[WGT_BLOCK] small array*/
    distrwgt13();

    /* DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[BLOCK] small array*/
    distrwgt14();

    /* DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[WGT_BLOCK] other weights*/
    distrwgt15();

    /* DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[*]*/
    distrwgt16();

    /* DISTRIBUTE arrA1[*]           REDISTRIBUTE arrA1[WGT_BLOCK]*/
    distrwgt17();

    /* DISTRIBUTE arrA2[WGT_BLOCK][*]   REDISTRIBUTE arrA2[*][WGT_BLOCK]*/
    distrwgt21();

    /*DISTRIBUTE arrA2[*][WGT_BLOCK]   REDISTRIBUTE arrA2[*][*]*/
    distrwgt22();

    /* DISTRIBUTE arrA2[*][*]           REDISTRIBUTE arrA2[*][WGT_BLOCK]*/
    distrwgt23();

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

/* ---------------------------------------------DISTR11*/
/* 11  DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[WGT_BLOCK]*/
void distrwgt11()
{
    #define AN1 16
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array distribute[block]
    int (*A1);
    char tname[] = "distrwgt11";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[wgtblock(wb, 6)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR12*/
/* 11   12  DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[BLOCK]*/
void distrwgt12()
{
    #define AN1 8
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrwgt12";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[wgtblock(wb, 6)])

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[block])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR13*/
/* 13  DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[WGT_BLOCK] small array*/
void distrwgt13()
{
    #define AN1 5
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array distribute[block]
    int (*A1);
    char tname[] = "distrwgt13";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[wgtblock(wb, 6)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR14*/
/* 14  DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[BLOCK] small array*/
void distrwgt14()
{
    #define AN1 5
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrwgt14";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[wgtblock(wb, 6)])

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[block])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR15*/
/* 15 DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[WGT_BLOCK] other weights*/
void distrwgt15()
{
    #define AN1 16
    double wb1[6] = {1.0, 2., 2., 3.0, 1., 1.};
    double wb2[6] = {2.0, 1., 2., 2.0, 2., 1.};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrwgt15";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[wgtblock(wb1, 6)])

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[wgtblock(wb2, 6)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR16*/
/* 16  DISTRIBUTE arrA1[WGT_BLOCK]   REDISTRIBUTE arrA1[*]*/
void distrwgt16()
{
    #define AN1 8
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrwgt16";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[wgtblock(wb, 6)])

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }

    #pragma dvm redistribute(A1[*])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR17*/
/* 17  DISTRIBUTE arrA1[*]   REDISTRIBUTE arrA1[WGT_BLOCK]*/
void distrwgt17()
{
    #define AN1 28
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array distribute[*]
    int (*A1);
    char tname[] = "distrwgt17";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));

    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
                    A1[i] = i;
    }

    #pragma dvm redistribute(A1[wgtblock(wb, 6)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) reduction(min(erri)), cuda_block(256)
        for (i = 0; i < AN1; i++)
            if (A1[i] != i)
                erri = Min(erri, i);
    }

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

/* ---------------------------------------------DISTR17*/
/* 21  DISTRIBUTE arrA2[WGT_BLOCK][*]   REDISTRIBUTE arrA2[*][WGT_BLOCK]*/
void distrwgt21()
{
    #define AN1 8
    #define AN2 8
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrwgt21";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[wgtblock(wb, 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[*][wgtblock(wb, 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)
                    erri = Min(erri, i * NL + j);
    }

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

/* ---------------------------------------------DISTR22*/
/* 22  DISTRIBUTE arrA2[*][WGT_BLOCK]   REDISTRIBUTE arrA2[*][*]*/
void distrwgt22()
{
    #define AN1 8
    #define AN2 8
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrwgt22";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));
    #pragma dvm redistribute(A2[*][wgtblock(wb, 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[*][*])
    #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);
    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------DISTR23*/
/* 23  DISTRIBUTE arrA2[*][*]           REDISTRIBUTE arrA2[*][WGT_BLOCK]*/
void distrwgt23()
{
    #define AN1 8
    #define AN2 8
    double wb[6] = {1.0, 2., 2., 3.0, 1., 1.};
    #pragma dvm array distribute[*][*]
    int (*A2)[AN2];
    char tname[] = "distrwgt23";

    erri = ER;

    A2 = malloc(AN1 * AN2 * sizeof(int));

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

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