////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 N  8
typedef double MyData;

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

void allocate(      span  *my_struct,
	      const size_t size)
{
  span tmp;
  /* allocate the buffer on the host memory */
  tmp.ptr = (MyData *)malloc(size * sizeof(MyData));
  assert(tmp.ptr != NULL);
  tmp.N = size;

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

  my_struct->ptr = tmp.ptr;
  my_struct->N   = tmp.N;
  
  return;
}

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

int main()
{
  span A, B;

  allocate(&A, N);
  allocate(&B, N);

  /* init on the GPU */
  #pragma omp target
  {
    for (size_t i=0 ; i<N ; i++)
      {
	A.ptr[i] = (MyData)(i);
	B.ptr[i] = (MyData)(2 * i);
      }
  }

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

  return 0;
}
