/*    DISTRGEN1
TESTING distribute and redistribute CLAUSE
for arrays distributed with GEN-block*/

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

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

#ifndef _DVMH
#define dvmh_get_num_procs(X) 1
#endif

static void distrg10();
static void distrg11();
static void distrg12();
static void distrg13();
static void distrg14();
static void distrg15();
static void distrg161();
static void distrg162();

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 DISTRGEN1 ===================\n");
    /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[GENBLOCK]*/
    distrg10();

    /* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[BLOCK]*/
    distrg11();

    /* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[*]*/
    distrg12();

    /* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*]*/
    distrg13();

    /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[BLOCK]*/
    distrg14();

    /* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[GENBLOCK]*/
    distrg15();

    /* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[GENBLOCK]
       with 0 in BS.1*/
    distrg161();

    /* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[GENBLOCK]
       with 0 in BS.2*/
    distrg162();

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

static int myRand() {
    const unsigned a = 1103515245U;
    const unsigned c = 12345U;
    const unsigned m = ((unsigned)RAND_MAX) + 1U;
    static unsigned prev = 5;
    prev = (a * prev + c) % m;
    return prev;
}

static void genBlocksAxis(const int procCount, const int weight, const int withoutZero, int **blocks) {
    *blocks = (int *)malloc(procCount * sizeof(int));
    int restWeight = weight, i, zeroind = -1;
    if (!withoutZero && procCount != 1)
        zeroind = myRand() % (procCount - 1);
    for (i = 0; i < (procCount - 1); i++) {
        if (i == zeroind)
            (*blocks)[i] = 0;
        else
            (*blocks)[i] = 1 * withoutZero + myRand() % (restWeight - (procCount - (i + 1)));
        restWeight -= (*blocks)[i];
    }
    (*blocks)[i] = restWeight;
}

/* ---------------------------------------------DISTR10*/
/* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[GENBLOCK]*/
void distrg10()
{
    #define AN1 8
    int* BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array distribute[block]
    int A1[AN1];
    char tname[] = "distrg10";
    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[genblock(BS)])
    #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(BS);
    #undef AN1
}

/* ---------------------------------------------DISTR11*/
/* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[BLOCK]*/
void distrg11()
{
    #define AN1 8
    int *BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array distribute[genblock(BS)]
    int A1[AN1];
    char tname[] = "distrg11";

    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);
    free(BS);
    #undef AN1
}

/* ---------------------------------------------DISTR12*/
/* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[*]*/
void distrg12()
{
    #define AN1 8
    int *BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array distribute[genblock(BS)]
    int A1[AN1];
    char tname[] = "distrg12";

    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);
    free(BS);
    #undef AN1
}

/* ---------------------------------------------DISTR13*/
/* DISTRIBUTE arrA1[BLOCK] REDISTRIBUTE arrA1[*]*/
void distrg13()
{
    #define AN1 8

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

    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]*/
void distrg14()
{
    #define AN1 8

    #pragma dvm array distribute[*]
    int A1[AN1];
    char tname[] = "distrg14";

    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
}

/* ---------------------------------------------DISTR15*/
/* DISTRIBUTE arrA1[*] REDISTRIBUTE arrA1[GENBLOCK]*/
void distrg15()
{
    #define AN1 8
    int *BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array distribute[*]
    int A1[AN1];
    char tname[] = "distrg15";

    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[genblock(BS)])
    #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(BS);
    #undef AN1
}

/* ---------------------------------------------DISTR161*/
/* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[GENBLOCK]
   wtih 0 in BS.1*/
/*{0, 8} - works, {8, 0} - cycle*/
void distrg161()
{
    #define AN1 8
    int *BS1, *BS2;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS2);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 0, &BS1);
    #pragma dvm array distribute[genblock(BS1)]
    int A1[AN1];
    char tname[] = "distrg161";

    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[genblock(BS2)])
    #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(BS1);
    free(BS2);
    #undef AN1
}

/* ---------------------------------------------DISTR162*/
/* DISTRIBUTE arrA1[GENBLOCK] REDISTRIBUTE arrA1[GENBLOCK]
   wtih 0 in BS.2*/
void distrg162()
{
    #define AN1 8
    int *BS1, *BS2;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 0, &BS2);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1);
    #pragma dvm array distribute[genblock(BS1)]
    int A1[AN1];
    char tname[] = "distrg162";

    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[genblock(BS2)])
    #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(BS1);
    free(BS2);
    #undef AN1
}

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

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