/*
   Defines some vector operation functions that are shared by
   sequential and parallel vectors.
*/
#include <../src/vec/vec/impls/dvecimpl.h>
#include <petsc/private/kernels/petscaxpy.h>

#if defined(PETSC_USE_FORTRAN_KERNEL_MDOT)
  #include <../src/vec/vec/impls/seq/ftn-kernels/fmdot.h>
PetscErrorCode VecMDot_Seq(Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  const PetscInt     n = xin->map->n;
  PetscInt           i = nv, nv_rem = nv & 0x3;
  PetscScalar        sum0 = 0., sum1 = 0., sum2 = 0., sum3;
  const PetscScalar *yy0, *yy1, *yy2, *yy3, *x;
  Vec               *yy = (Vec *)yin;

  PetscFunctionBegin;
  PetscCall(VecGetArrayRead(xin, &x));
  switch (nv_rem) {
  case 3:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    fortranmdot3_(x, yy0, yy1, yy2, &n, &sum0, &sum1, &sum2);
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    break;
  case 2:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    fortranmdot2_(x, yy0, yy1, &n, &sum0, &sum1);
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    z[0] = sum0;
    z[1] = sum1;
    break;
  case 1:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    fortranmdot1_(x, yy0, &n, &sum0);
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    z[0] = sum0;
    break;
  case 0:
    break;
  }
  z += nv_rem;
  i -= nv_rem;
  yy += nv_rem;

  while (i > 0) {
    sum0 = 0.;
    sum1 = 0.;
    sum2 = 0.;
    sum3 = 0.;
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    PetscCall(VecGetArrayRead(yy[3], &yy3));
    fortranmdot4_(x, yy0, yy1, yy2, yy3, &n, &sum0, &sum1, &sum2, &sum3);
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    PetscCall(VecRestoreArrayRead(yy[3], &yy3));
    yy += 4;
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    z[3] = sum3;
    z += 4;
    i -= 4;
  }
  PetscCall(VecRestoreArrayRead(xin, &x));
  PetscCall(PetscLogFlops(PetscMax(nv * (2.0 * n - 1), 0.0)));
  PetscFunctionReturn(PETSC_SUCCESS);
}

#else
PetscErrorCode VecMDot_Seq(Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  const PetscInt     n = xin->map->n;
  PetscInt           i = nv, j = n, nv_rem = nv & 0x3, j_rem;
  PetscScalar        sum0 = 0.0, sum1 = 0.0, sum2 = 0.0, sum3, x0, x1, x2, x3;
  const PetscScalar *yy0, *yy1, *yy2, *yy3, *x, *xbase;
  const Vec         *yy = (Vec *)yin;

  PetscFunctionBegin;
  if (n == 0) {
    PetscCall(PetscArrayzero(z, nv));
    PetscFunctionReturn(PETSC_SUCCESS);
  }
  PetscCall(VecGetArrayRead(xin, &xbase));
  x = xbase;
  switch (nv_rem) {
  case 3:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * PetscConj(yy0[2]);
      sum1 += x2 * PetscConj(yy1[2]);
      sum2 += x2 * PetscConj(yy2[2]); /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * PetscConj(yy0[1]);
      sum1 += x1 * PetscConj(yy1[1]);
      sum2 += x1 * PetscConj(yy2[1]); /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * PetscConj(yy0[0]);
      sum1 += x0 * PetscConj(yy1[0]);
      sum2 += x0 * PetscConj(yy2[0]); /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      yy2 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * PetscConj(yy0[0]) + x1 * PetscConj(yy0[1]) + x2 * PetscConj(yy0[2]) + x3 * PetscConj(yy0[3]);
      yy0 += 4;
      sum1 += x0 * PetscConj(yy1[0]) + x1 * PetscConj(yy1[1]) + x2 * PetscConj(yy1[2]) + x3 * PetscConj(yy1[3]);
      yy1 += 4;
      sum2 += x0 * PetscConj(yy2[0]) + x1 * PetscConj(yy2[1]) + x2 * PetscConj(yy2[2]) + x3 * PetscConj(yy2[3]);
      yy2 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    break;
  case 2:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * PetscConj(yy0[2]);
      sum1 += x2 * PetscConj(yy1[2]); /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * PetscConj(yy0[1]);
      sum1 += x1 * PetscConj(yy1[1]); /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * PetscConj(yy0[0]);
      sum1 += x0 * PetscConj(yy1[0]); /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * PetscConj(yy0[0]) + x1 * PetscConj(yy0[1]) + x2 * PetscConj(yy0[2]) + x3 * PetscConj(yy0[3]);
      yy0 += 4;
      sum1 += x0 * PetscConj(yy1[0]) + x1 * PetscConj(yy1[1]) + x2 * PetscConj(yy1[2]) + x3 * PetscConj(yy1[3]);
      yy1 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;

    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    break;
  case 1:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * PetscConj(yy0[2]); /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * PetscConj(yy0[1]); /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * PetscConj(yy0[0]); /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      sum0 += x[0] * PetscConj(yy0[0]) + x[1] * PetscConj(yy0[1]) + x[2] * PetscConj(yy0[2]) + x[3] * PetscConj(yy0[3]);
      yy0 += 4;
      j -= 4;
      x += 4;
    }
    z[0] = sum0;

    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    break;
  case 0:
    break;
  }
  z += nv_rem;
  i -= nv_rem;
  yy += nv_rem;

  while (i > 0) {
    sum0 = 0.;
    sum1 = 0.;
    sum2 = 0.;
    sum3 = 0.;
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    PetscCall(VecGetArrayRead(yy[3], &yy3));

    j = n;
    x = xbase;
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * PetscConj(yy0[2]);
      sum1 += x2 * PetscConj(yy1[2]);
      sum2 += x2 * PetscConj(yy2[2]);
      sum3 += x2 * PetscConj(yy3[2]); /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * PetscConj(yy0[1]);
      sum1 += x1 * PetscConj(yy1[1]);
      sum2 += x1 * PetscConj(yy2[1]);
      sum3 += x1 * PetscConj(yy3[1]); /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * PetscConj(yy0[0]);
      sum1 += x0 * PetscConj(yy1[0]);
      sum2 += x0 * PetscConj(yy2[0]);
      sum3 += x0 * PetscConj(yy3[0]); /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      yy2 += j_rem;
      yy3 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * PetscConj(yy0[0]) + x1 * PetscConj(yy0[1]) + x2 * PetscConj(yy0[2]) + x3 * PetscConj(yy0[3]);
      yy0 += 4;
      sum1 += x0 * PetscConj(yy1[0]) + x1 * PetscConj(yy1[1]) + x2 * PetscConj(yy1[2]) + x3 * PetscConj(yy1[3]);
      yy1 += 4;
      sum2 += x0 * PetscConj(yy2[0]) + x1 * PetscConj(yy2[1]) + x2 * PetscConj(yy2[2]) + x3 * PetscConj(yy2[3]);
      yy2 += 4;
      sum3 += x0 * PetscConj(yy3[0]) + x1 * PetscConj(yy3[1]) + x2 * PetscConj(yy3[2]) + x3 * PetscConj(yy3[3]);
      yy3 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    z[3] = sum3;
    z += 4;
    i -= 4;
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    PetscCall(VecRestoreArrayRead(yy[3], &yy3));
    yy += 4;
  }
  PetscCall(VecRestoreArrayRead(xin, &xbase));
  PetscCall(PetscLogFlops(PetscMax(nv * (2.0 * n - 1), 0.0)));
  PetscFunctionReturn(PETSC_SUCCESS);
}
#endif

/* ----------------------------------------------------------------------------*/
PetscErrorCode VecMTDot_Seq(Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  const PetscInt     n = xin->map->n;
  PetscInt           i = nv, j = n, nv_rem = nv & 0x3, j_rem;
  PetscScalar        sum0 = 0., sum1 = 0., sum2 = 0., sum3, x0, x1, x2, x3;
  const PetscScalar *yy0, *yy1, *yy2, *yy3, *x, *xbase;
  const Vec         *yy = (Vec *)yin;

  PetscFunctionBegin;
  PetscCall(VecGetArrayRead(xin, &xbase));
  x = xbase;

  switch (nv_rem) {
  case 3:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * yy0[2];
      sum1 += x2 * yy1[2];
      sum2 += x2 * yy2[2]; /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * yy0[1];
      sum1 += x1 * yy1[1];
      sum2 += x1 * yy2[1]; /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * yy0[0];
      sum1 += x0 * yy1[0];
      sum2 += x0 * yy2[0]; /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      yy2 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * yy0[0] + x1 * yy0[1] + x2 * yy0[2] + x3 * yy0[3];
      yy0 += 4;
      sum1 += x0 * yy1[0] + x1 * yy1[1] + x2 * yy1[2] + x3 * yy1[3];
      yy1 += 4;
      sum2 += x0 * yy2[0] + x1 * yy2[1] + x2 * yy2[2] + x3 * yy2[3];
      yy2 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    break;
  case 2:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * yy0[2];
      sum1 += x2 * yy1[2]; /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * yy0[1];
      sum1 += x1 * yy1[1]; /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * yy0[0];
      sum1 += x0 * yy1[0]; /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * yy0[0] + x1 * yy0[1] + x2 * yy0[2] + x3 * yy0[3];
      yy0 += 4;
      sum1 += x0 * yy1[0] + x1 * yy1[1] + x2 * yy1[2] + x3 * yy1[3];
      yy1 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;

    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    break;
  case 1:
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * yy0[2]; /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * yy0[1]; /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * yy0[0]; /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      sum0 += x[0] * yy0[0] + x[1] * yy0[1] + x[2] * yy0[2] + x[3] * yy0[3];
      yy0 += 4;
      j -= 4;
      x += 4;
    }
    z[0] = sum0;

    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    break;
  case 0:
    break;
  }
  z += nv_rem;
  i -= nv_rem;
  yy += nv_rem;

  while (i > 0) {
    sum0 = 0.;
    sum1 = 0.;
    sum2 = 0.;
    sum3 = 0.;
    PetscCall(VecGetArrayRead(yy[0], &yy0));
    PetscCall(VecGetArrayRead(yy[1], &yy1));
    PetscCall(VecGetArrayRead(yy[2], &yy2));
    PetscCall(VecGetArrayRead(yy[3], &yy3));
    x = xbase;

    j = n;
    switch (j_rem = j & 0x3) {
    case 3:
      x2 = x[2];
      sum0 += x2 * yy0[2];
      sum1 += x2 * yy1[2];
      sum2 += x2 * yy2[2];
      sum3 += x2 * yy3[2]; /* fall through */
    case 2:
      x1 = x[1];
      sum0 += x1 * yy0[1];
      sum1 += x1 * yy1[1];
      sum2 += x1 * yy2[1];
      sum3 += x1 * yy3[1]; /* fall through */
    case 1:
      x0 = x[0];
      sum0 += x0 * yy0[0];
      sum1 += x0 * yy1[0];
      sum2 += x0 * yy2[0];
      sum3 += x0 * yy3[0]; /* fall through */
    case 0:
      x += j_rem;
      yy0 += j_rem;
      yy1 += j_rem;
      yy2 += j_rem;
      yy3 += j_rem;
      j -= j_rem;
      break;
    }
    while (j > 0) {
      x0 = x[0];
      x1 = x[1];
      x2 = x[2];
      x3 = x[3];
      x += 4;

      sum0 += x0 * yy0[0] + x1 * yy0[1] + x2 * yy0[2] + x3 * yy0[3];
      yy0 += 4;
      sum1 += x0 * yy1[0] + x1 * yy1[1] + x2 * yy1[2] + x3 * yy1[3];
      yy1 += 4;
      sum2 += x0 * yy2[0] + x1 * yy2[1] + x2 * yy2[2] + x3 * yy2[3];
      yy2 += 4;
      sum3 += x0 * yy3[0] + x1 * yy3[1] + x2 * yy3[2] + x3 * yy3[3];
      yy3 += 4;
      j -= 4;
    }
    z[0] = sum0;
    z[1] = sum1;
    z[2] = sum2;
    z[3] = sum3;
    z += 4;
    i -= 4;
    PetscCall(VecRestoreArrayRead(yy[0], &yy0));
    PetscCall(VecRestoreArrayRead(yy[1], &yy1));
    PetscCall(VecRestoreArrayRead(yy[2], &yy2));
    PetscCall(VecRestoreArrayRead(yy[3], &yy3));
    yy += 4;
  }
  PetscCall(VecRestoreArrayRead(xin, &xbase));
  PetscCall(PetscLogFlops(PetscMax(nv * (2.0 * n - 1), 0.0)));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode VecMultiDot_Seq_GEMV(PetscBool conjugate, Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  PetscInt           i, j, nfail;
  const PetscScalar *xarray, *yarray, *yfirst, *ynext;
  PetscBool          stop  = PETSC_FALSE;
  const char        *trans = conjugate ? "C" : "T";
  PetscInt64         lda   = 0;
  PetscBLASInt       n, m;

  PetscFunctionBegin;
  if (xin->map->n == 0) { // otherwise BLAS complains illegal values (0) for lda
    PetscCall(PetscArrayzero(z, nv));
    PetscFunctionReturn(PETSC_SUCCESS);
  }

  // caller guarantees xin->map->n <= PETSC_BLAS_INT_MAX
  PetscCall(PetscBLASIntCast(xin->map->n, &n));
  PetscCall(VecGetArrayRead(xin, &xarray));
  i = nfail = 0;
  while (i < nv) {
    stop = PETSC_FALSE;
    PetscCall(VecGetArrayRead(yin[i], &yfirst));
    yarray = yfirst;
    for (j = i + 1; j < nv; j++) {
      PetscCall(VecGetArrayRead(yin[j], &ynext));
      if (j == i + 1) {
        lda = ynext - yarray;
        if (lda < 0 || lda > PETSC_BLAS_INT_MAX || lda - n > 64) stop = PETSC_TRUE;
      } else if (lda * (j - i) != ynext - yarray) { // not in the same stride? if so, stop here
        stop = PETSC_TRUE;
      }
      PetscCall(VecRestoreArrayRead(yin[j], &ynext));
      if (stop) break;
    }
    PetscCall(VecRestoreArrayRead(yin[i], &yfirst));

    // we found m vectors yin[i..j)
    PetscCall(PetscBLASIntCast(j - i, &m));
    if (m > 1) {
      PetscBLASInt ione = 1, lda2 = (PetscBLASInt)lda; // the cast is safe since we've screened out those lda > PETSC_BLAS_INT_MAX above
      PetscScalar  one = 1, zero = 0;

      PetscCallBLAS("BLASgemv", BLASgemv_(trans, &n, &m, &one, yarray, &lda2, xarray, &ione, &zero, z + i, &ione));
      PetscCall(PetscLogFlops(PetscMax(m * (2.0 * n - 1), 0.0)));
    } else {
      if (nfail == 0) {
        if (conjugate) PetscCall(VecDot_Seq(xin, yin[i], z + i));
        else PetscCall(VecTDot_Seq(xin, yin[i], z + i));
        nfail++;
      } else break;
    }
    i = j;
  }
  PetscCall(VecRestoreArrayRead(xin, &xarray));
  if (i < nv) { // finish the remaining if any
    if (conjugate) PetscCall(VecMDot_Seq(xin, nv - i, yin + i, z + i));
    else PetscCall(VecMTDot_Seq(xin, nv - i, yin + i, z + i));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecMDot_Seq_GEMV(Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  PetscFunctionBegin;
  if (xin->map->n > PETSC_BLAS_INT_MAX) {
    PetscCall(VecMDot_Seq(xin, nv, yin, z));
  } else {
    PetscCall(VecMultiDot_Seq_GEMV(PETSC_TRUE, xin, nv, yin, z));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecMTDot_Seq_GEMV(Vec xin, PetscInt nv, const Vec yin[], PetscScalar *z)
{
  PetscFunctionBegin;
  if (xin->map->n > PETSC_BLAS_INT_MAX) {
    PetscCall(VecMTDot_Seq(xin, nv, yin, z));
  } else {
    PetscCall(VecMultiDot_Seq_GEMV(PETSC_FALSE, xin, nv, yin, z));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode VecMinMax_Seq(Vec xin, PetscInt *idx, PetscReal *z, PetscReal minmax, int (*const cmp)(PetscReal, PetscReal))
{
  const PetscInt n = xin->map->n;
  PetscInt       j = -1;

  PetscFunctionBegin;
  if (n) {
    const PetscScalar *xx;

    PetscCall(VecGetArrayRead(xin, &xx));
    minmax = PetscRealPart(xx[(j = 0)]);
    for (PetscInt i = 1; i < n; ++i) {
      const PetscReal tmp = PetscRealPart(xx[i]);

      if (cmp(tmp, minmax)) {
        j      = i;
        minmax = tmp;
      }
    }
    PetscCall(VecRestoreArrayRead(xin, &xx));
  }
  *z = minmax;
  if (idx) *idx = j;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static int VecMax_Seq_GT(PetscReal l, PetscReal r)
{
  return l > r;
}

PetscErrorCode VecMax_Seq(Vec xin, PetscInt *idx, PetscReal *z)
{
  PetscFunctionBegin;
  PetscCall(VecMinMax_Seq(xin, idx, z, PETSC_MIN_REAL, VecMax_Seq_GT));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static int VecMin_Seq_LT(PetscReal l, PetscReal r)
{
  return l < r;
}

PetscErrorCode VecMin_Seq(Vec xin, PetscInt *idx, PetscReal *z)
{
  PetscFunctionBegin;
  PetscCall(VecMinMax_Seq(xin, idx, z, PETSC_MAX_REAL, VecMin_Seq_LT));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecSet_Seq(Vec xin, PetscScalar alpha)
{
  const PetscInt n = xin->map->n;
  PetscScalar   *xx;

  PetscFunctionBegin;
  PetscCall(VecGetArrayWrite(xin, &xx));
  if (alpha == (PetscScalar)0.0) {
    PetscCall(PetscArrayzero(xx, n));
  } else {
    for (PetscInt i = 0; i < n; i++) xx[i] = alpha;
  }
  PetscCall(VecRestoreArrayWrite(xin, &xx));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecMAXPY_Seq(Vec xin, PetscInt nv, const PetscScalar *alpha, Vec *y)
{
  const PetscInt     j_rem = nv & 0x3, n = xin->map->n;
  const PetscScalar *yptr[4];
  PetscScalar       *xx;
#if defined(PETSC_HAVE_PRAGMA_DISJOINT)
  #pragma disjoint(*xx, **yptr, *aptr)
#endif

  PetscFunctionBegin;
  PetscCall(PetscLogFlops(nv * 2.0 * n));
  PetscCall(VecGetArray(xin, &xx));
  for (PetscInt i = 0; i < j_rem; ++i) PetscCall(VecGetArrayRead(y[i], yptr + i));
  switch (j_rem) {
  case 3:
    PetscKernelAXPY3(xx, alpha[0], alpha[1], alpha[2], yptr[0], yptr[1], yptr[2], n);
    break;
  case 2:
    PetscKernelAXPY2(xx, alpha[0], alpha[1], yptr[0], yptr[1], n);
    break;
  case 1:
    PetscKernelAXPY(xx, alpha[0], yptr[0], n);
  default:
    break;
  }
  for (PetscInt i = 0; i < j_rem; ++i) PetscCall(VecRestoreArrayRead(y[i], yptr + i));
  alpha += j_rem;
  y += j_rem;
  for (PetscInt j = j_rem, inc = 4; j < nv; j += inc, alpha += inc, y += inc) {
    for (PetscInt i = 0; i < inc; ++i) PetscCall(VecGetArrayRead(y[i], yptr + i));
    PetscKernelAXPY4(xx, alpha[0], alpha[1], alpha[2], alpha[3], yptr[0], yptr[1], yptr[2], yptr[3], n);
    for (PetscInt i = 0; i < inc; ++i) PetscCall(VecRestoreArrayRead(y[i], yptr + i));
  }
  PetscCall(VecRestoreArray(xin, &xx));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*  y = y + sum alpha[i] x[i] */
PetscErrorCode VecMAXPY_Seq_GEMV(Vec yin, PetscInt nv, const PetscScalar alpha[], Vec xin[])
{
  PetscInt           i, j, nfail;
  PetscScalar       *yarray;
  const PetscScalar *xfirst, *xnext, *xarray;
  PetscBool          stop = PETSC_FALSE;
  PetscInt64         lda  = 0;
  PetscBLASInt       n, m;

  PetscFunctionBegin;
  if (yin->map->n == 0 || yin->map->n > PETSC_BLAS_INT_MAX) {
    PetscCall(VecMAXPY_Seq(yin, nv, alpha, xin));
    PetscFunctionReturn(PETSC_SUCCESS);
  }

  PetscCall(PetscBLASIntCast(yin->map->n, &n));
  PetscCall(VecGetArray(yin, &yarray));
  i = nfail = 0;
  while (i < nv) {
    stop = PETSC_FALSE;
    PetscCall(VecGetArrayRead(xin[i], &xfirst));
    xarray = xfirst;
    for (j = i + 1; j < nv; j++) {
      PetscCall(VecGetArrayRead(xin[j], &xnext));
      if (j == i + 1) {
        lda = xnext - xarray;
        if (lda < 0 || lda > PETSC_BLAS_INT_MAX || lda - n > 64) stop = PETSC_TRUE;
      } else if (lda * (j - i) != xnext - xarray) { // not in the same stride? if so, stop here
        stop = PETSC_TRUE;
      }
      PetscCall(VecRestoreArrayRead(xin[j], &xnext));
      if (stop) break;
    }
    PetscCall(VecRestoreArrayRead(xin[i], &xfirst));

    PetscCall(PetscBLASIntCast(j - i, &m));
    if (m > 1) {
      PetscBLASInt incx = 1, incy = 1, lda2 = (PetscBLASInt)lda; // the cast is safe since we've screened out those lda > PETSC_BLAS_INT_MAX above
      PetscScalar  one = 1;
      PetscCallBLAS("BLASgemv", BLASgemv_("N", &n, &m, &one, xarray, &lda2, alpha + i, &incx, &one, yarray, &incy));
      PetscCall(PetscLogFlops(m * 2.0 * n));
    } else {
      // we only allow falling back on VecAXPY once
      if (nfail++ == 0) PetscCall(VecAXPY_Seq(yin, alpha[i], xin[i]));
      else break; // break the while loop
    }
    i = j;
  }
  PetscCall(VecRestoreArray(yin, &yarray));

  // finish the remaining if any
  if (i < nv) PetscCall(VecMAXPY_Seq(yin, nv - i, alpha + i, xin + i));
  PetscFunctionReturn(PETSC_SUCCESS);
}

#include <../src/vec/vec/impls/seq/ftn-kernels/faypx.h>

PetscErrorCode VecAYPX_Seq(Vec yin, PetscScalar alpha, Vec xin)
{
  PetscFunctionBegin;
  if (alpha == (PetscScalar)0.0) {
    PetscCall(VecCopy(xin, yin));
  } else if (alpha == (PetscScalar)1.0) {
    PetscCall(VecAXPY_Seq(yin, alpha, xin));
  } else {
    const PetscInt     n = yin->map->n;
    const PetscScalar *xx;
    PetscScalar       *yy;

    PetscCall(VecGetArrayRead(xin, &xx));
    PetscCall(VecGetArray(yin, &yy));
    if (alpha == (PetscScalar)-1.0) {
      for (PetscInt i = 0; i < n; ++i) yy[i] = xx[i] - yy[i];
      PetscCall(PetscLogFlops(n));
    } else {
#if defined(PETSC_USE_FORTRAN_KERNEL_AYPX)
      fortranaypx_(&n, &alpha, xx, yy);
#else
      for (PetscInt i = 0; i < n; ++i) yy[i] = xx[i] + alpha * yy[i];
#endif
      PetscCall(PetscLogFlops(2 * n));
    }
    PetscCall(VecRestoreArrayRead(xin, &xx));
    PetscCall(VecRestoreArray(yin, &yy));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

#include <../src/vec/vec/impls/seq/ftn-kernels/fwaxpy.h>
/*
   IBM ESSL contains a routine dzaxpy() that is our WAXPY() but it appears
   to be slower than a regular C loop.  Hence,we do not include it.
   void ?zaxpy(int*,PetscScalar*,PetscScalar*,int*,PetscScalar*,int*,PetscScalar*,int*);
*/

PetscErrorCode VecWAXPY_Seq(Vec win, PetscScalar alpha, Vec xin, Vec yin)
{
  const PetscInt     n = win->map->n;
  const PetscScalar *yy, *xx;
  PetscScalar       *ww;

  PetscFunctionBegin;
  PetscCall(VecGetArrayRead(xin, &xx));
  PetscCall(VecGetArrayRead(yin, &yy));
  PetscCall(VecGetArray(win, &ww));
  if (alpha == (PetscScalar)1.0) {
    PetscCall(PetscLogFlops(n));
    /* could call BLAS axpy after call to memcopy, but may be slower */
    for (PetscInt i = 0; i < n; i++) ww[i] = yy[i] + xx[i];
  } else if (alpha == (PetscScalar)-1.0) {
    PetscCall(PetscLogFlops(n));
    for (PetscInt i = 0; i < n; i++) ww[i] = yy[i] - xx[i];
  } else if (alpha == (PetscScalar)0.0) {
    PetscCall(PetscArraycpy(ww, yy, n));
  } else {
    PetscCall(PetscLogFlops(2.0 * n));
#if defined(PETSC_USE_FORTRAN_KERNEL_WAXPY)
    fortranwaxpy_(&n, &alpha, xx, yy, ww);
#else
    for (PetscInt i = 0; i < n; i++) ww[i] = yy[i] + alpha * xx[i];
#endif
  }
  PetscCall(VecRestoreArrayRead(xin, &xx));
  PetscCall(VecRestoreArrayRead(yin, &yy));
  PetscCall(VecRestoreArray(win, &ww));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecMaxPointwiseDivide_Seq(Vec xin, Vec yin, PetscReal *max)
{
  const PetscInt     n = xin->map->n;
  const PetscScalar *xx, *yy;
  PetscReal          m = 0.0;

  PetscFunctionBegin;
  PetscCall(VecGetArrayRead(xin, &xx));
  PetscCall(VecGetArrayRead(yin, &yy));
  for (PetscInt i = 0; i < n; ++i) {
    const PetscReal v = PetscAbsScalar(yy[i] == (PetscScalar)0.0 ? xx[i] : xx[i] / yy[i]);

    // use a separate value to not re-evaluate side-effects
    m = PetscMax(v, m);
  }
  PetscCall(VecRestoreArrayRead(xin, &xx));
  PetscCall(VecRestoreArrayRead(yin, &yy));
  PetscCall(PetscLogFlops(n));
  *max = m;
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecPlaceArray_Seq(Vec vin, const PetscScalar *a)
{
  Vec_Seq *v = (Vec_Seq *)vin->data;

  PetscFunctionBegin;
  PetscCheck(!v->unplacedarray, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "VecPlaceArray() was already called on this vector, without a call to VecResetArray()");
  v->unplacedarray = v->array; /* save previous array so reset can bring it back */
  v->array         = (PetscScalar *)a;
  PetscFunctionReturn(PETSC_SUCCESS);
}

PetscErrorCode VecReplaceArray_Seq(Vec vin, const PetscScalar *a)
{
  Vec_Seq *v = (Vec_Seq *)vin->data;

  PetscFunctionBegin;
  PetscCall(PetscFree(v->array_allocated));
  v->array_allocated = v->array = (PetscScalar *)a;
  PetscFunctionReturn(PETSC_SUCCESS);
}
