/*    PARALLEL2
Testing PARALLEL directive */

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

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

static void parallel21();
static void parallel22();
static void parallel23();
static void parallel24();

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,ia,ja;

int main(int an, char **as)
{
    printf("=== START OF PARALLEL2 ===================\n");

    /* PARALLEL ON arrA[i][2*j]  stretching along j */
    parallel21();
    /* PARALLEL ON arrA[i+4][j]  shift along i */
    parallel22();
    /* PARALLEL ON arrA[-i+8][j] reverse on i */
//  parallel23();
    /* PARALLEL ON arrA[i+4][j+4]  shift along i and j */
    parallel24();

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

    return 0;
}

/* ---------------------------------------------parallel21 */
    /* PARALLEL ON arrA[i][2*j]  stretching along j */
void parallel21()
{
    #define AN1 8
    #define AN2 8

/*  parameters for PARALLEL arrA2[k1i * i + li][k2j * j + lj] */
    int k1i = 1, li = 0;
    int k2j = 2, lj = 0;

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

    char tname[] = "paral21";

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region local(A2)
    {
    #pragma dvm parallel([i][j] on A2[i][j])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            A2[i][j] = i*NL+j;

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

    #pragma dvm get_actual(erri)

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

    #undef AN1
    #undef AN2
}

/* ---------------------------------------------parallel22 */
    /* PARALLEL ON arrA[i+4][j]  shift along i */
void parallel22()
{
    #define AN1 16
    #define AN2 10

/*  parameters for PARALLEL arrA2[k1i * i + li][k2j * j + lj] */
    int k1i = 1, li = 4;
    int k2j = 1, lj = 0;

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

    char tname[] = "paral22";

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

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region
    {
    #pragma dvm parallel([i][j] on A2[i][j])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            A2[i][j] = i * NL + j + 2;

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

    #pragma dvm get_actual(erri)

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

    free (A2);

    #undef AN1
    #undef AN2
}

/* ---------------------------------------------parallel23  */
    /* PARALLEL ON arrA[-i+8][j] reverse on i*/
void parallel23()
{
    #define AN1 8
    #define AN2 14

/*  parameters for PARALLEL arrA2[k1i * i + li][k2j * j + lj] */
    int k1i = -1, li = 8;
    int k2j =  1, lj = 0;

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

    char tname[] = "paral23";

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

    erri = ER;
    #pragma dvm actual(erri)

    #pragma dvm region local(A2)
    {
    #pragma dvm parallel([i][j] on A2[i][j])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            A2[i][j] = i * NL + j - 3;

    #pragma dvm parallel([i][j] on A2[k1i*i+li][k2j*j+lj]) private(ia, ja), reduction(min(erri))
    for (i = 0; i < (AN1 - li) / k1i; i++)
        for (j = 0; j < (AN2 - lj) / k2j; j++)
        {
            ia = k1i * i + li;
            ja = k2j * j + lj;
            if (A2[ia][ja] != (ia * NL + ja) - 3)
                erri = Min(erri, ia * NL + ja);
        }
    } /* end region */

    #pragma dvm get_actual(erri)

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

    free(A2);
    #undef AN1
    #undef AN2
}

/* ---------------------------------------------parallel24 */
    /* PARALLEL ON arrA[i+4][j+4]  shift along i and j */
void parallel24()
{
    #define AN1 16
    #define AN2 15

/*  parameters for PARALLEL arrA2[k1i * i + li][k2j * j + lj] */
    int k1i = 1, li = 4;
    int k2j = 1, lj = 4;

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

    char tname[] = "paral24";

    A2 = malloc(sizeof(int[AN1][AN2]));

    erri= ER;
    #pragma dvm actual(erri)

    #pragma dvm region local(A2)
    {
    #pragma dvm parallel([i][j] on A2[i][j])
    for (i = 0; i < AN1; i++)
        for (j = 0; j < AN2; j++)
            A2[i][j] = i * NL + j;

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

    #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);
}
