static char help[] = "Tests MatGetRowMax(), MatGetRowMin(), MatGetRowMaxAbs()\n";

#include <petscmat.h>

#define M 5
#define N 6

int main(int argc, char **args)
{
  Mat         A;
  Vec         min, max, maxabs, e;
  PetscInt    m, n, j, imin[M], imax[M], imaxabs[M], indices[N], row, testcase = 0;
  PetscScalar values[N];
  PetscMPIInt size, rank;
  PetscReal   enorm;

  PetscFunctionBeginUser;
  PetscCall(PetscInitialize(&argc, &args, NULL, help));
  PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
  PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
  PetscCall(PetscOptionsGetInt(NULL, NULL, "-testcase", &testcase, NULL));

  PetscCall(MatCreate(PETSC_COMM_WORLD, &A));
  if (testcase == 1) { /* proc[0] holds entire A and other processes have no entry */
    if (rank == 0) {
      PetscCall(MatSetSizes(A, M, N, PETSC_DECIDE, PETSC_DECIDE));
    } else {
      PetscCall(MatSetSizes(A, 0, 0, PETSC_DECIDE, PETSC_DECIDE));
    }
    testcase = 0;
  } else {
    PetscCall(MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, M, N));
  }
  PetscCall(MatSetFromOptions(A));
  PetscCall(MatSetUp(A));

  if (rank == 0) { /* proc[0] sets matrix A */
    for (j = 0; j < N; j++) indices[j] = j;
    switch (testcase) {
    case 1: /* see testcast 0 */
      break;
    case 2:
      row       = 0;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      values[3] = -4.0;
      values[4] = 1.0;
      values[5] = 1.0;
      PetscCall(MatSetValues(A, 1, &row, N, indices, values, INSERT_VALUES));
      row        = 2;
      indices[0] = 0;
      indices[1] = 3;
      indices[2] = 5;
      values[0]  = -2.0;
      values[1]  = -2.0;
      values[2]  = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      row        = 3;
      indices[0] = 0;
      indices[1] = 1;
      indices[2] = 4;
      values[0]  = -2.0;
      values[1]  = -2.0;
      values[2]  = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      row        = 4;
      indices[0] = 0;
      indices[1] = 1;
      indices[2] = 2;
      values[0]  = -2.0;
      values[1]  = -2.0;
      values[2]  = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      break;
    case 3:
      row       = 0;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices + 1, values, INSERT_VALUES));
      row       = 1;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      row       = 2;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      row       = 3;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      values[3] = -1.0;
      PetscCall(MatSetValues(A, 1, &row, 4, indices, values, INSERT_VALUES));
      row       = 4;
      values[0] = -2.0;
      values[1] = -2.0;
      values[2] = -2.0;
      values[3] = -1.0;
      PetscCall(MatSetValues(A, 1, &row, 4, indices, values, INSERT_VALUES));
      break;

    default:
      row       = 0;
      values[0] = -1.0;
      values[1] = 0.0;
      values[2] = 1.0;
      values[3] = 3.0;
      values[4] = 4.0;
      values[5] = -5.0;
      PetscCall(MatSetValues(A, 1, &row, N, indices, values, INSERT_VALUES));
      row = 1;
      PetscCall(MatSetValues(A, 1, &row, 3, indices, values, INSERT_VALUES));
      row = 3;
      PetscCall(MatSetValues(A, 1, &row, 1, indices + 4, values + 4, INSERT_VALUES));
      row = 4;
      PetscCall(MatSetValues(A, 1, &row, 2, indices + 4, values + 4, INSERT_VALUES));
    }
  }
  PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
  PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
  PetscCall(MatView(A, PETSC_VIEWER_STDOUT_WORLD));

  PetscCall(MatGetLocalSize(A, &m, &n));
  PetscCall(VecCreate(PETSC_COMM_WORLD, &min));
  PetscCall(VecSetSizes(min, m, PETSC_DECIDE));
  PetscCall(VecSetFromOptions(min));
  PetscCall(VecDuplicate(min, &max));
  PetscCall(VecDuplicate(min, &maxabs));
  PetscCall(VecDuplicate(min, &e));

  /* MatGetRowMax() */
  PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n MatGetRowMax\n"));
  PetscCall(MatGetRowMax(A, max, NULL));
  PetscCall(MatGetRowMax(A, max, imax));
  PetscCall(VecView(max, PETSC_VIEWER_STDOUT_WORLD));
  PetscCall(VecGetLocalSize(max, &n));
  PetscCall(PetscIntView(n, imax, PETSC_VIEWER_STDOUT_WORLD));

  /* MatGetRowMin() */
  PetscCall(MatScale(A, -1.0));
  PetscCall(MatView(A, PETSC_VIEWER_STDOUT_WORLD));
  PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n MatGetRowMin\n"));
  PetscCall(MatGetRowMin(A, min, NULL));
  PetscCall(MatGetRowMin(A, min, imin));

  PetscCall(VecWAXPY(e, 1.0, max, min)); /* e = max + min */
  PetscCall(VecNorm(e, NORM_INFINITY, &enorm));
  PetscCheck(enorm <= PETSC_MACHINE_EPSILON, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "max+min > PETSC_MACHINE_EPSILON ");
  for (j = 0; j < n; j++) PetscCheck(imin[j] == imax[j], PETSC_COMM_SELF, PETSC_ERR_PLIB, "imin[%" PetscInt_FMT "] %" PetscInt_FMT " != imax %" PetscInt_FMT, j, imin[j], imax[j]);

  /* MatGetRowMaxAbs() */
  PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n MatGetRowMaxAbs\n"));
  PetscCall(MatGetRowMaxAbs(A, maxabs, NULL));
  PetscCall(MatGetRowMaxAbs(A, maxabs, imaxabs));
  PetscCall(VecView(maxabs, PETSC_VIEWER_STDOUT_WORLD));
  PetscCall(PetscIntView(n, imaxabs, PETSC_VIEWER_STDOUT_WORLD));

  /* MatGetRowMinAbs() */
  PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n MatGetRowMinAbs\n"));
  PetscCall(MatGetRowMinAbs(A, min, NULL));
  PetscCall(MatGetRowMinAbs(A, min, imin));
  PetscCall(VecView(min, PETSC_VIEWER_STDOUT_WORLD));
  PetscCall(PetscIntView(n, imin, PETSC_VIEWER_STDOUT_WORLD));

  if (size == 1) {
    /* Test MatGetRowMax, MatGetRowMin and MatGetRowMaxAbs for SeqDense and MPIBAIJ matrix */
    Mat Adense;
    Vec max_d, maxabs_d;
    PetscCall(VecDuplicate(min, &max_d));
    PetscCall(VecDuplicate(min, &maxabs_d));

    PetscCall(MatScale(A, -1.0));
    PetscCall(MatConvert(A, MATDENSE, MAT_INITIAL_MATRIX, &Adense));
    PetscCall(PetscPrintf(PETSC_COMM_WORLD, "MatGetRowMax for seqdense matrix\n"));
    PetscCall(MatGetRowMax(Adense, max_d, imax));

    PetscCall(VecWAXPY(e, -1.0, max, max_d)); /* e = -max + max_d */
    PetscCall(VecNorm(e, NORM_INFINITY, &enorm));
    PetscCheck(enorm <= PETSC_MACHINE_EPSILON, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "norm(-max + max_d) %g > PETSC_MACHINE_EPSILON", (double)enorm);

    PetscCall(MatScale(Adense, -1.0));
    PetscCall(PetscPrintf(PETSC_COMM_WORLD, "MatGetRowMin for seqdense matrix\n"));
    PetscCall(MatGetRowMin(Adense, min, imin));

    PetscCall(VecWAXPY(e, 1.0, max, min)); /* e = max + min */
    PetscCall(VecNorm(e, NORM_INFINITY, &enorm));
    PetscCheck(enorm <= PETSC_MACHINE_EPSILON, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "max+min > PETSC_MACHINE_EPSILON ");
    for (j = 0; j < n; j++) PetscCheck(imin[j] == imax[j], PETSC_COMM_SELF, PETSC_ERR_PLIB, "imin[%" PetscInt_FMT "] %" PetscInt_FMT " != imax %" PetscInt_FMT " for seqdense matrix", j, imin[j], imax[j]);

    PetscCall(PetscPrintf(PETSC_COMM_WORLD, "MatGetRowMaxAbs for seqdense matrix\n"));
    PetscCall(MatGetRowMaxAbs(Adense, maxabs_d, imaxabs));
    PetscCall(VecWAXPY(e, -1.0, maxabs, maxabs_d)); /* e = -maxabs + maxabs_d */
    PetscCall(VecNorm(e, NORM_INFINITY, &enorm));
    PetscCheck(enorm <= PETSC_MACHINE_EPSILON, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "norm(-maxabs + maxabs_d) %g > PETSC_MACHINE_EPSILON", (double)enorm);

    PetscCall(MatDestroy(&Adense));
    PetscCall(VecDestroy(&max_d));
    PetscCall(VecDestroy(&maxabs_d));
  }

  { /* BAIJ matrix */
    Mat                B;
    Vec                maxabsB, maxabsB2;
    PetscInt           bs = 2, *imaxabsB, *imaxabsB2, rstart, rend, cstart, cend, ncols, col, Brows[2], Bcols[2];
    const PetscInt    *cols;
    const PetscScalar *vals, *vals2;
    PetscScalar        Bvals[4];

    PetscCall(PetscMalloc2(M, &imaxabsB, bs * M, &imaxabsB2));

    /* bs = 1 */
    PetscCall(MatConvert(A, MATMPIBAIJ, MAT_INITIAL_MATRIX, &B));
    PetscCall(VecDuplicate(min, &maxabsB));
    PetscCall(MatGetRowMaxAbs(B, maxabsB, NULL));
    PetscCall(MatGetRowMaxAbs(B, maxabsB, imaxabsB));
    PetscCall(PetscPrintf(PETSC_COMM_WORLD, "\n MatGetRowMaxAbs for BAIJ matrix\n"));
    PetscCall(VecWAXPY(e, -1.0, maxabs, maxabsB)); /* e = -maxabs + maxabsB */
    PetscCall(VecNorm(e, NORM_INFINITY, &enorm));
    PetscCheck(enorm <= PETSC_MACHINE_EPSILON, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "norm(-maxabs + maxabs_d) %g > PETSC_MACHINE_EPSILON", (double)enorm);

    for (j = 0; j < n; j++) PetscCheck(imaxabs[j] == imaxabsB[j], PETSC_COMM_SELF, PETSC_ERR_PLIB, "imaxabs[%" PetscInt_FMT "] %" PetscInt_FMT " != imaxabsB %" PetscInt_FMT, j, imin[j], imax[j]);
    PetscCall(MatDestroy(&B));

    /* Test bs = 2: Create B with bs*bs block structure of A */
    PetscCall(VecCreate(PETSC_COMM_WORLD, &maxabsB2));
    PetscCall(VecSetSizes(maxabsB2, bs * m, PETSC_DECIDE));
    PetscCall(VecSetFromOptions(maxabsB2));

    PetscCall(MatGetOwnershipRange(A, &rstart, &rend));
    PetscCall(MatGetOwnershipRangeColumn(A, &cstart, &cend));
    PetscCall(MatCreate(PETSC_COMM_WORLD, &B));
    PetscCall(MatSetSizes(B, bs * (rend - rstart), bs * (cend - cstart), PETSC_DECIDE, PETSC_DECIDE));
    PetscCall(MatSetFromOptions(B));
    PetscCall(MatSetUp(B));

    for (row = rstart; row < rend; row++) {
      PetscCall(MatGetRow(A, row, &ncols, &cols, &vals));
      for (col = 0; col < ncols; col++) {
        for (j = 0; j < bs; j++) {
          Brows[j] = bs * row + j;
          Bcols[j] = bs * cols[col] + j;
        }
        for (j = 0; j < bs * bs; j++) Bvals[j] = vals[col];
        PetscCall(MatSetValues(B, bs, Brows, bs, Bcols, Bvals, INSERT_VALUES));
      }
      PetscCall(MatRestoreRow(A, row, &ncols, &cols, &vals));
    }
    PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
    PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));

    PetscCall(MatGetRowMaxAbs(B, maxabsB2, imaxabsB2));

    /* Check maxabsB2 and imaxabsB2 */
    PetscCall(VecGetArrayRead(maxabsB, &vals));
    PetscCall(VecGetArrayRead(maxabsB2, &vals2));
    for (row = 0; row < m; row++) PetscCheck(PetscAbsScalar(vals[row] - vals2[bs * row]) <= PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_PLIB, "row %" PetscInt_FMT " maxabsB != maxabsB2", row);
    PetscCall(VecRestoreArrayRead(maxabsB, &vals));
    PetscCall(VecRestoreArrayRead(maxabsB2, &vals2));

    for (col = 0; col < n; col++) PetscCheck(imaxabsB[col] == imaxabsB2[bs * col] / bs, PETSC_COMM_SELF, PETSC_ERR_PLIB, "col %" PetscInt_FMT " imaxabsB != imaxabsB2", col);
    PetscCall(VecDestroy(&maxabsB));
    PetscCall(MatDestroy(&B));
    PetscCall(VecDestroy(&maxabsB2));
    PetscCall(PetscFree2(imaxabsB, imaxabsB2));
  }

  PetscCall(VecDestroy(&min));
  PetscCall(VecDestroy(&max));
  PetscCall(VecDestroy(&maxabs));
  PetscCall(VecDestroy(&e));
  PetscCall(MatDestroy(&A));
  PetscCall(PetscFinalize());
  return 0;
}

/*TEST

   test:
      output_file: output/ex114.out

   test:
      suffix: 2
      args: -testcase 1
      output_file: output/ex114.out

   test:
      suffix: 3
      args: -testcase 2
      output_file: output/ex114_3.out

   test:
      suffix: 4
      args: -testcase 3
      output_file: output/ex114_4.out

   test:
      suffix: 5
      nsize: 3
      args: -testcase 0
      output_file: output/ex114_5.out

   test:
      suffix: 6
      nsize: 3
      args: -testcase 1
      output_file: output/ex114_6.out

   test:
      suffix: 7
      nsize: 3
      args: -testcase 2
      output_file: output/ex114_7.out

   test:
      suffix: 8
      nsize: 3
      args: -testcase 3
      output_file: output/ex114_8.out

TEST*/
