/*    DISTR1
TESTING distribute and redistribute CLAUSE*/

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

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

static void distr11();
static void distr12();
static void distr13();
static void distr14();
static void distr21();
static void distr22();
static void distr23();

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

int main(int an, char **as)
{
    printf("=== START OF DISTR1 ===================\n");
            /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*]*/
    distr11();
            /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[BLOCK]*/
    distr12();
            /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*] small array*/
    distr13();
             /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[BLOCK] small array*/
    distr14();
             /* DISTRIBUTE arrA2[BLOCK][*] REDISTRIBUTE arrA2[*][BLOCK]*/
    distr21();
             /* DISTRIBUTE arrA2[*][BLOCK] REDISTRIBUTE arrA2[*][*]*/
    distr22();
             /* DISTRIBUTE arrA2[*][*] REDISTRIBUTE arrA2[*][BLOCK]*/
    distr23();
    printf("=== END OF DISTR1 =====================\n");
    return 0;
}
/* ---------------------------------------------DISTR11*/
            /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*]*/
void distr11()
{
    #define AN1 8

    #pragma dvm array distribute[block]
    int A1[AN1];
    char tname[] = "distr11";

    erri = ER;
    #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);
    #undef AN1
}
/* ---------------------------------------------DISTR12*/
            /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[BLOCK]*/
void distr12()
{
    #define AN1 8

    #pragma dvm array distribute[*]
    float A1[AN1];
    char tname[] = "distr12";

    erri = ER;
    #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);
    #undef AN1
}
/* ---------------------------------------------DISTR13*/
            /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*] small array*/
void distr13()
{
    #define AN1 5

    #pragma dvm array distribute[block]
    double A1[AN1];
    char tname[] = "distr13";

    erri = ER;
    #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);
    #undef AN1
}
/* ---------------------------------------------DISTR14*/
             /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[BLOCK] small array*/
void distr14()
{
    #define AN1 5

    #pragma dvm array distribute[*]
    long A1[AN1];
    char tname[] = "distr14";

    erri = ER;
    #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);
    #undef AN1
}
/* ---------------------------------------------DISTR21*/
             /* DISTRIBUTE arrA2[BLOCK][*] REDISTRIBUTE arrA2[*][BLOCK]*/
void distr21()
{
    #define AN1 8
    #define AN2 8

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

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

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

    #pragma dvm array distribute[*][block]
    float A2[AN1][AN2];
    char tname[] = "distr22";

    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
}
/* ---------------------------------------------DISTR23*/
             /* DISTRIBUTE arrA2[*][*] REDISTRIBUTE arrA2[*][BLOCK]*/
void distr23()
{
    #define AN1 8
    #define AN2 8

    #pragma dvm array distribute[*][*]
    double A2[AN1][AN2];
    char tname[] = "distr23";

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

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

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

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