/*    DISTRMIX1
Testing DISTRIBUTE and REDISTRIBUTE directive
            GEN_BLOCK, WGT_BLOCK, MULT_BLOCK distributions
*/

#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 distrmix11();
static void distrmix12();
static void distrmix13();
static void distrmix14();
static void distrmix15();
static void distrmix16();

static void ansyes(const char tname[]);
static void ansno(const char tname[]);

static int NL = 1000;
static int ER = 10000;
static int erri, errib, i, j, k, ia, ib;

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

   /* DISTRIBUTE arrA3[GEN_BLOCK][GEN_BLOCK][GEN_BLOCK]
                REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK][GEN_BLOCK] other blocks*/
    distrmix12();
    /* DISTRIBUTE arrA3[GEN_BLOCK][GEN_BLOCK][GEN_BLOCK]
                REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK][GEN_BLOCK] other blocks*/
    distrmix13();
    /* DISTRIBUTE arrA3[GEN_BLOCK][GEN_BLOCK][GEN_BLOCK]
                REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK][GEN_BLOCK] other blocks*/
    distrmix14();
    /* DISTRIBUTE arrA3[GEN_BLOCK][GEN_BLOCK][GEN_BLOCK]
                REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK][GEN_BLOCK] other blocks*/
    distrmix15();
    /* DISTRIBUTE arrA3[GEN_BLOCK][GEN_BLOCK][GEN_BLOCK]
                REDISTRIBUTE [GEN_BLOCK][GEN_BLOCK][GEN_BLOCK] other blocks*/
    distrmix16();

    printf("=== END OF DISTRMIX1 =====================\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;
}

/* ---------------------------------------------DISTR11*/
/* 11  DISTRIBUTE arrA1[MULT_BLOCK]
                     REDISTRIBUTE arrA1[WGT_BLOCK]
                     REDISTRIBUTE arrA1[MULT_BLOCK]*/
void distrmix11()
{
    #define AN1 64
    int m1 = 4, m2 = 2;
    double wb[7] = {2.1, 4.6, 3., 2.0, 1.5, 2., 3.1};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix11";

    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[wgtblock(wb, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

/* ---------------------------------------------DISTR12*/
/* DISTRIBUTE arrA1[WGT_BLOCK]
                     REDISTRIBUTE arrA1[MULT_BLOCK]
                     REDISTRIBUTE arrA1[WGT_BLOCK]  */
void distrmix12()
{
    #define AN1 75
    int m1 = 15;
    double wb1[6] = {3.1, 1.6, 2., 3.0, 0.5, 2.};
    double wb2[8] = {1.5, 2.1, 2.6, 4.2, 2.5, 3.5, 1., 2.1};
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix12";

    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[multblock(m1)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #pragma dvm redistribute(A1[wgtblock(wb2, 8)])
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

/* ---------------------------------------------DISTR13*/
/* DISTRIBUTE arrA1[MULT_BLOCK]
                     REDISTRIBUTE arrA1[GEN_BLOCK]
                     REDISTRIBUTE arrA1[MULT_BLOCK]  */
void distrmix13()
{
    #define AN1 30
    int m1 = 5, m2 = 3;
    int* BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix13";

    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[genblock(BS)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

/* ---------------------------------------------DISTR14*/
/* DISTRIBUTE arrA1[GEN_BLOCK]
                     REDISTRIBUTE arrA1[MULT_BLOCK]
                     REDISTRIBUTE arrA1[GEN_BLOCK] */
void distrmix14()
{
    #define AN1 35
    int m1 = 7;
    int* BS1, * BS2;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS2);
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix14";

    erri = ER;

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

    #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(m1)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

/* ---------------------------------------------DISTR15*/
/* DISTRIBUTE arrA1[WGT_BLOCK]
                     REDISTRIBUTE arrA1[GEN_BLOCK]
                     REDISTRIBUTE arrA1[WGT_BLOCK]  */
void distrmix15()
{
    #define AN1 10
    double wb1[6] = {1.0, 2., 2., 3.0, 1., 1.};
    double wb2[5] = {2.0, 1., 2., 2.0, 2.};
    int* BS;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS);
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix15";

    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[genblock(BS)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #pragma dvm redistribute(A1[wgtblock(wb2, 5)])
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

/* ---------------------------------------------DISTR16*/
/* DISTRIBUTE arrA1[GEN_BLOCK]
                     REDISTRIBUTE arrA1[WGT_BLOCK]
                      REDISTRIBUTE arrA1[GEN_BLOCK]*/
void distrmix16()
{
    #define AN1 12
    double wb[7] = {1.0, 2., 2., 3.0, 1., 1., 0.5};
    int* BS1, * BS2;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1);
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS2);
    #pragma dvm array
    int (*A1);
    char tname[] = "distrmix16";

    erri = ER;

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

    #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, 7)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] += 1;
    }
    #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 + 1)
                erri = Min(erri, i + 1);
    }

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

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

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