////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Author: David Goz
// mail  : david.goz@inaf.it
// date  : 31.07.2024
// code tested using nvhpc
//
// - Compile the code:
//   $ nvc -mp=gpu -gpu=ccnative,debug,lineinfo -target=gpu -Minfo=all -v structure.c -o structure_omp
// - Run the code:
//   $ export OMP_TARGET_OFFLOAD=mandatory
//   $ ./structure_omp
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <omp.h>

#define SIZE   8
#define SIZE_2 (SIZE / 2)
typedef double MyData;

typedef struct my_span
{
  size_t N;
  MyData *ptr;
} span;

void allocate(      span  *my_struct,
	      const size_t size)
{
  /* allocate the buffer on the host memory */
  my_struct->ptr = (MyData *)calloc(size, sizeof(MyData));
  assert(my_struct->ptr != NULL);
  my_struct->N = size;
  
  return;
}

void print(const span *const A,
	   const char *const string)
{
  printf("\n");
  for (int i=0 ; i<A->N ; i++)
    printf("\n\t %s[%d] = %lg", string, i, A->ptr[i]);
  printf("\n\n");
  
  return;
}

int main()
{
  span A, B, C;

  allocate(&A, SIZE);
  allocate(&B, SIZE);
  allocate(&C, SIZE);

  /* declare how the object 'span' has to be mapped on the device */
  #pragma omp declare mapper(all  : span S) map(to: S) map(from: S.ptr[0    : S.N])
  #pragma omp declare mapper(left : span S) map(to: S) map(from: S.ptr[0    : S.N/2])
  #pragma omp declare mapper(right: span S) map(to: S) map(from: S.ptr[S.N/2: S.N/2])

  /* init on the GPU */
#pragma omp target map(mapper(all): A) map(mapper(left): B) map(mapper(right): C)
  {
    #pragma omp loop
    for (size_t i=0 ; i<SIZE ; i++)
      A.ptr[i] = (MyData)(i);

    #pragma omp loop
    for (size_t ii=0 ; ii<SIZE_2 ; ii++)
      B.ptr[ii] = (MyData)(ii);

    #pragma omp loop
    for (size_t iii=SIZE_2 ; iii<SIZE ; iii++)
      C.ptr[iii] = (MyData)(iii);
  }

  print(&A, "A");
  print(&B, "B");
  print(&C, "C");
  
  /* free the host's memory */
  free(A.ptr);
  free(B.ptr);
  free(C.ptr);

  return 0;
}
