/*    DISTRMULT1
    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 distrm11();
static void distrm12();
static void distrm13();
static void distrm14();
static void distrm15();
static void distrm16();
static void distrm17();
static void distrm21();
static void distrm22();
static void distrm23();

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 DISTRMULT1 ===================\n");

    /* 11  DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[MULT_BLOCK] */
    distrm11();
    /* 12  DISTRIBUTE arrA1[MULT_BLOCK]  REDISTRIBUTE arrA1[BLOCK] */
    distrm12();
    /* 13  DISTRIBUTE arrA1[BLOCK]       REDISTRIBUTE arrA1[MULT_BLOCK] small array*/
    distrm13();
    /* 14  DISTRIBUTE arrA1[MULT_BLOCK]  REDISTRIBUTE arrA1[BLOCK] small array*/
    distrm14();
    /* 15  DISTRIBUTE arrA1[MULT_BLOCK]  REDISTRIBUTE arrA1[MULT_BLOCK] other m*/
    distrm15();
    /* 16  DISTRIBUTE arrA1[MULT_BLOCK]  REDISTRIBUTE arrA1[*]*/
    distrm16();
    /* 17  DISTRIBUTE arrA1[*]           REDISTRIBUTE arrA1[MULT_BLOCK]*/
    distrm17();
    /* 21  DISTRIBUTE arrA2[MULT_BLOCK][*]   REDISTRIBUTE arrA2[*][MULT_BLOCK]*/
    distrm21();
    /* 22  DISTRIBUTE arrA2[*][MULT_BLOCK]   REDISTRIBUTE arrA2[*][*]*/
    distrm22();
    /* 23  DISTRIBUTE arrA2[*][*]            REDISTRIBUTE arrA2[*][MULT_BLOCK]*/
    distrm23();

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

/* ---------------------------------------------DISTR11*/
/* DISTR arrA1[BLOCK]    REDISTR arrA1[MULT_BLOCK]*/
void distrm11()
{
    #define AN1 25
    int m = 5;
    #pragma dvm array distribute[block]
    int (*A1);
    char tname[] = "distrm11";

    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[multblock(m)])
    #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*/
/* DISTRIBUTE arrA1[MULT_BLOCK]    REDISTRIBUTE arrA1[BLOCK] */
void distrm12()
{
    #define AN1 48
    int m = 6;
    #pragma dvm array
    int (*A1);
    char tname[] = "distrm12";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[multblock(m)])

    #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*/
/* DISTR arrA1[BLOCK]    REDISTR arrA1[MULT_BLOCK] small array */
void distrm13()
{
    #define AN1 4
    int m = 4;
    #pragma dvm array distribute[block]
    int (*A1);
    char tname[] = "distrm13";

    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[multblock(m)])
    #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*/
/*  DISTRIBUTE arrA1[MULT_BLOCK]   REDISTRIBUTE arrA1[BLOCK]  small array*/
void distrm14()
{
    #define AN1 3
    int m = 3;
    #pragma dvm array
    int (*A1);
    char tname[] = "distrm14";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[multblock(m)])

    #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*/
/*  DISTR arrA1[MULT_BLOCK]    REDISTR arrA1[MULT_BLOCK] other m */
void distrm15()
{
    #define AN1 24
    int m1 = 4, m2 = 3;
    #pragma dvm array
    int (*A1);
    char tname[] = "distrm15";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[multblock(m1)])

    #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[multblock(m2)])
    #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*/
/*  DISTR arrA1[MULT_BLOCK]    REDISTR arrA1[*]*/
void distrm16()
{
    #define AN1 50
    int m = 2;
    #pragma dvm array
    int (*A1);
    char tname[] = "distrm16";

    erri = ER;

    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[multblock(m)])

    #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*/
/*  DISTRIBUTE arrA1[*]    REDISTRIBUTE arrA1[MULT_BLOCK]*/
void distrm17()
{
    #define AN1 120
    int m = 10;
    #pragma dvm array distribute[*]
    int (*A1);
    char tname[] = "distrm17";

    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[multblock(m)])
    #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
}

/* ---------------------------------------------DISTR21*/
/*  DISTRIBUTE arrA2[MULT_BLOCK][*]    REDISTRIBUTE arrA2[*][MULT_BLOCK] */
void distrm21()
{
    #define AN1 36
    #define AN2 25
    int m1 = 6,m2 = 5;
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrm21";

    erri = ER;

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

    #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[*][multblock(m2)])
    #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*/
/*  DISTRIBUTE arrA2[*][MULT_BLOCK]    REDISTRIBUTE arrA2[*][*] */
void distrm22()
{
    #define AN1 8
    #define AN2 121
    int m2 = 11;
    #pragma dvm array
    int (*A2)[AN2];
    char tname[] = "distrm22";

    erri = ER;

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

    #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*/
/*  DISTRIBUTE arrA2[*][*]    REDISTRIBUTE arrA2[*][MULT_BLOCK] */
void distrm23()
{
    #define AN1 8
    #define AN2 63
    int m2 = 9;
    #pragma dvm array distribute[*][*]
    int (*A2)[AN2];
    char tname[] = "distrm23";

    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[*][multblock(m2)])
    #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);
}
