static char help[] = "Test MatSetPreallocationCOO and MatSetValuesCOO\n\n";

#include <petscmat.h>

int main(int argc, char **args)
{
  Mat                    A, At, AAt, T = NULL;
  Vec                    x, y, z;
  ISLocalToGlobalMapping rl2g, cl2g;
  IS                     is;
  PetscLayout            rmap, cmap;
  PetscInt              *it, *jt;
  PetscInt               n1 = 11, n2 = 9;
  PetscInt               i1[] = {7, 6, 2, 0, 4, 1, 1, 0, 2, 2, 1, -1, -1};
  PetscInt               j1[] = {1, 4, 3, 5, 3, 3, 4, 5, 0, 3, 1, -1, -1};
  PetscInt               i2[] = {7, 6, 2, 0, 4, 1, 1, 2, 1, -1, -1};
  PetscInt               j2[] = {1, 4, 3, 5, 3, 3, 4, 0, 1, -1, -1};
  PetscScalar            v1[] = {-1., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., PETSC_MAX_REAL, PETSC_MAX_REAL};
  PetscScalar            v2[] = {1., -1., -2., -3., -4., -5., -6., -7., -8., -9., -10., PETSC_MAX_REAL, PETSC_MAX_REAL};
  PetscInt               N = 6, m = 8, M, rstart, cstart, i;
  PetscMPIInt            size;
  PetscBool              loc      = PETSC_FALSE;
  PetscBool              locdiag  = PETSC_TRUE;
  PetscBool              localapi = PETSC_FALSE;
  PetscBool              neg      = PETSC_FALSE;
  PetscBool              ismatis, ismpiaij, ishypre;

  PetscFunctionBeginUser;
  PetscCall(PetscInitialize(&argc, &args, NULL, help));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-neg", &neg, NULL));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-loc", &loc, NULL));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-locdiag", &locdiag, NULL));
  PetscCall(PetscOptionsGetBool(NULL, NULL, "-localapi", &localapi, NULL));
  PetscCall(MatCreate(PETSC_COMM_WORLD, &A));
  if (loc) {
    if (locdiag) {
      PetscCall(MatSetSizes(A, m, N, PETSC_DECIDE, PETSC_DECIDE));
    } else {
      PetscCall(MatSetSizes(A, m, m + N, PETSC_DECIDE, PETSC_DECIDE));
    }
  } else {
    PetscCall(MatSetSizes(A, m, PETSC_DECIDE, PETSC_DECIDE, N));
  }
  PetscCall(MatSetFromOptions(A));
  PetscCall(MatGetLayouts(A, &rmap, &cmap));
  PetscCall(PetscLayoutSetUp(rmap));
  PetscCall(PetscLayoutSetUp(cmap));
  PetscCall(PetscLayoutGetRange(rmap, &rstart, NULL));
  PetscCall(PetscLayoutGetRange(cmap, &cstart, NULL));
  PetscCall(PetscLayoutGetSize(rmap, &M));
  PetscCall(PetscLayoutGetSize(cmap, &N));

  PetscCall(PetscObjectTypeCompare((PetscObject)A, MATIS, &ismatis));
  PetscCall(PetscObjectTypeCompare((PetscObject)A, MATHYPRE, &ishypre));

  /* create fake l2g maps to test the local API */
  PetscCall(ISCreateStride(PETSC_COMM_WORLD, M - rstart, rstart, 1, &is));
  PetscCall(ISLocalToGlobalMappingCreateIS(is, &rl2g));
  PetscCall(ISDestroy(&is));
  PetscCall(ISCreateStride(PETSC_COMM_WORLD, N, 0, 1, &is));
  PetscCall(ISLocalToGlobalMappingCreateIS(is, &cl2g));
  PetscCall(ISDestroy(&is));
  PetscCall(MatSetLocalToGlobalMapping(A, rl2g, cl2g));
  PetscCall(ISLocalToGlobalMappingDestroy(&rl2g));
  PetscCall(ISLocalToGlobalMappingDestroy(&cl2g));

  PetscCall(MatCreateVecs(A, &x, &y));
  PetscCall(MatCreateVecs(A, NULL, &z));
  PetscCall(VecSet(x, 1.));
  PetscCall(VecSet(z, 2.));
  if (!localapi)
    for (i = 0; i < n1; i++) i1[i] += rstart;
  if (!localapi)
    for (i = 0; i < n2; i++) i2[i] += rstart;
  if (loc) {
    if (locdiag) {
      for (i = 0; i < n1; i++) j1[i] += cstart;
      for (i = 0; i < n2; i++) j2[i] += cstart;
    } else {
      for (i = 0; i < n1; i++) j1[i] += cstart + m;
      for (i = 0; i < n2; i++) j2[i] += cstart + m;
    }
  }
  if (neg) {
    n1 += 2;
    n2 += 2;
  }
  /* MatSetPreallocationCOOLocal maps the indices! */
  PetscCall(PetscMalloc2(PetscMax(n1, n2), &it, PetscMax(n1, n2), &jt));
  /* test with repeated entries */
  if (!localapi) {
    PetscCall(MatSetPreallocationCOO(A, n1, i1, j1));
  } else {
    PetscCall(PetscArraycpy(it, i1, n1));
    PetscCall(PetscArraycpy(jt, j1, n1));
    PetscCall(MatSetPreallocationCOOLocal(A, n1, it, jt));
  }
  PetscCall(MatSetValuesCOO(A, v1, ADD_VALUES));
  PetscCall(MatMult(A, x, y));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(y, NULL));
  PetscCall(MatSetValuesCOO(A, v2, ADD_VALUES));
  PetscCall(MatMultAdd(A, x, y, y));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(y, NULL));
  T = A;
  if (ishypre) PetscCall(MatConvert(A, MATAIJ, MAT_INITIAL_MATRIX, &T));
  PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &At));
  if (!ismatis) {
    PetscCall(MatMatMult(T, At, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
    PetscCall(MatMatMult(At, T, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
  }
  PetscCall(MatDestroy(&At));
  if (ishypre) PetscCall(MatDestroy(&T));

  /* INSERT_VALUES will overwrite matrix entries but
     still perform the sum of the repeated entries */
  PetscCall(MatSetValuesCOO(A, v2, INSERT_VALUES));
  PetscCall(MatView(A, NULL));

  /* test with unique entries */
  PetscCall(PetscArraycpy(it, i2, n2));
  PetscCall(PetscArraycpy(jt, j2, n2));
  if (!localapi) {
    PetscCall(MatSetPreallocationCOO(A, n2, it, jt));
  } else {
    PetscCall(MatSetPreallocationCOOLocal(A, n2, it, jt));
  }
  PetscCall(MatSetValuesCOO(A, v1, ADD_VALUES));
  PetscCall(MatMult(A, x, y));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(y, NULL));
  PetscCall(MatSetValuesCOO(A, v2, ADD_VALUES));
  PetscCall(MatMultAdd(A, x, y, z));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(z, NULL));
  PetscCall(PetscArraycpy(it, i2, n2));
  PetscCall(PetscArraycpy(jt, j2, n2));
  if (!localapi) {
    PetscCall(MatSetPreallocationCOO(A, n2, it, jt));
  } else {
    PetscCall(MatSetPreallocationCOOLocal(A, n2, it, jt));
  }
  PetscCall(MatSetValuesCOO(A, v1, INSERT_VALUES));
  PetscCall(MatMult(A, x, y));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(y, NULL));
  PetscCall(MatSetValuesCOO(A, v2, INSERT_VALUES));
  PetscCall(MatMultAdd(A, x, y, z));
  PetscCall(MatView(A, NULL));
  PetscCall(VecView(z, NULL));
  T = A;
  if (ishypre) PetscCall(MatConvert(A, MATAIJ, MAT_INITIAL_MATRIX, &T));
  PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &At));
  if (!ismatis) {
    PetscCall(MatMatMult(T, At, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
    PetscCall(MatMatMult(At, T, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
  }
  PetscCall(MatDestroy(&At));
  if (ishypre) PetscCall(MatDestroy(&T));

  /* test providing diagonal first, then off-diagonal */
  PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
  PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATMPIAIJ, &ismpiaij));
  if ((ismpiaij || ishypre) && size > 1) {
    Mat                lA, lB;
    const PetscInt    *garray, *iA, *jA, *iB, *jB;
    const PetscScalar *vA, *vB;
    PetscScalar       *coo_v;
    PetscInt          *coo_i, *coo_j;
    PetscInt           i, j, nA, nB, nnz;
    PetscBool          flg;

    T = A;
    if (ishypre) PetscCall(MatConvert(A, MATAIJ, MAT_INITIAL_MATRIX, &T));
    PetscCall(MatMPIAIJGetSeqAIJ(T, &lA, &lB, &garray));
    PetscCall(MatSeqAIJGetArrayRead(lA, &vA));
    PetscCall(MatSeqAIJGetArrayRead(lB, &vB));
    PetscCall(MatGetRowIJ(lA, 0, PETSC_FALSE, PETSC_FALSE, &nA, &iA, &jA, &flg));
    PetscCall(MatGetRowIJ(lB, 0, PETSC_FALSE, PETSC_FALSE, &nB, &iB, &jB, &flg));
    nnz = iA[nA] + iB[nB];
    PetscCall(PetscMalloc3(nnz, &coo_i, nnz, &coo_j, nnz, &coo_v));
    nnz = 0;
    for (i = 0; i < nA; i++) {
      for (j = iA[i]; j < iA[i + 1]; j++, nnz++) {
        coo_i[nnz] = i + rstart;
        coo_j[nnz] = jA[j] + cstart;
        coo_v[nnz] = vA[j];
      }
    }
    for (i = 0; i < nB; i++) {
      for (j = iB[i]; j < iB[i + 1]; j++, nnz++) {
        coo_i[nnz] = i + rstart;
        coo_j[nnz] = garray[jB[j]];
        coo_v[nnz] = vB[j];
      }
    }
    PetscCall(MatRestoreRowIJ(lA, 0, PETSC_FALSE, PETSC_FALSE, &nA, &iA, &jA, &flg));
    PetscCall(MatRestoreRowIJ(lB, 0, PETSC_FALSE, PETSC_FALSE, &nB, &iB, &jB, &flg));
    PetscCall(MatSeqAIJRestoreArrayRead(lA, &vA));
    PetscCall(MatSeqAIJRestoreArrayRead(lB, &vB));
    if (ishypre) PetscCall(MatDestroy(&T));

    PetscCall(MatSetPreallocationCOO(A, nnz, coo_i, coo_j));
    PetscCall(MatSetValuesCOO(A, coo_v, ADD_VALUES));
    PetscCall(MatMult(A, x, y));
    PetscCall(MatView(A, NULL));
    PetscCall(VecView(y, NULL));
    PetscCall(MatSetValuesCOO(A, coo_v, INSERT_VALUES));
    PetscCall(MatMult(A, x, y));
    PetscCall(MatView(A, NULL));
    PetscCall(VecView(y, NULL));

    T = A;
    if (ishypre) PetscCall(MatConvert(A, MATAIJ, MAT_INITIAL_MATRIX, &T));
    PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &At));
    PetscCall(MatMatMult(T, At, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
    PetscCall(MatMatMult(At, T, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AAt));
    PetscCall(MatView(AAt, NULL));
    PetscCall(MatDestroy(&AAt));
    PetscCall(MatDestroy(&At));
    if (ishypre) PetscCall(MatDestroy(&T));

    PetscCall(PetscFree3(coo_i, coo_j, coo_v));
  }
  PetscCall(PetscFree2(it, jt));
  PetscCall(VecDestroy(&z));
  PetscCall(VecDestroy(&x));
  PetscCall(VecDestroy(&y));
  PetscCall(MatDestroy(&A));
  PetscCall(PetscFinalize());
  return 0;
}

/*TEST

   test:
     suffix: 1
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type {{seqaij mpiaij}} -localapi {{0 1}} -neg {{0 1}}

   test:
     requires: hypre
     suffix: 1_hypre
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type hypre -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_1.out

   test:
     requires: cuda
     suffix: 1_cuda
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type {{seqaijcusparse mpiaijcusparse}} -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_1.out

   test:
     requires: kokkos_kernels
     suffix: 1_kokkos
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type {{seqaijkokkos mpiaijkokkos}} -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_1.out

   test:
     suffix: 2
     nsize: 7
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaij -localapi {{0 1}} -neg {{0 1}}

   test:
     requires: hypre
     suffix: 2_hypre
     nsize: 7
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type hypre -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_2.out

   test:
     requires: cuda
     suffix: 2_cuda
     nsize: 7
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaijcusparse -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_2.out

   test:
     requires: kokkos_kernels
     suffix: 2_kokkos
     nsize: 7
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaijkokkos -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_2.out

   test:
     suffix: 3
     nsize: 3
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaij -loc -localapi {{0 1}} -neg {{0 1}}

   test:
     requires: hypre
     suffix: 3_hypre
     nsize: 3
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type hypre -loc -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_3.out

   test:
     requires: cuda
     suffix: 3_cuda
     nsize: 3
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaijcusparse -loc -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_3.out

   test:
     requires: kokkos_kernels
     suffix: 3_kokkos
     nsize: 3
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type aijkokkos -loc -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_3.out

   test:
     suffix: 4
     nsize: 4
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaij -loc -locdiag 0 -localapi {{0 1}} -neg {{0 1}}

   test:
     requires: hypre
     suffix: 4_hypre
     nsize: 4
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type hypre -loc -locdiag 0 -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_4.out

   test:
     requires: cuda
     suffix: 4_cuda
     nsize: 4
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type mpiaijcusparse -loc -locdiag 0 -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_4.out

   test:
     requires: kokkos_kernels
     suffix: 4_kokkos
     nsize: 4
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type aijkokkos -loc -locdiag 0 -localapi {{0 1}} -neg {{0 1}}
     output_file: output/ex123_4.out

   test:
     suffix: matis
     nsize: 3
     filter: grep -v type | grep -v "Mat Object"
     diff_args: -j
     args: -mat_type is -localapi {{0 1}} -neg {{0 1}}

TEST*/
