/* ----------------------------------------------------------------------
 * Main NVector Testing Routine
 * --------------------------------------------------------------------*/
int main(int argc, char *argv[]) 
{
  int      fails = 0;                   /* counter for test failures */
  long int local_length, global_length; /* vector lengths            */
  N_Vector W;
  N_Vector X, Y, Z;                     /* test vectors              */
  MPI_Comm comm;                        /* MPI Communicator          */
  int      nprocs, myid;                /* Number of procs, proc id  */
  long int veclen;                      /* vector length             */
  int      print_timing;
  HYPRE_Int *partitioning;      /* Vector Partitioning */
  HYPRE_ParVector Xhyp;         /* Instantiate hypre parallel vector */

  /* check input and set vector length */
  if (argc < 3) {
    SetTiming(0);
  } else {
   print_timing = atoi(argv[2]);
   SetTiming(print_timing);
  }
  
  if (argc < 2) {
    veclen = VECLEN;
  } else {
    veclen = atol(argv[1]); 
    if (veclen <= 0) {
      printf("ERROR: length of vector must be a positive integer \n");
      return(-1); 
    }
  }

  /* printf("\nRunning with vector length %ld \n \n", veclen); */
  /* Get processor number and total number of processes */
  MPI_Init(&argc, &argv);
  /* omp_set_num_threads(4); */
  comm = MPI_COMM_WORLD;
  MPI_Comm_size(comm, &nprocs);
  MPI_Comm_rank(comm, &myid);

  /* set partitioning */
  local_length = veclen;
  global_length = nprocs*local_length;
  if(HYPRE_AssumedPartitionCheck()) {
    partitioning = (HYPRE_Int*) malloc(2*sizeof(HYPRE_Int));
    partitioning[0] = myid*local_length;
    partitioning[1] = (myid+1)*local_length;
  } else {
    partitioning = (HYPRE_Int*) malloc((nprocs+1)*sizeof(HYPRE_Int));
    if (veclen <= 0) {
      printf("Using global partition.\n");
      printf("I don't do this stuff. Now exiting...\n");
      return -1;
    }
  }
  /* Create template hypre vector */
  HYPRE_ParVectorCreate(comm, global_length, partitioning, &Xhyp);
  HYPRE_ParVectorInitialize(Xhyp);

  /* Create empty vector */
  W = N_VNewEmpty_ParHyp(comm, local_length, global_length);

  /* NVector Test */

  /* Hypre specific tests */
  fails += Test_N_VMake(Xhyp, myid);

  /* Create hypre vector wrapper */
  X = N_VMake_ParHyp(Xhyp);

  /* Memory allocation tests */
  fails += Test_N_VCloneEmpty(X, myid);
  fails += Test_N_VClone(X, local_length, myid);
  fails += Test_N_VCloneEmptyVectorArray(5, X, myid);
  fails += Test_N_VCloneVectorArray(5, X, local_length, myid);

  /* Create a couple of more vectors by cloning X */
  Y = N_VClone(X);
  Z = N_VClone(X);
  
  /* Skipped tests */
  /* Accessing HYPRE vector raw data is not allowed from N_Vector interface */
  /* fails += Test_N_VSetArrayPointer(W, local_length, myid); */
  /* fails += Test_N_VGetArrayPointer(X, local_length, myid); */
  
  /* N_Vector interface tests */
  fails += Test_N_VGetVectorID(W, myid);
  fails += Test_N_VConst(X, local_length, myid);
  fails += Test_N_VLinearSum(X, Y, Z, local_length, myid);
  fails += Test_N_VProd(X, Y, Z, local_length, myid);
  fails += Test_N_VDiv(X, Y, Z, local_length, myid);
  fails += Test_N_VScale(X, Z, local_length, myid);
  fails += Test_N_VAbs(X, Z, local_length, myid);
  fails += Test_N_VInv(X, Z, local_length, myid);
  fails += Test_N_VAddConst(X, Z, local_length, myid);
  fails += Test_N_VDotProd(X, Y, local_length, global_length, myid);
  fails += Test_N_VMaxNorm(X, local_length, myid);
  fails += Test_N_VWrmsNorm(X, Y, local_length, myid);
  fails += Test_N_VWrmsNormMask(X, Y, Z, local_length, global_length, myid);
  fails += Test_N_VMin(X, local_length, myid);
  fails += Test_N_VWL2Norm(X, Y, local_length, global_length, myid);
  fails += Test_N_VL1Norm(X, local_length, global_length, myid);
  fails += Test_N_VCompare(X, Z, local_length, myid);
  fails += Test_N_VInvTest(X, Z, local_length, myid);
  fails += Test_N_VConstrMask(X, Y, Z, local_length, myid);
  fails += Test_N_VMinQuotient(X, Y, local_length, myid);

  /* Free vectors */
  N_VDestroy_ParHyp(W);
  N_VDestroy_ParHyp(X);
  N_VDestroy_ParHyp(Y);
  N_VDestroy_ParHyp(Z);
  
  /* Print result */
  if (fails) {
    printf("FAIL: NVector module failed %i tests, Proc %d \n \n", fails, myid);
  } else {
     if(myid == 0) {
       printf("SUCCESS: NVector module passed all tests, Proc %d \n \n",myid);
     }
  }

  /* Free hypre template vector */
  HYPRE_ParVectorDestroy(Xhyp);
  
  MPI_Finalize();
  return(0);
}
/* ----------------------------------------------------------------------
 * Main NVector Testing Routine
 * --------------------------------------------------------------------*/
int main(int argc, char *argv[]) 
{
  int      fails = 0;            /* counter for test failures */
  long int veclen;               /* vector length             */
  N_Vector W, X, Y, Z;           /* test vectors              */
  int      num_threads;
  int      print_timing;

  /* check input and set vector length */
  if (argc < 4){
    printf("ERROR: THREE (3) arguments required: <vector length> <number of threads> <print timing>\n");
    return(-1);
  }

  veclen = atol(argv[1]); 
  if (veclen <= 0) {
    printf("ERROR: length of vector must be a positive integer \n");
    return(-1); 
  }

  num_threads = atoi(argv[2]);
  if (num_threads <= 0) {
    printf("ERROR: numbber of threads must be a positive integer \n");
    return(-1); 
  }

  print_timing = atoi(argv[3]);
  SetTiming(print_timing);

  printf("\nRunning with vector length %ld \n \n", veclen);
  printf("\nRunning with number of threads %d \n \n", num_threads);

  /* Create vectors */
  W = N_VNewEmpty_OpenMP(veclen, num_threads);
  X = N_VNew_OpenMP(veclen, num_threads);
  Y = N_VNew_OpenMP(veclen, num_threads);
  Z = N_VNew_OpenMP(veclen, num_threads);
  
  /* NVector Tests */
  fails += Test_N_VSetArrayPointer(W, veclen, 0);
  fails += Test_N_VGetArrayPointer(X, veclen, 0);
  fails += Test_N_VLinearSum(X, Y, Z, veclen, 0);
  fails += Test_N_VConst(X, veclen, 0);
  fails += Test_N_VProd(X, Y, Z, veclen, 0);
  fails += Test_N_VDiv(X, Y, Z, veclen, 0);
  fails += Test_N_VScale(X, Z, veclen, 0);
  fails += Test_N_VAbs(X, Z, veclen, 0);
  fails += Test_N_VInv(X, Z, veclen, 0);
  fails += Test_N_VAddConst(X, Z, veclen, 0);
  fails += Test_N_VDotProd(X, Y, veclen, veclen, 0);
  fails += Test_N_VMaxNorm(X, veclen, 0);
  fails += Test_N_VWrmsNorm(X, Y, veclen, 0);
  fails += Test_N_VWrmsNormMask(X, Y, Z, veclen, veclen, 0);
  fails += Test_N_VMin(X, veclen, 0);
  fails += Test_N_VWL2Norm(X, Y, veclen, veclen, 0);
  fails += Test_N_VL1Norm(X, veclen, veclen, 0);
  fails += Test_N_VCompare(X, Z, veclen, 0);
  fails += Test_N_VInvTest(X, Z, veclen, 0);
  fails += Test_N_VConstrMask(X, Y, Z, veclen, 0);
  fails += Test_N_VMinQuotient(X, Y, veclen, 0);
  fails += Test_N_VCloneVectorArray(5, X, veclen, 0);
  fails += Test_N_VCloneEmptyVectorArray(5, X, 0);
  fails += Test_N_VCloneEmpty(X, 0);
  fails += Test_N_VClone(X, veclen, 0);

  /* Free vectors */
  N_VDestroy_OpenMP(W);
  N_VDestroy_OpenMP(X);
  N_VDestroy_OpenMP(Y);
  N_VDestroy_OpenMP(Z);

  /* Print results */
  if (fails) {
    printf("FAIL: NVector module failed %i tests \n \n", fails);
  } else {
    printf("SUCCESS: NVector module passed all tests \n \n");
  }

  return(0);
}