/*    PARALLEL3
Testing PARALLEL directive */

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

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

static void parallel31();
static void parallel32();
static void parallel33();
static void parallel34();
static void parallel341();
static void parallel35();
static void parallel36();

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

static int NL = 10000;
static int ER = 100000;

static int erri,i,j,n,l,ia,ja,na,ib,jb,nb;

int main(int an, char **as)
{
    printf("=== START OF PARALLEL3 ===================\n");
    /* PARALLEL ON  arrA[i][2* j][n] stretching */
    parallel31();
    /* PARALLEL ON  arrA[i+2][ j][n] shift */
    parallel32();
    /* PARALLEL ON  arrA[i][ j][-n+8] reverse */
//  parallel33();
    /* PARALLEL ON  arrA[i][ j][2]  compression */
    parallel34();
    /* PARALLEL ON  arrA[2*i][3*j][1] stretching and compression */
    parallel341 ();
    /* PARALLEL ON  arrA[][j][n]  replication */
    parallel35();
    /* PARALLEL ON arrA[1][2*j+1][3] */
    parallel36();

    printf ("=== END OF PARALLEL3 ===================\n");

    return 0;
}

/* ---------------------------------------------parallel31 */
    /* PARALLEL ON arrA[i][2*j][k] stretching */
void parallel31()
{
    #define AN1 6
    #define AN2 6
    #define AN3 4

/*  parameters for PARALLEL ON arrA[k1i*i+li][k2j*j+lj][k3n*n+ln] */
    int k1i = 1, li = 0;
    int k2j = 2, lj = 0;
    int k3n = 1, ln = 0;

    #pragma dvm array distribute[block][block][block]
    int A3[AN1][AN2][AN3];

    char tname[] = "paral31 ";

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region local(A3)
    {
    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++)
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;

    #pragma dvm parallel([i][j][n] on A3[k1i*i+li][k2j*j+lj][k3n*n+ln]) private(ia,ja,na), reduction(min(erri))
    for (i = 0; i < ((AN1 - li) / k1i); i++)
        for (j = 0; j < ((AN2 - lj) / k2j); j++)
            for (n = 0; n < ((AN3 - ln) / k3n); n++)
            {
                ia = k1i * i + li;
                ja = k2j * j + lj;
                na = k3n * n + ln;
                if (A3[ia][ja][na] != (ia * NL / 10 + ja * NL / 100 + na * NL / 1000))
                    erri = Min(erri, ia * NL / 10 + ja * NL / 100 + na * NL / 1000);
            }
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    #undef AN1
    #undef AN2
    #undef AN3
}

/* ---------------------------------------------parallel32 */
    /* PARALLEL ON arrA[i+2][j][k] shift */
void parallel32()
{
    #define AN1 5
    #define AN2 5
    #define AN3 5

/* parameters for PARALLEL ON  arrA[k1i*i+li][k2j*j+lj][k3n*n+ln] */
    int k1i = 1, li = 2;
    int k2j = 1, lj = 0;
    int k3n = 1, ln = 0;

    #pragma dvm array distribute[block][block][block]
    int (*A3)[AN2][AN3];

    char tname[] = "paral32 ";

    A3 = (int(*)[AN2][AN3])malloc(AN1*sizeof(int[AN2][AN3]));

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region
    {
    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++) {
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000 + 2;
            }

    #pragma dvm parallel([i][j][n] on A3[k1i*i+li][k2j*j+lj][k3n*n+ln]) private(ia,ja,na), reduction(min(erri))
    for (i = 0; i < ((AN1 - li) / k1i); i++)
        for (j = 0; j < ((AN2 - lj) / k2j); j++)
            for (n = 0; n < ((AN3 - ln) / k3n); n++)
            {
                ia = k1i * i + li;
                ja = k2j * j + lj;
                na = k3n * n + ln;
                if (A3[ia][ja][na] != (ia * NL / 10 + ja * NL / 100 + na * NL / 1000 + 2))
                    erri = Min(erri, ia * NL / 10 + ja * NL / 100 + na * NL / 1000);
            }
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    free (A3);

    #undef AN1
    #undef AN2
    #undef AN3
}
/* ---------------------------------------------parallel33 */
    /* PARALLEL ON  arrA[i][j][-k+8] reverse */
void parallel33()
{
    #define AN1 5
    #define AN2 5
    #define AN3 9

/* parameters for PARALLEL ON  arrA[k1i*i+li][k2j*j+lj][k3n*n+ln] */
    int k1i =  1, li = 0;
    int k2j =  1, lj = 0;
    int k3n = -1, ln = 8;

    #pragma dvm array distribute[block][block][block]
    int A3[AN1][AN2][AN3];

    char tname[] = "paral33 ";

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region inout(A3)
    {
    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++)
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL /1000;

    #pragma dvm parallel([i][j][n] on A3[k1i*i+li][k2j*j+lj][k3n*n+ln]) private(ia,ja,na), reduction(min(erri))
    for (i = 0; i < ((AN1 - li) / k1i); i++)
        for (j = 0; j < ((AN2 - lj - 1) / k2j); j++)
            for (n = 0; n < ((AN3 - ln - 1) / k3n); n++)
            {
                ia = k1i * i + li;
                ja = k2j * j + lj;
                na = k3n * n + ln;
                if (A3[ia][ja][na] != (ia * NL / 10 + ja * NL / 100 + na * NL/1000))
                    erri = Min(erri, i * NL / 10 + j * NL / 100 + n * NL / 1000);
            }
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    #undef AN1
    #undef AN2
    #undef AN3
}
/* ---------------------------------------------parallel34 */
    /* PARALLEL ON  arrA[i][ j][2] compression */
void parallel34()
{
    #define AN1 6
    #define AN2 6
    #define AN3 6
    #define BN1 3
    #define BN2 3
    #define BN3 3

/*  parameters for PARALLEL ON  arrA[k1i*i+li][k2j*j+lj][ln] */
    #define k1i 1
    #define li 0
    #define k2j 1
    #define lj 0
    #define k3n 0
    #define ln 2

    #pragma dvm array distribute[block][block][block]
    int (*A3)[AN2][AN3];
    #pragma dvm array     /* deferred aligning */
    int (*B3)[BN2][BN3];

    char tname[] = "paral34 ";

    A3 = (int(*)[AN2][AN3])malloc(AN1*sizeof(int[AN2][AN3]));
    B3 = (int(*)[BN2][BN3])malloc(BN1*sizeof(int[BN2][BN3]));

    #pragma dvm realign(B3[i][j][] with A3[k1i*i+li][k2j*j+lj][ln])

    erri = ER;

    #pragma dvm region inout(A3, B3)
    {
    #pragma dvm parallel([i][j][n] on B3[i][j][n])
    for(i = 0; i < BN1; i++)
        for(j = 0; j < BN2; j++)
            for(n = 0; n < BN3; n++) {
                B3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;
            }

    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for(i = 0; i < AN1; i++)
        for(j = 0; j < AN2; j++)
            for(n = 0; n < AN3; n++) {
                 A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;
             }
    } /*end region*/

    #pragma dvm actual(erri)

    #pragma dvm region
    {
    #pragma dvm parallel ([i][j][n] on  A3[k1i * i + li][k2j * j + lj][ln]) reduction(min(erri))
     for(i = 0; i < BN1; i++)
        for(j = 0; j < BN2; j++)
            for(n = 0; n < BN3; n++) {
                if (B3[i][j][n] != i * NL / 10 + j * NL / 100 + n * NL / 1000)
                    erri = Min(erri, i * NL / 10 + j * NL / 100 + n * NL / 1000);
             }
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
         ansyes(tname);
    else
        ansno(tname);

    free (B3);
    free (A3);

    #undef AN1
    #undef AN2
    #undef AN3
    #undef BN1
    #undef BN2
    #undef BN3
    #undef k1i
    #undef li
    #undef k2j
    #undef lj
    #undef k3n
    #undef ln
}
/* ---------------------------------------------parallel341 */
    /* PARALLEL ON arrA[2*i][3*j][1]  stretching and compression */
void parallel341()
{
    #define AN1 8
    #define AN2 10
    #define AN3 6
    #define BN1 3
    #define BN2 3
    #define BN3 3

/* parameters for PARALLEL ON  arrA[k1i*i+li][k2j*j+lj][ln] */
    int k1i = 2, li = 0;
    int k2j = 3, lj = 0;
    int k3n = 0, ln = 1;

    #pragma dvm array distribute[block][block][block]
    int A3[AN1][AN2][AN3];

    #pragma dvm array align([i][j][] with A3[k1i*i+li][k2j*j+lj][ln])
    int B3[BN1][BN2][BN3];

    char tname[] = "paral341";

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region local(A3, B3)
    {
    #pragma dvm parallel([i][j][n] on B3[i][j][n])
    for (i = 0; i < BN1; i++)
        for (j = 0; j < BN2; j++)
            for (n = 0; n < BN3; n++)
                B3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000 + 4;

    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++)
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;

    #pragma dvm parallel([i][j][n] on A3[k1i*i+li][k2j*j+lj][ln]) reduction(min(erri))
    for (i = 0; i <BN1; i++)
        for (j = 0; j < BN2; j++)
            for (n = 0; n < BN3; n++)
                if (B3[i][j][n] != (i * NL / 10 + j * NL / 100 + n * NL / 1000 + 4))
                    erri = Min(erri, i * NL / 10 + j * NL / 100 + n * NL / 1000);
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    #undef AN1
    #undef AN2
    #undef AN3
    #undef BN1
    #undef BN2
    #undef BN3
}

/* ---------------------------------------------parallel35 */
               /* PARALLEL ON arrA[][j][k] */
void parallel35()
{
    #define AN1 10
    #define AN2 12
    #define AN3 16
    #define BN1 6
    #define BN2 5
    #define BN3 4

/* parameters for PARALLEL ON arrA[*][k2j*j+lj][k3n*n+ln] */
    int k1i = 0, li = 0;
    int k2j = 1, lj = 0;
    int k3n = 1, ln = 0;

    #pragma dvm array distribute[block][block][block]
    int (*A3)[AN2][AN3];
    #pragma dvm array   /* deferred aligning */
    int (*B3)[BN2][BN3];

    char tname[] = "paral35 ";

    A3 = (int(*)[AN2][AN3])malloc(AN1*sizeof(int[AN2][AN3]));
    B3 = (int(*)[BN2][BN3])malloc(BN1*sizeof(int[BN2][BN3]));

    #pragma dvm realign(B3[][j][n] with A3[][k2j*j+lj][k3n*n+ln])

    erri = ER;

    #pragma dvm region in(A3, B3), out(A3, B3)
    {
    #pragma dvm parallel([i][j][n] on B3[i][j][n])
    for (i = 0; i < BN1; i++)
        for (j = 0; j < BN2; j++)
            for (n = 0; n < BN3; n++)
                B3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000 + 5;

    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++)
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000 + 7;
    } /* end region */

    #pragma dvm actual(erri)

    for (i = 0; i < BN1; i++)
    {
        #pragma dvm region
        {
        #pragma dvm parallel([j][n] on A3[][k2j*j+lj][k3n*n+ln]) reduction(min(erri))
        for (j = 0; j < BN2; j++)
            for (n = 0; n < BN3; n++)
                if (B3[i][j][n] != (i * NL / 10 + j * NL / 100 + n * NL / 1000 + 5))
                    erri = Min(erri, i * NL / 10 + j * NL / 100 + n * NL / 1000);
        } /* end region */
    }

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    free (B3);
    free (A3);

    #undef AN1
    #undef AN2
    #undef AN3
    #undef BN1
    #undef BN2
    #undef BN3
}
/* ---------------------------------------------parallel36 */
               /* PARALLEL ON arrA[1][2*j+1][3] */
void parallel36()
{
    #define AN1 6
    #define AN2 8
    #define AN3 7
    #define BN1 4
    #define BN2 3
    #define BN3 5

/* parameters for PARALLEL ON arrA[li][k2j*j+lj][ln] */
    int k1i = 0, li = 1;
    int k2j = 2, lj = 1;
    int k3n = 0, ln = 3;

    #pragma dvm array distribute[block][block][block]
    int A3[AN1][AN2][AN3];

    #pragma dvm array   /* deferred  aligning */
    int (*B3)[BN2][BN3];

    char tname[] = "paral36 ";

    B3 = (int(*)[BN2][BN3])malloc(BN1*sizeof(int[BN2][BN3]));
    #pragma dvm realign(B3[i][j][n] with A3[li][k2j*j+lj][ln])

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region inout(A3), inout(B3)
    {
    #pragma dvm parallel([i][j][n] on B3[i][j][n])
    for (i = 0; i < BN1; i++)
        for (j = 0; j < BN2; j++)
            for (n = 0; n < BN3; n++)
                B3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;

    #pragma dvm parallel([i][j][n] on A3[i][j][n])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            for (n = 0; n < AN3; n++)
                A3[i][j][n] = i * NL / 10 + j * NL / 100 + n * NL / 1000;

    #pragma dvm parallel([i][j][n] on A3[li][k2j*j+lj][ln]) reduction(min(erri))
    for (i = 0; i <BN1; i++)
       for (j = 0; j < BN2; j++)
          for (n = 0; n < BN3; n++)
                if (B3[i][j][n] != (i * NL / 10 + j * NL / 100 + n * NL / 1000))
                    erri = Min(erri, i * NL / 10 + j * NL / 100 + n * NL /1000);
    } /* end region */

    #pragma dvm get_actual(erri)

    if (erri == ER)
        ansyes(tname);
    else
        ansno(tname);

    free (B3);

    #undef AN1
    #undef AN2
    #undef AN3
    #undef BN1
    #undef BN2
    #undef BN3
}

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

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