//////////////////////////////////////////////////////////////////////////////////////////////////
// Author: David Goz
// mail  : david.goz@inaf.it
// date  : 03.09.2024
// code tested using nvhpc
//
// - Compile the code:
//   - nvc complains about the '#pragma omp target has_device_addr'
//   - clang works
//////////////////////////////////////////////////////////////////////////////////////////////////

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

static int answer = 42;
#define SIZE 3

int main()
{
  int *A = (int *)malloc(SIZE * sizeof(int));
  assert(A != NULL);
  memset(A, 0, (SIZE * sizeof(int)));
  
  printf("\n\t Host address of 'answer' [value: %d] is: %p", answer, &answer);
  printf("\n\t Host address of 'A' is: %p", A);

  // Map 'answer' to the device
  #pragma omp target data map(alloc: answer, A[0:SIZE])
  {
    #pragma omp target update to(answer, A[0:SIZE])
    
    // Update 'answer' on the device
    #pragma omp target map(answer)
    {
      answer += 1;
      for (int i=0 ; i<SIZE ; i++)
	A[i] += (i + 1);
    }

    #pragma omp target update from(answer, A[0:SIZE])
    
    printf("\n\t Address of 'answer' [value: %d] in target data region is: %p",
  	   answer, &answer);
    printf("\n\t Address of 'A' in target data region is: %p", A);
    printf("\n\t Value of A:");
    for (int i=0 ; i<SIZE ; i++)
      printf("\n\t\t A[%d] = %d", i, A[i]);
    printf("\n");

    #pragma omp target data use_device_addr(answer) use_device_ptr(A)
    {
      #pragma omp target has_device_addr(answer)
      {
	answer += 1;
	for (int i=0 ; i<SIZE ; i++)
	  A[i] += (i + 1);
      }
      
      printf("\n\t Device address of 'answer' is: %p", &answer);
      printf("\n\t Device address of 'A' is: %p", A);
    }

    #pragma omp target update from(answer, A[0:SIZE])
    printf("\n\t Last value of 'answer' is: %d", answer);
    printf("\n\t Last value if 'A' is:");
    for (int i=0 ; i<SIZE ; i++)
      printf("\n\t\t A[%d] = %d", i, A[i]);
    printf("\n\n");
  } // omp target data

  free(A);
  
  return 0;
}
