#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 distr11();
static void distr12();
static void distr13();
static void distr14();
static void distr15();
static void ansyes(const char tname[]);
static void ansno(const char tname[]);
static int ER = 10000;
static int erri, i, j, k;

int main(int an, char **as) {
    printf("=== START OF DELDISTR1 ===================\n");
    distr11();
    distr12();
    distr13();
    distr14();
    distr15();
    printf("=== END OF DELDISTR1 ===================\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;
}

void distr11()
{
    #define AN1 8
    #pragma dvm array
    int (*A1);
    char tname[] = "distr11";
    erri = ER;
    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[*])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }
    #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
}
void distr12()
{
    #define AN1 8
    #pragma dvm array
    int (*A1);
    char tname[] = "distr12";
    erri = ER;
    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[block])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }
    #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
}
void distr13()
{
    #define AN1 8
    int* BS1;
    genBlocksAxis(dvmh_get_num_procs(1), AN1, 1, &BS1);
    #pragma dvm array
    int (*A1);
    char tname[] = "distr13";
    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 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(A1);
    #undef AN1
}
void distr14()
{
    #define AN1 8
    double wb1[8] = {1.0, 2., 2., 3.0, 1., 1., 1.5, 2.};
    #pragma dvm array
    int (*A1);
    char tname[] = "distr14";
    erri = ER;
    A1 = malloc(AN1 * sizeof(int));
    #pragma dvm redistribute(A1[wgtblock(wb1, 8)])
    #pragma dvm region
    {
        #pragma dvm parallel([i] on A1[i]) cuda_block(256)
        for (i = 0; i < AN1; i++)
            A1[i] = i;
    }
    #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
}
void distr15()
{
    #define AN1 8
    int m1 = 4;
    #pragma dvm array
    int (*A1);
    char tname[] = "distr15";
    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 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
}
void ansyes(const char name[]) {
    printf("%s  -  complete\n", name);
}
void ansno(const char name[]) {
    printf("%s  -  ***error\n", name);
}
